@deepseek-harness-tui/dsh-tui 0.8.6 → 0.8.8
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 +15 -5
- package/bin/dsh-tui.js +269 -218
- package/cordis.patch.yml +48 -22
- package/lib/types/cc/figures.d.ts.map +1 -1
- package/lib/types/cc/figures.js +14 -13
- package/lib/types/cc/syntaxRuns.d.ts +16 -0
- package/lib/types/cc/syntaxRuns.d.ts.map +1 -0
- package/lib/types/cc/syntaxRuns.js +79 -0
- package/lib/types/commands.d.ts +14 -0
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +20 -1
- package/lib/types/components/Chat/SubagentMessage.d.ts +19 -0
- package/lib/types/components/Chat/SubagentMessage.d.ts.map +1 -0
- package/lib/types/components/Chat/SubagentMessage.js +75 -0
- package/lib/types/components/EffortInputBorder.d.ts.map +1 -1
- package/lib/types/components/EffortInputBorder.js +2 -2
- package/lib/types/components/EffortTierBadge.js +1 -1
- package/lib/types/components/FileSuggestions.d.ts +8 -4
- package/lib/types/components/FileSuggestions.d.ts.map +1 -1
- package/lib/types/components/FileSuggestions.js +22 -12
- package/lib/types/components/HelpMenu.d.ts +8 -1
- package/lib/types/components/HelpMenu.d.ts.map +1 -1
- package/lib/types/components/HelpMenu.js +15 -4
- package/lib/types/components/LogoV2.d.ts +7 -1
- package/lib/types/components/LogoV2.d.ts.map +1 -1
- package/lib/types/components/LogoV2.js +13 -4
- package/lib/types/components/MessageList.d.ts +6 -2
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +17 -6
- package/lib/types/components/ModelPicker.js +2 -2
- package/lib/types/components/PromptInput.d.ts +1 -1
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +100 -36
- package/lib/types/components/SkillsPicker.js +2 -2
- package/lib/types/components/SplitDiffView.d.ts +1 -13
- package/lib/types/components/SplitDiffView.d.ts.map +1 -1
- package/lib/types/components/SplitDiffView.js +6 -105
- package/lib/types/components/SubagentCard.d.ts +9 -0
- package/lib/types/components/SubagentCard.d.ts.map +1 -0
- package/lib/types/components/SubagentCard.js +26 -0
- package/lib/types/components/SubagentDashboard.d.ts +13 -0
- package/lib/types/components/SubagentDashboard.d.ts.map +1 -0
- package/lib/types/components/SubagentDashboard.js +50 -0
- package/lib/types/components/SubagentDetailScene.d.ts +16 -0
- package/lib/types/components/SubagentDetailScene.d.ts.map +1 -0
- package/lib/types/components/SubagentDetailScene.js +141 -0
- package/lib/types/components/SyntaxText.d.ts +15 -0
- package/lib/types/components/SyntaxText.d.ts.map +1 -0
- package/lib/types/components/SyntaxText.js +34 -0
- package/lib/types/components/ThinkingToggle.d.ts +3 -10
- package/lib/types/components/ThinkingToggle.d.ts.map +1 -1
- package/lib/types/components/ThinkingToggle.js +4 -9
- package/lib/types/components/design-system/ListItem.d.ts.map +1 -1
- package/lib/types/components/design-system/ListItem.js +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.js +18 -7
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +2 -0
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +42 -6
- package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
- package/lib/types/components/messages/UserPromptMessage.js +15 -2
- package/lib/types/dsh-adapter/channel.d.ts +64 -3
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +387 -37
- package/lib/types/dsh-adapter/contract.d.ts +67 -4
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +149 -15
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +6 -6
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +38 -0
- package/lib/types/dsh-adapter/shortcuts.js +3 -3
- package/lib/types/dsh-adapter/subagents.d.ts +87 -0
- package/lib/types/dsh-adapter/subagents.d.ts.map +1 -0
- package/lib/types/dsh-adapter/subagents.js +204 -0
- package/lib/types/herdr.d.ts +28 -0
- package/lib/types/herdr.d.ts.map +1 -0
- package/lib/types/herdr.js +133 -0
- package/lib/types/i18n.d.ts +233 -32
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +87 -16
- package/lib/types/ink/ink.d.ts +4 -0
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +84 -38
- package/lib/types/ink/render-node-to-output.d.ts +6 -3
- package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
- package/lib/types/ink/render-node-to-output.js +33 -7
- package/lib/types/ink/selection.d.ts +29 -4
- package/lib/types/ink/selection.d.ts.map +1 -1
- package/lib/types/ink/selection.js +41 -5
- package/lib/types/minimalMode.d.ts +13 -0
- package/lib/types/minimalMode.d.ts.map +1 -0
- package/lib/types/minimalMode.js +17 -0
- package/lib/types/screens/Chat.d.ts +1 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +94 -43
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +6 -2
- package/lib/types/screens/TrajectoryScene.d.ts.map +1 -1
- package/lib/types/screens/TrajectoryScene.js +4 -2
- package/lib/types/theme.d.ts +8 -0
- package/lib/types/theme.d.ts.map +1 -1
- package/lib/types/theme.js +38 -14
- package/lib/types/tips.d.ts.map +1 -1
- package/lib/types/tips.js +25 -7
- package/lib/types/update.d.ts +18 -0
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +73 -2
- package/lib/types/utils/execFileNoThrow.d.ts +3 -20
- package/lib/types/utils/execFileNoThrow.d.ts.map +1 -1
- package/lib/types/utils/execFileNoThrow.js +53 -32
- package/lib/types/utils/externalEditor.d.ts +12 -11
- package/lib/types/utils/externalEditor.d.ts.map +1 -1
- package/lib/types/utils/externalEditor.js +11 -11
- package/lib/types/utils/fileSuggestions.d.ts +19 -0
- package/lib/types/utils/fileSuggestions.d.ts.map +1 -0
- package/lib/types/utils/fileSuggestions.js +40 -0
- package/lib/types/utils/mentions.d.ts.map +1 -1
- package/lib/types/utils/mentions.js +3 -1
- package/package.json +81 -74
package/lib/types/ink/ink.js
CHANGED
|
@@ -30,7 +30,7 @@ import { applyPositionedHighlight, scanPositions } from './render-to-screen.js';
|
|
|
30
30
|
import createRenderer from './renderer.js';
|
|
31
31
|
import { CharPool, cellAt, createScreen, HyperlinkPool, isEmptyCellAt, migrateScreenPools, StylePool } from './screen.js';
|
|
32
32
|
import { applySearchHighlight } from './searchHighlight.js';
|
|
33
|
-
import { applySelectionOverlay, captureScrolledRows, clearSelection, createSelectionState, extendSelection, findPlainTextUrlAt, getSelectedText, hasSelection, moveFocus, selectLineAt, selectWordAt, shiftAnchor, shiftSelection, shiftSelectionForFollow, startSelection, updateSelection } from './selection.js';
|
|
33
|
+
import { applySelectionOverlay, captureScrolledRows, clearSelection, createSelectionState, extendSelection, findPlainTextUrlAt, getSelectedText, hasSelection, moveFocus, pickFollowForSelection, selectLineAt, selectWordAt, shiftAnchor, shiftSelection, shiftSelectionForFollow, startSelection, updateSelection } from './selection.js';
|
|
34
34
|
import { isDecstbmSafe, SYNC_OUTPUT_SUPPORTED, supportsExtendedKeys, supportsWin32InputMode, writeDiffToTerminal } from './terminal.js';
|
|
35
35
|
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
36
|
import { DBP, DFE, DISABLE_MOUSE_TRACKING, ENABLE_MOUSE_TRACKING, ENTER_ALT_SCREEN, EXIT_ALT_SCREEN, SHOW_CURSOR } from './termio/dec.js';
|
|
@@ -87,6 +87,10 @@ export default class Ink {
|
|
|
87
87
|
backFrame;
|
|
88
88
|
lastPoolResetTime = performance.now();
|
|
89
89
|
drainTimer = null;
|
|
90
|
+
// Every scheduled microtask carries the generation that created it. Immediate
|
|
91
|
+
// renders invalidate older trailing work before it can append an old frame.
|
|
92
|
+
renderGeneration = 0;
|
|
93
|
+
pendingRenderGeneration = null;
|
|
90
94
|
lastYogaCounters = {
|
|
91
95
|
ms: 0,
|
|
92
96
|
visited: 0,
|
|
@@ -195,8 +199,19 @@ export default class Ink {
|
|
|
195
199
|
// effects have committed, so the native cursor tracks the caret without
|
|
196
200
|
// a one-keystroke lag. Same event-loop tick, so throughput is unchanged.
|
|
197
201
|
// Test env uses onImmediateRender (direct onRender, no throttle) so
|
|
198
|
-
// existing synchronous lastFrame() tests are unaffected.
|
|
199
|
-
|
|
202
|
+
// existing synchronous lastFrame() tests are unaffected. Keep a
|
|
203
|
+
// generation on the microtask: an immediate render may supersede the
|
|
204
|
+
// leading frame before its deferred callback runs.
|
|
205
|
+
const deferredRender = () => {
|
|
206
|
+
const generation = ++this.renderGeneration;
|
|
207
|
+
this.pendingRenderGeneration = generation;
|
|
208
|
+
queueMicrotask(() => {
|
|
209
|
+
if (this.pendingRenderGeneration !== generation || this.renderGeneration !== generation)
|
|
210
|
+
return;
|
|
211
|
+
this.pendingRenderGeneration = null;
|
|
212
|
+
this.onRender();
|
|
213
|
+
});
|
|
214
|
+
};
|
|
200
215
|
this.scheduleRender = throttle(deferredRender, FRAME_INTERVAL_MS, {
|
|
201
216
|
leading: true,
|
|
202
217
|
trailing: true
|
|
@@ -220,7 +235,7 @@ export default class Ink {
|
|
|
220
235
|
this.rootNode.focusManager = this.focusManager;
|
|
221
236
|
this.renderer = createRenderer(this.rootNode, this.stylePool);
|
|
222
237
|
this.rootNode.onRender = this.scheduleRender;
|
|
223
|
-
this.rootNode.onImmediateRender = this.
|
|
238
|
+
this.rootNode.onImmediateRender = this.renderNow;
|
|
224
239
|
this.rootNode.onComputeLayout = () => {
|
|
225
240
|
// Calculate layout during React's commit phase so useLayoutEffect hooks
|
|
226
241
|
// have access to fresh layout data
|
|
@@ -452,6 +467,17 @@ export default class Ink {
|
|
|
452
467
|
return;
|
|
453
468
|
this.log.requestViewportReanchor();
|
|
454
469
|
}
|
|
470
|
+
/** Render synchronously and invalidate older trailing/drain callbacks. */
|
|
471
|
+
renderNow() {
|
|
472
|
+
this.renderGeneration++;
|
|
473
|
+
this.pendingRenderGeneration = null;
|
|
474
|
+
this.scheduleRender.cancel?.();
|
|
475
|
+
if (this.drainTimer !== null) {
|
|
476
|
+
clearTimeout(this.drainTimer);
|
|
477
|
+
this.drainTimer = null;
|
|
478
|
+
}
|
|
479
|
+
this.onRender();
|
|
480
|
+
}
|
|
455
481
|
onRender() {
|
|
456
482
|
if (this.isUnmounted || this.isPaused) {
|
|
457
483
|
return;
|
|
@@ -481,10 +507,11 @@ export default class Ink {
|
|
|
481
507
|
prevFrameContaminated: this.prevFrameContaminated
|
|
482
508
|
});
|
|
483
509
|
const rendererMs = performance.now() - renderStart;
|
|
484
|
-
// Sticky/auto-follow
|
|
485
|
-
// selection by the same delta so the highlight
|
|
486
|
-
// TEXT (native terminal behavior — the
|
|
487
|
-
// as content scrolls, eventually
|
|
510
|
+
// Sticky/auto-follow or wheel-drain scrolled one or more ScrollBoxes
|
|
511
|
+
// this frame. Translate the selection by the same delta so the highlight
|
|
512
|
+
// stays anchored to the TEXT (native terminal behavior — the
|
|
513
|
+
// selection walks up the screen as content scrolls, eventually
|
|
514
|
+
// clipping at the top). frontFrame
|
|
488
515
|
// still holds the PREVIOUS frame's screen (swap is at ~500 below), so
|
|
489
516
|
// captureScrolledRows reads the rows that are about to scroll out
|
|
490
517
|
// before they're overwritten — the text stays copyable until the
|
|
@@ -492,16 +519,28 @@ export default class Ink {
|
|
|
492
519
|
// (screen-local) so only anchor shifts — selection grows toward the
|
|
493
520
|
// mouse as the anchor walks up. After release, both ends are text-
|
|
494
521
|
// anchored and move as a block.
|
|
495
|
-
const follow = consumeFollowScroll();
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
522
|
+
const follow = pickFollowForSelection(consumeFollowScroll(), this.selection.anchor?.row ?? null);
|
|
523
|
+
// pickFollowForSelection already checked anchor-in-viewport (that IS
|
|
524
|
+
// the "selection is on scrollbox content" guard — footer/prompt
|
|
525
|
+
// selections on static text match no viewport and follow nothing).
|
|
526
|
+
// Innermost-viewport wins attributes correctly when several boxes
|
|
527
|
+
// scrolled this frame (transcript draining while an overlay panel's
|
|
528
|
+
// box scrolls): panels render on top, so overlap-row selections
|
|
529
|
+
// belong to the panel, not the covered transcript.
|
|
530
|
+
if (follow && this.selection.anchor) {
|
|
504
531
|
const { delta, viewportTop, viewportBottom } = follow;
|
|
532
|
+
// Signed delta: >0 = content moved up (at-bottom follow or
|
|
533
|
+
// wheel-down drain); <0 = content moved down (wheel-up drain, #438).
|
|
534
|
+
// The capture window is the viewport-edge rows about to scroll out
|
|
535
|
+
// (top edge when content moves up, bottom edge when it moves down),
|
|
536
|
+
// and the shift re-anchors the endpoints by the same amount in the
|
|
537
|
+
// OPPOSITE direction so they track the text, not the screen.
|
|
538
|
+
const rows = Math.abs(delta);
|
|
539
|
+
const up = delta > 0;
|
|
540
|
+
const firstRow = up ? viewportTop : viewportBottom - rows + 1;
|
|
541
|
+
const lastRow = up ? viewportTop + rows - 1 : viewportBottom;
|
|
542
|
+
const side = up ? 'above' : 'below';
|
|
543
|
+
const shift = up ? -rows : rows;
|
|
505
544
|
// captureScrolledRows and shift* are a pair: capture grabs rows about
|
|
506
545
|
// to scroll off, shift moves the selection endpoint so the same rows
|
|
507
546
|
// won't intersect again next frame. Capturing without shifting leaves
|
|
@@ -511,9 +550,9 @@ export default class Ink {
|
|
|
511
550
|
// each shift branch so the pairing can't be broken by a new guard.
|
|
512
551
|
if (this.selection.isDragging) {
|
|
513
552
|
if (hasSelection(this.selection)) {
|
|
514
|
-
captureScrolledRows(this.selection, this.frontFrame.screen,
|
|
553
|
+
captureScrolledRows(this.selection, this.frontFrame.screen, firstRow, lastRow, side);
|
|
515
554
|
}
|
|
516
|
-
shiftAnchor(this.selection,
|
|
555
|
+
shiftAnchor(this.selection, shift, viewportTop, viewportBottom);
|
|
517
556
|
}
|
|
518
557
|
else if (
|
|
519
558
|
// Flag-3 guard: the anchor check above only proves ONE endpoint is
|
|
@@ -530,14 +569,16 @@ export default class Ink {
|
|
|
530
569
|
// is correct there even when focus is in the footer).
|
|
531
570
|
!this.selection.focus || this.selection.focus.row >= viewportTop && this.selection.focus.row <= viewportBottom) {
|
|
532
571
|
if (hasSelection(this.selection)) {
|
|
533
|
-
captureScrolledRows(this.selection, this.frontFrame.screen,
|
|
572
|
+
captureScrolledRows(this.selection, this.frontFrame.screen, firstRow, lastRow, side);
|
|
534
573
|
}
|
|
535
|
-
const cleared = shiftSelectionForFollow(this.selection,
|
|
536
|
-
// Auto-clear (both ends overshot
|
|
537
|
-
//
|
|
538
|
-
//
|
|
539
|
-
//
|
|
540
|
-
//
|
|
574
|
+
const cleared = shiftSelectionForFollow(this.selection, shift, viewportTop, viewportBottom);
|
|
575
|
+
// Auto-clear (both ends overshot an edge — off the top via
|
|
576
|
+
// follow/wheel-down, off the bottom via wheel-up) must notify
|
|
577
|
+
// React-land so useHasSelection re-renders and the footer
|
|
578
|
+
// copy/escape hint disappears. notifySelectionChange() would
|
|
579
|
+
// recurse into onRender; fire the listeners directly — they
|
|
580
|
+
// schedule a React update for LATER, they don't re-enter this
|
|
581
|
+
// frame.
|
|
541
582
|
if (cleared)
|
|
542
583
|
for (const cb of this.selectionListeners)
|
|
543
584
|
cb();
|
|
@@ -689,6 +730,12 @@ export default class Ink {
|
|
|
689
730
|
// and no move is emitted.
|
|
690
731
|
const decl = this.cursorDeclaration;
|
|
691
732
|
const rect = decl !== null ? nodeCache.get(decl.node) : undefined;
|
|
733
|
+
// Keep the declared target in the same full-frame coordinate system as
|
|
734
|
+
// frame.cursor and displayCursor. Main-screen cursor moves are relative:
|
|
735
|
+
// subtracting the scrollback height from target alone makes the physical
|
|
736
|
+
// cursor climb that height on every park/preamble cycle, so later streaming
|
|
737
|
+
// diffs overwrite thinking, tool, and assistant rows. The terminal maps the
|
|
738
|
+
// full-frame relative move onto its viewport/scrollback position itself.
|
|
692
739
|
const target = decl !== null && rect !== undefined ? {
|
|
693
740
|
x: rect.x + decl.relativeX,
|
|
694
741
|
y: rect.y + decl.relativeY
|
|
@@ -781,15 +828,14 @@ export default class Ink {
|
|
|
781
828
|
// trailing-edge throttle invocation, timerId is undefined, and lodash's
|
|
782
829
|
// debounce sees timeSinceLastCall >= wait (last call was at the start
|
|
783
830
|
// of this window) → leadingEdge fires IMMEDIATELY → double render ~0.1ms
|
|
784
|
-
// apart → jank. Use a plain timeout. If a wheel event
|
|
785
|
-
//
|
|
786
|
-
// the top of onRender — no double.
|
|
831
|
+
// apart → jank. Use a plain timeout. If a wheel event or immediate
|
|
832
|
+
// render arrives first, renderNow cancels this timer — no double.
|
|
787
833
|
//
|
|
788
834
|
// Drain frames are cheap (DECSTBM + ~10 patches, ~200 bytes) so run at
|
|
789
835
|
// quarter interval (~250fps, setTimeout practical floor) for max scroll
|
|
790
836
|
// speed. Regular renders stay at FRAME_INTERVAL_MS via the throttle.
|
|
791
837
|
if (frame.scrollDrainPending) {
|
|
792
|
-
this.drainTimer = setTimeout(
|
|
838
|
+
this.drainTimer = setTimeout(this.renderNow, FRAME_INTERVAL_MS >> 2);
|
|
793
839
|
}
|
|
794
840
|
const yogaMs = getLastYogaMs();
|
|
795
841
|
const commitMs = getLastCommitMs();
|
|
@@ -825,12 +871,12 @@ export default class Ink {
|
|
|
825
871
|
// Flush pending React updates and render before pausing.
|
|
826
872
|
// @ts-ignore -- ported CC build; type drift tolerated flushSyncFromReconciler exists in react-reconciler 0.31 but not in @types/react-reconciler
|
|
827
873
|
reconciler.flushSyncFromReconciler();
|
|
828
|
-
this.
|
|
874
|
+
this.renderNow();
|
|
829
875
|
this.isPaused = true;
|
|
830
876
|
}
|
|
831
877
|
resume() {
|
|
832
878
|
this.isPaused = false;
|
|
833
|
-
this.
|
|
879
|
+
this.renderNow();
|
|
834
880
|
}
|
|
835
881
|
/**
|
|
836
882
|
* Reset frame buffers so the next render writes the full screen from scratch.
|
|
@@ -872,7 +918,7 @@ export default class Ink {
|
|
|
872
918
|
// diff sees no content. onRender resets the flag at frame end.
|
|
873
919
|
this.prevFrameContaminated = true;
|
|
874
920
|
}
|
|
875
|
-
this.
|
|
921
|
+
this.renderNow();
|
|
876
922
|
}
|
|
877
923
|
/**
|
|
878
924
|
* Establish a genuinely fresh terminal page: clear both the visible screen
|
|
@@ -895,7 +941,7 @@ export default class Ink {
|
|
|
895
941
|
this.repaint();
|
|
896
942
|
this.prevFrameContaminated = true;
|
|
897
943
|
}
|
|
898
|
-
this.
|
|
944
|
+
this.renderNow();
|
|
899
945
|
}
|
|
900
946
|
/**
|
|
901
947
|
* Mark the previous frame as untrustworthy for blit, forcing the next
|
|
@@ -1004,7 +1050,7 @@ export default class Ink {
|
|
|
1004
1050
|
// cursor position. Idempotent when nothing drifted — the user sees
|
|
1005
1051
|
// no change, at O(viewport) bytes once per >5s idle gap.
|
|
1006
1052
|
this.log.requestViewportReanchor();
|
|
1007
|
-
this.
|
|
1053
|
+
this.renderNow();
|
|
1008
1054
|
return;
|
|
1009
1055
|
}
|
|
1010
1056
|
// Mouse tracking — idempotent, safe to re-assert on every stdin gap.
|
|
@@ -1171,7 +1217,7 @@ export default class Ink {
|
|
|
1171
1217
|
// drops it silently unless allow-passthrough is on — no regression).
|
|
1172
1218
|
void setClipboard(text).then(raw => {
|
|
1173
1219
|
if (raw)
|
|
1174
|
-
this.
|
|
1220
|
+
this.writeRaw(raw);
|
|
1175
1221
|
});
|
|
1176
1222
|
}
|
|
1177
1223
|
return text;
|
|
@@ -1388,7 +1434,7 @@ export default class Ink {
|
|
|
1388
1434
|
return () => this.selectionListeners.delete(cb);
|
|
1389
1435
|
}
|
|
1390
1436
|
notifySelectionChange() {
|
|
1391
|
-
this.
|
|
1437
|
+
this.renderNow();
|
|
1392
1438
|
for (const cb of this.selectionListeners)
|
|
1393
1439
|
cb();
|
|
1394
1440
|
}
|
|
@@ -1596,7 +1642,7 @@ export default class Ink {
|
|
|
1596
1642
|
if (this.isUnmounted) {
|
|
1597
1643
|
return;
|
|
1598
1644
|
}
|
|
1599
|
-
this.
|
|
1645
|
+
this.renderNow();
|
|
1600
1646
|
this.unsubscribeExit();
|
|
1601
1647
|
if (typeof this.restoreConsole === 'function') {
|
|
1602
1648
|
this.restoreConsole();
|
|
@@ -47,10 +47,13 @@ export type FollowScroll = {
|
|
|
47
47
|
viewportBottom: number;
|
|
48
48
|
};
|
|
49
49
|
/**
|
|
50
|
-
* Read and clear the follow-scroll
|
|
51
|
-
*
|
|
50
|
+
* Read and clear the follow-scroll events recorded this frame. At most one
|
|
51
|
+
* per ScrollBox (a box that follows AND drains reports only the follow —
|
|
52
|
+
* the follow branch clears pendingScrollDelta before the drain runs);
|
|
53
|
+
* several boxes may each report when they scroll in the same frame.
|
|
54
|
+
* @returns this frame's follow-scroll events; empty when none.
|
|
52
55
|
*/
|
|
53
|
-
export declare function consumeFollowScroll(): FollowScroll
|
|
56
|
+
export declare function consumeFollowScroll(): FollowScroll[];
|
|
54
57
|
/**
|
|
55
58
|
* Build a mapping from each character position in the plain text to its segment index.
|
|
56
59
|
* Returns an array where charToSegment[i] is the segment index for character i.
|
|
@@ -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;AAK1C,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;AAWvE,qFAAqF;AACrF,wBAAgB,eAAe,IAAI,IAAI,CAItC;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;
|
|
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;AAK1C,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;AAWvE,qFAAqF;AACrF,wBAAgB,eAAe,IAAI,IAAI,CAItC;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,CAo4BN;AAyOD,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,CAAA;AAE1D,eAAe,kBAAkB,CAAA"}
|
|
@@ -71,14 +71,17 @@ export function resetScrollDrainNode() {
|
|
|
71
71
|
export function getScrollDrainNode() {
|
|
72
72
|
return scrollDrainNode;
|
|
73
73
|
}
|
|
74
|
-
let
|
|
74
|
+
let followScrolls = [];
|
|
75
75
|
/**
|
|
76
|
-
* Read and clear the follow-scroll
|
|
77
|
-
*
|
|
76
|
+
* Read and clear the follow-scroll events recorded this frame. At most one
|
|
77
|
+
* per ScrollBox (a box that follows AND drains reports only the follow —
|
|
78
|
+
* the follow branch clears pendingScrollDelta before the drain runs);
|
|
79
|
+
* several boxes may each report when they scroll in the same frame.
|
|
80
|
+
* @returns this frame's follow-scroll events; empty when none.
|
|
78
81
|
*/
|
|
79
82
|
export function consumeFollowScroll() {
|
|
80
|
-
const f =
|
|
81
|
-
|
|
83
|
+
const f = followScrolls;
|
|
84
|
+
followScrolls = [];
|
|
82
85
|
return f;
|
|
83
86
|
}
|
|
84
87
|
// ── Native terminal drain (iTerm2/Ghostty/etc. — proportional events) ──
|
|
@@ -684,11 +687,11 @@ function renderNodeToOutput(node, output, options) {
|
|
|
684
687
|
const followDelta = (node.scrollTop ?? 0) - scrollTopBeforeFollow;
|
|
685
688
|
if (followDelta > 0) {
|
|
686
689
|
const vpTop = node.scrollViewportTop ?? 0;
|
|
687
|
-
|
|
690
|
+
followScrolls.push({
|
|
688
691
|
delta: followDelta,
|
|
689
692
|
viewportTop: vpTop,
|
|
690
693
|
viewportBottom: vpTop + innerHeight - 1,
|
|
691
|
-
};
|
|
694
|
+
});
|
|
692
695
|
}
|
|
693
696
|
// Drain pendingScrollDelta. Native terminals (proportional burst
|
|
694
697
|
// events) use proportional drain; xterm.js (VS Code, sparse events +
|
|
@@ -749,6 +752,29 @@ function renderNodeToOutput(node, output, options) {
|
|
|
749
752
|
node.pendingScrollDelta = undefined;
|
|
750
753
|
if (node.pendingScrollDelta !== undefined)
|
|
751
754
|
scrollDrainNode = node;
|
|
755
|
+
// Wheel-drain selection translate (#438): the drain moved content
|
|
756
|
+
// by (scrollTop - scrollTopBeforeFollow) rows this frame, minus
|
|
757
|
+
// what at-bottom follow already reported above (followDelta is 0
|
|
758
|
+
// unless the follow branch fired — and when it did, it cleared
|
|
759
|
+
// pendingScrollDelta, so the drain contributed nothing). Record
|
|
760
|
+
// the remainder as a follow-scroll event with a SIGNED delta so
|
|
761
|
+
// ink.tsx re-anchors any active selection to the text. Without
|
|
762
|
+
// this, wheel scrolling leaves the highlight pinned to screen
|
|
763
|
+
// rows and copy-on-select grabs whatever scrolled under it.
|
|
764
|
+
// scrollTo/scrollToElement jumps never contribute: they write
|
|
765
|
+
// scrollTop before scrollTopBeforeFollow is captured. Multi-frame
|
|
766
|
+
// drains record per-frame portions; the selection's virtual-row
|
|
767
|
+
// tracking accumulates the clamp overshoot across frames. Multiple
|
|
768
|
+
// boxes may each record; ink.tsx attributes by viewport containment.
|
|
769
|
+
const wheelDelta = scrollTop - scrollTopBeforeFollow - followDelta;
|
|
770
|
+
if (wheelDelta !== 0) {
|
|
771
|
+
const wheelVpTop = node.scrollViewportTop ?? 0;
|
|
772
|
+
followScrolls.push({
|
|
773
|
+
delta: wheelDelta,
|
|
774
|
+
viewportTop: wheelVpTop,
|
|
775
|
+
viewportBottom: wheelVpTop + innerHeight - 1
|
|
776
|
+
});
|
|
777
|
+
}
|
|
752
778
|
// A manual scroll that lands exactly on the bottom re-pins sticky
|
|
753
779
|
// IMMEDIATELY on this frame — the follow-block restore above only
|
|
754
780
|
// fires when a later frame happens, but an idle stream (turn done,
|
|
@@ -213,10 +213,12 @@ export declare function shiftAnchor(s: SelectionState, dRow: number, minRow: num
|
|
|
213
213
|
* the selection is text-anchored at both ends — both must move. The
|
|
214
214
|
* isDragging check in ink.tsx picks which shift to apply.
|
|
215
215
|
*
|
|
216
|
-
* If both ends would shift strictly BELOW minRow
|
|
217
|
-
* text has scrolled entirely off the
|
|
218
|
-
*
|
|
219
|
-
*
|
|
216
|
+
* If both ends would shift strictly BELOW minRow or strictly ABOVE maxRow
|
|
217
|
+
* (unclamped), the selected text has scrolled entirely off the viewport —
|
|
218
|
+
* off the top via streaming follow / wheel-down, off the bottom via
|
|
219
|
+
* wheel-up. Clear it — otherwise a single inverted cell lingers at the
|
|
220
|
+
* edge as a ghost (native terminals drop the selection when it leaves
|
|
221
|
+
* scrollback). Landing AT the edge row is
|
|
220
222
|
* still valid: that cell holds the correct text. Returns true if the
|
|
221
223
|
* selection was cleared so the caller can notify React-land subscribers
|
|
222
224
|
* (useHasSelection) — the caller is inside onRender so it can't use
|
|
@@ -229,6 +231,29 @@ export declare function shiftAnchor(s: SelectionState, dRow: number, minRow: num
|
|
|
229
231
|
* entirely off the top, false otherwise.
|
|
230
232
|
*/
|
|
231
233
|
export declare function shiftSelectionForFollow(s: SelectionState, dRow: number, minRow: number, maxRow: number): boolean;
|
|
234
|
+
/** A scroll event reported by a ScrollBox this frame (follow or wheel
|
|
235
|
+
* drain): signed delta plus the box's viewport bounds. Structurally
|
|
236
|
+
* identical to FollowScroll in render-node-to-output. */
|
|
237
|
+
export type ScrollEvent = {
|
|
238
|
+
delta: number;
|
|
239
|
+
viewportTop: number;
|
|
240
|
+
viewportBottom: number;
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* Pick which scroll event a selection belongs to when several ScrollBoxes
|
|
244
|
+
* scrolled in the same frame (e.g., the transcript still draining a wheel
|
|
245
|
+
* burst while an overlay panel's box scrolls). The selection follows the
|
|
246
|
+
* INNERMOST viewport containing the anchor: overlay panels render on top
|
|
247
|
+
* of the transcript, so a selection inside the overlap rows belongs to
|
|
248
|
+
* the panel, not the covered transcript beneath it. A selection outside
|
|
249
|
+
* every viewport (footer/prompt, static text) follows nothing — scrolling
|
|
250
|
+
* doesn't move the content under it.
|
|
251
|
+
* @param events - this frame's scroll events (signed deltas).
|
|
252
|
+
* @param anchorRow - the selection anchor's screen row, or null when no
|
|
253
|
+
* selection is active.
|
|
254
|
+
* @returns the event the selection should be translated by, or null.
|
|
255
|
+
*/
|
|
256
|
+
export declare function pickFollowForSelection(events: ScrollEvent[], anchorRow: number | null): ScrollEvent | null;
|
|
232
257
|
/**
|
|
233
258
|
* True when a selection is active, meaning both anchor and focus are set.
|
|
234
259
|
* @param s - the selection state to inspect.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../../src/ink/selection.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../../src/ink/selection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAGpD,KAAK,KAAK,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAEzC;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,6DAA6D;IAC7D,MAAM,EAAE,KAAK,GAAG,IAAI,CAAA;IACpB,oEAAoE;IACpE,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB,4CAA4C;IAC5C,UAAU,EAAE,OAAO,CAAA;IACnB;;;;4EAIwE;IACxE,UAAU,EAAE;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,EAAE,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAClE;;;;0EAIsE;IACtE,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,qEAAqE;IACrE,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B;;;;;iBAKa;IACb,kBAAkB,EAAE,OAAO,EAAE,CAAA;IAC7B,oCAAoC;IACpC,kBAAkB,EAAE,OAAO,EAAE,CAAA;IAC7B;;;;uEAImE;IACnE,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,sBAAsB;IACtB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB;;;;gFAI4E;IAC5E,eAAe,EAAE,OAAO,CAAA;CACzB,CAAA;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,cAAc,CAYrD;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,CAAC,EAAE,cAAc,EACjB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,IAAI,CAeN;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,CAAC,EAAE,cAAc,EACjB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,IAAI,CAUN;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,cAAc,GAAG,IAAI,CAIvD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,cAAc,GAAG,IAAI,CAYtD;AAmGD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,CAAC,EAAE,cAAc,EACjB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,IAAI,CASN;AAYD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAmFpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,CAAC,EAAE,cAAc,EACjB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,GACV,IAAI,CAQN;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,CAAC,EAAE,cAAc,EACjB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,IAAI,CA2BN;AAED;gDACgD;AAChD,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,OAAO,GACP,IAAI,GACJ,MAAM,GACN,WAAW,GACX,SAAS,CAAA;AAEb;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAQ3E;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,cAAc,CAC5B,CAAC,EAAE,cAAc,EACjB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,IAAI,CAyFN;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,CAAC,EAAE,cAAc,EACjB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,IAAI,CAwBN;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,uBAAuB,CACrC,CAAC,EAAE,cAAc,EACjB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAkDT;AAED;;0DAE0D;AAC1D,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;CACvB,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,WAAW,EAAE,EACrB,SAAS,EAAE,MAAM,GAAG,IAAI,GACvB,WAAW,GAAG,IAAI,CAapB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,cAAc,GAAG,OAAO,CAEvD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,cAAc,GAAG;IAClD,KAAK,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IACnC,GAAG,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAClC,GAAG,IAAI,CAKP;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,CAAC,EAAE,cAAc,EACjB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,OAAO,CAQT;AAoDD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAsBzE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CACjC,CAAC,EAAE,cAAc,EACjB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,OAAO,GAAG,OAAO,GACtB,IAAI,CAwDN;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,cAAc,EACzB,SAAS,EAAE,SAAS,GACnB,IAAI,CAoBN"}
|
|
@@ -579,10 +579,12 @@ export function shiftAnchor(s, dRow, minRow, maxRow) {
|
|
|
579
579
|
* the selection is text-anchored at both ends — both must move. The
|
|
580
580
|
* isDragging check in ink.tsx picks which shift to apply.
|
|
581
581
|
*
|
|
582
|
-
* If both ends would shift strictly BELOW minRow
|
|
583
|
-
* text has scrolled entirely off the
|
|
584
|
-
*
|
|
585
|
-
*
|
|
582
|
+
* If both ends would shift strictly BELOW minRow or strictly ABOVE maxRow
|
|
583
|
+
* (unclamped), the selected text has scrolled entirely off the viewport —
|
|
584
|
+
* off the top via streaming follow / wheel-down, off the bottom via
|
|
585
|
+
* wheel-up. Clear it — otherwise a single inverted cell lingers at the
|
|
586
|
+
* edge as a ghost (native terminals drop the selection when it leaves
|
|
587
|
+
* scrollback). Landing AT the edge row is
|
|
586
588
|
* still valid: that cell holds the correct text. Returns true if the
|
|
587
589
|
* selection was cleared so the caller can notify React-land subscribers
|
|
588
590
|
* (useHasSelection) — the caller is inside onRender so it can't use
|
|
@@ -609,7 +611,11 @@ export function shiftSelectionForFollow(s, dRow, minRow, maxRow) {
|
|
|
609
611
|
const rawFocus = s.focus
|
|
610
612
|
? (s.virtualFocusRow ?? s.focus.row) + dRow
|
|
611
613
|
: undefined;
|
|
612
|
-
|
|
614
|
+
// Both ends strictly past the same edge = selection fully scrolled off
|
|
615
|
+
// (top: follow/wheel-down; bottom: wheel-up). Symmetric clear — a
|
|
616
|
+
// clamped-to-edge pair would render as a 1-row ghost highlight.
|
|
617
|
+
if ((rawAnchor < minRow && rawFocus !== undefined && rawFocus < minRow) ||
|
|
618
|
+
(rawAnchor > maxRow && rawFocus !== undefined && rawFocus > maxRow)) {
|
|
613
619
|
clearSelection(s);
|
|
614
620
|
return true;
|
|
615
621
|
}
|
|
@@ -640,6 +646,36 @@ export function shiftSelectionForFollow(s, dRow, minRow, maxRow) {
|
|
|
640
646
|
}
|
|
641
647
|
return false;
|
|
642
648
|
}
|
|
649
|
+
/**
|
|
650
|
+
* Pick which scroll event a selection belongs to when several ScrollBoxes
|
|
651
|
+
* scrolled in the same frame (e.g., the transcript still draining a wheel
|
|
652
|
+
* burst while an overlay panel's box scrolls). The selection follows the
|
|
653
|
+
* INNERMOST viewport containing the anchor: overlay panels render on top
|
|
654
|
+
* of the transcript, so a selection inside the overlap rows belongs to
|
|
655
|
+
* the panel, not the covered transcript beneath it. A selection outside
|
|
656
|
+
* every viewport (footer/prompt, static text) follows nothing — scrolling
|
|
657
|
+
* doesn't move the content under it.
|
|
658
|
+
* @param events - this frame's scroll events (signed deltas).
|
|
659
|
+
* @param anchorRow - the selection anchor's screen row, or null when no
|
|
660
|
+
* selection is active.
|
|
661
|
+
* @returns the event the selection should be translated by, or null.
|
|
662
|
+
*/
|
|
663
|
+
export function pickFollowForSelection(events, anchorRow) {
|
|
664
|
+
if (anchorRow === null)
|
|
665
|
+
return null;
|
|
666
|
+
let best = null;
|
|
667
|
+
let bestHeight = Infinity;
|
|
668
|
+
for (const e of events) {
|
|
669
|
+
if (anchorRow < e.viewportTop || anchorRow > e.viewportBottom)
|
|
670
|
+
continue;
|
|
671
|
+
const height = e.viewportBottom - e.viewportTop;
|
|
672
|
+
if (best === null || height < bestHeight) {
|
|
673
|
+
best = e;
|
|
674
|
+
bestHeight = height;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
return best;
|
|
678
|
+
}
|
|
643
679
|
/**
|
|
644
680
|
* True when a selection is active, meaning both anchor and focus are set.
|
|
645
681
|
* @param s - the selection state to inspect.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal mode: one process-wide flag gating decorative UI (header splash,
|
|
3
|
+
* emoji status glyphs, non-tool text colors, status-bar extras). The flag is
|
|
4
|
+
* owned by the channel (settings `dsh-tui.minimal`, applied live through
|
|
5
|
+
* scope.watch); leaf components read it here instead of threading a prop
|
|
6
|
+
* through four render layers. Code highlighting and tool-name colors are
|
|
7
|
+
* deliberately NOT gated — minimal keeps those.
|
|
8
|
+
*/
|
|
9
|
+
/** True when minimal mode is active (settings `dsh-tui.minimal`). */
|
|
10
|
+
export declare function isMinimalMode(): boolean;
|
|
11
|
+
/** @internal channel-owned setter; also called once at boot. */
|
|
12
|
+
export declare function setMinimalMode(enabled: boolean): void;
|
|
13
|
+
//# sourceMappingURL=minimalMode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"minimalMode.d.ts","sourceRoot":"","sources":["../../src/minimalMode.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,qEAAqE;AACrE,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED,gEAAgE;AAChE,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAErD"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal mode: one process-wide flag gating decorative UI (header splash,
|
|
3
|
+
* emoji status glyphs, non-tool text colors, status-bar extras). The flag is
|
|
4
|
+
* owned by the channel (settings `dsh-tui.minimal`, applied live through
|
|
5
|
+
* scope.watch); leaf components read it here instead of threading a prop
|
|
6
|
+
* through four render layers. Code highlighting and tool-name colors are
|
|
7
|
+
* deliberately NOT gated — minimal keeps those.
|
|
8
|
+
*/
|
|
9
|
+
let minimal = false;
|
|
10
|
+
/** True when minimal mode is active (settings `dsh-tui.minimal`). */
|
|
11
|
+
export function isMinimalMode() {
|
|
12
|
+
return minimal;
|
|
13
|
+
}
|
|
14
|
+
/** @internal channel-owned setter; also called once at boot. */
|
|
15
|
+
export function setMinimalMode(enabled) {
|
|
16
|
+
minimal = enabled;
|
|
17
|
+
}
|
|
@@ -44,5 +44,5 @@ export declare function Chat({ channel, questionStore, approvalStore, extensionD
|
|
|
44
44
|
* persisted flag when the host does not supply one.
|
|
45
45
|
*/
|
|
46
46
|
trajectorySeen?: boolean;
|
|
47
|
-
}): React.JSX.Element;
|
|
47
|
+
}): React.JSX.Element | null;
|
|
48
48
|
//# sourceMappingURL=Chat.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/screens/Chat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,OAAO,EAAqB,KAAK,OAAO,EAAsE,MAAM,2BAA2B,CAAA;AAC/I,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAGlE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/screens/Chat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,OAAO,EAAqB,KAAK,OAAO,EAAsE,MAAM,2BAA2B,CAAA;AAC/I,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAGlE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AA4I3D,wBAAgB,IAAI,CAAC,EACnB,OAAO,EACP,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,MAAM,EACN,QAAQ,EACR,UAAkB,EAClB,cAAc,EAAE,kBAAkB,GACnC,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,EAAE,aAAa,CAAA;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,cAAc,CAAA;IACjC,oEAAoE;IACpE,eAAe,CAAC,EAAE,cAAc,CAAA;IAChC,iDAAiD;IACjD,kBAAkB,CAAC,EAAE,eAAe,CAAA;IACpC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,4BAisEA"}
|