From 3dd47fa1789af23a44a3c90d8666de8ce412229c Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Thu, 8 Oct 2026 14:48:31 -0400 Subject: [PATCH] DRY pass #2, batch 5, F17: the web's smaller pairs (#5372) - desktop/bridge.ts listen(): the no-op-off-desktop event listener that onSynced, onCaptured and onReminderDue each wrote. - size.ts roughSize(): one decimal below 10, none above, for the client download sizes (MB) and the storage line (GB). - BaseModal's title prop draws the heading row and close button that LabelsModal and ShareDialog each built. - SyncView: switchChannel and switchSource share recheck(); the four update-card radios render from two option lists. Kept: the board's and the shell's is-typing guards (they ignore different elements), the drawer's nav beside the palette's commands (one is a laid- out list with tags between its entries, the other a command list), the status pills (each colours differently), and plurals (the copy is the operator's call, raised in #5371). Co-Authored-By: Claude Opus 5.5 --- frontend/src/components/BaseModal.vue | 14 ++- frontend/src/components/ClientDownloads.vue | 10 +- frontend/src/components/LabelsModal.vue | 6 +- frontend/src/components/ShareDialog.vue | 6 +- frontend/src/desktop/bridge.ts | 23 ++-- frontend/src/size.ts | 13 +++ frontend/src/views/AccountView.vue | 10 +- frontend/src/views/SyncView.vue | 123 +++++++++----------- 8 files changed, 104 insertions(+), 101 deletions(-) create mode 100644 frontend/src/size.ts diff --git a/frontend/src/components/BaseModal.vue b/frontend/src/components/BaseModal.vue index e5fe31d..ad7aff6 100644 --- a/frontend/src/components/BaseModal.vue +++ b/frontend/src/components/BaseModal.vue @@ -2,8 +2,8 @@ // The backdrop + centered dialog panel every modal shares: a fixed dimmed overlay and // a bordered, rounded panel with role="dialog". Emits `close` on Escape and on a // backdrop pointer-down (mousedown outside the panel). The caller sizes/shadows/pads -// the panel via `panelClass` and fills it (its own header + content) through the -// default slot. +// the panel via `panelClass` and fills it through the default slot; `title` adds the +// heading row with a close button that a titled dialog wants. // // Owns the dialog's focus contract on behalf of all four callers: focus moves in on // open, Tab is trapped inside, and focus returns to the opener on close. Doing it @@ -13,6 +13,7 @@ // NoteEditor deliberately does NOT use this — its backdrop mousedown is drag-guarded and // its Escape/⌘-Enter handling is bespoke (unsaved-edit safety), so it keeps its own shell. import { onBeforeUnmount, onMounted, ref } from "vue"; +import Icon from "./Icon.vue"; withDefaults( defineProps<{ @@ -22,6 +23,8 @@ withDefaults( align?: "start" | "center"; /** aria-label for the panel when it has no visible heading (e.g. the command palette). */ ariaLabel?: string; + /** A heading row with a close button, for a dialog that has a title. */ + title?: string; }>(), { panelClass: "w-full max-w-sm shadow-xl", align: "start" }, ); @@ -109,6 +112,13 @@ onBeforeUnmount(() => restoreTo?.focus?.()); @keydown.esc="emit('close')" @keydown.tab="onTab" > +
+

{{ title }}

+ +
diff --git a/frontend/src/components/ClientDownloads.vue b/frontend/src/components/ClientDownloads.vue index 8a70e33..257c3a2 100644 --- a/frontend/src/components/ClientDownloads.vue +++ b/frontend/src/components/ClientDownloads.vue @@ -7,6 +7,7 @@ // because a wrong guess must cost a person nothing. import { computed } from "vue"; import { useConfigStore, type ClientRelease } from "../stores/config"; +import { roughSize } from "../size"; const config = useConfigStore(); @@ -108,13 +109,6 @@ const missingPlatform = computed(() => const hasLinux = computed(() => Object.keys(config.clients).some((id) => id.startsWith("linux-"))); const installCommand = `curl -fsSL ${window.location.origin}/install.sh | sh`; -// One decimal below 10 MB, none above: these sit in one list where a 2.7 MB -// package and a 95 MB AppImage are compared, and "3 MB" next to "95 MB" loses the -// only distinction that matters at the small end. -function readableSize(bytes: number): string { - const mb = bytes / 1024 / 1024; - return `${mb < 10 ? mb.toFixed(1) : mb.toFixed(0)} MB`; -}