@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/session.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { ANSI_ENTER_ALTERNATE_SCREEN, ANSI_EXIT_ALTERNATE_SCREEN, ANSI_HIDE_CURSOR, ANSI_SHOW_CURSOR, createAnsiDiffWriter, formatPlainFrame, toAnsiFrame } from "./ansi.js";
|
|
1
|
+
import { ANSI_DISABLE_MOUSE_REPORTING, ANSI_ENABLE_MOUSE_REPORTING, ANSI_ENTER_ALTERNATE_SCREEN, ANSI_EXIT_ALTERNATE_SCREEN, ANSI_HIDE_CURSOR, ANSI_SHOW_CURSOR, createAnsiDiffWriter, formatPlainFrame, toAnsiFrame } from "./ansi.js";
|
|
2
2
|
import { createSystemClipboardAdapter } from "./clipboard.js";
|
|
3
3
|
import { createEditorState, insertEditorText, moveEditorCursor, removeEditorBackward, removeEditorForward } from "./editor-state.js";
|
|
4
4
|
import { copySelection, hasSelection, insertText, moveCursorEnd, moveCursorHome, moveCursorLeft, moveCursorRight, moveCursorWordLeft, moveCursorWordRight, normalizeInputState, parseTerminalKey, removeBackward, removeForward, selectAll } from "./events.js";
|
|
5
5
|
import { createResolvedTerminalKeymap, resolveTerminalKeyBinding } from "./keymap.js";
|
|
6
6
|
import { mergeVertical } from "./layout.js";
|
|
7
|
-
import { cursorFromHitbox, parseTerminalInput, resolvePointerTarget } from "./mouse.js";
|
|
7
|
+
import { cursorFromHitbox, parseTerminalInput, parseTerminalMousePrefix, resolvePointerTarget } from "./mouse.js";
|
|
8
8
|
import { createOutputWriter } from "./output-writer.js";
|
|
9
9
|
import { parseBracketedPaste } from "./paste.js";
|
|
10
10
|
import { renderTerminalFrame } from "./render.js";
|
|
@@ -20,11 +20,17 @@ const KNOWN_TERMINAL_KEY_SEQUENCES = [
|
|
|
20
20
|
"\u001b[13;129u",
|
|
21
21
|
"\u001b[27;2;13~",
|
|
22
22
|
"\u001b[13;2~",
|
|
23
|
+
"\u001b[1;2A",
|
|
24
|
+
"\u001b[1;2B",
|
|
23
25
|
"\u001b[1;2C",
|
|
24
26
|
"\u001b[1;2D",
|
|
25
27
|
"\u001b[1;3C",
|
|
26
28
|
"\u001b[1;3D",
|
|
27
29
|
"\u001b[3~",
|
|
30
|
+
"\u001b[5~",
|
|
31
|
+
"\u001b[6~",
|
|
32
|
+
"\u001b[1~",
|
|
33
|
+
"\u001b[4~",
|
|
28
34
|
"\u001b[Z",
|
|
29
35
|
"\u001b[A",
|
|
30
36
|
"\u001b[B",
|
|
@@ -84,6 +90,7 @@ function resolveRuntimeOptions(options) {
|
|
|
84
90
|
stdout,
|
|
85
91
|
alternateScreen: options.alternateScreen ?? ownsInteractiveTTY,
|
|
86
92
|
hideCursor: options.hideCursor ?? ownsInteractiveTTY,
|
|
93
|
+
mouseReporting: ownsInteractiveTTY,
|
|
87
94
|
writesAnsi: runtime === "app" && Boolean(stdout)
|
|
88
95
|
};
|
|
89
96
|
}
|
|
@@ -95,7 +102,7 @@ function resolveTerminalSize(options, stdout) {
|
|
|
95
102
|
rows: validateTerminalDimension("rows", rows)
|
|
96
103
|
};
|
|
97
104
|
}
|
|
98
|
-
function applyInteractiveState(nodes, focusedId, inputStateById, editorStateById, listIndexById, scrollOffsetById, listHoverById, scrollHoverRowById) {
|
|
105
|
+
function applyInteractiveState(nodes, focusedId, inputStateById, editorStateById, listIndexById, listSelectedIndexById, listViewportOffsetById, scrollOffsetById, listHoverById, scrollHoverRowById) {
|
|
99
106
|
for (let i = 0; i < nodes.length; i += 1) {
|
|
100
107
|
const node = nodes[i];
|
|
101
108
|
if (node.type !== "element") {
|
|
@@ -117,7 +124,23 @@ function applyInteractiveState(nodes, focusedId, inputStateById, editorStateById
|
|
|
117
124
|
node.props.__editorState = current;
|
|
118
125
|
}
|
|
119
126
|
if (node.tag === "terminal-list" && id) {
|
|
120
|
-
node.props.
|
|
127
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
128
|
+
const activeIndex = listIndexById.get(id) || 0;
|
|
129
|
+
const clampedActiveIndex = Math.max(0, Math.min(Math.max(0, items.length - 1), activeIndex));
|
|
130
|
+
if (!listIndexById.has(id)) {
|
|
131
|
+
listIndexById.set(id, clampedActiveIndex);
|
|
132
|
+
}
|
|
133
|
+
const selectedIndex = node.props.showActive === false
|
|
134
|
+
? null
|
|
135
|
+
: listSelectedIndexById.has(id)
|
|
136
|
+
? listSelectedIndexById.get(id)
|
|
137
|
+
: clampedActiveIndex;
|
|
138
|
+
if (!listSelectedIndexById.has(id)) {
|
|
139
|
+
listSelectedIndexById.set(id, selectedIndex === null ? null : Math.max(0, Math.min(Math.max(0, items.length - 1), selectedIndex)));
|
|
140
|
+
}
|
|
141
|
+
node.props.__activeIndex = clampedActiveIndex;
|
|
142
|
+
node.props.__selectedIndex = selectedIndex === null ? null : Math.max(0, Math.min(Math.max(0, items.length - 1), selectedIndex));
|
|
143
|
+
node.props.__scrollOffset = listViewportOffsetById.get(id) || 0;
|
|
121
144
|
if (listHoverById.has(id)) {
|
|
122
145
|
node.props.__hoveredIndex = listHoverById.get(id);
|
|
123
146
|
}
|
|
@@ -128,7 +151,7 @@ function applyInteractiveState(nodes, focusedId, inputStateById, editorStateById
|
|
|
128
151
|
node.props.__hoveredRow = scrollHoverRowById.get(id);
|
|
129
152
|
}
|
|
130
153
|
}
|
|
131
|
-
applyInteractiveState(node.children, focusedId, inputStateById, editorStateById, listIndexById, scrollOffsetById, listHoverById, scrollHoverRowById);
|
|
154
|
+
applyInteractiveState(node.children, focusedId, inputStateById, editorStateById, listIndexById, listSelectedIndexById, listViewportOffsetById, scrollOffsetById, listHoverById, scrollHoverRowById);
|
|
132
155
|
}
|
|
133
156
|
}
|
|
134
157
|
export function mountTerminal(input, options = {}) {
|
|
@@ -148,6 +171,8 @@ export function mountTerminal(input, options = {}) {
|
|
|
148
171
|
const inputStateById = new Map();
|
|
149
172
|
const editorStateById = new Map();
|
|
150
173
|
const listIndexById = new Map();
|
|
174
|
+
const listSelectedIndexById = new Map();
|
|
175
|
+
const listViewportOffsetById = new Map();
|
|
151
176
|
const scrollOffsetById = new Map();
|
|
152
177
|
const listHoverById = new Map();
|
|
153
178
|
const scrollHoverRowById = new Map();
|
|
@@ -161,7 +186,7 @@ export function mountTerminal(input, options = {}) {
|
|
|
161
186
|
renderNow();
|
|
162
187
|
});
|
|
163
188
|
let currentTree = terminalRuntime.project();
|
|
164
|
-
applyInteractiveState(currentTree, focusedId, inputStateById, editorStateById, listIndexById, scrollOffsetById, listHoverById, scrollHoverRowById);
|
|
189
|
+
applyInteractiveState(currentTree, focusedId, inputStateById, editorStateById, listIndexById, listSelectedIndexById, listViewportOffsetById, scrollOffsetById, listHoverById, scrollHoverRowById);
|
|
165
190
|
let currentFrame = renderTreeFrame(currentTree);
|
|
166
191
|
let currentOutput = formatPlainFrame(currentFrame, { theme: options.theme }).trimEnd();
|
|
167
192
|
let currentHitboxes = currentFrame.hitboxes;
|
|
@@ -186,6 +211,9 @@ export function mountTerminal(input, options = {}) {
|
|
|
186
211
|
if (runtimeOptions.hideCursor) {
|
|
187
212
|
writes.push(ANSI_HIDE_CURSOR);
|
|
188
213
|
}
|
|
214
|
+
if (runtimeOptions.mouseReporting) {
|
|
215
|
+
writes.push(ANSI_ENABLE_MOUSE_REPORTING);
|
|
216
|
+
}
|
|
189
217
|
if (writes.length > 0) {
|
|
190
218
|
outputWriter.write(writes.join(""), { force: true });
|
|
191
219
|
}
|
|
@@ -195,6 +223,9 @@ export function mountTerminal(input, options = {}) {
|
|
|
195
223
|
return;
|
|
196
224
|
}
|
|
197
225
|
const writes = [];
|
|
226
|
+
if (runtimeOptions.mouseReporting) {
|
|
227
|
+
writes.push(ANSI_DISABLE_MOUSE_REPORTING);
|
|
228
|
+
}
|
|
198
229
|
if (runtimeOptions.hideCursor) {
|
|
199
230
|
writes.push(ANSI_SHOW_CURSOR);
|
|
200
231
|
}
|
|
@@ -223,7 +254,7 @@ export function mountTerminal(input, options = {}) {
|
|
|
223
254
|
focusedId = activeFocusables[0]?.props.id || null;
|
|
224
255
|
}
|
|
225
256
|
skipFocusContainmentOnce = false;
|
|
226
|
-
applyInteractiveState(currentTree, focusedId, inputStateById, editorStateById, listIndexById, scrollOffsetById, listHoverById, scrollHoverRowById);
|
|
257
|
+
applyInteractiveState(currentTree, focusedId, inputStateById, editorStateById, listIndexById, listSelectedIndexById, listViewportOffsetById, scrollOffsetById, listHoverById, scrollHoverRowById);
|
|
227
258
|
currentFrame = renderTreeFrame(currentTree);
|
|
228
259
|
currentOutput = formatPlainFrame(currentFrame, { theme: options.theme }).trimEnd();
|
|
229
260
|
currentHitboxes = currentFrame.hitboxes;
|
|
@@ -362,10 +393,18 @@ export function mountTerminal(input, options = {}) {
|
|
|
362
393
|
return 1;
|
|
363
394
|
}
|
|
364
395
|
function sourceRowFromHitbox(node, hitbox, y) {
|
|
365
|
-
|
|
396
|
+
if (node.tag === "terminal-list" && typeof hitbox.__listItemIndex === "number" && y >= hitbox.y1 && y <= hitbox.y2) {
|
|
397
|
+
return Math.max(1, Math.min(rowCountForNode(node), hitbox.__listItemIndex + 1));
|
|
398
|
+
}
|
|
399
|
+
const sourceY = hitbox.contentY ?? hitbox.y1;
|
|
400
|
+
const visibleRow = Math.max(1, y - sourceY + 1);
|
|
366
401
|
if (node.tag !== "terminal-list") {
|
|
367
402
|
return Math.max(1, Math.min(rowCountForNode(node), visibleRow));
|
|
368
403
|
}
|
|
404
|
+
const mappedIndex = hitbox.itemIndexes?.[visibleRow - 1];
|
|
405
|
+
if (typeof mappedIndex === "number") {
|
|
406
|
+
return Math.max(1, Math.min(rowCountForNode(node), mappedIndex + 1));
|
|
407
|
+
}
|
|
369
408
|
return Math.max(1, Math.min(rowCountForNode(node), visibleRow + (hitbox.itemOffset || 0)));
|
|
370
409
|
}
|
|
371
410
|
function shouldPointerCapture(node) {
|
|
@@ -406,6 +445,100 @@ export function mountTerminal(input, options = {}) {
|
|
|
406
445
|
emitCaptureEvent(next, "capturestart", source, row ?? hoveredRowForNode(next), x, y);
|
|
407
446
|
}
|
|
408
447
|
}
|
|
448
|
+
function listItemKey(node, index) {
|
|
449
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
450
|
+
const item = items[index];
|
|
451
|
+
if (typeof node.props.itemKey === "function" && typeof item !== "undefined") {
|
|
452
|
+
const key = node.props.itemKey(item, index);
|
|
453
|
+
if (typeof key === "string" || typeof key === "number") {
|
|
454
|
+
return String(key);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return undefined;
|
|
458
|
+
}
|
|
459
|
+
function listViewportRows(node) {
|
|
460
|
+
const context = renderContext();
|
|
461
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
462
|
+
const hitbox = node.props.id ? currentHitboxes.find((box) => box.id === node.props.id) : null;
|
|
463
|
+
const overscan = typeof node.props.overscan === "number" ? Math.max(0, Math.floor(node.props.overscan)) : 0;
|
|
464
|
+
const renderedRows = hitbox ? Math.max(1, hitbox.y2 - hitbox.y1 + 1 - overscan * 2) : null;
|
|
465
|
+
const sourceRows = Number(node.props.height || renderedRows || context.rows || items.length || 1);
|
|
466
|
+
if (!Number.isFinite(sourceRows) || !Number.isInteger(sourceRows) || sourceRows <= 0) {
|
|
467
|
+
return Math.max(1, items.length || 1);
|
|
468
|
+
}
|
|
469
|
+
return Math.max(1, Math.min(items.length || 1, sourceRows));
|
|
470
|
+
}
|
|
471
|
+
function clampListIndex(node, index) {
|
|
472
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
473
|
+
if (items.length === 0) {
|
|
474
|
+
return 0;
|
|
475
|
+
}
|
|
476
|
+
return Math.max(0, Math.min(items.length - 1, index));
|
|
477
|
+
}
|
|
478
|
+
function currentListActiveIndex(node) {
|
|
479
|
+
return clampListIndex(node, listIndexById.get(node.props.id || "") || 0);
|
|
480
|
+
}
|
|
481
|
+
function currentListSelectedIndex(node) {
|
|
482
|
+
const id = node.props.id || "";
|
|
483
|
+
if (node.props.showActive === false) {
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
if (listSelectedIndexById.has(id)) {
|
|
487
|
+
const selectedIndex = listSelectedIndexById.get(id);
|
|
488
|
+
return typeof selectedIndex === "number" ? clampListIndex(node, selectedIndex) : null;
|
|
489
|
+
}
|
|
490
|
+
return currentListActiveIndex(node);
|
|
491
|
+
}
|
|
492
|
+
function currentListViewportOffset(node) {
|
|
493
|
+
const id = node.props.id || "";
|
|
494
|
+
return Math.max(0, Math.min(listMaxViewportOffset(node), listViewportOffsetById.get(id) || 0));
|
|
495
|
+
}
|
|
496
|
+
function listStatePayload(node) {
|
|
497
|
+
return {
|
|
498
|
+
activeIndex: currentListActiveIndex(node),
|
|
499
|
+
selectedIndex: currentListSelectedIndex(node),
|
|
500
|
+
viewportOffset: currentListViewportOffset(node),
|
|
501
|
+
viewportRows: listViewportRows(node)
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
function listMaxViewportOffset(node) {
|
|
505
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
506
|
+
return Math.max(0, items.length - listViewportRows(node));
|
|
507
|
+
}
|
|
508
|
+
function setListViewportOffset(node, offset, emit = true) {
|
|
509
|
+
const id = node.props.id;
|
|
510
|
+
if (!id) {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
const nextOffset = Math.max(0, Math.min(listMaxViewportOffset(node), offset));
|
|
514
|
+
const previous = listViewportOffsetById.get(id) || 0;
|
|
515
|
+
listViewportOffsetById.set(id, nextOffset);
|
|
516
|
+
if (emit && nextOffset !== previous) {
|
|
517
|
+
const payload = {
|
|
518
|
+
type: "viewportchange",
|
|
519
|
+
id,
|
|
520
|
+
offset: nextOffset,
|
|
521
|
+
rows: listViewportRows(node),
|
|
522
|
+
...listStatePayload(node)
|
|
523
|
+
};
|
|
524
|
+
dispatchNodeEvent(node, "viewportchange", payload);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
function ensureListActiveVisible(node, activeIndex) {
|
|
528
|
+
const id = node.props.id;
|
|
529
|
+
if (!id || !node.props.virtualized) {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
const currentOffset = listViewportOffsetById.get(id) || 0;
|
|
533
|
+
const rows = listViewportRows(node);
|
|
534
|
+
if (activeIndex < currentOffset) {
|
|
535
|
+
setListViewportOffset(node, activeIndex);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
if (activeIndex >= currentOffset + rows) {
|
|
539
|
+
setListViewportOffset(node, activeIndex - rows + 1);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
409
542
|
function dispatchListPressEvent(node, type, index) {
|
|
410
543
|
const id = node.props.id;
|
|
411
544
|
if (!id) {
|
|
@@ -415,7 +548,10 @@ export function mountTerminal(input, options = {}) {
|
|
|
415
548
|
if (typeof items[index] === "undefined") {
|
|
416
549
|
return false;
|
|
417
550
|
}
|
|
418
|
-
const
|
|
551
|
+
const key = listItemKey(node, index);
|
|
552
|
+
const payload = typeof key === "undefined"
|
|
553
|
+
? { type, id, index, value: items[index], ...listStatePayload(node) }
|
|
554
|
+
: { type, id, index, key, value: items[index], ...listStatePayload(node) };
|
|
419
555
|
return dispatchNodeEvent(node, type, payload);
|
|
420
556
|
}
|
|
421
557
|
function dispatchButtonPressEvent(node, type) {
|
|
@@ -425,6 +561,13 @@ export function mountTerminal(input, options = {}) {
|
|
|
425
561
|
}
|
|
426
562
|
return dispatchNodeEvent(node, type, { type, id });
|
|
427
563
|
}
|
|
564
|
+
function dispatchHitboxButtonPressEvent(hitbox, type) {
|
|
565
|
+
if (type !== "press" || typeof hitbox.__pressHandler !== "function") {
|
|
566
|
+
return false;
|
|
567
|
+
}
|
|
568
|
+
hitbox.__pressHandler({ type, id: hitbox.id });
|
|
569
|
+
return true;
|
|
570
|
+
}
|
|
428
571
|
function dispatchListPointerPressEvent(node, type, row) {
|
|
429
572
|
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
430
573
|
const index = Math.max(0, Math.min(items.length - 1, row - 1));
|
|
@@ -461,7 +604,10 @@ export function mountTerminal(input, options = {}) {
|
|
|
461
604
|
if (typeof items[index] === "undefined") {
|
|
462
605
|
return;
|
|
463
606
|
}
|
|
464
|
-
const
|
|
607
|
+
const key = listItemKey(node, index);
|
|
608
|
+
const payload = typeof key === "undefined"
|
|
609
|
+
? { type, id: node.props.id, row: index + 1, index, value: items[index], x, y }
|
|
610
|
+
: { type, id: node.props.id, row: index + 1, index, key, value: items[index], x, y };
|
|
465
611
|
dispatchNodeEvent(node, type, payload);
|
|
466
612
|
return;
|
|
467
613
|
}
|
|
@@ -551,28 +697,74 @@ export function mountTerminal(input, options = {}) {
|
|
|
551
697
|
setSemanticHoverFromHitbox(hitbox.id, x, y);
|
|
552
698
|
return rerender();
|
|
553
699
|
}
|
|
554
|
-
function
|
|
700
|
+
function moveListActiveTo(node, nextIndex) {
|
|
555
701
|
const id = node.props.id;
|
|
556
702
|
if (!id) {
|
|
557
703
|
return currentOutput;
|
|
558
704
|
}
|
|
559
705
|
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
560
|
-
const
|
|
561
|
-
|
|
706
|
+
const clampedIndex = clampListIndex(node, nextIndex);
|
|
707
|
+
listIndexById.set(id, clampedIndex);
|
|
708
|
+
ensureListActiveVisible(node, clampedIndex);
|
|
709
|
+
const key = listItemKey(node, clampedIndex);
|
|
710
|
+
const payload = typeof key === "undefined"
|
|
711
|
+
? { type: "change", id, index: clampedIndex, value: items[clampedIndex], ...listStatePayload(node) }
|
|
712
|
+
: { type: "change", id, index: clampedIndex, key, value: items[clampedIndex], ...listStatePayload(node) };
|
|
713
|
+
dispatchNodeEvent(node, "change", payload);
|
|
714
|
+
return rerender();
|
|
715
|
+
}
|
|
716
|
+
function changeListSelection(node, direction) {
|
|
717
|
+
return moveListActiveTo(node, currentListActiveIndex(node) + direction);
|
|
718
|
+
}
|
|
719
|
+
function pageListSelection(node, direction) {
|
|
720
|
+
const id = node.props.id;
|
|
721
|
+
if (!id) {
|
|
722
|
+
return currentOutput;
|
|
723
|
+
}
|
|
724
|
+
const nextIndex = clampListIndex(node, currentListActiveIndex(node) + direction * listViewportRows(node));
|
|
562
725
|
listIndexById.set(id, nextIndex);
|
|
563
|
-
|
|
726
|
+
setListViewportOffset(node, nextIndex);
|
|
727
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
728
|
+
const key = listItemKey(node, nextIndex);
|
|
729
|
+
const payload = typeof key === "undefined"
|
|
730
|
+
? { type: "change", id, index: nextIndex, value: items[nextIndex], ...listStatePayload(node) }
|
|
731
|
+
: { type: "change", id, index: nextIndex, key, value: items[nextIndex], ...listStatePayload(node) };
|
|
564
732
|
dispatchNodeEvent(node, "change", payload);
|
|
565
733
|
return rerender();
|
|
566
734
|
}
|
|
735
|
+
function moveListSelectionToBoundary(node, boundary) {
|
|
736
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
737
|
+
return moveListActiveTo(node, boundary === "start" ? 0 : Math.max(0, items.length - 1));
|
|
738
|
+
}
|
|
567
739
|
function pressListSelection(node) {
|
|
568
740
|
const id = node.props.id;
|
|
569
741
|
if (!id) {
|
|
570
742
|
return currentOutput;
|
|
571
743
|
}
|
|
572
|
-
const currentIndex =
|
|
744
|
+
const currentIndex = currentListActiveIndex(node);
|
|
745
|
+
if (node.props.showActive !== false) {
|
|
746
|
+
listSelectedIndexById.set(id, currentIndex);
|
|
747
|
+
}
|
|
573
748
|
dispatchListPressEvent(node, "press", currentIndex);
|
|
574
749
|
return rerender();
|
|
575
750
|
}
|
|
751
|
+
function pressListPointerSelection(node, row) {
|
|
752
|
+
const id = node.props.id;
|
|
753
|
+
if (!id) {
|
|
754
|
+
return currentOutput;
|
|
755
|
+
}
|
|
756
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
757
|
+
const index = Math.max(0, Math.min(items.length - 1, row - 1));
|
|
758
|
+
if (typeof items[index] === "undefined") {
|
|
759
|
+
return currentOutput;
|
|
760
|
+
}
|
|
761
|
+
listIndexById.set(id, index);
|
|
762
|
+
if (node.props.showActive !== false) {
|
|
763
|
+
listSelectedIndexById.set(id, index);
|
|
764
|
+
}
|
|
765
|
+
dispatchListPressEvent(node, "press", index);
|
|
766
|
+
return rerender();
|
|
767
|
+
}
|
|
576
768
|
function scrollFocusedNode(node, direction) {
|
|
577
769
|
const id = node.props.id;
|
|
578
770
|
if (!id) {
|
|
@@ -597,7 +789,9 @@ export function mountTerminal(input, options = {}) {
|
|
|
597
789
|
return scrollFocusedNode(node, direction);
|
|
598
790
|
}
|
|
599
791
|
if (node?.tag === "terminal-list" && node.props.virtualized) {
|
|
600
|
-
|
|
792
|
+
const currentOffset = listViewportOffsetById.get(node.props.id || "") || 0;
|
|
793
|
+
setListViewportOffset(node, currentOffset + direction);
|
|
794
|
+
return rerender();
|
|
601
795
|
}
|
|
602
796
|
return rerender();
|
|
603
797
|
}
|
|
@@ -763,6 +957,14 @@ export function mountTerminal(input, options = {}) {
|
|
|
763
957
|
return node?.tag === "terminal-list" ? changeListSelection(node, -1) : currentOutput;
|
|
764
958
|
case "list.next":
|
|
765
959
|
return node?.tag === "terminal-list" ? changeListSelection(node, 1) : currentOutput;
|
|
960
|
+
case "list.pageUp":
|
|
961
|
+
return node?.tag === "terminal-list" ? pageListSelection(node, -1) : currentOutput;
|
|
962
|
+
case "list.pageDown":
|
|
963
|
+
return node?.tag === "terminal-list" ? pageListSelection(node, 1) : currentOutput;
|
|
964
|
+
case "list.home":
|
|
965
|
+
return node?.tag === "terminal-list" ? moveListSelectionToBoundary(node, "start") : currentOutput;
|
|
966
|
+
case "list.end":
|
|
967
|
+
return node?.tag === "terminal-list" ? moveListSelectionToBoundary(node, "end") : currentOutput;
|
|
766
968
|
case "list.press":
|
|
767
969
|
return node?.tag === "terminal-list" ? pressListSelection(node) : currentOutput;
|
|
768
970
|
case "scroll.up":
|
|
@@ -798,6 +1000,19 @@ export function mountTerminal(input, options = {}) {
|
|
|
798
1000
|
return;
|
|
799
1001
|
}
|
|
800
1002
|
terminalSize = nextSize;
|
|
1003
|
+
const focusedNode = findFocused(currentTree, focusedId);
|
|
1004
|
+
if (focusedNode?.tag === "terminal-list" && focusedNode.props.id && focusedNode.props.virtualized) {
|
|
1005
|
+
const items = Array.isArray(focusedNode.props.items) ? focusedNode.props.items : [];
|
|
1006
|
+
const rows = Math.max(1, Math.min(items.length || 1, Number(focusedNode.props.height || terminalSize.rows || items.length || 1)));
|
|
1007
|
+
const activeIndex = currentListActiveIndex(focusedNode);
|
|
1008
|
+
const currentOffset = listViewportOffsetById.get(focusedNode.props.id) || 0;
|
|
1009
|
+
if (activeIndex < currentOffset) {
|
|
1010
|
+
listViewportOffsetById.set(focusedNode.props.id, activeIndex);
|
|
1011
|
+
}
|
|
1012
|
+
else if (activeIndex >= currentOffset + rows) {
|
|
1013
|
+
listViewportOffsetById.set(focusedNode.props.id, Math.max(0, activeIndex - rows + 1));
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
801
1016
|
rerender();
|
|
802
1017
|
},
|
|
803
1018
|
update() {
|
|
@@ -888,7 +1103,14 @@ export function mountTerminal(input, options = {}) {
|
|
|
888
1103
|
return currentOutput;
|
|
889
1104
|
}
|
|
890
1105
|
if (hitbox.tag === "terminal-button") {
|
|
891
|
-
|
|
1106
|
+
const node = findFocusableById(currentTree, hitbox.id);
|
|
1107
|
+
if (node?.tag === "terminal-button") {
|
|
1108
|
+
return this.click(hitbox.id);
|
|
1109
|
+
}
|
|
1110
|
+
if (dispatchHitboxButtonPressEvent(hitbox, "press")) {
|
|
1111
|
+
return rerender();
|
|
1112
|
+
}
|
|
1113
|
+
return currentOutput;
|
|
892
1114
|
}
|
|
893
1115
|
setSemanticHoverFromHitbox(hitbox.id, x, y);
|
|
894
1116
|
focusedId = hitbox.id;
|
|
@@ -896,6 +1118,12 @@ export function mountTerminal(input, options = {}) {
|
|
|
896
1118
|
mouseSelectionId = hitbox.id;
|
|
897
1119
|
return setCursorFromHitbox(hitbox.id, x, false);
|
|
898
1120
|
}
|
|
1121
|
+
if (hitbox.tag === "terminal-list") {
|
|
1122
|
+
const node = findFocusableById(currentTree, hitbox.id);
|
|
1123
|
+
if (node?.tag === "terminal-list") {
|
|
1124
|
+
return pressListPointerSelection(node, sourceRowFromHitbox(node, hitbox, y));
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
899
1127
|
rerender();
|
|
900
1128
|
return currentOutput;
|
|
901
1129
|
},
|
|
@@ -1082,6 +1310,79 @@ export function mountTerminal(input, options = {}) {
|
|
|
1082
1310
|
}
|
|
1083
1311
|
return currentOutput;
|
|
1084
1312
|
}
|
|
1313
|
+
function processParsedMouseInput(parsed) {
|
|
1314
|
+
if (parsed.action === "press") {
|
|
1315
|
+
const hitbox = resolvePointerTarget(currentHitboxes, parsed.x, parsed.y);
|
|
1316
|
+
if (isContextMouseButton(parsed.button)) {
|
|
1317
|
+
lastPrimaryPress = null;
|
|
1318
|
+
contextPressAt(parsed.x, parsed.y);
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
if (hitbox?.tag === "terminal-input") {
|
|
1322
|
+
mouseSelectionId = hitbox.id;
|
|
1323
|
+
}
|
|
1324
|
+
const node = hitbox ? findFocusableById(currentTree, hitbox.id) : null;
|
|
1325
|
+
if (hitbox && node && shouldPointerCapture(node)) {
|
|
1326
|
+
setPointerCapture(hitbox.id, "press", sourceRowFromHitbox(node, hitbox, parsed.y), parsed.x, parsed.y);
|
|
1327
|
+
}
|
|
1328
|
+
const isPrimaryPress = isPrimaryMouseButton(parsed.button);
|
|
1329
|
+
const isDoublePressEligible = Boolean(hitbox
|
|
1330
|
+
&& node
|
|
1331
|
+
&& (hitbox.tag === "terminal-button" || hitbox.tag === "terminal-list"));
|
|
1332
|
+
const primaryPressRow = hitbox && node && hitbox.tag === "terminal-list"
|
|
1333
|
+
? sourceRowFromHitbox(node, hitbox, parsed.y)
|
|
1334
|
+
: null;
|
|
1335
|
+
const shouldDispatchDoublePress = Boolean(isPrimaryPress
|
|
1336
|
+
&& isDoublePressEligible
|
|
1337
|
+
&& hitbox
|
|
1338
|
+
&& isDoublePrimaryPress(hitbox, primaryPressRow));
|
|
1339
|
+
if (isPrimaryPress && !isDoublePressEligible) {
|
|
1340
|
+
lastPrimaryPress = null;
|
|
1341
|
+
}
|
|
1342
|
+
if (hitbox && shouldDispatchDoublePress && hitbox.tag === "terminal-list") {
|
|
1343
|
+
doublePressAt(hitbox, parsed.x, parsed.y);
|
|
1344
|
+
}
|
|
1345
|
+
else {
|
|
1346
|
+
session.clickAt(parsed.x, parsed.y);
|
|
1347
|
+
if (hitbox && shouldDispatchDoublePress) {
|
|
1348
|
+
doublePressAt(hitbox, parsed.x, parsed.y);
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
else if (parsed.action === "drag") {
|
|
1353
|
+
if (mouseSelectionId) {
|
|
1354
|
+
setCursorFromHitbox(mouseSelectionId, parsed.x, true);
|
|
1355
|
+
}
|
|
1356
|
+
else {
|
|
1357
|
+
hoverAt(parsed.x, parsed.y);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
else if (parsed.action === "release") {
|
|
1361
|
+
mouseSelectionId = null;
|
|
1362
|
+
const capturedId = pointerCaptureId;
|
|
1363
|
+
const releaseHitbox = resolvePointerTarget(currentHitboxes, parsed.x, parsed.y);
|
|
1364
|
+
const releaseNode = releaseHitbox ? findFocusableById(currentTree, releaseHitbox.id) : null;
|
|
1365
|
+
const releaseRow = releaseHitbox && releaseNode
|
|
1366
|
+
? sourceRowFromHitbox(releaseNode, releaseHitbox, parsed.y)
|
|
1367
|
+
: null;
|
|
1368
|
+
setPointerCapture(null, "release", capturedId && releaseHitbox?.id === capturedId ? releaseRow : null, parsed.x, parsed.y);
|
|
1369
|
+
const hitbox = resolvePointerTarget(currentHitboxes, parsed.x, parsed.y);
|
|
1370
|
+
if (capturedId && hitbox && hitbox.id === capturedId && (hitbox.tag === "terminal-list" || hitbox.tag === "terminal-scroll")) {
|
|
1371
|
+
setSemanticHoverFromHitbox(hitbox.id, parsed.x, parsed.y);
|
|
1372
|
+
rerender();
|
|
1373
|
+
}
|
|
1374
|
+
else {
|
|
1375
|
+
clearSemanticHover(undefined, parsed.x, parsed.y);
|
|
1376
|
+
rerender();
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
else if (parsed.action === "wheel-up") {
|
|
1380
|
+
wheelAt(parsed.x, parsed.y, -1);
|
|
1381
|
+
}
|
|
1382
|
+
else if (parsed.action === "wheel-down") {
|
|
1383
|
+
wheelAt(parsed.x, parsed.y, 1);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1085
1386
|
function processInputStream(value) {
|
|
1086
1387
|
if (!value) {
|
|
1087
1388
|
return;
|
|
@@ -1133,74 +1434,15 @@ export function mountTerminal(input, options = {}) {
|
|
|
1133
1434
|
processInputStream(paste.rest);
|
|
1134
1435
|
return;
|
|
1135
1436
|
}
|
|
1437
|
+
const parsedMouse = parseTerminalMousePrefix(value);
|
|
1438
|
+
if (parsedMouse) {
|
|
1439
|
+
processParsedMouseInput(parsedMouse.input);
|
|
1440
|
+
processInputStream(parsedMouse.rest);
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1136
1443
|
const parsed = parseTerminalInput(value);
|
|
1137
1444
|
if (parsed.type === "mouse") {
|
|
1138
|
-
|
|
1139
|
-
const hitbox = resolvePointerTarget(currentHitboxes, parsed.x, parsed.y);
|
|
1140
|
-
if (isContextMouseButton(parsed.button)) {
|
|
1141
|
-
lastPrimaryPress = null;
|
|
1142
|
-
contextPressAt(parsed.x, parsed.y);
|
|
1143
|
-
return;
|
|
1144
|
-
}
|
|
1145
|
-
if (hitbox?.tag === "terminal-input") {
|
|
1146
|
-
mouseSelectionId = hitbox.id;
|
|
1147
|
-
}
|
|
1148
|
-
const node = hitbox ? findFocusableById(currentTree, hitbox.id) : null;
|
|
1149
|
-
if (hitbox && node && shouldPointerCapture(node)) {
|
|
1150
|
-
setPointerCapture(hitbox.id, "press", sourceRowFromHitbox(node, hitbox, parsed.y), parsed.x, parsed.y);
|
|
1151
|
-
}
|
|
1152
|
-
const isPrimaryPress = isPrimaryMouseButton(parsed.button);
|
|
1153
|
-
const isDoublePressEligible = Boolean(hitbox
|
|
1154
|
-
&& node
|
|
1155
|
-
&& (hitbox.tag === "terminal-button" || hitbox.tag === "terminal-list"));
|
|
1156
|
-
const primaryPressRow = hitbox && node && hitbox.tag === "terminal-list"
|
|
1157
|
-
? sourceRowFromHitbox(node, hitbox, parsed.y)
|
|
1158
|
-
: null;
|
|
1159
|
-
const shouldDispatchDoublePress = Boolean(isPrimaryPress
|
|
1160
|
-
&& isDoublePressEligible
|
|
1161
|
-
&& hitbox
|
|
1162
|
-
&& isDoublePrimaryPress(hitbox, primaryPressRow));
|
|
1163
|
-
if (isPrimaryPress && !isDoublePressEligible) {
|
|
1164
|
-
lastPrimaryPress = null;
|
|
1165
|
-
}
|
|
1166
|
-
session.clickAt(parsed.x, parsed.y);
|
|
1167
|
-
if (hitbox && shouldDispatchDoublePress) {
|
|
1168
|
-
doublePressAt(hitbox, parsed.x, parsed.y);
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
else if (parsed.action === "drag") {
|
|
1172
|
-
if (mouseSelectionId) {
|
|
1173
|
-
setCursorFromHitbox(mouseSelectionId, parsed.x, true);
|
|
1174
|
-
}
|
|
1175
|
-
else {
|
|
1176
|
-
hoverAt(parsed.x, parsed.y);
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
else if (parsed.action === "release") {
|
|
1180
|
-
mouseSelectionId = null;
|
|
1181
|
-
const capturedId = pointerCaptureId;
|
|
1182
|
-
const releaseHitbox = resolvePointerTarget(currentHitboxes, parsed.x, parsed.y);
|
|
1183
|
-
const releaseNode = releaseHitbox ? findFocusableById(currentTree, releaseHitbox.id) : null;
|
|
1184
|
-
const releaseRow = releaseHitbox && releaseNode
|
|
1185
|
-
? sourceRowFromHitbox(releaseNode, releaseHitbox, parsed.y)
|
|
1186
|
-
: null;
|
|
1187
|
-
setPointerCapture(null, "release", capturedId && releaseHitbox?.id === capturedId ? releaseRow : null, parsed.x, parsed.y);
|
|
1188
|
-
const hitbox = resolvePointerTarget(currentHitboxes, parsed.x, parsed.y);
|
|
1189
|
-
if (capturedId && hitbox && hitbox.id === capturedId && (hitbox.tag === "terminal-list" || hitbox.tag === "terminal-scroll")) {
|
|
1190
|
-
setSemanticHoverFromHitbox(hitbox.id, parsed.x, parsed.y);
|
|
1191
|
-
rerender();
|
|
1192
|
-
}
|
|
1193
|
-
else {
|
|
1194
|
-
clearSemanticHover(undefined, parsed.x, parsed.y);
|
|
1195
|
-
rerender();
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
else if (parsed.action === "wheel-up") {
|
|
1199
|
-
wheelAt(parsed.x, parsed.y, -1);
|
|
1200
|
-
}
|
|
1201
|
-
else if (parsed.action === "wheel-down") {
|
|
1202
|
-
wheelAt(parsed.x, parsed.y, 1);
|
|
1203
|
-
}
|
|
1445
|
+
processParsedMouseInput(parsed);
|
|
1204
1446
|
return;
|
|
1205
1447
|
}
|
|
1206
1448
|
processKeyStream(value);
|