@bramburn/qi-tui 0.0.0-stage → 0.85.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.
Files changed (172) hide show
  1. package/README.md +853 -2
  2. package/dist/alt-screen-search.d.ts +26 -0
  3. package/dist/alt-screen-search.d.ts.map +1 -0
  4. package/dist/alt-screen-search.js +122 -0
  5. package/dist/alt-screen-search.js.map +1 -0
  6. package/dist/autocomplete.d.ts +57 -0
  7. package/dist/autocomplete.d.ts.map +1 -0
  8. package/dist/autocomplete.js +665 -0
  9. package/dist/autocomplete.js.map +1 -0
  10. package/dist/components/alt-screen-flash.d.ts +13 -0
  11. package/dist/components/alt-screen-flash.d.ts.map +1 -0
  12. package/dist/components/alt-screen-flash.js +37 -0
  13. package/dist/components/alt-screen-flash.js.map +1 -0
  14. package/dist/components/box.d.ts +22 -0
  15. package/dist/components/box.d.ts.map +1 -0
  16. package/dist/components/box.js +104 -0
  17. package/dist/components/box.js.map +1 -0
  18. package/dist/components/cancellable-loader.d.ts +22 -0
  19. package/dist/components/cancellable-loader.d.ts.map +1 -0
  20. package/dist/components/cancellable-loader.js +35 -0
  21. package/dist/components/cancellable-loader.js.map +1 -0
  22. package/dist/components/editor.d.ts +385 -0
  23. package/dist/components/editor.d.ts.map +1 -0
  24. package/dist/components/editor.js +2246 -0
  25. package/dist/components/editor.js.map +1 -0
  26. package/dist/components/h-stack.d.ts +7 -0
  27. package/dist/components/h-stack.d.ts.map +1 -0
  28. package/dist/components/h-stack.js +43 -0
  29. package/dist/components/h-stack.js.map +1 -0
  30. package/dist/components/image.d.ts +28 -0
  31. package/dist/components/image.d.ts.map +1 -0
  32. package/dist/components/image.js +90 -0
  33. package/dist/components/image.js.map +1 -0
  34. package/dist/components/input.d.ts +37 -0
  35. package/dist/components/input.d.ts.map +1 -0
  36. package/dist/components/input.js +378 -0
  37. package/dist/components/input.js.map +1 -0
  38. package/dist/components/loader.d.ts +31 -0
  39. package/dist/components/loader.d.ts.map +1 -0
  40. package/dist/components/loader.js +69 -0
  41. package/dist/components/loader.js.map +1 -0
  42. package/dist/components/markdown.d.ts +103 -0
  43. package/dist/components/markdown.d.ts.map +1 -0
  44. package/dist/components/markdown.js +811 -0
  45. package/dist/components/markdown.js.map +1 -0
  46. package/dist/components/scroll-view.d.ts +56 -0
  47. package/dist/components/scroll-view.d.ts.map +1 -0
  48. package/dist/components/scroll-view.js +189 -0
  49. package/dist/components/scroll-view.js.map +1 -0
  50. package/dist/components/select-list.d.ts +50 -0
  51. package/dist/components/select-list.d.ts.map +1 -0
  52. package/dist/components/select-list.js +159 -0
  53. package/dist/components/select-list.js.map +1 -0
  54. package/dist/components/settings-list.d.ts +56 -0
  55. package/dist/components/settings-list.d.ts.map +1 -0
  56. package/dist/components/settings-list.js +202 -0
  57. package/dist/components/settings-list.js.map +1 -0
  58. package/dist/components/spacer.d.ts +12 -0
  59. package/dist/components/spacer.d.ts.map +1 -0
  60. package/dist/components/spacer.js +23 -0
  61. package/dist/components/spacer.js.map +1 -0
  62. package/dist/components/stack.d.ts +32 -0
  63. package/dist/components/stack.d.ts.map +1 -0
  64. package/dist/components/stack.js +111 -0
  65. package/dist/components/stack.js.map +1 -0
  66. package/dist/components/text.d.ts +19 -0
  67. package/dist/components/text.d.ts.map +1 -0
  68. package/dist/components/text.js +90 -0
  69. package/dist/components/text.js.map +1 -0
  70. package/dist/components/truncated-text.d.ts +13 -0
  71. package/dist/components/truncated-text.d.ts.map +1 -0
  72. package/dist/components/truncated-text.js +51 -0
  73. package/dist/components/truncated-text.js.map +1 -0
  74. package/dist/components/v-stack.d.ts +8 -0
  75. package/dist/components/v-stack.d.ts.map +1 -0
  76. package/dist/components/v-stack.js +26 -0
  77. package/dist/components/v-stack.js.map +1 -0
  78. package/dist/editor-component.d.ts +68 -0
  79. package/dist/editor-component.d.ts.map +1 -0
  80. package/dist/editor-component.js +2 -0
  81. package/dist/editor-component.js.map +1 -0
  82. package/dist/fuzzy.d.ts +16 -0
  83. package/dist/fuzzy.d.ts.map +1 -0
  84. package/dist/fuzzy.js +110 -0
  85. package/dist/fuzzy.js.map +1 -0
  86. package/dist/index.d.ts +31 -0
  87. package/dist/index.d.ts.map +1 -0
  88. package/dist/index.js +42 -0
  89. package/dist/index.js.map +1 -0
  90. package/dist/keybindings.d.ts +273 -0
  91. package/dist/keybindings.d.ts.map +1 -0
  92. package/dist/keybindings.js +232 -0
  93. package/dist/keybindings.js.map +1 -0
  94. package/dist/keys.d.ts +184 -0
  95. package/dist/keys.d.ts.map +1 -0
  96. package/dist/keys.js +1186 -0
  97. package/dist/keys.js.map +1 -0
  98. package/dist/kill-ring.d.ts +28 -0
  99. package/dist/kill-ring.d.ts.map +1 -0
  100. package/dist/kill-ring.js +44 -0
  101. package/dist/kill-ring.js.map +1 -0
  102. package/dist/latex.d.ts +10 -0
  103. package/dist/latex.d.ts.map +1 -0
  104. package/dist/latex.js +1271 -0
  105. package/dist/latex.js.map +1 -0
  106. package/dist/layout-node.d.ts +40 -0
  107. package/dist/layout-node.d.ts.map +1 -0
  108. package/dist/layout-node.js +6 -0
  109. package/dist/layout-node.js.map +1 -0
  110. package/dist/layout.d.ts +40 -0
  111. package/dist/layout.d.ts.map +1 -0
  112. package/dist/layout.js +321 -0
  113. package/dist/layout.js.map +1 -0
  114. package/dist/native-modifiers.d.ts +3 -0
  115. package/dist/native-modifiers.d.ts.map +1 -0
  116. package/dist/native-modifiers.js +54 -0
  117. package/dist/native-modifiers.js.map +1 -0
  118. package/dist/native-module-path.d.ts +7 -0
  119. package/dist/native-module-path.d.ts.map +1 -0
  120. package/dist/native-module-path.js +19 -0
  121. package/dist/native-module-path.js.map +1 -0
  122. package/dist/stdin-buffer.d.ts +56 -0
  123. package/dist/stdin-buffer.d.ts.map +1 -0
  124. package/dist/stdin-buffer.js +366 -0
  125. package/dist/stdin-buffer.js.map +1 -0
  126. package/dist/terminal-colors.d.ts +10 -0
  127. package/dist/terminal-colors.d.ts.map +1 -0
  128. package/dist/terminal-colors.js +59 -0
  129. package/dist/terminal-colors.js.map +1 -0
  130. package/dist/terminal-image.d.ts +118 -0
  131. package/dist/terminal-image.d.ts.map +1 -0
  132. package/dist/terminal-image.js +557 -0
  133. package/dist/terminal-image.js.map +1 -0
  134. package/dist/terminal.d.ts +117 -0
  135. package/dist/terminal.d.ts.map +1 -0
  136. package/dist/terminal.js +447 -0
  137. package/dist/terminal.js.map +1 -0
  138. package/dist/tui-alt-screen.d.ts +129 -0
  139. package/dist/tui-alt-screen.d.ts.map +1 -0
  140. package/dist/tui-alt-screen.js +1191 -0
  141. package/dist/tui-alt-screen.js.map +1 -0
  142. package/dist/tui-main-screen.d.ts +39 -0
  143. package/dist/tui-main-screen.d.ts.map +1 -0
  144. package/dist/tui-main-screen.js +621 -0
  145. package/dist/tui-main-screen.js.map +1 -0
  146. package/dist/tui.d.ts +308 -0
  147. package/dist/tui.d.ts.map +1 -0
  148. package/dist/tui.js +941 -0
  149. package/dist/tui.js.map +1 -0
  150. package/dist/undo-stack.d.ts +26 -0
  151. package/dist/undo-stack.d.ts.map +1 -0
  152. package/dist/undo-stack.js +36 -0
  153. package/dist/undo-stack.js.map +1 -0
  154. package/dist/utils.d.ts +96 -0
  155. package/dist/utils.d.ts.map +1 -0
  156. package/dist/utils.js +1191 -0
  157. package/dist/utils.js.map +1 -0
  158. package/dist/word-navigation.d.ts +25 -0
  159. package/dist/word-navigation.d.ts.map +1 -0
  160. package/dist/word-navigation.js +96 -0
  161. package/dist/word-navigation.js.map +1 -0
  162. package/native/darwin/README.md +20 -0
  163. package/native/darwin/build.sh +62 -0
  164. package/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
  165. package/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
  166. package/native/darwin/src/darwin-modifiers.c +70 -0
  167. package/native/win32/README.md +22 -0
  168. package/native/win32/build.mjs +229 -0
  169. package/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  170. package/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  171. package/native/win32/src/win32-console-mode.c +119 -0
  172. package/package.json +55 -5
