@m4ike1/ion-tui 0.1.0

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 (196) hide show
  1. package/README.md +114 -0
  2. package/dist/alt-screen-search.d.ts +46 -0
  3. package/dist/alt-screen-search.d.ts.map +1 -0
  4. package/dist/alt-screen-search.js +282 -0
  5. package/dist/alt-screen-search.js.map +1 -0
  6. package/dist/autocomplete.d.ts +75 -0
  7. package/dist/autocomplete.d.ts.map +1 -0
  8. package/dist/autocomplete.js +749 -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 +24 -0
  15. package/dist/components/box.d.ts.map +1 -0
  16. package/dist/components/box.js +133 -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 +266 -0
  23. package/dist/components/editor.d.ts.map +1 -0
  24. package/dist/components/editor.js +2087 -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 +48 -0
  35. package/dist/components/input.d.ts.map +1 -0
  36. package/dist/components/input.js +417 -0
  37. package/dist/components/input.js.map +1 -0
  38. package/dist/components/loader.d.ts +33 -0
  39. package/dist/components/loader.d.ts.map +1 -0
  40. package/dist/components/loader.js +76 -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 +858 -0
  45. package/dist/components/markdown.js.map +1 -0
  46. package/dist/components/mouse-region.d.ts +12 -0
  47. package/dist/components/mouse-region.d.ts.map +1 -0
  48. package/dist/components/mouse-region.js +21 -0
  49. package/dist/components/mouse-region.js.map +1 -0
  50. package/dist/components/scroll-view.d.ts +68 -0
  51. package/dist/components/scroll-view.d.ts.map +1 -0
  52. package/dist/components/scroll-view.js +199 -0
  53. package/dist/components/scroll-view.js.map +1 -0
  54. package/dist/components/select-list.d.ts +53 -0
  55. package/dist/components/select-list.d.ts.map +1 -0
  56. package/dist/components/select-list.js +206 -0
  57. package/dist/components/select-list.js.map +1 -0
  58. package/dist/components/settings-list.d.ts +60 -0
  59. package/dist/components/settings-list.d.ts.map +1 -0
  60. package/dist/components/settings-list.js +252 -0
  61. package/dist/components/settings-list.js.map +1 -0
  62. package/dist/components/spacer.d.ts +12 -0
  63. package/dist/components/spacer.d.ts.map +1 -0
  64. package/dist/components/spacer.js +23 -0
  65. package/dist/components/spacer.js.map +1 -0
  66. package/dist/components/stack.d.ts +32 -0
  67. package/dist/components/stack.d.ts.map +1 -0
  68. package/dist/components/stack.js +111 -0
  69. package/dist/components/stack.js.map +1 -0
  70. package/dist/components/text.d.ts +19 -0
  71. package/dist/components/text.d.ts.map +1 -0
  72. package/dist/components/text.js +90 -0
  73. package/dist/components/text.js.map +1 -0
  74. package/dist/components/truncated-text.d.ts +13 -0
  75. package/dist/components/truncated-text.d.ts.map +1 -0
  76. package/dist/components/truncated-text.js +51 -0
  77. package/dist/components/truncated-text.js.map +1 -0
  78. package/dist/components/v-stack.d.ts +8 -0
  79. package/dist/components/v-stack.d.ts.map +1 -0
  80. package/dist/components/v-stack.js +26 -0
  81. package/dist/components/v-stack.js.map +1 -0
  82. package/dist/components/virtual-list.d.ts +51 -0
  83. package/dist/components/virtual-list.d.ts.map +1 -0
  84. package/dist/components/virtual-list.js +170 -0
  85. package/dist/components/virtual-list.js.map +1 -0
  86. package/dist/editor-component.d.ts +39 -0
  87. package/dist/editor-component.d.ts.map +1 -0
  88. package/dist/editor-component.js +2 -0
  89. package/dist/editor-component.js.map +1 -0
  90. package/dist/fuzzy.d.ts +16 -0
  91. package/dist/fuzzy.d.ts.map +1 -0
  92. package/dist/fuzzy.js +110 -0
  93. package/dist/fuzzy.js.map +1 -0
  94. package/dist/index.d.ts +34 -0
  95. package/dist/index.d.ts.map +1 -0
  96. package/dist/index.js +46 -0
  97. package/dist/index.js.map +1 -0
  98. package/dist/keybindings.d.ts +366 -0
  99. package/dist/keybindings.d.ts.map +1 -0
  100. package/dist/keybindings.js +331 -0
  101. package/dist/keybindings.js.map +1 -0
  102. package/dist/keys.d.ts +184 -0
  103. package/dist/keys.d.ts.map +1 -0
  104. package/dist/keys.js +1174 -0
  105. package/dist/keys.js.map +1 -0
  106. package/dist/kill-ring.d.ts +28 -0
  107. package/dist/kill-ring.d.ts.map +1 -0
  108. package/dist/kill-ring.js +44 -0
  109. package/dist/kill-ring.js.map +1 -0
  110. package/dist/latex.d.ts +10 -0
  111. package/dist/latex.d.ts.map +1 -0
  112. package/dist/latex.js +1285 -0
  113. package/dist/latex.js.map +1 -0
  114. package/dist/layout-node.d.ts +59 -0
  115. package/dist/layout-node.d.ts.map +1 -0
  116. package/dist/layout-node.js +12 -0
  117. package/dist/layout-node.js.map +1 -0
  118. package/dist/layout.d.ts +42 -0
  119. package/dist/layout.d.ts.map +1 -0
  120. package/dist/layout.js +404 -0
  121. package/dist/layout.js.map +1 -0
  122. package/dist/native-modifiers.d.ts +4 -0
  123. package/dist/native-modifiers.d.ts.map +1 -0
  124. package/dist/native-modifiers.js +13 -0
  125. package/dist/native-modifiers.js.map +1 -0
  126. package/dist/native-module-path.d.ts +7 -0
  127. package/dist/native-module-path.d.ts.map +1 -0
  128. package/dist/native-module-path.js +19 -0
  129. package/dist/native-module-path.js.map +1 -0
  130. package/dist/native-platform.d.ts +18 -0
  131. package/dist/native-platform.d.ts.map +1 -0
  132. package/dist/native-platform.js +42 -0
  133. package/dist/native-platform.js.map +1 -0
  134. package/dist/pty-host.d.ts +224 -0
  135. package/dist/pty-host.d.ts.map +1 -0
  136. package/dist/pty-host.js +347 -0
  137. package/dist/pty-host.js.map +1 -0
  138. package/dist/stdin-buffer.d.ts +56 -0
  139. package/dist/stdin-buffer.d.ts.map +1 -0
  140. package/dist/stdin-buffer.js +366 -0
  141. package/dist/stdin-buffer.js.map +1 -0
  142. package/dist/terminal-colors.d.ts +10 -0
  143. package/dist/terminal-colors.d.ts.map +1 -0
  144. package/dist/terminal-colors.js +59 -0
  145. package/dist/terminal-colors.js.map +1 -0
  146. package/dist/terminal-image.d.ts +118 -0
  147. package/dist/terminal-image.d.ts.map +1 -0
  148. package/dist/terminal-image.js +550 -0
  149. package/dist/terminal-image.js.map +1 -0
  150. package/dist/terminal.d.ts +123 -0
  151. package/dist/terminal.d.ts.map +1 -0
  152. package/dist/terminal.js +442 -0
  153. package/dist/terminal.js.map +1 -0
  154. package/dist/tui-alt-screen.d.ts +165 -0
  155. package/dist/tui-alt-screen.d.ts.map +1 -0
  156. package/dist/tui-alt-screen.js +1515 -0
  157. package/dist/tui-alt-screen.js.map +1 -0
  158. package/dist/tui-main-screen.d.ts +39 -0
  159. package/dist/tui-main-screen.d.ts.map +1 -0
  160. package/dist/tui-main-screen.js +594 -0
  161. package/dist/tui-main-screen.js.map +1 -0
  162. package/dist/tui.d.ts +431 -0
  163. package/dist/tui.d.ts.map +1 -0
  164. package/dist/tui.js +1142 -0
  165. package/dist/tui.js.map +1 -0
  166. package/dist/undo-stack.d.ts +17 -0
  167. package/dist/undo-stack.d.ts.map +1 -0
  168. package/dist/undo-stack.js +25 -0
  169. package/dist/undo-stack.js.map +1 -0
  170. package/dist/utils.d.ts +98 -0
  171. package/dist/utils.d.ts.map +1 -0
  172. package/dist/utils.js +1200 -0
  173. package/dist/utils.js.map +1 -0
  174. package/dist/word-navigation.d.ts +25 -0
  175. package/dist/word-navigation.d.ts.map +1 -0
  176. package/dist/word-navigation.js +96 -0
  177. package/dist/word-navigation.js.map +1 -0
  178. package/native/clipboard.h +170 -0
  179. package/native/darwin/README.md +22 -0
  180. package/native/darwin/build.sh +69 -0
  181. package/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
  182. package/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
  183. package/native/darwin/src/darwin-platform.m +93 -0
  184. package/native/linux/README.md +23 -0
  185. package/native/linux/build.sh +65 -0
  186. package/native/linux/prebuilds/linux-arm64/linux-platform-x11.node +0 -0
  187. package/native/linux/prebuilds/linux-x64/linux-platform-x11.node +0 -0
  188. package/native/linux/src/clipboard-worker.h +70 -0
  189. package/native/linux/src/linux-platform-x11.c +274 -0
  190. package/native/napi.h +89 -0
  191. package/native/win32/README.md +31 -0
  192. package/native/win32/build.mjs +232 -0
  193. package/native/win32/prebuilds/win32-arm64/win32-platform.node +0 -0
  194. package/native/win32/prebuilds/win32-x64/win32-platform.node +0 -0
  195. package/native/win32/src/win32-platform.c +241 -0
  196. package/package.json +62 -0
