2372b6b03b
Add a `rawMode?: 'always' | 'auto'` mount option (replacing the dead, unwired
`rawMode?: boolean`), defaulting to 'always'.
- 'always' (default): the App takes a lifetime raw-mode hold at mount (gated on
interactive + a TTY stdin), so raw mode is held for the whole run regardless of
which input composables are mounted. Keystrokes never echo into the rendered
frame on a no-input/streaming screen, and Ctrl+C is handled consistently on
every screen (e.g. it reaches an agent's "interrupt generation" handler instead
of becoming a kernel SIGINT). Because owning raw mode ref()s stdin, the app
stays alive until an explicit unmount()/exit() — it does NOT auto-exit when idle.
- 'auto': Ink's original lazy model — raw mode is acquired only while a useInput /
useFocus / usePaste is mounted, so a no-input screen returns to cooked mode and a
no-input app auto-exits. The opt-out for inline / render-and-exit tools.
This is a deliberate divergence from Ink (the cross-framework norm — Bubble Tea,
Textual, Ratatui, prompt_toolkit all own the terminal for the program lifetime;
Ink's hook-driven model is the outlier). Documented in
.agents/docs/ink-divergences.md.
Implementation: the App holds a `lifetimeFloor` ref via holdRawModeForLifetime();
input composables stack above it. The per-consumer clearInputState is re-based to
the floor so a buffered partial escape (e.g. a lone ESC at a screen transition)
can't bleed into the next consumer — cleared both when the last consumer releases
and when the first consumer re-acquires above the floor (covers same-tick swaps
AND a delayed idle→input transition). The data listener and raw toggle stay on
until teardown, where dispose() releases the floor ref (raw disabled + stdin
unref'd exactly once).
Tests: rawMode-lifecycle ('always' holds raw with no input; 'auto' stays cooked;
no mid-session oscillation; no partial-escape bleed across a swap or an idle gap);
PTY exit-rawmode-always (a no-input 'always' app stays alive and exits on Ctrl+C).
The 6 auto-exit PTY fixtures are pinned to 'auto' (they model render-and-exit).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
24 lines
754 B
TypeScript
24 lines
754 B
TypeScript
import process from "node:process";
|
|
import { Box, Static, Text, createApp } from "@vue-tui/runtime";
|
|
import { Fragment, defineComponent, h } from "vue";
|
|
|
|
const EraseWithStatic = defineComponent(() => {
|
|
return () =>
|
|
h(Fragment, [
|
|
h(
|
|
Static,
|
|
{ items: ["A", "B", "C"] },
|
|
{ default: ({ item }: { item: string }) => h(Text, { key: item }, () => item) },
|
|
),
|
|
h(Box, { flexDirection: "column" }, () => [
|
|
h(Text, null, () => "D"),
|
|
h(Text, null, () => "E"),
|
|
h(Text, null, () => "F"),
|
|
]),
|
|
]);
|
|
});
|
|
|
|
process.stdout.rows = Number(process.argv[2]);
|
|
const app = createApp(EraseWithStatic);
|
|
app.mount({ rawMode: "auto" }); // relies on auto-exit (default "always" holds raw & never exits)
|