DRY pass #2, batch 5, F17: the web's smaller pairs (#5372)

- desktop/bridge.ts listen<T>(): 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 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 14:48:31 -04:00
co-authored by Claude Opus 5.5
parent 6e4c4930cb
commit 3dd47fa178
8 changed files with 104 additions and 101 deletions
+13
View File
@@ -0,0 +1,13 @@
// How big something is, in words a person reads at a glance.
const UNITS = { MB: 1024 ** 2, GB: 1024 ** 3 } as const;
/**
* `bytes` in `unit`, with one decimal below 10 and none above. Sizes sit in lists
* 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.
*/
export function roughSize(bytes: number, unit: keyof typeof UNITS): string {
const n = bytes / UNITS[unit];
return `${n < 10 ? n.toFixed(1) : n.toFixed(0)} ${unit}`;
}