@deepseek-harness-tui/dsh-tui 0.9.3 → 0.10.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -6
- package/cordis.patch.yml +117 -39
- package/cordis.yml +13 -10
- package/lib/types/api.d.ts +7 -0
- package/lib/types/api.d.ts.map +1 -0
- package/lib/types/api.js +1 -0
- package/lib/types/cc/hyperlink.d.ts.map +1 -1
- package/lib/types/cc/hyperlink.js +42 -4
- package/lib/types/cc/markdown.d.ts.map +1 -1
- package/lib/types/cc/markdown.js +15 -5
- package/lib/types/commands.d.ts +6 -0
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +12 -8
- package/lib/types/components/MessageList.d.ts +8 -4
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +94 -32
- package/lib/types/components/PermissionsPicker.d.ts +6 -7
- package/lib/types/components/PermissionsPicker.d.ts.map +1 -1
- package/lib/types/components/PermissionsPicker.js +20 -14
- package/lib/types/components/PromptEditor.d.ts +46 -0
- package/lib/types/components/PromptEditor.d.ts.map +1 -0
- package/lib/types/components/PromptEditor.js +60 -0
- package/lib/types/components/PromptInput.d.ts +12 -0
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +1240 -205
- package/lib/types/components/RewindPicker.js +1 -1
- package/lib/types/components/SplitDiffView.d.ts +11 -1
- package/lib/types/components/SplitDiffView.d.ts.map +1 -1
- package/lib/types/components/SplitDiffView.js +9 -2
- package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
- package/lib/types/components/StreamingMarkdown.js +140 -3
- package/lib/types/components/SubagentDashboard.d.ts.map +1 -1
- package/lib/types/components/SubagentDashboard.js +2 -2
- package/lib/types/components/SubagentDetailScene.js +1 -1
- package/lib/types/components/ThemePicker.d.ts +13 -15
- package/lib/types/components/ThemePicker.d.ts.map +1 -1
- package/lib/types/components/ThemePicker.js +24 -26
- package/lib/types/components/Tooltip.d.ts +67 -0
- package/lib/types/components/Tooltip.d.ts.map +1 -0
- package/lib/types/components/Tooltip.js +220 -0
- package/lib/types/components/approvals/ApprovalPanel.d.ts.map +1 -1
- package/lib/types/components/approvals/ApprovalPanel.js +1 -1
- package/lib/types/components/design-system/Divider.d.ts +14 -1
- package/lib/types/components/design-system/Divider.d.ts.map +1 -1
- package/lib/types/components/design-system/Divider.js +86 -8
- package/lib/types/components/design-system/ThemeProvider.d.ts +5 -2
- package/lib/types/components/design-system/ThemeProvider.d.ts.map +1 -1
- package/lib/types/components/design-system/ThemeProvider.js +52 -16
- package/lib/types/components/design-system/ThemedBox.d.ts +23 -2
- package/lib/types/components/design-system/ThemedBox.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts +11 -6
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.js +10 -6
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +13 -1
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +37 -6
- package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
- package/lib/types/components/messages/UserPromptMessage.js +7 -1
- package/lib/types/components/questions/AskUserQuestionPanel.js +1 -1
- package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -1
- package/lib/types/components/questions/PlanReviewPanel.js +29 -3
- package/lib/types/components/sessions/SessionListRow.d.ts +8 -1
- package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
- package/lib/types/components/sessions/SessionListRow.js +9 -2
- package/lib/types/components/sessions/SessionPreview.d.ts.map +1 -1
- package/lib/types/components/sessions/SessionPreview.js +5 -3
- package/lib/types/components/sessions/WorkspaceListRow.d.ts +17 -0
- package/lib/types/components/sessions/WorkspaceListRow.d.ts.map +1 -0
- package/lib/types/components/sessions/WorkspaceListRow.js +27 -0
- package/lib/types/components/smoothReveal.d.ts +97 -0
- package/lib/types/components/smoothReveal.d.ts.map +1 -0
- package/lib/types/components/smoothReveal.js +279 -0
- package/lib/types/customTheme.d.ts +4 -0
- package/lib/types/customTheme.d.ts.map +1 -1
- package/lib/types/customTheme.js +9 -5
- package/lib/types/dsh-adapter/approvals.d.ts +114 -2
- package/lib/types/dsh-adapter/approvals.d.ts.map +1 -1
- package/lib/types/dsh-adapter/approvals.js +241 -3
- package/lib/types/dsh-adapter/channel.d.ts +59 -2
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +499 -80
- package/lib/types/dsh-adapter/childStderr.d.ts.map +1 -1
- package/lib/types/dsh-adapter/childStderr.js +9 -0
- package/lib/types/dsh-adapter/contract.d.ts +17 -13
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +36 -27
- package/lib/types/dsh-adapter/credentialRefGuard.d.ts +40 -0
- package/lib/types/dsh-adapter/credentialRefGuard.d.ts.map +1 -0
- package/lib/types/dsh-adapter/credentialRefGuard.js +43 -0
- package/lib/types/dsh-adapter/effect-ledger.d.ts +1 -1
- package/lib/types/dsh-adapter/effect-ledger.d.ts.map +1 -1
- package/lib/types/dsh-adapter/effect-ledger.js +1 -0
- package/lib/types/dsh-adapter/extensions.d.ts +4 -1
- package/lib/types/dsh-adapter/extensions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/extensions.js +8 -1
- package/lib/types/dsh-adapter/index.d.ts +11 -1
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +2 -0
- package/lib/types/dsh-adapter/plugin-test-utils.d.ts +62 -0
- package/lib/types/dsh-adapter/plugin-test-utils.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-test-utils.js +61 -0
- package/lib/types/dsh-adapter/plugin.d.ts +20 -0
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +174 -39
- package/lib/types/dsh-adapter/plugins-info.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugins-info.js +12 -0
- package/lib/types/dsh-adapter/preset-resolution.d.ts +49 -0
- package/lib/types/dsh-adapter/preset-resolution.d.ts.map +1 -0
- package/lib/types/dsh-adapter/preset-resolution.js +42 -0
- package/lib/types/dsh-adapter/presets.d.ts +0 -26
- package/lib/types/dsh-adapter/presets.d.ts.map +1 -1
- package/lib/types/dsh-adapter/presets.js +4 -14
- package/lib/types/dsh-adapter/promptDebug.d.ts.map +1 -1
- package/lib/types/dsh-adapter/promptDebug.js +4 -1
- package/lib/types/dsh-adapter/providerGuard.d.ts +75 -0
- package/lib/types/dsh-adapter/providerGuard.d.ts.map +1 -0
- package/lib/types/dsh-adapter/providerGuard.js +104 -0
- package/lib/types/dsh-adapter/questions-answerer.d.ts +49 -0
- package/lib/types/dsh-adapter/questions-answerer.d.ts.map +1 -0
- package/lib/types/dsh-adapter/questions-answerer.js +61 -0
- package/lib/types/dsh-adapter/recap.d.ts.map +1 -1
- package/lib/types/dsh-adapter/recap.js +12 -6
- package/lib/types/dsh-adapter/sanitize.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sanitize.js +6 -1
- package/lib/types/dsh-adapter/sessions/digest.d.ts +21 -1
- package/lib/types/dsh-adapter/sessions/digest.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessions/digest.js +229 -3
- package/lib/types/dsh-adapter/sessions/frames.d.ts +3 -0
- package/lib/types/dsh-adapter/sessions/frames.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessions/frames.js +34 -19
- package/lib/types/dsh-adapter/sessions/list.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessions/list.js +148 -31
- package/lib/types/dsh-adapter/sessions/store.d.ts +8 -0
- package/lib/types/dsh-adapter/sessions/store.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessions/store.js +24 -4
- package/lib/types/dsh-adapter/sessions/types.d.ts +3 -0
- package/lib/types/dsh-adapter/sessions/types.d.ts.map +1 -1
- package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
- package/lib/types/dsh-adapter/settings-sections.js +13 -1
- package/lib/types/dsh-adapter/settingsEditor.d.ts +6 -0
- package/lib/types/dsh-adapter/settingsEditor.d.ts.map +1 -1
- package/lib/types/dsh-adapter/settingsEditor.js +15 -3
- package/lib/types/dsh-adapter/subagents.d.ts +4 -0
- package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
- package/lib/types/dsh-adapter/subagents.js +8 -0
- package/lib/types/dsh-adapter/themes.d.ts +51 -0
- package/lib/types/dsh-adapter/themes.d.ts.map +1 -0
- package/lib/types/dsh-adapter/themes.js +272 -0
- package/lib/types/dsh-adapter/toast.d.ts +67 -0
- package/lib/types/dsh-adapter/toast.d.ts.map +1 -0
- package/lib/types/dsh-adapter/toast.js +141 -0
- package/lib/types/dsh-adapter/types.d.ts +1 -1
- package/lib/types/extensions.d.ts +4 -0
- package/lib/types/extensions.d.ts.map +1 -1
- package/lib/types/extensions.js +5 -1
- package/lib/types/history.d.ts.map +1 -1
- package/lib/types/history.js +7 -2
- package/lib/types/hooks/useRuntimeThemeSnapshot.d.ts +10 -0
- package/lib/types/hooks/useRuntimeThemeSnapshot.d.ts.map +1 -0
- package/lib/types/hooks/useRuntimeThemeSnapshot.js +32 -0
- package/lib/types/i18n.d.ts +199 -41
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +82 -20
- package/lib/types/ink/components/App.d.ts +20 -0
- package/lib/types/ink/components/App.d.ts.map +1 -1
- package/lib/types/ink/components/App.js +142 -13
- package/lib/types/ink/components/Box.d.ts +39 -2
- package/lib/types/ink/components/Box.d.ts.map +1 -1
- package/lib/types/ink/components/Box.js +61 -37
- package/lib/types/ink/events/context-menu-event.d.ts +19 -0
- package/lib/types/ink/events/context-menu-event.d.ts.map +1 -0
- package/lib/types/ink/events/context-menu-event.js +20 -0
- package/lib/types/ink/events/drag-event.d.ts +44 -0
- package/lib/types/ink/events/drag-event.d.ts.map +1 -0
- package/lib/types/ink/events/drag-event.js +39 -0
- package/lib/types/ink/events/event-handlers.d.ts +8 -0
- package/lib/types/ink/events/event-handlers.d.ts.map +1 -1
- package/lib/types/ink/events/event-handlers.js +8 -0
- package/lib/types/ink/hit-test.d.ts +69 -0
- package/lib/types/ink/hit-test.d.ts.map +1 -1
- package/lib/types/ink/hit-test.js +260 -6
- package/lib/types/ink/ink.d.ts +25 -0
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +74 -10
- package/lib/types/ink/parse-keypress.d.ts +20 -0
- package/lib/types/ink/parse-keypress.d.ts.map +1 -1
- package/lib/types/ink/parse-keypress.js +190 -22
- package/lib/types/ink/renderer.d.ts.map +1 -1
- package/lib/types/ink/renderer.js +15 -1
- package/lib/types/ink/screen.d.ts +7 -0
- package/lib/types/ink/screen.d.ts.map +1 -1
- package/lib/types/ink/screen.js +8 -1
- package/lib/types/ink/termio/osc.d.ts +2 -1
- package/lib/types/ink/termio/osc.d.ts.map +1 -1
- package/lib/types/ink/termio/osc.js +26 -5
- package/lib/types/plugin-spec/registry.d.ts +1 -1
- package/lib/types/plugin-spec/registry.js +1 -1
- package/lib/types/presetPrefs.d.ts +6 -5
- package/lib/types/presetPrefs.d.ts.map +1 -1
- package/lib/types/presetPrefs.js +10 -5
- package/lib/types/screens/Chat.d.ts +4 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +169 -68
- package/lib/types/screens/SessionBrowser.d.ts +9 -4
- package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
- package/lib/types/screens/SessionBrowser.js +483 -75
- package/lib/types/screens/SessionTree.d.ts.map +1 -1
- package/lib/types/screens/SessionTree.js +5 -1
- package/lib/types/screens/Settings.d.ts.map +1 -1
- package/lib/types/screens/Settings.js +5 -1
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +5 -1
- package/lib/types/screens/chatOverlay.d.ts +3 -2
- package/lib/types/screens/chatOverlay.d.ts.map +1 -1
- package/lib/types/screens/chatOverlay.js +2 -0
- package/lib/types/sessionPins.d.ts +33 -0
- package/lib/types/sessionPins.d.ts.map +1 -0
- package/lib/types/sessionPins.js +181 -0
- package/lib/types/sessions/format.d.ts +2 -0
- package/lib/types/sessions/format.d.ts.map +1 -1
- package/lib/types/sessions/format.js +9 -0
- package/lib/types/sessions/view.d.ts +42 -6
- package/lib/types/sessions/view.d.ts.map +1 -1
- package/lib/types/sessions/view.js +133 -35
- package/lib/types/test-utils.d.ts +2 -0
- package/lib/types/test-utils.d.ts.map +1 -0
- package/lib/types/test-utils.js +8 -0
- package/lib/types/theme.d.ts +22 -5
- package/lib/types/theme.d.ts.map +1 -1
- package/lib/types/theme.js +57 -7
- package/lib/types/themeCatalog.d.ts +36 -0
- package/lib/types/themeCatalog.d.ts.map +1 -0
- package/lib/types/themeCatalog.js +157 -0
- package/lib/types/tips.d.ts.map +1 -1
- package/lib/types/tips.js +114 -18
- package/lib/types/update.d.ts +159 -3
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +497 -16
- package/lib/types/utils/debug.d.ts.map +1 -1
- package/lib/types/utils/debug.js +2 -1
- package/lib/types/utils/keymap.d.ts +1 -1
- package/lib/types/utils/keymap.d.ts.map +1 -1
- package/lib/types/utils/keymap.js +19 -2
- package/lib/types/utils/urlGuard.d.ts +28 -0
- package/lib/types/utils/urlGuard.d.ts.map +1 -0
- package/lib/types/utils/urlGuard.js +21 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/README.md +3 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/credentials.d.ts +7 -11
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/index.d.ts +16 -1
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/index.js +23 -2
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/interaction.d.ts +5 -5
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/pi-ai.d.ts +23 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/pi-ai.js +27 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/profiles.d.ts +17 -3
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/profiles.js +36 -4
- package/node_modules/@deepseek-harness-tui/dsh-auth/package.json +11 -12
- package/package.json +114 -74
- package/lib/types/dsh-adapter/packaged-skills.d.ts +0 -19
- package/lib/types/dsh-adapter/packaged-skills.d.ts.map +0 -1
- package/lib/types/dsh-adapter/packaged-skills.js +0 -78
- package/skills/audit/SKILL.md +0 -24
- package/skills/bug/SKILL.md +0 -27
- package/skills/pr-comments/SKILL.md +0 -20
- package/skills/practice/SKILL.md +0 -22
- package/skills/release-notes/SKILL.md +0 -25
- package/skills/review/SKILL.md +0 -25
- package/skills/vuln-check/SKILL.md +0 -25
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantThinkingMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantThinkingMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAczB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAUjE,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,4DAA4D;IAC5D,SAAS,EAAE,OAAO,CAAA;IAClB,
|
|
1
|
+
{"version":3,"file":"AssistantThinkingMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantThinkingMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAczB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAUjE,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB;;;sDAGkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4DAA4D;IAC5D,SAAS,EAAE,OAAO,CAAA;IAClB,4EAA4E;IAC5E,OAAO,EAAE,OAAO,CAAA;IAChB;;4CAEwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;8EAE0E;IAC1E,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;CAClC,CAAA;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,OAAO,EACP,SAAiB,EACjB,OAAe,EACf,UAAU,EACV,UAAkB,EAClB,OAAO,GACR,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CAuIzB"}
|
|
@@ -17,17 +17,21 @@ import { isMinimalMode } from '../../minimalMode.js';
|
|
|
17
17
|
* to exactly this many rows, keeps the block height stream-independent. */
|
|
18
18
|
const PREVIEW_ROWS = 3;
|
|
19
19
|
/**
|
|
20
|
-
* Thinking block:
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* style), settling back to the static anchor (`⚓`)
|
|
20
|
+
* Thinking block: settled rows fold to `⚓ Thinking (ctrl+o to expand)`;
|
|
21
|
+
* streaming rows switch between a three-line preview and the full reasoning
|
|
22
|
+
* text on click. The live leading mark is a rotating braille spinner
|
|
23
|
+
* (`⠋⠙⠹…`, Kimi Code style), settling back to the static anchor (`⚓`). When
|
|
24
24
|
* the channel records the reasoning duration, the label carries it
|
|
25
25
|
* (`⚓ Thinking · 12s …`) — dsh-tui's take on making thinking time visible in
|
|
26
26
|
* the transcript.
|
|
27
27
|
*/
|
|
28
|
-
export function AssistantThinkingMessage({ thinking, addMargin, verbose, streaming = false, preview = false, durationMs, isSelected = false, onClick, }) {
|
|
28
|
+
export function AssistantThinkingMessage({ thinking, textFull, addMargin, verbose, streaming = false, preview = false, durationMs, isSelected = false, onClick, }) {
|
|
29
29
|
if (!thinking)
|
|
30
30
|
return null;
|
|
31
|
+
// The preview ticker tracks the newest ARRIVED line (smooth streaming must
|
|
32
|
+
// not lag it behind the reveal); the expanded body below paints `thinking`
|
|
33
|
+
// — the revealed slice under smooth streaming, the full text otherwise.
|
|
34
|
+
const tickerText = textFull ?? thinking;
|
|
31
35
|
// Spinner frame (80ms cadence, only while the reasoning is still
|
|
32
36
|
// streaming — same pattern as BtwPanel's answering spinner).
|
|
33
37
|
const [frame, setFrame] = React.useState(0);
|
|
@@ -64,7 +68,7 @@ export function AssistantThinkingMessage({ thinking, addMargin, verbose, streami
|
|
|
64
68
|
// from the start (leading ellipsis) so the newest tokens — which grow
|
|
65
69
|
// at the line's end — stay visible while the line is longer than the
|
|
66
70
|
// width.
|
|
67
|
-
const lines =
|
|
71
|
+
const lines = tickerText.split('\n');
|
|
68
72
|
const visible = lines.slice(-PREVIEW_ROWS);
|
|
69
73
|
const clipped = lines.length > visible.length;
|
|
70
74
|
// Pad with single spaces — an empty-string Text renders with zero
|
|
@@ -45,6 +45,18 @@ type Props = {
|
|
|
45
45
|
* `+N lines` hint; verbose/expanded cards render the full title.
|
|
46
46
|
*/
|
|
47
47
|
foldTerminalCommand?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Smooth streaming reveal (settings `dsh-tui.smoothStreaming`): the card
|
|
50
|
+
* BODY (diff hunks / write content — model-authored prose, not tool
|
|
51
|
+
* output) paints through an even ~30fps line reveal when it first appears,
|
|
52
|
+
* instead of one jarring block. Only the pending CALL view animates; the
|
|
53
|
+
* settled result view paints complete (real output is progress, not
|
|
54
|
+
* prose), and so do replayed cards.
|
|
55
|
+
*/
|
|
56
|
+
smoothReveal?: boolean;
|
|
57
|
+
/** Live-arrived row (channel `fresh`): gates reveal participation —
|
|
58
|
+
* replayed cards must paint complete. */
|
|
59
|
+
fresh?: boolean;
|
|
48
60
|
};
|
|
49
61
|
export declare function toolNameColor(raw: string): keyof Theme;
|
|
50
62
|
/**
|
|
@@ -53,6 +65,6 @@ export declare function toolNameColor(raw: string): keyof Theme;
|
|
|
53
65
|
* output, read content — instead of the raw result dump (mirroring Claude Code's `AssistantToolUseMessage.tsx` + the dsh-tools presentation views the
|
|
54
66
|
* channel captures per call).
|
|
55
67
|
*/
|
|
56
|
-
export declare function AssistantToolUseMessage({ tool, addMargin, verbose, isSelected, isExpanded, onClick, footnote, diffLayout, toolBackground, onOpenFile, foldTerminalCommand, }: Props): React.ReactNode;
|
|
68
|
+
export declare function AssistantToolUseMessage({ tool, addMargin, verbose, isSelected, isExpanded, onClick, footnote, diffLayout, toolBackground, onOpenFile, foldTerminalCommand, smoothReveal, fresh, }: Props): React.ReactNode;
|
|
57
69
|
export {};
|
|
58
70
|
//# sourceMappingURL=AssistantToolUseMessage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantToolUseMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantToolUseMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,KAAK,EAA8C,OAAO,EAAE,MAAM,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"AssistantToolUseMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantToolUseMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,KAAK,EAA8C,OAAO,EAAE,MAAM,8BAA8B,CAAA;AAMvG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAGjE,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAA;IACb,4DAA4D;IAC5D,SAAS,EAAE,OAAO,CAAA;IAClB,iEAAiE;IACjE,OAAO,EAAE,OAAO,CAAA;IAChB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,sEAAsE;IACtE,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;OAIG;IACH,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IACjC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IACzC,2EAA2E;IAC3E,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB;8CAC0C;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAsFD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,KAAK,CAKtD;AA+OD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,IAAI,EACJ,SAAS,EACT,OAAO,EACP,UAAkB,EAClB,UAAkB,EAClB,OAAO,EACP,QAAQ,EACR,UAAmB,EACnB,cAAuB,EACvB,UAAU,EACV,mBAA2B,EAC3B,YAAoB,EACpB,KAAa,GACd,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CAiOzB"}
|
|
@@ -7,7 +7,9 @@ import { useAnimationFrame } from '../../ink/hooks/use-animation-frame.js';
|
|
|
7
7
|
import { ToolUseLoader } from '../ToolUseLoader.js';
|
|
8
8
|
import { SplitDiffView } from '../SplitDiffView.js';
|
|
9
9
|
import { SyntaxText } from '../SyntaxText.js';
|
|
10
|
+
import { useTooltip } from '../Tooltip.js';
|
|
10
11
|
import { formatDuration } from '../../cc/format.js';
|
|
12
|
+
import { getRevealVersion, revealLinesOf, snapReveal, subscribeReveal } from '../smoothReveal.js';
|
|
11
13
|
/** Tool display names: DSH emits lowercase tool ids (`bash`); Claude Code
|
|
12
14
|
* shows capitalized names (`Bash`). Map the common ones, fall back to the
|
|
13
15
|
* id with its first letter uppercased. */
|
|
@@ -223,11 +225,21 @@ function foldTerminalTitle(title) {
|
|
|
223
225
|
return { first, hidden };
|
|
224
226
|
}
|
|
225
227
|
function HeaderTitle({ name, title, isTerminal, folded, displayArgs, argsLanguage, nameColor, filePath, onOpenFile }) {
|
|
228
|
+
// Hover tooltip: the header line truncates long paths/commands, so the
|
|
229
|
+
// full string (or the unfolded terminal script) pops up after a dwell.
|
|
230
|
+
// Empty content is a no-op inside the hook.
|
|
231
|
+
const headerTooltip = useTooltip(() => {
|
|
232
|
+
if (title === undefined)
|
|
233
|
+
return displayArgs;
|
|
234
|
+
if (isTerminal)
|
|
235
|
+
return title;
|
|
236
|
+
return title.trim();
|
|
237
|
+
});
|
|
226
238
|
if (title === undefined) {
|
|
227
|
-
return (_jsxs(_Fragment, { children: [_jsx(Box, { flexShrink: 0, children: _jsx(Text, { bold: true, color: nameColor, wrap: "truncate-end", children: name }) }), displayArgs !== '' && (_jsxs(Box, { flexWrap: "nowrap", children: [_jsx(Text, { children: "(" }), _jsx(SyntaxText, { text: clipHeaderArgs(displayArgs), sourceText: displayArgs, language: argsLanguage }), _jsx(Text, { children: ")" })] }))] }));
|
|
239
|
+
return (_jsxs(_Fragment, { children: [_jsx(Box, { flexShrink: 0, children: _jsx(Text, { bold: true, color: nameColor, wrap: "truncate-end", children: name }) }), displayArgs !== '' && (_jsxs(Box, { flexWrap: "nowrap", ...headerTooltip, children: [_jsx(Text, { children: "(" }), _jsx(SyntaxText, { text: clipHeaderArgs(displayArgs), sourceText: displayArgs, language: argsLanguage }), _jsx(Text, { children: ")" })] }))] }));
|
|
228
240
|
}
|
|
229
241
|
if (isTerminal) {
|
|
230
|
-
return (_jsxs(_Fragment, { children: [_jsx(Box, { flexShrink: 0, children: _jsx(Text, { bold: true, color: nameColor, wrap: "truncate-end", children: name }) }), _jsx(Box, { flexWrap: "nowrap", children: folded === undefined ? (_jsxs(Text, { children: ["(", title, ")"] })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { children: ["(", folded.first, ")"] }), _jsx(Text, { dimColor: true, children: ` … +${folded.hidden} lines (ctrl+o to expand)` })] })) })] }));
|
|
242
|
+
return (_jsxs(_Fragment, { children: [_jsx(Box, { flexShrink: 0, children: _jsx(Text, { bold: true, color: nameColor, wrap: "truncate-end", children: name }) }), _jsx(Box, { flexWrap: "nowrap", ...headerTooltip, children: folded === undefined ? (_jsxs(Text, { children: ["(", title, ")"] })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { children: ["(", folded.first, ")"] }), _jsx(Text, { dimColor: true, children: ` … +${folded.hidden} lines (ctrl+o to expand)` })] })) })] }));
|
|
231
243
|
}
|
|
232
244
|
const trimmed = title.trim();
|
|
233
245
|
if (trimmed === '') {
|
|
@@ -242,7 +254,7 @@ function HeaderTitle({ name, title, isTerminal, folded, displayArgs, argsLanguag
|
|
|
242
254
|
const at = trimmed.indexOf(filePath);
|
|
243
255
|
const before = trimmed.slice(0, at);
|
|
244
256
|
const after = trimmed.slice(at + filePath.length);
|
|
245
|
-
return (_jsxs(Box, { flexWrap: "nowrap", children: [_jsx(Text, { bold: true, color: nameColor, wrap: "truncate-end", children: before }), _jsx(Box, { onClick: (event) => {
|
|
257
|
+
return (_jsxs(Box, { flexWrap: "nowrap", ...headerTooltip, children: [_jsx(Text, { bold: true, color: nameColor, wrap: "truncate-end", children: before }), _jsx(Box, { onClick: (event) => {
|
|
246
258
|
event.stopImmediatePropagation();
|
|
247
259
|
onOpenFile(filePath);
|
|
248
260
|
}, children: _jsx(Text, { underline: true, wrap: "truncate-end", children: filePath }) }), after !== '' && (_jsx(Text, { bold: false, color: "text", wrap: "truncate-end", children: after }))] }));
|
|
@@ -250,7 +262,7 @@ function HeaderTitle({ name, title, isTerminal, folded, displayArgs, argsLanguag
|
|
|
250
262
|
const space = trimmed.indexOf(' ');
|
|
251
263
|
const head = space === -1 ? trimmed : trimmed.slice(0, space);
|
|
252
264
|
const tail = space === -1 ? '' : trimmed.slice(space);
|
|
253
|
-
return (_jsx(Box, { flexWrap: "nowrap", children: _jsxs(Text, { bold: true, color: nameColor, wrap: "truncate-end", children: [head, _jsx(Text, { bold: false, color: "text", children: tail })] }) }));
|
|
265
|
+
return (_jsx(Box, { flexWrap: "nowrap", ...headerTooltip, children: _jsxs(Text, { bold: true, color: nameColor, wrap: "truncate-end", children: [head, _jsx(Text, { bold: false, color: "text", children: tail })] }) }));
|
|
254
266
|
}
|
|
255
267
|
/**
|
|
256
268
|
* Tool-call card: `● Edit /path` header with a blinking status dot, then the
|
|
@@ -258,7 +270,11 @@ function HeaderTitle({ name, title, isTerminal, folded, displayArgs, argsLanguag
|
|
|
258
270
|
* output, read content — instead of the raw result dump (mirroring Claude Code's `AssistantToolUseMessage.tsx` + the dsh-tools presentation views the
|
|
259
271
|
* channel captures per call).
|
|
260
272
|
*/
|
|
261
|
-
export function AssistantToolUseMessage({ tool, addMargin, verbose, isSelected = false, isExpanded = false, onClick, footnote, diffLayout = 'auto', toolBackground = 'none', onOpenFile, foldTerminalCommand = false, }) {
|
|
273
|
+
export function AssistantToolUseMessage({ tool, addMargin, verbose, isSelected = false, isExpanded = false, onClick, footnote, diffLayout = 'auto', toolBackground = 'none', onOpenFile, foldTerminalCommand = false, smoothReveal = false, fresh = false, }) {
|
|
274
|
+
// Reveal-frame wakeups: the shared scheduler bumps its version, which
|
|
275
|
+
// re-renders this card even while MemoRow props hold still (the reveal
|
|
276
|
+
// lives outside React — see smoothReveal.ts).
|
|
277
|
+
React.useSyncExternalStore(subscribeReveal, getRevealVersion);
|
|
262
278
|
const isRunning = tool.status === 'running';
|
|
263
279
|
const isError = tool.status === 'error';
|
|
264
280
|
const displayArgs = verbose ? tool.argsFull ?? tool.argsText : tool.argsText;
|
|
@@ -323,6 +339,21 @@ export function AssistantToolUseMessage({ tool, addMargin, verbose, isSelected =
|
|
|
323
339
|
// long error body must not be the reason it disappears.
|
|
324
340
|
const lines = capLines(body, cap, verbose);
|
|
325
341
|
const rendered = footnote === undefined ? lines : [...lines, { text: footnote, tone: 'hint' }];
|
|
342
|
+
// Smooth reveal (line-unit, pending CALL body only): model-authored prose
|
|
343
|
+
// (diff hunks, write content) flows in at ~30fps; the settled RESULT view,
|
|
344
|
+
// error bodies, verbose/expanded cards, and replayed (non-fresh) cards all
|
|
345
|
+
// paint complete. `snapReveal` on every non-revealable render retires a
|
|
346
|
+
// cursor the moment its card stops qualifying (result arrived, user
|
|
347
|
+
// expanded) — idempotent, safe during render.
|
|
348
|
+
const revealKey = `tool:${tool.callId}`;
|
|
349
|
+
const revealable = smoothReveal && !isError && isRunning && view !== undefined &&
|
|
350
|
+
tool.resultView === undefined && !verbose && !isExpanded && fresh;
|
|
351
|
+
if (!revealable)
|
|
352
|
+
snapReveal(revealKey);
|
|
353
|
+
const revealedLineCount = revealable
|
|
354
|
+
? revealLinesOf(revealKey, rendered.length, { enabled: true, active: true })
|
|
355
|
+
: rendered.length;
|
|
356
|
+
const shownLines = revealedLineCount >= rendered.length ? rendered : rendered.slice(0, revealedLineCount);
|
|
326
357
|
// Nested split-diff context panes must also yield to interaction highlights.
|
|
327
358
|
// `none` leaves them transparent so the selected/expanded root shows through.
|
|
328
359
|
const ordinaryToolBackground = isSelected || isExpanded ? 'none' : toolBackground;
|
|
@@ -344,7 +375,7 @@ export function AssistantToolUseMessage({ tool, addMargin, verbose, isSelected =
|
|
|
344
375
|
return (_jsx(Box, { ref: viewportRef, flexDirection: "row", justifyContent: "space-between", marginTop: addMargin ? 1 : 0, width: "100%", onClick: onClick,
|
|
345
376
|
// Only selection paints a highlight; the configured treatment applies
|
|
346
377
|
// to an ordinary card. Diff line tints stay - they are content, not chrome.
|
|
347
|
-
backgroundColor: isSelected ? 'messageActionsBackground' : hoverTint ? 'toolCardBackground' : ordinaryBackground, onMouseEnter: interactive ? () => setHovered(true) : undefined, onMouseLeave: interactive ? () => setHovered(false) : undefined, children: _jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { flexDirection: "row", flexWrap: "nowrap", minWidth: minWidth, children: [_jsx(ToolUseLoader, { shouldAnimate: isRunning, isUnresolved: isRunning, isError: isError, toolName: tool.name }), _jsx(HeaderTitle, { name: name, title: headerTitle, isTerminal: headerIsTerminal, folded: foldedHeader, displayArgs: displayArgs, argsLanguage: argsLanguage, nameColor: toolNameColor(tool.name), filePath: filePath, onOpenFile: onOpenFile }), !isRunning && (_jsx(Box, { flexWrap: "nowrap", children: _jsx(Text, { dimColor: !hovered, children: elapsedText }) })), hovered && (_jsx(Box, { flexShrink: 0, children: _jsx(Text, { dimColor: true, children: isExpanded ? '▴' : '▾' }) }))] }), useSplitDiff && view?.card === 'diff' ? (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 3, flexShrink: 0, children: _jsx(Text, { dimColor: true, children: GUTTER_FIRST }) }), _jsx(SplitDiffView, { diffs: view.diffs, width: columns - 4, maxRows: DIFF_BODY_MAX_LINES, verbose: verbose, toolBackground: ordinaryToolBackground })] })) : (
|
|
378
|
+
backgroundColor: isSelected ? 'messageActionsBackground' : hoverTint ? 'toolCardBackground' : ordinaryBackground, onMouseEnter: interactive ? () => setHovered(true) : undefined, onMouseLeave: interactive ? () => setHovered(false) : undefined, children: _jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { flexDirection: "row", flexWrap: "nowrap", minWidth: minWidth, children: [_jsx(ToolUseLoader, { shouldAnimate: isRunning, isUnresolved: isRunning, isError: isError, toolName: tool.name }), _jsx(HeaderTitle, { name: name, title: headerTitle, isTerminal: headerIsTerminal, folded: foldedHeader, displayArgs: displayArgs, argsLanguage: argsLanguage, nameColor: toolNameColor(tool.name), filePath: filePath, onOpenFile: onOpenFile }), !isRunning && (_jsx(Box, { flexWrap: "nowrap", children: _jsx(Text, { dimColor: !hovered, children: elapsedText }) })), hovered && (_jsx(Box, { flexShrink: 0, children: _jsx(Text, { dimColor: true, children: isExpanded ? '▴' : '▾' }) }))] }), useSplitDiff && view?.card === 'diff' ? (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 3, flexShrink: 0, children: _jsx(Text, { dimColor: true, children: GUTTER_FIRST }) }), _jsx(SplitDiffView, { diffs: view.diffs, width: columns - 4, maxRows: DIFF_BODY_MAX_LINES, verbose: verbose, toolBackground: ordinaryToolBackground, reveal: revealable ? { key: `${revealKey}:split` } : undefined })] })) : (shownLines.map((line, index) => (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 3, flexShrink: 0, children: _jsx(Text, { color: line.tone === 'add'
|
|
348
379
|
? 'diffAddedWord'
|
|
349
380
|
: line.tone === 'del'
|
|
350
381
|
? 'diffRemovedWord'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserPromptMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/UserPromptMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"UserPromptMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/UserPromptMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAEjE,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,SAAS,EAAE,OAAO,CAAA;IAClB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;CAClC,CAAA;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,SAAS,EACT,UAAkB,EAClB,OAAO,GACR,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CAmCzB"}
|
|
@@ -3,6 +3,7 @@ import { Box, Text, useTerminalSize } from '../../ui.js';
|
|
|
3
3
|
import { POINTER } from '../../cc/figures.js';
|
|
4
4
|
import { stringWidth } from '../../ink/stringWidth.js';
|
|
5
5
|
import { wrapWidth } from '../../sessions/format.js';
|
|
6
|
+
import { useTooltip } from '../Tooltip.js';
|
|
6
7
|
/**
|
|
7
8
|
* User prompt bubble: `❯ text` in bold briefLabelYou gold with no background
|
|
8
9
|
* fill (Kimi Code style: the user turn gets a distinct bold tint so it reads
|
|
@@ -20,5 +21,10 @@ export function UserPromptMessage({ text, addMargin, isSelected = false, onClick
|
|
|
20
21
|
// Ink; a second wrap would move the continuation back to column zero.
|
|
21
22
|
const lines = wrapWidth(text, Math.max(1, columns - prefixWidth - 3));
|
|
22
23
|
const continuationIndent = ' '.repeat(prefixWidth);
|
|
23
|
-
|
|
24
|
+
// Hover tooltip: a message that wrapped/truncated onto several visual
|
|
25
|
+
// lines pops its full original text in one floating card (the pointer
|
|
26
|
+
// row only shows one visual line at a time).
|
|
27
|
+
const promptTooltip = useTooltip(text);
|
|
28
|
+
const tooltipActive = lines.length > 1;
|
|
29
|
+
return (_jsx(Box, { flexDirection: "column", marginTop: addMargin ? 1 : 0, backgroundColor: isSelected ? 'messageActionsBackground' : undefined, paddingRight: 1, onClick: onClick, ...(tooltipActive ? promptTooltip : {}), children: lines.map((line, index) => (_jsxs(Text, { color: "briefLabelYou", bold: true, wrap: "truncate-end", children: [index === 0 ? `${POINTER} ` : continuationIndent, line] }, index))) }));
|
|
24
30
|
}
|
|
@@ -341,5 +341,5 @@ export function AskUserQuestionPanel({ question, position, total, answered, init
|
|
|
341
341
|
...(onBack === undefined ? [] : [t('question-hint-cancel')]),
|
|
342
342
|
...(multiSelect && checked.size > 0 ? [t('question-hint-selected', { n: checked.size })] : []),
|
|
343
343
|
];
|
|
344
|
-
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, paddingRight: 2, width: "100%", children: [_jsx(Divider, { color: "permission", title: headerTitle
|
|
344
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, paddingRight: 2, width: "100%", children: [_jsx(Divider, { color: "permission", title: headerTitle }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [question.header !== undefined && (_jsxs(Text, { color: "suggestion", bold: true, children: ["\u25C8 ", question.header] })), _jsx(Text, { bold: true, wrap: "wrap", children: question.question }), question.detail !== undefined && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: question.detail.split('\n').map((line, index) => (_jsx(Text, { dimColor: true, italic: true, wrap: "wrap", children: line }, index))) }))] }), renderOptions(), error !== null && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "error", children: error }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: hintParts.join(' · ') }) })] }));
|
|
345
345
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlanReviewPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/PlanReviewPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAKvE,MAAM,MAAM,oBAAoB,GAAG;IACjC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;QACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;YAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC3F,QAAQ,CAAC,MAAM,CAAC,EAAE;YAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;YAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAA;KAC7E,CAAA;IACD,QAAQ,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACzD,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;CAC9B,CAAA;AAED,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,QAAQ,EACR,QAAQ,GACT,EAAE,oBAAoB,GAAG,KAAK,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"PlanReviewPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/PlanReviewPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAKvE,MAAM,MAAM,oBAAoB,GAAG;IACjC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;QACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;YAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC3F,QAAQ,CAAC,MAAM,CAAC,EAAE;YAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;YAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAA;KAC7E,CAAA;IACD,QAAQ,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACzD,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;CAC9B,CAAA;AAED,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,QAAQ,EACR,QAAQ,GACT,EAAE,oBAAoB,GAAG,KAAK,CAAC,SAAS,CAiSxC"}
|
|
@@ -17,7 +17,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
17
17
|
*/
|
|
18
18
|
import React from 'react';
|
|
19
19
|
import { t } from '../../i18n.js';
|
|
20
|
-
import { Box, Text, useInput } from '../../ui.js';
|
|
20
|
+
import { Box, Text, useInput, ScrollBox, useTerminalSize } from '../../ui.js';
|
|
21
21
|
import { useDeclaredCursor } from '../../ink/hooks/use-declared-cursor.js';
|
|
22
22
|
import { Divider } from '../design-system/Divider.js';
|
|
23
23
|
import { Markdown } from '../Markdown.js';
|
|
@@ -34,6 +34,23 @@ export function PlanReviewPanel({ question, onAnswer, onCancel, }) {
|
|
|
34
34
|
const [feedback, setFeedback] = React.useState('');
|
|
35
35
|
const [cursor, setCursor] = React.useState(0);
|
|
36
36
|
const [error, setError] = React.useState(null);
|
|
37
|
+
const { rows: terminalRows } = useTerminalSize();
|
|
38
|
+
// Chat chrome (status line) + panel scaffolding (divider, question,
|
|
39
|
+
// spacings, hint) consume twelve rows before the plan body — same
|
|
40
|
+
// constant as AskUserQuestionPanel. Option/feedback/error rows are
|
|
41
|
+
// charged on top so a long markdown detail cannot push them off-screen.
|
|
42
|
+
// A definite `height` (not just maxHeight) is required: otherwise the
|
|
43
|
+
// ScrollBox grows with the markdown and scrollBy is a no-op.
|
|
44
|
+
const optionRows = options.reduce((sum, option) => sum + (option.description === undefined ? 1 : 2), 0) + 1 /* extra gap on the focused option */ + 1; /* feedback row */
|
|
45
|
+
const reservedRows = 12 + optionRows + (error === null ? 0 : 2);
|
|
46
|
+
// Floor at zero, not four: on a short terminal the decision rows fill the
|
|
47
|
+
// budget and no plan-body rows remain. Forcing four here would re-inflate
|
|
48
|
+
// the panel past the viewport and push the controls off-screen again — the
|
|
49
|
+
// exact regression this panel exists to prevent. When nothing remains, the
|
|
50
|
+
// body viewport is omitted entirely (a zero-height ScrollBox is neither
|
|
51
|
+
// readable nor a useful wheel target).
|
|
52
|
+
const detailMax = Math.max(0, terminalRows - reservedRows);
|
|
53
|
+
const detailScrollRef = React.useRef(null);
|
|
37
54
|
const inputFocused = focusIndex === options.length;
|
|
38
55
|
// Park the native terminal cursor on the feedback caret so IME preedit
|
|
39
56
|
// (pinyin) renders inline at the input instead of the screen's bottom row
|
|
@@ -87,7 +104,16 @@ export function PlanReviewPanel({ question, onAnswer, onCancel, }) {
|
|
|
87
104
|
const text = feedback.trim();
|
|
88
105
|
onAnswer({ selected: declineSelected(), ...(text !== '' ? { custom: text } : {}) });
|
|
89
106
|
};
|
|
90
|
-
useInput((input, key) => {
|
|
107
|
+
useInput((input, key, event) => {
|
|
108
|
+
// Steal the wheel while this panel is mounted so Chat's fallback cannot
|
|
109
|
+
// scroll the transcript. Position-first already hits the ScrollBox when
|
|
110
|
+
// the pointer is over it; this covers the option/hint rows and any
|
|
111
|
+
// dispatchWheelAt miss. Up/Down stay on the decision rows.
|
|
112
|
+
if (key.wheelUp || key.wheelDown) {
|
|
113
|
+
detailScrollRef.current?.scrollBy(key.wheelUp ? -3 : 3);
|
|
114
|
+
event.stopImmediatePropagation();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
91
117
|
if (key.escape || (key.ctrl && input === 'c')) {
|
|
92
118
|
onCancel();
|
|
93
119
|
return;
|
|
@@ -179,7 +205,7 @@ export function PlanReviewPanel({ question, onAnswer, onCancel, }) {
|
|
|
179
205
|
setFocusIndex(options.length);
|
|
180
206
|
setError(null);
|
|
181
207
|
};
|
|
182
|
-
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, paddingRight: 2, width: "100%", children: [_jsx(Divider, { color: "permission", title: ` ${question.header ?? t('plan-review-fallback-header')}
|
|
208
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, paddingRight: 2, width: "100%", children: [_jsx(Divider, { color: "permission", title: ` ${question.header ?? t('plan-review-fallback-header')} ` }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { bold: true, wrap: "wrap", children: question.question }), question.detail !== undefined && detailMax > 0 && (_jsx(Box, { flexDirection: "column", marginTop: 1, height: detailMax, flexShrink: 0, children: _jsx(ScrollBox, { ref: detailScrollRef, flexDirection: "column", flexGrow: 1, height: detailMax, children: _jsx(Markdown, { children: question.detail }) }) }))] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [options.map((option, index) => {
|
|
183
209
|
const focused = index === focusIndex;
|
|
184
210
|
const isApprove = option.label === approveLabel;
|
|
185
211
|
return (_jsxs(Box, { flexDirection: "row", marginTop: focused ? 1 : 0, onClick: () => clickOption(index), onMouseEnter: () => setHoverIndex(index), onMouseLeave: () => setHoverIndex(current => (current === index ? -1 : current)), backgroundColor: hoverIndex === index && !focused ? 'userMessageBackgroundHover' : undefined, children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: focused ? 'claude' : undefined, bold: focused, children: focused ? POINTER : ' ' }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [_jsxs(Text, { bold: focused, color: focused || isApprove ? 'claude' : undefined, wrap: "wrap", children: [index + 1, ". ", option.label] }), option.description !== undefined && (_jsx(Text, { dimColor: true, wrap: "wrap", children: option.description }))] })] }, option.label));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { ClickEvent } from '../../ink/events/click-event.js';
|
|
3
|
+
import type { ContextMenuEvent } from '../../ink/events/context-menu-event.js';
|
|
3
4
|
import type { SessionSummary } from '../../dsh-adapter/sessions/index.js';
|
|
4
5
|
/**
|
|
5
6
|
* One session in the browser's list: a title line and a metadata line.
|
|
@@ -14,16 +15,22 @@ import type { SessionSummary } from '../../dsh-adapter/sessions/index.js';
|
|
|
14
15
|
* stay exactly two lines at every terminal width, and a row that wraps
|
|
15
16
|
* destroys the alignment that lets the eye scan a list at all.
|
|
16
17
|
*/
|
|
17
|
-
export declare function SessionListRow({ session, width, depth, focused, now, onClick, }: {
|
|
18
|
+
export declare function SessionListRow({ session, width, depth, focused, pinned, now, onClick, onContextMenu, onTogglePin, }: {
|
|
18
19
|
session: SessionSummary;
|
|
19
20
|
/** Columns available to the row, indentation included. */
|
|
20
21
|
width: number;
|
|
21
22
|
/** 0 for a conversation, 1 for a sub-agent run under its parent. */
|
|
22
23
|
depth: number;
|
|
23
24
|
focused: boolean;
|
|
25
|
+
/** Whether the user pinned this session to the top of the browser. */
|
|
26
|
+
pinned: boolean;
|
|
24
27
|
/** Epoch ms used for every relative time in this render pass. */
|
|
25
28
|
now: number;
|
|
26
29
|
/** 鼠标点击行(fullscreen):恢复该会话(与 Enter 同路径)。 */
|
|
27
30
|
onClick?(event: ClickEvent): void;
|
|
31
|
+
/** 鼠标右键(fullscreen):在该行弹出操作菜单(打开/固定/重命名/删除)。 */
|
|
32
|
+
onContextMenu?(event: ContextMenuEvent): void;
|
|
33
|
+
/** 点击行内 ★/☆(fullscreen):切换固定状态,不冒泡成"打开会话"。 */
|
|
34
|
+
onTogglePin?(): void;
|
|
28
35
|
}): React.ReactNode;
|
|
29
36
|
//# sourceMappingURL=SessionListRow.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionListRow.d.ts","sourceRoot":"","sources":["../../../../src/components/sessions/SessionListRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAGvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;
|
|
1
|
+
{"version":3,"file":"SessionListRow.d.ts","sourceRoot":"","sources":["../../../../src/components/sessions/SessionListRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAGvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAA;AAS9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AAEzE;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,KAAK,EACL,KAAK,EACL,OAAO,EACP,MAAM,EACN,GAAG,EACH,OAAO,EACP,aAAa,EACb,WAAW,GACZ,EAAE;IACD,OAAO,EAAE,cAAc,CAAA;IACvB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAA;IACb,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;IAChB,sEAAsE;IACtE,MAAM,EAAE,OAAO,CAAA;IACf,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAA;IACX,4CAA4C;IAC5C,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IACjC,gDAAgD;IAChD,aAAa,CAAC,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAA;IAC7C,8CAA8C;IAC9C,WAAW,CAAC,IAAI,IAAI,CAAA;CACrB,GAAG,KAAK,CAAC,SAAS,CA8DlB"}
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { Box, Text } from '../../ui.js';
|
|
4
4
|
import { t } from '../../i18n.js';
|
|
5
|
+
import { useTooltip } from '../Tooltip.js';
|
|
5
6
|
import { formatBytes, formatWhen, kindMark, titleColor, truncateWidth, } from '../../sessions/format.js';
|
|
6
7
|
/**
|
|
7
8
|
* One session in the browser's list: a title line and a metadata line.
|
|
@@ -16,12 +17,13 @@ import { formatBytes, formatWhen, kindMark, titleColor, truncateWidth, } from '.
|
|
|
16
17
|
* stay exactly two lines at every terminal width, and a row that wraps
|
|
17
18
|
* destroys the alignment that lets the eye scan a list at all.
|
|
18
19
|
*/
|
|
19
|
-
export function SessionListRow({ session, width, depth, focused, now, onClick, }) {
|
|
20
|
+
export function SessionListRow({ session, width, depth, focused, pinned, now, onClick, onContextMenu, onTogglePin, }) {
|
|
20
21
|
const indent = depth * 2;
|
|
21
22
|
// Two cells for the focus marker, plus the indent for a nested run.
|
|
22
23
|
const body = Math.max(8, width - 2 - indent);
|
|
23
24
|
const mark = kindMark(session.kind);
|
|
24
25
|
const [hovered, setHovered] = useState(false);
|
|
26
|
+
const pinTooltip = useTooltip(t(pinned ? 'resume-menu-unpin' : 'resume-menu-pin'));
|
|
25
27
|
const facts = [formatWhen(session.updatedAt, now)];
|
|
26
28
|
if (session.branch !== undefined)
|
|
27
29
|
facts.push(session.branch);
|
|
@@ -33,5 +35,10 @@ export function SessionListRow({ session, width, depth, focused, now, onClick, }
|
|
|
33
35
|
if (session.childCount > 0 && depth === 0) {
|
|
34
36
|
facts.push(t('session-children', { n: session.childCount }));
|
|
35
37
|
}
|
|
36
|
-
return (_jsxs(Box, { flexDirection: "column", flexShrink: 0, onClick: onClick, onMouseEnter: onClick !== undefined ? () => setHovered(true) : undefined, onMouseLeave: onClick !== undefined ? () => setHovered(false) : undefined, backgroundColor:
|
|
38
|
+
return (_jsxs(Box, { flexDirection: "column", flexShrink: 0, onClick: onClick, onContextMenu: onContextMenu, onMouseEnter: onClick !== undefined || onContextMenu !== undefined ? () => setHovered(true) : undefined, onMouseLeave: onClick !== undefined || onContextMenu !== undefined ? () => setHovered(false) : undefined, backgroundColor: focused || hovered ? 'userMessageBackgroundHover' : undefined, children: [_jsxs(Box, { children: [_jsx(Text, { color: focused ? 'suggestion' : 'subtle', children: `${' '.repeat(indent)}${focused ? '❯ ' : ' '}` }), _jsx(Box, { ...pinTooltip, onClick: onTogglePin === undefined ? undefined : (event) => {
|
|
39
|
+
// The star is a control on the row, not the row: a click here
|
|
40
|
+
// toggles the pin and must never fall through to resume.
|
|
41
|
+
event.stopImmediatePropagation();
|
|
42
|
+
onTogglePin();
|
|
43
|
+
}, children: _jsx(Text, { color: pinned ? 'remember' : undefined, dimColor: !pinned, children: pinned ? '★ ' : '☆ ' }) }), mark !== undefined && _jsx(Text, { color: mark.color, children: `${mark.glyph} ` }), _jsx(Text, { color: titleColor(session.title.source, focused), bold: focused, children: truncateWidth(session.label ?? session.title.text, body - 2 - (mark === undefined ? 0 : 2)) })] }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: `${' '.repeat(indent + 2)}${truncateWidth(facts.join(' · '), body)}` }) })] }));
|
|
37
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionPreview.d.ts","sourceRoot":"","sources":["../../../../src/components/sessions/SessionPreview.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AAQvF;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,OAAO,EACP,OAAO,EACP,KAAK,EACL,MAAM,EACN,IAAI,EACJ,GAAG,GACJ,EAAE;IACD,OAAO,EAAE,cAAc,CAAA;IACvB,OAAO,EAAE,SAAS,YAAY,EAAE,CAAA;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;CACZ,GAAG,KAAK,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"SessionPreview.d.ts","sourceRoot":"","sources":["../../../../src/components/sessions/SessionPreview.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AAQvF;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,OAAO,EACP,OAAO,EACP,KAAK,EACL,MAAM,EACN,IAAI,EACJ,GAAG,GACJ,EAAE;IACD,OAAO,EAAE,cAAc,CAAA;IACvB,OAAO,EAAE,SAAS,YAAY,EAAE,CAAA;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;CACZ,GAAG,KAAK,CAAC,SAAS,CA2ElB"}
|
|
@@ -21,7 +21,9 @@ const ROLE = {
|
|
|
21
21
|
* session is the same amount of work as on a 40 KB one.
|
|
22
22
|
*/
|
|
23
23
|
export function SessionPreview({ session, entries, loading, width, height, home, now, }) {
|
|
24
|
-
const
|
|
24
|
+
const framed = width >= 24 && height >= 4;
|
|
25
|
+
const body = Math.max(8, width - (framed ? 4 : 2));
|
|
26
|
+
const bodyHeight = Math.max(0, height - (framed ? 2 : 0));
|
|
25
27
|
// The pane is a fixed-height box, so its content is laid out as a flat list
|
|
26
28
|
// of lines and cut to fit. Letting several adaptive paragraphs share a fixed
|
|
27
29
|
// box lets them overlap once their natural height exceeds it.
|
|
@@ -55,6 +57,6 @@ export function SessionPreview({ session, entries, loading, width, height, home,
|
|
|
55
57
|
}
|
|
56
58
|
// Keep the newest content: an overlong preview is cut at the TOP, so the
|
|
57
59
|
// last thing said is always the last thing visible.
|
|
58
|
-
const visible = lines.length >
|
|
59
|
-
return (_jsx(Box, { flexDirection: "column", width: width, height: height, flexShrink: 0, paddingLeft: 2, children: visible }));
|
|
60
|
+
const visible = lines.length > bodyHeight ? lines.slice(lines.length - bodyHeight) : lines;
|
|
61
|
+
return (_jsx(Box, { flexDirection: "column", width: width, height: height, flexShrink: 0, borderStyle: framed ? 'round' : undefined, borderColor: framed ? 'permission' : undefined, borderDimColor: true, paddingX: framed ? 1 : 0, paddingLeft: framed ? 1 : 2, overflow: "hidden", children: visible }));
|
|
60
62
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ClickEvent } from '../../ink/events/click-event.js';
|
|
3
|
+
import type { WorkspaceGroup } from '../../sessions/view.js';
|
|
4
|
+
/** One two-line working-directory choice in the /resume browser. */
|
|
5
|
+
export declare function WorkspaceListRow({ workspace, all, totalProjects, totalSessions, width, focused, selected, home, now, onClick, }: {
|
|
6
|
+
workspace?: WorkspaceGroup;
|
|
7
|
+
all?: boolean;
|
|
8
|
+
totalProjects: number;
|
|
9
|
+
totalSessions: number;
|
|
10
|
+
width: number;
|
|
11
|
+
focused: boolean;
|
|
12
|
+
selected: boolean;
|
|
13
|
+
home: string;
|
|
14
|
+
now: number;
|
|
15
|
+
onClick?(event: ClickEvent): void;
|
|
16
|
+
}): React.ReactNode;
|
|
17
|
+
//# sourceMappingURL=WorkspaceListRow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WorkspaceListRow.d.ts","sourceRoot":"","sources":["../../../../src/components/sessions/WorkspaceListRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAGvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAEjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAE5D,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,EAC/B,SAAS,EACT,GAAG,EACH,aAAa,EACb,aAAa,EACb,KAAK,EACL,OAAO,EACP,QAAQ,EACR,IAAI,EACJ,GAAG,EACH,OAAO,GACR,EAAE;IACD,SAAS,CAAC,EAAE,cAAc,CAAA;IAC1B,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;CAClC,GAAG,KAAK,CAAC,SAAS,CAwClB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Box, Text } from '../../ui.js';
|
|
4
|
+
import { t } from '../../i18n.js';
|
|
5
|
+
import { formatProject, formatWhen, projectName, spreadRow, truncateWidth } from '../../sessions/format.js';
|
|
6
|
+
/** One two-line working-directory choice in the /resume browser. */
|
|
7
|
+
export function WorkspaceListRow({ workspace, all, totalProjects, totalSessions, width, focused, selected, home, now, onClick, }) {
|
|
8
|
+
const [hovered, setHovered] = useState(false);
|
|
9
|
+
const body = Math.max(8, width - 4);
|
|
10
|
+
const count = all ? totalSessions : workspace?.count ?? 0;
|
|
11
|
+
const title = all ? t('session-workspace-all') : projectName(workspace?.cwd ?? '');
|
|
12
|
+
const badge = all
|
|
13
|
+
? t('session-workspace-project-count', { n: totalProjects })
|
|
14
|
+
: workspace?.current
|
|
15
|
+
? `${t('session-workspace-current')} · ${t('session-count-shown', { n: count })}`
|
|
16
|
+
: t('session-count-shown', { n: count });
|
|
17
|
+
const heading = spreadRow(`${all ? '◎' : '▣'} ${title}`, `${selected ? '✓ ' : ''}${badge}`, body);
|
|
18
|
+
const detail = all
|
|
19
|
+
? t('session-workspace-all-detail', { n: count })
|
|
20
|
+
: [
|
|
21
|
+
formatProject(workspace?.cwd ?? '', home),
|
|
22
|
+
count > 0 && workspace !== undefined
|
|
23
|
+
? formatWhen(workspace.updatedAt, now)
|
|
24
|
+
: t('session-workspace-empty'),
|
|
25
|
+
].filter(Boolean).join(' · ');
|
|
26
|
+
return (_jsxs(Box, { flexDirection: "column", flexShrink: 0, paddingX: 1, onClick: onClick, onMouseEnter: onClick === undefined ? undefined : () => setHovered(true), onMouseLeave: onClick === undefined ? undefined : () => setHovered(false), backgroundColor: focused || hovered ? 'userMessageBackgroundHover' : undefined, children: [_jsxs(Box, { height: 1, flexShrink: 0, overflow: "hidden", children: [_jsx(Text, { color: focused ? 'suggestion' : 'subtle', children: focused ? '❯ ' : ' ' }), _jsx(Text, { color: focused ? 'suggestion' : selected ? 'success' : 'text', bold: focused || selected, children: heading.left }), _jsx(Text, { dimColor: !focused, children: `${' '.repeat(heading.gap)}${heading.right}` })] }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: ` ${truncateWidth(detail, body - 2)}` })] }));
|
|
27
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smooth streaming reveal — decouples "text the provider has delivered" from
|
|
3
|
+
* "text the screen is showing" (the oh-my-pi `display.smoothStreaming` idea,
|
|
4
|
+
* see their `StreamingRevealController`). A shared ~30fps scheduler advances
|
|
5
|
+
* each reveal cursor by an adaptive catch-up step, so burst-delivered text (a
|
|
6
|
+
* slow provider dumping a large batch, or a non-streaming response arriving
|
|
7
|
+
* whole) paints as a brief even flow instead of one jarring jump, while
|
|
8
|
+
* genuinely streaming text is re-paced to a steady cadence regardless of
|
|
9
|
+
* chunk timing.
|
|
10
|
+
*
|
|
11
|
+
* Step math (per frame): `max(MIN_STEP, ceil(backlog / CATCHUP_FRAMES))` —
|
|
12
|
+
* an EXPONENTIAL decay over the backlog (each frame removes ~1/8 of what is
|
|
13
|
+
* still unrevealed, the oh-my-pi semantics exactly): a large burst lands its
|
|
14
|
+
* first three quarters within a few frames, the remainder tapers, and the
|
|
15
|
+
* MIN_STEP floor turns the tail into a steady linear crawl so the reveal
|
|
16
|
+
* always finishes. A ~2000-character one-shot delivery paints fully in
|
|
17
|
+
* roughly 1.5s; genuinely streaming text (small backlogs) paces at a steady
|
|
18
|
+
* per-frame rate. The reveal never runs ahead of what has arrived.
|
|
19
|
+
*
|
|
20
|
+
* ARCHITECTURE — module-level cursors, not component state. dsh-tui's
|
|
21
|
+
* transcript virtualization feeds on MemoRow prop changes (flattened
|
|
22
|
+
* primitives) and re-measures mounted rows after every MessageList commit;
|
|
23
|
+
* a reveal living in child-component state would change row heights without
|
|
24
|
+
* touching those props, and the memo/signature/height-measure chain would
|
|
25
|
+
* never notice (stale cached heights → blank bands). Instead the cursors
|
|
26
|
+
* live here keyed by row identity, the READS happen during render (they feed
|
|
27
|
+
* the same flattened `text` prop the streaming path already uses), and the
|
|
28
|
+
* WRITES happen on the scheduler tick, which bumps a version that
|
|
29
|
+
* MessageList subscribes to via useSyncExternalStore. A reveal therefore
|
|
30
|
+
* rides the exact same update pipeline as an arriving chunk: prop change →
|
|
31
|
+
* memo miss → re-render → post-commit measurement. Render-phase reads are
|
|
32
|
+
* idempotent and safe to re-run (a discarded concurrent render at worst
|
|
33
|
+
* creates a cursor the tick soon retires).
|
|
34
|
+
*
|
|
35
|
+
* Unit model: reveal positions are UTF-16 code units (Chinese text is one
|
|
36
|
+
* unit per character). Surrogate pairs are never split (see {@link
|
|
37
|
+
* safeSliceEnd}); grapheme clusters longer than a surrogate pair (emoji ZWJ
|
|
38
|
+
* chains) may briefly show a partial cluster mid-reveal — transient frames
|
|
39
|
+
* repaint within 33ms, and settled rendering is always the full text.
|
|
40
|
+
*/
|
|
41
|
+
/** Reveal tick cadence — ~30fps, matching oh-my-pi's controller. */
|
|
42
|
+
export declare const REVEAL_FRAME_MS: number;
|
|
43
|
+
/** Minimum units (code units or lines) advanced per tick while work remains. */
|
|
44
|
+
export declare const REVEAL_MIN_STEP = 3;
|
|
45
|
+
/** Catch-up bound: the reveal settles any backlog within this many frames. */
|
|
46
|
+
export declare const REVEAL_CATCHUP_FRAMES = 8;
|
|
47
|
+
/** Adaptive per-frame step for a backlog (exported for tests). */
|
|
48
|
+
export declare function revealStep(backlog: number): number;
|
|
49
|
+
/** Subscribe to reveal-frame wakeups (MessageList + tool cards). */
|
|
50
|
+
export declare function subscribeReveal(listener: () => void): () => void;
|
|
51
|
+
/** Snapshot for useSyncExternalStore; bumps once per advancing tick. */
|
|
52
|
+
export declare function getRevealVersion(): number;
|
|
53
|
+
/** Force-complete (and forget) a cursor: expanded card, replaced view, … */
|
|
54
|
+
export declare function snapReveal(key: string): void;
|
|
55
|
+
export type RevealReadOptions = {
|
|
56
|
+
/** Master switch (settings `dsh-tui.smoothStreaming`); false = full text. */
|
|
57
|
+
enabled: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Whether this content arrived "just now" (live streaming row, freshly
|
|
60
|
+
* settled row, live-created tool card). The FIRST read with active=true
|
|
61
|
+
* creates the cursor at zero — that is what turns a one-shot
|
|
62
|
+
* non-streaming delivery into a smooth paint. Reading with active=false
|
|
63
|
+
* (history / replayed content) never creates one: replaying a transcript
|
|
64
|
+
* must not typewrite every row. `active` only matters for cursor creation;
|
|
65
|
+
* settling mid-reveal deliberately keeps revealing (the "non-streaming
|
|
66
|
+
* becomes streaming" contract).
|
|
67
|
+
*/
|
|
68
|
+
active: boolean;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Revealed length of `text` for `key` — the cursor logic of
|
|
72
|
+
* {@link revealTextOf} without the slice allocation (layout signatures read
|
|
73
|
+
* this every render for every visible row).
|
|
74
|
+
*/
|
|
75
|
+
export declare function revealLengthOf(key: string, text: string, options: RevealReadOptions): number;
|
|
76
|
+
/**
|
|
77
|
+
* Revealed prefix of `text` for `key`. Monotonic appends (streaming deltas)
|
|
78
|
+
* keep the cursor; a non-prefix replacement (rewind, folded preview
|
|
79
|
+
* truncation) snaps to the new text — never animate across a discontinuity
|
|
80
|
+
* the reader did not witness as streaming. Feed the result into the
|
|
81
|
+
* streaming renderer (StreamingMarkdown); the monotonic prefix it receives
|
|
82
|
+
* keeps its stable-prefix memoization fully effective.
|
|
83
|
+
*/
|
|
84
|
+
export declare function revealTextOf(key: string, text: string, options: RevealReadOptions): string;
|
|
85
|
+
/**
|
|
86
|
+
* Revealed unit count for `key` over `total` units (tool-card body lines,
|
|
87
|
+
* split-diff rows). A SMALLER total always snaps (content that shrank is a
|
|
88
|
+
* replacement, not an append); growth keeps the cursor. Callers own the
|
|
89
|
+
* append-vs-replace decision for growing totals (pass a fresh key or call
|
|
90
|
+
* {@link snapReveal} when the content behind the same key is replaced).
|
|
91
|
+
*/
|
|
92
|
+
export declare function revealLinesOf(key: string, total: number, options: RevealReadOptions): number;
|
|
93
|
+
/** Clear every cursor/listener/timer (verify scripts between cases). */
|
|
94
|
+
export declare function resetRevealForTest(): void;
|
|
95
|
+
/** Whether the shared scheduler currently runs (refcount assertions). */
|
|
96
|
+
export declare function isRevealTimerRunning(): boolean;
|
|
97
|
+
//# sourceMappingURL=smoothReveal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smoothReveal.d.ts","sourceRoot":"","sources":["../../../src/components/smoothReveal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,oEAAoE;AACpE,eAAO,MAAM,eAAe,QAAY,CAAA;AACxC,gFAAgF;AAChF,eAAO,MAAM,eAAe,IAAI,CAAA;AAChC,8EAA8E;AAC9E,eAAO,MAAM,qBAAqB,IAAI,CAAA;AAEtC,kEAAkE;AAClE,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAElD;AAwGD,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAKhE;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE5C;AAMD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,6EAA6E;IAC7E,OAAO,EAAE,OAAO,CAAA;IAChB;;;;;;;;;OASG;IACH,MAAM,EAAE,OAAO,CAAA;CAChB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAiC5F;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAI1F;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAqC5F;AAMD,wEAAwE;AACxE,wBAAgB,kBAAkB,IAAI,IAAI,CAUzC;AAED,yEAAyE;AACzE,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C"}
|