@deepseek-harness-tui/dsh-tui 0.8.6 → 0.8.7
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 +13 -3
- 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/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 +3 -1
- package/lib/types/components/LogoV2.d.ts.map +1 -1
- package/lib/types/components/LogoV2.js +2 -2
- 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 +75 -15
- 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 +53 -2
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +294 -7
- package/lib/types/dsh-adapter/contract.d.ts +27 -4
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +56 -10
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +9 -3
- 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 +215 -31
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +65 -14
- 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 +46 -15
- 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.js +3 -3
- 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/package.json +64 -58
package/lib/types/ink/ink.js
CHANGED
|
@@ -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;
|
|
@@ -689,6 +715,12 @@ export default class Ink {
|
|
|
689
715
|
// and no move is emitted.
|
|
690
716
|
const decl = this.cursorDeclaration;
|
|
691
717
|
const rect = decl !== null ? nodeCache.get(decl.node) : undefined;
|
|
718
|
+
// Keep the declared target in the same full-frame coordinate system as
|
|
719
|
+
// frame.cursor and displayCursor. Main-screen cursor moves are relative:
|
|
720
|
+
// subtracting the scrollback height from target alone makes the physical
|
|
721
|
+
// cursor climb that height on every park/preamble cycle, so later streaming
|
|
722
|
+
// diffs overwrite thinking, tool, and assistant rows. The terminal maps the
|
|
723
|
+
// full-frame relative move onto its viewport/scrollback position itself.
|
|
692
724
|
const target = decl !== null && rect !== undefined ? {
|
|
693
725
|
x: rect.x + decl.relativeX,
|
|
694
726
|
y: rect.y + decl.relativeY
|
|
@@ -781,15 +813,14 @@ export default class Ink {
|
|
|
781
813
|
// trailing-edge throttle invocation, timerId is undefined, and lodash's
|
|
782
814
|
// debounce sees timeSinceLastCall >= wait (last call was at the start
|
|
783
815
|
// 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.
|
|
816
|
+
// apart → jank. Use a plain timeout. If a wheel event or immediate
|
|
817
|
+
// render arrives first, renderNow cancels this timer — no double.
|
|
787
818
|
//
|
|
788
819
|
// Drain frames are cheap (DECSTBM + ~10 patches, ~200 bytes) so run at
|
|
789
820
|
// quarter interval (~250fps, setTimeout practical floor) for max scroll
|
|
790
821
|
// speed. Regular renders stay at FRAME_INTERVAL_MS via the throttle.
|
|
791
822
|
if (frame.scrollDrainPending) {
|
|
792
|
-
this.drainTimer = setTimeout(
|
|
823
|
+
this.drainTimer = setTimeout(this.renderNow, FRAME_INTERVAL_MS >> 2);
|
|
793
824
|
}
|
|
794
825
|
const yogaMs = getLastYogaMs();
|
|
795
826
|
const commitMs = getLastCommitMs();
|
|
@@ -825,12 +856,12 @@ export default class Ink {
|
|
|
825
856
|
// Flush pending React updates and render before pausing.
|
|
826
857
|
// @ts-ignore -- ported CC build; type drift tolerated flushSyncFromReconciler exists in react-reconciler 0.31 but not in @types/react-reconciler
|
|
827
858
|
reconciler.flushSyncFromReconciler();
|
|
828
|
-
this.
|
|
859
|
+
this.renderNow();
|
|
829
860
|
this.isPaused = true;
|
|
830
861
|
}
|
|
831
862
|
resume() {
|
|
832
863
|
this.isPaused = false;
|
|
833
|
-
this.
|
|
864
|
+
this.renderNow();
|
|
834
865
|
}
|
|
835
866
|
/**
|
|
836
867
|
* Reset frame buffers so the next render writes the full screen from scratch.
|
|
@@ -872,7 +903,7 @@ export default class Ink {
|
|
|
872
903
|
// diff sees no content. onRender resets the flag at frame end.
|
|
873
904
|
this.prevFrameContaminated = true;
|
|
874
905
|
}
|
|
875
|
-
this.
|
|
906
|
+
this.renderNow();
|
|
876
907
|
}
|
|
877
908
|
/**
|
|
878
909
|
* Establish a genuinely fresh terminal page: clear both the visible screen
|
|
@@ -895,7 +926,7 @@ export default class Ink {
|
|
|
895
926
|
this.repaint();
|
|
896
927
|
this.prevFrameContaminated = true;
|
|
897
928
|
}
|
|
898
|
-
this.
|
|
929
|
+
this.renderNow();
|
|
899
930
|
}
|
|
900
931
|
/**
|
|
901
932
|
* Mark the previous frame as untrustworthy for blit, forcing the next
|
|
@@ -1004,7 +1035,7 @@ export default class Ink {
|
|
|
1004
1035
|
// cursor position. Idempotent when nothing drifted — the user sees
|
|
1005
1036
|
// no change, at O(viewport) bytes once per >5s idle gap.
|
|
1006
1037
|
this.log.requestViewportReanchor();
|
|
1007
|
-
this.
|
|
1038
|
+
this.renderNow();
|
|
1008
1039
|
return;
|
|
1009
1040
|
}
|
|
1010
1041
|
// Mouse tracking — idempotent, safe to re-assert on every stdin gap.
|
|
@@ -1171,7 +1202,7 @@ export default class Ink {
|
|
|
1171
1202
|
// drops it silently unless allow-passthrough is on — no regression).
|
|
1172
1203
|
void setClipboard(text).then(raw => {
|
|
1173
1204
|
if (raw)
|
|
1174
|
-
this.
|
|
1205
|
+
this.writeRaw(raw);
|
|
1175
1206
|
});
|
|
1176
1207
|
}
|
|
1177
1208
|
return text;
|
|
@@ -1388,7 +1419,7 @@ export default class Ink {
|
|
|
1388
1419
|
return () => this.selectionListeners.delete(cb);
|
|
1389
1420
|
}
|
|
1390
1421
|
notifySelectionChange() {
|
|
1391
|
-
this.
|
|
1422
|
+
this.renderNow();
|
|
1392
1423
|
for (const cb of this.selectionListeners)
|
|
1393
1424
|
cb();
|
|
1394
1425
|
}
|
|
@@ -1596,7 +1627,7 @@ export default class Ink {
|
|
|
1596
1627
|
if (this.isUnmounted) {
|
|
1597
1628
|
return;
|
|
1598
1629
|
}
|
|
1599
|
-
this.
|
|
1630
|
+
this.renderNow();
|
|
1600
1631
|
this.unsubscribeExit();
|
|
1601
1632
|
if (typeof this.restoreConsole === 'function') {
|
|
1602
1633
|
this.restoreConsole();
|
|
@@ -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"}
|
|
@@ -47,7 +47,10 @@ import { HistorySearchDialog } from '../components/HistorySearchDialog.js';
|
|
|
47
47
|
import { RewindPicker } from '../components/RewindPicker.js';
|
|
48
48
|
import { BtwPanel } from '../components/BtwPanel.js';
|
|
49
49
|
import { TipsPanel } from '../components/TipsPanel.js';
|
|
50
|
+
import { SubagentDashboard } from '../components/SubagentDashboard.js';
|
|
51
|
+
import { SubagentDetailScene } from '../components/SubagentDetailScene.js';
|
|
50
52
|
import { setClipboard } from '../ink/termio/osc.js';
|
|
53
|
+
import { TerminalWriteContext } from '../ink/useTerminalNotification.js';
|
|
51
54
|
import instances from '../ink/instances.js';
|
|
52
55
|
import { useAnimationFrame } from '../ink/hooks/use-animation-frame.js';
|
|
53
56
|
import { TrajectoryScene } from './TrajectoryScene.js';
|
|
@@ -135,6 +138,7 @@ let fallbackApprovalStore;
|
|
|
135
138
|
let fallbackDialogStore;
|
|
136
139
|
let fallbackStatusStore;
|
|
137
140
|
export function Chat({ channel, questionStore, approvalStore, extensionDialogs, extensionStatus, extensionShortcuts, onExit, onUpdate, fullscreen = false, trajectorySeen: trajectorySeenProp, }) {
|
|
141
|
+
const writeRaw = React.useContext(TerminalWriteContext);
|
|
138
142
|
// Re-render whenever the channel mutates; rows/status are read fresh below.
|
|
139
143
|
React.useSyncExternalStore(channel.subscribe, () => channel.version);
|
|
140
144
|
// Re-render on language switches so the whole UI hot-swaps its strings.
|
|
@@ -230,8 +234,6 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
230
234
|
const [thinkingVisible, setThinkingVisible] = React.useState(true);
|
|
231
235
|
const [thinkingOpen, setThinkingOpen] = React.useState(false);
|
|
232
236
|
const [thinkingFocus, setThinkingFocus] = React.useState(0);
|
|
233
|
-
/** Mid-conversation toggle waiting for Enter confirmation (CC semantics). */
|
|
234
|
-
const [thinkingConfirm, setThinkingConfirm] = React.useState(null);
|
|
235
237
|
/** ctrl+r history search dialog (ported from CC's HistorySearchDialog). */
|
|
236
238
|
const [historyOpen, setHistoryOpen] = React.useState(false);
|
|
237
239
|
const [historyQuery, setHistoryQuery] = React.useState('');
|
|
@@ -265,6 +267,15 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
265
267
|
};
|
|
266
268
|
/** /tips usage-tips overlay: pure UI state, no session side effects. */
|
|
267
269
|
const [tipsOpen, setTipsOpen] = React.useState(false);
|
|
270
|
+
/** Subagent dashboard (Ctrl+A): displays active/completed subagents. */
|
|
271
|
+
const [subagentDashboardOpen, setSubagentDashboardOpen] = React.useState(false);
|
|
272
|
+
/** Detail view for a specific subagent (opened from dashboard). */
|
|
273
|
+
const [subagentDetailId, setSubagentDetailId] = React.useState(null);
|
|
274
|
+
/**
|
|
275
|
+
* Hidden `/deepseek` easter egg: each invocation bumps this key so the
|
|
276
|
+
* logo header remounts and replays the whale spout + text shimmer.
|
|
277
|
+
*/
|
|
278
|
+
const [logoNonce, setLogoNonce] = React.useState(0);
|
|
268
279
|
React.useEffect(() => () => btwAbortRef.current?.abort(), []);
|
|
269
280
|
/**
|
|
270
281
|
* The trajectory scene (issue #80 evolution). Unlike every other overlay
|
|
@@ -1063,6 +1074,18 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
1063
1074
|
});
|
|
1064
1075
|
return true;
|
|
1065
1076
|
}
|
|
1077
|
+
case 'deepseek': {
|
|
1078
|
+
// Hidden easter egg: replay the logo header's whale spout + text
|
|
1079
|
+
// shimmer. The command is intentionally not in the suggestion/help
|
|
1080
|
+
// catalogs; PromptInput recognizes it through HIDDEN_COMMAND_NAMES.
|
|
1081
|
+
setHelpOpen(false);
|
|
1082
|
+
setLogoNonce(n => n + 1);
|
|
1083
|
+
// Bring the logo back into view if the transcript has scrolled.
|
|
1084
|
+
setTimeout(() => {
|
|
1085
|
+
handle?.scrollTo(0);
|
|
1086
|
+
}, 0);
|
|
1087
|
+
return true;
|
|
1088
|
+
}
|
|
1066
1089
|
case 'tips':
|
|
1067
1090
|
setHelpOpen(false);
|
|
1068
1091
|
setTipsOpen(true);
|
|
@@ -1353,12 +1376,35 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
1353
1376
|
// the field draft editor belong to it alone.
|
|
1354
1377
|
if (settingsOpen)
|
|
1355
1378
|
return;
|
|
1379
|
+
// Subagent dashboard or detail scene: it owns the keyboard while open.
|
|
1380
|
+
if (subagentDashboardOpen || subagentDetailId !== null)
|
|
1381
|
+
return;
|
|
1356
1382
|
// A plugin scene (dsh-tui-scenes) or the trajectory scene owns the whole
|
|
1357
1383
|
// screen while open: every key belongs to it. Unguarded, an Esc meant to
|
|
1358
1384
|
// CLOSE the scene also reached the chat:cancel branch below whenever a
|
|
1359
1385
|
// turn was in flight — closing the view and killing the turn in one key.
|
|
1360
1386
|
if (sceneOpen || channel.pluginScene !== undefined)
|
|
1361
1387
|
return;
|
|
1388
|
+
// Mouse wheel scrolls the transcript even while a question/approval/
|
|
1389
|
+
// dialog panel is open — those panels own arrow/Enter/Esc keys, but the
|
|
1390
|
+
// transcript above them should still be scrollable in fullscreen mode.
|
|
1391
|
+
// Help is different: its own ScrollBox owns wheel input, so Chat must
|
|
1392
|
+
// yield before stopping propagation or both covered layers would move.
|
|
1393
|
+
// Events only arrive with mouse tracking on; inline mode never sees
|
|
1394
|
+
// them, so this is a no-op there.
|
|
1395
|
+
if (key.wheelUp || key.wheelDown) {
|
|
1396
|
+
if (helpOpen)
|
|
1397
|
+
return;
|
|
1398
|
+
handle?.scrollBy(key.wheelUp ? -3 : 3);
|
|
1399
|
+
event.stopImmediatePropagation();
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
// Help is modal over Chat. Chat's listener registers before PromptInput's,
|
|
1403
|
+
// so yield every remaining key before any global/custom shortcut, search,
|
|
1404
|
+
// selection, or working-turn cancellation branch can mutate hidden state.
|
|
1405
|
+
// PromptInput then owns Esc, navigation, Tab guards, and ordinary typing.
|
|
1406
|
+
if (helpOpen)
|
|
1407
|
+
return;
|
|
1362
1408
|
// The questionnaire / approval panel / managed plugin dialog owns the
|
|
1363
1409
|
// keyboard while one is pending (the panel's own useInput handles
|
|
1364
1410
|
// ↑/↓/Space/Tab/Enter/Esc; the prompt input is unmounted, so nothing
|
|
@@ -1370,16 +1416,6 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
1370
1416
|
const plainReturn = returnCandidate && returnNow - lastModalEnterAtRef.current >= 80;
|
|
1371
1417
|
if (plainReturn)
|
|
1372
1418
|
lastModalEnterAtRef.current = returnNow;
|
|
1373
|
-
// Mouse wheel scrolls the transcript — in fullscreen there is no
|
|
1374
|
-
// terminal scrollback (alt-screen), so this is the only way back.
|
|
1375
|
-
// Imperative scrollBy: no React re-render per notch (CC semantics).
|
|
1376
|
-
// Events only arrive with mouse tracking on; inline mode never sees
|
|
1377
|
-
// them, so this is a no-op there.
|
|
1378
|
-
if (key.wheelUp || key.wheelDown) {
|
|
1379
|
-
handle?.scrollBy(key.wheelUp ? -3 : 3);
|
|
1380
|
-
event.stopImmediatePropagation();
|
|
1381
|
-
return;
|
|
1382
|
-
}
|
|
1383
1419
|
// Esc clears a settled mouse selection first (CC precedence), ahead of
|
|
1384
1420
|
// every other Esc meaning below (close pickers, interrupt the turn).
|
|
1385
1421
|
// hasSelection() is an imperative read — no subscription needed.
|
|
@@ -1447,33 +1483,14 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
1447
1483
|
return;
|
|
1448
1484
|
}
|
|
1449
1485
|
if (thinkingOpen) {
|
|
1450
|
-
if (
|
|
1451
|
-
// Confirmation state: Enter applies, Esc backs out to the select.
|
|
1452
|
-
if (plainReturn) {
|
|
1453
|
-
const enabled = thinkingConfirm;
|
|
1454
|
-
setThinkingVisible(enabled);
|
|
1455
|
-
setThinkingConfirm(null);
|
|
1456
|
-
setThinkingOpen(false);
|
|
1457
|
-
channel.notify(t('thinking-toggled', { state: enabled ? t('thinking-on') : t('thinking-off') }));
|
|
1458
|
-
}
|
|
1459
|
-
else if (key.escape) {
|
|
1460
|
-
setThinkingConfirm(null);
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
else if (key.upArrow || key.downArrow) {
|
|
1486
|
+
if (key.upArrow || key.downArrow) {
|
|
1464
1487
|
setThinkingFocus(index => (index === 0 ? 1 : 0));
|
|
1465
1488
|
}
|
|
1466
1489
|
else if (plainReturn) {
|
|
1467
|
-
const
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
}
|
|
1472
|
-
else {
|
|
1473
|
-
setThinkingVisible(enabled);
|
|
1474
|
-
setThinkingOpen(false);
|
|
1475
|
-
channel.notify(t('thinking-toggled', { state: enabled ? t('thinking-on') : t('thinking-off') }));
|
|
1476
|
-
}
|
|
1490
|
+
const visible = thinkingFocus === 0;
|
|
1491
|
+
setThinkingVisible(visible);
|
|
1492
|
+
setThinkingOpen(false);
|
|
1493
|
+
channel.notify(t('thinking-toggled', { state: visible ? t('thinking-on') : t('thinking-off') }));
|
|
1477
1494
|
}
|
|
1478
1495
|
else if (key.escape) {
|
|
1479
1496
|
setThinkingOpen(false);
|
|
@@ -1850,6 +1867,11 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
1850
1867
|
openScene();
|
|
1851
1868
|
return;
|
|
1852
1869
|
}
|
|
1870
|
+
if (isMod(key) && input === 'a') {
|
|
1871
|
+
// Ctrl+A opens the subagent dashboard.
|
|
1872
|
+
setSubagentDashboardOpen(true);
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1853
1875
|
if (isMod(key) && input === 'p' && loadedContextVisible) {
|
|
1854
1876
|
// Ctrl+P toggles the startup loaded-context panel while it is on
|
|
1855
1877
|
// screen (transcript still empty); once rows take over and the
|
|
@@ -1865,7 +1887,7 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
1865
1887
|
setHistoryOpen(true);
|
|
1866
1888
|
return;
|
|
1867
1889
|
}
|
|
1868
|
-
if (key.shift && key.upArrow && !selectionActive) {
|
|
1890
|
+
if (key.shift && key.upArrow && !selectionActive && !helpOpen) {
|
|
1869
1891
|
enterSelection();
|
|
1870
1892
|
}
|
|
1871
1893
|
else if (selectionActive) {
|
|
@@ -1883,7 +1905,7 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
1883
1905
|
setSelectedId(null);
|
|
1884
1906
|
}
|
|
1885
1907
|
}
|
|
1886
|
-
else if (key.escape && channel.working) {
|
|
1908
|
+
else if (key.escape && channel.working && !helpOpen) {
|
|
1887
1909
|
// CC's chat:cancel — esc interrupts a running turn (the prompt input
|
|
1888
1910
|
// only sees esc when idle, where it has the double-tap-clear meaning).
|
|
1889
1911
|
// With messages queued for delivery, interrupt-and-deliver them right
|
|
@@ -1899,8 +1921,11 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
1899
1921
|
}
|
|
1900
1922
|
event.stopImmediatePropagation();
|
|
1901
1923
|
}
|
|
1902
|
-
else if (isMod(key) && input === 'o') {
|
|
1924
|
+
else if (isMod(key) && input === 'o' && !helpOpen) {
|
|
1903
1925
|
// Leaving transcript mode (Ctrl+O) — search was already handled above.
|
|
1926
|
+
// Help is modal: toggling this state behind the overlay is invisible,
|
|
1927
|
+
// then the next `/` unexpectedly opens transcript search instead of
|
|
1928
|
+
// slash-command completion after Help closes.
|
|
1904
1929
|
setExpanded(previous => !previous);
|
|
1905
1930
|
// The toggle rewrites every thinking row's layout at once. The
|
|
1906
1931
|
// ordinary scroll-based diff pushes rows into terminal scrollback on
|
|
@@ -1913,7 +1938,7 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
1913
1938
|
const ink = instances.get(process.stdout) ?? instances.values().next().value;
|
|
1914
1939
|
ink?.reanchorViewport();
|
|
1915
1940
|
}
|
|
1916
|
-
else if (input === '/' && !key.ctrl && !key.meta && !key.super) {
|
|
1941
|
+
else if (input === '/' && !key.ctrl && !key.meta && !key.super && !helpOpen) {
|
|
1917
1942
|
// `/` in transcript mode (Ctrl+O expanded, CC's REPL semantics:
|
|
1918
1943
|
// search is active on the transcript screen where `/` isn't a command).
|
|
1919
1944
|
if (expanded) {
|
|
@@ -2011,6 +2036,31 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
2011
2036
|
const screen = _jsx(Settings, { channel: channel, onClose: () => setSettingsOpen(false) });
|
|
2012
2037
|
return fullscreen ? screen : _jsx(AlternateScreen, { children: screen });
|
|
2013
2038
|
}
|
|
2039
|
+
// Subagent detail scene: displays detailed view of a specific subagent.
|
|
2040
|
+
// Like the browser and settings, it replaces the conversation entirely.
|
|
2041
|
+
if (subagentDetailId !== null) {
|
|
2042
|
+
const subagent = channel.subagents.find(s => s.agentId === subagentDetailId);
|
|
2043
|
+
if (!subagent) {
|
|
2044
|
+
// Agent not found, go back to dashboard
|
|
2045
|
+
setSubagentDetailId(null);
|
|
2046
|
+
setSubagentDashboardOpen(true);
|
|
2047
|
+
return null;
|
|
2048
|
+
}
|
|
2049
|
+
const scene = (_jsx(SubagentDetailScene, { subagent: subagent, onInterrupt: (id) => channel.subagentControl.interrupt(id), onBack: () => {
|
|
2050
|
+
setSubagentDetailId(null);
|
|
2051
|
+
setSubagentDashboardOpen(true);
|
|
2052
|
+
} }));
|
|
2053
|
+
return fullscreen ? scene : _jsx(AlternateScreen, { children: scene });
|
|
2054
|
+
}
|
|
2055
|
+
// Subagent dashboard: displays all active and completed subagents.
|
|
2056
|
+
// Like the browser and settings, it replaces the conversation entirely.
|
|
2057
|
+
if (subagentDashboardOpen) {
|
|
2058
|
+
const dashboard = (_jsx(SubagentDashboard, { subagents: [...channel.subagents], onSelect: (id) => {
|
|
2059
|
+
setSubagentDashboardOpen(false);
|
|
2060
|
+
setSubagentDetailId(id);
|
|
2061
|
+
}, onClose: () => setSubagentDashboardOpen(false) }));
|
|
2062
|
+
return fullscreen ? dashboard : _jsx(AlternateScreen, { children: dashboard });
|
|
2063
|
+
}
|
|
2014
2064
|
/** Prompt input is inert while a modal dialog owns the keyboard. */
|
|
2015
2065
|
const promptSelectionActive = selectionActive || modelPickerOpen || skillsPickerOpen || workspacePickerOpen || workspaceFlow !== null || activityPickerOpen ||
|
|
2016
2066
|
effortSliderOpen || presetPickerOpen || themePickerOpen || thinkingOpen || historyOpen || rewindOpen || searchOpen ||
|
|
@@ -2044,8 +2094,9 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
2044
2094
|
seekRow(lastUser.id);
|
|
2045
2095
|
else
|
|
2046
2096
|
handle?.scrollToBottom();
|
|
2047
|
-
} })), _jsxs(ScrollBox, { ref: setHandle, flexDirection: "column", flexGrow: 1, flexShrink: 1, stickyScroll: true, children: [_jsx(LogoHeader, { model: channel.model, effort: channel.reasoningEffort, cwd: channel.displayCwd }), loadedContextVisible && (_jsx(LoadedContextPanel, { context: channel.loadedContext, open: loadedContextOpen, onToggle: toggleLoadedContext })), _jsx(MessageList, { rows: channel.rows, failureHintRowId: failureHintRowId, failureHint: t('traj-hint-failure', { key: `${modLabel}t` }), expanded: expanded, expandedRows: expandedRows, selectedId: selectionActive ? selectedId : null, onToggleRow: toggleRowExpanded, model: channel.model, diffLayout: channel.diffLayout, thinkingFold: channel.thinkingFold, toolBackground: channel.toolBackground, showAll: showAllMessages, thinkingVisible: thinkingVisible, onToggleAll: () => { setShowAllMessages(previous => !previous); }, onLoadOlder: () => channel.loadOlder(), registerRowRef: registerRowRef, scrollHandle: handle, forceMountRowId: forceMountRowId, newSinceRowId: isSticky ? null : lastSeenRowIdRef.current, onUnseenCount: setUnseenCount })] }), _jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [showPill && (_jsx(NewMessagesPill, { count: unseenCount, onClick: () => handle?.scrollToBottom() })), channel.working &&
|
|
2097
|
+
} })), _jsxs(ScrollBox, { ref: setHandle, flexDirection: "column", flexGrow: 1, flexShrink: 1, stickyScroll: true, children: [_jsx(LogoHeader, { model: channel.model, effort: channel.reasoningEffort, cwd: channel.displayCwd, whale: channel.whale }, logoNonce), loadedContextVisible && (_jsx(LoadedContextPanel, { context: channel.loadedContext, open: loadedContextOpen, onToggle: toggleLoadedContext })), _jsx(MessageList, { rows: channel.rows, failureHintRowId: failureHintRowId, failureHint: t('traj-hint-failure', { key: `${modLabel}t` }), expanded: expanded, expandedRows: expandedRows, selectedId: selectionActive ? selectedId : null, onToggleRow: toggleRowExpanded, model: channel.model, diffLayout: channel.diffLayout, thinkingFold: channel.thinkingFold, toolBackground: channel.toolBackground, activityFrames: channel.activityFrames, showAll: showAllMessages, thinkingVisible: thinkingVisible, onToggleAll: () => { setShowAllMessages(previous => !previous); }, onLoadOlder: () => channel.loadOlder(), registerRowRef: registerRowRef, scrollHandle: handle, forceMountRowId: forceMountRowId, newSinceRowId: isSticky ? null : lastSeenRowIdRef.current, onUnseenCount: setUnseenCount })] }), _jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [showPill && (_jsx(NewMessagesPill, { count: unseenCount, onClick: () => handle?.scrollToBottom() })), channel.working &&
|
|
2048
2098
|
(channel.activityEnabled &&
|
|
2099
|
+
!channel.minimal &&
|
|
2049
2100
|
channel.workingActivity !== undefined &&
|
|
2050
2101
|
channel.workingActivity.line !== '' &&
|
|
2051
2102
|
channel.workingActivity.phase !== 'idle' ? (_jsx(Box, { marginTop: 1, children: _jsx(ActivityLine, { activity: channel.workingActivity, activityFrames: channel.activityFrames, warnPct: activityWarnPct, warnDanger: activityWarnPct !== undefined && activityWarnPct >= 95,
|
|
@@ -2055,7 +2106,7 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
2055
2106
|
// inflating the reading next to a real upload number).
|
|
2056
2107
|
suffix: `${lastUploadTokens > 0 ? ` · ↑ ${formatTokens(lastUploadTokens)}` : ''} · ↓ ${formatTokens(Math.round(channel.responseChars / 4))} tokens` }) })) : (_jsx(WorkingSpinner, { mode: channel.spinnerMode, hasActiveTools: channel.activeToolCount > 0, responseLengthRef: responseLengthRef, uploadTokensRef: uploadTokensRef, loadingStartTimeRef: loadingStartTimeRef, totalPausedMsRef: totalPausedMsRef, pauseStartTimeRef: pauseStartTimeRef, thinkingStatus: thinkingStatus }))), _jsx(GoalTodoPanel, { channel: channel }), statusEntries.length > 0 && (_jsx(Text, { dimColor: true, wrap: "truncate", children: statusEntries.map(entry => entry.text).join(' · ') })), approvalSnapshot !== null ? (_jsx(ApprovalPanel, { approval: approvalSnapshot, onDecide: outcome => approvals.decide(outcome) }, approvalSnapshot.key)) : dialogSnapshot !== null ? (_jsx(ExtensionDialog, { dialog: dialogSnapshot, onDecide: value => dialogs.decide(dialogSnapshot.key, value), onCancel: () => dialogs.cancel(dialogSnapshot.key) }, dialogSnapshot.key)) : tipsOpen ? (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(TipsPanel, { onClose: () => setTipsOpen(false) }) })) : btw !== null ? (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(BtwPanel, { question: btw.question, answer: btw.answer, error: btw.error, streaming: !btw.done, onClose: closeBtw, onCopy: () => {
|
|
2057
2108
|
void setClipboard(btw.answer ?? '').then(raw => { if (raw)
|
|
2058
|
-
|
|
2109
|
+
writeRaw?.(raw); });
|
|
2059
2110
|
channel.notify(t('copied-chars', { n: (btw.answer ?? '').length }), { timeoutMs: 1500 });
|
|
2060
2111
|
} }) })) : questionSnapshot !== null ? (_jsx(AskUserQuestionPanel, { question: questionSnapshot.question, position: questionSnapshot.position, total: questionSnapshot.total, answered: questionSnapshot.answered, onAnswer: selection => questionStore.answerCurrent(selection), onCancel: () => questionStore.cancelCurrent() }, questionSnapshot.key)) : (_jsx(PromptInput, { channel: channel, helpOpen: helpOpen, onToggleHelp: () => { setHelpOpen(previous => !previous); }, onRunCommand: runCommand, selectionActive: promptSelectionActive, fillText: historyFill, onFillConsumed: () => { setHistoryFill(null); }, onRewindRequest: openRewind, controllerRef: promptControllerRef })), _jsx(StatusLine, { channel: channel, selectionActive: selectionActive, helpOpen: helpOpen, wake: wakeBand === undefined
|
|
2061
2112
|
? undefined
|
|
@@ -2063,7 +2114,7 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
2063
2114
|
band: wakeBand,
|
|
2064
2115
|
hint: trajectorySeen ? undefined : `${modLabel}t`,
|
|
2065
2116
|
tick: Math.floor(wakeTime / 120),
|
|
2066
|
-
} }), dialogOverlayOpen && (_jsxs(OverlayAbove, { maxHeight: Math.max(terminalRows - 8, 8), children: [thinkingOpen && (_jsx(ThinkingToggle, { currentValue: thinkingVisible, focusIndex: thinkingFocus
|
|
2117
|
+
} }), dialogOverlayOpen && (_jsxs(OverlayAbove, { maxHeight: Math.max(terminalRows - 8, 8), children: [thinkingOpen && (_jsx(ThinkingToggle, { currentValue: thinkingVisible, focusIndex: thinkingFocus })), workspacePickerOpen && workspaceTargets.length > 0 && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(WorkspacePicker, { targets: workspaceTargets, focusIndex: workspaceIndex, currentCwd: channel.cwd }) })), workspaceFlow !== null && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(WorkspaceFlowPicker, { title: workspaceFlow.title, choices: workspaceFlow.choices, focusIndex: workspaceFlowIndex, busy: workspaceFlowBusy, input: workspaceFlowInput }) })), modelPickerOpen && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: models.length === 0 ? (_jsx(ModelPickerLoading, {})) : (_jsx(ModelPicker, { models: models, focusIndex: modelIndex, currentModel: `${channel.provider}/${channel.model}` })) })), skillsPickerOpen && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: skillsList === null ? (_jsx(SkillsPickerLoading, {})) : (_jsx(SkillsPicker, { skills: skillsList, focusIndex: skillsIndex })) })), activityPickerOpen && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(ActivityPicker, { focusIndex: activityIndex, currentPreset: channel.activityFrames }) })), effortSliderOpen && effortOptions.length > 1 && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(EffortSlider, { options: effortOptions, focusIndex: effortIndex, currentId: channel.reasoningEffort }) })), presetPickerOpen && presetOptions.length > 0 && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(PresetPicker, { presets: presetOptions, focusIndex: presetIndex, currentPreset: channel.agentPreset }) })), themePickerOpen && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(ThemePicker, { focusIndex: themeIndex, currentTheme: themeName }) })), historyOpen && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(HistorySearchDialog, { query: historyQuery, cursorOffset: historyCursor, matches: historyMatches, focusIndex: historyFocus }) })), rewindOpen && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(RewindPicker, { rows: rewindRows, focusIndex: rewindIndex, confirmRow: rewindConfirm, modes: rewindModes, modeIndex: rewindModeIndex, busy: rewindBusy }) })), searchOpen && _jsx(TranscriptSearchBar, { query: searchQuery, cursorOffset: searchCursor, count: searchCount, current: searchCurrent })] }))] })] }));
|
|
2067
2118
|
}
|
|
2068
2119
|
/**
|
|
2069
2120
|
* The pinned prompt header shown above the ScrollBox while the user has
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatusLine.d.ts","sourceRoot":"","sources":["../../../src/screens/StatusLine.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAGxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAQvD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,eAAuB,EACvB,QAAgB,EAChB,IAAI,GACL,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CACvD,
|
|
1
|
+
{"version":3,"file":"StatusLine.d.ts","sourceRoot":"","sources":["../../../src/screens/StatusLine.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAGxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAQvD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,eAAuB,EACvB,QAAgB,EAChB,IAAI,GACL,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CACvD,qBA0PA;AAED,KAAK,aAAa,GAAG;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAKvF"}
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Box, Text, useTerminalSize, useTheme } from '../ui.js';
|
|
3
3
|
import { formatTokens } from '../cc/format.js';
|
|
4
4
|
import { t } from '../i18n.js';
|
|
5
|
-
import { formatContextUsage, normalizeStatusBar } from '../tuiDisplayPrefs.js';
|
|
5
|
+
import { formatContextUsage, DEFAULT_STATUS_BAR, normalizeStatusBar } from '../tuiDisplayPrefs.js';
|
|
6
6
|
import { Byline } from '../components/design-system/Byline.js';
|
|
7
7
|
import { ActivityLine, contextPressurePct } from '../components/ActivityLine.js';
|
|
8
8
|
import { modeDisplayName } from '../sessionModes.js';
|
|
@@ -19,7 +19,11 @@ import { renderContextBar, renderTpsGauge, renderTpsSparkline, speedColor, } fro
|
|
|
19
19
|
export function StatusLine({ channel, selectionActive = false, helpOpen = false, wake, }) {
|
|
20
20
|
const { columns } = useTerminalSize();
|
|
21
21
|
const [themeName] = useTheme();
|
|
22
|
-
const statusBar =
|
|
22
|
+
const statusBar = channel.minimal
|
|
23
|
+
// Minimal mode overrides every field switch: model + cwd only, so the
|
|
24
|
+
// footer can never grow decorations regardless of saved preferences.
|
|
25
|
+
? { ...DEFAULT_STATUS_BAR, compact: true, model: true, cwd: true }
|
|
26
|
+
: normalizeStatusBar(channel.statusBar);
|
|
23
27
|
const usage = channel.lastUsage;
|
|
24
28
|
const contextUsed = usage === undefined
|
|
25
29
|
? undefined
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TrajectoryScene.d.ts","sourceRoot":"","sources":["../../../src/screens/TrajectoryScene.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAczB,OAAO,EAML,KAAK,SAAS,EACf,MAAM,oCAAoC,CAAA;AAE3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AA+BxD,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,SAAS,CAAA;AAEnD,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EACP,KAAK,EACL,OAAO,GACR,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB;;;;OAIG;IACH,KAAK,EAAE,SAAS,CAAA;IAChB,sDAAsD;IACtD,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,GAAG,KAAK,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"TrajectoryScene.d.ts","sourceRoot":"","sources":["../../../src/screens/TrajectoryScene.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAczB,OAAO,EAML,KAAK,SAAS,EACf,MAAM,oCAAoC,CAAA;AAE3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AA+BxD,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,SAAS,CAAA;AAEnD,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EACP,KAAK,EACL,OAAO,GACR,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB;;;;OAIG;IACH,KAAK,EAAE,SAAS,CAAA;IAChB,sDAAsD;IACtD,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,GAAG,KAAK,CAAC,SAAS,CAyYlB"}
|
|
@@ -227,12 +227,14 @@ export function TrajectoryScene({ channel, build, onClose, }) {
|
|
|
227
227
|
return move(-ledgerRows);
|
|
228
228
|
if (key.pageDown)
|
|
229
229
|
return move(ledgerRows);
|
|
230
|
-
|
|
230
|
+
// Bare-letter jumps must not fire on Ctrl+G (the prompt's external-editor
|
|
231
|
+
// key) or other modified chords that share the letter.
|
|
232
|
+
if (input === 'g' && !key.ctrl && !key.meta && !key.super) {
|
|
231
233
|
setCursor(0);
|
|
232
234
|
setFollow(false);
|
|
233
235
|
return;
|
|
234
236
|
}
|
|
235
|
-
if (input === 'G') {
|
|
237
|
+
if (input === 'G' && !key.ctrl && !key.meta && !key.super) {
|
|
236
238
|
setCursor(Math.max(0, filtered.length - 1));
|
|
237
239
|
setFollow(true);
|
|
238
240
|
return;
|
package/lib/types/theme.d.ts
CHANGED
|
@@ -104,6 +104,14 @@ export type Theme = {
|
|
|
104
104
|
rainbow_blue_shimmer: string;
|
|
105
105
|
rainbow_indigo_shimmer: string;
|
|
106
106
|
rainbow_violet_shimmer: string;
|
|
107
|
+
subagentBullet: string;
|
|
108
|
+
subagentDescription: string;
|
|
109
|
+
subagentModel: string;
|
|
110
|
+
subagentElapsed: string;
|
|
111
|
+
subagentToolName: string;
|
|
112
|
+
subagentStatusRunning: string;
|
|
113
|
+
subagentStatusCompleted: string;
|
|
114
|
+
subagentStatusFailed: string;
|
|
107
115
|
};
|
|
108
116
|
/** The built-in theme names, in display order. */
|
|
109
117
|
export declare const THEME_NAMES: readonly ["dark", "dark-ansi", "light"];
|
package/lib/types/theme.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/theme.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/theme.ts"],"names":[],"mappings":"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"}
|