@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.
- package/3d.js +1 -1
- package/{index-z5bb2h2z.js → index-y49e47t2.js} +685 -455
- package/{index-z5bb2h2z.js.map → index-y49e47t2.js.map} +15 -14
- package/index.js +62 -44
- package/index.js.map +7 -7
- package/lib/KeyHandler.d.ts +4 -9
- package/lib/ascii.font.d.ts +4 -4
- package/lib/extmarks.d.ts +1 -1
- package/lib/keymapping.d.ts +1 -0
- package/lib/stdin-buffer.d.ts +8 -6
- package/lib/terminal-capability-detection.d.ts +30 -0
- package/package.json +8 -8
- package/renderables/ASCIIFont.d.ts +28 -19
- package/renderables/Textarea.d.ts +2 -1
- package/renderables/__tests__/renderable-test-utils.d.ts +7 -0
- package/renderer.d.ts +11 -0
- package/testing/mock-keys.d.ts +5 -1
- package/testing/test-renderer.d.ts +2 -0
- package/testing.js +162 -27
- package/testing.js.map +4 -4
- package/zig-structs.d.ts +8 -0
package/lib/KeyHandler.d.ts
CHANGED
|
@@ -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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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(
|
|
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;
|
package/lib/ascii.font.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { OptimizedBuffer } from "../buffer";
|
|
2
|
-
import {
|
|
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,
|
|
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
|
-
|
|
296
|
-
|
|
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
|
|
70
|
+
private offsetExcludingNewlines;
|
|
71
71
|
create(options: ExtmarkOptions): number;
|
|
72
72
|
delete(id: number): boolean;
|
|
73
73
|
get(id: number): Extmark | null;
|
package/lib/keymapping.d.ts
CHANGED
|
@@ -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>[];
|
package/lib/stdin-buffer.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* StdinBuffer
|
|
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
|
-
*
|
|
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
|
|
35
|
-
private
|
|
36
|
-
constructor(
|
|
37
|
-
|
|
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-
|
|
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": "
|
|
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-
|
|
59
|
-
"@opentui/core-darwin-arm64": "0.0.0-
|
|
60
|
-
"@opentui/core-linux-x64": "0.0.0-
|
|
61
|
-
"@opentui/core-linux-arm64": "0.0.0-
|
|
62
|
-
"@opentui/core-win32-x64": "0.0.0-
|
|
63
|
-
"@opentui/core-win32-arm64": "0.0.0-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
selectionBg?:
|
|
13
|
-
selectionFg?:
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
32
|
-
set
|
|
33
|
-
get
|
|
34
|
-
set
|
|
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;
|
package/testing/mock-keys.d.ts
CHANGED
|
@@ -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
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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=
|
|
539
|
+
//# debugId=87F3E86F73786CEC64756E2164756E21
|
|
405
540
|
//# sourceMappingURL=testing.js.map
|