@opentui/core 0.0.0-20251108-0c7899b1 → 0.0.0-20251112-613689c1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -36,16 +36,11 @@ export type KeyHandlerEventMap = {
36
36
  paste: [PasteEvent];
37
37
  };
38
38
  export declare class KeyHandler extends EventEmitter<KeyHandlerEventMap> {
39
- protected stdin: NodeJS.ReadStream;
40
39
  protected useKittyKeyboard: boolean;
41
- protected pasteMode: boolean;
42
- protected pasteBuffer: string[];
43
40
  private suspended;
44
- private stdinBuffer;
45
- private dataListener;
46
- constructor(stdin?: NodeJS.ReadStream, useKittyKeyboard?: boolean);
47
- private processSequence;
48
- destroy(): void;
41
+ constructor(useKittyKeyboard?: boolean);
42
+ processInput(data: string): boolean;
43
+ processPaste(data: string): void;
49
44
  suspend(): void;
50
45
  resume(): void;
51
46
  }
@@ -55,7 +50,7 @@ export declare class KeyHandler extends EventEmitter<KeyHandlerEventMap> {
55
50
  */
56
51
  export declare class InternalKeyHandler extends KeyHandler {
57
52
  private renderableHandlers;
58
- constructor(stdin?: NodeJS.ReadStream, useKittyKeyboard?: boolean);
53
+ constructor(useKittyKeyboard?: boolean);
59
54
  emit<K extends keyof KeyHandlerEventMap>(event: K, ...args: KeyHandlerEventMap[K]): boolean;
60
55
  private emitWithPriority;
61
56
  onInternal<K extends keyof KeyHandlerEventMap>(event: K, handler: (...args: KeyHandlerEventMap[K]) => void): void;
@@ -1,5 +1,5 @@
1
1
  import { OptimizedBuffer } from "../buffer";
2
- import { RGBA } from "./RGBA";
2
+ import { type ColorInput } from "./RGBA";
3
3
  export type ASCIIFontName = "tiny" | "block" | "shade" | "slick";
4
4
  export declare const fonts: {
5
5
  tiny: {
@@ -288,12 +288,12 @@ export declare function measureText({ text, font }: {
288
288
  };
289
289
  export declare function getCharacterPositions(text: string, font?: keyof typeof fonts): number[];
290
290
  export declare function coordinateToCharacterIndex(x: number, text: string, font?: keyof typeof fonts): number;
291
- export declare function renderFontToFrameBuffer(buffer: OptimizedBuffer, { text, x, y, fg, bg, font, }: {
291
+ export declare function renderFontToFrameBuffer(buffer: OptimizedBuffer, { text, x, y, color, backgroundColor, font, }: {
292
292
  text: string;
293
293
  x?: number;
294
294
  y?: number;
295
- fg?: RGBA | RGBA[];
296
- bg?: RGBA;
295
+ color?: ColorInput | ColorInput[];
296
+ backgroundColor?: ColorInput;
297
297
  font?: keyof typeof fonts;
298
298
  }): {
299
299
  width: number;
package/lib/extmarks.d.ts CHANGED
@@ -67,7 +67,7 @@ export declare class ExtmarksController {
67
67
  private offsetToPosition;
68
68
  private positionToOffset;
69
69
  private updateHighlights;
70
- private offsetToCharOffset;
70
+ private offsetExcludingNewlines;
71
71
  create(options: ExtmarkOptions): number;
72
72
  delete(id: number): boolean;
73
73
  get(id: number): Extmark | null;
@@ -3,6 +3,7 @@ export interface KeyBinding<Action extends string = string> {
3
3
  ctrl?: boolean;
4
4
  shift?: boolean;
5
5
  meta?: boolean;
6
+ super?: boolean;
6
7
  action: Action;
7
8
  }
8
9
  export declare function mergeKeyBindings<Action extends string>(defaults: KeyBinding<Action>[], custom: KeyBinding<Action>[]): KeyBinding<Action>[];
@@ -1,5 +1,5 @@
1
1
  /**
2
- * StdinBuffer wraps a stdin stream and emits complete sequences.
2
+ * StdinBuffer buffers input and emits complete sequences.
3
3
  *
4
4
  * This is necessary because stdin data events can arrive in partial chunks,
5
5
  * especially for escape sequences like mouse events. Without buffering,
@@ -11,6 +11,7 @@
11
11
  * - Event 3: `;20;5m`
12
12
  *
13
13
  * The buffer accumulates these until a complete sequence is detected.
14
+ * Call the `process()` method to feed input data.
14
15
  */
15
16
  import { EventEmitter } from "events";
16
17
  export type StdinBufferOptions = {
@@ -22,19 +23,20 @@ export type StdinBufferOptions = {
22
23
  };
23
24
  export type StdinBufferEventMap = {
24
25
  data: [string];
26
+ paste: [string];
25
27
  };
26
28
  /**
27
- * Wraps a stdin stream and emits complete sequences via the 'data' event.
29
+ * Buffers stdin input and emits complete sequences via the 'data' event.
28
30
  * Handles partial escape sequences that arrive across multiple chunks.
29
31
  */
30
32
  export declare class StdinBuffer extends EventEmitter<StdinBufferEventMap> {
31
33
  private buffer;
32
34
  private timeout;
33
35
  private readonly timeoutMs;
34
- private readonly stdin;
35
- private readonly stdinListener;
36
- constructor(stdin: NodeJS.ReadStream, options?: StdinBufferOptions);
37
- private handleData;
36
+ private pasteMode;
37
+ private pasteBuffer;
38
+ constructor(options?: StdinBufferOptions);
39
+ process(data: string | Buffer): void;
38
40
  flush(): string[];
39
41
  clear(): void;
40
42
  getBuffer(): string;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Terminal capability response detection utilities.
3
+ *
4
+ * Detects various terminal capability response sequences:
5
+ * - DECRPM (DEC Request Mode): ESC[?...;N$y where N is 0,1,2,3,4
6
+ * - CPR (Cursor Position Report): ESC[row;colR (used for width detection)
7
+ * - XTVersion: ESC P >| ... ESC \
8
+ * - Kitty Graphics: ESC _ G ... ESC \
9
+ * - Kitty Keyboard Query: ESC[?Nu where N is 0,1,2,etc
10
+ * - DA1 (Device Attributes): ESC[?...c
11
+ * - Pixel Resolution: ESC[4;height;widtht
12
+ */
13
+ /**
14
+ * Check if a sequence is a terminal capability response.
15
+ * Returns true if the sequence matches any known capability response pattern.
16
+ */
17
+ export declare function isCapabilityResponse(sequence: string): boolean;
18
+ /**
19
+ * Check if a sequence is a pixel resolution response.
20
+ * Format: ESC[4;height;widtht
21
+ */
22
+ export declare function isPixelResolutionResponse(sequence: string): boolean;
23
+ /**
24
+ * Parse pixel resolution from response sequence.
25
+ * Returns { width, height } or null if not a valid resolution response.
26
+ */
27
+ export declare function parsePixelResolution(sequence: string): {
28
+ width: number;
29
+ height: number;
30
+ } | null;
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "main": "index.js",
5
5
  "types": "index.d.ts",
6
6
  "type": "module",
7
- "version": "0.0.0-20251108-0c7899b1",
7
+ "version": "0.0.0-20251112-613689c1",
8
8
  "description": "OpenTUI is a TypeScript library for building terminal user interfaces (TUIs)",
9
9
  "license": "MIT",
10
10
  "repository": {
@@ -35,7 +35,7 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "bun-ffi-structs": "^0.1.0",
38
+ "bun-ffi-structs": "0.1.2",
39
39
  "jimp": "1.6.0",
40
40
  "yoga-layout": "3.2.1"
41
41
  },
@@ -55,11 +55,11 @@
55
55
  "bun-webgpu": "0.1.3",
56
56
  "planck": "^1.4.2",
57
57
  "three": "0.177.0",
58
- "@opentui/core-darwin-x64": "0.0.0-20251108-0c7899b1",
59
- "@opentui/core-darwin-arm64": "0.0.0-20251108-0c7899b1",
60
- "@opentui/core-linux-x64": "0.0.0-20251108-0c7899b1",
61
- "@opentui/core-linux-arm64": "0.0.0-20251108-0c7899b1",
62
- "@opentui/core-win32-x64": "0.0.0-20251108-0c7899b1",
63
- "@opentui/core-win32-arm64": "0.0.0-20251108-0c7899b1"
58
+ "@opentui/core-darwin-x64": "0.0.0-20251112-613689c1",
59
+ "@opentui/core-darwin-arm64": "0.0.0-20251112-613689c1",
60
+ "@opentui/core-linux-x64": "0.0.0-20251112-613689c1",
61
+ "@opentui/core-linux-arm64": "0.0.0-20251112-613689c1",
62
+ "@opentui/core-win32-x64": "0.0.0-20251112-613689c1",
63
+ "@opentui/core-win32-arm64": "0.0.0-20251112-613689c1"
64
64
  }
65
65
  }
@@ -1,37 +1,46 @@
1
+ import { type ASCIIFontName, type fonts } from "../lib/ascii.font";
2
+ import { type ColorInput } from "../lib/RGBA";
3
+ import { Selection, type LocalSelectionBounds } from "../lib/selection";
1
4
  import type { RenderableOptions } from "../Renderable";
2
- import { Selection } from "../lib/selection";
3
- import { type fonts, type ASCIIFontName } from "../lib/ascii.font";
4
- import { RGBA } from "../lib/RGBA";
5
- import { FrameBufferRenderable } from "./FrameBuffer";
6
5
  import type { RenderContext } from "../types";
6
+ import { FrameBufferRenderable } from "./FrameBuffer";
7
7
  export interface ASCIIFontOptions extends Omit<RenderableOptions<ASCIIFontRenderable>, "width" | "height"> {
8
8
  text?: string;
9
9
  font?: ASCIIFontName;
10
- fg?: RGBA | RGBA[];
11
- bg?: RGBA;
12
- selectionBg?: string | RGBA;
13
- selectionFg?: string | RGBA;
10
+ color?: ColorInput | ColorInput[];
11
+ backgroundColor?: ColorInput;
12
+ selectionBg?: ColorInput;
13
+ selectionFg?: ColorInput;
14
14
  selectable?: boolean;
15
15
  }
16
16
  export declare class ASCIIFontRenderable extends FrameBufferRenderable {
17
17
  selectable: boolean;
18
- private _text;
19
- private _font;
20
- private _fg;
21
- private _bg;
22
- private _selectionBg;
23
- private _selectionFg;
24
- private lastLocalSelection;
18
+ protected static readonly _defaultOptions: {
19
+ text: string;
20
+ font: "tiny";
21
+ color: string;
22
+ backgroundColor: string;
23
+ selectionBg: undefined;
24
+ selectionFg: undefined;
25
+ selectable: true;
26
+ };
27
+ protected _text: string;
28
+ protected _font: keyof typeof fonts;
29
+ protected _color: ColorInput | ColorInput[];
30
+ protected _backgroundColor: ColorInput;
31
+ protected _selectionBg: ColorInput | undefined;
32
+ protected _selectionFg: ColorInput | undefined;
33
+ protected lastLocalSelection: LocalSelectionBounds | null;
25
34
  private selectionHelper;
26
35
  constructor(ctx: RenderContext, options: ASCIIFontOptions);
27
36
  get text(): string;
28
37
  set text(value: string);
29
38
  get font(): keyof typeof fonts;
30
39
  set font(value: keyof typeof fonts);
31
- get fg(): RGBA[];
32
- set fg(value: RGBA | RGBA[] | string | string[]);
33
- get bg(): RGBA;
34
- set bg(value: RGBA | string);
40
+ get color(): ColorInput | ColorInput[];
41
+ set color(value: ColorInput | ColorInput[]);
42
+ get backgroundColor(): ColorInput;
43
+ set backgroundColor(value: ColorInput);
35
44
  private updateDimensions;
36
45
  shouldStartSelection(x: number, y: number): boolean;
37
46
  onSelectionChanged(selection: Selection | null): boolean;
@@ -5,7 +5,7 @@ import { RGBA, type ColorInput } from "../lib/RGBA";
5
5
  import { type KeyBinding as BaseKeyBinding } from "../lib/keymapping";
6
6
  import { type StyledText } from "../lib/styled-text";
7
7
  import type { ExtmarksController } from "../lib/extmarks";
8
- export type TextareaAction = "move-left" | "move-right" | "move-up" | "move-down" | "select-left" | "select-right" | "select-up" | "select-down" | "line-home" | "line-end" | "select-line-home" | "select-line-end" | "buffer-home" | "buffer-end" | "delete-line" | "delete-to-line-end" | "backspace" | "delete" | "newline" | "undo" | "redo" | "word-forward" | "word-backward" | "select-word-forward" | "select-word-backward" | "delete-word-forward" | "delete-word-backward" | "submit";
8
+ export type TextareaAction = "move-left" | "move-right" | "move-up" | "move-down" | "select-left" | "select-right" | "select-up" | "select-down" | "line-home" | "line-end" | "select-line-home" | "select-line-end" | "buffer-home" | "buffer-end" | "delete-line" | "delete-to-line-end" | "delete-to-line-start" | "backspace" | "delete" | "newline" | "undo" | "redo" | "word-forward" | "word-backward" | "select-word-forward" | "select-word-backward" | "delete-word-forward" | "delete-word-backward" | "submit";
9
9
  export type KeyBinding = BaseKeyBinding<TextareaAction>;
10
10
  export interface SubmitEvent {
11
11
  }
@@ -65,6 +65,7 @@ export declare class TextareaRenderable extends EditBufferRenderable {
65
65
  gotoBufferHome(): boolean;
66
66
  gotoBufferEnd(): boolean;
67
67
  deleteToLineEnd(): boolean;
68
+ deleteToLineStart(): boolean;
68
69
  undo(): boolean;
69
70
  redo(): boolean;
70
71
  moveWordForward(options?: {
@@ -0,0 +1,7 @@
1
+ import { TextareaRenderable } from "../Textarea";
2
+ import { type TestRenderer } from "../../testing/test-renderer";
3
+ import { type TextareaOptions } from "../Textarea";
4
+ export declare function createTextareaRenderable(renderer: TestRenderer, renderOnce: () => Promise<void>, options: TextareaOptions): Promise<{
5
+ textarea: TextareaRenderable;
6
+ root: any;
7
+ }>;
package/renderer.d.ts CHANGED
@@ -30,6 +30,8 @@ export interface CliRendererConfig {
30
30
  useKittyKeyboard?: boolean;
31
31
  backgroundColor?: ColorInput;
32
32
  openConsoleOnError?: boolean;
33
+ prependInputHandlers?: ((sequence: string) => boolean)[];
34
+ onDestroy?: () => void;
33
35
  }
34
36
  export type PixelResolution = {
35
37
  width: number;
@@ -126,6 +128,7 @@ export declare class CliRenderer extends EventEmitter implements RenderContext {
126
128
  private _console;
127
129
  private _resolution;
128
130
  private _keyHandler;
131
+ private _stdinBuffer;
129
132
  private animationRequest;
130
133
  private resizeTimeoutId;
131
134
  private resizeDebounceDelay;
@@ -157,6 +160,9 @@ export declare class CliRenderer extends EventEmitter implements RenderContext {
157
160
  private _paletteDetector;
158
161
  private _cachedPalette;
159
162
  private _paletteDetectionPromise;
163
+ private _onDestroy?;
164
+ private inputHandlers;
165
+ private prependedInputHandlers;
160
166
  private handleError;
161
167
  private dumpOutputCache;
162
168
  private exitHandler;
@@ -202,6 +208,11 @@ export declare class CliRenderer extends EventEmitter implements RenderContext {
202
208
  set useThread(useThread: boolean);
203
209
  setupTerminal(): Promise<void>;
204
210
  private stdinListener;
211
+ addInputHandler(handler: (sequence: string) => boolean): void;
212
+ prependInputHandler(handler: (sequence: string) => boolean): void;
213
+ removeInputHandler(handler: (sequence: string) => boolean): void;
214
+ private capabilityHandler;
215
+ private focusHandler;
205
216
  private setupInput;
206
217
  private handleMouseData;
207
218
  private takeMemorySnapshot;
@@ -26,7 +26,11 @@ export declare const KeyCodes: {
26
26
  readonly F12: "\u001B[24~";
27
27
  };
28
28
  export type KeyInput = string | keyof typeof KeyCodes;
29
- export declare function createMockKeys(renderer: CliRenderer): {
29
+ export interface MockKeysOptions {
30
+ kittyKeyboard?: boolean;
31
+ otherModifiersMode?: boolean;
32
+ }
33
+ export declare function createMockKeys(renderer: CliRenderer, options?: MockKeysOptions): {
30
34
  pressKeys: (keys: KeyInput[], delayMs?: number) => Promise<void>;
31
35
  pressKey: (key: KeyInput, modifiers?: {
32
36
  shift?: boolean;
@@ -4,6 +4,8 @@ import { createMockMouse } from "./mock-mouse";
4
4
  export interface TestRendererOptions extends CliRendererConfig {
5
5
  width?: number;
6
6
  height?: number;
7
+ kittyKeyboard?: boolean;
8
+ otherModifiersMode?: boolean;
7
9
  }
8
10
  export interface TestRenderer extends CliRenderer {
9
11
  }
package/testing.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  CliRenderer,
5
5
  TreeSitterClient,
6
6
  resolveRenderLib
7
- } from "./index-z5bb2h2z.js";
7
+ } from "./index-y49e47t2.js";
8
8
 
9
9
  // src/testing/mock-keys.ts
10
10
  var KeyCodes = {
@@ -34,22 +34,88 @@ var KeyCodes = {
34
34
  F11: "\x1B[23~",
35
35
  F12: "\x1B[24~"
36
36
  };
37
- function createMockKeys(renderer) {
37
+ var kittyKeyCodeMap = {
38
+ escape: 27,
39
+ tab: 9,
40
+ return: 13,
41
+ backspace: 127,
42
+ insert: 57348,
43
+ delete: 57349,
44
+ left: 57350,
45
+ right: 57351,
46
+ up: 57352,
47
+ down: 57353,
48
+ pageup: 57354,
49
+ pagedown: 57355,
50
+ home: 57356,
51
+ end: 57357,
52
+ f1: 57364,
53
+ f2: 57365,
54
+ f3: 57366,
55
+ f4: 57367,
56
+ f5: 57368,
57
+ f6: 57369,
58
+ f7: 57370,
59
+ f8: 57371,
60
+ f9: 57372,
61
+ f10: 57373,
62
+ f11: 57374,
63
+ f12: 57375
64
+ };
65
+ function encodeKittySequence(codepoint, modifiers) {
66
+ let modMask = 0;
67
+ if (modifiers?.shift)
68
+ modMask |= 1;
69
+ if (modifiers?.meta)
70
+ modMask |= 2;
71
+ if (modifiers?.ctrl)
72
+ modMask |= 4;
73
+ if (modMask === 0) {
74
+ return `\x1B[${codepoint}u`;
75
+ } else {
76
+ return `\x1B[${codepoint};${modMask + 1}u`;
77
+ }
78
+ }
79
+ function encodeModifyOtherKeysSequence(charCode, modifiers) {
80
+ let modMask = 0;
81
+ if (modifiers?.shift)
82
+ modMask |= 1;
83
+ if (modifiers?.meta)
84
+ modMask |= 2;
85
+ if (modifiers?.ctrl)
86
+ modMask |= 4;
87
+ if (modMask === 0) {
88
+ return String.fromCharCode(charCode);
89
+ }
90
+ return `\x1B[27;${modMask + 1};${charCode}~`;
91
+ }
92
+ function resolveKeyInput(key) {
93
+ let keyValue;
94
+ let keyName;
95
+ if (typeof key === "string") {
96
+ if (key in KeyCodes) {
97
+ keyValue = KeyCodes[key];
98
+ keyName = key.toLowerCase();
99
+ } else {
100
+ keyValue = key;
101
+ keyName = undefined;
102
+ }
103
+ } else {
104
+ keyValue = KeyCodes[key];
105
+ if (!keyValue) {
106
+ throw new Error(`Unknown key: ${key}`);
107
+ }
108
+ keyName = String(key).toLowerCase();
109
+ }
110
+ return { keyValue, keyName };
111
+ }
112
+ function createMockKeys(renderer, options) {
113
+ const useKittyKeyboard = options?.kittyKeyboard ?? false;
114
+ const useOtherModifiersMode = options?.otherModifiersMode ?? false;
115
+ const effectiveOtherModifiersMode = useOtherModifiersMode && !useKittyKeyboard;
38
116
  const pressKeys = async (keys, delayMs = 0) => {
39
117
  for (const key of keys) {
40
- let keyCode;
41
- if (typeof key === "string") {
42
- if (key in KeyCodes) {
43
- keyCode = KeyCodes[key];
44
- } else {
45
- keyCode = key;
46
- }
47
- } else {
48
- keyCode = KeyCodes[key];
49
- if (!keyCode) {
50
- throw new Error(`Unknown key: ${key}`);
51
- }
52
- }
118
+ const { keyValue: keyCode } = resolveKeyInput(key);
53
119
  renderer.stdin.emit("data", Buffer.from(keyCode));
54
120
  if (delayMs > 0) {
55
121
  await new Promise((resolve) => setTimeout(resolve, delayMs));
@@ -57,19 +123,66 @@ function createMockKeys(renderer) {
57
123
  }
58
124
  };
59
125
  const pressKey = (key, modifiers) => {
60
- let keyCode;
61
- if (typeof key === "string") {
62
- if (key in KeyCodes) {
63
- keyCode = KeyCodes[key];
64
- } else {
65
- keyCode = key;
126
+ if (useKittyKeyboard) {
127
+ let { keyValue, keyName } = resolveKeyInput(key);
128
+ const valueToKeyNameMap = {
129
+ "\b": "backspace",
130
+ "\r": "return",
131
+ "\n": "return",
132
+ "\t": "tab",
133
+ "\x1B": "escape",
134
+ "\x1B[A": "up",
135
+ "\x1B[B": "down",
136
+ "\x1B[C": "right",
137
+ "\x1B[D": "left",
138
+ "\x1B[H": "home",
139
+ "\x1B[F": "end",
140
+ "\x1B[3~": "delete"
141
+ };
142
+ if (keyValue && valueToKeyNameMap[keyValue]) {
143
+ keyName = valueToKeyNameMap[keyValue];
66
144
  }
67
- } else {
68
- keyCode = KeyCodes[key];
69
- if (!keyCode) {
70
- throw new Error(`Unknown key: ${key}`);
145
+ if (keyName && keyName.startsWith("arrow_")) {
146
+ keyName = keyName.substring(6);
147
+ }
148
+ if (keyName && kittyKeyCodeMap[keyName]) {
149
+ const kittyCode = kittyKeyCodeMap[keyName];
150
+ const sequence = encodeKittySequence(kittyCode, modifiers);
151
+ renderer.stdin.emit("data", Buffer.from(sequence));
152
+ return;
153
+ }
154
+ if (keyValue && keyValue.length === 1 && !keyValue.startsWith("\x1B")) {
155
+ const codepoint = keyValue.codePointAt(0);
156
+ if (codepoint) {
157
+ const sequence = encodeKittySequence(codepoint, modifiers);
158
+ renderer.stdin.emit("data", Buffer.from(sequence));
159
+ return;
160
+ }
71
161
  }
72
162
  }
163
+ if (effectiveOtherModifiersMode && modifiers) {
164
+ let { keyValue, keyName } = resolveKeyInput(key);
165
+ const valueToCharCodeMap = {
166
+ "\b": 127,
167
+ "\r": 13,
168
+ "\n": 13,
169
+ "\t": 9,
170
+ "\x1B": 27,
171
+ " ": 32
172
+ };
173
+ let charCode;
174
+ if (keyValue && valueToCharCodeMap[keyValue] !== undefined) {
175
+ charCode = valueToCharCodeMap[keyValue];
176
+ } else if (keyValue && keyValue.length === 1 && !keyValue.startsWith("\x1B")) {
177
+ charCode = keyValue.charCodeAt(0);
178
+ }
179
+ if (charCode !== undefined) {
180
+ const sequence = encodeModifyOtherKeysSequence(charCode, modifiers);
181
+ renderer.stdin.emit("data", Buffer.from(sequence));
182
+ return;
183
+ }
184
+ }
185
+ let keyCode = resolveKeyInput(key).keyValue;
73
186
  if (modifiers) {
74
187
  if (keyCode.startsWith("\x1B[") && keyCode.length > 2) {
75
188
  const modifier = 1 + (modifiers.shift ? 1 : 0) + (modifiers.meta ? 2 : 0) + (modifiers.ctrl ? 4 : 0);
@@ -84,6 +197,24 @@ function createMockKeys(renderer) {
84
197
  keyCode = String.fromCharCode(char.charCodeAt(0) - 96);
85
198
  } else if (char >= "A" && char <= "Z") {
86
199
  keyCode = String.fromCharCode(char.charCodeAt(0) - 64);
200
+ } else {
201
+ const specialCtrlMap = {
202
+ "[": "\x1B",
203
+ "\\": "\x1C",
204
+ "]": "\x1D",
205
+ "^": "\x1E",
206
+ _: "\x1F",
207
+ "?": "\x7F",
208
+ "/": "\x1F",
209
+ "-": "\x1F",
210
+ ".": "\x1E",
211
+ ",": "\x1C",
212
+ "@": "\x00",
213
+ " ": "\x00"
214
+ };
215
+ if (char in specialCtrlMap) {
216
+ keyCode = specialCtrlMap[char];
217
+ }
87
218
  }
88
219
  if (modifiers.meta) {
89
220
  keyCode = `\x1B${keyCode}`;
@@ -305,7 +436,10 @@ async function createTestRenderer(options) {
305
436
  useConsole: false
306
437
  });
307
438
  renderer.disableStdoutInterception();
308
- const mockInput = createMockKeys(renderer);
439
+ const mockInput = createMockKeys(renderer, {
440
+ kittyKeyboard: options.kittyKeyboard,
441
+ otherModifiersMode: options.otherModifiersMode
442
+ });
309
443
  const mockMouse = createMockMouse(renderer);
310
444
  const renderOnce = async () => {
311
445
  await renderer.loop();
@@ -344,6 +478,7 @@ async function setupTestRenderer(config) {
344
478
  }
345
479
  ziglib.setUseThread(rendererPtr, config.useThread);
346
480
  const renderer = new CliRenderer(ziglib, rendererPtr, stdin, stdout, width, height, config);
481
+ process.off("SIGWINCH", renderer["sigwinchHandler"]);
347
482
  return renderer;
348
483
  }
349
484
  // src/testing/mock-tree-sitter-client.ts
@@ -401,5 +536,5 @@ export {
401
536
  KeyCodes
402
537
  };
403
538
 
404
- //# debugId=334F11C8682F8A0A64756E2164756E21
539
+ //# debugId=87F3E86F73786CEC64756E2164756E21
405
540
  //# sourceMappingURL=testing.js.map