package/dist/layout.js ADDED
@@ -0,0 +1,404 @@
1
+ import { allocateStackSizes, visibleStackEntries } from "./components/stack.js";
2
+ import { getLayoutNode, isVirtualizedContent } from "./layout-node.js";
3
+ import { cropKittyImageLine, getKittyImageMetadata, isImageLine } from "./terminal-image.js";
4
+ import { CURSOR_MARKER, compositeTuiLine } from "./tui.js";
5
+ import { extractAnsiCode, getActiveBackgroundAnsi, 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
+ /**
49
+ * Viewport-budgeted paint for virtualized transcript content. Only the
50
+ * visible rows plus the scroll view overscan are pulled from the per-message
51
+ * caches; the full model stays available as scrollContentLines so search,
52
+ * selection, and the scrollbar keep covering every message.
53
+ */
54
+ function layoutVirtualScrollView(context, node, x, y, safeWidth, contentWidth, height, clip) {
55
+ const virtual = node.component;
56
+ if (!isVirtualizedContent(virtual))
57
+ throw new Error("layoutVirtualScrollView requires virtualized content");
58
+ const viewportHeight = Math.max(0, Math.floor(height));
59
+ const contentHeight = virtual.getTotalHeight(contentWidth);
60
+ node.state.updateLayout(contentHeight, viewportHeight, context.requestRender);
61
+ const scrollTop = node.state.scrollTop;
62
+ const overscan = Math.max(0, Math.floor(node.state.overscan));
63
+ const windowStart = Math.max(0, scrollTop - overscan);
64
+ const windowEnd = Math.min(contentHeight, scrollTop + viewportHeight + overscan);
65
+ const windowLines = virtual.getWindowLines(contentWidth, windowStart, windowEnd);
66
+ const scrollView = node.state;
67
+ if (node.state.primary || !context.primaryScrollView)
68
+ context.primaryScrollView = scrollView;
69
+ const rect = { x, y, width: safeWidth, height: viewportHeight };
70
+ const childClip = intersect(clip, rect);
71
+ const target = {
72
+ render: () => windowLines,
73
+ invalidate: () => { },
74
+ handleMouse: (event) => virtual.handleWindowMouseEvent?.(windowStart, event),
75
+ };
76
+ const box = {
77
+ component: node.component,
78
+ rect,
79
+ clip: childClip,
80
+ children: [],
81
+ scrollView,
82
+ scrollContentLines: virtual.getContentLines(contentWidth),
83
+ layer: 0,
84
+ };
85
+ const childBox = {
86
+ component: target,
87
+ rect: { x, y: y - scrollTop + windowStart, width: contentWidth, height: Math.max(0, windowEnd - windowStart) },
88
+ clip: intersect(childClip, {
89
+ x,
90
+ y: y - scrollTop + windowStart,
91
+ width: contentWidth,
92
+ height: Math.max(0, windowEnd - windowStart),
93
+ }),
94
+ children: [],
95
+ parent: box,
96
+ lines: windowLines,
97
+ lineOffset: 0,
98
+ layer: 0,
99
+ };
100
+ box.children.push(childBox);
101
+ return box;
102
+ }
103
+ function layoutComponent(context, component, x, y, width, height, clip) {
104
+ const safeWidth = Math.max(1, Math.floor(width));
105
+ const node = getLayoutNode(component);
106
+ if (!node) {
107
+ const lines = renderCached(context, component, safeWidth);
108
+ const allocatedHeight = height === undefined ? lines.length : Math.max(0, Math.floor(height));
109
+ let lineOffset = 0;
110
+ if (lines.length > allocatedHeight && allocatedHeight > 0) {
111
+ const cursorLine = lines.findIndex((line) => line.includes(CURSOR_MARKER));
112
+ if (cursorLine >= allocatedHeight)
113
+ lineOffset = cursorLine - allocatedHeight + 1;
114
+ }
115
+ return {
116
+ component,
117
+ rect: { x, y, width: safeWidth, height: allocatedHeight },
118
+ clip: intersect(clip, { x, y, width: safeWidth, height: allocatedHeight }),
119
+ children: [],
120
+ lines,
121
+ lineOffset,
122
+ layer: 0,
123
+ };
124
+ }
125
+ if (node.type === "scroll") {
126
+ const contentWidth = node.state.getContentWidth(safeWidth);
127
+ if (height !== undefined && isVirtualizedContent(node.component)) {
128
+ return layoutVirtualScrollView(context, node, x, y, safeWidth, contentWidth, height, clip);
129
+ }
130
+ const previousScrollTop = node.state.scrollTop;
131
+ const childBox = layoutComponent(context, node.component, x, y - previousScrollTop, contentWidth, undefined, clip);
132
+ const contentHeight = childBox.rect.height;
133
+ const viewportHeight = height === undefined ? contentHeight : Math.max(0, Math.floor(height));
134
+ node.state.updateLayout(contentHeight, viewportHeight, context.requestRender);
135
+ translateBox(childBox, previousScrollTop - node.state.scrollTop);
136
+ const scrollView = node.state;
137
+ if (node.state.primary || !context.primaryScrollView)
138
+ context.primaryScrollView = scrollView;
139
+ const rect = { x, y, width: safeWidth, height: viewportHeight };
140
+ const childClip = intersect(clip, rect);
141
+ const box = {
142
+ component,
143
+ rect,
144
+ clip: childClip,
145
+ children: [childBox],
146
+ scrollView,
147
+ scrollContentLines: renderCached(context, node.component, contentWidth),
148
+ layer: 0,
149
+ };
150
+ childBox.parent = box;
151
+ updateClips(childBox, childClip);
152
+ return box;
153
+ }
154
+ const entries = visibleStackEntries(node.entries, context.viewport);
155
+ const gapTotal = Math.max(0, entries.length - 1) * node.gap;
156
+ if (node.type === "vstack") {
157
+ const intrinsicHeights = entries.map((entry) => typeof entry.basis === "number" ? entry.basis : measureHeight(context, entry.component, safeWidth));
158
+ const sizes = allocateStackSizes(entries, intrinsicHeights, height, node.gap);
159
+ const naturalHeight = sizes.reduce((sum, size) => sum + size, 0) + gapTotal;
160
+ const allocatedHeight = height === undefined ? naturalHeight : Math.max(0, Math.floor(height));
161
+ const rect = { x, y, width: safeWidth, height: allocatedHeight };
162
+ const box = {
163
+ component,
164
+ rect,
165
+ clip: intersect(clip, rect),
166
+ children: [],
167
+ layer: 0,
168
+ };
169
+ let childY = y;
170
+ for (let index = 0; index < entries.length; index++) {
171
+ box.children.push(withParent(layoutComponent(context, entries[index].component, x, childY, safeWidth, sizes[index], box.clip), box));
172
+ childY += sizes[index] + node.gap;
173
+ }
174
+ return box;
175
+ }
176
+ const intrinsicWidths = entries.map((entry) => typeof entry.basis === "number" ? entry.basis : measureWidth(context, entry.component, safeWidth));
177
+ const widths = allocateStackSizes(entries, intrinsicWidths, safeWidth, node.gap);
178
+ const intrinsicHeights = entries.map((entry, index) => measureHeight(context, entry.component, Math.max(1, widths[index])));
179
+ const allocatedHeight = height === undefined
180
+ ? intrinsicHeights.reduce((max, childHeight) => Math.max(max, childHeight), 0)
181
+ : Math.max(0, height);
182
+ const rect = { x, y, width: safeWidth, height: allocatedHeight };
183
+ const box = {
184
+ component,
185
+ rect,
186
+ clip: intersect(clip, rect),
187
+ children: [],
188
+ layer: 0,
189
+ };
190
+ let childX = x;
191
+ for (let index = 0; index < entries.length; index++) {
192
+ const naturalChildHeight = intrinsicHeights[index];
193
+ const childHeight = node.align === "stretch" ? allocatedHeight : Math.min(allocatedHeight, naturalChildHeight);
194
+ let childY = y;
195
+ if (node.align === "center")
196
+ childY += Math.floor((allocatedHeight - childHeight) / 2);
197
+ else if (node.align === "end")
198
+ childY += allocatedHeight - childHeight;
199
+ const childWidth = widths[index];
200
+ if (childWidth === 0) {
201
+ box.children.push({
202
+ component: entries[index].component,
203
+ rect: { x: childX, y: childY, width: 0, height: childHeight },
204
+ clip: { x: childX, y: childY, width: 0, height: 0 },
205
+ children: [],
206
+ parent: box,
207
+ layer: 0,
208
+ });
209
+ }
210
+ else {
211
+ box.children.push(withParent(layoutComponent(context, entries[index].component, childX, childY, childWidth, childHeight, box.clip), box));
212
+ }
213
+ childX += childWidth + node.gap;
214
+ }
215
+ return box;
216
+ }
217
+ function replaceScrollbarCell(line, column, totalWidth, replacement, preserveTargetBackground) {
218
+ if (isImageLine(line))
219
+ return line;
220
+ const graphemeRange = getGraphemeCellRange(line, column);
221
+ const start = graphemeRange?.start ?? column;
222
+ const end = graphemeRange?.end ?? column + 1;
223
+ const before = sliceByColumn(line, 0, start, true);
224
+ const target = sliceByColumn(line, start, end - start, true);
225
+ const after = sliceByColumn(line, end, Math.max(0, totalWidth - end), true);
226
+ let targetPrefix = "";
227
+ let targetIndex = 0;
228
+ while (targetIndex < target.length) {
229
+ const ansi = extractAnsiCode(target, targetIndex);
230
+ if (!ansi)
231
+ break;
232
+ targetPrefix += ansi.code;
233
+ targetIndex += ansi.length;
234
+ }
235
+ const beforePadding = " ".repeat(Math.max(0, start - visibleWidth(before)));
236
+ const cellPaddingBefore = " ".repeat(Math.max(0, column - start));
237
+ const cellPaddingAfter = " ".repeat(Math.max(0, end - column - 1));
238
+ const targetStyle = `\x1b[0m\x1b]8;;\x07${preserveTargetBackground ? getActiveBackgroundAnsi(targetPrefix) : ""}`;
239
+ return `${before}${beforePadding}${targetStyle}${cellPaddingBefore}${replacement}${cellPaddingAfter}${after}`;
240
+ }
241
+ export function getScrollbarGeometry(box, includeHiddenAuto = false) {
242
+ if (!box.scrollView || box.rect.width <= 0 || box.rect.height <= 0)
243
+ return undefined;
244
+ // Prefer the full model line count: windowed scroll children only span the
245
+ // painted viewport window, while scrollContentLines always covers every row.
246
+ const contentHeight = box.scrollContentLines?.length ?? box.children[0]?.rect.height ?? 0;
247
+ const trackHeight = box.rect.height;
248
+ const canRevealHiddenAuto = includeHiddenAuto && box.scrollView.scrollbar === "auto" && contentHeight > trackHeight;
249
+ if (!box.scrollView.isScrollbarVisible && !canRevealHiddenAuto)
250
+ return undefined;
251
+ const minThumbHeight = Math.min(2, trackHeight);
252
+ const thumbHeight = Math.max(minThumbHeight, Math.min(trackHeight, Math.round((trackHeight * trackHeight) / contentHeight)));
253
+ const maxScrollTop = Math.max(0, contentHeight - trackHeight);
254
+ const maxThumbTop = trackHeight - thumbHeight;
255
+ const thumbOffset = maxScrollTop === 0 ? 0 : Math.round((box.scrollView.scrollTop / maxScrollTop) * maxThumbTop);
256
+ const column = box.rect.x + box.rect.width - 1;
257
+ if (column < box.clip.x || column >= box.clip.x + box.clip.width)
258
+ return undefined;
259
+ return {
260
+ column,
261
+ trackTop: box.rect.y,
262
+ trackHeight,
263
+ thumbTop: box.rect.y + thumbOffset,
264
+ thumbHeight,
265
+ maxScrollTop,
266
+ };
267
+ }
268
+ function paintScrollbar(box, screen, totalWidth) {
269
+ const geometry = getScrollbarGeometry(box);
270
+ if (!geometry || !box.scrollView)
271
+ return;
272
+ for (let offset = 0; offset < geometry.trackHeight; offset++) {
273
+ const row = geometry.trackTop + offset;
274
+ if (row < box.clip.y || row >= box.clip.y + box.clip.height || row < 0 || row >= screen.length)
275
+ continue;
276
+ const isThumb = row >= geometry.thumbTop && row < geometry.thumbTop + geometry.thumbHeight;
277
+ const replacement = isThumb
278
+ ? box.scrollView.scrollbarThumbStyle(box.scrollView.isScrollbarActive ? "█" : "┃")
279
+ : box.scrollView.scrollbarTrackStyle("│");
280
+ screen[row] = replaceScrollbarCell(screen[row] ?? "", geometry.column, totalWidth, replacement, box.scrollView.scrollbar !== "always");
281
+ }
282
+ }
283
+ function paintBox(box, screen, totalWidth) {
284
+ if (box.lines) {
285
+ const offset = box.lineOffset ?? 0;
286
+ const firstRow = Math.max(box.rect.y, box.clip.y, 0);
287
+ const lastRow = Math.min(box.rect.y + box.rect.height, box.clip.y + box.clip.height, screen.length);
288
+ for (let row = firstRow; row < lastRow; row++) {
289
+ const sourceLine = box.lines[offset + row - box.rect.y];
290
+ if (sourceLine === undefined)
291
+ continue;
292
+ let line = sourceLine.replace(OSC133_ZONE_PREFIX, "");
293
+ const imageMetadata = getKittyImageMetadata(line);
294
+ if (imageMetadata) {
295
+ const clipBottom = Math.min(screen.length, box.clip.y + box.clip.height);
296
+ const visibleRows = Math.min(imageMetadata.rows, clipBottom - row);
297
+ if (visibleRows < imageMetadata.rows)
298
+ line = cropKittyImageLine(line, 0, visibleRows);
299
+ }
300
+ // Fast path: a full-width box painting onto an untouched row can use the
301
+ // source line reference directly. Compositing here would rebuild the row
302
+ // string through ANSI/grapheme segmentation every frame; padding is
303
+ // unnecessary because rows are written with erase-line and the final
304
+ // width clamp still truncates over-wide lines.
305
+ if (box.rect.x === 0 && box.rect.width >= totalWidth && (isImageLine(line) || !screen[row])) {
306
+ screen[row] = line;
307
+ }
308
+ else {
309
+ screen[row] = compositeTuiLine(screen[row] ?? "", line, box.rect.x, box.rect.width, totalWidth);
310
+ }
311
+ }
312
+ }
313
+ for (const child of box.children)
314
+ paintBox(child, screen, totalWidth);
315
+ if (box.scrollView && box.scrollContentLines && box.scrollView.scrollTop > 0 && box.rect.height > 0) {
316
+ for (let imageRow = box.scrollView.scrollTop - 1; imageRow >= 0; imageRow--) {
317
+ const imageLine = box.scrollContentLines[imageRow] ?? "";
318
+ const metadata = getKittyImageMetadata(imageLine);
319
+ if (metadata) {
320
+ const hiddenRows = box.scrollView.scrollTop - imageRow;
321
+ if (hiddenRows < metadata.rows) {
322
+ const visibleRows = Math.min(box.rect.height, metadata.rows - hiddenRows);
323
+ const cropped = cropKittyImageLine(imageLine, hiddenRows, visibleRows);
324
+ if (box.rect.x === 0 && box.rect.width >= totalWidth)
325
+ screen[box.rect.y] = cropped;
326
+ }
327
+ break;
328
+ }
329
+ if (imageLine !== "")
330
+ break;
331
+ }
332
+ }
333
+ paintScrollbar(box, screen, totalWidth);
334
+ }
335
+ export function renderLayoutFrame(root, width, height, requestRender) {
336
+ const safeWidth = Math.max(1, Math.floor(width));
337
+ const safeHeight = Math.max(1, Math.floor(height));
338
+ const context = {
339
+ viewport: { width: safeWidth, height: safeHeight },
340
+ renderCache: new Map(),
341
+ requestRender,
342
+ primaryScrollView: undefined,
343
+ };
344
+ const rootBox = layoutComponent(context, root, 0, 0, safeWidth, safeHeight, {
345
+ x: 0,
346
+ y: 0,
347
+ width: safeWidth,
348
+ height: safeHeight,
349
+ });
350
+ const lines = Array.from({ length: safeHeight }, () => "");
351
+ paintBox(rootBox, lines, safeWidth);
352
+ return {
353
+ root: rootBox,
354
+ width: safeWidth,
355
+ height: safeHeight,
356
+ lines,
357
+ ...(context.primaryScrollView === undefined ? {} : { primaryScrollView: context.primaryScrollView }),
358
+ };
359
+ }
360
+ function containsPoint(rect, x, y) {
361
+ return x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height;
362
+ }
363
+ /** Return the visual hit path from the deepest component to the layout root. */
364
+ export function getLayoutBoxesAt(frame, x, y) {
365
+ const result = [];
366
+ const visit = (box, depth) => {
367
+ if (!containsPoint(box.clip, x, y))
368
+ return;
369
+ result.push({ box, depth });
370
+ for (const child of box.children)
371
+ visit(child, depth + 1);
372
+ };
373
+ visit(frame.root, 0);
374
+ result.sort((a, b) => b.box.layer - a.box.layer || b.depth - a.depth);
375
+ return result.map(({ box }) => box);
376
+ }
377
+ export function getScrollViewBox(frame, scrollView) {
378
+ const visit = (box) => {
379
+ if (box.scrollView === scrollView)
380
+ return box;
381
+ for (const child of box.children) {
382
+ const match = visit(child);
383
+ if (match)
384
+ return match;
385
+ }
386
+ return undefined;
387
+ };
388
+ return visit(frame.root);
389
+ }
390
+ export function getScrollViewsAt(frame, x, y) {
391
+ const result = [];
392
+ const visit = (box, depth) => {
393
+ if (!containsPoint(box.clip, x, y))
394
+ return;
395
+ if (box.scrollView && containsPoint(box.rect, x, y))
396
+ result.push({ scrollView: box.scrollView, depth });
397
+ for (const child of box.children)
398
+ visit(child, depth + 1);
399
+ };
400
+ visit(frame.root, 0);
401
+ result.sort((a, b) => b.depth - a.depth);
402
+ return result.map((entry) => entry.scrollView);
403
+ }
404
+ //# 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, isVirtualizedContent, type ScrollLayoutNode } from \"./layout-node.ts\";\nimport { cropKittyImageLine, getKittyImageMetadata, isImageLine } from \"./terminal-image.ts\";\nimport { type Component, CURSOR_MARKER, compositeTuiLine } from \"./tui.ts\";\nimport {\n\textractAnsiCode,\n\tgetActiveBackgroundAnsi,\n\tgetGraphemeCellRange,\n\tsliceByColumn,\n\tvisibleWidth,\n} 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\n/**\n * Viewport-budgeted paint for virtualized transcript content. Only the\n * visible rows plus the scroll view overscan are pulled from the per-message\n * caches; the full model stays available as scrollContentLines so search,\n * selection, and the scrollbar keep covering every message.\n */\nfunction layoutVirtualScrollView(\n\tcontext: LayoutContext,\n\tnode: ScrollLayoutNode,\n\tx: number,\n\ty: number,\n\tsafeWidth: number,\n\tcontentWidth: number,\n\theight: number,\n\tclip: LayoutRect,\n): LayoutBox {\n\tconst virtual = node.component;\n\tif (!isVirtualizedContent(virtual)) throw new Error(\"layoutVirtualScrollView requires virtualized content\");\n\tconst viewportHeight = Math.max(0, Math.floor(height));\n\tconst contentHeight = virtual.getTotalHeight(contentWidth);\n\tnode.state.updateLayout(contentHeight, viewportHeight, context.requestRender);\n\tconst scrollTop = node.state.scrollTop;\n\tconst overscan = Math.max(0, Math.floor(node.state.overscan));\n\tconst windowStart = Math.max(0, scrollTop - overscan);\n\tconst windowEnd = Math.min(contentHeight, scrollTop + viewportHeight + overscan);\n\tconst windowLines = virtual.getWindowLines(contentWidth, windowStart, windowEnd);\n\tconst scrollView = node.state as ScrollView;\n\tif (node.state.primary || !context.primaryScrollView) context.primaryScrollView = scrollView;\n\tconst rect = { x, y, width: safeWidth, height: viewportHeight };\n\tconst childClip = intersect(clip, rect);\n\tconst target: Component = {\n\t\trender: () => windowLines,\n\t\tinvalidate: () => {},\n\t\thandleMouse: (event) => virtual.handleWindowMouseEvent?.(windowStart, event),\n\t};\n\tconst box: LayoutBox = {\n\t\tcomponent: node.component,\n\t\trect,\n\t\tclip: childClip,\n\t\tchildren: [],\n\t\tscrollView,\n\t\tscrollContentLines: virtual.getContentLines(contentWidth),\n\t\tlayer: 0,\n\t};\n\tconst childBox: LayoutBox = {\n\t\tcomponent: target,\n\t\trect: { x, y: y - scrollTop + windowStart, width: contentWidth, height: Math.max(0, windowEnd - windowStart) },\n\t\tclip: intersect(childClip, {\n\t\t\tx,\n\t\t\ty: y - scrollTop + windowStart,\n\t\t\twidth: contentWidth,\n\t\t\theight: Math.max(0, windowEnd - windowStart),\n\t\t}),\n\t\tchildren: [],\n\t\tparent: box,\n\t\tlines: windowLines,\n\t\tlineOffset: 0,\n\t\tlayer: 0,\n\t};\n\tbox.children.push(childBox);\n\treturn box;\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 contentWidth = node.state.getContentWidth(safeWidth);\n\t\tif (height !== undefined && isVirtualizedContent(node.component)) {\n\t\t\treturn layoutVirtualScrollView(context, node, x, y, safeWidth, contentWidth, height, clip);\n\t\t}\n\t\tconst previousScrollTop = node.state.scrollTop;\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 replaceScrollbarCell(\n\tline: string,\n\tcolumn: number,\n\ttotalWidth: number,\n\treplacement: string,\n\tpreserveTargetBackground: boolean,\n): 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 beforePadding = \" \".repeat(Math.max(0, start - visibleWidth(before)));\n\tconst cellPaddingBefore = \" \".repeat(Math.max(0, column - start));\n\tconst cellPaddingAfter = \" \".repeat(Math.max(0, end - column - 1));\n\tconst targetStyle = `\\x1b[0m\\x1b]8;;\\x07${preserveTargetBackground ? getActiveBackgroundAnsi(targetPrefix) : \"\"}`;\n\treturn `${before}${beforePadding}${targetStyle}${cellPaddingBefore}${replacement}${cellPaddingAfter}${after}`;\n}\n\nexport function getScrollbarGeometry(box: LayoutBox, includeHiddenAuto = false): ScrollbarGeometry | undefined {\n\tif (!box.scrollView || box.rect.width <= 0 || box.rect.height <= 0) return undefined;\n\n\t// Prefer the full model line count: windowed scroll children only span the\n\t// painted viewport window, while scrollContentLines always covers every row.\n\tconst contentHeight = box.scrollContentLines?.length ?? box.children[0]?.rect.height ?? 0;\n\tconst trackHeight = box.rect.height;\n\tconst canRevealHiddenAuto = includeHiddenAuto && box.scrollView.scrollbar === \"auto\" && contentHeight > trackHeight;\n\tif (!box.scrollView.isScrollbarVisible && !canRevealHiddenAuto) return undefined;\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.trackHeight; offset++) {\n\t\tconst row = geometry.trackTop + offset;\n\t\tif (row < box.clip.y || row >= box.clip.y + box.clip.height || row < 0 || row >= screen.length) continue;\n\t\tconst isThumb = row >= geometry.thumbTop && row < geometry.thumbTop + geometry.thumbHeight;\n\t\tconst replacement = isThumb\n\t\t\t? box.scrollView.scrollbarThumbStyle(box.scrollView.isScrollbarActive ? \"█\" : \"┃\")\n\t\t\t: box.scrollView.scrollbarTrackStyle(\"│\");\n\t\tscreen[row] = replaceScrollbarCell(\n\t\t\tscreen[row] ?? \"\",\n\t\t\tgeometry.column,\n\t\t\ttotalWidth,\n\t\t\treplacement,\n\t\t\tbox.scrollView.scrollbar !== \"always\",\n\t\t);\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\n/** Return the visual hit path from the deepest component to the layout root. */\nexport function getLayoutBoxesAt(frame: LayoutFrame, x: number, y: number): LayoutBox[] {\n\tconst result: Array<{ box: LayoutBox; depth: number }> = [];\n\tconst visit = (box: LayoutBox, depth: number): void => {\n\t\tif (!containsPoint(box.clip, x, y)) return;\n\t\tresult.push({ box, 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.box.layer - a.box.layer || b.depth - a.depth);\n\treturn result.map(({ box }) => box);\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,4 @@
1
+ import { type ModifierKey } from "./native-platform.ts";
2
+ export type { ModifierKey } from "./native-platform.ts";
3
+ export declare function isNativeModifierPressed(key: ModifierKey): boolean;
4
+ //# 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":"AAAA,OAAO,EAA2B,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEjF,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAQjE","sourcesContent":["import { getNativePlatformHelper, type ModifierKey } from \"./native-platform.ts\";\n\nexport type { ModifierKey } from \"./native-platform.ts\";\n\nexport function isNativeModifierPressed(key: ModifierKey): boolean {\n\tconst helper = getNativePlatformHelper();\n\tif (!helper?.isModifierPressed) 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,13 @@
1
+ import { getNativePlatformHelper } from "./native-platform.js";
2
+ export function isNativeModifierPressed(key) {
3
+ const helper = getNativePlatformHelper();
4
+ if (!helper?.isModifierPressed)
5
+ return false;
6
+ try {
7
+ return helper.isModifierPressed(key) === true;
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ }
13
+ //# 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,uBAAuB,EAAoB,MAAM,sBAAsB,CAAC;AAIjF,MAAM,UAAU,uBAAuB,CAAC,GAAgB,EAAW;IAClE,MAAM,MAAM,GAAG,uBAAuB,EAAE,CAAC;IACzC,IAAI,CAAC,MAAM,EAAE,iBAAiB;QAAE,OAAO,KAAK,CAAC;IAC7C,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 { getNativePlatformHelper, type ModifierKey } from \"./native-platform.ts\";\n\nexport type { ModifierKey } from \"./native-platform.ts\";\n\nexport function isNativeModifierPressed(key: ModifierKey): boolean {\n\tconst helper = getNativePlatformHelper();\n\tif (!helper?.isModifierPressed) 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 = \"@m4ike1/ion-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 = "@m4ike1/ion-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,iBAAiB,CAAC;AAQ3C,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 = \"@m4ike1/ion-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,18 @@
1
+ export type ModifierKey = "shift" | "command" | "control" | "option";
2
+ export interface NativeClipboard {
3
+ /** Undefined means unavailable, null means no text; transfer failures reject. */
4
+ getText(): Promise<string | null | undefined>;
5
+ /** Undefined means unavailable, null means no image; transfer failures reject. */
6
+ getImage(): Promise<Uint8Array | null | undefined>;
7
+ /** Linux uses command-line tools to retain clipboard ownership instead. */
8
+ setText?(text: string): Promise<void>;
9
+ }
10
+ type NativePlatformHelper = NativeClipboard & {
11
+ enableVirtualTerminalInput?: () => boolean;
12
+ isModifierPressed?: (name: ModifierKey) => boolean;
13
+ };
14
+ export declare function getNativePlatformHelper(): NativePlatformHelper | undefined;
15
+ /** Load a clipboard helper without opening the display until a read is requested. */
16
+ export declare function getNativeClipboard(): NativeClipboard | undefined;
17
+ export {};
18
+ //# sourceMappingURL=native-platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-platform.d.ts","sourceRoot":"","sources":["../src/native-platform.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAErE,MAAM,WAAW,eAAe;IAC/B,iFAAiF;IACjF,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IAC9C,kFAAkF;IAClF,QAAQ,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACnD,2EAA2E;IAC3E,OAAO,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAED,KAAK,oBAAoB,GAAG,eAAe,GAAG;IAC7C,0BAA0B,CAAC,EAAE,MAAM,OAAO,CAAC;IAC3C,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC;CACnD,CAAC;AAgCF,wBAAgB,uBAAuB,IAAI,oBAAoB,GAAG,SAAS,CAG1E;AAED,qFAAqF;AACrF,wBAAgB,kBAAkB,IAAI,eAAe,GAAG,SAAS,CAIhE","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\nexport interface NativeClipboard {\n\t/** Undefined means unavailable, null means no text; transfer failures reject. */\n\tgetText(): Promise<string | null | undefined>;\n\t/** Undefined means unavailable, null means no image; transfer failures reject. */\n\tgetImage(): Promise<Uint8Array | null | undefined>;\n\t/** Linux uses command-line tools to retain clipboard ownership instead. */\n\tsetText?(text: string): Promise<void>;\n}\n\ntype NativePlatformHelper = NativeClipboard & {\n\tenableVirtualTerminalInput?: () => boolean;\n\tisModifierPressed?: (name: ModifierKey) => boolean;\n};\n\n// Cache module loading, not display availability: a disconnected display can recover.\nconst helpers = new Map<string, NativePlatformHelper | undefined>();\n\nfunction loadNativePlatformHelper(platform: string, suffix = \"\"): NativePlatformHelper | undefined {\n\tconst arch = process.arch;\n\tif (arch !== \"x64\" && arch !== \"arm64\") return undefined;\n\tconst nativePath = path.join(\n\t\t\"native\",\n\t\tplatform,\n\t\t\"prebuilds\",\n\t\t`${platform}-${arch}`,\n\t\t`${platform}-platform${suffix}.node`,\n\t);\n\tif (helpers.has(nativePath)) return helpers.get(nativePath);\n\n\tfor (const modulePath of getNativeModuleCandidates(nativePath)) {\n\t\ttry {\n\t\t\tconst helper = cjsRequire(modulePath) as Partial<NativePlatformHelper> | null;\n\t\t\tif (typeof helper?.getText === \"function\" && typeof helper.getImage === \"function\") {\n\t\t\t\thelpers.set(nativePath, helper as NativePlatformHelper);\n\t\t\t\treturn helper as NativePlatformHelper;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Try the next possible packaging location.\n\t\t}\n\t}\n\thelpers.set(nativePath, undefined);\n\treturn undefined;\n}\n\nexport function getNativePlatformHelper(): NativePlatformHelper | undefined {\n\tif (process.platform !== \"darwin\" && process.platform !== \"win32\") return undefined;\n\treturn loadNativePlatformHelper(process.platform);\n}\n\n/** Load a clipboard helper without opening the display until a read is requested. */\nexport function getNativeClipboard(): NativeClipboard | undefined {\n\tif (process.platform !== \"linux\") return getNativePlatformHelper();\n\tif (!process.env.DISPLAY) return undefined;\n\treturn loadNativePlatformHelper(\"linux\", \"-x11\");\n}\n"]}
@@ -0,0 +1,42 @@
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
+ // Cache module loading, not display availability: a disconnected display can recover.
6
+ const helpers = new Map();
7
+ function loadNativePlatformHelper(platform, suffix = "") {
8
+ const arch = process.arch;
9
+ if (arch !== "x64" && arch !== "arm64")
10
+ return undefined;
11
+ const nativePath = path.join("native", platform, "prebuilds", `${platform}-${arch}`, `${platform}-platform${suffix}.node`);
12
+ if (helpers.has(nativePath))
13
+ return helpers.get(nativePath);
14
+ for (const modulePath of getNativeModuleCandidates(nativePath)) {
15
+ try {
16
+ const helper = cjsRequire(modulePath);
17
+ if (typeof helper?.getText === "function" && typeof helper.getImage === "function") {
18
+ helpers.set(nativePath, helper);
19
+ return helper;
20
+ }
21
+ }
22
+ catch {
23
+ // Try the next possible packaging location.
24
+ }
25
+ }
26
+ helpers.set(nativePath, undefined);
27
+ return undefined;
28
+ }
29
+ export function getNativePlatformHelper() {
30
+ if (process.platform !== "darwin" && process.platform !== "win32")
31
+ return undefined;
32
+ return loadNativePlatformHelper(process.platform);
33
+ }
34
+ /** Load a clipboard helper without opening the display until a read is requested. */
35
+ export function getNativeClipboard() {
36
+ if (process.platform !== "linux")
37
+ return getNativePlatformHelper();
38
+ if (!process.env.DISPLAY)
39
+ return undefined;
40
+ return loadNativePlatformHelper("linux", "-x11");
41
+ }
42
+ //# sourceMappingURL=native-platform.js.map