Commit Graph
5 Commits
Author SHA1 Message Date
amosblomqvist c32915b232 Thin working settings UI (owner-approved plan): /settings + live appearance + keyboard rebinding
The validation surface for the settings foundation — deliberately thin,
real read/write against the delta record, polish is a later wave.

Mount (owner decisions): dedicated page, single scroll; entry = sidebar
bottom row + ⌘/Ctrl+, (open-settings in the registry); chat defaults NOT
shown (they live on /chat/new); mode dark-only (light palette = design
wave); final tool list = research/demo/visual/pdf (master-adjudicated).

Sections (PUT /api/settings/[section], replace-whole per section):
- General: auto-update toggle.
- Agent: six background-role model+thinking selects (Default = config
  pin; a stored id stays VISIBLE even when the catalog hasn't loaded or
  dropped it — a select showing Default over a live override would lie),
  the four tool toggles, prompt override switches (file-gated) with
  source/override text editors over the segment routes.
- Appearance: applied LIVE as runtime CSS variables by an
  AppearanceProvider at the root layout — proseFontSize → the one
  reading-size var, fontFamily → --font-prose, background → --background
  + the derived raised/accent/edge palette (dark curve, today's exact
  spacing: +8/+22/+47 per channel, clamped), sessionNameSerif →
  --font-title (titles flip with ZERO chat.tsx edits), headingWeight →
  new --lh-heading-weight token, bold/italic colors → the lh-ink vars.
  Mode renders honestly DISABLED.
- Graph: the ladder grays a rung when any ANCESTOR is effectively off
  (stored values persist, resolve inert — switch shows stored, graying
  shows effective).
- Keyboard: view/rebind/reset over the registry; overrides persist in
  lh:keymap and apply INSTANTLY (dispatcher reads per keydown); bare
  modifier presses are ignored while armed; displayOnly actions list but
  never rebind.

First-boot fix: {settings: null} is no-record, not fetch-failure.

CROSS-LANE (flagged for master): src/core/keyboard.ts gained the
open-settings action/binding and exports saveOverrides/loadOverrides
(one-liners; no existing pins); app/layout.tsx mounts the provider.

Live-verified: structure, record writes (general/tools/graph/appearance),
prose-size applying live (16→20px), rebind + chord navigation from
another page, ladder graying, no console errors. Gates: typecheck,
lint, 689/689 (28 new), build.
2026-09-19 18:27:37 +03:00
amosblomqvist 61bd43282d feat: model menu keyboard machine + graph center-anchor on resize
Owner polish pair before the settings milestone:

- Model menu: arrows walk the filtered results (clamped, no wrap) while
  focus stays in the search field; Enter picks the walked row or, with no
  walk yet, the first result; typing resets the walk; click and Enter
  share one pickModel path; walked row gets a muted ring + scrollIntoView;
  cheat-sheet display entries added.

- Graph: window resizes re-anchor the camera so the world point under the
  pre-resize viewport center stays under the new center — the graph stays
  visually still while scaling (was top-left pinned). viewW/H tracked in
  syncSize; re-anchor applied through the zoom behavior (persistence and
  card-dismiss unchanged); kFit refresh as before. Pixel-verified: 7/7
  center samples identical across a 300x200 shrink.
2026-09-18 08:53:31 +03:00
amosblomqvist ab5d5da9fb fix: keyboard round 3 — question walk clamps, Tab focus toggle, Alt+arrow isolation, search Esc
Owner live-test rounds on waves A/B:
- Alt+arrows no longer ALSO walk the question rows (dock ignores
  alt/meta/ctrl combos; session cycling keeps them exclusively)
- question walk clamps at the ends — no wrap (faster to navigate)
- plain Tab toggles focus between the thinking field and the answer
  rows (walked row by default); focus follows the walk while in the
  rows; Shift+Tab stays the global thinking-level cycle
- Esc in the sessions search blurs back to normal (claims the key so
  the window Esc chain does not fire)
- cheat sheet gains the Tab-in-question display entry
2026-09-18 08:34:46 +03:00
amosblomqvist 8b158972db feat: keyboard waves A/B — global shortcut registry, question row machine, Esc-cancel dictation
Wave A (owner key map): registry in src/core/keyboard.ts + KeyboardShortcuts
provider; Cmd+D dictation, Cmd+K model picker, Shift+Tab thinking cycle,
Cmd+B sidebar, Cmd+Shift+F sessions search, Alt+arrows session cycle,
Alt+Enter open, Cmd+G graph, Cmd+N new, F2 rename, ? cheat sheet. Tab
fully suppressed; Esc chain popover > dictation > abort (local stream).
Esc now CANCELS dictation (discard) — flush stays on Cmd+D/Stop. Owner
bug reports fixed: focus outline removed, Alt+Enter setState-in-render,
Cmd+F graph node search (surface-scoped lh:kbd-find).

Wave B: question dock row machine — arrows walk options + dont-know/Other
with wrap + ring highlight, Space picks (mirrors click semantics), Enter
submits when canSubmit; typing surfaces and in-dock keys keep native
behavior. aria-checked={isSel} restored on option buttons.

docs/keyboard.md records the full map.
2026-09-17 21:18:56 +03:00
amosblomqvistandClaude Fable 4.5 plus-plus 682edde844 feat: keyboard shortcuts wave A — registry, provider, global bindings
The shortcut architecture (owner-approved plan, 2026-09-17):
- src/keyboard.ts: pure action registry (id, label, scope, default
  binding) + lh:keymap localStorage overrides + event normalization.
  The settings milestone renders this same registry as its editor.
- components/keyboard-shortcuts.tsx: the single document-level keydown
  listener (window level, bubble) owning the pieces no surface owns:
  - Tab is DEAD (owner decision): browser tab-focus navigation is
    abandoned; Tab is suppressed everywhere, Shift+Tab cycles the
    thinking level.
  - Esc is a layered dismiss: topmost popover first (popovers
    stopPropagation their own Esc — usePopoverDismiss + ModelControl),
    then dictation stop (lh:kbd-stop-dictation), then abort of an
    in-flight turn (lh:kbd-abort-turn; server-side wake turns are
    honestly non-stoppable — the seam only stops local streams).
  - Global actions dispatch lh:kbd-<action> window events (the
    lh:chat-renamed pattern); surfaces that aren't mounted simply
    don't listen.
- The input guard: inside a typing surface only mod/alt combos (and
  Tab) fire; bare keys pass through to the field.
- Cheat sheet overlay (?) renders from the registry, grouped by scope.

Bindings wired this wave: cmd+d dictation toggle, cmd+k model picker,
shift+tab thinking cycle, cmd+b sidebar, cmd+shift+f sessions search,
alt+up/down + alt+enter session cycling, cmd+g graph view, cmd+n new
session, f2 rename (chat), ? cheat sheet. Wave B adds the question
keyboard machine; wave C the per-surface bindings (find, import).

Verified live in the dev shell (CDP): all of the above via
Input.dispatchKeyEvent.

Co-Authored-By: Claude Fable 4.5 plus-plus <noreply@anthropic.com>
2026-09-17 20:47:23 +03:00