@valyrianjs/terminal 0.2.1 → 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.map +1 -1
- package/dist/ansi.js +12 -14
- package/dist/ansi.js.map +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +4 -0
- 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/layout.d.ts +5 -1
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +53 -23
- package/dist/layout.js.map +1 -1
- package/dist/mouse.d.ts.map +1 -1
- package/dist/mouse.js +8 -1
- package/dist/mouse.js.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +87 -48
- package/dist/render.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +2 -0
- 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/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/api-reference.md +6 -3
- package/docs/cookbook.md +1 -1
- package/docs/interaction-model.md +5 -5
- package/docs/primitive-gallery.md +4 -4
- 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 +16 -13
- package/package.json +3 -2
- package/src/ansi.ts +12 -14
- package/src/events.ts +2 -0
- package/src/frame-style.ts +36 -0
- package/src/layout.ts +57 -24
- package/src/mouse.ts +10 -1
- package/src/render.ts +92 -48
- package/src/session.ts +2 -0
- package/src/text.ts +148 -0
- package/src/types.ts +3 -0
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,7 +227,7 @@ 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);
|
|
@@ -288,8 +289,14 @@ function wrapPlainText(value, width) {
|
|
|
288
289
|
continue;
|
|
289
290
|
}
|
|
290
291
|
let remaining = sourceRow;
|
|
291
|
-
while (remaining
|
|
292
|
-
const slice = remaining
|
|
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
|
+
}
|
|
293
300
|
const breakAt = slice.lastIndexOf(" ");
|
|
294
301
|
if (breakAt > 0 && breakAt >= Math.floor(width * 0.6)) {
|
|
295
302
|
rows.push(remaining.slice(0, breakAt));
|
|
@@ -297,7 +304,7 @@ function wrapPlainText(value, width) {
|
|
|
297
304
|
}
|
|
298
305
|
else {
|
|
299
306
|
rows.push(slice);
|
|
300
|
-
remaining = remaining.slice(
|
|
307
|
+
remaining = remaining.slice(slice.length);
|
|
301
308
|
}
|
|
302
309
|
}
|
|
303
310
|
rows.push(remaining);
|
|
@@ -412,7 +419,8 @@ function decorateContainerFrame(frame, node, options = {}, context) {
|
|
|
412
419
|
if ((typeof width === "number" && width > 0) || (typeof height === "number" && height > 0)) {
|
|
413
420
|
next = constrainFrame(next, {
|
|
414
421
|
width: typeof width === "number" && width > 0 ? width : undefined,
|
|
415
|
-
height: typeof height === "number" && height > 0 ? height : undefined
|
|
422
|
+
height: typeof height === "number" && height > 0 ? height : undefined,
|
|
423
|
+
expandFullFrameSpans: true
|
|
416
424
|
});
|
|
417
425
|
}
|
|
418
426
|
}
|
|
@@ -421,7 +429,8 @@ function decorateContainerFrame(frame, node, options = {}, context) {
|
|
|
421
429
|
if (options.constrain) {
|
|
422
430
|
next = constrainFrame(next, {
|
|
423
431
|
width: typeof options.width === "undefined" ? positiveDimension(node.props.width, "width") : options.width,
|
|
424
|
-
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
|
|
425
434
|
});
|
|
426
435
|
}
|
|
427
436
|
next = addContainerStyleSpans(next, node, resolved);
|
|
@@ -473,6 +482,16 @@ function resolveContainerChildContext(node, dimensions, context) {
|
|
|
473
482
|
theme: context?.theme
|
|
474
483
|
};
|
|
475
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
|
+
}
|
|
476
495
|
function renderInputLine(value, inputState, padding = { top: 0, right: 0, bottom: 0, left: 0 }) {
|
|
477
496
|
const state = normalizeInputState(inputState, value.length);
|
|
478
497
|
const { start, end } = getSelectionRange(state);
|
|
@@ -481,11 +500,27 @@ function renderInputLine(value, inputState, padding = { top: 0, right: 0, bottom
|
|
|
481
500
|
const textStart = padding.left + 1;
|
|
482
501
|
return {
|
|
483
502
|
line: paddedLine,
|
|
484
|
-
cursor: { x: textStart + state.cursor, y: 1 },
|
|
485
|
-
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 }]
|
|
505
|
+
};
|
|
506
|
+
}
|
|
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
|
|
486
521
|
};
|
|
487
522
|
}
|
|
488
|
-
function renderEditorFrame(node) {
|
|
523
|
+
function renderEditorFrame(node, context) {
|
|
489
524
|
const value = typeof node.props.value !== "undefined" ? plainText(node.props.value) : "";
|
|
490
525
|
const placeholder = typeof node.props.placeholder !== "undefined" ? plainText(node.props.placeholder) : "";
|
|
491
526
|
const displayValue = value.length === 0 && !node.props.__focused && placeholder ? placeholder : value;
|
|
@@ -499,21 +534,26 @@ function renderEditorFrame(node) {
|
|
|
499
534
|
}
|
|
500
535
|
return `> ${line.slice(0, focusedColumn)}|${line.slice(focusedColumn)}`;
|
|
501
536
|
});
|
|
502
|
-
const width = lines.reduce((max, line) => Math.max(max, line
|
|
503
|
-
const cursor = node.props.__focused ? { x: 3 + focusedColumn, y: focusedLine + 1 } : null;
|
|
504
|
-
const spans = node.props.__focused ? [{ kind: "focus", x1: 1, x2: Math.max(2, lines[focusedLine]
|
|
505
|
-
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);
|
|
506
541
|
const frame = createFrame(lines, [], cursor, spans);
|
|
507
|
-
const scrollOffset = node.props.__focused && typeof height !== "undefined"
|
|
508
|
-
? 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))
|
|
509
544
|
: 0;
|
|
510
|
-
const
|
|
545
|
+
const croppedFrame = typeof dimensions.height === "undefined"
|
|
511
546
|
? frame
|
|
512
|
-
:
|
|
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 });
|
|
513
553
|
if (!node.props.id) {
|
|
514
554
|
return constrainedFrame;
|
|
515
555
|
}
|
|
516
|
-
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);
|
|
517
557
|
}
|
|
518
558
|
function notifyLayoutContextProbe(node, context) {
|
|
519
559
|
if (context && typeof node.props.__layoutContextProbe === "function") {
|
|
@@ -543,12 +583,11 @@ function renderTableFrame(node, context) {
|
|
|
543
583
|
const cells = new Array(columnCount).fill(createFrame([""])).map((cell, index) => row[index] || cell);
|
|
544
584
|
const rowHeight = cells.reduce((max, cell) => Math.max(max, getFrameHeight(cell)), 0);
|
|
545
585
|
const normalized = cells.map((cell, index) => ({
|
|
546
|
-
frame: cell,
|
|
547
|
-
width: columnWidths[index]
|
|
548
|
-
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]
|
|
549
588
|
}));
|
|
550
589
|
for (let rowIndex = 0; rowIndex < rowHeight; rowIndex += 1) {
|
|
551
|
-
lines.push(normalized.map((cell) => cell.lines[rowIndex]).join(" | "));
|
|
590
|
+
lines.push(normalized.map((cell) => cell.frame.lines[rowIndex]).join(" | "));
|
|
552
591
|
}
|
|
553
592
|
let xOffset = 0;
|
|
554
593
|
for (let index = 0; index < normalized.length; index += 1) {
|
|
@@ -694,7 +733,7 @@ function renderSplitFrame(node, context) {
|
|
|
694
733
|
if (cellWidth <= 0 || cellHeight <= 0) {
|
|
695
734
|
continue;
|
|
696
735
|
}
|
|
697
|
-
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 }));
|
|
698
737
|
}
|
|
699
738
|
const frame = frames.length
|
|
700
739
|
? direction === "row" ? mergeHorizontal(frames, { gap }) : mergeVertical(frames, { gap })
|
|
@@ -728,7 +767,7 @@ function renderBodyFrame(children, props, context) {
|
|
|
728
767
|
return direction === "row" ? mergeHorizontal(frames, { gap }) : mergeVertical(frames, { gap });
|
|
729
768
|
}
|
|
730
769
|
function renderFixedChildFrame(node, width, height) {
|
|
731
|
-
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 });
|
|
732
771
|
}
|
|
733
772
|
function renderFixedCompositionFrame(node, width, height) {
|
|
734
773
|
if (!Number.isFinite(width) || !Number.isInteger(width) || width <= 0 || !Number.isFinite(height) || !Number.isInteger(height) || height <= 0) {
|
|
@@ -766,12 +805,12 @@ function renderFixedCompositionFrame(node, width, height) {
|
|
|
766
805
|
const leftFrames = fixedNodes.left.map((fixed) => renderFixedChildFrame(fixed, positiveInteger(fixed.props.size, "Fixed size"), middleHeight));
|
|
767
806
|
const rightFrames = fixedNodes.right.map((fixed) => renderFixedChildFrame(fixed, positiveInteger(fixed.props.size, "Fixed size"), middleHeight));
|
|
768
807
|
const bodyFrames = bodyWidth > 0
|
|
769
|
-
? [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 })]
|
|
770
809
|
: [];
|
|
771
|
-
middleFrames.push(constrainFrame(mergeHorizontal([...leftFrames, ...bodyFrames, ...rightFrames]), { width, height: middleHeight }));
|
|
810
|
+
middleFrames.push(constrainFrame(mergeHorizontal([...leftFrames, ...bodyFrames, ...rightFrames]), { width, height: middleHeight, expandFullRowSpans: true }));
|
|
772
811
|
}
|
|
773
812
|
const frame = mergeVertical([...topFrames, ...middleFrames, ...bottomFrames]);
|
|
774
|
-
return constrainFrame(frame, { width, height });
|
|
813
|
+
return constrainFrame(frame, { width, height, expandFullFrameSpans: true });
|
|
775
814
|
}
|
|
776
815
|
function renderStandaloneFixedFrame(node, context) {
|
|
777
816
|
const position = fixedPosition(node.props.position);
|
|
@@ -832,7 +871,7 @@ function overlayGeometry(node, width, height) {
|
|
|
832
871
|
}
|
|
833
872
|
function renderOverlayChildFrame(node, width, height, context) {
|
|
834
873
|
const geometry = overlayGeometry(node, width, height);
|
|
835
|
-
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 });
|
|
836
875
|
frame = addContainerStyleSpans(frame, node, resolveNodeStyle(node, context));
|
|
837
876
|
if (node.props.id && isFocusable(node)) {
|
|
838
877
|
frame = addFocusableHitbox(frame, node);
|
|
@@ -883,7 +922,7 @@ function renderScreenFrame(node, context) {
|
|
|
883
922
|
}
|
|
884
923
|
base = mergeVertical(parts);
|
|
885
924
|
if (context && overlays.length) {
|
|
886
|
-
base = constrainFrame(base, { width: context.cols, height: context.rows });
|
|
925
|
+
base = constrainFrame(base, { width: context.cols, height: context.rows, expandFullRowSpans: true });
|
|
887
926
|
}
|
|
888
927
|
}
|
|
889
928
|
return overlays.length ? applyDirectOverlays(base, overlays, context) : base;
|
|
@@ -949,13 +988,13 @@ function renderLogViewFrame(node, context) {
|
|
|
949
988
|
}
|
|
950
989
|
}
|
|
951
990
|
if (typeof innerWidth !== "undefined") {
|
|
952
|
-
frame = constrainFrame(frame, { width: innerWidth, height: innerHeight });
|
|
991
|
+
frame = constrainFrame(frame, { width: innerWidth, height: innerHeight, expandFullFrameSpans: true });
|
|
953
992
|
}
|
|
954
993
|
}
|
|
955
994
|
frame = padFrameSides(frame, padding);
|
|
956
995
|
frame = addBorder(frame, border);
|
|
957
996
|
if (typeof dimensions.width !== "undefined") {
|
|
958
|
-
frame = constrainFrame(frame, { width: dimensions.width, height: dimensions.height });
|
|
997
|
+
frame = constrainFrame(frame, { width: dimensions.width, height: dimensions.height, expandFullFrameSpans: true });
|
|
959
998
|
}
|
|
960
999
|
else if (typeof dimensions.height !== "undefined") {
|
|
961
1000
|
frame = cropFrame(frame, 0, dimensions.height);
|
|
@@ -984,18 +1023,18 @@ function renderSeparatedRowFrame(frames, separator = " | ") {
|
|
|
984
1023
|
const frame = frames[index];
|
|
985
1024
|
const width = widths[index];
|
|
986
1025
|
for (let row = 0; row < height; row += 1) {
|
|
987
|
-
lines[row] += (frame.lines[row] || ""
|
|
1026
|
+
lines[row] += padEndTerminalCells(frame.lines[row] || "", width);
|
|
988
1027
|
if (index < frames.length - 1) {
|
|
989
1028
|
lines[row] += separator;
|
|
990
1029
|
}
|
|
991
1030
|
}
|
|
992
|
-
const shifted = shiftFrame(frame, xOffset, 0);
|
|
1031
|
+
const shifted = shiftFrame(fitFrame(frame, width, height, { expandFullFrameSpans: true }), xOffset, 0);
|
|
993
1032
|
hitboxes.push(...shifted.hitboxes);
|
|
994
1033
|
spans.push(...shifted.spans);
|
|
995
1034
|
if (!cursor && frame.cursor) {
|
|
996
1035
|
cursor = { x: frame.cursor.x + xOffset, y: frame.cursor.y };
|
|
997
1036
|
}
|
|
998
|
-
xOffset += width + (index < frames.length - 1 ? separator
|
|
1037
|
+
xOffset += width + (index < frames.length - 1 ? terminalCellWidth(separator) : 0);
|
|
999
1038
|
}
|
|
1000
1039
|
return createFrame(lines, hitboxes, cursor, spans);
|
|
1001
1040
|
}
|
|
@@ -1023,7 +1062,7 @@ function renderElementFrame(node, context) {
|
|
|
1023
1062
|
if (node.tag === "terminal-scroll") {
|
|
1024
1063
|
const offset = numeric(node.props.__scrollOffset, 0);
|
|
1025
1064
|
if (typeof dimensions.width !== "undefined") {
|
|
1026
|
-
frame = constrainFrame(frame, { width: dimensions.width });
|
|
1065
|
+
frame = constrainFrame(frame, { width: dimensions.width, expandFullFrameSpans: true });
|
|
1027
1066
|
}
|
|
1028
1067
|
const height = numeric(dimensions.height ?? node.props.height, getFrameHeight(frame));
|
|
1029
1068
|
frame = cropFrame(frame, offset, height || getFrameHeight(frame));
|
|
@@ -1032,7 +1071,7 @@ function renderElementFrame(node, context) {
|
|
|
1032
1071
|
const spans = frame.spans.slice();
|
|
1033
1072
|
for (let index = 0; index < frame.lines.length; index += 1) {
|
|
1034
1073
|
const row = index + 1;
|
|
1035
|
-
const width = frame.lines[index]
|
|
1074
|
+
const width = terminalCellWidth(frame.lines[index]) + 1;
|
|
1036
1075
|
if (highlightRows.includes(row)) {
|
|
1037
1076
|
spans.push({ kind: "highlight", x1: 1, x2: width, y: row });
|
|
1038
1077
|
}
|
|
@@ -1111,15 +1150,15 @@ function renderElementFrame(node, context) {
|
|
|
1111
1150
|
for (let index = 0; index < frameLines.length; index += 1) {
|
|
1112
1151
|
const sourceIndex = frameItemIndexes[index];
|
|
1113
1152
|
const y = itemY + index;
|
|
1114
|
-
spans.push({ kind: "list.base", x1: 1, x2: width + 1, y });
|
|
1153
|
+
spans.push(markFullRowSpan({ kind: "list.base", x1: 1, x2: width + 1, y }));
|
|
1115
1154
|
if (selectedIndex !== null && selectedIndex !== activeIndex && sourceIndex === selectedIndex) {
|
|
1116
|
-
spans.push({ kind: "list.selected", x1: 1, x2: width + 1, y });
|
|
1155
|
+
spans.push(markFullRowSpan({ kind: "list.selected", x1: 1, x2: width + 1, y }));
|
|
1117
1156
|
}
|
|
1118
1157
|
if (sourceIndex === activeIndex) {
|
|
1119
|
-
spans.push({ kind: "list.current", x1: 1, x2: width + 1, y });
|
|
1158
|
+
spans.push(markFullRowSpan({ kind: "list.current", x1: 1, x2: width + 1, y }));
|
|
1120
1159
|
}
|
|
1121
1160
|
if (sourceIndex === hoveredIndex) {
|
|
1122
|
-
spans.push({ kind: "list.hover", x1: 1, x2: width + 1, y });
|
|
1161
|
+
spans.push(markFullRowSpan({ kind: "list.hover", x1: 1, x2: width + 1, y }));
|
|
1123
1162
|
}
|
|
1124
1163
|
}
|
|
1125
1164
|
const listHitboxes = [];
|
|
@@ -1159,7 +1198,7 @@ function renderElementFrame(node, context) {
|
|
|
1159
1198
|
}
|
|
1160
1199
|
const frame = createFrame(decorated.lines, [...listHitboxes, ...itemHitboxes, ...decorated.hitboxes], decorated.cursor, spans);
|
|
1161
1200
|
const styled = addFullFrameSpans(frame, resolveNodeStyle(node, context, { includeBase: false }).spanKinds);
|
|
1162
|
-
return typeof frameHeight === "number" ? constrainFrame(styled, { height: frameHeight }) : styled;
|
|
1201
|
+
return typeof frameHeight === "number" ? constrainFrame(styled, { height: frameHeight, expandFullFrameSpans: true }) : styled;
|
|
1163
1202
|
}
|
|
1164
1203
|
case "terminal-table":
|
|
1165
1204
|
return renderTableFrame(node, context);
|
|
@@ -1193,7 +1232,7 @@ function renderElementFrame(node, context) {
|
|
|
1193
1232
|
const decorated = addBorder(createFrame([line]), inputBorder);
|
|
1194
1233
|
const width = Math.max(1, getFrameWidth(decorated));
|
|
1195
1234
|
const height = Math.max(1, getFrameHeight(decorated));
|
|
1196
|
-
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) }] : [];
|
|
1197
1236
|
const spans = fullFrameSpans(["input.base"], width, height);
|
|
1198
1237
|
return addFullFrameSpans(createFrame(decorated.lines, hitboxes, decorated.cursor, spans), resolveNodeStyle(node, context, { includeBase: false }).spanKinds);
|
|
1199
1238
|
}
|
|
@@ -1201,12 +1240,12 @@ function renderElementFrame(node, context) {
|
|
|
1201
1240
|
const decorated = addBorder(createFrame([rendered.line], [], rendered.cursor, rendered.spans), inputBorder);
|
|
1202
1241
|
const width = Math.max(1, getFrameWidth(decorated));
|
|
1203
1242
|
const height = Math.max(1, getFrameHeight(decorated));
|
|
1204
|
-
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) }] : [];
|
|
1205
1244
|
const spans = [...fullFrameSpans(["input.base", "input.focus"], width, height), ...decorated.spans];
|
|
1206
1245
|
return addFullFrameSpans(createFrame(decorated.lines, hitboxes, decorated.cursor, spans), resolveNodeStyle(node, context, { includeBase: false }).spanKinds);
|
|
1207
1246
|
}
|
|
1208
1247
|
case "terminal-editor":
|
|
1209
|
-
return addFullFrameSpans(renderEditorFrame(node), resolveNodeStyle(node, context).spanKinds);
|
|
1248
|
+
return addFullFrameSpans(renderEditorFrame(node, context), resolveNodeStyle(node, context).spanKinds);
|
|
1210
1249
|
case "terminal-button": {
|
|
1211
1250
|
const label = typeof node.props.label !== "undefined" ? plainText(node.props.label) : plainText(node.children.map(textContent).join(""));
|
|
1212
1251
|
const layoutStyle = resolveLayoutStyle("button.base", node, context);
|