@chengchenccc/tui 0.1.1-rc.1
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/dist/autocomplete.d.ts +56 -0
- package/dist/autocomplete.d.ts.map +1 -0
- package/dist/autocomplete.js +647 -0
- package/dist/components/box.d.ts +22 -0
- package/dist/components/box.d.ts.map +1 -0
- package/dist/components/box.js +103 -0
- package/dist/components/card.d.ts +33 -0
- package/dist/components/card.d.ts.map +1 -0
- package/dist/components/card.js +49 -0
- package/dist/components/editor.d.ts +257 -0
- package/dist/components/editor.d.ts.map +1 -0
- package/dist/components/editor.js +1980 -0
- package/dist/components/input.d.ts +37 -0
- package/dist/components/input.d.ts.map +1 -0
- package/dist/components/input.js +389 -0
- package/dist/components/loader.d.ts +33 -0
- package/dist/components/loader.d.ts.map +1 -0
- package/dist/components/loader.js +100 -0
- package/dist/components/markdown.d.ts +108 -0
- package/dist/components/markdown.d.ts.map +1 -0
- package/dist/components/markdown.js +847 -0
- package/dist/components/markdown.test.d.ts +2 -0
- package/dist/components/markdown.test.d.ts.map +1 -0
- package/dist/components/markdown.test.js +81 -0
- package/dist/components/output-block.d.ts +44 -0
- package/dist/components/output-block.d.ts.map +1 -0
- package/dist/components/output-block.js +131 -0
- package/dist/components/select-list.d.ts +50 -0
- package/dist/components/select-list.d.ts.map +1 -0
- package/dist/components/select-list.js +166 -0
- package/dist/components/spacer.d.ts +12 -0
- package/dist/components/spacer.d.ts.map +1 -0
- package/dist/components/spacer.js +22 -0
- package/dist/components/statusline.d.ts +30 -0
- package/dist/components/statusline.d.ts.map +1 -0
- package/dist/components/statusline.js +46 -0
- package/dist/components/text.d.ts +19 -0
- package/dist/components/text.d.ts.map +1 -0
- package/dist/components/text.js +90 -0
- package/dist/components/truncated-text.d.ts +13 -0
- package/dist/components/truncated-text.d.ts.map +1 -0
- package/dist/components/truncated-text.js +50 -0
- package/dist/editor-component.d.ts +39 -0
- package/dist/editor-component.d.ts.map +1 -0
- package/dist/editor-component.js +1 -0
- package/dist/fuzzy.d.ts +16 -0
- package/dist/fuzzy.d.ts.map +1 -0
- package/dist/fuzzy.js +109 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/keybindings.d.ts +193 -0
- package/dist/keybindings.d.ts.map +1 -0
- package/dist/keybindings.js +191 -0
- package/dist/keys.d.ts +184 -0
- package/dist/keys.d.ts.map +1 -0
- package/dist/keys.js +1191 -0
- package/dist/kill-ring.d.ts +28 -0
- package/dist/kill-ring.d.ts.map +1 -0
- package/dist/kill-ring.js +43 -0
- package/dist/mermaid.d.ts +23 -0
- package/dist/mermaid.d.ts.map +1 -0
- package/dist/mermaid.js +77 -0
- package/dist/mouse.d.ts +40 -0
- package/dist/mouse.d.ts.map +1 -0
- package/dist/mouse.js +39 -0
- package/dist/mouse.test.d.ts +2 -0
- package/dist/mouse.test.d.ts.map +1 -0
- package/dist/mouse.test.js +36 -0
- package/dist/native-modifiers.d.ts +3 -0
- package/dist/native-modifiers.d.ts.map +1 -0
- package/dist/native-modifiers.js +52 -0
- package/dist/stdin-buffer.d.ts +50 -0
- package/dist/stdin-buffer.d.ts.map +1 -0
- package/dist/stdin-buffer.js +360 -0
- package/dist/terminal-colors.d.ts +10 -0
- package/dist/terminal-colors.d.ts.map +1 -0
- package/dist/terminal-colors.js +58 -0
- package/dist/terminal-image.d.ts +90 -0
- package/dist/terminal-image.d.ts.map +1 -0
- package/dist/terminal-image.js +375 -0
- package/dist/terminal.d.ts +110 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +432 -0
- package/dist/tui-scrollback.test.d.ts +2 -0
- package/dist/tui-scrollback.test.d.ts.map +1 -0
- package/dist/tui-scrollback.test.js +51 -0
- package/dist/tui.d.ts +330 -0
- package/dist/tui.d.ts.map +1 -0
- package/dist/tui.js +1568 -0
- package/dist/undo-stack.d.ts +17 -0
- package/dist/undo-stack.d.ts.map +1 -0
- package/dist/undo-stack.js +24 -0
- package/dist/utils.d.ts +85 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +1066 -0
- package/dist/vendor/mermaid-ascii/ascii/ansi.d.ts +42 -0
- package/dist/vendor/mermaid-ascii/ascii/ansi.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/ansi.js +361 -0
- package/dist/vendor/mermaid-ascii/ascii/canvas.d.ts +90 -0
- package/dist/vendor/mermaid-ascii/ascii/canvas.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/canvas.js +414 -0
- package/dist/vendor/mermaid-ascii/ascii/class-diagram.d.ts +8 -0
- package/dist/vendor/mermaid-ascii/ascii/class-diagram.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/class-diagram.js +623 -0
- package/dist/vendor/mermaid-ascii/ascii/converter.d.ts +13 -0
- package/dist/vendor/mermaid-ascii/ascii/converter.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/converter.js +233 -0
- package/dist/vendor/mermaid-ascii/ascii/draw.d.ts +67 -0
- package/dist/vendor/mermaid-ascii/ascii/draw.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/draw.js +1313 -0
- package/dist/vendor/mermaid-ascii/ascii/edge-bundling.d.ts +49 -0
- package/dist/vendor/mermaid-ascii/ascii/edge-bundling.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/edge-bundling.js +289 -0
- package/dist/vendor/mermaid-ascii/ascii/edge-routing.d.ts +44 -0
- package/dist/vendor/mermaid-ascii/ascii/edge-routing.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/edge-routing.js +302 -0
- package/dist/vendor/mermaid-ascii/ascii/er-diagram.d.ts +8 -0
- package/dist/vendor/mermaid-ascii/ascii/er-diagram.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/er-diagram.js +380 -0
- package/dist/vendor/mermaid-ascii/ascii/grid.d.ts +57 -0
- package/dist/vendor/mermaid-ascii/ascii/grid.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/grid.js +516 -0
- package/dist/vendor/mermaid-ascii/ascii/index.d.ts +66 -0
- package/dist/vendor/mermaid-ascii/ascii/index.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/index.js +133 -0
- package/dist/vendor/mermaid-ascii/ascii/multiline-utils.d.ts +28 -0
- package/dist/vendor/mermaid-ascii/ascii/multiline-utils.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/multiline-utils.js +60 -0
- package/dist/vendor/mermaid-ascii/ascii/pathfinder.d.ts +18 -0
- package/dist/vendor/mermaid-ascii/ascii/pathfinder.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/pathfinder.js +224 -0
- package/dist/vendor/mermaid-ascii/ascii/sequence.d.ts +8 -0
- package/dist/vendor/mermaid-ascii/ascii/sequence.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/sequence.js +416 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/circle.d.ts +12 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/circle.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/circle.js +22 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/corners.d.ts +35 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/corners.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/corners.js +91 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/diamond.d.ts +12 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/diamond.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/diamond.js +22 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/hexagon.d.ts +12 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/hexagon.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/hexagon.js +22 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/index.d.ts +27 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/index.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/index.js +66 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/rectangle.d.ts +32 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/rectangle.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/rectangle.js +149 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/rounded.d.ts +12 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/rounded.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/rounded.js +22 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/special.d.ts +60 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/special.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/special.js +253 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/stadium.d.ts +18 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/stadium.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/stadium.js +103 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/state.d.ts +31 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/state.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/state.js +167 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/types.d.ts +56 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/types.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/shapes/types.js +4 -0
- package/dist/vendor/mermaid-ascii/ascii/types.d.ts +207 -0
- package/dist/vendor/mermaid-ascii/ascii/types.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/types.js +39 -0
- package/dist/vendor/mermaid-ascii/ascii/validate.d.ts +52 -0
- package/dist/vendor/mermaid-ascii/ascii/validate.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/validate.js +97 -0
- package/dist/vendor/mermaid-ascii/ascii/xychart.d.ts +3 -0
- package/dist/vendor/mermaid-ascii/ascii/xychart.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/ascii/xychart.js +719 -0
- package/dist/vendor/mermaid-ascii/class/parser.d.ts +7 -0
- package/dist/vendor/mermaid-ascii/class/parser.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/class/parser.js +265 -0
- package/dist/vendor/mermaid-ascii/class/types.d.ts +103 -0
- package/dist/vendor/mermaid-ascii/class/types.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/class/types.js +7 -0
- package/dist/vendor/mermaid-ascii/er/parser.d.ts +7 -0
- package/dist/vendor/mermaid-ascii/er/parser.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/er/parser.js +162 -0
- package/dist/vendor/mermaid-ascii/er/types.d.ts +77 -0
- package/dist/vendor/mermaid-ascii/er/types.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/er/types.js +7 -0
- package/dist/vendor/mermaid-ascii/index.d.ts +2 -0
- package/dist/vendor/mermaid-ascii/index.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/index.js +13 -0
- package/dist/vendor/mermaid-ascii/multiline-utils.d.ts +10 -0
- package/dist/vendor/mermaid-ascii/multiline-utils.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/multiline-utils.js +29 -0
- package/dist/vendor/mermaid-ascii/parser.d.ts +8 -0
- package/dist/vendor/mermaid-ascii/parser.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/parser.js +558 -0
- package/dist/vendor/mermaid-ascii/sequence/parser.d.ts +7 -0
- package/dist/vendor/mermaid-ascii/sequence/parser.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/sequence/parser.js +182 -0
- package/dist/vendor/mermaid-ascii/sequence/types.d.ts +131 -0
- package/dist/vendor/mermaid-ascii/sequence/types.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/sequence/types.js +7 -0
- package/dist/vendor/mermaid-ascii/text-metrics.d.ts +24 -0
- package/dist/vendor/mermaid-ascii/text-metrics.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/text-metrics.js +69 -0
- package/dist/vendor/mermaid-ascii/types.d.ts +115 -0
- package/dist/vendor/mermaid-ascii/types.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/types.js +4 -0
- package/dist/vendor/mermaid-ascii/xychart/colors.d.ts +26 -0
- package/dist/vendor/mermaid-ascii/xychart/colors.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/xychart/colors.js +145 -0
- package/dist/vendor/mermaid-ascii/xychart/parser.d.ts +7 -0
- package/dist/vendor/mermaid-ascii/xychart/parser.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/xychart/parser.js +105 -0
- package/dist/vendor/mermaid-ascii/xychart/types.d.ts +146 -0
- package/dist/vendor/mermaid-ascii/xychart/types.d.ts.map +1 -0
- package/dist/vendor/mermaid-ascii/xychart/types.js +8 -0
- package/dist/virtual-terminal.d.ts +80 -0
- package/dist/virtual-terminal.d.ts.map +1 -0
- package/dist/virtual-terminal.js +284 -0
- package/dist/word-navigation.d.ts +25 -0
- package/dist/word-navigation.d.ts.map +1 -0
- package/dist/word-navigation.js +97 -0
- package/package.json +38 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import xterm from "@xterm/headless";
|
|
2
|
+
// Extract Terminal class from the module
|
|
3
|
+
const XtermTerminal = xterm.Terminal;
|
|
4
|
+
/**
|
|
5
|
+
* Virtual terminal for testing using xterm.js for accurate terminal emulation
|
|
6
|
+
*/
|
|
7
|
+
export class VirtualTerminal {
|
|
8
|
+
xterm;
|
|
9
|
+
inputHandler;
|
|
10
|
+
resizeHandler;
|
|
11
|
+
_columns;
|
|
12
|
+
_rows;
|
|
13
|
+
constructor(columns = 80, rows = 24) {
|
|
14
|
+
this._columns = columns;
|
|
15
|
+
this._rows = rows;
|
|
16
|
+
// Create xterm instance with specified dimensions
|
|
17
|
+
this.xterm = new XtermTerminal({
|
|
18
|
+
cols: columns,
|
|
19
|
+
rows: rows,
|
|
20
|
+
// Disable all interactive features for testing
|
|
21
|
+
disableStdin: true,
|
|
22
|
+
allowProposedApi: true,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
start(onInput, onResize) {
|
|
26
|
+
this.inputHandler = onInput;
|
|
27
|
+
this.resizeHandler = onResize;
|
|
28
|
+
// Enable bracketed paste mode for consistency with ProcessTerminal
|
|
29
|
+
this.xterm.write("\x1b[?2004h");
|
|
30
|
+
}
|
|
31
|
+
async drainInput(_maxMs, _idleMs) {
|
|
32
|
+
// No-op for virtual terminal - no stdin to drain
|
|
33
|
+
}
|
|
34
|
+
stop() {
|
|
35
|
+
// Disable bracketed paste mode
|
|
36
|
+
this.xterm.write("\x1b[?2004l");
|
|
37
|
+
this.inputHandler = undefined;
|
|
38
|
+
this.resizeHandler = undefined;
|
|
39
|
+
}
|
|
40
|
+
write(data) {
|
|
41
|
+
this.xterm.write(data);
|
|
42
|
+
}
|
|
43
|
+
get columns() {
|
|
44
|
+
return this._columns;
|
|
45
|
+
}
|
|
46
|
+
get rows() {
|
|
47
|
+
return this._rows;
|
|
48
|
+
}
|
|
49
|
+
get kittyProtocolActive() {
|
|
50
|
+
// Virtual terminal always reports Kitty protocol as active for testing
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
moveBy(lines) {
|
|
54
|
+
if (lines > 0) {
|
|
55
|
+
// Move down
|
|
56
|
+
this.xterm.write(`\x1b[${lines}B`);
|
|
57
|
+
}
|
|
58
|
+
else if (lines < 0) {
|
|
59
|
+
// Move up
|
|
60
|
+
this.xterm.write(`\x1b[${-lines}A`);
|
|
61
|
+
}
|
|
62
|
+
// lines === 0: no movement
|
|
63
|
+
}
|
|
64
|
+
hideCursor() {
|
|
65
|
+
this.xterm.write("\x1b[?25l");
|
|
66
|
+
}
|
|
67
|
+
showCursor() {
|
|
68
|
+
this.xterm.write("\x1b[?25h");
|
|
69
|
+
}
|
|
70
|
+
clearLine() {
|
|
71
|
+
this.xterm.write("\x1b[K");
|
|
72
|
+
}
|
|
73
|
+
clearFromCursor() {
|
|
74
|
+
this.xterm.write("\x1b[J");
|
|
75
|
+
}
|
|
76
|
+
clearScreen() {
|
|
77
|
+
this.xterm.write("\x1b[2J\x1b[H"); // Clear screen and move to home (1,1)
|
|
78
|
+
}
|
|
79
|
+
setTitle(title) {
|
|
80
|
+
// OSC 0;title BEL - set terminal window title
|
|
81
|
+
this.xterm.write(`\x1b]0;${title}\x07`);
|
|
82
|
+
}
|
|
83
|
+
setProgress(_active) { }
|
|
84
|
+
// Test-specific methods not in Terminal interface
|
|
85
|
+
/**
|
|
86
|
+
* Simulate keyboard input
|
|
87
|
+
*/
|
|
88
|
+
sendInput(data) {
|
|
89
|
+
if (this.inputHandler) {
|
|
90
|
+
this.inputHandler(data);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Resize the terminal
|
|
95
|
+
*/
|
|
96
|
+
resize(columns, rows) {
|
|
97
|
+
this._columns = columns;
|
|
98
|
+
this._rows = rows;
|
|
99
|
+
this.xterm.resize(columns, rows);
|
|
100
|
+
if (this.resizeHandler) {
|
|
101
|
+
this.resizeHandler();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Wait for all pending writes to complete. Viewport and scroll buffer will be updated.
|
|
106
|
+
*/
|
|
107
|
+
async flush() {
|
|
108
|
+
// Write an empty string to ensure all previous writes are flushed
|
|
109
|
+
return new Promise((resolve) => {
|
|
110
|
+
this.xterm.write("", () => resolve());
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Flush and get viewport - convenience method for tests
|
|
115
|
+
*/
|
|
116
|
+
async flushAndGetViewport() {
|
|
117
|
+
await this.flush();
|
|
118
|
+
return this.getViewport();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get the visible viewport (what's currently on screen)
|
|
122
|
+
* Note: You should use getViewportAfterWrite() for testing after writing data
|
|
123
|
+
*/
|
|
124
|
+
getViewport() {
|
|
125
|
+
const lines = [];
|
|
126
|
+
const buffer = this.xterm.buffer.active;
|
|
127
|
+
// Get only the visible lines (viewport)
|
|
128
|
+
for (let i = 0; i < this.xterm.rows; i++) {
|
|
129
|
+
const line = buffer.getLine(buffer.viewportY + i);
|
|
130
|
+
if (line) {
|
|
131
|
+
lines.push(line.translateToString(true));
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
lines.push("");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return lines;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get the entire scroll buffer
|
|
141
|
+
*/
|
|
142
|
+
/**
|
|
143
|
+
* Like getViewport() but re-emits per-cell SGR styling (fg/bg/bold/dim/…)
|
|
144
|
+
* so snapshot tooling can see colors and backgrounds, not just text.
|
|
145
|
+
*/
|
|
146
|
+
getViewportAnsi() {
|
|
147
|
+
const lines = [];
|
|
148
|
+
const buffer = this.xterm.buffer.active;
|
|
149
|
+
for (let i = 0; i < this.xterm.rows; i++) {
|
|
150
|
+
const line = buffer.getLine(buffer.viewportY + i);
|
|
151
|
+
if (!line) {
|
|
152
|
+
lines.push("");
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
lines.push(lineToAnsi(line));
|
|
156
|
+
}
|
|
157
|
+
return lines;
|
|
158
|
+
}
|
|
159
|
+
/** Scrollback + viewport with per-cell SGR re-emitted. */
|
|
160
|
+
getScrollBufferAnsi() {
|
|
161
|
+
const lines = [];
|
|
162
|
+
const buffer = this.xterm.buffer.active;
|
|
163
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
164
|
+
const line = buffer.getLine(i);
|
|
165
|
+
lines.push(line ? lineToAnsi(line) : "");
|
|
166
|
+
}
|
|
167
|
+
return lines;
|
|
168
|
+
}
|
|
169
|
+
getScrollBuffer() {
|
|
170
|
+
const lines = [];
|
|
171
|
+
const buffer = this.xterm.buffer.active;
|
|
172
|
+
// Get all lines in the buffer (including scrollback)
|
|
173
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
174
|
+
const line = buffer.getLine(i);
|
|
175
|
+
if (line) {
|
|
176
|
+
lines.push(line.translateToString(true));
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
lines.push("");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return lines;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Clear the terminal viewport
|
|
186
|
+
*/
|
|
187
|
+
clear() {
|
|
188
|
+
this.xterm.clear();
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Reset the terminal completely
|
|
192
|
+
*/
|
|
193
|
+
reset() {
|
|
194
|
+
this.xterm.reset();
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Get cursor position
|
|
198
|
+
*/
|
|
199
|
+
getCursorPosition() {
|
|
200
|
+
const buffer = this.xterm.buffer.active;
|
|
201
|
+
return {
|
|
202
|
+
x: buffer.cursorX,
|
|
203
|
+
y: buffer.cursorY,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/** Wait for TUI's throttled render pipeline to settle. */
|
|
207
|
+
async waitForRender() {
|
|
208
|
+
await new Promise((resolve) => process.nextTick(resolve));
|
|
209
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
210
|
+
await this.flush();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/** SGR escape for one cell's styling; empty string means default attrs. */
|
|
214
|
+
function cellSgr(cell) {
|
|
215
|
+
const codes = [];
|
|
216
|
+
if (cell.isBold())
|
|
217
|
+
codes.push("1");
|
|
218
|
+
if (cell.isDim())
|
|
219
|
+
codes.push("2");
|
|
220
|
+
if (cell.isItalic())
|
|
221
|
+
codes.push("3");
|
|
222
|
+
if (cell.isUnderline())
|
|
223
|
+
codes.push("4");
|
|
224
|
+
if (cell.isInverse())
|
|
225
|
+
codes.push("7");
|
|
226
|
+
if (cell.isStrikethrough())
|
|
227
|
+
codes.push("9");
|
|
228
|
+
const fg = colorCode(cell.getFgColor(), cell.getFgColorMode(), 30);
|
|
229
|
+
if (fg)
|
|
230
|
+
codes.push(fg);
|
|
231
|
+
const bg = colorCode(cell.getBgColor(), cell.getBgColorMode(), 40);
|
|
232
|
+
if (bg)
|
|
233
|
+
codes.push(bg);
|
|
234
|
+
return codes.length > 0 ? `\x1b[${codes.join(";")}m` : "";
|
|
235
|
+
}
|
|
236
|
+
/** Map xterm color (palette index or packed rgb) to an SGR parameter. */
|
|
237
|
+
function colorCode(color, mode, base) {
|
|
238
|
+
// xterm packs the color mode in bits 24-25: 0 = default, 1 = P16 palette,
|
|
239
|
+
// 2 = P256 palette, 3 = RGB.
|
|
240
|
+
const kind = mode >>> 24;
|
|
241
|
+
if (kind === 1) {
|
|
242
|
+
if (color < 8)
|
|
243
|
+
return `${base + color}`;
|
|
244
|
+
return `${base + 60 + color - 8}`;
|
|
245
|
+
}
|
|
246
|
+
if (kind === 2)
|
|
247
|
+
return `${base + 8};5;${color}`;
|
|
248
|
+
if (kind === 3) {
|
|
249
|
+
const r = (color >> 16) & 0xff;
|
|
250
|
+
const g = (color >> 8) & 0xff;
|
|
251
|
+
const b = color & 0xff;
|
|
252
|
+
return `${base + 8};2;${r};${g};${b}`;
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
/** One buffer row with SGR codes re-emitted between attribute runs. */
|
|
257
|
+
export function lineToAnsi(line) {
|
|
258
|
+
let out = "";
|
|
259
|
+
let current = "";
|
|
260
|
+
let x = 0;
|
|
261
|
+
while (x < line.length) {
|
|
262
|
+
const cell = line.getCell(x);
|
|
263
|
+
if (!cell)
|
|
264
|
+
break;
|
|
265
|
+
const sgr = cellSgr(cell);
|
|
266
|
+
if (sgr !== current) {
|
|
267
|
+
current = sgr;
|
|
268
|
+
out += "\x1b[0m";
|
|
269
|
+
out += sgr;
|
|
270
|
+
}
|
|
271
|
+
const width = cell.getWidth();
|
|
272
|
+
if (width === 0) {
|
|
273
|
+
// Zero-width cell (e.g. after a wide char): escape only, no glyph.
|
|
274
|
+
out += cell.getChars();
|
|
275
|
+
x += 1;
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
out += cell.getChars() || " ";
|
|
279
|
+
x += width;
|
|
280
|
+
}
|
|
281
|
+
if (current)
|
|
282
|
+
out += "\x1b[0m";
|
|
283
|
+
return out;
|
|
284
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for word navigation functions.
|
|
3
|
+
* When omitted, uses the default Intl.Segmenter word segmentation.
|
|
4
|
+
*/
|
|
5
|
+
export interface WordNavigationOptions {
|
|
6
|
+
/** Custom segmenter returning word segments for the given text. */
|
|
7
|
+
segment?: (text: string) => Iterable<Intl.SegmentData>;
|
|
8
|
+
/** Predicate identifying atomic segments that should be treated as single units (e.g. paste markers). */
|
|
9
|
+
isAtomicSegment?: (segment: string) => boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Find the cursor position after moving one word backward from `cursor` in `text`.
|
|
13
|
+
* Skips trailing whitespace, then stops at the next word/punctuation boundary.
|
|
14
|
+
*
|
|
15
|
+
* Pure function - does not mutate any state.
|
|
16
|
+
*/
|
|
17
|
+
export declare function findWordBackward(text: string, cursor: number, options?: WordNavigationOptions): number;
|
|
18
|
+
/**
|
|
19
|
+
* Find the cursor position after moving one word forward from `cursor` in `text`.
|
|
20
|
+
* Skips leading whitespace, then stops at the next word/punctuation boundary.
|
|
21
|
+
*
|
|
22
|
+
* Pure function - does not mutate any state.
|
|
23
|
+
*/
|
|
24
|
+
export declare function findWordForward(text: string, cursor: number, options?: WordNavigationOptions): number;
|
|
25
|
+
//# sourceMappingURL=word-navigation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"word-navigation.d.ts","sourceRoot":"","sources":["../src/word-navigation.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,mEAAmE;IACnE,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACvD,yGAAyG;IACzG,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;CAChD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,MAAM,CAkDR;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,MAAM,CAuCR"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { getWordSegmenter, isWhitespaceChar, PUNCTUATION_REGEX } from "./utils.js";
|
|
2
|
+
const wordSegmenter = getWordSegmenter();
|
|
3
|
+
/**
|
|
4
|
+
* Find the cursor position after moving one word backward from `cursor` in `text`.
|
|
5
|
+
* Skips trailing whitespace, then stops at the next word/punctuation boundary.
|
|
6
|
+
*
|
|
7
|
+
* Pure function - does not mutate any state.
|
|
8
|
+
*/
|
|
9
|
+
export function findWordBackward(text, cursor, options) {
|
|
10
|
+
if (cursor <= 0)
|
|
11
|
+
return 0;
|
|
12
|
+
const textBeforeCursor = text.slice(0, cursor);
|
|
13
|
+
const segmentFn = options?.segment;
|
|
14
|
+
const isAtomic = options?.isAtomicSegment;
|
|
15
|
+
const segments = segmentFn
|
|
16
|
+
? [...segmentFn(textBeforeCursor)]
|
|
17
|
+
: [...wordSegmenter.segment(textBeforeCursor)];
|
|
18
|
+
let newCursor = cursor;
|
|
19
|
+
// Skip trailing whitespace
|
|
20
|
+
while (segments.length > 0 &&
|
|
21
|
+
!isAtomic?.(segments[segments.length - 1]?.segment || "") &&
|
|
22
|
+
isWhitespaceChar(segments[segments.length - 1]?.segment || "")) {
|
|
23
|
+
newCursor -= segments.pop()?.segment.length || 0;
|
|
24
|
+
}
|
|
25
|
+
if (segments.length === 0)
|
|
26
|
+
return newCursor;
|
|
27
|
+
const last = segments[segments.length - 1];
|
|
28
|
+
if (isAtomic?.(last.segment)) {
|
|
29
|
+
// Skip one atomic segment.
|
|
30
|
+
newCursor -= last.segment.length;
|
|
31
|
+
}
|
|
32
|
+
else if (last.isWordLike) {
|
|
33
|
+
// Skip inside one word-like segment, preserving ASCII punctuation boundaries.
|
|
34
|
+
const segment = last.segment;
|
|
35
|
+
const matches = [...segment.matchAll(new RegExp(PUNCTUATION_REGEX, "g"))];
|
|
36
|
+
if (matches.length <= 0) {
|
|
37
|
+
newCursor -= segment.length;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
const lastMatch = matches[matches.length - 1];
|
|
41
|
+
newCursor -= segment.length - (lastMatch.index + lastMatch[0].length);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// Skip non-word non-whitespace run (punctuation)
|
|
46
|
+
while (segments.length > 0 &&
|
|
47
|
+
!isAtomic?.(segments[segments.length - 1]?.segment || "") &&
|
|
48
|
+
!segments[segments.length - 1]?.isWordLike &&
|
|
49
|
+
!isWhitespaceChar(segments[segments.length - 1]?.segment || "")) {
|
|
50
|
+
newCursor -= segments.pop()?.segment.length || 0;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return newCursor;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Find the cursor position after moving one word forward from `cursor` in `text`.
|
|
57
|
+
* Skips leading whitespace, then stops at the next word/punctuation boundary.
|
|
58
|
+
*
|
|
59
|
+
* Pure function - does not mutate any state.
|
|
60
|
+
*/
|
|
61
|
+
export function findWordForward(text, cursor, options) {
|
|
62
|
+
if (cursor >= text.length)
|
|
63
|
+
return text.length;
|
|
64
|
+
const textAfterCursor = text.slice(cursor);
|
|
65
|
+
const segmentFn = options?.segment;
|
|
66
|
+
const isAtomic = options?.isAtomicSegment;
|
|
67
|
+
const segments = segmentFn ? segmentFn(textAfterCursor) : wordSegmenter.segment(textAfterCursor);
|
|
68
|
+
const iterator = segments[Symbol.iterator]();
|
|
69
|
+
let next = iterator.next();
|
|
70
|
+
let newCursor = cursor;
|
|
71
|
+
// Skip leading whitespace
|
|
72
|
+
while (!next.done && !isAtomic?.(next.value.segment) && isWhitespaceChar(next.value.segment)) {
|
|
73
|
+
newCursor += next.value.segment.length;
|
|
74
|
+
next = iterator.next();
|
|
75
|
+
}
|
|
76
|
+
if (next.done)
|
|
77
|
+
return newCursor;
|
|
78
|
+
if (isAtomic?.(next.value.segment)) {
|
|
79
|
+
// Skip one atomic segment.
|
|
80
|
+
newCursor += next.value.segment.length;
|
|
81
|
+
}
|
|
82
|
+
else if (next.value.isWordLike) {
|
|
83
|
+
// Skip inside one word-like segment, preserving ASCII punctuation boundaries.
|
|
84
|
+
newCursor += PUNCTUATION_REGEX.exec(next.value.segment)?.index ?? next.value.segment.length;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
// Skip non-word non-whitespace run (punctuation)
|
|
88
|
+
while (!next.done &&
|
|
89
|
+
!isAtomic?.(next.value.segment) &&
|
|
90
|
+
!next.value.isWordLike &&
|
|
91
|
+
!isWhitespaceChar(next.value.segment)) {
|
|
92
|
+
newCursor += next.value.segment.length;
|
|
93
|
+
next = iterator.next();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return newCursor;
|
|
97
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chengchenccc/tui",
|
|
3
|
+
"version": "0.1.1-rc.1",
|
|
4
|
+
"description": "Terminal UI library with differential rendering (vendored from @earendil-works/pi-tui, MIT)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"default": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./testing": {
|
|
12
|
+
"types": "./dist/virtual-terminal.d.ts",
|
|
13
|
+
"default": "./dist/virtual-terminal.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc -p tsconfig.json",
|
|
22
|
+
"lint": "biome check . && eslint .",
|
|
23
|
+
"test": "bun test --pass-with-no-tests",
|
|
24
|
+
"typecheck": "tsc -p tsconfig.test.json --noEmit"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"get-east-asian-width": "^1.6.0",
|
|
28
|
+
"marked": "^18.0.5"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@xterm/headless": "5.5.0"
|
|
32
|
+
},
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/Chengchcc/my-agent-team.git",
|
|
36
|
+
"directory": "packages/tui"
|
|
37
|
+
}
|
|
38
|
+
}
|