@valyrianjs/terminal 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ansi.d.ts +2 -0
- package/dist/ansi.d.ts.map +1 -1
- package/dist/ansi.js +23 -13
- package/dist/ansi.js.map +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +10 -2
- package/dist/events.js.map +1 -1
- package/dist/frame-style.d.ts +7 -0
- package/dist/frame-style.d.ts.map +1 -0
- package/dist/frame-style.js +27 -0
- package/dist/frame-style.js.map +1 -0
- package/dist/keymap.d.ts.map +1 -1
- package/dist/keymap.js +4 -2
- package/dist/keymap.js.map +1 -1
- package/dist/layout.d.ts +5 -1
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +55 -24
- package/dist/layout.js.map +1 -1
- package/dist/mouse.d.ts +6 -0
- package/dist/mouse.d.ts.map +1 -1
- package/dist/mouse.js +38 -17
- package/dist/mouse.js.map +1 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +8 -1
- package/dist/primitives.js.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +266 -70
- package/dist/render.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +13 -5
- package/dist/runtime.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +325 -83
- package/dist/session.js.map +1 -1
- package/dist/text.d.ts +7 -0
- package/dist/text.d.ts.map +1 -1
- package/dist/text.js +114 -0
- package/dist/text.js.map +1 -1
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +3 -0
- package/dist/theme.js.map +1 -1
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +18 -4
- package/dist/tree.js.map +1 -1
- package/dist/types.d.ts +41 -4
- package/dist/types.d.ts.map +1 -1
- package/docs/api-reference.md +18 -8
- package/docs/cookbook.md +1 -1
- package/docs/interaction-model.md +10 -8
- package/docs/primitive-gallery.md +9 -5
- package/examples/basic.tsx +22 -0
- package/examples/cli.tsx +55 -0
- package/examples/demo.tsx +98 -0
- package/examples/docs/background-fill.tsx +107 -0
- package/examples/docs/component-composition.tsx +140 -0
- package/examples/docs/cursor.tsx +121 -0
- package/examples/docs/employees-list.tsx +138 -0
- package/examples/docs/hello.tsx +98 -0
- package/examples/docs/interactive-note.tsx +111 -0
- package/examples/docs/module-api-dashboard.tsx +307 -0
- package/examples/docs/module-flux-store.tsx +181 -0
- package/examples/docs/module-form-workflow.tsx +339 -0
- package/examples/docs/module-forms.tsx +218 -0
- package/examples/docs/module-money.tsx +175 -0
- package/examples/docs/module-native-store.tsx +188 -0
- package/examples/docs/module-pulses.tsx +142 -0
- package/examples/docs/module-query.tsx +209 -0
- package/examples/docs/module-request.tsx +194 -0
- package/examples/docs/module-state-workbench.tsx +283 -0
- package/examples/docs/module-tasks.tsx +223 -0
- package/examples/docs/module-translate.tsx +194 -0
- package/examples/docs/module-utils.tsx +168 -0
- package/examples/docs/module-valyrian-core.tsx +159 -0
- package/examples/docs/pizza-builder.tsx +463 -0
- package/examples/docs/primitive-activity-console.tsx +113 -0
- package/examples/docs/primitive-command-panel.tsx +186 -0
- package/examples/docs/primitive-data-explorer.tsx +155 -0
- package/examples/docs/primitive-input-workbench.tsx +128 -0
- package/examples/docs/primitive-layout-shell.tsx +115 -0
- package/examples/docs/responsive-split.tsx +186 -0
- package/examples/docs/style-system.tsx +209 -0
- package/examples/docs/theme-colors.tsx +225 -0
- package/examples/docs/virtualized-list-workbench.tsx +232 -0
- package/examples/opencode-dogfood-app.tsx +215 -0
- package/examples/opencode-dogfood-lifecycle.tsx +194 -0
- package/examples/opencode-dogfood.tsx +11 -0
- package/llms-full.txt +38 -22
- package/package.json +3 -2
- package/src/ansi.ts +23 -13
- package/src/events.ts +6 -2
- package/src/frame-style.ts +36 -0
- package/src/keymap.ts +4 -2
- package/src/layout.ts +59 -25
- package/src/mouse.ts +41 -16
- package/src/primitives.ts +8 -1
- package/src/render.ts +286 -71
- package/src/runtime.ts +13 -5
- package/src/session.ts +343 -79
- package/src/text.ts +148 -0
- package/src/theme.ts +3 -0
- package/src/tree.ts +19 -4
- package/src/types.ts +48 -3
package/dist/render.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { constrainFrame, createFrame, cropFrame, fitFrame, getFrameHeight, getFrameWidth, mergeHorizontal, mergeVertical, overlayFrame, shiftFrame } from "./layout.js";
|
|
2
2
|
import { getSelectionRange, normalizeInputState } from "./events.js";
|
|
3
|
+
import { markFullFrameSpan, markFullRowSpan } from "./frame-style.js";
|
|
3
4
|
import { createEditorState } from "./editor-state.js";
|
|
4
5
|
import { isFocusable, textContent } from "./tree.js";
|
|
5
6
|
import { renderValyrianTerminal } from "./runtime.js";
|
|
6
|
-
import { plainText } from "./text.js";
|
|
7
|
+
import { cursorCellOffset, padEndTerminalCells, plainText, sliceTerminalCells, terminalCellToStringIndex, terminalCellWidth, terminalGraphemes } from "./text.js";
|
|
7
8
|
import { resolveTerminalStyle } from "./theme.js";
|
|
8
9
|
function validateRenderContextDimension(name, value) {
|
|
9
10
|
if (!Number.isInteger(value) || value < 1) {
|
|
@@ -148,7 +149,7 @@ function fullFrameSpans(kinds, width, height) {
|
|
|
148
149
|
const spans = [];
|
|
149
150
|
for (const kind of kinds) {
|
|
150
151
|
for (let y = 1; y <= height; y += 1) {
|
|
151
|
-
spans.push({ kind, x1: 1, x2: width + 1, y });
|
|
152
|
+
spans.push(markFullFrameSpan({ kind, x1: 1, x2: width + 1, y }));
|
|
152
153
|
}
|
|
153
154
|
}
|
|
154
155
|
return spans;
|
|
@@ -184,7 +185,7 @@ function padFrameSides(frame, padding) {
|
|
|
184
185
|
const bottomLines = new Array(padding.bottom).fill(" ".repeat(contentWidth));
|
|
185
186
|
const lines = [
|
|
186
187
|
...topLines,
|
|
187
|
-
...frame.lines.map((line) => `${" ".repeat(padding.left)}${line
|
|
188
|
+
...frame.lines.map((line) => `${" ".repeat(padding.left)}${padEndTerminalCells(line, width)}${" ".repeat(padding.right)}`),
|
|
188
189
|
...bottomLines
|
|
189
190
|
];
|
|
190
191
|
return shiftFrame(createFrame(lines, frame.hitboxes, frame.cursor, frame.spans), padding.left, padding.top);
|
|
@@ -202,7 +203,7 @@ function addBorder(frame, border) {
|
|
|
202
203
|
lines.push(`${border.left ? chars.topLeft : chars.horizontal}${chars.horizontal.repeat(Math.max(0, width - left - right))}${border.right ? chars.topRight : chars.horizontal}`.slice(0, width));
|
|
203
204
|
}
|
|
204
205
|
for (const line of frame.lines) {
|
|
205
|
-
lines.push(`${border.left ? chars.vertical : ""}${line
|
|
206
|
+
lines.push(`${border.left ? chars.vertical : ""}${padEndTerminalCells(line, innerWidth)}${border.right ? chars.vertical : ""}`);
|
|
206
207
|
}
|
|
207
208
|
if (border.bottom) {
|
|
208
209
|
lines.push(`${border.left ? chars.bottomLeft : chars.horizontal}${chars.horizontal.repeat(Math.max(0, width - left - right))}${border.right ? chars.bottomRight : chars.horizontal}`.slice(0, width));
|
|
@@ -226,26 +227,115 @@ function addFullFrameSpans(frame, kinds) {
|
|
|
226
227
|
const spans = frame.spans.slice();
|
|
227
228
|
for (const span of kinds) {
|
|
228
229
|
for (let y = 1; y <= height; y += 1) {
|
|
229
|
-
spans.push({ ...span, x1: 1, x2: width + 1, y });
|
|
230
|
+
spans.push(markFullFrameSpan({ ...span, x1: 1, x2: width + 1, y }));
|
|
230
231
|
}
|
|
231
232
|
}
|
|
232
233
|
return createFrame(frame.lines, frame.hitboxes, frame.cursor, spans);
|
|
233
234
|
}
|
|
234
|
-
function
|
|
235
|
+
function listViewportRows(node, itemCount, context) {
|
|
236
|
+
const explicitHeight = positiveDimension(node.props.height, "height");
|
|
237
|
+
const viewportSourceRows = explicitHeight ?? context?.rows ?? (itemCount || 1);
|
|
238
|
+
return Math.max(1, Math.min(itemCount || 1, positiveInteger(viewportSourceRows, "List viewport height")));
|
|
239
|
+
}
|
|
240
|
+
function clampListIndex(index, itemCount) {
|
|
241
|
+
if (itemCount <= 0) {
|
|
242
|
+
return 0;
|
|
243
|
+
}
|
|
244
|
+
return Math.max(0, Math.min(itemCount - 1, index));
|
|
245
|
+
}
|
|
246
|
+
function listVirtualRange(node, itemCount, context) {
|
|
235
247
|
if (!node.props.virtualized) {
|
|
236
|
-
return { start: 0, end: itemCount };
|
|
248
|
+
return { start: 0, end: itemCount, visibleStart: 0, viewportRows: itemCount || 1 };
|
|
237
249
|
}
|
|
238
250
|
if (typeof node.props.itemHeight !== "undefined" && node.props.itemHeight !== 1) {
|
|
239
251
|
throw new RangeError("List itemHeight must be 1");
|
|
240
252
|
}
|
|
241
253
|
const overscan = nonNegativeInteger(node.props.overscan, "List overscan");
|
|
242
|
-
const
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
const visibleStart = Math.max(0, Math.min(selected, selected - viewportRows + 1));
|
|
254
|
+
const viewportRows = listViewportRows(node, itemCount, context);
|
|
255
|
+
const maxOffset = Math.max(0, itemCount - viewportRows);
|
|
256
|
+
let visibleStart = Math.max(0, Math.min(maxOffset, nonNegativeInteger(node.props.__scrollOffset, "List viewport offset")));
|
|
246
257
|
const start = Math.max(0, visibleStart - overscan);
|
|
247
258
|
const end = Math.min(itemCount, visibleStart + viewportRows + overscan);
|
|
248
|
-
return { start, end };
|
|
259
|
+
return { start, end, visibleStart, viewportRows };
|
|
260
|
+
}
|
|
261
|
+
function listItemKey(node, item, index) {
|
|
262
|
+
if (typeof node.props.itemKey === "function") {
|
|
263
|
+
const key = node.props.itemKey(item, index);
|
|
264
|
+
if (typeof key !== "string" && typeof key !== "number") {
|
|
265
|
+
throw new RangeError("List itemKey must return a string or number");
|
|
266
|
+
}
|
|
267
|
+
return String(key);
|
|
268
|
+
}
|
|
269
|
+
return String(index);
|
|
270
|
+
}
|
|
271
|
+
function listItemRenderer(node) {
|
|
272
|
+
if (typeof node.props.__childrenRenderer === "function") {
|
|
273
|
+
return { type: "children", render: node.props.__childrenRenderer };
|
|
274
|
+
}
|
|
275
|
+
if (typeof node.props.renderItem === "function") {
|
|
276
|
+
return { type: "renderItem", render: node.props.renderItem };
|
|
277
|
+
}
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
function wrapPlainText(value, width) {
|
|
281
|
+
if (!Number.isFinite(width) || !Number.isInteger(width) || width <= 0) {
|
|
282
|
+
return [""];
|
|
283
|
+
}
|
|
284
|
+
const rows = [];
|
|
285
|
+
const sourceRows = value.split("\n");
|
|
286
|
+
for (const sourceRow of sourceRows) {
|
|
287
|
+
if (sourceRow.length === 0) {
|
|
288
|
+
rows.push("");
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
let remaining = sourceRow;
|
|
292
|
+
while (terminalCellWidth(remaining) > width) {
|
|
293
|
+
const slice = sliceTerminalCells(remaining, width);
|
|
294
|
+
if (slice.length === 0) {
|
|
295
|
+
const [firstGrapheme = ""] = terminalGraphemes(remaining);
|
|
296
|
+
rows.push(firstGrapheme);
|
|
297
|
+
remaining = remaining.slice(firstGrapheme.length);
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
const breakAt = slice.lastIndexOf(" ");
|
|
301
|
+
if (breakAt > 0 && breakAt >= Math.floor(width * 0.6)) {
|
|
302
|
+
rows.push(remaining.slice(0, breakAt));
|
|
303
|
+
remaining = remaining.slice(breakAt + 1);
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
rows.push(slice);
|
|
307
|
+
remaining = remaining.slice(slice.length);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
rows.push(remaining);
|
|
311
|
+
}
|
|
312
|
+
return rows.length ? rows : [""];
|
|
313
|
+
}
|
|
314
|
+
function renderListItemFrame(node, item, index, viewportIndex, activeIndex, selectedIndex, wrapWidth, context) {
|
|
315
|
+
const key = listItemKey(node, item, index);
|
|
316
|
+
const renderer = listItemRenderer(node);
|
|
317
|
+
if (!renderer) {
|
|
318
|
+
const label = plainText(item);
|
|
319
|
+
return createFrame(node.props.wrap === true ? wrapPlainText(label, wrapWidth) : label.split("\n"));
|
|
320
|
+
}
|
|
321
|
+
const ctx = {
|
|
322
|
+
index,
|
|
323
|
+
key,
|
|
324
|
+
active: index === activeIndex,
|
|
325
|
+
selected: selectedIndex !== null && index === selectedIndex,
|
|
326
|
+
viewportIndex,
|
|
327
|
+
item
|
|
328
|
+
};
|
|
329
|
+
const rendered = renderer.type === "children" ? renderer.render(item, ctx) : renderer.render(item, index);
|
|
330
|
+
if (typeof rendered === "string" || typeof rendered === "number") {
|
|
331
|
+
const label = plainText(rendered);
|
|
332
|
+
return createFrame(node.props.wrap === true ? wrapPlainText(label, wrapWidth) : label.split("\n"));
|
|
333
|
+
}
|
|
334
|
+
const frame = mergeVertical(renderValyrianTerminal(rendered).map((child) => renderTerminalFrame(child, context)));
|
|
335
|
+
if (node.props.wrap === true && frame.hitboxes.length === 0) {
|
|
336
|
+
return createFrame(frame.lines.flatMap((line) => wrapPlainText(line, wrapWidth)));
|
|
337
|
+
}
|
|
338
|
+
return frame;
|
|
249
339
|
}
|
|
250
340
|
function fixedPosition(value) {
|
|
251
341
|
if (value === "top" || value === "bottom" || value === "left" || value === "right") {
|
|
@@ -329,7 +419,8 @@ function decorateContainerFrame(frame, node, options = {}, context) {
|
|
|
329
419
|
if ((typeof width === "number" && width > 0) || (typeof height === "number" && height > 0)) {
|
|
330
420
|
next = constrainFrame(next, {
|
|
331
421
|
width: typeof width === "number" && width > 0 ? width : undefined,
|
|
332
|
-
height: typeof height === "number" && height > 0 ? height : undefined
|
|
422
|
+
height: typeof height === "number" && height > 0 ? height : undefined,
|
|
423
|
+
expandFullFrameSpans: true
|
|
333
424
|
});
|
|
334
425
|
}
|
|
335
426
|
}
|
|
@@ -338,7 +429,8 @@ function decorateContainerFrame(frame, node, options = {}, context) {
|
|
|
338
429
|
if (options.constrain) {
|
|
339
430
|
next = constrainFrame(next, {
|
|
340
431
|
width: typeof options.width === "undefined" ? positiveDimension(node.props.width, "width") : options.width,
|
|
341
|
-
height: typeof options.height === "undefined" ? positiveDimension(node.props.height, "height") : options.height
|
|
432
|
+
height: typeof options.height === "undefined" ? positiveDimension(node.props.height, "height") : options.height,
|
|
433
|
+
expandFullFrameSpans: true
|
|
342
434
|
});
|
|
343
435
|
}
|
|
344
436
|
next = addContainerStyleSpans(next, node, resolved);
|
|
@@ -390,6 +482,16 @@ function resolveContainerChildContext(node, dimensions, context) {
|
|
|
390
482
|
theme: context?.theme
|
|
391
483
|
};
|
|
392
484
|
}
|
|
485
|
+
function interactiveTextMetadata(value) {
|
|
486
|
+
const textLength = value.length;
|
|
487
|
+
const textCellToStringIndex = terminalCellToStringIndex(value);
|
|
488
|
+
const usesLinearIndexes = textCellToStringIndex.length === textLength + 1
|
|
489
|
+
&& textCellToStringIndex.every((index, cellOffset) => index === cellOffset);
|
|
490
|
+
if (usesLinearIndexes) {
|
|
491
|
+
return { textLength };
|
|
492
|
+
}
|
|
493
|
+
return { textLength, textCellToStringIndex };
|
|
494
|
+
}
|
|
393
495
|
function renderInputLine(value, inputState, padding = { top: 0, right: 0, bottom: 0, left: 0 }) {
|
|
394
496
|
const state = normalizeInputState(inputState, value.length);
|
|
395
497
|
const { start, end } = getSelectionRange(state);
|
|
@@ -398,11 +500,27 @@ function renderInputLine(value, inputState, padding = { top: 0, right: 0, bottom
|
|
|
398
500
|
const textStart = padding.left + 1;
|
|
399
501
|
return {
|
|
400
502
|
line: paddedLine,
|
|
401
|
-
cursor: { x: textStart + state.cursor, y: 1 },
|
|
402
|
-
spans: start === end ? [] : [{ kind: "input.selection", x1: textStart + start, x2: textStart + end, y: 1 }]
|
|
503
|
+
cursor: { x: textStart + cursorCellOffset(value, state.cursor), y: 1 },
|
|
504
|
+
spans: start === end ? [] : [{ kind: "input.selection", x1: textStart + cursorCellOffset(value, start), x2: textStart + cursorCellOffset(value, end), y: 1 }]
|
|
403
505
|
};
|
|
404
506
|
}
|
|
405
|
-
function
|
|
507
|
+
function resolveEditorDimensions(node, context) {
|
|
508
|
+
const explicitWidth = positiveDimension(node.props.width, "width");
|
|
509
|
+
const explicitHeight = positiveDimension(node.props.height, "height");
|
|
510
|
+
return {
|
|
511
|
+
width: typeof explicitWidth !== "undefined"
|
|
512
|
+
? explicitWidth
|
|
513
|
+
: node.props.fill === true
|
|
514
|
+
? fillContextDimension(context, "width", "Editor")
|
|
515
|
+
: undefined,
|
|
516
|
+
height: typeof explicitHeight !== "undefined"
|
|
517
|
+
? explicitHeight
|
|
518
|
+
: node.props.fill === true
|
|
519
|
+
? fillContextDimension(context, "height", "Editor")
|
|
520
|
+
: undefined
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
function renderEditorFrame(node, context) {
|
|
406
524
|
const value = typeof node.props.value !== "undefined" ? plainText(node.props.value) : "";
|
|
407
525
|
const placeholder = typeof node.props.placeholder !== "undefined" ? plainText(node.props.placeholder) : "";
|
|
408
526
|
const displayValue = value.length === 0 && !node.props.__focused && placeholder ? placeholder : value;
|
|
@@ -416,21 +534,26 @@ function renderEditorFrame(node) {
|
|
|
416
534
|
}
|
|
417
535
|
return `> ${line.slice(0, focusedColumn)}|${line.slice(focusedColumn)}`;
|
|
418
536
|
});
|
|
419
|
-
const width = lines.reduce((max, line) => Math.max(max, line
|
|
420
|
-
const cursor = node.props.__focused ? { x: 3 + focusedColumn, y: focusedLine + 1 } : null;
|
|
421
|
-
const spans = node.props.__focused ? [{ kind: "focus", x1: 1, x2: Math.max(2, lines[focusedLine]
|
|
422
|
-
const
|
|
537
|
+
const width = lines.reduce((max, line) => Math.max(max, terminalCellWidth(line)), 0);
|
|
538
|
+
const cursor = node.props.__focused ? { x: 3 + cursorCellOffset(focusedState.lines[focusedLine], focusedColumn), y: focusedLine + 1 } : null;
|
|
539
|
+
const spans = node.props.__focused ? [{ kind: "focus", x1: 1, x2: Math.max(2, terminalCellWidth(lines[focusedLine]) + 1), y: focusedLine + 1 }] : [];
|
|
540
|
+
const dimensions = resolveEditorDimensions(node, context);
|
|
423
541
|
const frame = createFrame(lines, [], cursor, spans);
|
|
424
|
-
const scrollOffset = node.props.__focused && typeof height !== "undefined"
|
|
425
|
-
? Math.min(Math.max(0, focusedLine - height + 1), Math.max(0, lines.length - height))
|
|
542
|
+
const scrollOffset = node.props.__focused && typeof dimensions.height !== "undefined"
|
|
543
|
+
? Math.min(Math.max(0, focusedLine - dimensions.height + 1), Math.max(0, lines.length - dimensions.height))
|
|
426
544
|
: 0;
|
|
427
|
-
const
|
|
545
|
+
const croppedFrame = typeof dimensions.height === "undefined"
|
|
428
546
|
? frame
|
|
429
|
-
:
|
|
547
|
+
: scrollOffset > 0
|
|
548
|
+
? cropFrame(frame, scrollOffset, dimensions.height)
|
|
549
|
+
: frame;
|
|
550
|
+
const constrainedFrame = typeof dimensions.width === "undefined" && typeof dimensions.height === "undefined"
|
|
551
|
+
? croppedFrame
|
|
552
|
+
: constrainFrame(croppedFrame, { ...dimensions, expandFullFrameSpans: true });
|
|
430
553
|
if (!node.props.id) {
|
|
431
554
|
return constrainedFrame;
|
|
432
555
|
}
|
|
433
|
-
return createFrame(constrainedFrame.lines, [{ id: node.props.id, tag: node.tag, x1: 1, x2: Math.max(1, getFrameWidth(constrainedFrame)
|
|
556
|
+
return createFrame(constrainedFrame.lines, [{ id: node.props.id, tag: node.tag, x1: 1, x2: Math.max(1, getFrameWidth(constrainedFrame)), y1: 1, y2: getFrameHeight(constrainedFrame), textStartX: 3, ...interactiveTextMetadata(value) }], constrainedFrame.cursor, constrainedFrame.spans);
|
|
434
557
|
}
|
|
435
558
|
function notifyLayoutContextProbe(node, context) {
|
|
436
559
|
if (context && typeof node.props.__layoutContextProbe === "function") {
|
|
@@ -460,12 +583,11 @@ function renderTableFrame(node, context) {
|
|
|
460
583
|
const cells = new Array(columnCount).fill(createFrame([""])).map((cell, index) => row[index] || cell);
|
|
461
584
|
const rowHeight = cells.reduce((max, cell) => Math.max(max, getFrameHeight(cell)), 0);
|
|
462
585
|
const normalized = cells.map((cell, index) => ({
|
|
463
|
-
frame: cell,
|
|
464
|
-
width: columnWidths[index]
|
|
465
|
-
lines: [...cell.lines.map((line) => line.padEnd(columnWidths[index], " ")), ...new Array(Math.max(0, rowHeight - getFrameHeight(cell))).fill(" ".repeat(columnWidths[index]))]
|
|
586
|
+
frame: fitFrame(cell, columnWidths[index], rowHeight, { expandFullFrameSpans: true }),
|
|
587
|
+
width: columnWidths[index]
|
|
466
588
|
}));
|
|
467
589
|
for (let rowIndex = 0; rowIndex < rowHeight; rowIndex += 1) {
|
|
468
|
-
lines.push(normalized.map((cell) => cell.lines[rowIndex]).join(" | "));
|
|
590
|
+
lines.push(normalized.map((cell) => cell.frame.lines[rowIndex]).join(" | "));
|
|
469
591
|
}
|
|
470
592
|
let xOffset = 0;
|
|
471
593
|
for (let index = 0; index < normalized.length; index += 1) {
|
|
@@ -611,7 +733,7 @@ function renderSplitFrame(node, context) {
|
|
|
611
733
|
if (cellWidth <= 0 || cellHeight <= 0) {
|
|
612
734
|
continue;
|
|
613
735
|
}
|
|
614
|
-
frames.push(constrainFrame(renderSplitChildFrame(node.children[index], cellWidth, cellHeight, context), { width: cellWidth, height: cellHeight }));
|
|
736
|
+
frames.push(constrainFrame(renderSplitChildFrame(node.children[index], cellWidth, cellHeight, context), { width: cellWidth, height: cellHeight, expandFullFrameSpans: true }));
|
|
615
737
|
}
|
|
616
738
|
const frame = frames.length
|
|
617
739
|
? direction === "row" ? mergeHorizontal(frames, { gap }) : mergeVertical(frames, { gap })
|
|
@@ -645,7 +767,7 @@ function renderBodyFrame(children, props, context) {
|
|
|
645
767
|
return direction === "row" ? mergeHorizontal(frames, { gap }) : mergeVertical(frames, { gap });
|
|
646
768
|
}
|
|
647
769
|
function renderFixedChildFrame(node, width, height) {
|
|
648
|
-
return constrainFrame(renderBodyFrame(node.children, {}, { cols: width, rows: height }), { width, height });
|
|
770
|
+
return constrainFrame(renderBodyFrame(node.children, {}, { cols: width, rows: height }), { width, height, expandFullFrameSpans: true });
|
|
649
771
|
}
|
|
650
772
|
function renderFixedCompositionFrame(node, width, height) {
|
|
651
773
|
if (!Number.isFinite(width) || !Number.isInteger(width) || width <= 0 || !Number.isFinite(height) || !Number.isInteger(height) || height <= 0) {
|
|
@@ -683,12 +805,12 @@ function renderFixedCompositionFrame(node, width, height) {
|
|
|
683
805
|
const leftFrames = fixedNodes.left.map((fixed) => renderFixedChildFrame(fixed, positiveInteger(fixed.props.size, "Fixed size"), middleHeight));
|
|
684
806
|
const rightFrames = fixedNodes.right.map((fixed) => renderFixedChildFrame(fixed, positiveInteger(fixed.props.size, "Fixed size"), middleHeight));
|
|
685
807
|
const bodyFrames = bodyWidth > 0
|
|
686
|
-
? [constrainFrame(renderBodyFrame(bodyChildren, node.props, { cols: bodyWidth, rows: middleHeight }), { width: bodyWidth, height: middleHeight })]
|
|
808
|
+
? [constrainFrame(renderBodyFrame(bodyChildren, node.props, { cols: bodyWidth, rows: middleHeight }), { width: bodyWidth, height: middleHeight, expandFullRowSpans: true })]
|
|
687
809
|
: [];
|
|
688
|
-
middleFrames.push(constrainFrame(mergeHorizontal([...leftFrames, ...bodyFrames, ...rightFrames]), { width, height: middleHeight }));
|
|
810
|
+
middleFrames.push(constrainFrame(mergeHorizontal([...leftFrames, ...bodyFrames, ...rightFrames]), { width, height: middleHeight, expandFullRowSpans: true }));
|
|
689
811
|
}
|
|
690
812
|
const frame = mergeVertical([...topFrames, ...middleFrames, ...bottomFrames]);
|
|
691
|
-
return constrainFrame(frame, { width, height });
|
|
813
|
+
return constrainFrame(frame, { width, height, expandFullFrameSpans: true });
|
|
692
814
|
}
|
|
693
815
|
function renderStandaloneFixedFrame(node, context) {
|
|
694
816
|
const position = fixedPosition(node.props.position);
|
|
@@ -749,18 +871,21 @@ function overlayGeometry(node, width, height) {
|
|
|
749
871
|
}
|
|
750
872
|
function renderOverlayChildFrame(node, width, height, context) {
|
|
751
873
|
const geometry = overlayGeometry(node, width, height);
|
|
752
|
-
let frame = constrainFrame(renderBodyFrame(node.children, {}, { cols: geometry.width, rows: geometry.height, theme: context?.theme }), { width: geometry.width, height: geometry.height });
|
|
874
|
+
let frame = constrainFrame(renderBodyFrame(node.children, {}, { cols: geometry.width, rows: geometry.height, theme: context?.theme }), { width: geometry.width, height: geometry.height, expandFullRowSpans: true });
|
|
753
875
|
frame = addContainerStyleSpans(frame, node, resolveNodeStyle(node, context));
|
|
754
876
|
if (node.props.id && isFocusable(node)) {
|
|
755
877
|
frame = addFocusableHitbox(frame, node);
|
|
756
878
|
}
|
|
757
879
|
return { frame, geometry };
|
|
758
880
|
}
|
|
881
|
+
function orderedDirectOverlays(overlays) {
|
|
882
|
+
return overlays.map((overlay, sourceOrder) => ({ overlay, sourceOrder })).sort((a, b) => a.sourceOrder - b.sourceOrder).map(({ overlay }) => overlay);
|
|
883
|
+
}
|
|
759
884
|
function applyDirectOverlays(base, overlays, context) {
|
|
760
885
|
let frame = base;
|
|
761
886
|
const width = Math.max(1, getFrameWidth(base));
|
|
762
887
|
const height = Math.max(1, getFrameHeight(base));
|
|
763
|
-
for (const overlay of overlays) {
|
|
888
|
+
for (const overlay of orderedDirectOverlays(overlays)) {
|
|
764
889
|
const rendered = renderOverlayChildFrame(overlay, width, height, context);
|
|
765
890
|
frame = overlayFrame(frame, rendered.frame, rendered.geometry);
|
|
766
891
|
}
|
|
@@ -797,7 +922,7 @@ function renderScreenFrame(node, context) {
|
|
|
797
922
|
}
|
|
798
923
|
base = mergeVertical(parts);
|
|
799
924
|
if (context && overlays.length) {
|
|
800
|
-
base = constrainFrame(base, { width: context.cols, height: context.rows });
|
|
925
|
+
base = constrainFrame(base, { width: context.cols, height: context.rows, expandFullRowSpans: true });
|
|
801
926
|
}
|
|
802
927
|
}
|
|
803
928
|
return overlays.length ? applyDirectOverlays(base, overlays, context) : base;
|
|
@@ -863,13 +988,13 @@ function renderLogViewFrame(node, context) {
|
|
|
863
988
|
}
|
|
864
989
|
}
|
|
865
990
|
if (typeof innerWidth !== "undefined") {
|
|
866
|
-
frame = constrainFrame(frame, { width: innerWidth, height: innerHeight });
|
|
991
|
+
frame = constrainFrame(frame, { width: innerWidth, height: innerHeight, expandFullFrameSpans: true });
|
|
867
992
|
}
|
|
868
993
|
}
|
|
869
994
|
frame = padFrameSides(frame, padding);
|
|
870
995
|
frame = addBorder(frame, border);
|
|
871
996
|
if (typeof dimensions.width !== "undefined") {
|
|
872
|
-
frame = constrainFrame(frame, { width: dimensions.width, height: dimensions.height });
|
|
997
|
+
frame = constrainFrame(frame, { width: dimensions.width, height: dimensions.height, expandFullFrameSpans: true });
|
|
873
998
|
}
|
|
874
999
|
else if (typeof dimensions.height !== "undefined") {
|
|
875
1000
|
frame = cropFrame(frame, 0, dimensions.height);
|
|
@@ -898,18 +1023,18 @@ function renderSeparatedRowFrame(frames, separator = " | ") {
|
|
|
898
1023
|
const frame = frames[index];
|
|
899
1024
|
const width = widths[index];
|
|
900
1025
|
for (let row = 0; row < height; row += 1) {
|
|
901
|
-
lines[row] += (frame.lines[row] || ""
|
|
1026
|
+
lines[row] += padEndTerminalCells(frame.lines[row] || "", width);
|
|
902
1027
|
if (index < frames.length - 1) {
|
|
903
1028
|
lines[row] += separator;
|
|
904
1029
|
}
|
|
905
1030
|
}
|
|
906
|
-
const shifted = shiftFrame(frame, xOffset, 0);
|
|
1031
|
+
const shifted = shiftFrame(fitFrame(frame, width, height, { expandFullFrameSpans: true }), xOffset, 0);
|
|
907
1032
|
hitboxes.push(...shifted.hitboxes);
|
|
908
1033
|
spans.push(...shifted.spans);
|
|
909
1034
|
if (!cursor && frame.cursor) {
|
|
910
1035
|
cursor = { x: frame.cursor.x + xOffset, y: frame.cursor.y };
|
|
911
1036
|
}
|
|
912
|
-
xOffset += width + (index < frames.length - 1 ? separator
|
|
1037
|
+
xOffset += width + (index < frames.length - 1 ? terminalCellWidth(separator) : 0);
|
|
913
1038
|
}
|
|
914
1039
|
return createFrame(lines, hitboxes, cursor, spans);
|
|
915
1040
|
}
|
|
@@ -937,7 +1062,7 @@ function renderElementFrame(node, context) {
|
|
|
937
1062
|
if (node.tag === "terminal-scroll") {
|
|
938
1063
|
const offset = numeric(node.props.__scrollOffset, 0);
|
|
939
1064
|
if (typeof dimensions.width !== "undefined") {
|
|
940
|
-
frame = constrainFrame(frame, { width: dimensions.width });
|
|
1065
|
+
frame = constrainFrame(frame, { width: dimensions.width, expandFullFrameSpans: true });
|
|
941
1066
|
}
|
|
942
1067
|
const height = numeric(dimensions.height ?? node.props.height, getFrameHeight(frame));
|
|
943
1068
|
frame = cropFrame(frame, offset, height || getFrameHeight(frame));
|
|
@@ -946,7 +1071,7 @@ function renderElementFrame(node, context) {
|
|
|
946
1071
|
const spans = frame.spans.slice();
|
|
947
1072
|
for (let index = 0; index < frame.lines.length; index += 1) {
|
|
948
1073
|
const row = index + 1;
|
|
949
|
-
const width = frame.lines[index]
|
|
1074
|
+
const width = terminalCellWidth(frame.lines[index]) + 1;
|
|
950
1075
|
if (highlightRows.includes(row)) {
|
|
951
1076
|
spans.push({ kind: "highlight", x1: 1, x2: width, y: row });
|
|
952
1077
|
}
|
|
@@ -968,41 +1093,112 @@ function renderElementFrame(node, context) {
|
|
|
968
1093
|
return renderLogViewFrame(node, context);
|
|
969
1094
|
case "terminal-list": {
|
|
970
1095
|
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
971
|
-
const
|
|
1096
|
+
const activeIndex = clampListIndex(numeric(node.props.__activeIndex ?? node.props.__selectedIndex, 0), items.length);
|
|
1097
|
+
const selectedIndex = typeof node.props.__selectedIndex === "number" ? clampListIndex(Number(node.props.__selectedIndex), items.length) : null;
|
|
972
1098
|
const hoveredIndex = typeof node.props.__hoveredIndex === "number" ? Number(node.props.__hoveredIndex) : -1;
|
|
973
|
-
const range = listVirtualRange(node, items.length,
|
|
974
|
-
const lines = [];
|
|
975
|
-
for (let index = range.start; index < range.end; index += 1) {
|
|
976
|
-
const item = items[index];
|
|
977
|
-
const label = typeof node.props.renderItem === "function" ? plainText(node.props.renderItem(item, index)) : plainText(item);
|
|
978
|
-
lines.push(label);
|
|
979
|
-
}
|
|
980
|
-
const visibleLines = lines.length ? lines : [""];
|
|
1099
|
+
const range = listVirtualRange(node, items.length, context);
|
|
981
1100
|
const layoutStyle = resolveLayoutStyle("list.base", node, context);
|
|
982
1101
|
const padding = normalizeSpacing(layoutStyle.padding, "List padding");
|
|
983
1102
|
const border = normalizeBorder(layoutStyle.border);
|
|
984
|
-
const
|
|
1103
|
+
const horizontalDecoration = padding.left + padding.right + (border.left ? 1 : 0) + (border.right ? 1 : 0);
|
|
1104
|
+
const wrapWidth = typeof context?.cols === "number" ? Math.max(1, context.cols - horizontalDecoration) : 1;
|
|
1105
|
+
const visibleLines = [];
|
|
1106
|
+
const itemIndexes = [];
|
|
1107
|
+
const childHitboxes = [];
|
|
1108
|
+
for (let index = range.start; index < range.end; index += 1) {
|
|
1109
|
+
const item = items[index];
|
|
1110
|
+
const itemFrame = renderListItemFrame(node, item, index, index - range.visibleStart, activeIndex, selectedIndex, wrapWidth, context);
|
|
1111
|
+
const rowOffset = visibleLines.length;
|
|
1112
|
+
visibleLines.push(...itemFrame.lines);
|
|
1113
|
+
for (let row = 0; row < itemFrame.lines.length; row += 1) {
|
|
1114
|
+
itemIndexes.push(index);
|
|
1115
|
+
}
|
|
1116
|
+
childHitboxes.push(...shiftFrame(itemFrame, 0, rowOffset).hitboxes);
|
|
1117
|
+
}
|
|
1118
|
+
if (!visibleLines.length) {
|
|
1119
|
+
visibleLines.push("");
|
|
1120
|
+
itemIndexes.push(0);
|
|
1121
|
+
}
|
|
1122
|
+
const frameHeight = node.props.virtualized && node.props.wrap === true
|
|
1123
|
+
? positiveDimension(node.props.height, "height") ?? (typeof context?.rows === "number" ? Math.max(1, context.rows) : undefined)
|
|
1124
|
+
: undefined;
|
|
1125
|
+
const contentHeight = typeof frameHeight === "number"
|
|
1126
|
+
? Math.max(1, frameHeight - padding.top - padding.bottom - (border.top ? 1 : 0) - (border.bottom ? 1 : 0))
|
|
1127
|
+
: visibleLines.length;
|
|
1128
|
+
let visibleLineStart = 0;
|
|
1129
|
+
if (node.props.virtualized && node.props.wrap === true && visibleLines.length > contentHeight) {
|
|
1130
|
+
const activeLineIndex = itemIndexes.findIndex((sourceIndex) => sourceIndex === activeIndex);
|
|
1131
|
+
if (activeLineIndex >= contentHeight) {
|
|
1132
|
+
visibleLineStart = activeLineIndex - contentHeight + 1;
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
const frameLines = visibleLines.slice(visibleLineStart, visibleLineStart + contentHeight);
|
|
1136
|
+
const frameItemIndexes = itemIndexes.slice(visibleLineStart, visibleLineStart + contentHeight);
|
|
1137
|
+
const frameChildHitboxes = childHitboxes
|
|
1138
|
+
.filter((box) => box.y2 > visibleLineStart && box.y1 <= visibleLineStart + contentHeight)
|
|
1139
|
+
.map((box) => ({
|
|
1140
|
+
...box,
|
|
1141
|
+
y1: Math.max(1, box.y1 - visibleLineStart),
|
|
1142
|
+
y2: Math.min(contentHeight, box.y2 - visibleLineStart),
|
|
1143
|
+
contentY: typeof box.contentY === "number" ? Math.max(1, box.contentY - visibleLineStart) : undefined
|
|
1144
|
+
}));
|
|
1145
|
+
const decorated = addBorder(padFrameSides(createFrame(frameLines, frameChildHitboxes), padding), border);
|
|
985
1146
|
const width = Math.max(1, getFrameWidth(decorated));
|
|
986
1147
|
const height = Math.max(1, getFrameHeight(decorated));
|
|
987
1148
|
const itemY = 1 + (border.top ? 1 : 0) + padding.top;
|
|
988
1149
|
const spans = [];
|
|
989
|
-
for (let index = 0; index <
|
|
990
|
-
const sourceIndex =
|
|
1150
|
+
for (let index = 0; index < frameLines.length; index += 1) {
|
|
1151
|
+
const sourceIndex = frameItemIndexes[index];
|
|
991
1152
|
const y = itemY + index;
|
|
992
|
-
spans.push({ kind: "list.base", x1: 1, x2: width + 1, y });
|
|
993
|
-
if (sourceIndex === selectedIndex) {
|
|
994
|
-
spans.push({ kind: "list.
|
|
1153
|
+
spans.push(markFullRowSpan({ kind: "list.base", x1: 1, x2: width + 1, y }));
|
|
1154
|
+
if (selectedIndex !== null && selectedIndex !== activeIndex && sourceIndex === selectedIndex) {
|
|
1155
|
+
spans.push(markFullRowSpan({ kind: "list.selected", x1: 1, x2: width + 1, y }));
|
|
1156
|
+
}
|
|
1157
|
+
if (sourceIndex === activeIndex) {
|
|
1158
|
+
spans.push(markFullRowSpan({ kind: "list.current", x1: 1, x2: width + 1, y }));
|
|
995
1159
|
}
|
|
996
1160
|
if (sourceIndex === hoveredIndex) {
|
|
997
|
-
spans.push({ kind: "list.hover", x1: 1, x2: width + 1, y });
|
|
1161
|
+
spans.push(markFullRowSpan({ kind: "list.hover", x1: 1, x2: width + 1, y }));
|
|
998
1162
|
}
|
|
999
1163
|
}
|
|
1000
|
-
const
|
|
1001
|
-
const
|
|
1002
|
-
if (
|
|
1003
|
-
|
|
1164
|
+
const listHitboxes = [];
|
|
1165
|
+
const itemHitboxes = [];
|
|
1166
|
+
if (node.props.id) {
|
|
1167
|
+
listHitboxes.push({
|
|
1168
|
+
id: node.props.id,
|
|
1169
|
+
tag: node.tag,
|
|
1170
|
+
x1: 1,
|
|
1171
|
+
x2: width,
|
|
1172
|
+
y1: 1,
|
|
1173
|
+
y2: Math.min(height, typeof frameHeight === "number" ? frameHeight : height),
|
|
1174
|
+
itemOffset: range.start,
|
|
1175
|
+
itemIndexes: frameItemIndexes,
|
|
1176
|
+
contentY: itemY
|
|
1177
|
+
});
|
|
1178
|
+
let itemStart = 0;
|
|
1179
|
+
while (itemStart < frameItemIndexes.length) {
|
|
1180
|
+
const sourceIndex = frameItemIndexes[itemStart];
|
|
1181
|
+
let itemEnd = itemStart;
|
|
1182
|
+
while (itemEnd + 1 < frameItemIndexes.length && frameItemIndexes[itemEnd + 1] === sourceIndex) {
|
|
1183
|
+
itemEnd += 1;
|
|
1184
|
+
}
|
|
1185
|
+
itemHitboxes.push({
|
|
1186
|
+
id: node.props.id,
|
|
1187
|
+
tag: node.tag,
|
|
1188
|
+
x1: 1,
|
|
1189
|
+
x2: width,
|
|
1190
|
+
y1: itemY + itemStart,
|
|
1191
|
+
y2: itemY + itemEnd,
|
|
1192
|
+
itemOffset: range.start,
|
|
1193
|
+
__listItemIndex: sourceIndex,
|
|
1194
|
+
itemIndexes: new Array(itemEnd - itemStart + 1).fill(sourceIndex)
|
|
1195
|
+
});
|
|
1196
|
+
itemStart = itemEnd + 1;
|
|
1197
|
+
}
|
|
1004
1198
|
}
|
|
1005
|
-
|
|
1199
|
+
const frame = createFrame(decorated.lines, [...listHitboxes, ...itemHitboxes, ...decorated.hitboxes], decorated.cursor, spans);
|
|
1200
|
+
const styled = addFullFrameSpans(frame, resolveNodeStyle(node, context, { includeBase: false }).spanKinds);
|
|
1201
|
+
return typeof frameHeight === "number" ? constrainFrame(styled, { height: frameHeight, expandFullFrameSpans: true }) : styled;
|
|
1006
1202
|
}
|
|
1007
1203
|
case "terminal-table":
|
|
1008
1204
|
return renderTableFrame(node, context);
|
|
@@ -1036,7 +1232,7 @@ function renderElementFrame(node, context) {
|
|
|
1036
1232
|
const decorated = addBorder(createFrame([line]), inputBorder);
|
|
1037
1233
|
const width = Math.max(1, getFrameWidth(decorated));
|
|
1038
1234
|
const height = Math.max(1, getFrameHeight(decorated));
|
|
1039
|
-
const hitboxes = node.props.id ? [{ id: node.props.id, tag: node.tag, x1: 1, x2: width, y1: 1, y2: height, textStartX,
|
|
1235
|
+
const hitboxes = node.props.id ? [{ id: node.props.id, tag: node.tag, x1: 1, x2: width, y1: 1, y2: height, textStartX, ...interactiveTextMetadata(stringValue) }] : [];
|
|
1040
1236
|
const spans = fullFrameSpans(["input.base"], width, height);
|
|
1041
1237
|
return addFullFrameSpans(createFrame(decorated.lines, hitboxes, decorated.cursor, spans), resolveNodeStyle(node, context, { includeBase: false }).spanKinds);
|
|
1042
1238
|
}
|
|
@@ -1044,19 +1240,19 @@ function renderElementFrame(node, context) {
|
|
|
1044
1240
|
const decorated = addBorder(createFrame([rendered.line], [], rendered.cursor, rendered.spans), inputBorder);
|
|
1045
1241
|
const width = Math.max(1, getFrameWidth(decorated));
|
|
1046
1242
|
const height = Math.max(1, getFrameHeight(decorated));
|
|
1047
|
-
const hitboxes = node.props.id ? [{ id: node.props.id, tag: node.tag, x1: 1, x2: width, y1: 1, y2: height, textStartX,
|
|
1243
|
+
const hitboxes = node.props.id ? [{ id: node.props.id, tag: node.tag, x1: 1, x2: width, y1: 1, y2: height, textStartX, ...interactiveTextMetadata(stringValue) }] : [];
|
|
1048
1244
|
const spans = [...fullFrameSpans(["input.base", "input.focus"], width, height), ...decorated.spans];
|
|
1049
1245
|
return addFullFrameSpans(createFrame(decorated.lines, hitboxes, decorated.cursor, spans), resolveNodeStyle(node, context, { includeBase: false }).spanKinds);
|
|
1050
1246
|
}
|
|
1051
1247
|
case "terminal-editor":
|
|
1052
|
-
return addFullFrameSpans(renderEditorFrame(node), resolveNodeStyle(node, context).spanKinds);
|
|
1248
|
+
return addFullFrameSpans(renderEditorFrame(node, context), resolveNodeStyle(node, context).spanKinds);
|
|
1053
1249
|
case "terminal-button": {
|
|
1054
1250
|
const label = typeof node.props.label !== "undefined" ? plainText(node.props.label) : plainText(node.children.map(textContent).join(""));
|
|
1055
1251
|
const layoutStyle = resolveLayoutStyle("button.base", node, context);
|
|
1056
1252
|
const decorated = decoratedControlFrame([String(label)], layoutStyle);
|
|
1057
1253
|
const width = Math.max(1, getFrameWidth(decorated));
|
|
1058
1254
|
const height = Math.max(1, getFrameHeight(decorated));
|
|
1059
|
-
const hitboxes = node.props.id ? [{ id: node.props.id, tag: node.tag, x1: 1, x2: width, y1: 1, y2: height }] : [];
|
|
1255
|
+
const hitboxes = node.props.id ? [{ id: node.props.id, tag: node.tag, x1: 1, x2: width, y1: 1, y2: height, __pressHandler: typeof node.props.onpress === "function" ? node.props.onpress : undefined }] : [];
|
|
1060
1256
|
const kinds = ["button.base", ...nodeStates(node).map((state) => `button.${state}`)];
|
|
1061
1257
|
const spans = fullFrameSpans(kinds, width, height);
|
|
1062
1258
|
return addFullFrameSpans(createFrame(decorated.lines, hitboxes, decorated.cursor, spans), resolveNodeStyle(node, context, { includeBase: false }).spanKinds);
|