feat(annotate): hide HTML tools by default, toggle chords for annotate mode and tools, shortcut tooltips on the header controls (#1531)
* feat(annotate): hide the HTML surface tools by default An HTML document is authored to fill the viewport, so a raw-HTML (or live-app) annotate session now opens with the floating chrome over the page removed and the header eye rendering its "Show tools" state from the first paint. DEFAULT_HTML_CHROME_STATE.toolsHidden flips to true and App seeds htmlToolsHidden true to match, so there is no flash of tools before the restore effect runs. A fresh persisted record still wins in both directions: a reviewer who explicitly showed the tools keeps them next session. Hidden can never strand anyone — the eye lives in the header on desktop and the compact Options menu offers "Show tools" — which is the same reasoning that already made restoring a hidden cookie safe. * feat(annotate): make Mod+Shift+A a real toggle and add Mod+Shift+X for the tools Mod+Shift+A already flipped annotate mode in both directions in the app; the scope described it as a re-arm only. It is now documented as the toggle it is — the answer to "Esc dropped me to Interact, how do I get back?" — and pinned by a test that presses it twice. Adds Mod+Shift+X for the header eye, wired through the same scope so it reaches the help modal and the generated marketing shortcuts page, and mirrored inside the sandboxed iframe by the bridge on the capture phase exactly like the annotate chord (additive message type, no protocol bump), so it works whichever document owns focus. The binding is deliberately un-mnemonic: every mnemonic letter is a browser chord (H Home/history, E/I/J/K/C devtools, B/O bookmarks, V paste-as-plain-text, T reopen tab), and a chord the browser eats is worse than an arbitrary one. The tools chord is not gated on documentReadOnly, matching the eye. Disarming through either path already tears the pending draft down via the bridge's set-annotate-mode(false) handler, so toggling to Interact cannot leave a draft floating over a page that now takes clicks. * feat(annotate): describe the HTML header controls with real tooltips The eye, the pen and Refresh leaned on the native title attribute, which never appears on keyboard focus, is unstyled, and cannot show a shortcut legibly. They now use the app's Tooltip (the same primitive the review header and DecisionControl use — no new dependency): two lines, the control's description over its shortcut as keycaps rendered through the registry's platform-aware formatter, so a Mac shows glyphs and everyone else Ctrl rather than a hardcoded Cmd. The description strings are the same host-overridable labels the titles carried, so a @plannotator/ui consumer's overrides keep working; the new shortcuts prop overrides the bindings per control and defaults to the html-annotate scope, so a tooltip cannot claim a chord the app does not dispatch. Because title no longer supplies the accessible name, the pen states an aria-label (defaulting to its description), the eye keeps its sr-only text and Refresh its aria-label, and the shortcut is attached as a persistent aria-describedby span — this Base UI build puts no ARIA on the popup at all, so the tooltip alone would never be announced. Refresh has no chord and renders no keycap row.
M
Michael Ramos committed
85f66fdebbc6d42eb9d50ac6f4e11bb4a6385d0f
Parent: 01eda97
Committed by GitHub <noreply@github.com>
on 9/15/2026, 4:28:03 AM