diff --git a/docs/TUI-inline-eval.md b/docs/TUI-inline-eval.md new file mode 100644 index 000000000..59a30d764 --- /dev/null +++ b/docs/TUI-inline-eval.md @@ -0,0 +1,55 @@ +# Inline mode evaluation + +Decision record for an inline-versus-fullscreen split on `@opentui/core` 0.6.4. This record changes no code. The shell stays a fullscreen layout with zone floors, one scroll lease, and a prompt that owns its own editing state. + +## Recommendation + +**Decline** inline mode. Do not adopt a scrollback-only shell, do not treat program status as a layout mode, and file no follow-up. + +## What exists, and why it is not inline mode + +`CliRenderer.writeToScrollback(write)` (`renderer.d.ts`) takes a `ScrollbackWriter` and commits a snapshot into terminal scrollback. The implementation throws unless `screenMode` is `"split-footer"` and `externalOutputMode` is `"capture-stdout"`. The writer must return a snapshot root. That is a split-footer capture path. It is not an inline-mode renderable, not an alternate scene, and not a fullscreen toggle. + +`createScrollbackSurface` sits in the same family: a surface that commits rows into that scrollback. It does not replace the shell tree. + +`ScreenMode` is `"alternate-screen" | "main-screen" | "split-footer"`. None of those values is an inline mode. The shell does not switch among them to mean "inline". + +Program status is OSC 7501. `setProgramStatus` reports a `ProgramStatus` record (`types.d.ts`, "OSC 7501 Program Status Protocol"). Terminals that ignore the protocol drop the report. OSC 7511 does not appear anywhere in the 0.6.4 package. Do not invent it, and do not treat 7501 as a layout escape: it publishes status, it does not paint the transcript or the prompt. + +`enableLayout: false` is not an inline escape. Creating a native scene node with that option throws `Native scene requires Yoga layout`. Do not propose it. + +Nothing in the 0.6.4 types is an inline-mode renderable, an alternate inline scene, or a fullscreen-versus-inline switch. + +## What inline mode would not give you + +`writeToScrollback` writes a snapshot. It does not provide: + +- zone floors (idle transcript floor, overlay transcript floor, prompt floor, collapse order when a floor would be breached) +- one scroll lease (wheel stays on the surface that holds it; arrows never scroll) +- overlay selectors (empty-state fail-close, painted-id accept, shared list viewport) +- the prompt kill ring + +A raw scrollback log also has no place for the shell-output preview the host paints on a timer. Sticky polling exists because the preview is a zone in the fullscreen tree, not a line already committed to scrollback. + +## What stays + +- `src/tui/shell/layout.ts` applies terminal geometry: floors, prompt-row sync, landing split, relayout inputs. +- `src/tui/geometry/zones.ts` defines the floors and the collapse order the layout refuses to violate. +- `src/tui/shell/chrome.ts` `relayout` is the live entry that re-resolves the tree when visibility, prompt rows, or overlay mode change. +- The `stickyPoll` interval in `src/tui/product-host.ts` keeps the shell-output preview and sticky chrome clocks painted. It is not scrollback. + +## Deleted if inline mode were adopted + +It is not adopted. If it were, the deletion this record refuses is: + +- `shell/layout.ts`, the `geometry/zones.ts` floors, and `chrome.ts` relayout would be deleted or bypassed for a scrollback-only writer. +- `stickyPoll` would go, because a raw scrollback commit has no sticky zone to repaint. + +Those files stay. Bypassing them for `writeToScrollback` would drop the floors, the single scroll lease, overlay selectors, and the prompt kill ring. None of that is implied by a scrollback snapshot. + +## Checks + +- [x] Decline inline mode. `writeToScrollback` is real and insufficient. Program status is OSC 7501. OSC 7511 is absent. +- [ ] Ship an inline renderable and delete the fullscreen layout. Cannot check. No such type exists in 0.6.4, and `enableLayout: false` throws rather than opting out of layout. +- [ ] Implement OSC 7511 as an inline channel. Cannot check. The sequence is not in the 0.6.4 types. +- [x] File no follow-up. This record does not adopt a path that needs one. diff --git a/docs/TUI-keymap-eval.md b/docs/TUI-keymap-eval.md new file mode 100644 index 000000000..633590936 --- /dev/null +++ b/docs/TUI-keymap-eval.md @@ -0,0 +1,39 @@ +# Keymap evaluation + +Decision record for a global keymap on the imperative `@opentui/core` 0.6.4 surface. This record changes no code. + +## Recommendation + +**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. + +## What 0.6.4 actually provides + +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. + +`mergeKeyBindings(defaults, custom)` in `lib/keybinding.internal.d.ts` is that merge: two `KeyBinding[]` 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. + +## What stays + +- `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. +- `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. + +Neither file is a stand-in for `mergeKeyBindings`. Replacing them with widget tables would drop behavior the tables cannot express. + +## Deleted if a global keymap were adopted + +It is not adopted. If it were, the deletion this record refuses is: + +- `src/tui/shell/keys.ts` would be deleted or reduced to widget binding tables. +- `src/tui/keybindings.ts` would be deleted in favor of `mergeKeyBindings` defaults. + +Those files stay. Reducing them would drop three policies that are not widget bindings: + +- **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. +- **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. +- **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. + +## Checks + +- [x] Decline a global keymap. `mergeKeyBindings` and per-widget `keyBindings` are the only real key APIs, and they are not an application map. +- [ ] 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. +- [x] File no follow-up. This record does not adopt a path that needs one.