Preview Magnifier (Loupe) — Design Spec
Milestone: preview-magnifier Manual: TBD — a “Magnifying a source” section for the reading/transcribing part: how to summon the loupe, park it, resize it, change magnification, and what ⌥ does.
Design-led (Testing Constitution). The creative director owns this intent; tests enforce it; code makes them pass. Status: DRAFT. Tags: [OK] built · [GAP] intended, never built · [BROKEN] code contradicts the line.
Intent (the design)
The magnifier is a reading instrument on a source, not a window feature. A palaeographer working a hard hand needs to enlarge a patch of ink without losing their place in the page: the loupe sits where you leave it, follows only when asked, and says what magnification it is at.
Split from panes-workspaces (creative-director, 2026-09-17): “magnifiers for preview image
editor… that’s in preview, source. not really workspaces, other than they should sync.” This spec
owns the instrument; [[panes-workspaces]] owns only how loupe state behaves across MORE THAN ONE
pane (panes.magnifier.per-pane-open-state, panes.zoom.sync-across-panes).
Behaviors
magnifier.parks-where-left[PARTIAL] (implemented, unpinned; #4806) — the loupe stays where you put it and does not ride the cursor; ⌥-click parks it at the click. (Daniel, 2026-09-01: “it should live where you leave it”.)TrackingImageView.swift.magnifier.option-summons-transient[PARTIAL] (implemented, unpinned; #4806) — holding ⌥ alone summons a transient loupe while the toggled loupe is off; releasing dismisses it. ⌥ must be the SOLE modifier — a ⌘⌥ chord is a keyboard shortcut, not a loupe request (fixed 2026-09-16: ⌘⌥1 was popping the loupe).ZoomableImagePreviewMac.installOptionLoupeMonitor.magnifier.follows-only-while-option[PARTIAL] (implemented, unpinned; #4806) — a parked loupe follows the pointer only while ⌥ is held and it is unlocked; otherwise it holds position.TrackingImageView.mouseMoved.magnifier.lockable[PARTIAL] (implemented, unpinned; #4806) — the loupe can be locked so it ignores follow entirely, with a lock badge drawn on it. ⌘⌥M toggles the lock.magnifier.scroll-adjusts-magnification[PARTIAL] (implemented, unpinned; #4806) — scrolling OVER a parked loupe changes its magnification; scrolling anywhere else pans the page. Bounded byMagnifierLimits.magnifier.edge-resize[PARTIAL] (implemented, unpinned; #4806) — dragging the loupe’s edge resizes it; dragging its middle moves what it looks at along with it.magnifier.states-its-power[PARTIAL] (implemented, unpinned; #4806) — the loupe shows aN.Nx · NNNpxbadge so magnification is never guessed.magnifier.right-click-dismiss[PARTIAL] (implemented, unpinned; #4806) — right-clicking a placed loupe removes it.magnifier.follow-mouse-bar[GAP] (#4716) — a bottom magnifier BAR that tracks the pointer and magnifies the strip under it (distinct from the round loupe), for scanning a line of text.magnifier.per-source-memory[GAP] (#4717) — a source remembers its loupe size/magnification, so returning to a hand you were working resumes where you were.magnifier.pdf-viewer-has-the-loupe-too[GAP] (#2419, redirected from the legacy “Reader View - Page” milestone while foldingreader-view.md’s pass 2) the PDF viewer should offer the same loupe the image viewer already has, with its summoning control at the BOTTOM of the PDF view — this spec’s behaviors above are all read against the image-viewer loupe; not verified whether any apply to the PDF viewer already, or whether it has none at all.
Test matrix
| Behavior | Test | State |
|---|---|---|
| option-summons-transient (⌥ sole modifier) | MenuShortcutUniquenessTests (chord uniqueness) |
⚠ partial — needs a direct modifier test |
| parks-where-left / follows-only-while-option | — | ❌ |
| lockable / scroll-adjusts / edge-resize | — | ❌ |
| states-its-power | — | ❌ |
| follow-mouse-bar | — | ❌ [GAP] |
| per-source-memory | — | ❌ [GAP] |
Open questions
- Does the magnifier belong to the source or to the rendition? A deskewed rendition and the base page are different pixels — does the loupe follow the source across renditions?
- Is the bottom magnifier BAR (
follow-mouse-bar) still wanted, given the parked loupe works? - Should magnification be absolute (3.0x) or relative to the page’s current zoom?
References
fichero/fichero/Views/Preview/ImageViewer/TrackingImageView.swift(parking, follow, resize, badge)fichero/fichero/Views/Preview/ImageViewer/ZoomableImagePreviewMac.swift(⌥ monitor, transient loupe)fichero/fichero/App/Menus/ImagePreviewMenuCommands.swift(⌘⌥L toggle, ⌘⌥M lock, zoom in/out)- Cross-pane behavior: [[panes-workspaces]] §B