feat: app-wide zoom shortcuts (⌘/Ctrl +, −, 0), persisted - #123
Merged
mattenarle10 merged 1 commit intoJul 27, 2026
Conversation
Webview-level zoom so editor, preview, sidebar and chrome scale together, complementing the reading-size controls (mattenarle10#84/mattenarle10#89), which keep governing prose type. 10% steps clamped to 50%-300%, normalized on load so corrupted storage can't escape the range, persisted across restarts. Shared use-app-zoom hook runs in both window roots (main + detached preview); numpad +/- supported via the shortcut matcher's code map; the macOS traffic-light inset is counter-scaled so native chrome stays aligned at any zoom. Unit tests for the normalize/clamp helper. Adds the set-webview-zoom capability, README keyboard rows, and help-overlay entries in all ten locales. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sashamerzliakov
added a commit
to sashamerzliakov/markamd
that referenced
this pull request
Jul 27, 2026
…attenarle10#123) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sashamerzliakov
added a commit
to sashamerzliakov/markamd
that referenced
this pull request
Jul 29, 2026
- pr-plan.md: fork track marked merged (#1 / d312b49), the three-way split replaced with what actually landed and why it landed as one PR, order of operations rewritten around what's done and what's next. - CUSTOM.md: files-touched list was three features out of date — it predates the watcher rewrite, the minimal-diff dispatch, the tsv work, the tests and the checklist. - Removed docs/upstream-submission-drafts.md and docs/upstream-zoom-pr-draft.md. Both described PRs that merged upstream as mattenarle10#122 and mattenarle10#123 in July; leaving them reads as pending work. The release gap is now stated in the plan rather than implied: with no fork release, this repo's install link and badges point at upstream's builds, so a visitor downloading from the front page gets an app without any of the features listed above the link. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Transparency up front: this feature was built with AI assistance (Claude — see the commit trailer) in my fork. I've verified the behavior on my own machine (macOS, Apple Silicon); platform notes below state exactly what was and wasn't tested.
Motivation — the gap #89 doesn't cover
#84 asked for Cmd+/− zoom for presenting, and #89 answered it with reading-size controls plus quick zoom buttons in reading mode — which solve it well for prose. This PR is about the surface #89 doesn't reach: the sidebar, tabs, status bar, dialogs, and the editor pane stay at fixed size, and outside reading mode there's no zoom at all. Whole-UI scaling is the browser-standard behavior for ⌘/Ctrl
+−0(which are currently unbound), and it matters for the same presenting/eyesight scenarios as #84 — just for the whole app rather than the article body.The two axes stay complementary and independent: the reading-size controls keep governing prose type; these shortcuts scale the entire UI around it. (⌘0 resets the UI scale only — it deliberately leaves your reading-size preference alone.)
Why not
zoomHotkeysEnabled?Tauri ships built-in zoom hotkeys behind a one-line config flag, and I considered it first. The built-in polyfill resets to 100% on every launch (no persistence), steps 20% at a time over a 20%–1000% range, and isn't integrated with the app (no help-overlay/README documentation surface, no way to reason about interactions like reading mode). This implementation persists across restarts, steps 10% over a saner 50%–300%, and lives in the same shortcut system as everything else. Happy to switch to the flag if you'd rather have the one-liner — but I think the persistence alone is worth the ~70 lines.
What it does
+) zooms in 10%, ⌘− (and numpad−) zooms out 10%, ⌘0 resets — clamped to 50%–300%. On Windows/Linux the same bindings arrive as Ctrl via the existingmodhandling.getCurrentWebview().setZoom()), so editor, preview, sidebar, and chrome scale together.mdview.zoomLevelstorage key), and persisted values are normalized on load (finite, one decimal, clamped) so corrupted localStorage can never drive the webview outside the supported range.--mdv-zoomCSS variable — the native buttons keep their physical size under page zoom, so a fixed 84px inset would otherwise slide content beneath them below 100%.Implementation
src/hooks/use-app-zoom.ts— new hook: persisted + normalized zoom state, thesetZoomeffect (a rejected call logs aconsole.errorrather than leaving an unhandled rejection), the--mdv-zoomCSS variable, and the four shortcut bindingssrc/app.tsx,src/components/editor/preview-window.tsx— oneuseAppZoom()call in each window rootsrc/hooks/use-shortcuts.ts— the shifted-symbol code map now accepts multiple codes per key, addingNumpadAdd/NumpadSubtractso keypad zoom works (numpad+reportse.key === "+", which can't be expressed in the combo syntax and would otherwise never match)src/styles/chrome/titlebar.css— traffic-light inset counter-scaled by--mdv-zoomsrc/lib/storage.ts— one storage key;src-tauri/capabilities/default.json— addscore:webview:allow-set-webview-zoom(already granted to both windows)tests/app-zoom.test.ts— unit tests for the normalize/clamp helper: bounds, corrupt hydration values, rounding/drift over full-range steppingNo new dependencies.
mod+=,mod+shift+=,mod+-, andmod+0are unbound today (verified against the current shortcut map and against CodeMirror's default/search/history/vim keymaps, none of which bind these keys — events bubble to the window listener).Platform notes
Tauri's
Webview.setZoom(wry 0.55, per the lockfile) maps to WKWebViewsetPageZoomon macOS,Controller.SetZoomFactoron Windows WebView2, andset_zoom_levelon webkitgtk on Linux.setPageZoomis macOS 11+ (per wry's wkwebview module and Tauri's rustdoc). One thing worth your call: the README advertises macOS 13+, buttauri.conf.jsonsets nominimumSystemVersion, so the built binary's deployment target is Tauri's default 10.13 — on 10.13–10.15 the zoom call would be a no-op at best. If 13+ is the intended floor, pinningminimumSystemVersionwould close that gap independently of this PR.Verified manually on macOS (Apple Silicon): stepping, clamping at both ends, reset, persistence across restart, the detached preview window, the help overlay rows, titlebar/traffic-light alignment at 50%/100%/300%, and interplay with the reading-size settings. I have not tested Windows or Linux — happy to adjust if either behaves differently.
Testing
bun test— 50 pass (3 new zoom tests);bunx tsc --noEmitclean.