package/dist/layout.js ADDED
@@ -0,0 +1,321 @@
1
+ import { allocateStackSizes, visibleStackEntries } from "./components/stack.js";
2
+ import { getLayoutNode } from "./layout-node.js";
3
+ import { cropKittyImageLine, getKittyImageMetadata, isImageLine } from "./terminal-image.js";
4
+ import { CURSOR_MARKER, compositeTuiLine } from "./tui.js";
5
+ import { extractAnsiCode, getGraphemeCellRange, sliceByColumn, visibleWidth } from "./utils.js";
6
+ const OSC133_ZONE_PREFIX = /^(?:\x1b\]133;[ABC](?:\x07|\x1b\\))+/;
7
+ function intersect(a, b) {
8
+ const x = Math.max(a.x, b.x);
9
+ const y = Math.max(a.y, b.y);
10
+ const right = Math.min(a.x + a.width, b.x + b.width);
11
+ const bottom = Math.min(a.y + a.height, b.y + b.height);
12
+ return { x, y, width: Math.max(0, right - x), height: Math.max(0, bottom - y) };
13
+ }
14
+ function renderCached(context, component, width) {
15
+ const safeWidth = Math.max(1, Math.floor(width));
16
+ let widths = context.renderCache.get(component);
17
+ if (!widths) {
18
+ widths = new Map();
19
+ context.renderCache.set(component, widths);
20
+ }
21
+ let lines = widths.get(safeWidth);
22
+ if (!lines) {
23
+ lines = component.render(safeWidth);
24
+ widths.set(safeWidth, lines);
25
+ }
26
+ return lines;
27
+ }
28
+ function measureHeight(context, component, width) {
29
+ return renderCached(context, component, width).length;
30
+ }
31
+ function measureWidth(context, component, width) {
32
+ return renderCached(context, component, width).reduce((max, line) => Math.max(max, visibleWidth(line)), 0);
33
+ }
34
+ function withParent(box, parent) {
35
+ box.parent = parent;
36
+ return box;
37
+ }
38
+ function translateBox(box, deltaY) {
39
+ box.rect.y += deltaY;
40
+ for (const child of box.children)
41
+ translateBox(child, deltaY);
42
+ }
43
+ function updateClips(box, parentClip) {
44
+ box.clip = intersect(parentClip, box.rect);
45
+ for (const child of box.children)
46
+ updateClips(child, box.clip);
47
+ }
48
+ function layoutComponent(context, component, x, y, width, height, clip) {
49
+ const safeWidth = Math.max(1, Math.floor(width));
50
+ const node = getLayoutNode(component);
51
+ if (!node) {
52
+ const lines = renderCached(context, component, safeWidth);
53
+ const allocatedHeight = height === undefined ? lines.length : Math.max(0, Math.floor(height));
54
+ let lineOffset = 0;
55
+ if (lines.length > allocatedHeight && allocatedHeight > 0) {
56
+ const cursorLine = lines.findIndex((line) => line.includes(CURSOR_MARKER));
57
+ if (cursorLine >= allocatedHeight)
58
+ lineOffset = cursorLine - allocatedHeight + 1;
59
+ }
60
+ return {
61
+ component,
62
+ rect: { x, y, width: safeWidth, height: allocatedHeight },
63
+ clip: intersect(clip, { x, y, width: safeWidth, height: allocatedHeight }),
64
+ children: [],
65
+ lines,
66
+ lineOffset,
67
+ layer: 0,
68
+ };
69
+ }
70
+ if (node.type === "scroll") {
71
+ const previousScrollTop = node.state.scrollTop;
72
+ const contentWidth = node.state.getContentWidth(safeWidth);
73
+ const childBox = layoutComponent(context, node.component, x, y - previousScrollTop, contentWidth, undefined, clip);
74
+ const contentHeight = childBox.rect.height;
75
+ const viewportHeight = height === undefined ? contentHeight : Math.max(0, Math.floor(height));
76
+ node.state.updateLayout(contentHeight, viewportHeight, context.requestRender);
77
+ translateBox(childBox, previousScrollTop - node.state.scrollTop);
78
+ const scrollView = node.state;
79
+ if (node.state.primary || !context.primaryScrollView)
80
+ context.primaryScrollView = scrollView;
81
+ const rect = { x, y, width: safeWidth, height: viewportHeight };
82
+ const childClip = intersect(clip, rect);
83
+ const box = {
84
+ component,
85
+ rect,
86
+ clip: childClip,
87
+ children: [childBox],
88
+ scrollView,
89
+ scrollContentLines: renderCached(context, node.component, contentWidth),
90
+ layer: 0,
91
+ };
92
+ childBox.parent = box;
93
+ updateClips(childBox, childClip);
94
+ return box;
95
+ }
96
+ const entries = visibleStackEntries(node.entries, context.viewport);
97
+ const gapTotal = Math.max(0, entries.length - 1) * node.gap;
98
+ if (node.type === "vstack") {
99
+ const intrinsicHeights = entries.map((entry) => typeof entry.basis === "number" ? entry.basis : measureHeight(context, entry.component, safeWidth));
100
+ const sizes = allocateStackSizes(entries, intrinsicHeights, height, node.gap);
101
+ const naturalHeight = sizes.reduce((sum, size) => sum + size, 0) + gapTotal;
102
+ const allocatedHeight = height === undefined ? naturalHeight : Math.max(0, Math.floor(height));
103
+ const rect = { x, y, width: safeWidth, height: allocatedHeight };
104
+ const box = {
105
+ component,
106
+ rect,
107
+ clip: intersect(clip, rect),
108
+ children: [],
109
+ layer: 0,
110
+ };
111
+ let childY = y;
112
+ for (let index = 0; index < entries.length; index++) {
113
+ box.children.push(withParent(layoutComponent(context, entries[index].component, x, childY, safeWidth, sizes[index], box.clip), box));
114
+ childY += sizes[index] + node.gap;
115
+ }
116
+ return box;
117
+ }
118
+ const intrinsicWidths = entries.map((entry) => typeof entry.basis === "number" ? entry.basis : measureWidth(context, entry.component, safeWidth));
119
+ const widths = allocateStackSizes(entries, intrinsicWidths, safeWidth, node.gap);
120
+ const intrinsicHeights = entries.map((entry, index) => measureHeight(context, entry.component, Math.max(1, widths[index])));
121
+ const allocatedHeight = height === undefined
122
+ ? intrinsicHeights.reduce((max, childHeight) => Math.max(max, childHeight), 0)
123
+ : Math.max(0, height);
124
+ const rect = { x, y, width: safeWidth, height: allocatedHeight };
125
+ const box = {
126
+ component,
127
+ rect,
128
+ clip: intersect(clip, rect),
129
+ children: [],
130
+ layer: 0,
131
+ };
132
+ let childX = x;
133
+ for (let index = 0; index < entries.length; index++) {
134
+ const naturalChildHeight = intrinsicHeights[index];
135
+ const childHeight = node.align === "stretch" ? allocatedHeight : Math.min(allocatedHeight, naturalChildHeight);
136
+ let childY = y;
137
+ if (node.align === "center")
138
+ childY += Math.floor((allocatedHeight - childHeight) / 2);
139
+ else if (node.align === "end")
140
+ childY += allocatedHeight - childHeight;
141
+ const childWidth = widths[index];
142
+ if (childWidth === 0) {
143
+ box.children.push({
144
+ component: entries[index].component,
145
+ rect: { x: childX, y: childY, width: 0, height: childHeight },
146
+ clip: { x: childX, y: childY, width: 0, height: 0 },
147
+ children: [],
148
+ parent: box,
149
+ layer: 0,
150
+ });
151
+ }
152
+ else {
153
+ box.children.push(withParent(layoutComponent(context, entries[index].component, childX, childY, childWidth, childHeight, box.clip), box));
154
+ }
155
+ childX += childWidth + node.gap;
156
+ }
157
+ return box;
158
+ }
159
+ function styleScrollbarCell(line, column, totalWidth, style) {
160
+ if (isImageLine(line))
161
+ return line;
162
+ const graphemeRange = getGraphemeCellRange(line, column);
163
+ const start = graphemeRange?.start ?? column;
164
+ const end = graphemeRange?.end ?? column + 1;
165
+ const before = sliceByColumn(line, 0, start, true);
166
+ const target = sliceByColumn(line, start, end - start, true);
167
+ const after = sliceByColumn(line, end, Math.max(0, totalWidth - end), true);
168
+ let targetPrefix = "";
169
+ let targetIndex = 0;
170
+ while (targetIndex < target.length) {
171
+ const ansi = extractAnsiCode(target, targetIndex);
172
+ if (!ansi)
173
+ break;
174
+ targetPrefix += ansi.code;
175
+ targetIndex += ansi.length;
176
+ }
177
+ const targetText = target.slice(targetIndex) || " ".repeat(end - start);
178
+ const beforePadding = " ".repeat(Math.max(0, start - visibleWidth(before)));
179
+ return `${before}${beforePadding}${targetPrefix}${style(targetText)}${after}`;
180
+ }
181
+ export function getScrollbarGeometry(box) {
182
+ if (!box.scrollView?.isScrollbarVisible || box.rect.width <= 0 || box.rect.height <= 0)
183
+ return undefined;
184
+ const contentHeight = box.children[0]?.rect.height ?? box.scrollContentLines?.length ?? 0;
185
+ const trackHeight = box.rect.height;
186
+ const minThumbHeight = Math.min(2, trackHeight);
187
+ const thumbHeight = Math.max(minThumbHeight, Math.min(trackHeight, Math.round((trackHeight * trackHeight) / contentHeight)));
188
+ const maxScrollTop = Math.max(0, contentHeight - trackHeight);
189
+ const maxThumbTop = trackHeight - thumbHeight;
190
+ const thumbOffset = maxScrollTop === 0 ? 0 : Math.round((box.scrollView.scrollTop / maxScrollTop) * maxThumbTop);
191
+ const column = box.rect.x + box.rect.width - 1;
192
+ if (column < box.clip.x || column >= box.clip.x + box.clip.width)
193
+ return undefined;
194
+ return {
195
+ column,
196
+ trackTop: box.rect.y,
197
+ trackHeight,
198
+ thumbTop: box.rect.y + thumbOffset,
199
+ thumbHeight,
200
+ maxScrollTop,
201
+ };
202
+ }
203
+ function paintScrollbar(box, screen, totalWidth) {
204
+ const geometry = getScrollbarGeometry(box);
205
+ if (!geometry || !box.scrollView)
206
+ return;
207
+ for (let offset = 0; offset < geometry.thumbHeight; offset++) {
208
+ const row = geometry.thumbTop + offset;
209
+ if (row < box.clip.y || row >= box.clip.y + box.clip.height || row < 0 || row >= screen.length)
210
+ continue;
211
+ screen[row] = styleScrollbarCell(screen[row] ?? "", geometry.column, totalWidth, box.scrollView.scrollbarStyle);
212
+ }
213
+ }
214
+ function paintBox(box, screen, totalWidth) {
215
+ if (box.lines) {
216
+ const offset = box.lineOffset ?? 0;
217
+ const firstRow = Math.max(box.rect.y, box.clip.y, 0);
218
+ const lastRow = Math.min(box.rect.y + box.rect.height, box.clip.y + box.clip.height, screen.length);
219
+ for (let row = firstRow; row < lastRow; row++) {
220
+ const sourceLine = box.lines[offset + row - box.rect.y];
221
+ if (sourceLine === undefined)
222
+ continue;
223
+ let line = sourceLine.replace(OSC133_ZONE_PREFIX, "");
224
+ const imageMetadata = getKittyImageMetadata(line);
225
+ if (imageMetadata) {
226
+ const clipBottom = Math.min(screen.length, box.clip.y + box.clip.height);
227
+ const visibleRows = Math.min(imageMetadata.rows, clipBottom - row);
228
+ if (visibleRows < imageMetadata.rows)
229
+ line = cropKittyImageLine(line, 0, visibleRows);
230
+ }
231
+ // Fast path: a full-width box painting onto an untouched row can use the
232
+ // source line reference directly. Compositing here would rebuild the row
233
+ // string through ANSI/grapheme segmentation every frame; padding is
234
+ // unnecessary because rows are written with erase-line and the final
235
+ // width clamp still truncates over-wide lines.
236
+ if (box.rect.x === 0 && box.rect.width >= totalWidth && (isImageLine(line) || !screen[row])) {
237
+ screen[row] = line;
238
+ }
239
+ else {
240
+ screen[row] = compositeTuiLine(screen[row] ?? "", line, box.rect.x, box.rect.width, totalWidth);
241
+ }
242
+ }
243
+ }
244
+ for (const child of box.children)
245
+ paintBox(child, screen, totalWidth);
246
+ if (box.scrollView && box.scrollContentLines && box.scrollView.scrollTop > 0 && box.rect.height > 0) {
247
+ for (let imageRow = box.scrollView.scrollTop - 1; imageRow >= 0; imageRow--) {
248
+ const imageLine = box.scrollContentLines[imageRow] ?? "";
249
+ const metadata = getKittyImageMetadata(imageLine);
250
+ if (metadata) {
251
+ const hiddenRows = box.scrollView.scrollTop - imageRow;
252
+ if (hiddenRows < metadata.rows) {
253
+ const visibleRows = Math.min(box.rect.height, metadata.rows - hiddenRows);
254
+ const cropped = cropKittyImageLine(imageLine, hiddenRows, visibleRows);
255
+ if (box.rect.x === 0 && box.rect.width >= totalWidth)
256
+ screen[box.rect.y] = cropped;
257
+ }
258
+ break;
259
+ }
260
+ if (imageLine !== "")
261
+ break;
262
+ }
263
+ }
264
+ paintScrollbar(box, screen, totalWidth);
265
+ }
266
+ export function renderLayoutFrame(root, width, height, requestRender) {
267
+ const safeWidth = Math.max(1, Math.floor(width));
268
+ const safeHeight = Math.max(1, Math.floor(height));
269
+ const context = {
270
+ viewport: { width: safeWidth, height: safeHeight },
271
+ renderCache: new Map(),
272
+ requestRender,
273
+ primaryScrollView: undefined,
274
+ };
275
+ const rootBox = layoutComponent(context, root, 0, 0, safeWidth, safeHeight, {
276
+ x: 0,
277
+ y: 0,
278
+ width: safeWidth,
279
+ height: safeHeight,
280
+ });
281
+ const lines = Array.from({ length: safeHeight }, () => "");
282
+ paintBox(rootBox, lines, safeWidth);
283
+ return {
284
+ root: rootBox,
285
+ width: safeWidth,
286
+ height: safeHeight,
287
+ lines,
288
+ ...(context.primaryScrollView === undefined ? {} : { primaryScrollView: context.primaryScrollView }),
289
+ };
290
+ }
291
+ function containsPoint(rect, x, y) {
292
+ return x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height;
293
+ }
294
+ export function getScrollViewBox(frame, scrollView) {
295
+ const visit = (box) => {
296
+ if (box.scrollView === scrollView)
297
+ return box;
298
+ for (const child of box.children) {
299
+ const match = visit(child);
300
+ if (match)
301
+ return match;
302
+ }
303
+ return undefined;
304
+ };
305
+ return visit(frame.root);
306
+ }
307
+ export function getScrollViewsAt(frame, x, y) {
308
+ const result = [];
309
+ const visit = (box, depth) => {
310
+ if (!containsPoint(box.clip, x, y))
311
+ return;
312
+ if (box.scrollView && containsPoint(box.rect, x, y))
313
+ result.push({ scrollView: box.scrollView, depth });
314
+ for (const child of box.children)
315
+ visit(child, depth + 1);
316
+ };
317
+ visit(frame.root, 0);
318
+ result.sort((a, b) => b.depth - a.depth);
319
+ return result.map((entry) => entry.scrollView);
320
+ }
321
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ScrollView } from \"./components/scroll-view.ts\";\nimport { allocateStackSizes, visibleStackEntries } from \"./components/stack.ts\";\nimport { getLayoutNode } from \"./layout-node.ts\";\nimport { cropKittyImageLine, getKittyImageMetadata, isImageLine } from \"./terminal-image.ts\";\nimport { type Component, CURSOR_MARKER, compositeTuiLine } from \"./tui.ts\";\nimport { extractAnsiCode, getGraphemeCellRange, sliceByColumn, visibleWidth } from \"./utils.ts\";\n\nconst OSC133_ZONE_PREFIX = /^(?:\\x1b\\]133;[ABC](?:\\x07|\\x1b\\\\))+/;\n\nexport interface LayoutRect {\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n}\n\nexport interface LayoutBox {\n\tcomponent: Component;\n\trect: LayoutRect;\n\tclip: LayoutRect;\n\tchildren: LayoutBox[];\n\tparent?: LayoutBox;\n\tlines?: readonly string[];\n\tlineOffset?: number;\n\tscrollView?: ScrollView;\n\tscrollContentLines?: readonly string[];\n\tlayer: number;\n}\n\nexport interface LayoutFrame {\n\troot: LayoutBox;\n\twidth: number;\n\theight: number;\n\tlines: string[];\n\tprimaryScrollView?: ScrollView;\n}\n\nexport interface ScrollbarGeometry {\n\tcolumn: number;\n\ttrackTop: number;\n\ttrackHeight: number;\n\tthumbTop: number;\n\tthumbHeight: number;\n\tmaxScrollTop: number;\n}\n\ninterface LayoutContext {\n\tviewport: { width: number; height: number };\n\trenderCache: Map<Component, Map<number, string[]>>;\n\trequestRender: () => void;\n\tprimaryScrollView: ScrollView | undefined;\n}\n\nfunction intersect(a: LayoutRect, b: LayoutRect): LayoutRect {\n\tconst x = Math.max(a.x, b.x);\n\tconst y = Math.max(a.y, b.y);\n\tconst right = Math.min(a.x + a.width, b.x + b.width);\n\tconst bottom = Math.min(a.y + a.height, b.y + b.height);\n\treturn { x, y, width: Math.max(0, right - x), height: Math.max(0, bottom - y) };\n}\n\nfunction renderCached(context: LayoutContext, component: Component, width: number): string[] {\n\tconst safeWidth = Math.max(1, Math.floor(width));\n\tlet widths = context.renderCache.get(component);\n\tif (!widths) {\n\t\twidths = new Map<number, string[]>();\n\t\tcontext.renderCache.set(component, widths);\n\t}\n\tlet lines = widths.get(safeWidth);\n\tif (!lines) {\n\t\tlines = component.render(safeWidth);\n\t\twidths.set(safeWidth, lines);\n\t}\n\treturn lines;\n}\n\nfunction measureHeight(context: LayoutContext, component: Component, width: number): number {\n\treturn renderCached(context, component, width).length;\n}\n\nfunction measureWidth(context: LayoutContext, component: Component, width: number): number {\n\treturn renderCached(context, component, width).reduce((max, line) => Math.max(max, visibleWidth(line)), 0);\n}\n\nfunction withParent(box: LayoutBox, parent: LayoutBox): LayoutBox {\n\tbox.parent = parent;\n\treturn box;\n}\n\nfunction translateBox(box: LayoutBox, deltaY: number): void {\n\tbox.rect.y += deltaY;\n\tfor (const child of box.children) translateBox(child, deltaY);\n}\n\nfunction updateClips(box: LayoutBox, parentClip: LayoutRect): void {\n\tbox.clip = intersect(parentClip, box.rect);\n\tfor (const child of box.children) updateClips(child, box.clip);\n}\n\nfunction layoutComponent(\n\tcontext: LayoutContext,\n\tcomponent: Component,\n\tx: number,\n\ty: number,\n\twidth: number,\n\theight: number | undefined,\n\tclip: LayoutRect,\n): LayoutBox {\n\tconst safeWidth = Math.max(1, Math.floor(width));\n\tconst node = getLayoutNode(component);\n\tif (!node) {\n\t\tconst lines = renderCached(context, component, safeWidth);\n\t\tconst allocatedHeight = height === undefined ? lines.length : Math.max(0, Math.floor(height));\n\t\tlet lineOffset = 0;\n\t\tif (lines.length > allocatedHeight && allocatedHeight > 0) {\n\t\t\tconst cursorLine = lines.findIndex((line) => line.includes(CURSOR_MARKER));\n\t\t\tif (cursorLine >= allocatedHeight) lineOffset = cursorLine - allocatedHeight + 1;\n\t\t}\n\t\treturn {\n\t\t\tcomponent,\n\t\t\trect: { x, y, width: safeWidth, height: allocatedHeight },\n\t\t\tclip: intersect(clip, { x, y, width: safeWidth, height: allocatedHeight }),\n\t\t\tchildren: [],\n\t\t\tlines,\n\t\t\tlineOffset,\n\t\t\tlayer: 0,\n\t\t};\n\t}\n\n\tif (node.type === \"scroll\") {\n\t\tconst previousScrollTop = node.state.scrollTop;\n\t\tconst contentWidth = node.state.getContentWidth(safeWidth);\n\t\tconst childBox = layoutComponent(\n\t\t\tcontext,\n\t\t\tnode.component,\n\t\t\tx,\n\t\t\ty - previousScrollTop,\n\t\t\tcontentWidth,\n\t\t\tundefined,\n\t\t\tclip,\n\t\t);\n\t\tconst contentHeight = childBox.rect.height;\n\t\tconst viewportHeight = height === undefined ? contentHeight : Math.max(0, Math.floor(height));\n\t\tnode.state.updateLayout(contentHeight, viewportHeight, context.requestRender);\n\t\ttranslateBox(childBox, previousScrollTop - node.state.scrollTop);\n\t\tconst scrollView = node.state as ScrollView;\n\t\tif (node.state.primary || !context.primaryScrollView) context.primaryScrollView = scrollView;\n\t\tconst rect = { x, y, width: safeWidth, height: viewportHeight };\n\t\tconst childClip = intersect(clip, rect);\n\t\tconst box: LayoutBox = {\n\t\t\tcomponent,\n\t\t\trect,\n\t\t\tclip: childClip,\n\t\t\tchildren: [childBox],\n\t\t\tscrollView,\n\t\t\tscrollContentLines: renderCached(context, node.component, contentWidth),\n\t\t\tlayer: 0,\n\t\t};\n\t\tchildBox.parent = box;\n\t\tupdateClips(childBox, childClip);\n\t\treturn box;\n\t}\n\n\tconst entries = visibleStackEntries(node.entries, context.viewport);\n\tconst gapTotal = Math.max(0, entries.length - 1) * node.gap;\n\tif (node.type === \"vstack\") {\n\t\tconst intrinsicHeights = entries.map((entry) =>\n\t\t\ttypeof entry.basis === \"number\" ? entry.basis : measureHeight(context, entry.component, safeWidth),\n\t\t);\n\t\tconst sizes = allocateStackSizes(entries, intrinsicHeights, height, node.gap);\n\t\tconst naturalHeight = sizes.reduce((sum, size) => sum + size, 0) + gapTotal;\n\t\tconst allocatedHeight = height === undefined ? naturalHeight : Math.max(0, Math.floor(height));\n\t\tconst rect = { x, y, width: safeWidth, height: allocatedHeight };\n\t\tconst box: LayoutBox = {\n\t\t\tcomponent,\n\t\t\trect,\n\t\t\tclip: intersect(clip, rect),\n\t\t\tchildren: [],\n\t\t\tlayer: 0,\n\t\t};\n\t\tlet childY = y;\n\t\tfor (let index = 0; index < entries.length; index++) {\n\t\t\tbox.children.push(\n\t\t\t\twithParent(\n\t\t\t\t\tlayoutComponent(context, entries[index]!.component, x, childY, safeWidth, sizes[index]!, box.clip),\n\t\t\t\t\tbox,\n\t\t\t\t),\n\t\t\t);\n\t\t\tchildY += sizes[index]! + node.gap;\n\t\t}\n\t\treturn box;\n\t}\n\n\tconst intrinsicWidths = entries.map((entry) =>\n\t\ttypeof entry.basis === \"number\" ? entry.basis : measureWidth(context, entry.component, safeWidth),\n\t);\n\tconst widths = allocateStackSizes(entries, intrinsicWidths, safeWidth, node.gap);\n\tconst intrinsicHeights = entries.map((entry, index) =>\n\t\tmeasureHeight(context, entry.component, Math.max(1, widths[index]!)),\n\t);\n\tconst allocatedHeight =\n\t\theight === undefined\n\t\t\t? intrinsicHeights.reduce((max, childHeight) => Math.max(max, childHeight), 0)\n\t\t\t: Math.max(0, height);\n\tconst rect = { x, y, width: safeWidth, height: allocatedHeight };\n\tconst box: LayoutBox = {\n\t\tcomponent,\n\t\trect,\n\t\tclip: intersect(clip, rect),\n\t\tchildren: [],\n\t\tlayer: 0,\n\t};\n\tlet childX = x;\n\tfor (let index = 0; index < entries.length; index++) {\n\t\tconst naturalChildHeight = intrinsicHeights[index]!;\n\t\tconst childHeight = node.align === \"stretch\" ? allocatedHeight : Math.min(allocatedHeight, naturalChildHeight);\n\t\tlet childY = y;\n\t\tif (node.align === \"center\") childY += Math.floor((allocatedHeight - childHeight) / 2);\n\t\telse if (node.align === \"end\") childY += allocatedHeight - childHeight;\n\t\tconst childWidth = widths[index]!;\n\t\tif (childWidth === 0) {\n\t\t\tbox.children.push({\n\t\t\t\tcomponent: entries[index]!.component,\n\t\t\t\trect: { x: childX, y: childY, width: 0, height: childHeight },\n\t\t\t\tclip: { x: childX, y: childY, width: 0, height: 0 },\n\t\t\t\tchildren: [],\n\t\t\t\tparent: box,\n\t\t\t\tlayer: 0,\n\t\t\t});\n\t\t} else {\n\t\t\tbox.children.push(\n\t\t\t\twithParent(\n\t\t\t\t\tlayoutComponent(context, entries[index]!.component, childX, childY, childWidth, childHeight, box.clip),\n\t\t\t\t\tbox,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tchildX += childWidth + node.gap;\n\t}\n\treturn box;\n}\n\nfunction styleScrollbarCell(line: string, column: number, totalWidth: number, style: (text: string) => string): string {\n\tif (isImageLine(line)) return line;\n\n\tconst graphemeRange = getGraphemeCellRange(line, column);\n\tconst start = graphemeRange?.start ?? column;\n\tconst end = graphemeRange?.end ?? column + 1;\n\tconst before = sliceByColumn(line, 0, start, true);\n\tconst target = sliceByColumn(line, start, end - start, true);\n\tconst after = sliceByColumn(line, end, Math.max(0, totalWidth - end), true);\n\n\tlet targetPrefix = \"\";\n\tlet targetIndex = 0;\n\twhile (targetIndex < target.length) {\n\t\tconst ansi = extractAnsiCode(target, targetIndex);\n\t\tif (!ansi) break;\n\t\ttargetPrefix += ansi.code;\n\t\ttargetIndex += ansi.length;\n\t}\n\tconst targetText = target.slice(targetIndex) || \" \".repeat(end - start);\n\tconst beforePadding = \" \".repeat(Math.max(0, start - visibleWidth(before)));\n\treturn `${before}${beforePadding}${targetPrefix}${style(targetText)}${after}`;\n}\n\nexport function getScrollbarGeometry(box: LayoutBox): ScrollbarGeometry | undefined {\n\tif (!box.scrollView?.isScrollbarVisible || box.rect.width <= 0 || box.rect.height <= 0) return undefined;\n\n\tconst contentHeight = box.children[0]?.rect.height ?? box.scrollContentLines?.length ?? 0;\n\tconst trackHeight = box.rect.height;\n\n\tconst minThumbHeight = Math.min(2, trackHeight);\n\tconst thumbHeight = Math.max(\n\t\tminThumbHeight,\n\t\tMath.min(trackHeight, Math.round((trackHeight * trackHeight) / contentHeight)),\n\t);\n\tconst maxScrollTop = Math.max(0, contentHeight - trackHeight);\n\tconst maxThumbTop = trackHeight - thumbHeight;\n\tconst thumbOffset = maxScrollTop === 0 ? 0 : Math.round((box.scrollView.scrollTop / maxScrollTop) * maxThumbTop);\n\tconst column = box.rect.x + box.rect.width - 1;\n\tif (column < box.clip.x || column >= box.clip.x + box.clip.width) return undefined;\n\n\treturn {\n\t\tcolumn,\n\t\ttrackTop: box.rect.y,\n\t\ttrackHeight,\n\t\tthumbTop: box.rect.y + thumbOffset,\n\t\tthumbHeight,\n\t\tmaxScrollTop,\n\t};\n}\n\nfunction paintScrollbar(box: LayoutBox, screen: string[], totalWidth: number): void {\n\tconst geometry = getScrollbarGeometry(box);\n\tif (!geometry || !box.scrollView) return;\n\n\tfor (let offset = 0; offset < geometry.thumbHeight; offset++) {\n\t\tconst row = geometry.thumbTop + offset;\n\t\tif (row < box.clip.y || row >= box.clip.y + box.clip.height || row < 0 || row >= screen.length) continue;\n\t\tscreen[row] = styleScrollbarCell(screen[row] ?? \"\", geometry.column, totalWidth, box.scrollView.scrollbarStyle);\n\t}\n}\n\nfunction paintBox(box: LayoutBox, screen: string[], totalWidth: number): void {\n\tif (box.lines) {\n\t\tconst offset = box.lineOffset ?? 0;\n\t\tconst firstRow = Math.max(box.rect.y, box.clip.y, 0);\n\t\tconst lastRow = Math.min(box.rect.y + box.rect.height, box.clip.y + box.clip.height, screen.length);\n\t\tfor (let row = firstRow; row < lastRow; row++) {\n\t\t\tconst sourceLine = box.lines[offset + row - box.rect.y];\n\t\t\tif (sourceLine === undefined) continue;\n\t\t\tlet line = sourceLine.replace(OSC133_ZONE_PREFIX, \"\");\n\t\t\tconst imageMetadata = getKittyImageMetadata(line);\n\t\t\tif (imageMetadata) {\n\t\t\t\tconst clipBottom = Math.min(screen.length, box.clip.y + box.clip.height);\n\t\t\t\tconst visibleRows = Math.min(imageMetadata.rows, clipBottom - row);\n\t\t\t\tif (visibleRows < imageMetadata.rows) line = cropKittyImageLine(line, 0, visibleRows);\n\t\t\t}\n\t\t\t// Fast path: a full-width box painting onto an untouched row can use the\n\t\t\t// source line reference directly. Compositing here would rebuild the row\n\t\t\t// string through ANSI/grapheme segmentation every frame; padding is\n\t\t\t// unnecessary because rows are written with erase-line and the final\n\t\t\t// width clamp still truncates over-wide lines.\n\t\t\tif (box.rect.x === 0 && box.rect.width >= totalWidth && (isImageLine(line) || !screen[row])) {\n\t\t\t\tscreen[row] = line;\n\t\t\t} else {\n\t\t\t\tscreen[row] = compositeTuiLine(screen[row] ?? \"\", line, box.rect.x, box.rect.width, totalWidth);\n\t\t\t}\n\t\t}\n\t}\n\tfor (const child of box.children) paintBox(child, screen, totalWidth);\n\n\tif (box.scrollView && box.scrollContentLines && box.scrollView.scrollTop > 0 && box.rect.height > 0) {\n\t\tfor (let imageRow = box.scrollView.scrollTop - 1; imageRow >= 0; imageRow--) {\n\t\t\tconst imageLine = box.scrollContentLines[imageRow] ?? \"\";\n\t\t\tconst metadata = getKittyImageMetadata(imageLine);\n\t\t\tif (metadata) {\n\t\t\t\tconst hiddenRows = box.scrollView.scrollTop - imageRow;\n\t\t\t\tif (hiddenRows < metadata.rows) {\n\t\t\t\t\tconst visibleRows = Math.min(box.rect.height, metadata.rows - hiddenRows);\n\t\t\t\t\tconst cropped = cropKittyImageLine(imageLine, hiddenRows, visibleRows);\n\t\t\t\t\tif (box.rect.x === 0 && box.rect.width >= totalWidth) screen[box.rect.y] = cropped;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (imageLine !== \"\") break;\n\t\t}\n\t}\n\n\tpaintScrollbar(box, screen, totalWidth);\n}\n\nexport function renderLayoutFrame(\n\troot: Component,\n\twidth: number,\n\theight: number,\n\trequestRender: () => void,\n): LayoutFrame {\n\tconst safeWidth = Math.max(1, Math.floor(width));\n\tconst safeHeight = Math.max(1, Math.floor(height));\n\tconst context: LayoutContext = {\n\t\tviewport: { width: safeWidth, height: safeHeight },\n\t\trenderCache: new Map(),\n\t\trequestRender,\n\t\tprimaryScrollView: undefined,\n\t};\n\tconst rootBox = layoutComponent(context, root, 0, 0, safeWidth, safeHeight, {\n\t\tx: 0,\n\t\ty: 0,\n\t\twidth: safeWidth,\n\t\theight: safeHeight,\n\t});\n\tconst lines = Array.from({ length: safeHeight }, () => \"\");\n\tpaintBox(rootBox, lines, safeWidth);\n\treturn {\n\t\troot: rootBox,\n\t\twidth: safeWidth,\n\t\theight: safeHeight,\n\t\tlines,\n\t\t...(context.primaryScrollView === undefined ? {} : { primaryScrollView: context.primaryScrollView }),\n\t};\n}\n\nfunction containsPoint(rect: LayoutRect, x: number, y: number): boolean {\n\treturn x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height;\n}\n\nexport function getScrollViewBox(frame: LayoutFrame, scrollView: ScrollView): LayoutBox | undefined {\n\tconst visit = (box: LayoutBox): LayoutBox | undefined => {\n\t\tif (box.scrollView === scrollView) return box;\n\t\tfor (const child of box.children) {\n\t\t\tconst match = visit(child);\n\t\t\tif (match) return match;\n\t\t}\n\t\treturn undefined;\n\t};\n\treturn visit(frame.root);\n}\n\nexport function getScrollViewsAt(frame: LayoutFrame, x: number, y: number): ScrollView[] {\n\tconst result: Array<{ scrollView: ScrollView; depth: number }> = [];\n\tconst visit = (box: LayoutBox, depth: number): void => {\n\t\tif (!containsPoint(box.clip, x, y)) return;\n\t\tif (box.scrollView && containsPoint(box.rect, x, y)) result.push({ scrollView: box.scrollView, depth });\n\t\tfor (const child of box.children) visit(child, depth + 1);\n\t};\n\tvisit(frame.root, 0);\n\tresult.sort((a, b) => b.depth - a.depth);\n\treturn result.map((entry) => entry.scrollView);\n}\n"]}
@@ -0,0 +1,3 @@
1
+ export type ModifierKey = "shift" | "command" | "control" | "option";
2
+ export declare function isNativeModifierPressed(key: ModifierKey): boolean;
3
+ //# sourceMappingURL=native-modifiers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-modifiers.d.ts","sourceRoot":"","sources":["../src/native-modifiers.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AA4CrE,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAQjE","sourcesContent":["import { createRequire } from \"node:module\";\nimport * as path from \"node:path\";\nimport { getNativeModuleCandidates } from \"./native-module-path.ts\";\n\nconst cjsRequire = createRequire(import.meta.url);\n\nexport type ModifierKey = \"shift\" | \"command\" | \"control\" | \"option\";\n\ntype NativeModifiersHelper = {\n\tisModifierPressed: (name: ModifierKey) => boolean;\n};\n\nlet nativeModifiersHelper: NativeModifiersHelper | null | undefined;\n\nfunction isNativeModifiersHelper(value: unknown): value is NativeModifiersHelper {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst candidate = (value as { isModifierPressed?: unknown }).isModifierPressed;\n\treturn typeof candidate === \"function\";\n}\n\nfunction loadNativeModifiersHelper(): NativeModifiersHelper | undefined {\n\tif (nativeModifiersHelper !== undefined) return nativeModifiersHelper ?? undefined;\n\tnativeModifiersHelper = null;\n\tconst arch = process.arch;\n\tif (arch !== \"x64\" && arch !== \"arm64\") return undefined;\n\n\tlet nativePath: string;\n\tif (process.platform === \"darwin\") {\n\t\tnativePath = path.join(\"native\", \"darwin\", \"prebuilds\", `darwin-${arch}`, \"darwin-modifiers.node\");\n\t} else if (process.platform === \"win32\") {\n\t\tnativePath = path.join(\"native\", \"win32\", \"prebuilds\", `win32-${arch}`, \"win32-console-mode.node\");\n\t} else {\n\t\treturn undefined;\n\t}\n\n\tfor (const modulePath of getNativeModuleCandidates(nativePath)) {\n\t\ttry {\n\t\t\tconst helper = cjsRequire(modulePath) as unknown;\n\t\t\tif (isNativeModifiersHelper(helper)) {\n\t\t\t\tnativeModifiersHelper = helper;\n\t\t\t\treturn helper;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Try the next possible packaging location.\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nexport function isNativeModifierPressed(key: ModifierKey): boolean {\n\tconst helper = loadNativeModifiersHelper();\n\tif (!helper) return false;\n\ttry {\n\t\treturn helper.isModifierPressed(key) === true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n"]}
@@ -0,0 +1,54 @@
1
+ import { createRequire } from "node:module";
2
+ import * as path from "node:path";
3
+ import { getNativeModuleCandidates } from "./native-module-path.js";
4
+ const cjsRequire = createRequire(import.meta.url);
5
+ let nativeModifiersHelper;
6
+ function isNativeModifiersHelper(value) {
7
+ if (typeof value !== "object" || value === null)
8
+ return false;
9
+ const candidate = value.isModifierPressed;
10
+ return typeof candidate === "function";
11
+ }
12
+ function loadNativeModifiersHelper() {
13
+ if (nativeModifiersHelper !== undefined)
14
+ return nativeModifiersHelper ?? undefined;
15
+ nativeModifiersHelper = null;
16
+ const arch = process.arch;
17
+ if (arch !== "x64" && arch !== "arm64")
18
+ return undefined;
19
+ let nativePath;
20
+ if (process.platform === "darwin") {
21
+ nativePath = path.join("native", "darwin", "prebuilds", `darwin-${arch}`, "darwin-modifiers.node");
22
+ }
23
+ else if (process.platform === "win32") {
24
+ nativePath = path.join("native", "win32", "prebuilds", `win32-${arch}`, "win32-console-mode.node");
25
+ }
26
+ else {
27
+ return undefined;
28
+ }
29
+ for (const modulePath of getNativeModuleCandidates(nativePath)) {
30
+ try {
31
+ const helper = cjsRequire(modulePath);
32
+ if (isNativeModifiersHelper(helper)) {
33
+ nativeModifiersHelper = helper;
34
+ return helper;
35
+ }
36
+ }
37
+ catch {
38
+ // Try the next possible packaging location.
39
+ }
40
+ }
41
+ return undefined;
42
+ }
43
+ export function isNativeModifierPressed(key) {
44
+ const helper = loadNativeModifiersHelper();
45
+ if (!helper)
46
+ return false;
47
+ try {
48
+ return helper.isModifierPressed(key) === true;
49
+ }
50
+ catch {
51
+ return false;
52
+ }
53
+ }
54
+ //# sourceMappingURL=native-modifiers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-modifiers.js","sourceRoot":"","sources":["../src/native-modifiers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAEpE,MAAM,UAAU,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AAQlD,IAAI,qBAA+D,CAAC;AAEpE,SAAS,uBAAuB,CAAC,KAAc,EAAkC;IAChF,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,SAAS,GAAI,KAAyC,CAAC,iBAAiB,CAAC;IAC/E,OAAO,OAAO,SAAS,KAAK,UAAU,CAAC;AAAA,CACvC;AAED,SAAS,yBAAyB,GAAsC;IACvE,IAAI,qBAAqB,KAAK,SAAS;QAAE,OAAO,qBAAqB,IAAI,SAAS,CAAC;IACnF,qBAAqB,GAAG,IAAI,CAAC;IAC7B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1B,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,SAAS,CAAC;IAEzD,IAAI,UAAkB,CAAC;IACvB,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACnC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,IAAI,EAAE,EAAE,uBAAuB,CAAC,CAAC;IACpG,CAAC;SAAM,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,IAAI,EAAE,EAAE,yBAAyB,CAAC,CAAC;IACpG,CAAC;SAAM,CAAC;QACP,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,KAAK,MAAM,UAAU,IAAI,yBAAyB,CAAC,UAAU,CAAC,EAAE,CAAC;QAChE,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,CAAY,CAAC;YACjD,IAAI,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;gBACrC,qBAAqB,GAAG,MAAM,CAAC;gBAC/B,OAAO,MAAM,CAAC;YACf,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,4CAA4C;QAC7C,CAAC;IACF,CAAC;IAED,OAAO,SAAS,CAAC;AAAA,CACjB;AAED,MAAM,UAAU,uBAAuB,CAAC,GAAgB,EAAW;IAClE,MAAM,MAAM,GAAG,yBAAyB,EAAE,CAAC;IAC3C,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1B,IAAI,CAAC;QACJ,OAAO,MAAM,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AAAA,CACD","sourcesContent":["import { createRequire } from \"node:module\";\nimport * as path from \"node:path\";\nimport { getNativeModuleCandidates } from \"./native-module-path.ts\";\n\nconst cjsRequire = createRequire(import.meta.url);\n\nexport type ModifierKey = \"shift\" | \"command\" | \"control\" | \"option\";\n\ntype NativeModifiersHelper = {\n\tisModifierPressed: (name: ModifierKey) => boolean;\n};\n\nlet nativeModifiersHelper: NativeModifiersHelper | null | undefined;\n\nfunction isNativeModifiersHelper(value: unknown): value is NativeModifiersHelper {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst candidate = (value as { isModifierPressed?: unknown }).isModifierPressed;\n\treturn typeof candidate === \"function\";\n}\n\nfunction loadNativeModifiersHelper(): NativeModifiersHelper | undefined {\n\tif (nativeModifiersHelper !== undefined) return nativeModifiersHelper ?? undefined;\n\tnativeModifiersHelper = null;\n\tconst arch = process.arch;\n\tif (arch !== \"x64\" && arch !== \"arm64\") return undefined;\n\n\tlet nativePath: string;\n\tif (process.platform === \"darwin\") {\n\t\tnativePath = path.join(\"native\", \"darwin\", \"prebuilds\", `darwin-${arch}`, \"darwin-modifiers.node\");\n\t} else if (process.platform === \"win32\") {\n\t\tnativePath = path.join(\"native\", \"win32\", \"prebuilds\", `win32-${arch}`, \"win32-console-mode.node\");\n\t} else {\n\t\treturn undefined;\n\t}\n\n\tfor (const modulePath of getNativeModuleCandidates(nativePath)) {\n\t\ttry {\n\t\t\tconst helper = cjsRequire(modulePath) as unknown;\n\t\t\tif (isNativeModifiersHelper(helper)) {\n\t\t\t\tnativeModifiersHelper = helper;\n\t\t\t\treturn helper;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Try the next possible packaging location.\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nexport function isNativeModifierPressed(key: ModifierKey): boolean {\n\tconst helper = loadNativeModifiersHelper();\n\tif (!helper) return false;\n\ttry {\n\t\treturn helper.isModifierPressed(key) === true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n"]}
@@ -0,0 +1,7 @@
1
+ export interface NativeModuleCandidateOptions {
2
+ moduleUrl?: string;
3
+ execPath?: string;
4
+ resolvePackage?: (specifier: string) => string;
5
+ }
6
+ export declare function getNativeModuleCandidates(nativePath: string, options?: NativeModuleCandidateOptions): string[];
7
+ //# sourceMappingURL=native-module-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-module-path.d.ts","sourceRoot":"","sources":["../src/native-module-path.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,4BAA4B;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;CAC/C;AAED,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,4BAAiC,GAAG,MAAM,EAAE,CAiBlH","sourcesContent":["import { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst moduleRequire = createRequire(import.meta.url);\nconst TUI_PACKAGE_NAME = \"@bramburn/qi-tui\";\n\nexport interface NativeModuleCandidateOptions {\n\tmoduleUrl?: string;\n\texecPath?: string;\n\tresolvePackage?: (specifier: string) => string;\n}\n\nexport function getNativeModuleCandidates(nativePath: string, options: NativeModuleCandidateOptions = {}): string[] {\n\tconst moduleDir = dirname(fileURLToPath(options.moduleUrl ?? import.meta.url));\n\tconst candidates: string[] = [];\n\n\ttry {\n\t\tconst packageEntry = (options.resolvePackage ?? moduleRequire.resolve)(TUI_PACKAGE_NAME);\n\t\tcandidates.push(join(dirname(packageEntry), \"..\", nativePath));\n\t} catch {\n\t\t// Standalone binaries do not have an installed TUI package.\n\t}\n\n\tcandidates.push(\n\t\tjoin(moduleDir, \"..\", nativePath),\n\t\tjoin(moduleDir, nativePath),\n\t\tjoin(dirname(options.execPath ?? process.execPath), nativePath),\n\t);\n\treturn Array.from(new Set(candidates));\n}\n"]}
@@ -0,0 +1,19 @@
1
+ import { createRequire } from "node:module";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const moduleRequire = createRequire(import.meta.url);
5
+ const TUI_PACKAGE_NAME = "@bramburn/qi-tui";
6
+ export function getNativeModuleCandidates(nativePath, options = {}) {
7
+ const moduleDir = dirname(fileURLToPath(options.moduleUrl ?? import.meta.url));
8
+ const candidates = [];
9
+ try {
10
+ const packageEntry = (options.resolvePackage ?? moduleRequire.resolve)(TUI_PACKAGE_NAME);
11
+ candidates.push(join(dirname(packageEntry), "..", nativePath));
12
+ }
13
+ catch {
14
+ // Standalone binaries do not have an installed TUI package.
15
+ }
16
+ candidates.push(join(moduleDir, "..", nativePath), join(moduleDir, nativePath), join(dirname(options.execPath ?? process.execPath), nativePath));
17
+ return Array.from(new Set(candidates));
18
+ }
19
+ //# sourceMappingURL=native-module-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-module-path.js","sourceRoot":"","sources":["../src/native-module-path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,aAAa,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AACrD,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAQ5C,MAAM,UAAU,yBAAyB,CAAC,UAAkB,EAAE,OAAO,GAAiC,EAAE,EAAY;IACnH,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,SAAS,IAAI,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/E,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,IAAI,CAAC;QACJ,MAAM,YAAY,GAAG,CAAC,OAAO,CAAC,cAAc,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC,gBAAgB,CAAC,CAAC;QACzF,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACR,4DAA4D;IAC7D,CAAC;IAED,UAAU,CAAC,IAAI,CACd,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,UAAU,CAAC,EACjC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAC3B,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC,CAC/D,CAAC;IACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;AAAA,CACvC","sourcesContent":["import { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst moduleRequire = createRequire(import.meta.url);\nconst TUI_PACKAGE_NAME = \"@bramburn/qi-tui\";\n\nexport interface NativeModuleCandidateOptions {\n\tmoduleUrl?: string;\n\texecPath?: string;\n\tresolvePackage?: (specifier: string) => string;\n}\n\nexport function getNativeModuleCandidates(nativePath: string, options: NativeModuleCandidateOptions = {}): string[] {\n\tconst moduleDir = dirname(fileURLToPath(options.moduleUrl ?? import.meta.url));\n\tconst candidates: string[] = [];\n\n\ttry {\n\t\tconst packageEntry = (options.resolvePackage ?? moduleRequire.resolve)(TUI_PACKAGE_NAME);\n\t\tcandidates.push(join(dirname(packageEntry), \"..\", nativePath));\n\t} catch {\n\t\t// Standalone binaries do not have an installed TUI package.\n\t}\n\n\tcandidates.push(\n\t\tjoin(moduleDir, \"..\", nativePath),\n\t\tjoin(moduleDir, nativePath),\n\t\tjoin(dirname(options.execPath ?? process.execPath), nativePath),\n\t);\n\treturn Array.from(new Set(candidates));\n}\n"]}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * StdinBuffer buffers input and emits complete sequences.
3
+ *
4
+ * This is necessary because stdin data events can arrive in partial chunks,
5
+ * especially for escape sequences like mouse events. Without buffering,
6
+ * partial sequences can be misinterpreted as regular keypresses.
7
+ *
8
+ * For example, the mouse SGR sequence `\x1b[<35;20;5m` might arrive as:
9
+ * - Event 1: `\x1b`
10
+ * - Event 2: `[<35`
11
+ * - Event 3: `;20;5m`
12
+ *
13
+ * The buffer accumulates these until a complete sequence is detected.
14
+ * Call the `process()` method to feed input data.
15
+ *
16
+ * Based on code from OpenTUI (https://github.com/anomalyco/opentui)
17
+ * MIT License - Copyright (c) 2025 opentui
18
+ */
19
+ import { EventEmitter } from "events";
20
+ export type StdinBufferOptions = {
21
+ /**
22
+ * Maximum time to wait for an incomplete sequence such as CSI or mouse
23
+ * (default: 50ms).
24
+ */
25
+ timeout?: number;
26
+ /**
27
+ * Maximum time to wait after a lone ESC before treating it as Escape
28
+ * (default: 10ms). Increase for high-latency Alt+key input (SSH).
29
+ */
30
+ escapeTimeout?: number;
31
+ };
32
+ export type StdinBufferEventMap = {
33
+ data: [string];
34
+ paste: [string];
35
+ };
36
+ /**
37
+ * Buffers stdin input and emits complete sequences via the 'data' event.
38
+ * Handles partial escape sequences that arrive across multiple chunks.
39
+ */
40
+ export declare class StdinBuffer extends EventEmitter<StdinBufferEventMap> {
41
+ private buffer;
42
+ private timeout;
43
+ private readonly timeoutMs;
44
+ private readonly escapeTimeoutMs;
45
+ private pasteMode;
46
+ private pasteBuffer;
47
+ private pendingKittyPrintableCodepoint;
48
+ constructor(options?: StdinBufferOptions);
49
+ process(data: string | Buffer): void;
50
+ private emitDataSequence;
51
+ flush(): string[];
52
+ clear(): void;
53
+ getBuffer(): string;
54
+ destroy(): void;
55
+ }
56
+ //# sourceMappingURL=stdin-buffer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdin-buffer.d.ts","sourceRoot":"","sources":["../src/stdin-buffer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AA+OtC,MAAM,MAAM,kBAAkB,GAAG;IAChC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IACjC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC;IACf,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC;CAChB,CAAC;AAEF;;;GAGG;AACH,qBAAa,WAAY,SAAQ,YAAY,CAAC,mBAAmB,CAAC;IACjE,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,OAAO,CAA8C;IAC7D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,8BAA8B,CAAqB;IAE3D,YAAY,OAAO,GAAE,kBAAuB,EAI3C;IAEM,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqG1C;IAED,OAAO,CAAC,gBAAgB;IAWxB,KAAK,IAAI,MAAM,EAAE,CAchB;IAED,KAAK,IAAI,IAAI,CASZ;IAED,SAAS,IAAI,MAAM,CAElB;IAED,OAAO,IAAI,IAAI,CAEd;CACD","sourcesContent":["/**\n * StdinBuffer buffers input and emits complete sequences.\n *\n * This is necessary because stdin data events can arrive in partial chunks,\n * especially for escape sequences like mouse events. Without buffering,\n * partial sequences can be misinterpreted as regular keypresses.\n *\n * For example, the mouse SGR sequence `\\x1b[<35;20;5m` might arrive as:\n * - Event 1: `\\x1b`\n * - Event 2: `[<35`\n * - Event 3: `;20;5m`\n *\n * The buffer accumulates these until a complete sequence is detected.\n * Call the `process()` method to feed input data.\n *\n * Based on code from OpenTUI (https://github.com/anomalyco/opentui)\n * MIT License - Copyright (c) 2025 opentui\n */\n\nimport { EventEmitter } from \"events\";\n\nconst ESC = \"\\x1b\";\nconst DEFAULT_SEQUENCE_TIMEOUT_MS = 50;\nconst DEFAULT_ESCAPE_TIMEOUT_MS = 10;\nconst BRACKETED_PASTE_START = \"\\x1b[200~\";\nconst BRACKETED_PASTE_END = \"\\x1b[201~\";\n\n/**\n * Check if a string is a complete escape sequence or needs more data\n */\nfunction isCompleteSequence(data: string): \"complete\" | \"incomplete\" | \"not-escape\" {\n\tif (!data.startsWith(ESC)) {\n\t\treturn \"not-escape\";\n\t}\n\n\tif (data.length === 1) {\n\t\treturn \"incomplete\";\n\t}\n\n\tconst afterEsc = data.slice(1);\n\n\t// CSI sequences: ESC [\n\tif (afterEsc.startsWith(\"[\")) {\n\t\t// Check for old-style mouse sequence: ESC[M + 3 bytes\n\t\tif (afterEsc.startsWith(\"[M\")) {\n\t\t\t// Old-style mouse needs ESC[M + 3 bytes = 6 total\n\t\t\treturn data.length >= 6 ? \"complete\" : \"incomplete\";\n\t\t}\n\t\treturn isCompleteCsiSequence(data);\n\t}\n\n\t// OSC sequences: ESC ]\n\tif (afterEsc.startsWith(\"]\")) {\n\t\treturn isCompleteOscSequence(data);\n\t}\n\n\t// DCS sequences: ESC P ... ESC \\ (includes XTVersion responses)\n\tif (afterEsc.startsWith(\"P\")) {\n\t\treturn isCompleteDcsSequence(data);\n\t}\n\n\t// APC sequences: ESC _ ... ESC \\ (includes Kitty graphics responses)\n\tif (afterEsc.startsWith(\"_\")) {\n\t\treturn isCompleteApcSequence(data);\n\t}\n\n\t// SS3 sequences: ESC O\n\tif (afterEsc.startsWith(\"O\")) {\n\t\t// ESC O followed by a single character\n\t\treturn afterEsc.length >= 2 ? \"complete\" : \"incomplete\";\n\t}\n\n\t// Meta key sequences: ESC followed by a single character\n\tif (afterEsc.length === 1) {\n\t\treturn \"complete\";\n\t}\n\n\t// Unknown escape sequence - treat as complete\n\treturn \"complete\";\n}\n\n/**\n * Check if CSI sequence is complete\n * CSI sequences: ESC [ ... followed by a final byte (0x40-0x7E)\n */\nfunction isCompleteCsiSequence(data: string): \"complete\" | \"incomplete\" {\n\tif (!data.startsWith(`${ESC}[`)) {\n\t\treturn \"complete\";\n\t}\n\n\t// Need at least ESC [ and one more character\n\tif (data.length < 3) {\n\t\treturn \"incomplete\";\n\t}\n\n\tconst payload = data.slice(2);\n\n\t// CSI sequences end with a byte in the range 0x40-0x7E (@-~)\n\t// This includes all letters and several special characters\n\tconst lastChar = payload[payload.length - 1];\n\tconst lastCharCode = lastChar.charCodeAt(0);\n\n\tif (lastCharCode >= 0x40 && lastCharCode <= 0x7e) {\n\t\t// Special handling for SGR mouse sequences\n\t\t// Format: ESC[<B;X;Ym or ESC[<B;X;YM\n\t\tif (payload.startsWith(\"<\")) {\n\t\t\t// Must have format: <digits;digits;digits[Mm]\n\t\t\tconst mouseMatch = /^<\\d+;\\d+;\\d+[Mm]$/.test(payload);\n\t\t\tif (mouseMatch) {\n\t\t\t\treturn \"complete\";\n\t\t\t}\n\t\t\t// If it ends with M or m but doesn't match the pattern, still incomplete\n\t\t\tif (lastChar === \"M\" || lastChar === \"m\") {\n\t\t\t\t// Check if we have the right structure\n\t\t\t\tconst parts = payload.slice(1, -1).split(\";\");\n\t\t\t\tif (parts.length === 3 && parts.every((p) => /^\\d+$/.test(p))) {\n\t\t\t\t\treturn \"complete\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn \"incomplete\";\n\t\t}\n\n\t\treturn \"complete\";\n\t}\n\n\treturn \"incomplete\";\n}\n\n/**\n * Check if OSC sequence is complete\n * OSC sequences: ESC ] ... ST (where ST is ESC \\ or BEL)\n */\nfunction isCompleteOscSequence(data: string): \"complete\" | \"incomplete\" {\n\tif (!data.startsWith(`${ESC}]`)) {\n\t\treturn \"complete\";\n\t}\n\n\t// OSC sequences end with ST (ESC \\) or BEL (\\x07)\n\tif (data.endsWith(`${ESC}\\\\`) || data.endsWith(\"\\x07\")) {\n\t\treturn \"complete\";\n\t}\n\n\treturn \"incomplete\";\n}\n\n/**\n * Check if DCS (Device Control String) sequence is complete\n * DCS sequences: ESC P ... ST (where ST is ESC \\)\n * Used for XTVersion responses like ESC P >| ... ESC \\\n */\nfunction isCompleteDcsSequence(data: string): \"complete\" | \"incomplete\" {\n\tif (!data.startsWith(`${ESC}P`)) {\n\t\treturn \"complete\";\n\t}\n\n\t// DCS sequences end with ST (ESC \\)\n\tif (data.endsWith(`${ESC}\\\\`)) {\n\t\treturn \"complete\";\n\t}\n\n\treturn \"incomplete\";\n}\n\n/**\n * Check if APC (Application Program Command) sequence is complete\n * APC sequences: ESC _ ... ST (where ST is ESC \\)\n * Used for Kitty graphics responses like ESC _ G ... ESC \\\n */\nfunction isCompleteApcSequence(data: string): \"complete\" | \"incomplete\" {\n\tif (!data.startsWith(`${ESC}_`)) {\n\t\treturn \"complete\";\n\t}\n\n\t// APC sequences end with ST (ESC \\)\n\tif (data.endsWith(`${ESC}\\\\`)) {\n\t\treturn \"complete\";\n\t}\n\n\treturn \"incomplete\";\n}\n\n/**\n * Split accumulated buffer into complete sequences\n */\nfunction parseUnmodifiedKittyPrintableCodepoint(sequence: string): number | undefined {\n\tconst match = sequence.match(/^\\x1b\\[(\\d+)(?::\\d*)?(?::\\d+)?u$/);\n\tif (!match) return undefined;\n\n\tconst codepoint = parseInt(match[1]!, 10);\n\treturn codepoint >= 32 ? codepoint : undefined;\n}\n\nfunction extractCompleteSequences(buffer: string): { sequences: string[]; remainder: string } {\n\tconst sequences: string[] = [];\n\tlet pos = 0;\n\n\twhile (pos < buffer.length) {\n\t\tconst remaining = buffer.slice(pos);\n\n\t\t// Try to extract a sequence starting at this position\n\t\tif (remaining.startsWith(ESC)) {\n\t\t\t// Find the end of this escape sequence\n\t\t\tlet seqEnd = 1;\n\t\t\twhile (seqEnd <= remaining.length) {\n\t\t\t\tconst candidate = remaining.slice(0, seqEnd);\n\t\t\t\tconst status = isCompleteSequence(candidate);\n\n\t\t\t\tif (status === \"complete\") {\n\t\t\t\t\t// WezTerm with enable_kitty_keyboard sends the Escape key press as a\n\t\t\t\t\t// raw '\\x1b' byte (simple text path in encode_kitty, ignoring\n\t\t\t\t\t// DISAMBIGUATE_ESCAPE_CODES) and the release as a full Kitty CSI-u\n\t\t\t\t\t// sequence. These arrive concatenated as '\\x1b\\x1b[27;...u'.\n\t\t\t\t\t// The buffer would normally treat '\\x1b\\x1b' as a complete meta-key\n\t\t\t\t\t// sequence (ESC + single char), leaving '[27;...u' to be typed as\n\t\t\t\t\t// plain text. If the character immediately following '\\x1b\\x1b'\n\t\t\t\t\t// would begin a new escape sequence, emit only the first ESC and\n\t\t\t\t\t// restart from the second.\n\t\t\t\t\tif (candidate === \"\\x1b\\x1b\") {\n\t\t\t\t\t\tconst nextChar = remaining[seqEnd];\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tnextChar === \"[\" || // CSI\n\t\t\t\t\t\t\tnextChar === \"]\" || // OSC\n\t\t\t\t\t\t\tnextChar === \"O\" || // SS3\n\t\t\t\t\t\t\tnextChar === \"P\" || // DCS\n\t\t\t\t\t\t\tnextChar === \"_\" // APC\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tsequences.push(ESC);\n\t\t\t\t\t\t\tpos += 1;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tsequences.push(candidate);\n\t\t\t\t\tpos += seqEnd;\n\t\t\t\t\tbreak;\n\t\t\t\t} else if (status === \"incomplete\") {\n\t\t\t\t\tseqEnd++;\n\t\t\t\t} else {\n\t\t\t\t\t// Should not happen when starting with ESC\n\t\t\t\t\tsequences.push(candidate);\n\t\t\t\t\tpos += seqEnd;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (seqEnd > remaining.length) {\n\t\t\t\treturn { sequences, remainder: remaining };\n\t\t\t}\n\t\t} else {\n\t\t\t// Not an escape sequence - take a single character\n\t\t\tsequences.push(remaining[0]!);\n\t\t\tpos++;\n\t\t}\n\t}\n\n\treturn { sequences, remainder: \"\" };\n}\n\nexport type StdinBufferOptions = {\n\t/**\n\t * Maximum time to wait for an incomplete sequence such as CSI or mouse\n\t * (default: 50ms).\n\t */\n\ttimeout?: number;\n\t/**\n\t * Maximum time to wait after a lone ESC before treating it as Escape\n\t * (default: 10ms). Increase for high-latency Alt+key input (SSH).\n\t */\n\tescapeTimeout?: number;\n};\n\nexport type StdinBufferEventMap = {\n\tdata: [string];\n\tpaste: [string];\n};\n\n/**\n * Buffers stdin input and emits complete sequences via the 'data' event.\n * Handles partial escape sequences that arrive across multiple chunks.\n */\nexport class StdinBuffer extends EventEmitter<StdinBufferEventMap> {\n\tprivate buffer: string = \"\";\n\tprivate timeout: ReturnType<typeof setTimeout> | null = null;\n\tprivate readonly timeoutMs: number;\n\tprivate readonly escapeTimeoutMs: number;\n\tprivate pasteMode: boolean = false;\n\tprivate pasteBuffer: string = \"\";\n\tprivate pendingKittyPrintableCodepoint: number | undefined;\n\n\tconstructor(options: StdinBufferOptions = {}) {\n\t\tsuper();\n\t\tthis.timeoutMs = options.timeout ?? DEFAULT_SEQUENCE_TIMEOUT_MS;\n\t\tthis.escapeTimeoutMs = options.escapeTimeout ?? DEFAULT_ESCAPE_TIMEOUT_MS;\n\t}\n\n\tpublic process(data: string | Buffer): void {\n\t\t// Clear any pending timeout\n\t\tif (this.timeout) {\n\t\t\tclearTimeout(this.timeout);\n\t\t\tthis.timeout = null;\n\t\t}\n\n\t\t// Handle high-byte conversion (for compatibility with parseKeypress)\n\t\t// If buffer has single byte > 127, convert to ESC + (byte - 128)\n\t\tlet str: string;\n\t\tif (Buffer.isBuffer(data)) {\n\t\t\tif (data.length === 1 && data[0]! > 127) {\n\t\t\t\tconst byte = data[0]! - 128;\n\t\t\t\tstr = `\\x1b${String.fromCharCode(byte)}`;\n\t\t\t} else {\n\t\t\t\tstr = data.toString();\n\t\t\t}\n\t\t} else {\n\t\t\tstr = data;\n\t\t}\n\n\t\tif (str.length === 0 && this.buffer.length === 0) {\n\t\t\tthis.emitDataSequence(\"\");\n\t\t\treturn;\n\t\t}\n\n\t\tthis.buffer += str;\n\n\t\tif (this.pasteMode) {\n\t\t\tthis.pasteBuffer += this.buffer;\n\t\t\tthis.buffer = \"\";\n\n\t\t\tconst endIndex = this.pasteBuffer.indexOf(BRACKETED_PASTE_END);\n\t\t\tif (endIndex !== -1) {\n\t\t\t\tconst pastedContent = this.pasteBuffer.slice(0, endIndex);\n\t\t\t\tconst remaining = this.pasteBuffer.slice(endIndex + BRACKETED_PASTE_END.length);\n\n\t\t\t\tthis.pasteMode = false;\n\t\t\t\tthis.pasteBuffer = \"\";\n\t\t\t\tthis.pendingKittyPrintableCodepoint = undefined;\n\n\t\t\t\tthis.emit(\"paste\", pastedContent);\n\n\t\t\t\tif (remaining.length > 0) {\n\t\t\t\t\tthis.process(remaining);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst startIndex = this.buffer.indexOf(BRACKETED_PASTE_START);\n\t\tif (startIndex !== -1) {\n\t\t\tif (startIndex > 0) {\n\t\t\t\tconst beforePaste = this.buffer.slice(0, startIndex);\n\t\t\t\tconst result = extractCompleteSequences(beforePaste);\n\t\t\t\tfor (const sequence of result.sequences) {\n\t\t\t\t\tthis.emitDataSequence(sequence);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.pendingKittyPrintableCodepoint = undefined;\n\t\t\tthis.buffer = this.buffer.slice(startIndex + BRACKETED_PASTE_START.length);\n\t\t\tthis.pasteMode = true;\n\t\t\tthis.pasteBuffer = this.buffer;\n\t\t\tthis.buffer = \"\";\n\n\t\t\tconst endIndex = this.pasteBuffer.indexOf(BRACKETED_PASTE_END);\n\t\t\tif (endIndex !== -1) {\n\t\t\t\tconst pastedContent = this.pasteBuffer.slice(0, endIndex);\n\t\t\t\tconst remaining = this.pasteBuffer.slice(endIndex + BRACKETED_PASTE_END.length);\n\n\t\t\t\tthis.pasteMode = false;\n\t\t\t\tthis.pasteBuffer = \"\";\n\t\t\t\tthis.pendingKittyPrintableCodepoint = undefined;\n\n\t\t\t\tthis.emit(\"paste\", pastedContent);\n\n\t\t\t\tif (remaining.length > 0) {\n\t\t\t\t\tthis.process(remaining);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst result = extractCompleteSequences(this.buffer);\n\t\tthis.buffer = result.remainder;\n\n\t\tfor (const sequence of result.sequences) {\n\t\t\tthis.emitDataSequence(sequence);\n\t\t}\n\n\t\tif (this.buffer.length > 0) {\n\t\t\tconst timeoutMs = this.buffer === ESC ? this.escapeTimeoutMs : this.timeoutMs;\n\t\t\tthis.timeout = setTimeout(() => {\n\t\t\t\tconst flushed = this.flush();\n\n\t\t\t\tfor (const sequence of flushed) {\n\t\t\t\t\tthis.emitDataSequence(sequence);\n\t\t\t\t}\n\t\t\t}, timeoutMs);\n\t\t}\n\t}\n\n\tprivate emitDataSequence(sequence: string): void {\n\t\tconst rawCodepoint = sequence.length === 1 ? sequence.codePointAt(0) : undefined;\n\t\tif (rawCodepoint !== undefined && rawCodepoint === this.pendingKittyPrintableCodepoint) {\n\t\t\tthis.pendingKittyPrintableCodepoint = undefined;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.pendingKittyPrintableCodepoint = parseUnmodifiedKittyPrintableCodepoint(sequence);\n\t\tthis.emit(\"data\", sequence);\n\t}\n\n\tflush(): string[] {\n\t\tif (this.timeout) {\n\t\t\tclearTimeout(this.timeout);\n\t\t\tthis.timeout = null;\n\t\t}\n\n\t\tif (this.buffer.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst sequences = [this.buffer];\n\t\tthis.buffer = \"\";\n\t\tthis.pendingKittyPrintableCodepoint = undefined;\n\t\treturn sequences;\n\t}\n\n\tclear(): void {\n\t\tif (this.timeout) {\n\t\t\tclearTimeout(this.timeout);\n\t\t\tthis.timeout = null;\n\t\t}\n\t\tthis.buffer = \"\";\n\t\tthis.pasteMode = false;\n\t\tthis.pasteBuffer = \"\";\n\t\tthis.pendingKittyPrintableCodepoint = undefined;\n\t}\n\n\tgetBuffer(): string {\n\t\treturn this.buffer;\n\t}\n\n\tdestroy(): void {\n\t\tthis.clear();\n\t}\n}\n"]}