feat: wire kitty keyboard controller into mount/teardown and add exports

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Yunfei He
2026-05-27 14:50:07 +08:00
parent 387791c142
commit 838aaaf806
3 changed files with 27 additions and 0 deletions
+1
View File
@@ -32,3 +32,4 @@ export {
} from "./composables/useBoxMetrics.ts";
export { renderScreenReaderOutput, type ScreenReaderOptions } from "./paint/screen-reader.ts";
export type { DevState, DevErrorInfo } from "./hmr.ts";
export { kittyFlags, type KittyKeyboardOptions, type KittyFlagName } from "./io/kitty-keyboard.ts";
+8
View File
@@ -11,3 +11,11 @@ export {
} from "./host/nodes.ts";
export { renderScreenReaderOutput, type ScreenReaderOptions } from "./paint/screen-reader.ts";
export type { AppContext } from "./context.ts";
export {
createKittyKeyboardController,
matchKittyQueryResponse,
hasCompleteKittyQueryResponse,
stripKittyQueryResponsesAndTrailingPartial,
resolveFlags,
type KittyKeyboardController,
} from "./io/kitty-keyboard.ts";
+18
View File
@@ -15,6 +15,7 @@ import isInCi from "is-in-ci";
import patchConsoleFn from "patch-console";
import ansiEscapes from "ansi-escapes";
import { createInputParser, type InputEvent } from "./io/input-parser.ts";
import { createKittyKeyboardController, type KittyKeyboardOptions } from "./io/kitty-keyboard.ts";
import { createRoot, type TuiRoot, type TuiNode } from "./host/nodes.ts";
import { attachYoga, detachYoga } from "./host/yoga.ts";
import { buildNodeOps } from "./host/node-ops.ts";
@@ -101,6 +102,14 @@ export interface MountOptions {
* @default false
*/
alternateScreen?: boolean;
/**
* Configure kitty keyboard protocol support for enhanced keyboard input.
* Enables additional modifiers (super, hyper, capsLock, numLock) and
* disambiguated key events in terminals that support the protocol.
*
* @see https://sw.kovidgoyal.net/kitty/keyboard-protocol/
*/
kittyKeyboard?: KittyKeyboardOptions;
}
export interface TuiApp extends Omit<VueApp<TuiNode>, "mount"> {
@@ -162,6 +171,7 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp
let mountedScheduler: ReturnType<typeof createCommitScheduler> | null = null;
let mountedCommit: (() => void) | null = null;
let mountedAlternateScreen = false;
let mountedKittyController: ReturnType<typeof createKittyKeyboardController> | null = null;
// 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
@@ -231,6 +241,10 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp
} catch {
// Vue's unmount may throw on double-unmount; swallow for idempotency.
}
if (mountedKittyController) {
mountedKittyController.dispose();
mountedKittyController = null;
}
if (!mountedDebug && !mountedInteractive && mountedAppContext) {
// Non-interactive: write the deferred last frame at unmount (matching Ink).
const lastFrame = mountedGetLastOutput?.() ?? "";
@@ -425,6 +439,10 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp
});
mountedStdinController = stdinController;
const kittyController = createKittyKeyboardController(stdin, stdout);
kittyController.init(options.kittyKeyboard, interactive);
mountedKittyController = kittyController;
const tuiRoot = createRoot(appContext);
attachYoga(tuiRoot);
tuiRoot.yoga.setWidth(stdout.columns ?? 80);