Add parsing for Home (ESC[H, ESC[1~, ESC[7~), End (ESC[F, ESC[4~, ESC[8~), Ctrl+arrow (CSI 1;5 A/B/C/D), and Meta+arrow (double-ESC prefix) key sequences. Add home/end fields to the Key interface. Implement 12 new useInput tests for these key combinations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vue-tui/runtime
Vue-idiomatic terminal renderer in the spirit of React Ink. Platform-specific runtime parallel to @vue/runtime-dom.
Install
pnpm add @vue-tui/runtime vue
Quickstart
import { defineComponent, h, ref } from "vue";
import { createApp, Box, Text, useInput } from "@vue-tui/runtime";
const Counter = defineComponent({
setup() {
const count = ref(0);
useInput((input) => {
if (input === "+") count.value++;
if (input === "-") count.value--;
});
return () => h(Box, null, h(Text, null, `Count: ${count.value}`));
},
});
createApp(Counter).mount();
API
Single entry point — createApp(root, rootProps?). Returns a TuiApp (Omit<VueApp<TuiNode>, "mount"> plus four TUI methods).
import { createApp } from "@vue-tui/runtime";
const app = createApp(App, { initialState }).use(myPlugin).provide(themeKey, dark);
app.mount(); // all defaults (process.*)
app.mount({ debug: true }); // just flags
app.mount({ stdout: customWritable }); // partial stream override
app.mount({ stdout, stdin, stderr }); // explicit streams (testing)
await app.waitUntilExit();
app.unmount(); // optional — see "Cleanup" below
Cleanup
mount() registers a process.on("exit") listener that runs the same teardown
as unmount(). So you only need app.unmount() if you want to tear down the
app before the process is ready to exit (mid-program, in tests, or when one
process hosts multiple UIs sequentially). For a normal CLI that exits when the
user quits or the script ends, cleanup happens automatically.
TuiApp interface
interface TuiApp extends VueApp<TuiNode> {
mount(options?: MountOptions): ComponentPublicInstance;
unmount(): void;
waitUntilExit(): Promise<void>;
}
interface MountOptions {
stdout?: NodeJS.WriteStream; // default: process.stdout
stdin?: NodeJS.ReadStream; // default: process.stdin
stderr?: NodeJS.WriteStream; // default: process.stderr
debug?: boolean; // default: false
exitOnCtrlC?: boolean; // default: true
}
All fields optional with per-field fallback. Matches Ink's RenderOptions shape.
Components
Box, Text, Newline, Spacer, Static, Transform.
Composables
useExit, useInput, useFocus, useFocusManager, useStdin, useStdout, useStderr, useTerminalSize.
Tab / Shift+Tab / Escape are handled automatically when any component uses useFocus. useFocus returns { isFocused, focus } and manages raw mode. useFocusManager exposes activeId in addition to focusNext / focusPrevious / focus / enableFocus / disableFocus.
Waiting on exit / handling errors
// Fire-and-forget (most common):
createApp(App).mount();
// Wait for the app to exit:
const app = createApp(App);
app.mount();
await app.waitUntilExit();
// Catch errors thrown from setup / render / useExit(err):
const app = createApp(App);
app.mount();
app.waitUntilExit().catch((err) => {
console.error(err);
process.exitCode = 1;
});
See docs/superpowers/specs/2026-05-18-vue-tui-core-design.md for the full design.