The ten most-duplicated inline SVGs (Check, Close, ChevronDown/Right,
Plus, Photo, Folder, Warning, Star, Play) become shared components --
34+ copies across 15 files replaced. Stroke icons take a per-site
strokeWidth since weights legitimately differ by context; icons that
appear once or use variant path data stay inline.
Right-clicking an image in the Gallery or Timeline now offers an
"Add to Album" submenu listing all albums with their counts — the first
use of the new SubMenu primitive. Filing a single image away no longer
requires starting a multi-select.
Five hand-rolled context menu implementations (image tiles, sidebar
folders, sidebar albums, title bar theme switcher, plus duplicated
close-listener effects in Gallery/Timeline) are replaced by shared
primitives in src/components/menu/:
- useDismissable: one outside-pointerdown + Escape dismissal hook
- Menu.tsx: MenuPanel chrome, MenuItem (danger/disabled/checked/hint),
MenuSeparator, MenuLabel, and SubMenu with viewport edge-flipping
- ContextMenu: portal-rendered wrapper that measures and clamps to the
viewport, fixing menus rendering off-screen and the latent
fixed-inside-transform bug under framer-motion Reorder items
The image right-click menu moves to ImageContextMenu.tsx, shared by
Gallery and Timeline.