feat(runtime): enable raw mode by default on mount
TUI apps should suppress terminal echo to prevent keystroke pollution in the rendered UI. Raw mode is now on by default (matching Bubbletea, Textual, Ratatui), with exitOnCtrlC also defaulting to true. Pass rawMode: false to mount() for pure-output CLI tools that don't need input suppression. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -151,6 +151,12 @@ vp run -r build # build all packages
|
||||
vue-tui dev # start an example with HMR
|
||||
```
|
||||
|
||||
## Caveats
|
||||
|
||||
- vue-tui enables [raw mode](https://en.wikipedia.org/wiki/Terminal_mode) by default so the terminal won't echo keystrokes into your UI.
|
||||
- Ctrl+C still exits — `exitOnCtrlC` defaults to `true`.
|
||||
- For pure-output tools that don't need input suppression, pass `rawMode: false` to `mount()`.
|
||||
|
||||
## Credits
|
||||
|
||||
vue-tui started as a Vue port of [Ink](https://github.com/vadimdemedes/ink), the library that proved terminal UIs could be built with the same component patterns we use on the web. The component model, yoga-based layout, focus system, rendering pipeline — all of it originates in Ink's design, adapted to follow Vue's philosophy and conventions. Thank you to [Vadim Demedes](https://github.com/vadimdemedes), [Sindre Sorhus](https://github.com/sindresorhus), and the [Ink contributors](https://github.com/vadimdemedes/ink/graphs/contributors) for creating such a solid foundation.
|
||||
|
||||
@@ -26,6 +26,7 @@ export interface MountOptions {
|
||||
stderr?: NodeJS.WriteStream;
|
||||
debug?: boolean;
|
||||
exitOnCtrlC?: boolean;
|
||||
rawMode?: boolean;
|
||||
}
|
||||
|
||||
export interface TuiApp extends Omit<VueApp<TuiNode>, "mount"> {
|
||||
@@ -55,6 +56,7 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp
|
||||
let mountedExitListener: (() => void) | null = null;
|
||||
let mountedFocusListener: (() => void) | null = null;
|
||||
let mountedDebug = false;
|
||||
let mountedRawMode = false;
|
||||
|
||||
// The renderer's onCommit closure is wired at createApp time but only does
|
||||
// real work after mount swaps in scheduler.schedule. One renderer per app
|
||||
@@ -85,6 +87,10 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp
|
||||
if (mountedFocusListener) {
|
||||
mountedFocusListener();
|
||||
}
|
||||
if (mountedRawMode && mountedAppContext) {
|
||||
mountedAppContext.setRawMode(false);
|
||||
mountedRawMode = false;
|
||||
}
|
||||
if (mountedStdinController) {
|
||||
mountedStdinController.dispose();
|
||||
}
|
||||
@@ -110,6 +116,7 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp
|
||||
const stderr = options.stderr ?? process.stderr;
|
||||
const debug = options.debug ?? false;
|
||||
const exitOnCtrlC = options.exitOnCtrlC ?? true;
|
||||
const rawMode = options.rawMode ?? true;
|
||||
mountedDebug = debug;
|
||||
|
||||
const appContext: AppContext = {
|
||||
@@ -189,6 +196,11 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp
|
||||
appContext.exit(err instanceof Error ? err : new Error(String(err)));
|
||||
};
|
||||
|
||||
if (rawMode && appContext.isRawModeSupported) {
|
||||
appContext.setRawMode(true);
|
||||
mountedRawMode = true;
|
||||
}
|
||||
|
||||
// Built-in Tab / Shift+Tab / Escape focus navigation (matches Ink).
|
||||
// Placed AFTER mount so a sync mount failure doesn't leak the listener.
|
||||
const focusInputListener = (chunk: Buffer | string) => {
|
||||
|
||||
Reference in New Issue
Block a user