You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit a4149eb
Browse filesBrowse the repository at this point in the historyBrowse files
Decision record for a global keymap on the imperative `@opentui/core` 0.6.4 surface. This record changes no code.
3
+
Decision record for application key bindings on `@opentui/core` 0.6.6. The engine is `@opentui/keymap`.
4
4
5
5
## Recommendation
6
6
7
-
**Decline**a global keymap. Do not adopt one later under another name, and file no follow-up. The library does not ship an application keymap, and the shell behaviors that would have to move into one are not widget bindings.
7
+
**Adopt**`@opentui/keymap` for application commands. Do not move widget-local editing, the scroll lease, or the kill ring into it.
8
8
9
-
## What 0.6.4 actually provides
9
+
`createDefaultOpenTuiKeymap(renderer)` installs the default parser, the event matcher, and the enabled and metadata fields. The shell registers one layer: `commands` (`name`, `run`) and `bindings` (`key`, `cmd`). Each `run` calls the existing handler in `src/tui/shell/keys.ts`. The help overlay reads those rows from the same catalog as the registration (`keymapShortcuts()` plus `promptLocalShortcuts()`).
10
10
11
-
Bindings are per widget. `TextareaRenderable` takes `keyBindings` of `TextareaAction` (caret, selection, kill-to-line, undo, submit, and the rest of the editor action set). `SelectRenderable` takes `keyBindings` of `SelectAction`: `move-up`, `move-down`, `move-up-fast`, `move-down-fast`, `select-current`. The same shape exists on `TabSelect` and the terminal console; `Input` reuses the textarea actions. Each widget merges its defaults with the caller's list and looks up one event.
11
+
## What stays a command body
12
12
13
-
`mergeKeyBindings(defaults, custom)` in `lib/keybinding.internal.d.ts` is that merge: two `KeyBinding<Action>[]` lists become one list for one widget. It does not route across the shell, it does not know which surface holds focus, and it does not encode policy that spans widgets. There is no keymap engine package on this core. The repo depends on `@opentui/core` only.
13
+
Widget `keyBindings` cannot express policy that spans the shell:
14
14
15
-
## What stays
15
+
-**Scroll lease.** One surface holds the lease. The wheel must not move the transcript while a modal is open. Arrow keys never scroll the transcript.
16
+
-**Kill ring.** Ctrl+K/U/W and Alt+D kill, Ctrl+Y yanks, Alt+Y yank-pops. Those stay Textarea-local in `prompt-kill-ring.ts`. The keymap does not bind them.
17
+
-**Ctrl+C.** Pause, stop sub-agents, and quit stay `handleCtrlC`. The keymap command calls that handler; it does not reimplement the arming window. A decision gate still declines before the arming handler runs.
16
18
17
-
-`src/tui/shell/keys.ts` is the shell dispatcher: paste guard, overlay keys, prompt wheel forwarding, kill/yank orchestration, and Ctrl+C arming. It is not a binding table.
18
-
-`src/tui/keybindings.ts` is the shortcut catalog the help overlay reads. `keybindings.test.ts` drives each row through a live shell. The catalog describes chords; it does not implement them.
19
-
20
-
Neither file is a stand-in for `mergeKeyBindings`. Replacing them with widget tables would drop behavior the tables cannot express.
21
-
22
-
## Deleted if a global keymap were adopted
23
-
24
-
It is not adopted. If it were, the deletion this record refuses is:
25
-
26
-
-`src/tui/shell/keys.ts` would be deleted or reduced to widget binding tables.
27
-
-`src/tui/keybindings.ts` would be deleted in favor of `mergeKeyBindings` defaults.
28
-
29
-
Those files stay. Reducing them would drop three policies that are not widget bindings:
30
-
31
-
-**Scroll lease.** One surface holds the lease. The wheel must not move the transcript while a modal is open on top of it. Prompt wheel events are forwarded to the transcript only because the prompt itself does not scroll. Arrow keys never scroll anything: in the prompt they are caret motion or history recall, and in an overlay list they move the selection. Page keys move only the surface that holds the lease.
32
-
-**Kill ring.** The prompt kill ring (kill chords and yank / yank-pop) is shell state. Textarea delete actions discard or edit a buffer; they are not that ring.
33
-
-**Ctrl+C.** The first press pauses a busy run or clears an idle prompt. A later press inside the arming window quits, and live sub-agents insert a stop press before quit. That is session policy, not a textarea `submit` or select `select-current` binding.
19
+
Paste, overlay accept, and prompt newline chords stay where they were. Enter, Up, and Down stay in `keys.ts` because the same chord sends, steers, recalls history, or moves the caret. Ctrl+X is the held-item drop and is a keymap command.
34
20
35
21
## Checks
36
22
37
-
-[x]Decline a global keymap. `mergeKeyBindings` and per-widget `keyBindings` are the only real key APIs, and they are not an application map.
38
-
-[ ] Adopt a global keymap and delete `shell/keys.ts` and `keybindings.ts`. Cannot check. No keymap engine exists on this core, and the adopt path would drop the scroll lease, the kill ring, and Ctrl+C policy. Leave this unchecked.
39
-
-[x] File no follow-up. This record does not adopt a path that needs one.
23
+
-[x]Use `@opentui/keymap` for application commands. Help rows for those chords come from the keymap catalog.
24
+
-[x] Leave the scroll lease, the kill ring, and Ctrl+C policy as command bodies. Widget bindings cannot express them.
25
+
-[ ] Delete `shell/keys.ts` and `keybindings.ts`. Cannot check. The dispatcher and the prompt-local catalog stay.
"queue a follow-up delivered only when the run goes idle; does nothing unless a run is busy",
28
+
command: "shell.queue",
29
+
bindings: ["alt+return"],
24
30
},
25
31
{
26
32
keys: "Up / Down / Enter / Ctrl+X",
27
33
description:
28
34
"on held items above the prompt: select, send now, drop (Esc backs out)",
35
+
// Up, Down, and Enter also move the caret, recall history, or send.
36
+
// Only Ctrl+X is unambiguous; the others stay in keys.ts.
37
+
command: "shell.held.drop",
38
+
bindings: ["ctrl+x"],
29
39
},
30
40
{
31
41
keys: "Ctrl+C",
32
42
description:
33
43
"pause a busy run and hold the queue, or clear the prompt and attachments when idle; Ctrl+C again to exit — with live sub-agents, a 2nd press stops them, a 3rd quits",
44
+
command: "shell.interrupt",
45
+
bindings: ["ctrl+c"],
34
46
},
35
47
{
36
48
keys: "Ctrl+G",
37
49
description:
38
50
"pop the most recently queued or steered message back into the prompt for editing",
51
+
command: "shell.cancel",
52
+
bindings: ["ctrl+g"],
39
53
},
40
54
{
41
55
keys: "Alt+C",
42
56
description:
43
57
"copy mode: pick a message, tool output, or diff; press again to close it",
58
+
command: "shell.copy",
59
+
bindings: ["alt+c"],
44
60
},
45
61
{
46
62
keys: "Alt+M",
47
63
description:
48
64
"toggle DEC mouse capture (on by default: wheel scroll, click-to-expand, drag-to-copy); off restores native terminal drag-select",
65
+
command: "shell.mouse",
66
+
bindings: ["alt+m"],
49
67
},
50
68
{
51
69
keys: "Alt+E",
52
70
description:
53
71
"expand or collapse every collapsible row (tool call, diff, skill, reasoning)",
72
+
command: "shell.expand",
73
+
bindings: ["alt+e"],
54
74
},
55
75
{
56
76
keys: "Alt+T",
57
77
description:
58
78
"show or hide the task list above the prompt (hidden by default)",
79
+
command: "shell.tasks",
80
+
bindings: ["alt+t"],
59
81
},
60
82
{
61
83
keys: "Alt+O",
62
84
description:
63
85
"observe a live subagent session; a system row says so when there is none",
86
+
command: "shell.observe",
87
+
bindings: ["alt+o"],
64
88
},
65
89
{
66
90
keys: "Tab",
67
91
description: "move focus between the prompt and the transcript",
92
+
command: "shell.focus",
93
+
bindings: ["tab"],
68
94
},
69
95
{
70
96
keys: "Shift+Tab",
71
97
description: "cycle reasoning effort for the current model",
98
+
command: "shell.effort",
99
+
bindings: ["shift+tab"],
72
100
},
73
101
{
74
102
keys: "Esc",
75
103
description: "close the open overlay, or leave subagent observe",
0 commit comments