@deepseek-harness-tui/dsh-tui 0.8.1 → 0.8.3
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 +30 -15
- package/bin/dsh-tui.js +43 -7
- package/dsh-ecosystem-spec/protocols/profile-definitions.d.ts +13 -0
- package/dsh-ecosystem-spec/protocols/profile-definitions.js +59 -0
- package/dsh-ecosystem-spec/protocols/tui-channel-http.d.ts +15 -0
- package/dsh-ecosystem-spec/protocols/tui-channel-http.js +32 -0
- package/dsh-ecosystem-spec/protocols/tui-channel.d.ts +59 -0
- package/dsh-ecosystem-spec/protocols/tui-channel.js +166 -0
- package/dsh-ecosystem-spec/protocols/tui-contributions.d.ts +42 -0
- package/dsh-ecosystem-spec/protocols/tui-contributions.js +143 -0
- package/dsh-ecosystem-spec/registry/README.md +14 -0
- package/dsh-ecosystem-spec/registry/contracts/scene-v1alpha1.json +13 -0
- package/dsh-ecosystem-spec/registry/contracts/settings-section-v1alpha1.json +13 -0
- package/dsh-ecosystem-spec/registry/contracts/tui-channel-v1alpha1.json +30 -0
- package/dsh-ecosystem-spec/registry/contracts/workspace-provider-v1alpha1.json +6 -0
- package/{ecosystem-spec → dsh-ecosystem-spec}/registry/permissions-0.1.json +1 -1
- package/dsh-ecosystem-spec/registry/registry-0.15.json +149 -0
- package/lib/types/command-trees.d.ts +2 -1
- package/lib/types/command-trees.d.ts.map +1 -1
- package/lib/types/command-trees.js +1 -1
- package/lib/types/components/LoadedContextPanel.d.ts +10 -5
- package/lib/types/components/LoadedContextPanel.d.ts.map +1 -1
- package/lib/types/components/LoadedContextPanel.js +18 -7
- package/lib/types/components/LogoV2.d.ts.map +1 -1
- package/lib/types/components/LogoV2.js +16 -7
- package/lib/types/components/Markdown.d.ts +13 -1
- package/lib/types/components/Markdown.d.ts.map +1 -1
- package/lib/types/components/Markdown.js +15 -1
- package/lib/types/components/MessageList.d.ts +3 -1
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +90 -9
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +26 -12
- package/lib/types/components/StreamingMarkdown.d.ts +2 -8
- package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
- package/lib/types/components/StreamingMarkdown.js +51 -9
- package/lib/types/components/activityFrames.d.ts +1 -1
- package/lib/types/components/activityFrames.d.ts.map +1 -1
- package/lib/types/components/activityFrames.js +4 -1
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts +5 -1
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.js +26 -3
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +12 -1
- package/lib/types/dsh-adapter/channel.d.ts +31 -4
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +267 -102
- package/lib/types/dsh-adapter/childStderr.d.ts.map +1 -1
- package/lib/types/dsh-adapter/childStderr.js +10 -0
- package/lib/types/dsh-adapter/command-attribution.d.ts.map +1 -1
- package/lib/types/dsh-adapter/command-attribution.js +2 -2
- package/lib/types/dsh-adapter/command-trees.d.ts +7 -1
- package/lib/types/dsh-adapter/command-trees.d.ts.map +1 -1
- package/lib/types/dsh-adapter/command-trees.js +70 -20
- package/lib/types/dsh-adapter/component-identity.d.ts +5 -0
- package/lib/types/dsh-adapter/component-identity.d.ts.map +1 -1
- package/lib/types/dsh-adapter/component-identity.js +14 -14
- package/lib/types/dsh-adapter/decision-guard.d.ts.map +1 -1
- package/lib/types/dsh-adapter/decision-guard.js +10 -16
- package/lib/types/dsh-adapter/dialogs.d.ts +2 -3
- package/lib/types/dsh-adapter/dialogs.d.ts.map +1 -1
- package/lib/types/dsh-adapter/dialogs.js +114 -60
- package/lib/types/dsh-adapter/effect-ledger.d.ts +0 -8
- package/lib/types/dsh-adapter/effect-ledger.d.ts.map +1 -1
- package/lib/types/dsh-adapter/effect-ledger.js +73 -53
- package/lib/types/dsh-adapter/extension-events.d.ts.map +1 -1
- package/lib/types/dsh-adapter/extension-events.js +39 -1
- package/lib/types/dsh-adapter/grants.d.ts.map +1 -1
- package/lib/types/dsh-adapter/grants.js +43 -14
- package/lib/types/dsh-adapter/host-access.d.ts +37 -0
- package/lib/types/dsh-adapter/host-access.d.ts.map +1 -0
- package/lib/types/dsh-adapter/host-access.js +652 -0
- package/lib/types/dsh-adapter/host-descriptor.d.ts +2 -2
- package/lib/types/dsh-adapter/host-descriptor.d.ts.map +1 -1
- package/lib/types/dsh-adapter/host-descriptor.js +31 -8
- package/lib/types/dsh-adapter/index.d.ts +4 -0
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +1 -0
- package/lib/types/dsh-adapter/message-observer.d.ts +10 -15
- package/lib/types/dsh-adapter/message-observer.d.ts.map +1 -1
- package/lib/types/dsh-adapter/message-observer.js +114 -70
- package/lib/types/dsh-adapter/plugin-host.d.ts +39 -6
- package/lib/types/dsh-adapter/plugin-host.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin-host.js +139 -44
- package/lib/types/dsh-adapter/plugin-storage.d.ts +0 -5
- package/lib/types/dsh-adapter/plugin-storage.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin-storage.js +48 -36
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +97 -16
- package/lib/types/dsh-adapter/promptDebug.d.ts +0 -6
- package/lib/types/dsh-adapter/promptDebug.d.ts.map +1 -1
- package/lib/types/dsh-adapter/promptDebug.js +13 -4
- package/lib/types/dsh-adapter/renderers.d.ts +6 -14
- package/lib/types/dsh-adapter/renderers.d.ts.map +1 -1
- package/lib/types/dsh-adapter/renderers.js +81 -46
- package/lib/types/dsh-adapter/scenes.d.ts +8 -4
- package/lib/types/dsh-adapter/scenes.d.ts.map +1 -1
- package/lib/types/dsh-adapter/scenes.js +136 -37
- package/lib/types/dsh-adapter/settings-sections.d.ts +7 -3
- package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
- package/lib/types/dsh-adapter/settings-sections.js +106 -26
- package/lib/types/dsh-adapter/shortcuts.d.ts +8 -10
- package/lib/types/dsh-adapter/shortcuts.d.ts.map +1 -1
- package/lib/types/dsh-adapter/shortcuts.js +108 -36
- package/lib/types/dsh-adapter/status.d.ts +6 -7
- package/lib/types/dsh-adapter/status.d.ts.map +1 -1
- package/lib/types/dsh-adapter/status.js +88 -22
- package/lib/types/dsh-adapter/workspaces.d.ts +16 -5
- package/lib/types/dsh-adapter/workspaces.d.ts.map +1 -1
- package/lib/types/dsh-adapter/workspaces.js +287 -170
- package/lib/types/extensions.d.ts +10 -5
- package/lib/types/extensions.d.ts.map +1 -1
- package/lib/types/extensions.js +11 -5
- package/lib/types/force-production-react.d.ts +1 -0
- package/lib/types/force-production-react.d.ts.map +1 -0
- package/lib/types/force-production-react.js +12 -0
- package/lib/types/i18n.d.ts +22 -6
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +14 -2
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +1 -0
- package/lib/types/ink/components/App.d.ts +2 -0
- package/lib/types/ink/components/App.d.ts.map +1 -1
- package/lib/types/ink/components/App.js +4 -0
- package/lib/types/ink/ink.d.ts +20 -3
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +57 -5
- package/lib/types/ink/log-update.d.ts.map +1 -1
- package/lib/types/ink/log-update.js +128 -9
- package/lib/types/ink/output.d.ts +28 -2
- package/lib/types/ink/output.d.ts.map +1 -1
- package/lib/types/ink/output.js +158 -12
- package/lib/types/ink/screen.d.ts +39 -0
- package/lib/types/ink/screen.d.ts.map +1 -1
- package/lib/types/ink/screen.js +117 -0
- package/lib/types/ink/terminal.d.ts.map +1 -1
- package/lib/types/ink/terminal.js +10 -2
- package/lib/types/ink/wrapAnsi.d.ts +1 -1
- package/lib/types/ink/wrapAnsi.d.ts.map +1 -1
- package/lib/types/ink/wrapAnsi.js +29 -2
- package/lib/types/plugin-host.d.ts +13 -11
- package/lib/types/plugin-host.d.ts.map +1 -1
- package/lib/types/plugin-host.js +16 -16
- package/lib/types/plugin-spec/registry.d.ts +2 -2
- package/lib/types/plugin-spec/registry.d.ts.map +1 -1
- package/lib/types/plugin-spec/registry.js +113 -18
- package/lib/types/plugin-spec/tui-extension.d.ts.map +1 -1
- package/lib/types/plugin-spec/tui-extension.js +2 -0
- package/lib/types/plugin-spec/validate.d.ts.map +1 -1
- package/lib/types/plugin-spec/validate.js +20 -1
- package/lib/types/scenes.d.ts +2 -1
- package/lib/types/scenes.d.ts.map +1 -1
- package/lib/types/scenes.js +1 -1
- package/lib/types/screens/Chat.d.ts +3 -3
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +38 -17
- package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
- package/lib/types/screens/SessionBrowser.js +10 -3
- package/lib/types/sessions/view.d.ts +3 -2
- package/lib/types/sessions/view.d.ts.map +1 -1
- package/lib/types/sessions/view.js +18 -0
- package/lib/types/settings-sections.d.ts +2 -1
- package/lib/types/settings-sections.d.ts.map +1 -1
- package/lib/types/settings-sections.js +1 -1
- package/lib/types/utils/execFileNoThrow.d.ts.map +1 -1
- package/lib/types/utils/execFileNoThrow.js +3 -0
- package/lib/types/workspaces.d.ts +2 -1
- package/lib/types/workspaces.d.ts.map +1 -1
- package/lib/types/workspaces.js +1 -1
- package/node_modules/@dsh-std/command/CHANGELOG.md +8 -0
- package/node_modules/@dsh-std/command/package.json +1 -1
- package/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
- package/node_modules/@dsh-std/connection/lib/memory.d.ts +1 -1
- package/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -1
- package/node_modules/@dsh-std/connection/lib/memory.js +1 -0
- package/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
- package/node_modules/@dsh-std/connection/package.json +1 -1
- package/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/node_modules/@dsh-std/core/package.json +1 -1
- package/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -0
- package/node_modules/@dsh-std/manifest/package.json +1 -1
- package/node_modules/@dsh-std/messages/CHANGELOG.md +8 -0
- package/node_modules/@dsh-std/messages/package.json +1 -1
- package/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -0
- package/node_modules/@dsh-std/presentation/README.md +2 -1
- package/node_modules/@dsh-std/presentation/README.zh.md +2 -1
- package/node_modules/@dsh-std/presentation/lib/callback.d.ts +2 -0
- package/node_modules/@dsh-std/presentation/lib/callback.js +179 -0
- package/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -0
- package/node_modules/@dsh-std/presentation/lib/index.d.ts +44 -1
- package/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -1
- package/node_modules/@dsh-std/presentation/lib/index.js +6 -3
- package/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
- package/node_modules/@dsh-std/presentation/package.json +7 -3
- package/node_modules/@dsh-std/storage/CHANGELOG.md +8 -0
- package/node_modules/@dsh-std/storage/package.json +1 -1
- package/package.json +10 -5
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/package.json +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/package.json +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/package.json +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.md +2 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.zh.md +2 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.d.ts +2 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js +179 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts +44 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js +6 -3
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/package.json +7 -3
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/package.json +1 -1
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/package.json +1 -1
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/package.json +1 -1
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts +1 -1
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -1
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js +1 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/package.json +1 -1
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/package.json +1 -1
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/package.json +1 -1
- package/ecosystem-spec/registry/registry-0.15.json +0 -57
- /package/{ecosystem-spec → dsh-ecosystem-spec}/registry/contracts/decision-events-v1alpha1.json +0 -0
- /package/{ecosystem-spec → dsh-ecosystem-spec}/registry/host-descriptor.tui.example.json +0 -0
- /package/{ecosystem-spec → dsh-ecosystem-spec}/schemas/conformance-claim.schema.json +0 -0
- /package/{ecosystem-spec → dsh-ecosystem-spec}/schemas/effect-ledger-record.schema.json +0 -0
- /package/{ecosystem-spec → dsh-ecosystem-spec}/schemas/host-descriptor.schema.json +0 -0
|
@@ -37,7 +37,7 @@ const DEFAULT_ROW_HEIGHT = 2;
|
|
|
37
37
|
/** Cold-start estimate of the header block above the rows; corrected by the
|
|
38
38
|
* first layout measurement. */
|
|
39
39
|
const DEFAULT_HEADER_LINES = 14;
|
|
40
|
-
export function MessageList({ rows, expanded, expandedRows, selectedId, onToggleRow, model, diffLayout = 'auto', showAll, onToggleAll, onLoadOlder, thinkingVisible = true, registerRowRef, scrollHandle, forceMountRowId, newSinceRowId, onUnseenCount, failureHintRowId, failureHint, }) {
|
|
40
|
+
export function MessageList({ rows, expanded, expandedRows, selectedId, onToggleRow, model, diffLayout = 'auto', thinkingFold = 'preview', showAll, onToggleAll, onLoadOlder, thinkingVisible = true, registerRowRef, scrollHandle, forceMountRowId, newSinceRowId, onUnseenCount, failureHintRowId, failureHint, }) {
|
|
41
41
|
const hiddenCount = rows.length - MAX_RENDERED_ROWS;
|
|
42
42
|
// The thinking filter runs BEFORE virtualization so window indices line up.
|
|
43
43
|
const visibleRows = (showAll || hiddenCount <= 0
|
|
@@ -65,7 +65,7 @@ export function MessageList({ rows, expanded, expandedRows, selectedId, onToggle
|
|
|
65
65
|
return undefined;
|
|
66
66
|
};
|
|
67
67
|
// --- layout virtualization ---------------------------------------------
|
|
68
|
-
const { columns } = useTerminalSize();
|
|
68
|
+
const { columns, rows: termRows } = useTerminalSize();
|
|
69
69
|
// Measured row heights, remembered after a row unmounts so virtualization
|
|
70
70
|
// can compute total content height. Bounded: row ids grow monotonically
|
|
71
71
|
// and rows are never removed from the transcript (foldRows keeps the
|
|
@@ -75,6 +75,31 @@ export function MessageList({ rows, expanded, expandedRows, selectedId, onToggle
|
|
|
75
75
|
const HEIGHTS_CACHE_MAX = 5000;
|
|
76
76
|
const heightsRef = React.useRef(new Map());
|
|
77
77
|
const localRefs = React.useRef(new Map());
|
|
78
|
+
/** Row ids that have been mounted (and therefore painted into the
|
|
79
|
+
* terminal) at least once. The sticky window may skip a row ONLY after
|
|
80
|
+
* this: an unpainted row above the window has no scrollback copy, so
|
|
81
|
+
* skipping it would erase it from the user's history entirely — preset
|
|
82
|
+
* history at boot (session resume) landed exactly there. Cleared when
|
|
83
|
+
* the list head changes identity (rewind / new session / loadOlder
|
|
84
|
+
* prepends restored rows that must paint again). */
|
|
85
|
+
const paintedOnceRef = React.useRef(new Set());
|
|
86
|
+
const paintedBaseRef = React.useRef(undefined);
|
|
87
|
+
/** Window-expansion hold: after the window WIDENS (new rows mounted),
|
|
88
|
+
* refuse to tighten for a short hold so the mounted rows actually reach
|
|
89
|
+
* the terminal. React commits within one ink frame coalesce — a render
|
|
90
|
+
* that mounts rows followed by the measure-tick re-render that drops
|
|
91
|
+
* them paints only the DROPPED layout, and never-mounted rows have no
|
|
92
|
+
* scrollback copy (preset history at boot vanished — CI
|
|
93
|
+
* repro-inline-scrollback). After the hold, tightening is visually
|
|
94
|
+
* free: those rows sit in scrollback and the diff skips them. */
|
|
95
|
+
const lastStartRef = React.useRef(-1);
|
|
96
|
+
const holdUntilRef = React.useRef(0);
|
|
97
|
+
const listHeadId = visibleRows[0]?.id;
|
|
98
|
+
if (listHeadId !== undefined && paintedBaseRef.current !== undefined && listHeadId !== paintedBaseRef.current) {
|
|
99
|
+
paintedOnceRef.current = new Set();
|
|
100
|
+
}
|
|
101
|
+
if (listHeadId !== undefined)
|
|
102
|
+
paintedBaseRef.current = listHeadId;
|
|
78
103
|
/** Content-space offset of visibleRows[0] (header + dividers), measured. */
|
|
79
104
|
const baseRef = React.useRef(null);
|
|
80
105
|
const measureQueuedRef = React.useRef(false);
|
|
@@ -128,6 +153,15 @@ export function MessageList({ rows, expanded, expandedRows, selectedId, onToggle
|
|
|
128
153
|
// back to the real bottom: a self-sustaining ping-pong that blanks the
|
|
129
154
|
// transcript mid-stream.
|
|
130
155
|
if (sticky && visibleRows.length > 0) {
|
|
156
|
+
// Sticky (follow-bottom): the viewport shows the TAIL of the content —
|
|
157
|
+
// mount exactly the tail window the floor walk covers, not everything
|
|
158
|
+
// from the scrollTop scan. Main-screen ScrollBox reports its viewport
|
|
159
|
+
// as the CONTENT height (the terminal itself is the scroller), so both
|
|
160
|
+
// the scan and an unclamped floor walk mount EVERY row in long
|
|
161
|
+
// sessions — and React's commit traverses every fiber of every mounted
|
|
162
|
+
// row per frame (measured as the dominant long-session stall). The
|
|
163
|
+
// user only ever sees terminal rows: clamp the walk-back coverage to
|
|
164
|
+
// the TERMINAL viewport plus overscan.
|
|
131
165
|
start = Math.min(start, visibleRows.length - 1);
|
|
132
166
|
// Blank-band guard: sticky scrollTop tracks the renderer's FRESH Yoga
|
|
133
167
|
// scrollHeight, while these offsets use per-row heights measured one to
|
|
@@ -135,15 +169,44 @@ export function MessageList({ rows, expanded, expandedRows, selectedId, onToggle
|
|
|
135
169
|
// deeper through the underestimated offsets than the real viewport does,
|
|
136
170
|
// unmounting rows that are still on screen (visible spacer band). Walk
|
|
137
171
|
// backwards from the tail with the known heights and mount at least one
|
|
138
|
-
// viewport plus overscan of content above it, so the window
|
|
139
|
-
// open a gap inside what the user is looking at.
|
|
140
|
-
let covered = viewport + OVERSCAN_LINES;
|
|
172
|
+
// terminal viewport plus overscan of content above it, so the window
|
|
173
|
+
// can never open a gap inside what the user is looking at.
|
|
174
|
+
let covered = Math.min(viewport, termRows) + OVERSCAN_LINES;
|
|
141
175
|
let floor = visibleRows.length - 1;
|
|
142
176
|
while (floor > 0 && covered > 0) {
|
|
143
177
|
covered -= heightOf(visibleRows[floor]);
|
|
144
178
|
floor--;
|
|
145
179
|
}
|
|
146
|
-
|
|
180
|
+
// The walk exhausted the whole list: every row is within coverage —
|
|
181
|
+
// floor+1 here would drop row 0 (its content then has no terminal copy
|
|
182
|
+
// anywhere; preset history lost its head — CI repro-inline-scrollback).
|
|
183
|
+
start = floor === 0 && covered > 0 ? 0 : floor + 1;
|
|
184
|
+
// Paint-at-least-once: extend the window over any row that has never
|
|
185
|
+
// been mounted. A row the window skips keeps only its terminal/scrollback
|
|
186
|
+
// copy — a row that was never painted has NO copy anywhere, so preset
|
|
187
|
+
// history (session resume, repro-inline-scrollback's #39 family) would
|
|
188
|
+
// vanish from the user's scrollback. Extending mounts everything above
|
|
189
|
+
// on the first frame (topPad 0, full paint), then the set fills and the
|
|
190
|
+
// window tightens to the tail.
|
|
191
|
+
const paintedOnce = paintedOnceRef.current;
|
|
192
|
+
for (let i = 0; i < start; i++) {
|
|
193
|
+
if (!paintedOnce.has(visibleRows[i].id)) {
|
|
194
|
+
start = i;
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Expansion hold — AFTER the extension so it tracks the FINAL window:
|
|
199
|
+
// never tighten within the hold window after a widen. React commits
|
|
200
|
+
// inside one ink frame coalesce; a mount followed by the measure-tick
|
|
201
|
+
// re-render that drops the row paints only the DROPPED layout, and the
|
|
202
|
+
// row's painted-once mark (set at the first commit) is a lie.
|
|
203
|
+
if (lastStartRef.current >= 0 && start > lastStartRef.current && performance.now() < holdUntilRef.current) {
|
|
204
|
+
start = lastStartRef.current;
|
|
205
|
+
}
|
|
206
|
+
if (lastStartRef.current < 0 || start < lastStartRef.current) {
|
|
207
|
+
holdUntilRef.current = performance.now() + 120;
|
|
208
|
+
}
|
|
209
|
+
lastStartRef.current = start;
|
|
147
210
|
}
|
|
148
211
|
if (forceMountRowId !== undefined && forceMountRowId !== null) {
|
|
149
212
|
const idx = visibleRows.findIndex(row => row.id === forceMountRowId);
|
|
@@ -152,6 +215,15 @@ export function MessageList({ rows, expanded, expandedRows, selectedId, onToggle
|
|
|
152
215
|
end = Math.max(end, idx + 1);
|
|
153
216
|
}
|
|
154
217
|
}
|
|
218
|
+
// The newest failed tool call carries the trajectory footnote
|
|
219
|
+
// (failureHint). Virtualization must not unmount it: before the window
|
|
220
|
+
// clamp the row was always mounted, now keep mounting it explicitly while
|
|
221
|
+
// the hint is live (verify-trace-scene's footnote check).
|
|
222
|
+
if (failureHintRowId !== undefined && failureHintRowId !== null) {
|
|
223
|
+
const idx = visibleRows.findIndex(row => row.id === failureHintRowId);
|
|
224
|
+
if (idx !== -1)
|
|
225
|
+
start = Math.min(start, idx);
|
|
226
|
+
}
|
|
155
227
|
const topPad = offsets[start] ?? 0;
|
|
156
228
|
const mountedBottom = end < visibleRows.length ? offsets[end] : total;
|
|
157
229
|
const bottomPad = total - mountedBottom;
|
|
@@ -185,6 +257,12 @@ export function MessageList({ rows, expanded, expandedRows, selectedId, onToggle
|
|
|
185
257
|
// mounted coverage so burst scrolls never show blank spacer.
|
|
186
258
|
React.useLayoutEffect(() => {
|
|
187
259
|
let changed = false;
|
|
260
|
+
// Mounted ⇒ painted: record rows eligible for window skipping.
|
|
261
|
+
const paintedOnce = paintedOnceRef.current;
|
|
262
|
+
for (const id of localRefs.current.keys()) {
|
|
263
|
+
if (!paintedOnce.has(id))
|
|
264
|
+
paintedOnce.add(id);
|
|
265
|
+
}
|
|
188
266
|
for (const [id, el] of localRefs.current) {
|
|
189
267
|
const h = el.yogaNode?.getComputedHeight();
|
|
190
268
|
if (h !== undefined && h > 0 && heightsRef.current.get(id) !== h) {
|
|
@@ -256,10 +334,10 @@ export function MessageList({ rows, expanded, expandedRows, selectedId, onToggle
|
|
|
256
334
|
// spacing; only the very first row of the whole list has none.
|
|
257
335
|
const addMargin = margins.get(row.id) === true;
|
|
258
336
|
const tool = row.tool;
|
|
259
|
-
return (_jsx(MemoRow, { rowId: row.id, kind: row.kind, text: row.text, executionTarget: row.executionTarget, streaming: row.streaming === true, durationMs: row.durationMs, time: row.time, addMargin: addMargin, isSelected: selectedId === row.id, isExpanded: expandedRows.has(row.id), expanded: expanded, model: model, diffLayout: diffLayout, background: rowBackground(row.id), toolCallId: tool?.callId, toolName: tool?.name, toolArgsText: tool?.argsText, toolArgsFull: tool?.argsFull, toolStatus: tool?.status, toolResultText: tool?.resultText, toolResultFull: tool?.resultFull, toolErrorText: tool?.errorText, toolFootnote: failureHintRowId === row.id ? failureHint : undefined, toolCallView: tool?.callView, toolResultView: tool?.resultView, toolStartedAt: tool?.startedAt, toolDurationMs: tool?.durationMs, nowSec: tool?.status === 'running' ? nowSec : undefined, onToggleRow: onToggleRow, setRowRef: setRowRef }, row.id));
|
|
337
|
+
return (_jsx(MemoRow, { rowId: row.id, kind: row.kind, text: row.text, executionTarget: row.executionTarget, streaming: row.streaming === true, durationMs: row.durationMs, time: row.time, addMargin: addMargin, isSelected: selectedId === row.id, isExpanded: expandedRows.has(row.id), expanded: expanded, model: model, diffLayout: diffLayout, thinkingFold: thinkingFold, background: rowBackground(row.id), toolCallId: tool?.callId, toolName: tool?.name, toolArgsText: tool?.argsText, toolArgsFull: tool?.argsFull, toolStatus: tool?.status, toolResultText: tool?.resultText, toolResultFull: tool?.resultFull, toolErrorText: tool?.errorText, toolFootnote: failureHintRowId === row.id ? failureHint : undefined, toolCallView: tool?.callView, toolResultView: tool?.resultView, toolStartedAt: tool?.startedAt, toolDurationMs: tool?.durationMs, nowSec: tool?.status === 'running' ? nowSec : undefined, onToggleRow: onToggleRow, setRowRef: setRowRef }, row.id));
|
|
260
338
|
}), bottomPad > 0 && _jsx(Box, { height: bottomPad, flexShrink: 0 })] }));
|
|
261
339
|
}
|
|
262
|
-
function TranscriptRow({ rowId, kind, text, executionTarget, streaming, durationMs, time, addMargin, isSelected, isExpanded, expanded, model, diffLayout, background, toolCallId, toolName, toolArgsText, toolArgsFull, toolStatus, toolResultText, toolResultFull, toolErrorText, toolFootnote, toolCallView, toolResultView, toolStartedAt, toolDurationMs, onToggleRow, setRowRef, }) {
|
|
340
|
+
function TranscriptRow({ rowId, kind, text, executionTarget, streaming, durationMs, time, addMargin, isSelected, isExpanded, expanded, model, diffLayout, thinkingFold, background, toolCallId, toolName, toolArgsText, toolArgsFull, toolStatus, toolResultText, toolResultFull, toolErrorText, toolFootnote, toolCallView, toolResultView, toolStartedAt, toolDurationMs, onToggleRow, setRowRef, }) {
|
|
263
341
|
const ref = React.useCallback((el) => {
|
|
264
342
|
setRowRef(rowId, el);
|
|
265
343
|
}, [setRowRef, rowId]);
|
|
@@ -272,7 +350,10 @@ function TranscriptRow({ rowId, kind, text, executionTarget, streaming, duration
|
|
|
272
350
|
case 'assistant':
|
|
273
351
|
return streaming ? (_jsxs(Box, { alignItems: "flex-start", flexDirection: "row", marginTop: addMargin ? 1 : 0, width: "100%", backgroundColor: background, children: [_jsx(Box, { minWidth: 2, children: _jsx(Text, { color: "text", children: "\u25CF" }) }), _jsx(Box, { flexDirection: "column", children: _jsx(StreamingMarkdown, { children: stripNarration(text) }) })] })) : (_jsxs(Box, { width: "100%", flexDirection: "column", backgroundColor: background, ref: ref, children: [expanded && (_jsx(Box, { flexDirection: "row", justifyContent: "flex-end", gap: 1, marginTop: 1, children: _jsx(MessageMetadata, { timestamp: time, model: model }) })), _jsx(AssistantTextMessage, { text: stripNarration(text), addMargin: addMargin, isSelected: isSelected, isExpanded: isExpanded, onClick: onClick })] }));
|
|
274
352
|
case 'reasoning':
|
|
275
|
-
return (_jsx(Box, { flexDirection: "column", ref: ref, children: _jsx(AssistantThinkingMessage, { thinking: text, addMargin: addMargin,
|
|
353
|
+
return (_jsx(Box, { flexDirection: "column", ref: ref, children: _jsx(AssistantThinkingMessage, { thinking: text, addMargin: addMargin, preview: streaming &&
|
|
354
|
+
thinkingFold === 'preview' &&
|
|
355
|
+
!expanded &&
|
|
356
|
+
!isExpanded,
|
|
276
357
|
// Streaming reasoning shows expanded live, then folds
|
|
277
358
|
// automatically once the turn settles (unless Ctrl+O or a
|
|
278
359
|
// single-row expansion keeps it open).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PromptInput.d.ts","sourceRoot":"","sources":["../../../src/components/PromptInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAkDxD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,IAAI,OAAO,CAAA;IAClB,KAAK,IAAI,IAAI,CAAA;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAA;IAChB,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,IAAI,IAAI,CAAA;IACpB;;;OAGG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;IACrD,6EAA6E;IAC7E,eAAe,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,cAAc,CAAC,IAAI,IAAI,CAAA;IACvB,8EAA8E;IAC9E,eAAe,CAAC,IAAI,IAAI,CAAA;IACxB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;CACzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,cAAc,EACd,eAAe,EACf,aAAa,GACd,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"PromptInput.d.ts","sourceRoot":"","sources":["../../../src/components/PromptInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAkDxD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,IAAI,OAAO,CAAA;IAClB,KAAK,IAAI,IAAI,CAAA;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAA;IAChB,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,IAAI,IAAI,CAAA;IACpB;;;OAGG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;IACrD,6EAA6E;IAC7E,eAAe,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,cAAc,CAAC,IAAI,IAAI,CAAA;IACvB,8EAA8E;IAC9E,eAAe,CAAC,IAAI,IAAI,CAAA;IACxB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;CACzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,cAAc,EACd,eAAe,EACf,aAAa,GACd,EAAE,gBAAgB,qBA65BlB"}
|
|
@@ -116,6 +116,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
116
116
|
const [selectedCommand, setSelectedCommand] = React.useState(0);
|
|
117
117
|
const history = React.useRef([]);
|
|
118
118
|
const historyIndex = React.useRef(-1);
|
|
119
|
+
const historyDraft = React.useRef('');
|
|
119
120
|
// ctrl+r history fill: replace the input when a new fill arrives, then
|
|
120
121
|
// tell the caller to clear it.
|
|
121
122
|
const lastFill = React.useRef(null);
|
|
@@ -528,11 +529,17 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
528
529
|
if (!tryRunCommand(value))
|
|
529
530
|
submitText(value);
|
|
530
531
|
};
|
|
532
|
+
// Ctrl+J is the only portable multiline fallback when a terminal cannot
|
|
533
|
+
// report modifiers on Enter. The parser names its bare LF `enter`, while
|
|
534
|
+
// the physical Enter key arrives as CR (`return`).
|
|
535
|
+
if (input === '\n' && event?.keypress.name === 'enter') {
|
|
536
|
+
insertAtCaret('\n');
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
531
539
|
// Whole-line input from Windows ConPTY pipelines (cmd batch -> node):
|
|
532
|
-
// the trailing CR/LF marks a complete line to submit. A bare
|
|
533
|
-
//
|
|
534
|
-
//
|
|
535
|
-
// direct-submit path.
|
|
540
|
+
// the trailing CR/LF marks a complete line to submit. A bare CR/CRLF is
|
|
541
|
+
// Enter, while real multi-char piped lines keep the legacy direct-submit
|
|
542
|
+
// path.
|
|
536
543
|
if (input.includes('\n') || input.includes('\r')) {
|
|
537
544
|
if (/^[\r\n]+$/.test(input)) {
|
|
538
545
|
handleEnter();
|
|
@@ -622,9 +629,13 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
622
629
|
}
|
|
623
630
|
if (history.current.length === 0)
|
|
624
631
|
return;
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
632
|
+
if (historyIndex.current < 0) {
|
|
633
|
+
historyDraft.current = value;
|
|
634
|
+
historyIndex.current = history.current.length - 1;
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
historyIndex.current = Math.max(0, historyIndex.current - 1);
|
|
638
|
+
}
|
|
628
639
|
const entry = history.current[historyIndex.current] ?? '';
|
|
629
640
|
setInput(entry);
|
|
630
641
|
return;
|
|
@@ -649,11 +660,14 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
649
660
|
}
|
|
650
661
|
if (historyIndex.current < 0)
|
|
651
662
|
return;
|
|
652
|
-
historyIndex.current
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
663
|
+
if (historyIndex.current >= history.current.length - 1) {
|
|
664
|
+
historyIndex.current = -1;
|
|
665
|
+
setInput(historyDraft.current);
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
historyIndex.current += 1;
|
|
669
|
+
setInput(history.current[historyIndex.current] ?? '');
|
|
670
|
+
}
|
|
657
671
|
return;
|
|
658
672
|
}
|
|
659
673
|
if (isMod(key) && key.leftArrow) {
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
3
|
-
* Renders markdown during streaming by splitting at the last top-level block
|
|
4
|
-
* boundary: everything before is stable (memoized, never re-parsed), only the
|
|
5
|
-
* final block is re-parsed per delta, mirroring Claude Code's
|
|
6
|
-
* `StreamingMarkdown.tsx`). marked.lexer() correctly handles unclosed code
|
|
7
|
-
* fences as a single token, so block boundaries are always safe.
|
|
8
|
-
*/
|
|
9
|
-
export declare function StreamingMarkdown({ children, }: {
|
|
2
|
+
export declare function StreamingMarkdown({ children, dimColor, }: {
|
|
10
3
|
children: string;
|
|
4
|
+
dimColor?: boolean;
|
|
11
5
|
}): React.ReactNode;
|
|
12
6
|
//# sourceMappingURL=StreamingMarkdown.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StreamingMarkdown.d.ts","sourceRoot":"","sources":["../../../src/components/StreamingMarkdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"StreamingMarkdown.d.ts","sourceRoot":"","sources":["../../../src/components/StreamingMarkdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAmDzB,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,QAAgB,GACjB,EAAE;IACD,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,GAAG,KAAK,CAAC,SAAS,CA0ClB"}
|
|
@@ -3,6 +3,7 @@ import React from 'react';
|
|
|
3
3
|
import { marked } from 'marked';
|
|
4
4
|
import Box from '../ink/components/Box.js';
|
|
5
5
|
import { stripPromptXMLTags } from '../cc/markdown.js';
|
|
6
|
+
import { t } from '../i18n.js';
|
|
6
7
|
import { Markdown } from './Markdown.js';
|
|
7
8
|
/**
|
|
8
9
|
* Renders markdown during streaming by splitting at the last top-level block
|
|
@@ -11,15 +12,56 @@ import { Markdown } from './Markdown.js';
|
|
|
11
12
|
* `StreamingMarkdown.tsx`). marked.lexer() correctly handles unclosed code
|
|
12
13
|
* fences as a single token, so block boundaries are always safe.
|
|
13
14
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Tail budget for the unstable suffix during streaming. The sticky view only
|
|
17
|
+
* ever shows the last viewport of rows, but the suffix Text is re-wrapped
|
|
18
|
+
* every frame — an unbounded suffix (a single huge paragraph with no block
|
|
19
|
+
* boundary to advance the prefix) made that O(total) per frame. The suffix
|
|
20
|
+
* is clipped to this many characters (preferring a paragraph boundary),
|
|
21
|
+
* with a leading marker naming the dropped amount; settling renders the
|
|
22
|
+
* full text once through the non-streaming path.
|
|
23
|
+
*
|
|
24
|
+
* The cut point is STICKY (advances only when the suffix outgrows budget +
|
|
25
|
+
* step): a cut that slid every frame would break append-only growth, and
|
|
26
|
+
* the layout layer's incremental wrap would fall back to a full re-wrap of
|
|
27
|
+
* the whole tail on every token.
|
|
28
|
+
*/
|
|
29
|
+
const SUFFIX_TAIL_BUDGET = 3584;
|
|
30
|
+
const SUFFIX_BOUNDARY_LOOKBACK = 2048;
|
|
31
|
+
const SUFFIX_CUT_STEP = 1024;
|
|
32
|
+
function clipSuffixTail(suffix, cut) {
|
|
33
|
+
const total = suffix.length;
|
|
34
|
+
if (total <= SUFFIX_TAIL_BUDGET) {
|
|
35
|
+
cut.current = 0;
|
|
36
|
+
return suffix;
|
|
37
|
+
}
|
|
38
|
+
// Advance the sticky cut only once the suffix outgrew the budget by a
|
|
39
|
+
// full step, preferring a paragraph boundary inside the lookback window.
|
|
40
|
+
if (total - cut.current > SUFFIX_TAIL_BUDGET + SUFFIX_CUT_STEP) {
|
|
41
|
+
const windowStart = total - SUFFIX_TAIL_BUDGET;
|
|
42
|
+
const boundary = suffix.lastIndexOf('\n\n', windowStart + SUFFIX_BOUNDARY_LOOKBACK);
|
|
43
|
+
cut.current = boundary !== -1 && boundary >= windowStart - SUFFIX_BOUNDARY_LOOKBACK
|
|
44
|
+
? boundary + 2
|
|
45
|
+
: windowStart;
|
|
46
|
+
}
|
|
47
|
+
const dropped = cut.current;
|
|
48
|
+
return `${t('streaming-folded', { count: dropped })}\n\n${suffix.slice(cut.current)}`;
|
|
49
|
+
}
|
|
50
|
+
export function StreamingMarkdown({ children, dimColor = false, }) {
|
|
51
|
+
// The stable prefix is kept as ONE string identity across renders: a
|
|
52
|
+
// fresh substring per render would break Markdown's React.memo and
|
|
53
|
+
// re-layout the entire finished transcript tail on every token. The
|
|
54
|
+
// identity only changes when a new block boundary advances the prefix.
|
|
55
|
+
const prefixRef = React.useRef('');
|
|
56
|
+
const cutRef = React.useRef(0);
|
|
16
57
|
const stripped = stripPromptXMLTags(children);
|
|
17
58
|
// Reset if text was replaced (defensive; normally unmount handles this)
|
|
18
|
-
if (!stripped.startsWith(
|
|
19
|
-
|
|
59
|
+
if (!stripped.startsWith(prefixRef.current)) {
|
|
60
|
+
prefixRef.current = '';
|
|
61
|
+
cutRef.current = 0;
|
|
20
62
|
}
|
|
21
63
|
// Lex only from current boundary — O(unstable length), not O(full text)
|
|
22
|
-
const boundary =
|
|
64
|
+
const boundary = prefixRef.current.length;
|
|
23
65
|
const tokens = marked.lexer(stripped.substring(boundary));
|
|
24
66
|
// Last non-space token is the growing block; everything before is final
|
|
25
67
|
let lastContentIdx = tokens.length - 1;
|
|
@@ -31,9 +73,9 @@ export function StreamingMarkdown({ children, }) {
|
|
|
31
73
|
advance += tokens[i].raw.length;
|
|
32
74
|
}
|
|
33
75
|
if (advance > 0) {
|
|
34
|
-
|
|
76
|
+
prefixRef.current = stripped.substring(0, boundary + advance);
|
|
35
77
|
}
|
|
36
|
-
const stablePrefix =
|
|
37
|
-
const unstableSuffix = stripped.substring(stablePrefix.length);
|
|
38
|
-
return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [stablePrefix && _jsx(Markdown, { children: stablePrefix }), unstableSuffix && _jsx(Markdown, { cacheTokens: false, children: unstableSuffix })] }));
|
|
78
|
+
const stablePrefix = prefixRef.current;
|
|
79
|
+
const unstableSuffix = clipSuffixTail(stripped.substring(stablePrefix.length), cutRef);
|
|
80
|
+
return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [stablePrefix && _jsx(Markdown, { dimColor: dimColor, children: stablePrefix }), unstableSuffix && _jsx(Markdown, { dimColor: dimColor, cacheTokens: false, children: unstableSuffix })] }));
|
|
39
81
|
}
|
|
@@ -13,7 +13,7 @@ export interface FramePreset {
|
|
|
13
13
|
/** Named working-activity frame presets, keyed by preset name (`claude`, `moon`, `sand`, ...). */
|
|
14
14
|
export declare const FRAME_PRESETS: Record<string, FramePreset>;
|
|
15
15
|
/** The pi extension's default preset. */
|
|
16
|
-
export declare const DEFAULT_PRESET = "
|
|
16
|
+
export declare const DEFAULT_PRESET = "moon8";
|
|
17
17
|
/** Every selectable preset name, `random` first (the pi selector order). */
|
|
18
18
|
export declare const PRESET_NAMES: readonly string[];
|
|
19
19
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activityFrames.d.ts","sourceRoot":"","sources":["../../../src/components/activityFrames.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,kFAAkF;AAClF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED,kGAAkG;AAClG,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"activityFrames.d.ts","sourceRoot":"","sources":["../../../src/components/activityFrames.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,kFAAkF;AAClF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED,kGAAkG;AAClG,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAgDrD,CAAA;AAED,yCAAyC;AACzC,eAAO,MAAM,cAAc,UAAU,CAAA;AAErC,4EAA4E;AAC5E,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAA8C,CAAA;AAExF;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,CASnE"}
|
|
@@ -32,6 +32,9 @@ export const FRAME_PRESETS = {
|
|
|
32
32
|
},
|
|
33
33
|
hamburger: { frames: ['☱', '☲', '☴'], intervalMs: 220 },
|
|
34
34
|
moon: { frames: ['◐', '◓', '◑', '◒'], intervalMs: 240 },
|
|
35
|
+
// kimi-code MoonLoader 同款:8 帧 emoji 月相,120ms 一帧,比半圆版更丝滑。
|
|
36
|
+
// 不带 \uFE0E:保留彩色 emoji 渲染(Windows Terminal 等现代终端效果最佳)。
|
|
37
|
+
moon8: { frames: ['🌑', '🌒', '🌓', '🌔', '🌕', '🌖', '🌗', '🌘'], intervalMs: 120 },
|
|
35
38
|
comet: {
|
|
36
39
|
frames: ['● ', ' ● ', ' ● ', ' ● ', ' ●', ' ● ', ' ● ', ' ● '],
|
|
37
40
|
intervalMs: 160,
|
|
@@ -55,7 +58,7 @@ export const FRAME_PRESETS = {
|
|
|
55
58
|
toggle: { frames: ['⊶', '⊷'], intervalMs: 300 },
|
|
56
59
|
};
|
|
57
60
|
/** The pi extension's default preset. */
|
|
58
|
-
export const DEFAULT_PRESET = '
|
|
61
|
+
export const DEFAULT_PRESET = 'moon8';
|
|
59
62
|
/** Every selectable preset name, `random` first (the pi selector order). */
|
|
60
63
|
export const PRESET_NAMES = ['random', ...Object.keys(FRAME_PRESETS)];
|
|
61
64
|
/**
|
|
@@ -5,6 +5,10 @@ type Props = {
|
|
|
5
5
|
addMargin: boolean;
|
|
6
6
|
/** True when Ctrl+O transcript/verbose mode is on — show the full text. */
|
|
7
7
|
verbose: boolean;
|
|
8
|
+
/** Streaming compact mode (thinkingFold=preview): a 3-row live ticker of
|
|
9
|
+
* the model's latest reasoning lines instead of the full block —
|
|
10
|
+
* kimicode-style constant height; the block never resizes mid-stream. */
|
|
11
|
+
preview?: boolean;
|
|
8
12
|
/** Thinking wall-clock duration once the reasoning block settled (ms). */
|
|
9
13
|
durationMs?: number;
|
|
10
14
|
/** Message-selection mode highlight. */
|
|
@@ -18,6 +22,6 @@ type Props = {
|
|
|
18
22
|
* reasoning duration, the label carries it (`∴ Thinking · 12s …`) — dsh-tui's
|
|
19
23
|
* take on making thinking time visible in the transcript.
|
|
20
24
|
*/
|
|
21
|
-
export declare function AssistantThinkingMessage({ thinking, addMargin, verbose, durationMs, isSelected, onClick, }: Props): React.ReactNode;
|
|
25
|
+
export declare function AssistantThinkingMessage({ thinking, addMargin, verbose, preview, durationMs, isSelected, onClick, }: Props): React.ReactNode;
|
|
22
26
|
export {};
|
|
23
27
|
//# sourceMappingURL=AssistantThinkingMessage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantThinkingMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantThinkingMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"AssistantThinkingMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantThinkingMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAczB,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,4DAA4D;IAC5D,SAAS,EAAE,OAAO,CAAA;IAClB,2EAA2E;IAC3E,OAAO,EAAE,OAAO,CAAA;IAChB;;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,IAAI,IAAI,CAAA;CACjB,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,SAAS,EACT,OAAO,EACP,OAAe,EACf,UAAU,EACV,UAAkB,EAClB,OAAO,GACR,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CAqFzB"}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from '../../ui.js';
|
|
3
3
|
import { t } from '../../i18n.js';
|
|
4
|
-
import {
|
|
4
|
+
import { StreamingMarkdown } from '../StreamingMarkdown.js';
|
|
5
5
|
import { formatDuration } from '../../cc/format.js';
|
|
6
|
+
/** Preview body rows — a FIXED row count (kimicode-style constant-height
|
|
7
|
+
* ticker). Ink's truncate slices the whole string across newlines as one
|
|
8
|
+
* logical line, so a single joined Text collapses to 1-2 rows whenever
|
|
9
|
+
* the combined width passes the terminal width, then bounces back as
|
|
10
|
+
* lines shift. One Text per row, each truncated to the width and padded
|
|
11
|
+
* to exactly this many rows, keeps the block height stream-independent. */
|
|
12
|
+
const PREVIEW_ROWS = 3;
|
|
6
13
|
/**
|
|
7
14
|
* Thinking block: folded `∴ Thinking (ctrl+o to expand)`, expanded shows the
|
|
8
15
|
* full reasoning text indented under `∴ Thinking…`, mirroring Claude Code's
|
|
@@ -10,14 +17,30 @@ import { formatDuration } from '../../cc/format.js';
|
|
|
10
17
|
* reasoning duration, the label carries it (`∴ Thinking · 12s …`) — dsh-tui's
|
|
11
18
|
* take on making thinking time visible in the transcript.
|
|
12
19
|
*/
|
|
13
|
-
export function AssistantThinkingMessage({ thinking, addMargin, verbose, durationMs, isSelected = false, onClick, }) {
|
|
20
|
+
export function AssistantThinkingMessage({ thinking, addMargin, verbose, preview = false, durationMs, isSelected = false, onClick, }) {
|
|
14
21
|
if (!thinking)
|
|
15
22
|
return null;
|
|
16
23
|
const duration = durationMs !== undefined && durationMs >= 1000
|
|
17
24
|
? ` · ${formatDuration(durationMs)}`
|
|
18
25
|
: '';
|
|
26
|
+
if (preview) {
|
|
27
|
+
// Live ticker: the model's last few reasoning lines, dimmed, one Text
|
|
28
|
+
// per row so each truncates to the width independently, padded to a
|
|
29
|
+
// constant PREVIEW_ROWS-tall block that follows the stream. The folded
|
|
30
|
+
// summary takes over when the step settles. The LAST row truncates
|
|
31
|
+
// from the start (leading ellipsis) so the newest tokens — which grow
|
|
32
|
+
// at the line's end — stay visible while the line is longer than the
|
|
33
|
+
// width.
|
|
34
|
+
const lines = thinking.split('\n');
|
|
35
|
+
const visible = lines.slice(-PREVIEW_ROWS);
|
|
36
|
+
const clipped = lines.length > visible.length;
|
|
37
|
+
// Pad with single spaces — an empty-string Text renders with zero
|
|
38
|
+
// height in ink, so '' padding would not hold the row open.
|
|
39
|
+
const rows = Array.from({ length: PREVIEW_ROWS }, (_, i) => visible[i] ?? ' ');
|
|
40
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: addMargin ? 1 : 0, backgroundColor: isSelected ? 'messageActionsBackground' : undefined, onClick: onClick, children: [_jsxs(Text, { dimColor: true, italic: true, children: ["\u2234 ", t('thinking-label'), duration, "\u2026"] }), _jsx(Box, { flexDirection: "column", paddingLeft: 2, children: rows.map((line, i) => (_jsx(Text, { dimColor: true, italic: true, wrap: i === rows.length - 1 ? 'truncate-start' : 'truncate', children: i === 0 && clipped ? `…${line}` : line }, i))) })] }));
|
|
41
|
+
}
|
|
19
42
|
if (!verbose) {
|
|
20
43
|
return (_jsx(Box, { marginTop: addMargin ? 1 : 0, backgroundColor: isSelected ? 'messageActionsBackground' : undefined, onClick: onClick, children: _jsxs(Text, { dimColor: true, italic: true, children: ["\u2234 ", t('thinking-label'), duration, " ", t('hint-expand-ctrl-o')] }) }));
|
|
21
44
|
}
|
|
22
|
-
return (_jsxs(Box, { flexDirection: "column", gap: 1, marginTop: addMargin ? 1 : 0, width: "100%", backgroundColor: isSelected ? 'messageActionsBackground' : undefined, onClick: onClick, children: [_jsxs(Text, { dimColor: true, italic: true, children: ["\u2234 ", t('thinking-label'), duration, "\u2026"] }), _jsx(Box, { paddingLeft: 2, children: _jsx(
|
|
45
|
+
return (_jsxs(Box, { flexDirection: "column", gap: 1, marginTop: addMargin ? 1 : 0, width: "100%", backgroundColor: isSelected ? 'messageActionsBackground' : undefined, onClick: onClick, children: [_jsxs(Text, { dimColor: true, italic: true, children: ["\u2234 ", t('thinking-label'), duration, "\u2026"] }), _jsx(Box, { paddingLeft: 2, children: _jsx(StreamingMarkdown, { dimColor: true, children: thinking }) })] }));
|
|
23
46
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantToolUseMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantToolUseMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAA8C,OAAO,EAAE,MAAM,8BAA8B,CAAA;AAKvG,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;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;CAC1C,CAAA;
|
|
1
|
+
{"version":3,"file":"AssistantToolUseMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantToolUseMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAA8C,OAAO,EAAE,MAAM,8BAA8B,CAAA;AAKvG,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;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;CAC1C,CAAA;AAqND;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,IAAI,EACJ,SAAS,EACT,OAAO,EACP,UAAkB,EAClB,UAAkB,EAClB,QAAQ,EACR,UAAmB,GACpB,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CAqIzB"}
|
|
@@ -145,9 +145,20 @@ function capLines(lines, max, verbose) {
|
|
|
145
145
|
* (`Edit /path`, `Read /path (1 - 100)`) with the first word bold. The
|
|
146
146
|
* result view's title replaces the call view's only when present — a
|
|
147
147
|
* settled terminal card carries output but no title of its own. */
|
|
148
|
+
/** Header args display budget: the parenthesized summary is a pointer, not
|
|
149
|
+
* the payload — full args live in the verbose/expanded body. A streaming
|
|
150
|
+
* tool call's args can grow to hundreds of KB, and wrapping that in the
|
|
151
|
+
* header Text every frame was the dominant long-output stall (string-width
|
|
152
|
+
* via wrap-ansi, 60%+ of CPU in profiles). */
|
|
153
|
+
const HEADER_ARGS_BUDGET = 480;
|
|
154
|
+
function clipHeaderArgs(args) {
|
|
155
|
+
if (args.length <= HEADER_ARGS_BUDGET)
|
|
156
|
+
return args;
|
|
157
|
+
return `${args.slice(0, HEADER_ARGS_BUDGET)}…`;
|
|
158
|
+
}
|
|
148
159
|
function HeaderTitle({ name, title, isTerminal, displayArgs }) {
|
|
149
160
|
if (title === undefined) {
|
|
150
|
-
return (_jsxs(_Fragment, { children: [_jsx(Box, { flexShrink: 0, children: _jsx(Text, { bold: true, wrap: "truncate-end", children: name }) }), displayArgs !== '' && (_jsx(Box, { flexWrap: "nowrap", children: _jsxs(Text, { children: ["(", displayArgs, ")"] }) }))] }));
|
|
161
|
+
return (_jsxs(_Fragment, { children: [_jsx(Box, { flexShrink: 0, children: _jsx(Text, { bold: true, wrap: "truncate-end", children: name }) }), displayArgs !== '' && (_jsx(Box, { flexWrap: "nowrap", children: _jsxs(Text, { children: ["(", clipHeaderArgs(displayArgs), ")"] }) }))] }));
|
|
151
162
|
}
|
|
152
163
|
if (isTerminal) {
|
|
153
164
|
return (_jsxs(_Fragment, { children: [_jsx(Box, { flexShrink: 0, children: _jsx(Text, { bold: true, wrap: "truncate-end", children: name }) }), _jsx(Box, { flexWrap: "nowrap", children: _jsxs(Text, { children: ["(", title, ")"] }) })] }));
|
|
@@ -10,9 +10,9 @@ import { type SessionModeSpec } from '../sessionModes.js';
|
|
|
10
10
|
import type { SpinnerMode } from '../components/Spinner/spinnerMode.js';
|
|
11
11
|
import { type ActivityState } from 'dsh-working-activity/status';
|
|
12
12
|
import { type TuiWorkspaceCommand, type TuiWorkspaceCommandResult, type TuiWorkspaceTarget } from './workspaces.js';
|
|
13
|
-
import type
|
|
13
|
+
import { type TuiSettingsSection } from './settings-sections.js';
|
|
14
14
|
import type { SettingsHost } from './settingsEditor.js';
|
|
15
|
-
import type
|
|
15
|
+
import { type TuiSceneDescriptor } from './scenes.js';
|
|
16
16
|
import type { TuiRewindMode } from './extension-events.js';
|
|
17
17
|
type ChannelImageBlock = Extract<ContentBlock, {
|
|
18
18
|
type: 'image';
|
|
@@ -193,6 +193,23 @@ export interface ChannelGoal {
|
|
|
193
193
|
message: string;
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
|
+
/** The observable outcome of adopting a persisted session. */
|
|
197
|
+
export type ResumeResult = {
|
|
198
|
+
readonly ok: true;
|
|
199
|
+
} | {
|
|
200
|
+
readonly ok: false;
|
|
201
|
+
readonly reason: 'working';
|
|
202
|
+
} | {
|
|
203
|
+
readonly ok: false;
|
|
204
|
+
readonly reason: 'unavailable';
|
|
205
|
+
} | {
|
|
206
|
+
readonly ok: false;
|
|
207
|
+
readonly reason: 'cancelled';
|
|
208
|
+
} | {
|
|
209
|
+
readonly ok: false;
|
|
210
|
+
readonly reason: 'failed';
|
|
211
|
+
readonly error: string;
|
|
212
|
+
};
|
|
196
213
|
/** Secret-free credential metadata for configuration and status surfaces. */
|
|
197
214
|
export interface CredentialStatus {
|
|
198
215
|
configured: boolean;
|
|
@@ -320,6 +337,9 @@ export interface Channel {
|
|
|
320
337
|
readonly activityFrames: string | undefined;
|
|
321
338
|
/** Edit/Write diff presentation preference (`auto`/`split`/`unified`). */
|
|
322
339
|
readonly diffLayout: 'auto' | 'split' | 'unified';
|
|
340
|
+
/** Thinking-block display (`preview` = 2-3 line live stream + fold per
|
|
341
|
+
* step; `full` = expanded until turn end). */
|
|
342
|
+
readonly thinkingFold: 'preview' | 'full';
|
|
323
343
|
/** Whether the in-process working-activity line is shown (config.activity). */
|
|
324
344
|
readonly activityEnabled: boolean;
|
|
325
345
|
/** Whether the segmented context bar row shows in the status footer
|
|
@@ -434,7 +454,7 @@ export interface Channel {
|
|
|
434
454
|
modes: readonly TuiRewindMode[];
|
|
435
455
|
} | 'cancel' | null>;
|
|
436
456
|
/** Switch the live agent to a persisted session, replaying its history. */
|
|
437
|
-
resumeTo(sessionId: string): Promise<
|
|
457
|
+
resumeTo(sessionId: string): Promise<ResumeResult>;
|
|
438
458
|
/** Start a fresh conversation (`/new`): a brand-new agent + session, the
|
|
439
459
|
* transcript cleared, the resume marker forgotten. */
|
|
440
460
|
newSession(): Promise<boolean>;
|
|
@@ -662,8 +682,12 @@ export interface ChannelState {
|
|
|
662
682
|
activityFrames: string | undefined;
|
|
663
683
|
/** Diff presentation preference (see the public Channel type). */
|
|
664
684
|
diffLayout: 'auto' | 'split' | 'unified';
|
|
685
|
+
/** Thinking-block display (see the public Channel type). */
|
|
686
|
+
thinkingFold: 'preview' | 'full';
|
|
665
687
|
/** Apply a diff-layout change (see the public Channel type). */
|
|
666
688
|
setDiffLayout(layout: 'auto' | 'split' | 'unified'): void;
|
|
689
|
+
/** Apply a thinking-display change (see the public Channel type). */
|
|
690
|
+
setThinkingFold(mode: 'preview' | 'full'): void;
|
|
667
691
|
/** Working-activity display switch (see the public Channel type). */
|
|
668
692
|
activityEnabled: boolean;
|
|
669
693
|
/** Context bar row switch (see the public Channel type). */
|
|
@@ -725,7 +749,7 @@ export interface ChannelState {
|
|
|
725
749
|
modes: readonly TuiRewindMode[];
|
|
726
750
|
} | 'cancel' | null>;
|
|
727
751
|
/** Switch the live agent to a persisted session, replaying its history. */
|
|
728
|
-
resumeTo(sessionId: string): Promise<
|
|
752
|
+
resumeTo(sessionId: string): Promise<ResumeResult>;
|
|
729
753
|
/** Start a fresh conversation (`/new`). */
|
|
730
754
|
newSession(): Promise<boolean>;
|
|
731
755
|
listWorkspaces(): Promise<readonly TuiWorkspaceTarget[]>;
|
|
@@ -837,6 +861,9 @@ export declare function createChannel(ctx: Context, initialAgent: Agent, options
|
|
|
837
861
|
/** Edit/Write diff presentation; default `auto` (side-by-side ≥110
|
|
838
862
|
* columns, unified below). */
|
|
839
863
|
diffLayout?: 'auto' | 'split' | 'unified';
|
|
864
|
+
/** Thinking-block display; default `preview` (2-3 line live preview,
|
|
865
|
+
* fold per step) — `full` keeps thinking expanded until turn end. */
|
|
866
|
+
thinkingFold?: 'preview' | 'full';
|
|
840
867
|
/** Show the segmented context bar row in the status footer; default on
|
|
841
868
|
* (cordis.yml `contextBar: false` hides it, issue #29). */
|
|
842
869
|
contextBar?: boolean;
|