feat(quick-look): capture a snippet without leaving the home row - #34
Merged
Conversation
The launcher could find, preview and copy by keyboard but not CREATE: startCompose had exactly one caller, the + button's @click, and Tab is hostile there by design — the whole key driver hangs off the search box's @keydown, so reaching the button kills arrow navigation and nothing hands the keyboard back. - Cmd/Ctrl+N opens compose from anywhere, carrying the query in as the name. Not registered in the app menu: on macOS an accelerator fires whichever window has focus and would swallow the launcher's own key, which is why its ⌘C and ⌘↵ already live only in the renderer. - A kind:'create' row, last in the list so the selection stays on the best match. It is the discoverability fix and rides the arrow keys and .ql-res markup that already exist. - The draft stops forcing language:'plaintext', which was OVERRIDING the detection snippetStore.add already runs — costing every launcher snippet its highlighting and its format tag. A native <select> (Auto default) can pin it, and a pinned choice is never re-guessed. - The body is syntax-coloured as you type: a transparent textarea over a <pre> of miniLines() output (useHighlightedInput), so undo, IME and selection stay native and rule 8 holds — tokens, never markup. Three latent defects fixed on the way: - The create row crashed the preview pane. ACTIONS had no entry for it, so previewAction(current).icon read .icon off undefined; it surfaced as the launcher silently vanishing, because installUpdateRecovery treats the failure as a stale build and relaunches. - --font-mono was used once and defined NOWHERE, so the compose textarea was never actually monospaced. Harmless alone, fatal for a two-layer overlay. - .btn had no :focus-visible rule anywhere, so every Tab stop in the app fell back to Chromium's UA ring. The ring is color-mix(--accent 80%, --text): bare --accent scores 2.58 on solar and 2.98 on meridian against --bg-elevated, under the 3:1 floor. The language chip also moves off accent-as-ink (3.52 solar / 3.88 meridian / 4.35 sepia, under the 4.5:1 text floor) to --text with the accent on the keyline, where solar's 3.52 clears the non-text floor. footHints and previewHints move from raw English in source to catalogue key IDs. Three size ratchets moved DOWN (useQuickLook 270/288 → 223, useQuickLookCompose 66 → 62, useQuickLookKeys 105 → 97); none was raised. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Agent review + QA on f0abc7a. The structural finding was the worst and was mine: three `// prettier-ignore` blocks — the first in the repo — were hand-compacting destructures purely to fit the size caps. Deleting them puts QuickLook.vue's script at 111 (cap 100) and useQuickLookCompose at 71 (cap 62), so the ratchet entry recorded a number that no longer measured the function. Compressing lines is the same evasion as raising the cap. Split instead of compressed, as the standards prescribe: - QuickLookPreview.vue takes the preview pane out of QuickLook.vue. - useLauncherFocus.js takes the focus/reclaim rule and its watches — event logic, which belongs in a composable anyway. - useDraftFields() sits beside useDraftLanguage(). useQuickLookCompose now beats the REAL 60-line cap, so its legacy entry is deleted (27 -> 26); useQuickLook retightened 224 -> 215. Three bugs QA proved, all fixed with tests: - An unmodified inline edit rewrote the snippet's stored language both ways: snippetRows exposed only the RESOLVED value (plaintext flattened to ''), and editCurrent saved that back. An explicit 'plaintext' became 'auto'; an 'auto' snippet froze. This touched real data — every launcher-created snippet on main carries explicit 'plaintext'. Rows now carry `language` beside `lang`. - ArrowLeft on the language select discarded the whole draft: useCaretBackOut only deferred to INPUT/TEXTAREA, and ArrowLeft is the native way to change a closed select on Windows and Linux — exactly what the ⇧Tab hint invites. - Cmd/Ctrl+N swallowed AltGr+N, which reports as Ctrl+Alt on European layouts. Two pieces of wrong user-visible copy: the compose chip said ⇧Tab reaches the language when one press lands on the name, and createHint promised Enter would save when it only opens compose. Also: one derivation of the ranked tool list instead of two; hint tables moved to utils/quickLookHints.js and now own their key glyphs; the Tools header carries a message id rather than raw English in the one file that cannot translate; the language select moves from --chip-h (a key-chip token, 20px) to --control-h-sm; the duplicated focus ring becomes a .focus-ring utility in ui.css; open() takes the snippet's own field names; isPlain/textareaEl/overlayEl per the naming rules; the raw U+200B is now an escape. Not fixed, with evidence: a stale compose draft survives a re-summon because refresh() never runs on one at all — onMounted fires but its show listener does not, and the search query survives on a clean main worktree too. Pre-existing; recorded as a finding. Co-Authored-By: Claude Opus 5 <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.
Why
The launcher could already find, preview and copy a snippet by keyboard. It could not create one.
startComposehad exactly one caller — the+button's@click— and Tab is hostile in that window by design: the whole key driver hangs off the search box's@keydown, so the moment focus lands on the button, arrows, Enter and⌘Call die and nothing hands the keyboard back.What
⌘/Ctrl+Nopens compose from anywhere in the launcher, carrying the query in as the name — you searched for a thing, didn't find it, and are now creating it.⌘↵to save already worked. Summon → type →⌘N→ paste →⌘↵.Create snippet "…"row, last in the list so the selection stays on the best match. This is the discoverability fix, and it rides the arrow keys and.ql-resmarkup that already exist — no new focus target.language: 'plaintext', which was overriding the detectionsnippetStore.addalready runs. Every launcher snippet was losing its highlighting and its format tag (so a SQL snippet was never findable by "sql"). A native<select>(Auto by default) can pin the language, and a pinned choice is never re-guessed.<pre>ofminiLines()output (useHighlightedInput), so undo, IME and selection stay native. Tokens, never markup, so rule 8 holds.Three latent defects found and fixed on the way
ACTIONShad nocreateentry, sopreviewAction(current).iconread.iconoffundefined. It surfaced as the launcher silently vanishing —installUpdateRecoverytreats the failure as a stale build and relaunches, so there was no visible error at all.--font-monowas used once and defined nowhere, so the compose textarea was never actually monospaced. Harmless as a lone textarea; fatal for a two-layer overlay, where half a pixel of drift desynchronises every line..btnhad no:focus-visiblerule anywhere, so every Tab stop in the app fell back to Chromium's UA ring.Contrast, measured across all 14 themes
Parsed from
themes.css, not eyeballed:color: var(--accent)— 3.52 solar / 3.88 meridian / 4.35 sepia, under the 4.5:1 text floor. It moves to--textwith the accent on the keyline, where the 3:1 non-text floor applies and solar's 3.52 clears it.--accentfocus ring scores 2.58 solar / 2.98 meridian on--bg-elevated.--focus-ringiscolor-mix(--accent 80%, --text); worst case across 14 themes × 3 grounds is 3.34.contrastandbeaconkeep their hard keylines — the ring is additive.Housekeeping
footHintsandpreviewHintsmoved from raw English in source to catalogue key IDs — a pre-existing standards violation in the exact array this change edits. Three size ratchets moved down (useQuickLook270/288 → 223,useQuickLookCompose66 → 62,useQuickLookKeys105 → 97); none was raised, anduseQuickLook.jsdropped under the real 250-line cap.One declared deviation
⌘Nis not in the application menu orMenuBar.vue, which the standards normally require for a shortcut. On macOS an app-menu accelerator fires whichever window holds focus, so a menu entry would swallow the launcher's own key — the launcher's⌘Cand⌘↵already live only in the renderer for the same reason. Recorded in the plan's Decisions table.Testing
Every one of the six problems got a failing test first. Two subtle guarantees in
useHighlightedInputwere additionally proven by reverting the fix and watching them go red, then restored.npm run check→ exit 0. Coverage: statements 95.16% (floor 93), branches 88.1% (86), functions 95.9% (92), lines 96.15% (95).e2e/quick-look-keyboard.spec.mjs(5 cases, never touches.ql-add),useHighlightedInput.test.js(10), plusresultRows/snippetRows/ hint-table coverage.mainworktree (openMenutiming out against the menubar on darwin). None is a Quick Look spec. Pre-existing, logged as a finding;make e2ein Docker is the canonical run and should be checked before merge.Spec:
specs/2026-08-07-quicklook-keyboard-capture/plan.md🤖 Generated with Claude Code