@deepseek-harness-tui/dsh-tui 0.8.8 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -8
- package/bin/dsh-tui.js +1 -1
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +4 -2
- package/lib/types/components/ActivityPicker.d.ts +4 -1
- package/lib/types/components/ActivityPicker.d.ts.map +1 -1
- package/lib/types/components/ActivityPicker.js +2 -2
- package/lib/types/components/BtwPanel.d.ts.map +1 -1
- package/lib/types/components/BtwPanel.js +1 -1
- package/lib/types/components/Chat/SubagentMessage.d.ts +2 -1
- package/lib/types/components/Chat/SubagentMessage.d.ts.map +1 -1
- package/lib/types/components/Chat/SubagentMessage.js +11 -3
- package/lib/types/components/CommandSuggestions.d.ts +21 -4
- package/lib/types/components/CommandSuggestions.d.ts.map +1 -1
- package/lib/types/components/CommandSuggestions.js +41 -14
- package/lib/types/components/EffortSlider.d.ts +4 -1
- package/lib/types/components/EffortSlider.d.ts.map +1 -1
- package/lib/types/components/EffortSlider.js +4 -2
- package/lib/types/components/ExtensionDialog.js +6 -2
- package/lib/types/components/FileSuggestions.d.ts +24 -7
- package/lib/types/components/FileSuggestions.d.ts.map +1 -1
- package/lib/types/components/FileSuggestions.js +39 -12
- package/lib/types/components/GoalTodoPanel.d.ts +26 -2
- package/lib/types/components/GoalTodoPanel.d.ts.map +1 -1
- package/lib/types/components/GoalTodoPanel.js +86 -17
- package/lib/types/components/HelpMenu.d.ts +7 -1
- package/lib/types/components/HelpMenu.d.ts.map +1 -1
- package/lib/types/components/HelpMenu.js +12 -4
- package/lib/types/components/HistorySearchDialog.d.ts +4 -1
- package/lib/types/components/HistorySearchDialog.d.ts.map +1 -1
- package/lib/types/components/HistorySearchDialog.js +2 -2
- package/lib/types/components/LangPicker.d.ts +14 -0
- package/lib/types/components/LangPicker.d.ts.map +1 -0
- package/lib/types/components/LangPicker.js +17 -0
- package/lib/types/components/LoadedContextPanel.d.ts.map +1 -1
- package/lib/types/components/LoadedContextPanel.js +3 -1
- package/lib/types/components/Markdown.d.ts.map +1 -1
- package/lib/types/components/Markdown.js +12 -3
- package/lib/types/components/MessageList.d.ts +43 -2
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +553 -41
- package/lib/types/components/ModelPicker.d.ts +4 -1
- package/lib/types/components/ModelPicker.d.ts.map +1 -1
- package/lib/types/components/ModelPicker.js +2 -2
- package/lib/types/components/PermissionsPicker.d.ts +21 -0
- package/lib/types/components/PermissionsPicker.d.ts.map +1 -0
- package/lib/types/components/PermissionsPicker.js +27 -0
- package/lib/types/components/PlanPicker.d.ts +15 -0
- package/lib/types/components/PlanPicker.d.ts.map +1 -0
- package/lib/types/components/PlanPicker.js +18 -0
- package/lib/types/components/PresetPicker.d.ts +4 -1
- package/lib/types/components/PresetPicker.d.ts.map +1 -1
- package/lib/types/components/PresetPicker.js +2 -2
- package/lib/types/components/PromptInput.d.ts +4 -3
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +248 -29
- package/lib/types/components/RewindPicker.d.ts +12 -1
- package/lib/types/components/RewindPicker.d.ts.map +1 -1
- package/lib/types/components/RewindPicker.js +6 -4
- package/lib/types/components/ScrollbarGutter.d.ts +28 -0
- package/lib/types/components/ScrollbarGutter.d.ts.map +1 -0
- package/lib/types/components/ScrollbarGutter.js +68 -0
- package/lib/types/components/Select.d.ts +8 -1
- package/lib/types/components/Select.d.ts.map +1 -1
- package/lib/types/components/Select.js +4 -2
- package/lib/types/components/SkillsPicker.d.ts +4 -1
- package/lib/types/components/SkillsPicker.d.ts.map +1 -1
- package/lib/types/components/SkillsPicker.js +2 -2
- package/lib/types/components/SubagentCard.d.ts +2 -1
- package/lib/types/components/SubagentCard.d.ts.map +1 -1
- package/lib/types/components/SubagentCard.js +6 -1
- package/lib/types/components/SubagentDashboard.d.ts +4 -0
- package/lib/types/components/SubagentDashboard.d.ts.map +1 -1
- package/lib/types/components/SubagentDashboard.js +9 -1
- package/lib/types/components/SubagentDetailScene.d.ts.map +1 -1
- package/lib/types/components/SubagentDetailScene.js +5 -4
- package/lib/types/components/SuggestionCard.d.ts +63 -0
- package/lib/types/components/SuggestionCard.d.ts.map +1 -0
- package/lib/types/components/SuggestionCard.js +77 -0
- package/lib/types/components/ThemePicker.d.ts +4 -1
- package/lib/types/components/ThemePicker.d.ts.map +1 -1
- package/lib/types/components/ThemePicker.js +2 -2
- package/lib/types/components/ThinkingToggle.d.ts +4 -1
- package/lib/types/components/ThinkingToggle.d.ts.map +1 -1
- package/lib/types/components/ThinkingToggle.js +2 -2
- package/lib/types/components/TimelineRail.d.ts +53 -0
- package/lib/types/components/TimelineRail.d.ts.map +1 -0
- package/lib/types/components/TimelineRail.js +190 -0
- package/lib/types/components/WorkspaceFlowPicker.d.ts +6 -1
- package/lib/types/components/WorkspaceFlowPicker.d.ts.map +1 -1
- package/lib/types/components/WorkspaceFlowPicker.js +4 -2
- package/lib/types/components/WorkspaceMenuPicker.d.ts +19 -0
- package/lib/types/components/WorkspaceMenuPicker.d.ts.map +1 -0
- package/lib/types/components/WorkspaceMenuPicker.js +19 -0
- package/lib/types/components/WorkspacePicker.d.ts +4 -1
- package/lib/types/components/WorkspacePicker.d.ts.map +1 -1
- package/lib/types/components/WorkspacePicker.js +2 -2
- package/lib/types/components/approvals/ApprovalPanel.d.ts.map +1 -1
- package/lib/types/components/approvals/ApprovalPanel.js +5 -1
- package/lib/types/components/design-system/ListItem.d.ts +8 -1
- package/lib/types/components/design-system/ListItem.d.ts.map +1 -1
- package/lib/types/components/design-system/ListItem.js +8 -3
- package/lib/types/components/messages/AssistantTextMessage.d.ts +5 -2
- package/lib/types/components/messages/AssistantTextMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantTextMessage.js +6 -2
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts +2 -1
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.js +10 -4
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +8 -1
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +4 -2
- package/lib/types/components/messages/UserPromptMessage.d.ts +2 -1
- package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.js +34 -2
- package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -1
- package/lib/types/components/questions/PlanReviewPanel.js +13 -2
- package/lib/types/components/sessions/SessionListRow.d.ts +4 -1
- package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
- package/lib/types/components/sessions/SessionListRow.js +4 -2
- package/lib/types/components/trajectory/HotspotView.d.ts +7 -1
- package/lib/types/components/trajectory/HotspotView.d.ts.map +1 -1
- package/lib/types/components/trajectory/HotspotView.js +8 -2
- package/lib/types/components/trajectory/Ledger.d.ts +9 -1
- package/lib/types/components/trajectory/Ledger.d.ts.map +1 -1
- package/lib/types/components/trajectory/Ledger.js +23 -5
- package/lib/types/components/trajectory/WaveBand.d.ts +10 -1
- package/lib/types/components/trajectory/WaveBand.d.ts.map +1 -1
- package/lib/types/components/trajectory/WaveBand.js +7 -2
- package/lib/types/dsh-adapter/channel.d.ts +16 -5
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +332 -29
- package/lib/types/dsh-adapter/index.d.ts +10 -2
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +4 -1
- package/lib/types/dsh-adapter/plugin.d.ts +0 -9
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +136 -6
- package/lib/types/i18n.d.ts +176 -15
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +51 -8
- package/lib/types/ink/components/AlternateScreen.d.ts.map +1 -1
- package/lib/types/ink/components/AlternateScreen.js +13 -1
- package/lib/types/ink/components/App.d.ts +12 -1
- package/lib/types/ink/components/App.d.ts.map +1 -1
- package/lib/types/ink/components/App.js +101 -4
- package/lib/types/ink/components/ClockContext.d.ts.map +1 -1
- package/lib/types/ink/components/ClockContext.js +2 -0
- package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
- package/lib/types/ink/components/ScrollBox.js +170 -91
- package/lib/types/ink/constants.d.ts +9 -0
- package/lib/types/ink/constants.d.ts.map +1 -1
- package/lib/types/ink/constants.js +9 -0
- package/lib/types/ink/events/click-event.d.ts +7 -16
- package/lib/types/ink/events/click-event.d.ts.map +1 -1
- package/lib/types/ink/events/click-event.js +16 -19
- package/lib/types/ink/events/event-handlers.d.ts +11 -1
- package/lib/types/ink/events/event-handlers.d.ts.map +1 -1
- package/lib/types/ink/events/event-handlers.js +2 -0
- package/lib/types/ink/events/input-event.d.ts +9 -0
- package/lib/types/ink/events/input-event.d.ts.map +1 -1
- package/lib/types/ink/events/input-event.js +4 -0
- package/lib/types/ink/events/pointer-event.d.ts +61 -0
- package/lib/types/ink/events/pointer-event.d.ts.map +1 -0
- package/lib/types/ink/events/pointer-event.js +61 -0
- package/lib/types/ink/events/wheel-event.d.ts +17 -0
- package/lib/types/ink/events/wheel-event.d.ts.map +1 -0
- package/lib/types/ink/events/wheel-event.js +20 -0
- package/lib/types/ink/geometry-trace.d.ts +51 -0
- package/lib/types/ink/geometry-trace.d.ts.map +1 -0
- package/lib/types/ink/geometry-trace.js +83 -0
- package/lib/types/ink/hit-test.d.ts +56 -1
- package/lib/types/ink/hit-test.d.ts.map +1 -1
- package/lib/types/ink/hit-test.js +146 -8
- package/lib/types/ink/ink.d.ts +58 -1
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +230 -29
- package/lib/types/ink/node-cache.d.ts +5 -0
- package/lib/types/ink/node-cache.d.ts.map +1 -1
- package/lib/types/ink/parse-keypress.d.ts +8 -0
- package/lib/types/ink/parse-keypress.d.ts.map +1 -1
- package/lib/types/ink/parse-keypress.js +101 -12
- package/lib/types/ink/render-node-to-output.d.ts +10 -0
- package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
- package/lib/types/ink/render-node-to-output.js +111 -18
- package/lib/types/ink/timeline-rail.d.ts +138 -0
- package/lib/types/ink/timeline-rail.d.ts.map +1 -0
- package/lib/types/ink/timeline-rail.js +179 -0
- package/lib/types/ink/wrap-text.d.ts.map +1 -1
- package/lib/types/ink/wrap-text.js +47 -5
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +819 -337
- package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
- package/lib/types/screens/SessionBrowser.js +56 -29
- package/lib/types/screens/Settings.d.ts.map +1 -1
- package/lib/types/screens/Settings.js +64 -22
- package/lib/types/screens/StatusLine.d.ts +2 -1
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +17 -1
- package/lib/types/screens/TrajectoryScene.d.ts.map +1 -1
- package/lib/types/screens/TrajectoryScene.js +81 -14
- package/lib/types/screens/chatOverlay.d.ts +221 -0
- package/lib/types/screens/chatOverlay.d.ts.map +1 -0
- package/lib/types/screens/chatOverlay.js +114 -0
- package/lib/types/tips.d.ts.map +1 -1
- package/lib/types/tips.js +42 -6
- package/lib/types/tuiDisplayPrefs.d.ts +15 -0
- package/lib/types/tuiDisplayPrefs.d.ts.map +1 -1
- package/lib/types/tuiDisplayPrefs.js +9 -0
- package/lib/types/utils/debug.d.ts +10 -0
- package/lib/types/utils/debug.d.ts.map +1 -1
- package/lib/types/utils/debug.js +24 -0
- package/lib/types/utils/paths.d.ts.map +1 -1
- package/lib/types/utils/paths.js +1 -0
- package/package.json +4 -2
package/lib/types/ink/ink.js
CHANGED
|
@@ -12,12 +12,14 @@ import { logError } from '../utils/log.js';
|
|
|
12
12
|
import { format } from 'util';
|
|
13
13
|
import { colorize } from './colorize.js';
|
|
14
14
|
import App from './components/App.js';
|
|
15
|
-
import { FRAME_INTERVAL_MS } from './constants.js';
|
|
15
|
+
import { FRAME_INTERVAL_MS, PTY_BACKLOG_BYTES } from './constants.js';
|
|
16
16
|
import * as dom from './dom.js';
|
|
17
|
+
import { beginGeometryFrame, endGeometryFrame, GEOMETRY_TRACE_ENABLED, noteFrameCause } from './geometry-trace.js';
|
|
17
18
|
import { KeyboardEvent } from './events/keyboard-event.js';
|
|
18
19
|
import { FocusManager } from './focus.js';
|
|
19
20
|
import { emptyFrame } from './frame.js';
|
|
20
|
-
import { dispatchClick, dispatchHover } from './hit-test.js';
|
|
21
|
+
import { dispatchClick, dispatchHover, dispatchWheel, clearHovered } from './hit-test.js';
|
|
22
|
+
import { logMouseDebug } from '../utils/debug.js';
|
|
21
23
|
import instances from './instances.js';
|
|
22
24
|
import { suppressInputFor } from './input-suppression.js';
|
|
23
25
|
import { LogUpdate } from './log-update.js';
|
|
@@ -35,6 +37,7 @@ import { isDecstbmSafe, SYNC_OUTPUT_SUPPORTED, supportsExtendedKeys, supportsWin
|
|
|
35
37
|
import { CURSOR_HOME, cursorMove, cursorPosition, DISABLE_KITTY_KEYBOARD, DISABLE_MODIFY_OTHER_KEYS, DISABLE_WIN32_INPUT_MODE, ENABLE_KITTY_KEYBOARD, ENABLE_MODIFY_OTHER_KEYS, ENABLE_WIN32_INPUT_MODE, ERASE_SCREEN, ERASE_SCROLLBACK, SGR_RESET } from './termio/csi.js';
|
|
36
38
|
import { DBP, DFE, DISABLE_MOUSE_TRACKING, ENABLE_MOUSE_TRACKING, ENTER_ALT_SCREEN, EXIT_ALT_SCREEN, SHOW_CURSOR } from './termio/dec.js';
|
|
37
39
|
import { CLEAR_ITERM2_PROGRESS, CLEAR_TAB_STATUS, setClipboard, supportsTabStatus, wrapForMultiplexer } from './termio/osc.js';
|
|
40
|
+
import { decrqm } from './terminal-querier.js';
|
|
38
41
|
import { TerminalWriteProvider } from './useTerminalNotification.js';
|
|
39
42
|
// Alt-screen: renderer.ts sets cursor.visible = !isTTY || screen.height===0,
|
|
40
43
|
// which is always false in alt-screen (TTY + content fills screen).
|
|
@@ -310,9 +313,31 @@ export default class Ink {
|
|
|
310
313
|
// frame resets and renders.
|
|
311
314
|
if (cols === this.terminalColumns && rows === this.terminalRows)
|
|
312
315
|
return;
|
|
316
|
+
noteFrameCause('resize');
|
|
313
317
|
this.terminalColumns = cols;
|
|
314
318
|
this.terminalRows = rows;
|
|
315
319
|
this.altScreenParkPatch = makeAltScreenParkPatch(this.terminalRows);
|
|
320
|
+
// Reflow moved every rect the pointer state was tracking: hover sets
|
|
321
|
+
// and the multi-click chain reference pre-resize geometry. Fire the
|
|
322
|
+
// leave handlers FIRST — a bare clear() strands the crossed rows'
|
|
323
|
+
// hovered=true React state forever (stuck highlights) — then drop the
|
|
324
|
+
// set so a post-resize click is a fresh single click and hover re-fires
|
|
325
|
+
// from scratch. (Coordinates in in-flight events are clamped at the
|
|
326
|
+
// App boundary against the new dimensions.)
|
|
327
|
+
clearHovered(this.hoveredNodes);
|
|
328
|
+
this.app?.resetPointerState();
|
|
329
|
+
// Invalidate every render that was scheduled against the OLD size: a
|
|
330
|
+
// queued microtask generation or a scroll-drain timer would otherwise
|
|
331
|
+
// fire after this resize completes and paint a frame computed for the
|
|
332
|
+
// pre-resize layout (mixed-width rows, off-by-reflow writes). The
|
|
333
|
+
// re-render below schedules fresh work at the new dimensions.
|
|
334
|
+
this.renderGeneration++;
|
|
335
|
+
this.pendingRenderGeneration = null;
|
|
336
|
+
this.scheduleRender.cancel?.();
|
|
337
|
+
if (this.drainTimer !== null) {
|
|
338
|
+
clearTimeout(this.drainTimer);
|
|
339
|
+
this.drainTimer = null;
|
|
340
|
+
}
|
|
316
341
|
// Every cached measurement in the tree was taken against a width that no
|
|
317
342
|
// longer exists. Nothing here is "dirty" in the reconciler's sense — no
|
|
318
343
|
// props changed — so without an explicit sweep the text nodes keep
|
|
@@ -333,9 +358,12 @@ export default class Ink {
|
|
|
333
358
|
// doesn't exit alt-screen. Do NOT write ERASE_SCREEN: render() below
|
|
334
359
|
// can take ~80ms; erasing first leaves the screen blank that whole time.
|
|
335
360
|
if (this.altScreenActive && !this.isPaused && this.options.stdout.isTTY) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
361
|
+
// Blind mouse re-assert + 1049 probe: conpty resets modes on resize
|
|
362
|
+
// too, and a dropped 1049 means every subsequent frame paints onto
|
|
363
|
+
// the MAIN screen (looks like the app spontaneously exited
|
|
364
|
+
// fullscreen). The probe's re-entry is gated on a positive DECRPM
|
|
365
|
+
// "reset" answer, so this stays inert on healthy terminals.
|
|
366
|
+
this.probeAltScreenHealth();
|
|
339
367
|
this.resetFramesForAltScreen();
|
|
340
368
|
this.needsEraseBeforePaint = true;
|
|
341
369
|
}
|
|
@@ -465,10 +493,12 @@ export default class Ink {
|
|
|
465
493
|
reanchorViewport() {
|
|
466
494
|
if (this.altScreenActive)
|
|
467
495
|
return;
|
|
496
|
+
noteFrameCause('reanchor');
|
|
468
497
|
this.log.requestViewportReanchor();
|
|
469
498
|
}
|
|
470
499
|
/** Render synchronously and invalidate older trailing/drain callbacks. */
|
|
471
500
|
renderNow() {
|
|
501
|
+
noteFrameCause('immediate');
|
|
472
502
|
this.renderGeneration++;
|
|
473
503
|
this.pendingRenderGeneration = null;
|
|
474
504
|
this.scheduleRender.cancel?.();
|
|
@@ -478,10 +508,49 @@ export default class Ink {
|
|
|
478
508
|
}
|
|
479
509
|
this.onRender();
|
|
480
510
|
}
|
|
511
|
+
/**
|
|
512
|
+
* Schedule the next scroll-drain frame — Grok Build's Presenter, ported.
|
|
513
|
+
* Grok keeps TWO cadence knobs (min_draw_ms for renders, scroll_ms for
|
|
514
|
+
* scroll); collapsing both to FRAME_INTERVAL_MS made big flicks steppy
|
|
515
|
+
* (the proportional step, 75%-of-remaining, × 4× the interval = chunky
|
|
516
|
+
* ramp and ~4× longer settle), so the drain keeps its own fast cadence:
|
|
517
|
+
*
|
|
518
|
+
* - CADENCE: quarter interval (~250fps). Drain frames are cheap
|
|
519
|
+
* (DECSTBM + ~10 patches); scroll throughput is unchanged.
|
|
520
|
+
* - IN-FLIGHT GATE: while stdout still holds unflushed bytes above
|
|
521
|
+
* PTY_BACKLOG_BYTES (slow ConPTY round trip, ssh link), queue nothing
|
|
522
|
+
* further — re-probe at quarter interval instead. Grok's equivalent
|
|
523
|
+
* (in_flight_target + writer ack) exists to keep latency bounded
|
|
524
|
+
* under exactly this backpressure; without it each stacked frame adds
|
|
525
|
+
* its full render+write to the input→paint latency, which reads as
|
|
526
|
+
* sticky, laggy scrolling on Windows terminals.
|
|
527
|
+
*
|
|
528
|
+
* The gate holds only DRAIN frames; React-driven renders (keystrokes,
|
|
529
|
+
* streaming) still render via the normal throttle — user-visible updates
|
|
530
|
+
* must never wait behind scroll output.
|
|
531
|
+
*/
|
|
532
|
+
scheduleDrain() {
|
|
533
|
+
if (this.drainTimer !== null)
|
|
534
|
+
return;
|
|
535
|
+
const stdout = this.options.stdout;
|
|
536
|
+
const backlog = typeof stdout.writableLength === 'number'
|
|
537
|
+
? stdout.writableLength
|
|
538
|
+
: 0;
|
|
539
|
+
if (backlog > PTY_BACKLOG_BYTES) {
|
|
540
|
+
this.drainTimer = setTimeout(() => {
|
|
541
|
+
this.drainTimer = null;
|
|
542
|
+
this.scheduleDrain();
|
|
543
|
+
}, FRAME_INTERVAL_MS >> 2);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
this.drainTimer = setTimeout(this.renderNow, FRAME_INTERVAL_MS >> 2);
|
|
547
|
+
}
|
|
481
548
|
onRender() {
|
|
482
549
|
if (this.isUnmounted || this.isPaused) {
|
|
483
550
|
return;
|
|
484
551
|
}
|
|
552
|
+
if (GEOMETRY_TRACE_ENABLED)
|
|
553
|
+
beginGeometryFrame(this.renderGeneration);
|
|
485
554
|
// Entering a render cancels any pending drain tick — this render will
|
|
486
555
|
// handle the drain (and re-schedule below if needed). Prevents a
|
|
487
556
|
// wheel-event-triggered render AND a drain-timer render both firing.
|
|
@@ -494,9 +563,25 @@ export default class Ink {
|
|
|
494
563
|
// Done before the render to avoid dirtying state that would trigger
|
|
495
564
|
// an extra React re-render cycle.
|
|
496
565
|
flushInteractionTime();
|
|
566
|
+
// Dimension consistency: onComputeLayout laid the tree out against
|
|
567
|
+
// this.terminalColumns/Rows (the cached values handleResize owns), so
|
|
568
|
+
// the renderer and the diff engine MUST paint that same size. Reading
|
|
569
|
+
// the live stdout size here mixed the two when a resize event had not
|
|
570
|
+
// fired yet (Windows Terminal emits 'resize' after columns already
|
|
571
|
+
// changed): Yoga laid out for the old width while log-update wrapped
|
|
572
|
+
// and diffed at the new one — rows painted off by the reflow delta.
|
|
573
|
+
// On drift, route through the resize path (cache sync + markTreeDirty
|
|
574
|
+
// + re-render) and bail: the render handleResize schedules paints the
|
|
575
|
+
// correctly-laid-out frame.
|
|
576
|
+
const liveColumns = this.options.stdout.columns || 80;
|
|
577
|
+
const liveRows = this.options.stdout.rows || 24;
|
|
578
|
+
if (this.options.stdout.isTTY && (liveColumns !== this.terminalColumns || liveRows !== this.terminalRows)) {
|
|
579
|
+
this.handleResize();
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
497
582
|
const renderStart = performance.now();
|
|
498
|
-
const terminalWidth = this.
|
|
499
|
-
const terminalRows = this.
|
|
583
|
+
const terminalWidth = this.terminalColumns;
|
|
584
|
+
const terminalRows = this.terminalRows;
|
|
500
585
|
const frame = this.renderer({
|
|
501
586
|
frontFrame: this.frontFrame,
|
|
502
587
|
backFrame: this.backFrame,
|
|
@@ -824,18 +909,16 @@ export default class Ink {
|
|
|
824
909
|
// are only ever true in alt-screen; in main-screen this is false→false.
|
|
825
910
|
this.prevFrameContaminated = selActive || hlActive;
|
|
826
911
|
// A ScrollBox has pendingScrollDelta left to drain — schedule the next
|
|
827
|
-
// frame
|
|
828
|
-
//
|
|
829
|
-
//
|
|
830
|
-
//
|
|
831
|
-
//
|
|
832
|
-
// render arrives first, renderNow cancels
|
|
833
|
-
//
|
|
834
|
-
// Drain frames are cheap (DECSTBM + ~10 patches, ~200 bytes) so run at
|
|
835
|
-
// quarter interval (~250fps, setTimeout practical floor) for max scroll
|
|
836
|
-
// speed. Regular renders stay at FRAME_INTERVAL_MS via the throttle.
|
|
912
|
+
// frame via scheduleDrain (cadence + pty backpressure gate, see there).
|
|
913
|
+
// MUST NOT call this.scheduleRender() here: we're inside a trailing-edge
|
|
914
|
+
// throttle invocation, timerId is undefined, and lodash's debounce sees
|
|
915
|
+
// timeSinceLastCall >= wait (last call was at the start of this window)
|
|
916
|
+
// → leadingEdge fires IMMEDIATELY → double render ~0.1ms apart → jank.
|
|
917
|
+
// If a wheel event or immediate render arrives first, renderNow cancels
|
|
918
|
+
// this timer — no double.
|
|
837
919
|
if (frame.scrollDrainPending) {
|
|
838
|
-
|
|
920
|
+
noteFrameCause('scroll-drain');
|
|
921
|
+
this.scheduleDrain();
|
|
839
922
|
}
|
|
840
923
|
const yogaMs = getLastYogaMs();
|
|
841
924
|
const commitMs = getLastCommitMs();
|
|
@@ -849,6 +932,7 @@ export default class Ink {
|
|
|
849
932
|
cacheHits: 0,
|
|
850
933
|
live: 0
|
|
851
934
|
};
|
|
935
|
+
endGeometryFrame(performance.now() - renderStart);
|
|
852
936
|
this.options.onFrame?.({
|
|
853
937
|
durationMs: performance.now() - renderStart,
|
|
854
938
|
phases: {
|
|
@@ -968,6 +1052,14 @@ export default class Ink {
|
|
|
968
1052
|
return;
|
|
969
1053
|
this.altScreenActive = active;
|
|
970
1054
|
this.altScreenMouseTracking = active && mouseTracking;
|
|
1055
|
+
// Screen geometry/context just changed wholesale: hover sets, the
|
|
1056
|
+
// multi-click chain, and any pending hyperlink open belong to the old
|
|
1057
|
+
// screen. Fire leave handlers before dropping the set — a bare clear()
|
|
1058
|
+
// strands the old screen's rows with hovered=true forever (stuck
|
|
1059
|
+
// highlights). Stale clickCount would turn the first click into a
|
|
1060
|
+
// double-click; stale hovered nodes would suppress real onMouseEnter.
|
|
1061
|
+
clearHovered(this.hoveredNodes);
|
|
1062
|
+
this.app?.resetPointerState();
|
|
971
1063
|
if (active) {
|
|
972
1064
|
this.mainScreenFrameState = {
|
|
973
1065
|
frontFrame: this.frontFrame,
|
|
@@ -1053,10 +1145,10 @@ export default class Ink {
|
|
|
1053
1145
|
this.renderNow();
|
|
1054
1146
|
return;
|
|
1055
1147
|
}
|
|
1056
|
-
// Mouse tracking —
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1148
|
+
// Mouse tracking + alt-screen health — the probe re-asserts mouse
|
|
1149
|
+
// blindly (idempotent) and re-enters alt only if the terminal answers
|
|
1150
|
+
// DECRPM with "1049 reset".
|
|
1151
|
+
this.probeAltScreenHealth();
|
|
1060
1152
|
// Alt-screen re-entry — destructive (ERASE_SCREEN). Only for callers that
|
|
1061
1153
|
// have a strong signal the terminal actually dropped mode 1049.
|
|
1062
1154
|
if (includeAltScreen) {
|
|
@@ -1079,6 +1171,10 @@ export default class Ink {
|
|
|
1079
1171
|
// Cancel any pending throttled render so it doesn't fire between
|
|
1080
1172
|
// cleanupTerminalModes() and process.exit() and write to main screen.
|
|
1081
1173
|
this.scheduleRender.cancel?.();
|
|
1174
|
+
if (this.drainTimer !== null) {
|
|
1175
|
+
clearTimeout(this.drainTimer);
|
|
1176
|
+
this.drainTimer = null;
|
|
1177
|
+
}
|
|
1082
1178
|
this.app?.detachForShutdown();
|
|
1083
1179
|
// Shutdown bypasses the normal unmount path, so release the process and
|
|
1084
1180
|
// stdout listeners here as well. Otherwise a SIGCONT or resize arriving
|
|
@@ -1086,6 +1182,11 @@ export default class Ink {
|
|
|
1086
1182
|
// through this detached instance after terminal cleanup has completed.
|
|
1087
1183
|
this.unsubscribeTTYHandlers?.();
|
|
1088
1184
|
this.unsubscribeExit();
|
|
1185
|
+
// unmount() early-returns on isUnmounted above, so its instances.delete
|
|
1186
|
+
// never runs — a detached instance would stay in the map and a later
|
|
1187
|
+
// instances.get(stdout) lookup (Chat's reanchorViewport plumbing) could
|
|
1188
|
+
// hand out a dead renderer. Remove the mapping here too.
|
|
1189
|
+
instances.delete(this.options.stdout);
|
|
1089
1190
|
// `detachForShutdown()` deliberately makes later `unmount()` calls a
|
|
1090
1191
|
// no-op, so release process-level output patches here rather than relying
|
|
1091
1192
|
// on unmount() to do it. The shutdown continuation may run an updater
|
|
@@ -1151,6 +1252,57 @@ export default class Ink {
|
|
|
1151
1252
|
drainStdin() {
|
|
1152
1253
|
drainStdin(this.options.stdin);
|
|
1153
1254
|
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Self-heal after a terminal-side mode reset. Windows conpty drops DEC
|
|
1257
|
+
* private modes on DPI changes, window moves between monitors and renderer
|
|
1258
|
+
* restarts — the user sees the app "exit fullscreen" with a dead mouse
|
|
1259
|
+
* while altScreenActive still claims we are in alt. Two layers:
|
|
1260
|
+
*
|
|
1261
|
+
* 1. Blind, idempotent mouse-tracking re-assert (covers the mode reset
|
|
1262
|
+
* without a round trip; ~30 bytes).
|
|
1263
|
+
* 2. DECRQM probe of mode 1049. Re-entry (destructive: ERASE) happens
|
|
1264
|
+
* ONLY on a positive "reset" answer, so iTerm2's
|
|
1265
|
+
* enter-clears-when-already-in-alt quirk can never fire on a healthy
|
|
1266
|
+
* screen, and terminals that ignore DECRQM stay inert.
|
|
1267
|
+
*
|
|
1268
|
+
* Called from every interaction dispatch (click/hover/wheel/key) plus the
|
|
1269
|
+
* focus/resize/stdin-gap triggers. The 250ms throttle keeps the round
|
|
1270
|
+
* trip bounded while active use is going on — a dropped 1049 heals on
|
|
1271
|
+
* the FIRST interaction after the drop instead of waiting for a focus
|
|
1272
|
+
* event or a >5s idle gap (mouse motion keeps lastStdinTime fresh, so
|
|
1273
|
+
* the gap path never fires during active use — the exact pattern that
|
|
1274
|
+
* left the app broken until the user gave up).
|
|
1275
|
+
*/
|
|
1276
|
+
lastHealthProbeAt = 0;
|
|
1277
|
+
probeAltScreenHealth = () => {
|
|
1278
|
+
const now = Date.now();
|
|
1279
|
+
if (now - this.lastHealthProbeAt < 250)
|
|
1280
|
+
return;
|
|
1281
|
+
this.lastHealthProbeAt = now;
|
|
1282
|
+
if (!this.options.stdout.isTTY || this.isPaused || !this.altScreenActive)
|
|
1283
|
+
return;
|
|
1284
|
+
if (this.altScreenMouseTracking) {
|
|
1285
|
+
this.options.stdout.write(ENABLE_MOUSE_TRACKING);
|
|
1286
|
+
}
|
|
1287
|
+
const querier = this.app?.querier;
|
|
1288
|
+
if (querier === undefined)
|
|
1289
|
+
return;
|
|
1290
|
+
void Promise.all([querier.send(decrqm(1049)), querier.flush()]).then(([reply]) => {
|
|
1291
|
+
// DECRPM status: 1/3 = set, 2/4 = reset, 0/undefined = unknown.
|
|
1292
|
+
// Heal only on a POSITIVE reset — an unanswered probe must not
|
|
1293
|
+
// trigger the destructive re-entry.
|
|
1294
|
+
if (reply !== undefined && (reply.status === 2 || reply.status === 4)) {
|
|
1295
|
+
this.reenterAltScreen();
|
|
1296
|
+
}
|
|
1297
|
+
}).catch(() => {
|
|
1298
|
+
/* probe is best-effort; the next trigger retries */
|
|
1299
|
+
});
|
|
1300
|
+
};
|
|
1301
|
+
/** Refocus = first observable moment after a conpty-side mode reset. */
|
|
1302
|
+
handleTerminalFocusProbe = (focused) => {
|
|
1303
|
+
if (focused)
|
|
1304
|
+
this.probeAltScreenHealth();
|
|
1305
|
+
};
|
|
1154
1306
|
/**
|
|
1155
1307
|
* Re-enter alt-screen, clear, home, re-enable mouse tracking, and reset
|
|
1156
1308
|
* frame buffers so the next render repaints from scratch. Self-heal for
|
|
@@ -1444,19 +1596,49 @@ export default class Ink {
|
|
|
1444
1596
|
* Returns true if a DOM handler consumed the click. Gated on
|
|
1445
1597
|
* altScreenActive — clicks only make sense with a fixed viewport where
|
|
1446
1598
|
* nodeCache rects map 1:1 to terminal cells (no scrollback offset).
|
|
1599
|
+
* The button byte is the raw SGR release code; its modifier bits land
|
|
1600
|
+
* on ClickEvent.shift/alt/ctrl.
|
|
1447
1601
|
*/
|
|
1448
|
-
dispatchClick(col, row) {
|
|
1449
|
-
|
|
1602
|
+
dispatchClick(col, row, button = 0) {
|
|
1603
|
+
// Interaction-level health probe: the first click after a conpty-side
|
|
1604
|
+
// mode drop triggers the heal (probe is throttled + no-op on healthy
|
|
1605
|
+
// screens). Runs before the gate so a dropped 1049 is re-entered
|
|
1606
|
+
// within a round trip instead of the click silently missing.
|
|
1607
|
+
this.probeAltScreenHealth();
|
|
1608
|
+
if (!this.altScreenActive) {
|
|
1609
|
+
logMouseDebug('dispatchClick skipped — alt screen inactive', { col, row });
|
|
1450
1610
|
return false;
|
|
1611
|
+
}
|
|
1451
1612
|
const blank = isEmptyCellAt(this.frontFrame.screen, col, row);
|
|
1452
|
-
|
|
1613
|
+
const handled = dispatchClick(this.rootNode, col, row, blank, button);
|
|
1614
|
+
logMouseDebug('dispatchClick', { col, row, handled });
|
|
1615
|
+
return handled;
|
|
1616
|
+
}
|
|
1617
|
+
/**
|
|
1618
|
+
* Route a wheel event to the ScrollBox (any onWheel handler) under the
|
|
1619
|
+
* pointer. Returns true when a handler consumed it, so App can skip the
|
|
1620
|
+
* legacy global wheel-key path and exactly one layer scrolls. Gated on
|
|
1621
|
+
* altScreenActive like dispatchClick — without mouse tracking there are
|
|
1622
|
+
* no wheel coordinates to route by.
|
|
1623
|
+
*/
|
|
1624
|
+
dispatchWheelAt(col, row, deltaY, deltaX = 0, button = 0) {
|
|
1625
|
+
this.probeAltScreenHealth();
|
|
1626
|
+
if (!this.altScreenActive)
|
|
1627
|
+
return false;
|
|
1628
|
+
const handled = dispatchWheel(this.rootNode, col, row, deltaY, deltaX, button);
|
|
1629
|
+
if (handled) {
|
|
1630
|
+
logMouseDebug('dispatchWheelAt consumed', { col, row, deltaY, deltaX });
|
|
1631
|
+
}
|
|
1632
|
+
return handled;
|
|
1453
1633
|
}
|
|
1454
1634
|
dispatchHover(col, row) {
|
|
1635
|
+
this.probeAltScreenHealth();
|
|
1455
1636
|
if (!this.altScreenActive)
|
|
1456
1637
|
return;
|
|
1457
1638
|
dispatchHover(this.rootNode, col, row, this.hoveredNodes);
|
|
1458
1639
|
}
|
|
1459
1640
|
dispatchKeyboardEvent(parsedKey) {
|
|
1641
|
+
this.probeAltScreenHealth();
|
|
1460
1642
|
const target = this.focusManager.activeElement ?? this.rootNode;
|
|
1461
1643
|
const event = new KeyboardEvent(parsedKey);
|
|
1462
1644
|
dispatcher.dispatchDiscrete(target, event);
|
|
@@ -1619,6 +1801,9 @@ export default class Ink {
|
|
|
1619
1801
|
// cascades through useContext → <AlternateScreen>'s useLayoutEffect dep
|
|
1620
1802
|
// array → spurious exit+re-enter of the alt screen on every SIGWINCH.
|
|
1621
1803
|
writeRaw(data) {
|
|
1804
|
+
if (data.includes('\x1b[?1049')) {
|
|
1805
|
+
logMouseDebug('stdout:1049', { len: data.length, head: data.slice(0, 60) });
|
|
1806
|
+
}
|
|
1622
1807
|
this.options.stdout.write(data);
|
|
1623
1808
|
}
|
|
1624
1809
|
setCursorDeclaration = (decl, clearIfNode) => {
|
|
@@ -1632,7 +1817,7 @@ export default class Ink {
|
|
|
1632
1817
|
}
|
|
1633
1818
|
render(node) {
|
|
1634
1819
|
this.currentNode = node;
|
|
1635
|
-
const tree = _jsx(App, { ref: this.setAppRef, stdin: this.options.stdin, stdout: this.options.stdout, stderr: this.options.stderr, exitOnCtrlC: this.options.exitOnCtrlC, onExit: this.unmount, terminalColumns: this.terminalColumns, terminalRows: this.terminalRows, selection: this.selection, onSelectionChange: this.notifySelectionChange, onClickAt: this.dispatchClick, onHoverAt: this.dispatchHover, getHyperlinkAt: this.getHyperlinkAt, onOpenHyperlink: this.openHyperlink, onMultiClick: this.handleMultiClick, onSelectionDrag: this.handleSelectionDrag, onStdinResume: this.reassertTerminalModes, onCursorDeclaration: this.setCursorDeclaration, dispatchKeyboardEvent: this.dispatchKeyboardEvent, children: _jsx(TerminalWriteProvider, { value: this.writeRaw, children: node }) });
|
|
1820
|
+
const tree = _jsx(App, { ref: this.setAppRef, stdin: this.options.stdin, stdout: this.options.stdout, stderr: this.options.stderr, exitOnCtrlC: this.options.exitOnCtrlC, onExit: this.unmount, terminalColumns: this.terminalColumns, terminalRows: this.terminalRows, selection: this.selection, onSelectionChange: this.notifySelectionChange, onClickAt: this.dispatchClick, onHoverAt: this.dispatchHover, onWheelAt: this.dispatchWheelAt, getHyperlinkAt: this.getHyperlinkAt, onOpenHyperlink: this.openHyperlink, onMultiClick: this.handleMultiClick, onSelectionDrag: this.handleSelectionDrag, onStdinResume: this.reassertTerminalModes, onTerminalFocus: this.handleTerminalFocusProbe, onCursorDeclaration: this.setCursorDeclaration, dispatchKeyboardEvent: this.dispatchKeyboardEvent, children: _jsx(TerminalWriteProvider, { value: this.writeRaw, children: node }) });
|
|
1636
1821
|
// @ts-ignore -- ported CC build; type drift tolerated updateContainerSync exists in react-reconciler but not in @types/react-reconciler
|
|
1637
1822
|
reconciler.updateContainerSync(tree, this.container, null, noop);
|
|
1638
1823
|
// @ts-ignore -- ported CC build; type drift tolerated flushSyncWork exists in react-reconciler but not in @types/react-reconciler
|
|
@@ -1806,9 +1991,25 @@ export default class Ink {
|
|
|
1806
1991
|
logForDebugging(`[stderr] ${text}`, {
|
|
1807
1992
|
level: 'warn'
|
|
1808
1993
|
});
|
|
1809
|
-
if (
|
|
1810
|
-
this.
|
|
1811
|
-
|
|
1994
|
+
if (!this.isUnmounted && !this.isPaused) {
|
|
1995
|
+
if (this.altScreenActive) {
|
|
1996
|
+
this.prevFrameContaminated = true;
|
|
1997
|
+
this.scheduleRender();
|
|
1998
|
+
}
|
|
1999
|
+
else {
|
|
2000
|
+
// Main-screen (inline): the diff engine's moves are purely
|
|
2001
|
+
// relative to the physical cursor, so ANY unobserved tty write
|
|
2002
|
+
// (one that slipped through before this patch existed, or via
|
|
2003
|
+
// a path it can't intercept — a snapshotted ESM writer) shifts
|
|
2004
|
+
// every later write by N rows with nothing to detect it after
|
|
2005
|
+
// the fact. The stdin-gap reassert covers slow leaks; this
|
|
2006
|
+
// per-write defensive net closes the fast ones: blind
|
|
2007
|
+
// idempotent viewport repaint from the physical cursor. When
|
|
2008
|
+
// nothing drifted (the usual case — the write was swallowed)
|
|
2009
|
+
// it paints the same pixels again at O(viewport) bytes.
|
|
2010
|
+
this.log.requestViewportReanchor();
|
|
2011
|
+
this.scheduleRender();
|
|
2012
|
+
}
|
|
1812
2013
|
}
|
|
1813
2014
|
}
|
|
1814
2015
|
finally {
|
|
@@ -12,6 +12,11 @@ export type CachedLayout = {
|
|
|
12
12
|
width: number;
|
|
13
13
|
height: number;
|
|
14
14
|
top?: number;
|
|
15
|
+
/** Effective background color (own ?? inherited) at the last render.
|
|
16
|
+
* Renderer compares it per frame so a background change refuses the
|
|
17
|
+
* prevScreen blit — children would otherwise resurrect the stale color
|
|
18
|
+
* (stuck hover highlight). */
|
|
19
|
+
bg?: string;
|
|
15
20
|
};
|
|
16
21
|
/** Layout bounds cached per rendered node, used for blitting and clearing. */
|
|
17
22
|
export declare const nodeCache: WeakMap<DOMElement, CachedLayout>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-cache.d.ts","sourceRoot":"","sources":["../../../src/ink/node-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAErD;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"node-cache.d.ts","sourceRoot":"","sources":["../../../src/ink/node-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAErD;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;;mCAG+B;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,8EAA8E;AAC9E,eAAO,MAAM,SAAS,mCAA0C,CAAA;AAEhE,kEAAkE;AAClE,eAAO,MAAM,aAAa,kCAAyC,CAAA;AAanE;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,SAAS,EACf,UAAU,EAAE,OAAO,GAClB,IAAI,CAUN;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAIpD"}
|
|
@@ -131,6 +131,14 @@ export type ParsedKey = {
|
|
|
131
131
|
raw: string | undefined;
|
|
132
132
|
code?: string;
|
|
133
133
|
isPasted: boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Pointer column (0-indexed) for wheel keys — SGR/X10 wheel sequences
|
|
136
|
+
* carry the position the wheel event occurred at. Undefined for
|
|
137
|
+
* non-wheel keys.
|
|
138
|
+
*/
|
|
139
|
+
mouseCol?: number;
|
|
140
|
+
/** Pointer row (0-indexed) for wheel keys. See mouseCol. */
|
|
141
|
+
mouseRow?: number;
|
|
134
142
|
};
|
|
135
143
|
/** A terminal response sequence (DECRPM, DA1, OSC reply, etc.) parsed
|
|
136
144
|
* out of the input stream. Not user input — consumers should dispatch
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-keypress.d.ts","sourceRoot":"","sources":["../../../src/ink/parse-keypress.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE/B,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAoJtE,gDAAgD;AAChD,eAAO,MAAM,aAAa;;;;;;CAMhB,CAAA;AAEV;;;GAGG;AACH,MAAM,MAAM,gBAAgB;AAC1B,iEAAiE;AAC/D;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AAClD,oEAAoE;GAClE;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE;AACnC,+DAA+D;GAC7D;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE;AACnC,iEAAiE;GAC/D;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAC1C,sDAAsD;GACpD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE;AACtD,kFAAkF;GAChF;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AAC7C;wEACwE;GACtE;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAyTvC,iEAAiE;AACjE,MAAM,MAAM,eAAe,GAAG;IAC5B,wDAAwD;IACxD,MAAM,EAAE,OAAO,CAAA;IACf,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAA;IACf,yDAAyD;IACzD,IAAI,EAAE,SAAS,EAAE,CAAA;IACjB,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAsFD;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAA;IAC3B,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,eAAe,CAAA;IAE5B,UAAU,CAAC,EAAE,SAAS,CAAA;CACvB,CAAA;AAED,kDAAkD;AAClD,eAAO,MAAM,aAAa,EAAE,aAI3B,CAAA;AAmBD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,aAAa,EACxB,KAAK,GAAE,MAAM,GAAG,MAAM,GAAG,IAAS,GACjC,CAAC,WAAW,EAAE,EAAE,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"parse-keypress.d.ts","sourceRoot":"","sources":["../../../src/ink/parse-keypress.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE/B,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAoJtE,gDAAgD;AAChD,eAAO,MAAM,aAAa;;;;;;CAMhB,CAAA;AAEV;;;GAGG;AACH,MAAM,MAAM,gBAAgB;AAC1B,iEAAiE;AAC/D;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AAClD,oEAAoE;GAClE;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE;AACnC,+DAA+D;GAC7D;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE;AACnC,iEAAiE;GAC/D;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAC1C,sDAAsD;GACpD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE;AACtD,kFAAkF;GAChF;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AAC7C;wEACwE;GACtE;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAyTvC,iEAAiE;AACjE,MAAM,MAAM,eAAe,GAAG;IAC5B,wDAAwD;IACxD,MAAM,EAAE,OAAO,CAAA;IACf,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAA;IACf,yDAAyD;IACzD,IAAI,EAAE,SAAS,EAAE,CAAA;IACjB,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAsFD;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAA;IAC3B,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,eAAe,CAAA;IAE5B,UAAU,CAAC,EAAE,SAAS,CAAA;CACvB,CAAA;AAED,kDAAkD;AAClD,eAAO,MAAM,aAAa,EAAE,aAI3B,CAAA;AAmBD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,aAAa,EACxB,KAAK,GAAE,MAAM,GAAG,MAAM,GAAG,IAAS,GACjC,CAAC,WAAW,EAAE,EAAE,aAAa,CAAC,CAuKhC;AA0GD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,UAe/B,CAAA;AA0HD;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,KAAK,CAAA;IACX,EAAE,EAAE,OAAO,CAAA;IACX,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;IACxB,IAAI,EAAE,OAAO,CAAA;IACb,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5B,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,OAAO,CAAA;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED;;6BAE6B;AAC7B,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,UAAU,CAAA;IAChB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,gBAAgB,CAAA;CAC3B,CAAA;AAED;;0DAE0D;AAC1D,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,OAAO,CAAA;IACb;4DACwD;IACxD,MAAM,EAAE,MAAM,CAAA;IACd,2DAA2D;IAC3D,MAAM,EAAE,OAAO,GAAG,SAAS,CAAA;IAC3B,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAA;IACX,oCAAoC;IACpC,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED;2EAC2E;AAC3E,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,cAAc,CAAA"}
|
|
@@ -610,7 +610,12 @@ export function parseMultipleKeypresses(prevState, input = '') {
|
|
|
610
610
|
keys.push({ kind: 'response', sequence: token.value, response });
|
|
611
611
|
}
|
|
612
612
|
else {
|
|
613
|
-
|
|
613
|
+
// SGR first (1006); X10 (legacy 1000/1002 without SGR) as the
|
|
614
|
+
// compatibility fallback for clicks/drags. Wheel falls through
|
|
615
|
+
// to parseKeypress, which turns it into a wheel key WITH the
|
|
616
|
+
// pointer coordinates for position-based routing.
|
|
617
|
+
const mouse = parseMouseEvent(token.value) ??
|
|
618
|
+
parseX10MouseEvent(token.value);
|
|
614
619
|
if (mouse) {
|
|
615
620
|
keys.push(mouse);
|
|
616
621
|
}
|
|
@@ -819,6 +824,8 @@ export const nonAlphanumericKeys = [
|
|
|
819
824
|
'backspace',
|
|
820
825
|
'wheelup',
|
|
821
826
|
'wheeldown',
|
|
827
|
+
'wheelleft',
|
|
828
|
+
'wheelright',
|
|
822
829
|
'mouse',
|
|
823
830
|
];
|
|
824
831
|
const isShiftKey = (code) => {
|
|
@@ -955,6 +962,39 @@ function parseMouseEvent(s) {
|
|
|
955
962
|
sequence: s,
|
|
956
963
|
};
|
|
957
964
|
}
|
|
965
|
+
/**
|
|
966
|
+
* Parse an X10 mouse sequence (CSI M + Cb/Cx/Cy raw bytes, each +32) into
|
|
967
|
+
* a ParsedMouse, or null for anything else. Serves terminals that honor
|
|
968
|
+
* DECSET 1000/1002 but ignore 1006 (SGR): clicks and button-drags become
|
|
969
|
+
* real ParsedMouse events so text selection works there too.
|
|
970
|
+
*
|
|
971
|
+
* Protocol limits (documented, accepted): X10 has no release events — a
|
|
972
|
+
* started selection finishes via App's lost-release recovery (next press /
|
|
973
|
+
* focus-out / no-button motion), and click dispatch (release-based) stays
|
|
974
|
+
* SGR-only. No-button motion (hover) is unrepresentable without the drag
|
|
975
|
+
* bit, so hover stays SGR-only. Wheel returns null (parseKeypress's wheel
|
|
976
|
+
* branch turns it into a key with coordinates).
|
|
977
|
+
*/
|
|
978
|
+
function parseX10MouseEvent(s) {
|
|
979
|
+
if (s.length !== 6 || !s.startsWith('\x1b[M'))
|
|
980
|
+
return null;
|
|
981
|
+
const button = s.charCodeAt(3) - 32;
|
|
982
|
+
// Wheel (bit 6) → key path; no-button without drag bit → unsupported
|
|
983
|
+
// hover encoding, swallow upstream.
|
|
984
|
+
if ((button & 0x40) !== 0)
|
|
985
|
+
return null;
|
|
986
|
+
if ((button & 0x03) === 3 && (button & 0x20) === 0)
|
|
987
|
+
return null;
|
|
988
|
+
return {
|
|
989
|
+
kind: 'mouse',
|
|
990
|
+
button,
|
|
991
|
+
action: 'press',
|
|
992
|
+
// X10 coords are 1-indexed like SGR (charCode - 32).
|
|
993
|
+
col: s.charCodeAt(4) - 32,
|
|
994
|
+
row: s.charCodeAt(5) - 32,
|
|
995
|
+
sequence: s,
|
|
996
|
+
};
|
|
997
|
+
}
|
|
958
998
|
function parseKeypress(s = '') {
|
|
959
999
|
let parts;
|
|
960
1000
|
const key = {
|
|
@@ -1020,26 +1060,53 @@ function parseKeypress(s = '') {
|
|
|
1020
1060
|
// + direction while ignoring modifier bits (Shift=0x04, Meta=0x08,
|
|
1021
1061
|
// Ctrl=0x10) — modified wheel events (e.g. Ctrl+scroll, button=80)
|
|
1022
1062
|
// should still be recognized as wheelup/wheeldown.
|
|
1063
|
+
//
|
|
1064
|
+
// The SGR sequence carries the pointer position (CSI < btn;col;row M) —
|
|
1065
|
+
// preserved on the ParsedKey (mouseCol/mouseRow, 0-indexed) so wheel
|
|
1066
|
+
// routing can hit-test the ScrollBox under the pointer instead of
|
|
1067
|
+
// scrolling a hardcoded target. Buttons 66/67 are horizontal wheel
|
|
1068
|
+
// (wheelleft/wheelright); kept as keys with coords for future consumers.
|
|
1023
1069
|
if ((match = SGR_MOUSE_RE.exec(s))) {
|
|
1024
1070
|
const button = parseInt(match[1], 10);
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1071
|
+
const col = parseInt(match[2], 10) - 1;
|
|
1072
|
+
const row = parseInt(match[3], 10) - 1;
|
|
1073
|
+
const dir = button & 0x43;
|
|
1074
|
+
if (dir === 0x40)
|
|
1075
|
+
return createWheelKey(s, 'wheelup', col, row, button);
|
|
1076
|
+
if (dir === 0x41)
|
|
1077
|
+
return createWheelKey(s, 'wheeldown', col, row, button);
|
|
1078
|
+
if (dir === 0x42)
|
|
1079
|
+
return createWheelKey(s, 'wheelleft', col, row, button);
|
|
1080
|
+
if (dir === 0x43)
|
|
1081
|
+
return createWheelKey(s, 'wheelright', col, row, button);
|
|
1029
1082
|
// Shouldn't reach here (parseMouseEvent catches non-wheel) but be safe
|
|
1030
1083
|
return createNavKey(s, 'mouse', false);
|
|
1031
1084
|
}
|
|
1032
1085
|
// X10 mouse: CSI M + 3 raw bytes (Cb+32, Cx+32, Cy+32). Terminals that
|
|
1033
1086
|
// ignore DECSET 1006 (SGR) but honor 1000/1002 emit this legacy encoding.
|
|
1034
|
-
// Button bits match SGR: 0x40 = wheel, low bit = direction.
|
|
1035
|
-
//
|
|
1036
|
-
//
|
|
1087
|
+
// Button bits match SGR: 0x40 = wheel, low bit = direction, 0x20 = drag.
|
|
1088
|
+
// Wheel events become wheel keys (with coordinates, like SGR); clicks and
|
|
1089
|
+
// drags become ParsedMouse so X10-only terminals get selection support.
|
|
1090
|
+
// X10 cannot report button release — click dispatch (release-based) stays
|
|
1091
|
+
// SGR-only; a started selection finishes via the lost-release recovery
|
|
1092
|
+
// paths (next press / focus-out / no-button motion).
|
|
1037
1093
|
if (s.length === 6 && s.startsWith('\x1b[M')) {
|
|
1038
1094
|
const button = s.charCodeAt(3) - 32;
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1095
|
+
const col = s.charCodeAt(4) - 32 - 1;
|
|
1096
|
+
const row = s.charCodeAt(5) - 32 - 1;
|
|
1097
|
+
const dir = button & 0x43;
|
|
1098
|
+
if (dir === 0x40)
|
|
1099
|
+
return createWheelKey(s, 'wheelup', col, row, button);
|
|
1100
|
+
if (dir === 0x41)
|
|
1101
|
+
return createWheelKey(s, 'wheeldown', col, row, button);
|
|
1102
|
+
if (dir === 0x42)
|
|
1103
|
+
return createWheelKey(s, 'wheelleft', col, row, button);
|
|
1104
|
+
if (dir === 0x43)
|
|
1105
|
+
return createWheelKey(s, 'wheelright', col, row, button);
|
|
1106
|
+
// Non-wheel X10 clicks/drags are intercepted by parseX10MouseEvent in
|
|
1107
|
+
// parseMultipleKeypresses before this function runs; reaching here means
|
|
1108
|
+
// a flush path bypassed it (no-button motion without the drag bit — X10
|
|
1109
|
+
// hover, unsupported). Swallow as before.
|
|
1043
1110
|
return createNavKey(s, 'mouse', false);
|
|
1044
1111
|
}
|
|
1045
1112
|
if (s === '\r') {
|
|
@@ -1157,3 +1224,25 @@ function createNavKey(s, name, ctrl) {
|
|
|
1157
1224
|
isPasted: false,
|
|
1158
1225
|
};
|
|
1159
1226
|
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Build a wheel ParsedKey preserving the pointer position and modifier
|
|
1229
|
+
* bits from the protocol's button byte (Shift=0x04, Meta/Alt=0x08,
|
|
1230
|
+
* Ctrl=0x10 — same bit layout decodeModifier documents).
|
|
1231
|
+
*/
|
|
1232
|
+
function createWheelKey(s, name, col, row, button) {
|
|
1233
|
+
return {
|
|
1234
|
+
kind: 'key',
|
|
1235
|
+
name,
|
|
1236
|
+
ctrl: (button & 0x10) !== 0,
|
|
1237
|
+
meta: (button & 0x08) !== 0,
|
|
1238
|
+
shift: (button & 0x04) !== 0,
|
|
1239
|
+
option: false,
|
|
1240
|
+
super: false,
|
|
1241
|
+
fn: false,
|
|
1242
|
+
sequence: s,
|
|
1243
|
+
raw: s,
|
|
1244
|
+
isPasted: false,
|
|
1245
|
+
mouseCol: col,
|
|
1246
|
+
mouseRow: row,
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DOMElement } from './dom.js';
|
|
2
|
+
import type { Rectangle } from './layout/geometry.js';
|
|
2
3
|
import type Output from './output.js';
|
|
3
4
|
import type { Screen } from './screen.js';
|
|
4
5
|
import { type StyledSegment } from './squash-text-nodes.js';
|
|
@@ -22,6 +23,15 @@ export type ScrollHint = {
|
|
|
22
23
|
bottom: number;
|
|
23
24
|
delta: number;
|
|
24
25
|
};
|
|
26
|
+
export type AbsoluteHitEntry = {
|
|
27
|
+
node: DOMElement;
|
|
28
|
+
rect: Rectangle;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* The current frame's absolute-positioned nodes in paint order.
|
|
32
|
+
* @returns read-only list; reverse-iterate for topmost-first hit-testing.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getAbsoluteHitList(): readonly AbsoluteHitEntry[];
|
|
25
35
|
/** Reset the scroll hint for the next frame and rotate the absolute-rect buffers. */
|
|
26
36
|
export declare function resetScrollHint(): void;
|
|
27
37
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-node-to-output.d.ts","sourceRoot":"","sources":["../../../src/ink/render-node-to-output.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"render-node-to-output.d.ts","sourceRoot":"","sources":["../../../src/ink/render-node-to-output.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAG1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAGrD,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AAErC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EACL,KAAK,aAAa,EAEnB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAqBxC,6CAA6C;AAC7C,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAOD;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAkBvE,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAAA;AAGpE;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,SAAS,gBAAgB,EAAE,CAEhE;AAED,qFAAqF;AACrF,wBAAgB,eAAe,IAAI,IAAI,CAKtC;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,UAAU,GAAG,IAAI,CAEjD;AASD,8DAA8D;AAC9D,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,UAAU,GAAG,IAAI,CAEtD;AAgBD;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;CACvB,CAAA;AAGD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,IAAI,YAAY,EAAE,CAIpD;AAoFD;;;;;GAKG;AACH,iBAAS,qBAAqB,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,MAAM,EAAE,CASlE;AAED;;;;;;;;;;;GAWG;AACH,iBAAS,wBAAwB,CAC/B,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,aAAa,EAAE,EACzB,aAAa,EAAE,MAAM,EAAE,EACvB,aAAa,EAAE,MAAM,EACrB,WAAW,GAAE,OAAe,GAC3B,MAAM,CA0GR;AA+DD;;;;;;;;;GASG;AACH,iBAAS,kBAAkB,CACzB,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE;IACP,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IAM9B,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,wBAAwB,CAAC,EAAE,KAAK,CAAA;CACjC,GACA,IAAI,CAq9BN;AA0OD,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,CAAA;AAE1D,eAAe,kBAAkB,CAAA"}
|