@valyrianjs/terminal 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ansi.d.ts +2 -0
- package/dist/ansi.d.ts.map +1 -1
- package/dist/ansi.js +12 -0
- package/dist/ansi.js.map +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +6 -2
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- 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.map +1 -1
- package/dist/layout.js +2 -1
- 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 +30 -16
- package/dist/mouse.js.map +1 -1
- package/dist/primitives.d.ts +8 -3
- 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 +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +290 -51
- package/dist/render.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +13 -11
- package/dist/runtime.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +434 -65
- package/dist/session.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 +61 -13
- package/dist/types.d.ts.map +1 -1
- package/docs/api-reference.md +40 -28
- package/docs/cookbook.md +2 -2
- package/docs/core-concepts.md +1 -1
- package/docs/interaction-model.md +18 -6
- package/docs/primitive-gallery.md +19 -10
- package/llms-full.txt +80 -47
- package/package.json +1 -1
- package/src/ansi.ts +12 -0
- package/src/events.ts +4 -2
- package/src/index.ts +3 -0
- package/src/keymap.ts +4 -2
- package/src/layout.ts +2 -1
- package/src/mouse.ts +31 -15
- package/src/primitives.ts +15 -5
- package/src/render.ts +320 -52
- package/src/runtime.ts +13 -11
- package/src/session.ts +469 -59
- package/src/theme.ts +3 -0
- package/src/tree.ts +19 -4
- package/src/types.ts +72 -12
package/src/session.ts
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";
|
|
@@ -24,11 +24,14 @@ import type {
|
|
|
24
24
|
TerminalCaptureEventPayload,
|
|
25
25
|
TerminalClipboardAdapter,
|
|
26
26
|
TerminalFocusNode,
|
|
27
|
+
TerminalHitbox,
|
|
27
28
|
TerminalListChangeEventPayload,
|
|
28
29
|
TerminalListPressEventPayload,
|
|
30
|
+
TerminalListViewportChangeEventPayload,
|
|
29
31
|
TerminalMountOptions,
|
|
30
32
|
TerminalOutputStream,
|
|
31
33
|
TerminalNode,
|
|
34
|
+
ParsedTerminalInput,
|
|
32
35
|
TerminalPointerSource,
|
|
33
36
|
TerminalRowPointerEventPayload,
|
|
34
37
|
TerminalSession,
|
|
@@ -43,6 +46,7 @@ interface ResolvedRuntimeOptions {
|
|
|
43
46
|
stdout?: TerminalOutputStream;
|
|
44
47
|
alternateScreen: boolean;
|
|
45
48
|
hideCursor: boolean;
|
|
49
|
+
mouseReporting: boolean;
|
|
46
50
|
writesAnsi: boolean;
|
|
47
51
|
}
|
|
48
52
|
|
|
@@ -59,6 +63,10 @@ const KNOWN_TERMINAL_KEY_SEQUENCES = [
|
|
|
59
63
|
"\u001b[1;3C",
|
|
60
64
|
"\u001b[1;3D",
|
|
61
65
|
"\u001b[3~",
|
|
66
|
+
"\u001b[5~",
|
|
67
|
+
"\u001b[6~",
|
|
68
|
+
"\u001b[1~",
|
|
69
|
+
"\u001b[4~",
|
|
62
70
|
"\u001b[Z",
|
|
63
71
|
"\u001b[A",
|
|
64
72
|
"\u001b[B",
|
|
@@ -71,6 +79,7 @@ const KNOWN_TERMINAL_KEY_SEQUENCES = [
|
|
|
71
79
|
];
|
|
72
80
|
const ESCAPE = "\u001b";
|
|
73
81
|
const CSI_PREFIX = "\u001b[";
|
|
82
|
+
const DOUBLE_PRESS_INTERVAL_MS = 500;
|
|
74
83
|
|
|
75
84
|
function isBracketedPasteStartPrefix(value: string) {
|
|
76
85
|
return value.length > 0 && value.length < BRACKETED_PASTE_START.length && BRACKETED_PASTE_START.startsWith(value);
|
|
@@ -129,6 +138,7 @@ function resolveRuntimeOptions(options: TerminalMountOptions): ResolvedRuntimeOp
|
|
|
129
138
|
stdout,
|
|
130
139
|
alternateScreen: options.alternateScreen ?? ownsInteractiveTTY,
|
|
131
140
|
hideCursor: options.hideCursor ?? ownsInteractiveTTY,
|
|
141
|
+
mouseReporting: ownsInteractiveTTY,
|
|
132
142
|
writesAnsi: runtime === "app" && Boolean(stdout)
|
|
133
143
|
};
|
|
134
144
|
}
|
|
@@ -148,6 +158,8 @@ function applyInteractiveState(
|
|
|
148
158
|
inputStateById: Map<string, InputInteractionState>,
|
|
149
159
|
editorStateById: Map<string, EditorState>,
|
|
150
160
|
listIndexById: Map<string, number>,
|
|
161
|
+
listSelectedIndexById: Map<string, number | null>,
|
|
162
|
+
listViewportOffsetById: Map<string, number>,
|
|
151
163
|
scrollOffsetById: Map<string, number>,
|
|
152
164
|
listHoverById: Map<string, number>,
|
|
153
165
|
scrollHoverRowById: Map<string, number>
|
|
@@ -174,7 +186,23 @@ function applyInteractiveState(
|
|
|
174
186
|
node.props.__editorState = current;
|
|
175
187
|
}
|
|
176
188
|
if (node.tag === "terminal-list" && id) {
|
|
177
|
-
node.props.
|
|
189
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
190
|
+
const activeIndex = listIndexById.get(id) || 0;
|
|
191
|
+
const clampedActiveIndex = Math.max(0, Math.min(Math.max(0, items.length - 1), activeIndex));
|
|
192
|
+
if (!listIndexById.has(id)) {
|
|
193
|
+
listIndexById.set(id, clampedActiveIndex);
|
|
194
|
+
}
|
|
195
|
+
const selectedIndex = node.props.showActive === false
|
|
196
|
+
? null
|
|
197
|
+
: listSelectedIndexById.has(id)
|
|
198
|
+
? listSelectedIndexById.get(id)!
|
|
199
|
+
: clampedActiveIndex;
|
|
200
|
+
if (!listSelectedIndexById.has(id)) {
|
|
201
|
+
listSelectedIndexById.set(id, selectedIndex === null ? null : Math.max(0, Math.min(Math.max(0, items.length - 1), selectedIndex)));
|
|
202
|
+
}
|
|
203
|
+
node.props.__activeIndex = clampedActiveIndex;
|
|
204
|
+
node.props.__selectedIndex = selectedIndex === null ? null : Math.max(0, Math.min(Math.max(0, items.length - 1), selectedIndex));
|
|
205
|
+
node.props.__scrollOffset = listViewportOffsetById.get(id) || 0;
|
|
178
206
|
if (listHoverById.has(id)) {
|
|
179
207
|
node.props.__hoveredIndex = listHoverById.get(id);
|
|
180
208
|
}
|
|
@@ -185,7 +213,7 @@ function applyInteractiveState(
|
|
|
185
213
|
node.props.__hoveredRow = scrollHoverRowById.get(id);
|
|
186
214
|
}
|
|
187
215
|
}
|
|
188
|
-
applyInteractiveState(node.children, focusedId, inputStateById, editorStateById, listIndexById, scrollOffsetById, listHoverById, scrollHoverRowById);
|
|
216
|
+
applyInteractiveState(node.children, focusedId, inputStateById, editorStateById, listIndexById, listSelectedIndexById, listViewportOffsetById, scrollOffsetById, listHoverById, scrollHoverRowById);
|
|
189
217
|
}
|
|
190
218
|
}
|
|
191
219
|
|
|
@@ -198,6 +226,7 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
198
226
|
let pendingPasteChunk = "";
|
|
199
227
|
let pendingKeyChunk = "";
|
|
200
228
|
let pendingEscapeFlush: ReturnType<typeof setTimeout> | null = null;
|
|
229
|
+
let lastPrimaryPress: { id: string; tag: string; row: number | null; at: number } | null = null;
|
|
201
230
|
let destroyed = false;
|
|
202
231
|
let autoProjectionEnabled = false;
|
|
203
232
|
let suppressAutoProjection = false;
|
|
@@ -205,6 +234,8 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
205
234
|
const inputStateById = new Map<string, InputInteractionState>();
|
|
206
235
|
const editorStateById = new Map<string, EditorState>();
|
|
207
236
|
const listIndexById = new Map<string, number>();
|
|
237
|
+
const listSelectedIndexById = new Map<string, number | null>();
|
|
238
|
+
const listViewportOffsetById = new Map<string, number>();
|
|
208
239
|
const scrollOffsetById = new Map<string, number>();
|
|
209
240
|
const listHoverById = new Map<string, number>();
|
|
210
241
|
const scrollHoverRowById = new Map<string, number>();
|
|
@@ -218,7 +249,7 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
218
249
|
renderNow();
|
|
219
250
|
});
|
|
220
251
|
let currentTree = terminalRuntime.project();
|
|
221
|
-
applyInteractiveState(currentTree, focusedId, inputStateById, editorStateById, listIndexById, scrollOffsetById, listHoverById, scrollHoverRowById);
|
|
252
|
+
applyInteractiveState(currentTree, focusedId, inputStateById, editorStateById, listIndexById, listSelectedIndexById, listViewportOffsetById, scrollOffsetById, listHoverById, scrollHoverRowById);
|
|
222
253
|
let currentFrame = renderTreeFrame(currentTree);
|
|
223
254
|
let currentOutput = formatPlainFrame(currentFrame, { theme: options.theme }).trimEnd();
|
|
224
255
|
let currentHitboxes = currentFrame.hitboxes;
|
|
@@ -247,6 +278,9 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
247
278
|
if (runtimeOptions.hideCursor) {
|
|
248
279
|
writes.push(ANSI_HIDE_CURSOR);
|
|
249
280
|
}
|
|
281
|
+
if (runtimeOptions.mouseReporting) {
|
|
282
|
+
writes.push(ANSI_ENABLE_MOUSE_REPORTING);
|
|
283
|
+
}
|
|
250
284
|
if (writes.length > 0) {
|
|
251
285
|
outputWriter.write(writes.join(""), { force: true });
|
|
252
286
|
}
|
|
@@ -257,6 +291,9 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
257
291
|
return;
|
|
258
292
|
}
|
|
259
293
|
const writes: string[] = [];
|
|
294
|
+
if (runtimeOptions.mouseReporting) {
|
|
295
|
+
writes.push(ANSI_DISABLE_MOUSE_REPORTING);
|
|
296
|
+
}
|
|
260
297
|
if (runtimeOptions.hideCursor) {
|
|
261
298
|
writes.push(ANSI_SHOW_CURSOR);
|
|
262
299
|
}
|
|
@@ -287,7 +324,7 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
287
324
|
focusedId = activeFocusables[0]?.props.id || null;
|
|
288
325
|
}
|
|
289
326
|
skipFocusContainmentOnce = false;
|
|
290
|
-
applyInteractiveState(currentTree, focusedId, inputStateById, editorStateById, listIndexById, scrollOffsetById, listHoverById, scrollHoverRowById);
|
|
327
|
+
applyInteractiveState(currentTree, focusedId, inputStateById, editorStateById, listIndexById, listSelectedIndexById, listViewportOffsetById, scrollOffsetById, listHoverById, scrollHoverRowById);
|
|
291
328
|
currentFrame = renderTreeFrame(currentTree);
|
|
292
329
|
currentOutput = formatPlainFrame(currentFrame, { theme: options.theme }).trimEnd();
|
|
293
330
|
currentHitboxes = currentFrame.hitboxes;
|
|
@@ -441,12 +478,22 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
441
478
|
return 1;
|
|
442
479
|
}
|
|
443
480
|
|
|
444
|
-
function sourceRowFromHitbox(node: TerminalFocusNode, hitbox: { itemOffset?: number; y1: number }, y: number) {
|
|
445
|
-
|
|
481
|
+
function sourceRowFromHitbox(node: TerminalFocusNode, hitbox: { itemOffset?: number; itemIndexes?: number[]; y1: number; y2: number; contentY?: number; __listItemIndex?: number }, y: number) {
|
|
482
|
+
if (node.tag === "terminal-list" && typeof hitbox.__listItemIndex === "number" && y >= hitbox.y1 && y <= hitbox.y2) {
|
|
483
|
+
return Math.max(1, Math.min(rowCountForNode(node), hitbox.__listItemIndex + 1));
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const sourceY = hitbox.contentY ?? hitbox.y1;
|
|
487
|
+
const visibleRow = Math.max(1, y - sourceY + 1);
|
|
446
488
|
if (node.tag !== "terminal-list") {
|
|
447
489
|
return Math.max(1, Math.min(rowCountForNode(node), visibleRow));
|
|
448
490
|
}
|
|
449
491
|
|
|
492
|
+
const mappedIndex = hitbox.itemIndexes?.[visibleRow - 1];
|
|
493
|
+
if (typeof mappedIndex === "number") {
|
|
494
|
+
return Math.max(1, Math.min(rowCountForNode(node), mappedIndex + 1));
|
|
495
|
+
}
|
|
496
|
+
|
|
450
497
|
return Math.max(1, Math.min(rowCountForNode(node), visibleRow + (hitbox.itemOffset || 0)));
|
|
451
498
|
}
|
|
452
499
|
|
|
@@ -492,6 +539,172 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
492
539
|
}
|
|
493
540
|
}
|
|
494
541
|
|
|
542
|
+
|
|
543
|
+
function listItemKey(node: TerminalFocusNode, index: number) {
|
|
544
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
545
|
+
const item = items[index];
|
|
546
|
+
if (typeof node.props.itemKey === "function" && typeof item !== "undefined") {
|
|
547
|
+
const key = node.props.itemKey(item, index);
|
|
548
|
+
if (typeof key === "string" || typeof key === "number") {
|
|
549
|
+
return String(key);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return undefined;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function listViewportRows(node: TerminalFocusNode) {
|
|
556
|
+
const context = renderContext();
|
|
557
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
558
|
+
const hitbox = node.props.id ? currentHitboxes.find((box) => box.id === node.props.id) : null;
|
|
559
|
+
const overscan = typeof node.props.overscan === "number" ? Math.max(0, Math.floor(node.props.overscan)) : 0;
|
|
560
|
+
const renderedRows = hitbox ? Math.max(1, hitbox.y2 - hitbox.y1 + 1 - overscan * 2) : null;
|
|
561
|
+
const sourceRows = Number(node.props.height || renderedRows || context.rows || items.length || 1);
|
|
562
|
+
if (!Number.isFinite(sourceRows) || !Number.isInteger(sourceRows) || sourceRows <= 0) {
|
|
563
|
+
return Math.max(1, items.length || 1);
|
|
564
|
+
}
|
|
565
|
+
return Math.max(1, Math.min(items.length || 1, sourceRows));
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function clampListIndex(node: TerminalFocusNode, index: number) {
|
|
569
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
570
|
+
if (items.length === 0) {
|
|
571
|
+
return 0;
|
|
572
|
+
}
|
|
573
|
+
return Math.max(0, Math.min(items.length - 1, index));
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function currentListActiveIndex(node: TerminalFocusNode) {
|
|
577
|
+
return clampListIndex(node, listIndexById.get(node.props.id || "") || 0);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function currentListSelectedIndex(node: TerminalFocusNode) {
|
|
581
|
+
const id = node.props.id || "";
|
|
582
|
+
if (node.props.showActive === false) {
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
if (listSelectedIndexById.has(id)) {
|
|
586
|
+
const selectedIndex = listSelectedIndexById.get(id);
|
|
587
|
+
return typeof selectedIndex === "number" ? clampListIndex(node, selectedIndex) : null;
|
|
588
|
+
}
|
|
589
|
+
return currentListActiveIndex(node);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function currentListViewportOffset(node: TerminalFocusNode) {
|
|
593
|
+
const id = node.props.id || "";
|
|
594
|
+
return Math.max(0, Math.min(listMaxViewportOffset(node), listViewportOffsetById.get(id) || 0));
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function listStatePayload(node: TerminalFocusNode) {
|
|
598
|
+
return {
|
|
599
|
+
activeIndex: currentListActiveIndex(node),
|
|
600
|
+
selectedIndex: currentListSelectedIndex(node),
|
|
601
|
+
viewportOffset: currentListViewportOffset(node),
|
|
602
|
+
viewportRows: listViewportRows(node)
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
function listMaxViewportOffset(node: TerminalFocusNode) {
|
|
607
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
608
|
+
return Math.max(0, items.length - listViewportRows(node));
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function setListViewportOffset(node: TerminalFocusNode, offset: number, emit = true) {
|
|
612
|
+
const id = node.props.id;
|
|
613
|
+
if (!id) {
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
const nextOffset = Math.max(0, Math.min(listMaxViewportOffset(node), offset));
|
|
617
|
+
const previous = listViewportOffsetById.get(id) || 0;
|
|
618
|
+
listViewportOffsetById.set(id, nextOffset);
|
|
619
|
+
if (emit && nextOffset !== previous) {
|
|
620
|
+
const payload: TerminalListViewportChangeEventPayload = {
|
|
621
|
+
type: "viewportchange",
|
|
622
|
+
id,
|
|
623
|
+
offset: nextOffset,
|
|
624
|
+
rows: listViewportRows(node),
|
|
625
|
+
...listStatePayload(node)
|
|
626
|
+
};
|
|
627
|
+
dispatchNodeEvent(node, "viewportchange", payload);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function ensureListActiveVisible(node: TerminalFocusNode, activeIndex: number) {
|
|
632
|
+
const id = node.props.id;
|
|
633
|
+
if (!id || !node.props.virtualized) {
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
const currentOffset = listViewportOffsetById.get(id) || 0;
|
|
637
|
+
const rows = listViewportRows(node);
|
|
638
|
+
if (activeIndex < currentOffset) {
|
|
639
|
+
setListViewportOffset(node, activeIndex);
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (activeIndex >= currentOffset + rows) {
|
|
643
|
+
setListViewportOffset(node, activeIndex - rows + 1);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function dispatchListPressEvent(node: TerminalFocusNode, type: TerminalListPressEventPayload["type"], index: number) {
|
|
648
|
+
const id = node.props.id;
|
|
649
|
+
if (!id) {
|
|
650
|
+
return false;
|
|
651
|
+
}
|
|
652
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
653
|
+
if (typeof items[index] === "undefined") {
|
|
654
|
+
return false;
|
|
655
|
+
}
|
|
656
|
+
const key = listItemKey(node, index);
|
|
657
|
+
const payload: TerminalListPressEventPayload = typeof key === "undefined"
|
|
658
|
+
? { type, id, index, value: items[index], ...listStatePayload(node) }
|
|
659
|
+
: { type, id, index, key, value: items[index], ...listStatePayload(node) };
|
|
660
|
+
return dispatchNodeEvent(node, type, payload);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
function dispatchButtonPressEvent(node: TerminalFocusNode, type: "press" | "doublepress" | "contextpress") {
|
|
664
|
+
const id = String(node.props.id || "");
|
|
665
|
+
if (!id) {
|
|
666
|
+
return false;
|
|
667
|
+
}
|
|
668
|
+
return dispatchNodeEvent(node, type, { type, id });
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
function dispatchHitboxButtonPressEvent(hitbox: TerminalHitbox, type: "press" | "doublepress" | "contextpress") {
|
|
672
|
+
if (type !== "press" || typeof hitbox.__pressHandler !== "function") {
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
hitbox.__pressHandler({ type, id: hitbox.id });
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
function dispatchListPointerPressEvent(node: TerminalFocusNode, type: "doublepress" | "contextpress", row: number) {
|
|
680
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
681
|
+
const index = Math.max(0, Math.min(items.length - 1, row - 1));
|
|
682
|
+
return dispatchListPressEvent(node, type, index);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
function dispatchInputContextPressEvent(node: TerminalFocusNode, hitbox: TerminalHitbox, x: number | null, y: number | null) {
|
|
686
|
+
const id = String(node.props.id || "");
|
|
687
|
+
if (!id) {
|
|
688
|
+
return false;
|
|
689
|
+
}
|
|
690
|
+
const value = stripTerminalControls(node.props.value ?? "");
|
|
691
|
+
const current = normalizeInputState(inputStateById.get(id), value.length);
|
|
692
|
+
const cursor = typeof x === "number" ? cursorFromHitbox(hitbox, x) : current.cursor;
|
|
693
|
+
return dispatchNodeEvent(node, "contextpress", { type: "contextpress", id, value, cursor, x, y });
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
function dispatchScrollContextPressEvent(node: TerminalFocusNode, row: number, x: number | null, y: number | null) {
|
|
697
|
+
if (!node.props.id) {
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
const lines = visibleScrollLines(node);
|
|
701
|
+
const index = Math.max(0, Math.min(lines.length - 1, row - 1));
|
|
702
|
+
if (typeof lines[index] === "undefined") {
|
|
703
|
+
return false;
|
|
704
|
+
}
|
|
705
|
+
return dispatchNodeEvent(node, "contextpress", { type: "contextpress", id: node.props.id, row: index + 1, value: lines[index], x, y });
|
|
706
|
+
}
|
|
707
|
+
|
|
495
708
|
function emitMouseRowEvent(node: TerminalFocusNode, type: TerminalRowPointerEventPayload["type"], row: number, x: number | null = null, y: number | null = null) {
|
|
496
709
|
if (!node.props.id) {
|
|
497
710
|
return;
|
|
@@ -503,7 +716,10 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
503
716
|
if (typeof items[index] === "undefined") {
|
|
504
717
|
return;
|
|
505
718
|
}
|
|
506
|
-
const
|
|
719
|
+
const key = listItemKey(node, index);
|
|
720
|
+
const payload: TerminalRowPointerEventPayload = typeof key === "undefined"
|
|
721
|
+
? { type, id: node.props.id, row: index + 1, index, value: items[index], x, y }
|
|
722
|
+
: { type, id: node.props.id, row: index + 1, index, key, value: items[index], x, y };
|
|
507
723
|
dispatchNodeEvent(node, type, payload);
|
|
508
724
|
return;
|
|
509
725
|
}
|
|
@@ -602,29 +818,77 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
602
818
|
return rerender();
|
|
603
819
|
}
|
|
604
820
|
|
|
605
|
-
function
|
|
821
|
+
function moveListActiveTo(node: TerminalFocusNode, nextIndex: number) {
|
|
606
822
|
const id = node.props.id;
|
|
607
823
|
if (!id) {
|
|
608
824
|
return currentOutput;
|
|
609
825
|
}
|
|
610
826
|
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
611
|
-
const
|
|
612
|
-
|
|
827
|
+
const clampedIndex = clampListIndex(node, nextIndex);
|
|
828
|
+
listIndexById.set(id, clampedIndex);
|
|
829
|
+
ensureListActiveVisible(node, clampedIndex);
|
|
830
|
+
const key = listItemKey(node, clampedIndex);
|
|
831
|
+
const payload: TerminalListChangeEventPayload = typeof key === "undefined"
|
|
832
|
+
? { type: "change", id, index: clampedIndex, value: items[clampedIndex], ...listStatePayload(node) }
|
|
833
|
+
: { type: "change", id, index: clampedIndex, key, value: items[clampedIndex], ...listStatePayload(node) };
|
|
834
|
+
dispatchNodeEvent(node, "change", payload);
|
|
835
|
+
return rerender();
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
function changeListSelection(node: TerminalFocusNode, direction: -1 | 1) {
|
|
839
|
+
return moveListActiveTo(node, currentListActiveIndex(node) + direction);
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function pageListSelection(node: TerminalFocusNode, direction: -1 | 1) {
|
|
843
|
+
const id = node.props.id;
|
|
844
|
+
if (!id) {
|
|
845
|
+
return currentOutput;
|
|
846
|
+
}
|
|
847
|
+
const nextIndex = clampListIndex(node, currentListActiveIndex(node) + direction * listViewportRows(node));
|
|
613
848
|
listIndexById.set(id, nextIndex);
|
|
614
|
-
|
|
849
|
+
setListViewportOffset(node, nextIndex);
|
|
850
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
851
|
+
const key = listItemKey(node, nextIndex);
|
|
852
|
+
const payload: TerminalListChangeEventPayload = typeof key === "undefined"
|
|
853
|
+
? { type: "change", id, index: nextIndex, value: items[nextIndex], ...listStatePayload(node) }
|
|
854
|
+
: { type: "change", id, index: nextIndex, key, value: items[nextIndex], ...listStatePayload(node) };
|
|
615
855
|
dispatchNodeEvent(node, "change", payload);
|
|
616
856
|
return rerender();
|
|
617
857
|
}
|
|
618
858
|
|
|
859
|
+
function moveListSelectionToBoundary(node: TerminalFocusNode, boundary: "start" | "end") {
|
|
860
|
+
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
861
|
+
return moveListActiveTo(node, boundary === "start" ? 0 : Math.max(0, items.length - 1));
|
|
862
|
+
}
|
|
863
|
+
|
|
619
864
|
function pressListSelection(node: TerminalFocusNode) {
|
|
865
|
+
const id = node.props.id;
|
|
866
|
+
if (!id) {
|
|
867
|
+
return currentOutput;
|
|
868
|
+
}
|
|
869
|
+
const currentIndex = currentListActiveIndex(node);
|
|
870
|
+
if (node.props.showActive !== false) {
|
|
871
|
+
listSelectedIndexById.set(id, currentIndex);
|
|
872
|
+
}
|
|
873
|
+
dispatchListPressEvent(node, "press", currentIndex);
|
|
874
|
+
return rerender();
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
function pressListPointerSelection(node: TerminalFocusNode, row: number) {
|
|
620
878
|
const id = node.props.id;
|
|
621
879
|
if (!id) {
|
|
622
880
|
return currentOutput;
|
|
623
881
|
}
|
|
624
882
|
const items = Array.isArray(node.props.items) ? node.props.items : [];
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
|
|
883
|
+
const index = Math.max(0, Math.min(items.length - 1, row - 1));
|
|
884
|
+
if (typeof items[index] === "undefined") {
|
|
885
|
+
return currentOutput;
|
|
886
|
+
}
|
|
887
|
+
listIndexById.set(id, index);
|
|
888
|
+
if (node.props.showActive !== false) {
|
|
889
|
+
listSelectedIndexById.set(id, index);
|
|
890
|
+
}
|
|
891
|
+
dispatchListPressEvent(node, "press", index);
|
|
628
892
|
return rerender();
|
|
629
893
|
}
|
|
630
894
|
|
|
@@ -654,7 +918,9 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
654
918
|
return scrollFocusedNode(node, direction);
|
|
655
919
|
}
|
|
656
920
|
if (node?.tag === "terminal-list" && node.props.virtualized) {
|
|
657
|
-
|
|
921
|
+
const currentOffset = listViewportOffsetById.get(node.props.id || "") || 0;
|
|
922
|
+
setListViewportOffset(node, currentOffset + direction);
|
|
923
|
+
return rerender();
|
|
658
924
|
}
|
|
659
925
|
return rerender();
|
|
660
926
|
}
|
|
@@ -821,8 +1087,7 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
821
1087
|
return currentOutput;
|
|
822
1088
|
case "button.press":
|
|
823
1089
|
if (node?.tag === "terminal-button") {
|
|
824
|
-
|
|
825
|
-
dispatchNodeEvent(node, "press", { type: "press", id });
|
|
1090
|
+
dispatchButtonPressEvent(node, "press");
|
|
826
1091
|
return rerender();
|
|
827
1092
|
}
|
|
828
1093
|
return currentOutput;
|
|
@@ -830,6 +1095,14 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
830
1095
|
return node?.tag === "terminal-list" ? changeListSelection(node, -1) : currentOutput;
|
|
831
1096
|
case "list.next":
|
|
832
1097
|
return node?.tag === "terminal-list" ? changeListSelection(node, 1) : currentOutput;
|
|
1098
|
+
case "list.pageUp":
|
|
1099
|
+
return node?.tag === "terminal-list" ? pageListSelection(node, -1) : currentOutput;
|
|
1100
|
+
case "list.pageDown":
|
|
1101
|
+
return node?.tag === "terminal-list" ? pageListSelection(node, 1) : currentOutput;
|
|
1102
|
+
case "list.home":
|
|
1103
|
+
return node?.tag === "terminal-list" ? moveListSelectionToBoundary(node, "start") : currentOutput;
|
|
1104
|
+
case "list.end":
|
|
1105
|
+
return node?.tag === "terminal-list" ? moveListSelectionToBoundary(node, "end") : currentOutput;
|
|
833
1106
|
case "list.press":
|
|
834
1107
|
return node?.tag === "terminal-list" ? pressListSelection(node) : currentOutput;
|
|
835
1108
|
case "scroll.up":
|
|
@@ -866,6 +1139,18 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
866
1139
|
return;
|
|
867
1140
|
}
|
|
868
1141
|
terminalSize = nextSize;
|
|
1142
|
+
const focusedNode = findFocused(currentTree, focusedId);
|
|
1143
|
+
if (focusedNode?.tag === "terminal-list" && focusedNode.props.id && focusedNode.props.virtualized) {
|
|
1144
|
+
const items = Array.isArray(focusedNode.props.items) ? focusedNode.props.items : [];
|
|
1145
|
+
const rows = Math.max(1, Math.min(items.length || 1, Number(focusedNode.props.height || terminalSize.rows || items.length || 1)));
|
|
1146
|
+
const activeIndex = currentListActiveIndex(focusedNode);
|
|
1147
|
+
const currentOffset = listViewportOffsetById.get(focusedNode.props.id) || 0;
|
|
1148
|
+
if (activeIndex < currentOffset) {
|
|
1149
|
+
listViewportOffsetById.set(focusedNode.props.id, activeIndex);
|
|
1150
|
+
} else if (activeIndex >= currentOffset + rows) {
|
|
1151
|
+
listViewportOffsetById.set(focusedNode.props.id, Math.max(0, activeIndex - rows + 1));
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
869
1154
|
rerender();
|
|
870
1155
|
},
|
|
871
1156
|
update() {
|
|
@@ -946,7 +1231,7 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
946
1231
|
if (id) {
|
|
947
1232
|
focusedId = node.props.id || focusedId;
|
|
948
1233
|
}
|
|
949
|
-
|
|
1234
|
+
dispatchButtonPressEvent(node, "press");
|
|
950
1235
|
return rerender();
|
|
951
1236
|
},
|
|
952
1237
|
clickAt(x: number, y: number) {
|
|
@@ -956,7 +1241,14 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
956
1241
|
return currentOutput;
|
|
957
1242
|
}
|
|
958
1243
|
if (hitbox.tag === "terminal-button") {
|
|
959
|
-
|
|
1244
|
+
const node = findFocusableById(currentTree, hitbox.id);
|
|
1245
|
+
if (node?.tag === "terminal-button") {
|
|
1246
|
+
return this.click(hitbox.id);
|
|
1247
|
+
}
|
|
1248
|
+
if (dispatchHitboxButtonPressEvent(hitbox, "press")) {
|
|
1249
|
+
return rerender();
|
|
1250
|
+
}
|
|
1251
|
+
return currentOutput;
|
|
960
1252
|
}
|
|
961
1253
|
setSemanticHoverFromHitbox(hitbox.id, x, y);
|
|
962
1254
|
focusedId = hitbox.id;
|
|
@@ -964,6 +1256,12 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
964
1256
|
mouseSelectionId = hitbox.id;
|
|
965
1257
|
return setCursorFromHitbox(hitbox.id, x, false);
|
|
966
1258
|
}
|
|
1259
|
+
if (hitbox.tag === "terminal-list") {
|
|
1260
|
+
const node = findFocusableById(currentTree, hitbox.id);
|
|
1261
|
+
if (node?.tag === "terminal-list") {
|
|
1262
|
+
return pressListPointerSelection(node, sourceRowFromHitbox(node, hitbox, y));
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
967
1265
|
rerender();
|
|
968
1266
|
return currentOutput;
|
|
969
1267
|
},
|
|
@@ -1100,6 +1398,148 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
1100
1398
|
}
|
|
1101
1399
|
}
|
|
1102
1400
|
|
|
1401
|
+
|
|
1402
|
+
function isPrimaryMouseButton(button: number) {
|
|
1403
|
+
return button < 64 && (button & 3) === 0;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
function isContextMouseButton(button: number) {
|
|
1407
|
+
return button < 64 && (button & 3) === 2;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
function isDoublePrimaryPress(hitbox: { id: string; tag: string }, row: number | null) {
|
|
1411
|
+
const now = Date.now();
|
|
1412
|
+
const isDouble = Boolean(
|
|
1413
|
+
lastPrimaryPress
|
|
1414
|
+
&& lastPrimaryPress.id === hitbox.id
|
|
1415
|
+
&& lastPrimaryPress.tag === hitbox.tag
|
|
1416
|
+
&& lastPrimaryPress.row === row
|
|
1417
|
+
&& now - lastPrimaryPress.at <= DOUBLE_PRESS_INTERVAL_MS
|
|
1418
|
+
);
|
|
1419
|
+
lastPrimaryPress = { id: hitbox.id, tag: hitbox.tag, row, at: now };
|
|
1420
|
+
return isDouble;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
function doublePressAt(hitbox: TerminalHitbox, x: number, y: number) {
|
|
1424
|
+
const node = findFocusableById(currentTree, hitbox.id);
|
|
1425
|
+
if (!node) {
|
|
1426
|
+
return currentOutput;
|
|
1427
|
+
}
|
|
1428
|
+
focusedId = hitbox.id;
|
|
1429
|
+
setSemanticHoverFromHitbox(hitbox.id, x, y);
|
|
1430
|
+
if (node.tag === "terminal-button") {
|
|
1431
|
+
dispatchButtonPressEvent(node, "doublepress");
|
|
1432
|
+
return rerender();
|
|
1433
|
+
}
|
|
1434
|
+
if (node.tag === "terminal-list") {
|
|
1435
|
+
dispatchListPointerPressEvent(node, "doublepress", sourceRowFromHitbox(node, hitbox, y));
|
|
1436
|
+
return rerender();
|
|
1437
|
+
}
|
|
1438
|
+
return currentOutput;
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
function contextPressAt(x: number, y: number) {
|
|
1442
|
+
const hitbox = resolvePointerTarget(currentHitboxes, x, y);
|
|
1443
|
+
if (!hitbox) {
|
|
1444
|
+
clearSemanticHover(undefined, x, y);
|
|
1445
|
+
return currentOutput;
|
|
1446
|
+
}
|
|
1447
|
+
const node = findFocusableById(currentTree, hitbox.id);
|
|
1448
|
+
if (!node) {
|
|
1449
|
+
return currentOutput;
|
|
1450
|
+
}
|
|
1451
|
+
focusedId = hitbox.id;
|
|
1452
|
+
setSemanticHoverFromHitbox(hitbox.id, x, y);
|
|
1453
|
+
if (node.tag === "terminal-button") {
|
|
1454
|
+
dispatchButtonPressEvent(node, "contextpress");
|
|
1455
|
+
return rerender();
|
|
1456
|
+
}
|
|
1457
|
+
if (node.tag === "terminal-list") {
|
|
1458
|
+
dispatchListPointerPressEvent(node, "contextpress", sourceRowFromHitbox(node, hitbox, y));
|
|
1459
|
+
return rerender();
|
|
1460
|
+
}
|
|
1461
|
+
if (node.tag === "terminal-input") {
|
|
1462
|
+
dispatchInputContextPressEvent(node, hitbox, x, y);
|
|
1463
|
+
return rerender();
|
|
1464
|
+
}
|
|
1465
|
+
if (node.tag === "terminal-scroll") {
|
|
1466
|
+
dispatchScrollContextPressEvent(node, sourceRowFromHitbox(node, hitbox, y), x, y);
|
|
1467
|
+
return rerender();
|
|
1468
|
+
}
|
|
1469
|
+
return currentOutput;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
function processParsedMouseInput(parsed: Extract<ParsedTerminalInput, { type: "mouse" }>) {
|
|
1473
|
+
if (parsed.action === "press") {
|
|
1474
|
+
const hitbox = resolvePointerTarget(currentHitboxes, parsed.x, parsed.y);
|
|
1475
|
+
if (isContextMouseButton(parsed.button)) {
|
|
1476
|
+
lastPrimaryPress = null;
|
|
1477
|
+
contextPressAt(parsed.x, parsed.y);
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
if (hitbox?.tag === "terminal-input") {
|
|
1481
|
+
mouseSelectionId = hitbox.id;
|
|
1482
|
+
}
|
|
1483
|
+
const node = hitbox ? findFocusableById(currentTree, hitbox.id) : null;
|
|
1484
|
+
if (hitbox && node && shouldPointerCapture(node)) {
|
|
1485
|
+
setPointerCapture(hitbox.id, "press", sourceRowFromHitbox(node, hitbox, parsed.y), parsed.x, parsed.y);
|
|
1486
|
+
}
|
|
1487
|
+
const isPrimaryPress = isPrimaryMouseButton(parsed.button);
|
|
1488
|
+
const isDoublePressEligible = Boolean(
|
|
1489
|
+
hitbox
|
|
1490
|
+
&& node
|
|
1491
|
+
&& (hitbox.tag === "terminal-button" || hitbox.tag === "terminal-list")
|
|
1492
|
+
);
|
|
1493
|
+
const primaryPressRow = hitbox && node && hitbox.tag === "terminal-list"
|
|
1494
|
+
? sourceRowFromHitbox(node, hitbox, parsed.y)
|
|
1495
|
+
: null;
|
|
1496
|
+
const shouldDispatchDoublePress = Boolean(
|
|
1497
|
+
isPrimaryPress
|
|
1498
|
+
&& isDoublePressEligible
|
|
1499
|
+
&& hitbox
|
|
1500
|
+
&& isDoublePrimaryPress(hitbox, primaryPressRow)
|
|
1501
|
+
);
|
|
1502
|
+
if (isPrimaryPress && !isDoublePressEligible) {
|
|
1503
|
+
lastPrimaryPress = null;
|
|
1504
|
+
}
|
|
1505
|
+
if (hitbox && shouldDispatchDoublePress && hitbox.tag === "terminal-list") {
|
|
1506
|
+
doublePressAt(hitbox, parsed.x, parsed.y);
|
|
1507
|
+
} else {
|
|
1508
|
+
session.clickAt(parsed.x, parsed.y);
|
|
1509
|
+
if (hitbox && shouldDispatchDoublePress) {
|
|
1510
|
+
doublePressAt(hitbox, parsed.x, parsed.y);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
} else if (parsed.action === "drag") {
|
|
1514
|
+
if (mouseSelectionId) {
|
|
1515
|
+
setCursorFromHitbox(mouseSelectionId, parsed.x, true);
|
|
1516
|
+
} else {
|
|
1517
|
+
hoverAt(parsed.x, parsed.y);
|
|
1518
|
+
}
|
|
1519
|
+
} else if (parsed.action === "release") {
|
|
1520
|
+
mouseSelectionId = null;
|
|
1521
|
+
const capturedId = pointerCaptureId;
|
|
1522
|
+
const releaseHitbox = resolvePointerTarget(currentHitboxes, parsed.x, parsed.y);
|
|
1523
|
+
const releaseNode = releaseHitbox ? findFocusableById(currentTree, releaseHitbox.id) : null;
|
|
1524
|
+
const releaseRow = releaseHitbox && releaseNode
|
|
1525
|
+
? sourceRowFromHitbox(releaseNode, releaseHitbox, parsed.y)
|
|
1526
|
+
: null;
|
|
1527
|
+
setPointerCapture(null, "release", capturedId && releaseHitbox?.id === capturedId ? releaseRow : null, parsed.x, parsed.y);
|
|
1528
|
+
const hitbox = resolvePointerTarget(currentHitboxes, parsed.x, parsed.y);
|
|
1529
|
+
if (capturedId && hitbox && hitbox.id === capturedId && (hitbox.tag === "terminal-list" || hitbox.tag === "terminal-scroll")) {
|
|
1530
|
+
setSemanticHoverFromHitbox(hitbox.id, parsed.x, parsed.y);
|
|
1531
|
+
rerender();
|
|
1532
|
+
} else {
|
|
1533
|
+
clearSemanticHover(undefined, parsed.x, parsed.y);
|
|
1534
|
+
rerender();
|
|
1535
|
+
}
|
|
1536
|
+
} else if (parsed.action === "wheel-up") {
|
|
1537
|
+
wheelAt(parsed.x, parsed.y, -1);
|
|
1538
|
+
} else if (parsed.action === "wheel-down") {
|
|
1539
|
+
wheelAt(parsed.x, parsed.y, 1);
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1103
1543
|
function processInputStream(value: string) {
|
|
1104
1544
|
if (!value) {
|
|
1105
1545
|
return;
|
|
@@ -1157,46 +1597,16 @@ export function mountTerminal(input: any, options: TerminalMountOptions = {}): T
|
|
|
1157
1597
|
return;
|
|
1158
1598
|
}
|
|
1159
1599
|
|
|
1600
|
+
const parsedMouse = parseTerminalMousePrefix(value);
|
|
1601
|
+
if (parsedMouse) {
|
|
1602
|
+
processParsedMouseInput(parsedMouse.input);
|
|
1603
|
+
processInputStream(parsedMouse.rest);
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1160
1607
|
const parsed = parseTerminalInput(value);
|
|
1161
1608
|
if (parsed.type === "mouse") {
|
|
1162
|
-
|
|
1163
|
-
const hitbox = resolvePointerTarget(currentHitboxes, parsed.x, parsed.y);
|
|
1164
|
-
if (hitbox?.tag === "terminal-input") {
|
|
1165
|
-
mouseSelectionId = hitbox.id;
|
|
1166
|
-
}
|
|
1167
|
-
const node = hitbox ? findFocusableById(currentTree, hitbox.id) : null;
|
|
1168
|
-
if (hitbox && node && shouldPointerCapture(node)) {
|
|
1169
|
-
setPointerCapture(hitbox.id, "press", sourceRowFromHitbox(node, hitbox, parsed.y), parsed.x, parsed.y);
|
|
1170
|
-
}
|
|
1171
|
-
session.clickAt(parsed.x, parsed.y);
|
|
1172
|
-
} else if (parsed.action === "drag") {
|
|
1173
|
-
if (mouseSelectionId) {
|
|
1174
|
-
setCursorFromHitbox(mouseSelectionId, parsed.x, true);
|
|
1175
|
-
} else {
|
|
1176
|
-
hoverAt(parsed.x, parsed.y);
|
|
1177
|
-
}
|
|
1178
|
-
} else if (parsed.action === "release") {
|
|
1179
|
-
mouseSelectionId = null;
|
|
1180
|
-
const capturedId = pointerCaptureId;
|
|
1181
|
-
const releaseHitbox = resolvePointerTarget(currentHitboxes, parsed.x, parsed.y);
|
|
1182
|
-
const releaseNode = releaseHitbox ? findFocusableById(currentTree, releaseHitbox.id) : null;
|
|
1183
|
-
const releaseRow = releaseHitbox && releaseNode
|
|
1184
|
-
? sourceRowFromHitbox(releaseNode, releaseHitbox, parsed.y)
|
|
1185
|
-
: null;
|
|
1186
|
-
setPointerCapture(null, "release", capturedId && releaseHitbox?.id === capturedId ? releaseRow : null, parsed.x, parsed.y);
|
|
1187
|
-
const hitbox = resolvePointerTarget(currentHitboxes, parsed.x, parsed.y);
|
|
1188
|
-
if (capturedId && hitbox && hitbox.id === capturedId && (hitbox.tag === "terminal-list" || hitbox.tag === "terminal-scroll")) {
|
|
1189
|
-
setSemanticHoverFromHitbox(hitbox.id, parsed.x, parsed.y);
|
|
1190
|
-
rerender();
|
|
1191
|
-
} else {
|
|
1192
|
-
clearSemanticHover(undefined, parsed.x, parsed.y);
|
|
1193
|
-
rerender();
|
|
1194
|
-
}
|
|
1195
|
-
} else if (parsed.action === "wheel-up") {
|
|
1196
|
-
wheelAt(parsed.x, parsed.y, -1);
|
|
1197
|
-
} else if (parsed.action === "wheel-down") {
|
|
1198
|
-
wheelAt(parsed.x, parsed.y, 1);
|
|
1199
|
-
}
|
|
1609
|
+
processParsedMouseInput(parsed);
|
|
1200
1610
|
return;
|
|
1201
1611
|
}
|
|
1202
1612
|
|