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.
This commit is contained in:
amosblomqvist
2026-09-18 08:53:31 +03:00
parent 76ea6f5a53
commit 61bd43282d
5 changed files with 117 additions and 19 deletions
+4
View File
@@ -33,6 +33,8 @@ export type KeyAction =
| "rename-session"
| "cheat-sheet"
| "find"
| "model-menu-walk"
| "model-menu-pick"
| "question-focus"
| "question-next-row"
| "question-prev-row"
@@ -76,6 +78,8 @@ export const KEYBOARD_ACTIONS: readonly KeyActionDef[] = [
// the keys while a question is expanded; the registry never dispatches
// them, so bare arrows stay free everywhere else.
{ id: "question-focus", label: "Question · focus thinking ↔ rows", scope: "chat", binding: "tab", displayOnly: true },
{ id: "model-menu-walk", label: "Model menu · walk results", scope: "global", binding: "arrowup", displayOnly: true },
{ id: "model-menu-pick", label: "Model menu · pick result", scope: "global", binding: "enter", displayOnly: true },
{ id: "question-next-row", label: "Question · next row", scope: "chat", binding: "arrowdown", displayOnly: true },
{ id: "question-prev-row", label: "Question · previous row", scope: "chat", binding: "arrowup", displayOnly: true },
{ id: "question-pick-row", label: "Question · pick row", scope: "chat", binding: "space", displayOnly: true },