@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
|
@@ -44,7 +44,7 @@ export function RewindPicker({ rows, focusIndex, confirmRow, modes = null, modeI
|
|
|
44
44
|
//(ListItem 保证单行截断)。rewind 是不可见确认的高危操作,焦点必须
|
|
45
45
|
// 始终在屏。框架行:浮层预留 8 + Pane 2 + 标题块 3 + 页脚 1 = 14。
|
|
46
46
|
const { start, end } = listWindow(rows.map((_, i) => (i === 0 ? 2 : 1)), focusIndex, Math.max(terminalRows - 14, 2));
|
|
47
|
-
return (_jsxs(Pane, { color: "permission", children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: "remember", bold: true, children: t('rewind-title') }), _jsx(Text, { dimColor: true, children: t('rewind-subtitle') })] }), rows.length === 0 ? (_jsx(ListItem, { isFocused: false, children: t('rewind-empty') })) : (rows.slice(start, end).map((row, index) => {
|
|
47
|
+
return (_jsxs(Pane, { color: "permission", children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [_jsx(Text, { color: "remember", bold: true, children: t('rewind-title') }), _jsx(Text, { dimColor: true, children: t('rewind-subtitle') })] }), rows.length === 0 ? (_jsx(ListItem, { isFocused: false, children: t('rewind-empty') })) : (rows.slice(start, end).map((row, index) => {
|
|
48
48
|
const absoluteIndex = start + index;
|
|
49
49
|
return (_jsx(ListItem, { isFocused: absoluteIndex === focusIndex, description: absoluteIndex === 0 ? t('rewind-last-message') : undefined, showScrollUp: absoluteIndex === start && start > 0, showScrollDown: absoluteIndex === end - 1 && end < rows.length, onClick: onPickRow !== undefined && !busy
|
|
50
50
|
? () => onPickRow(absoluteIndex)
|
|
@@ -3,7 +3,7 @@ import type { ToolFileDiff } from '../dsh-adapter/channel.js';
|
|
|
3
3
|
export { chalkFromToken } from '../cc/syntaxTheme.js';
|
|
4
4
|
export { parseAnsiRuns, highlightLines } from '../cc/syntaxRuns.js';
|
|
5
5
|
import type { ToolBackground } from '../tuiDisplayPrefs.js';
|
|
6
|
-
export declare function SplitDiffView({ diffs, width, maxRows, verbose, toolBackground, }: {
|
|
6
|
+
export declare function SplitDiffView({ diffs, width, maxRows, verbose, toolBackground, reveal, }: {
|
|
7
7
|
readonly diffs: readonly ToolFileDiff[];
|
|
8
8
|
/** Content width available to the whole two-pane block (divider included). */
|
|
9
9
|
readonly width: number;
|
|
@@ -11,5 +11,15 @@ export declare function SplitDiffView({ diffs, width, maxRows, verbose, toolBack
|
|
|
11
11
|
readonly maxRows: number;
|
|
12
12
|
readonly verbose: boolean;
|
|
13
13
|
readonly toolBackground?: ToolBackground;
|
|
14
|
+
/**
|
|
15
|
+
* Smooth-streaming participation (the card owning this view computes
|
|
16
|
+
* eligibility): when present, the capped row list reveals line-by-line at
|
|
17
|
+
* the shared ~30fps cadence instead of painting as one block. The `+N
|
|
18
|
+
* lines` fold hint stays rendered throughout — it describes the cap, not
|
|
19
|
+
* the reveal.
|
|
20
|
+
*/
|
|
21
|
+
readonly reveal?: {
|
|
22
|
+
readonly key: string;
|
|
23
|
+
};
|
|
14
24
|
}): React.ReactNode;
|
|
15
25
|
//# sourceMappingURL=SplitDiffView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SplitDiffView.d.ts","sourceRoot":"","sources":["../../../src/components/SplitDiffView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAO7D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAGnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"SplitDiffView.d.ts","sourceRoot":"","sources":["../../../src/components/SplitDiffView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAO7D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAGnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAwR3D,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,KAAK,EACL,OAAO,EACP,OAAO,EACP,cAAuB,EACvB,MAAM,GACP,EAAE;IACD,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;IACvC,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAA;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAC3C,GAAG,KAAK,CAAC,SAAS,CAwFlB"}
|
|
@@ -11,6 +11,7 @@ export { chalkFromToken } from '../cc/syntaxTheme.js';
|
|
|
11
11
|
export { parseAnsiRuns, highlightLines } from '../cc/syntaxRuns.js';
|
|
12
12
|
import { getTheme } from '../theme.js';
|
|
13
13
|
import { useTheme } from './design-system/ThemeProvider.js';
|
|
14
|
+
import { revealLinesOf } from './smoothReveal.js';
|
|
14
15
|
/** Replace tabs so width math and alignment hold (pi convention). */
|
|
15
16
|
const expandTabs = (text) => text.replaceAll('\t', ' ');
|
|
16
17
|
/**
|
|
@@ -210,7 +211,7 @@ function PaneLine({ side, kind, width, tone, toolBackground, padLeft = false, })
|
|
|
210
211
|
const prefix = padLeft ? ` ${marker}` : marker;
|
|
211
212
|
return (_jsxs(Box, { width: width, flexShrink: 0, backgroundColor: backgroundColor, children: [_jsx(Text, { dimColor: true, backgroundColor: backgroundColor, children: `${prefix} ` }), side === undefined ? (_jsx(Text, { backgroundColor: backgroundColor, children: " " })) : (_jsx(Text, { wrap: "truncate", backgroundColor: backgroundColor, children: side.segments.map((segment, index) => segment.changed ? (_jsx(Text, { color: wordColor, bold: true, backgroundColor: backgroundColor, children: segment.text }, index)) : (_jsx(Text, { color: segment.color, backgroundColor: backgroundColor, children: segment.text }, index))) }))] }));
|
|
212
213
|
}
|
|
213
|
-
export function SplitDiffView({ diffs, width, maxRows, verbose, toolBackground = 'none', }) {
|
|
214
|
+
export function SplitDiffView({ diffs, width, maxRows, verbose, toolBackground = 'none', reveal, }) {
|
|
214
215
|
const [hl, setHl] = React.useState(null);
|
|
215
216
|
React.useEffect(() => {
|
|
216
217
|
let mounted = true;
|
|
@@ -243,6 +244,12 @@ export function SplitDiffView({ diffs, width, maxRows, verbose, toolBackground =
|
|
|
243
244
|
const capped = verbose || totalRows <= maxRows || totalRows - maxRows === 1;
|
|
244
245
|
const visible = capped ? rows : rows.slice(0, maxRows);
|
|
245
246
|
const hidden = totalRows - visible.length;
|
|
247
|
+
// Smooth reveal reads happen during render (the owning card subscribes to
|
|
248
|
+
// the scheduler's version, so its re-render drives this view too).
|
|
249
|
+
const revealedRows = reveal !== undefined
|
|
250
|
+
? revealLinesOf(reveal.key, visible.length, { enabled: true, active: true })
|
|
251
|
+
: visible.length;
|
|
252
|
+
const shown = revealedRows >= visible.length ? visible : visible.slice(0, revealedRows);
|
|
246
253
|
const paneWidth = Math.max(20, Math.floor((width - 1) / 2));
|
|
247
254
|
// Whole-hunk highlight per file (multi-line lexer state preserved);
|
|
248
255
|
// only the visible rows merge syntax runs with word flags below.
|
|
@@ -253,7 +260,7 @@ export function SplitDiffView({ diffs, width, maxRows, verbose, toolBackground =
|
|
|
253
260
|
next: highlightLines(expandTabs(diff.newText), language, hl, syntaxTheme, themeSig),
|
|
254
261
|
};
|
|
255
262
|
});
|
|
256
|
-
return (_jsxs(Box, { flexDirection: "column", width: paneWidth * 2 + 1, children: [
|
|
263
|
+
return (_jsxs(Box, { flexDirection: "column", width: paneWidth * 2 + 1, children: [shown.map((row, index) => {
|
|
257
264
|
if ('separator' in row) {
|
|
258
265
|
return (_jsx(Box, { width: paneWidth * 2 + 1, children: _jsx(Text, { dimColor: true, wrap: "truncate", children: row.separator === '⋯' ? '⋯' : ` ${row.separator}` }) }, index));
|
|
259
266
|
}
|
|
@@ -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;AAmLzB,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,CAkFlB"}
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { marked } from 'marked';
|
|
4
4
|
import Box from '../ink/components/Box.js';
|
|
5
|
-
import { stripPromptXMLTags } from '../cc/markdown.js';
|
|
5
|
+
import { formatToken, stripPromptXMLTags } from '../cc/markdown.js';
|
|
6
6
|
import { t } from '../i18n.js';
|
|
7
7
|
import { Markdown } from './Markdown.js';
|
|
8
8
|
/**
|
|
@@ -47,6 +47,113 @@ function clipSuffixTail(suffix, cut) {
|
|
|
47
47
|
const dropped = cut.current;
|
|
48
48
|
return `${t('streaming-folded', { count: dropped })}\n\n${suffix.slice(cut.current)}`;
|
|
49
49
|
}
|
|
50
|
+
const UNSAFE_BOUNDARY = {
|
|
51
|
+
safe: false,
|
|
52
|
+
gap: 0,
|
|
53
|
+
endsWithTable: false,
|
|
54
|
+
trailingEmptyTextNode: false,
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Token types whose formatted output is provably blank, taken from
|
|
58
|
+
* `cc/markdown.ts`'s dispatcher table: `space`/`br` emit a single newline,
|
|
59
|
+
* `def`/`del`/`html` emit nothing. Kept in step with `analyzeSuffixStart`,
|
|
60
|
+
* which splits the same set into "newline" and "empty" halves.
|
|
61
|
+
*/
|
|
62
|
+
function isBlankTokenType(type) {
|
|
63
|
+
return type === 'space' || type === 'br' || type === 'def' || type === 'del' || type === 'html';
|
|
64
|
+
}
|
|
65
|
+
/** Newlines contributed by a blank token: one for `space`/`br`, none otherwise. */
|
|
66
|
+
function blankTokenNewlines(type) {
|
|
67
|
+
return type === 'space' || type === 'br' ? 1 : 0;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Analyze the candidate stable tokens using the same formatter as Markdown.
|
|
71
|
+
* Both split halves trim their outer whitespace, so the trailing newline count
|
|
72
|
+
* determines the Yoga gap: one newline merely starts the next row; every
|
|
73
|
+
* additional newline is one genuinely blank row. Tables are separate layout
|
|
74
|
+
* nodes and therefore keep Markdown's fixed one-row node gap.
|
|
75
|
+
*
|
|
76
|
+
* Only the LAST token that can produce visible text decides the outcome, so
|
|
77
|
+
* the region is walked backwards and formatted one token at a time instead of
|
|
78
|
+
* concatenating (and syntax-highlighting) every settled block on each new
|
|
79
|
+
* boundary. `formatToken` runs once for the common case.
|
|
80
|
+
*/
|
|
81
|
+
function analyzeStableBoundary(tokens, suffixIndex) {
|
|
82
|
+
// A table resets the accumulated text, so only the segment after the last
|
|
83
|
+
// one matters; anything before it is already folded into the table branch.
|
|
84
|
+
let segmentStart = 0;
|
|
85
|
+
let hasTable = false;
|
|
86
|
+
for (let i = suffixIndex - 1; i >= 0; i--) {
|
|
87
|
+
if (tokens[i].type === 'table') {
|
|
88
|
+
segmentStart = i + 1;
|
|
89
|
+
hasTable = true;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Skip the trailing run of blank tokens, banking their newlines: they sit
|
|
94
|
+
// after the last visible text and therefore only extend its newline run.
|
|
95
|
+
let blankNewlines = 0;
|
|
96
|
+
let cursor = suffixIndex - 1;
|
|
97
|
+
while (cursor >= segmentStart && isBlankTokenType(tokens[cursor].type)) {
|
|
98
|
+
blankNewlines += blankTokenNewlines(tokens[cursor].type);
|
|
99
|
+
cursor--;
|
|
100
|
+
}
|
|
101
|
+
// Format backwards until a token actually produces visible text. A token
|
|
102
|
+
// that formats to blank is folded into the newline run and skipped over.
|
|
103
|
+
for (let i = cursor; i >= segmentStart; i--) {
|
|
104
|
+
const token = tokens[i];
|
|
105
|
+
const ansiText = formatToken(token);
|
|
106
|
+
if (ansiText.trim() === '') {
|
|
107
|
+
blankNewlines += ansiText.match(/\n+$/)?.[0].length ?? 0;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const trailingNewlines = (ansiText.match(/\n+$/)?.[0].length ?? 0) + blankNewlines;
|
|
111
|
+
// A zero-newline boundary (notably `hr` followed immediately by prose)
|
|
112
|
+
// belongs in one Markdown component; separate column children would force
|
|
113
|
+
// a line break that the whole-document formatter does not contain.
|
|
114
|
+
if (trailingNewlines === 0)
|
|
115
|
+
return UNSAFE_BOUNDARY;
|
|
116
|
+
return {
|
|
117
|
+
safe: true,
|
|
118
|
+
gap: Math.max(0, trailingNewlines - 1),
|
|
119
|
+
endsWithTable: false,
|
|
120
|
+
trailingEmptyTextNode: false,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
if (hasTable) {
|
|
124
|
+
return {
|
|
125
|
+
safe: true,
|
|
126
|
+
gap: 1,
|
|
127
|
+
endsWithTable: true,
|
|
128
|
+
// Whitespace after the table still occupies a zero-height text node
|
|
129
|
+
// between two tables.
|
|
130
|
+
trailingEmptyTextNode: blankNewlines > 0,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
// Definitions/raw HTML alone render nothing; advancing them buys no stable
|
|
134
|
+
// work and can lose whitespace that belongs to the next visible block.
|
|
135
|
+
return UNSAFE_BOUNDARY;
|
|
136
|
+
}
|
|
137
|
+
/** Find the first visible suffix node without formatting the growing token.
|
|
138
|
+
* Whitespace around invisible definitions/HTML is internal in a whole render
|
|
139
|
+
* but gets trimmed at the start of the split suffix, so count it into the
|
|
140
|
+
* boundary gap explicitly. */
|
|
141
|
+
function analyzeSuffixStart(tokens, startIndex) {
|
|
142
|
+
let leadingNewlines = 0;
|
|
143
|
+
for (let i = startIndex; i < tokens.length; i++) {
|
|
144
|
+
const type = tokens[i]?.type;
|
|
145
|
+
if (type === 'table')
|
|
146
|
+
return { kind: 'table', leadingNewlines };
|
|
147
|
+
if (type === 'space' || type === 'br') {
|
|
148
|
+
leadingNewlines += 1;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (type === 'def' || type === 'del' || type === 'html')
|
|
152
|
+
continue;
|
|
153
|
+
return { kind: 'text', leadingNewlines };
|
|
154
|
+
}
|
|
155
|
+
return { kind: undefined, leadingNewlines };
|
|
156
|
+
}
|
|
50
157
|
export function StreamingMarkdown({ children, dimColor = false, }) {
|
|
51
158
|
// The stable prefix is kept as ONE string identity across renders: a
|
|
52
159
|
// fresh substring per render would break Markdown's React.memo and
|
|
@@ -54,11 +161,19 @@ export function StreamingMarkdown({ children, dimColor = false, }) {
|
|
|
54
161
|
// identity only changes when a new block boundary advances the prefix.
|
|
55
162
|
const prefixRef = React.useRef('');
|
|
56
163
|
const cutRef = React.useRef(0);
|
|
164
|
+
const boundaryGapRef = React.useRef(0);
|
|
165
|
+
const prefixVisibleRef = React.useRef(false);
|
|
166
|
+
const prefixEndsWithTableRef = React.useRef(false);
|
|
167
|
+
const prefixTrailingEmptyTextRef = React.useRef(false);
|
|
57
168
|
const stripped = stripPromptXMLTags(children);
|
|
58
169
|
// Reset if text was replaced (defensive; normally unmount handles this)
|
|
59
170
|
if (!stripped.startsWith(prefixRef.current)) {
|
|
60
171
|
prefixRef.current = '';
|
|
61
172
|
cutRef.current = 0;
|
|
173
|
+
boundaryGapRef.current = 0;
|
|
174
|
+
prefixVisibleRef.current = false;
|
|
175
|
+
prefixEndsWithTableRef.current = false;
|
|
176
|
+
prefixTrailingEmptyTextRef.current = false;
|
|
62
177
|
}
|
|
63
178
|
// Lex only from current boundary — O(unstable length), not O(full text)
|
|
64
179
|
const boundary = prefixRef.current.length;
|
|
@@ -72,12 +187,34 @@ export function StreamingMarkdown({ children, dimColor = false, }) {
|
|
|
72
187
|
for (let i = 0; i < lastContentIdx; i++) {
|
|
73
188
|
advance += tokens[i].raw.length;
|
|
74
189
|
}
|
|
190
|
+
let suffixTokenIndex = 0;
|
|
75
191
|
if (advance > 0) {
|
|
76
|
-
|
|
192
|
+
const stableBoundary = analyzeStableBoundary(tokens, lastContentIdx);
|
|
193
|
+
if (stableBoundary.safe) {
|
|
194
|
+
prefixRef.current = stripped.substring(0, boundary + advance);
|
|
195
|
+
boundaryGapRef.current = stableBoundary.gap;
|
|
196
|
+
prefixVisibleRef.current = true;
|
|
197
|
+
prefixEndsWithTableRef.current = stableBoundary.endsWithTable;
|
|
198
|
+
prefixTrailingEmptyTextRef.current = stableBoundary.trailingEmptyTextNode;
|
|
199
|
+
suffixTokenIndex = lastContentIdx;
|
|
200
|
+
}
|
|
77
201
|
}
|
|
78
202
|
const stablePrefix = prefixRef.current;
|
|
79
203
|
const suffixSource = stripped.substring(stablePrefix.length);
|
|
80
204
|
const unstableSuffix = clipSuffixTail(suffixSource, cutRef);
|
|
205
|
+
const suffixStart = cutRef.current > 0
|
|
206
|
+
? { kind: 'text', leadingNewlines: 0 }
|
|
207
|
+
: analyzeSuffixStart(tokens, suffixTokenIndex);
|
|
208
|
+
const boundaryGap = prefixVisibleRef.current && suffixStart.kind !== undefined
|
|
209
|
+
? prefixEndsWithTableRef.current
|
|
210
|
+
? suffixStart.kind === 'table' &&
|
|
211
|
+
(prefixTrailingEmptyTextRef.current || suffixStart.leadingNewlines > 0)
|
|
212
|
+
? 2
|
|
213
|
+
: 1
|
|
214
|
+
: suffixStart.kind === 'table'
|
|
215
|
+
? 1
|
|
216
|
+
: boundaryGapRef.current + suffixStart.leadingNewlines
|
|
217
|
+
: 0;
|
|
81
218
|
// A lexer boundary must be strictly outside the stable prefix. If marked
|
|
82
219
|
// reports a raw span that ends at the current cursor (possible around an
|
|
83
220
|
// unfinished fence/table while deltas arrive), rendering both branches can
|
|
@@ -85,5 +222,5 @@ export function StreamingMarkdown({ children, dimColor = false, }) {
|
|
|
85
222
|
// an overlapping empty/duplicate boundary.
|
|
86
223
|
const hasDistinctSuffix = unstableSuffix !== '' &&
|
|
87
224
|
(stablePrefix === '' || !unstableSuffix.startsWith(stablePrefix));
|
|
88
|
-
return (_jsxs(Box, { flexDirection: "column", gap:
|
|
225
|
+
return (_jsxs(Box, { flexDirection: "column", gap: boundaryGap, children: [stablePrefix && _jsx(Markdown, { dimColor: dimColor, children: stablePrefix }), hasDistinctSuffix && _jsx(Markdown, { dimColor: dimColor, cacheTokens: false, children: unstableSuffix })] }));
|
|
89
226
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubagentDashboard.d.ts","sourceRoot":"","sources":["../../../src/components/SubagentDashboard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAMhE,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,aAAa,EAAE,CAAA;IAC1B,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACrC;AAED,yCAAyC;AACzC,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,GAAG,KAAK,CAAC,SAAS,CAWhF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,SAAS,EACT,OAAO,EACP,QAAQ,EACT,EAAE,sBAAsB,GAAG,KAAK,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"SubagentDashboard.d.ts","sourceRoot":"","sources":["../../../src/components/SubagentDashboard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAMhE,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,aAAa,EAAE,CAAA;IAC1B,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACrC;AAED,yCAAyC;AACzC,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,GAAG,KAAK,CAAC,SAAS,CAWhF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,SAAS,EACT,OAAO,EACP,QAAQ,EACT,EAAE,sBAAsB,GAAG,KAAK,CAAC,SAAS,CA0G1C"}
|
|
@@ -49,10 +49,10 @@ export function SubagentDashboard({ subagents, onClose, onSelect }) {
|
|
|
49
49
|
const running = subagents.filter(s => s.status === 'running').length;
|
|
50
50
|
const completed = subagents.filter(s => s.status === 'completed').length;
|
|
51
51
|
const failed = subagents.filter(s => s.status === 'failed').length;
|
|
52
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsx(Divider, { color: "claude", title: t('subagent-dashboard-title')
|
|
52
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsx(Divider, { color: "claude", title: t('subagent-dashboard-title') }), _jsxs(Box, { flexDirection: "row", gap: 3, marginTop: 1, marginBottom: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: "claude", children: running }), _jsxs(Text, { dimColor: true, children: [" ", t('subagent-count-running')] })] }), _jsxs(Text, { children: [_jsx(Text, { color: "success", children: completed }), _jsxs(Text, { dimColor: true, children: [" ", t('subagent-count-completed')] })] }), failed > 0 && (_jsxs(Text, { children: [_jsx(Text, { color: "error", children: failed }), _jsxs(Text, { dimColor: true, children: [" ", t('subagent-count-failed')] })] })), _jsx(Box, { flexGrow: 1 }), _jsx(ExitButton, { onClick: onClose })] }), _jsx(Box, { flexDirection: "column", maxHeight: Math.max(10, rows - 10), marginTop: 1, children: _jsx(ScrollBox, { ref: scrollRef, flexDirection: "column", flexGrow: 1, children: subagents.length === 0 ? (_jsxs(Box, { flexDirection: "column", alignItems: "center", marginTop: Math.max(2, Math.floor((rows - 16) / 3)), children: [_jsx(Text, { dimColor: true, children: '○' }), _jsx(Text, { dimColor: true, children: t('subagent-none') }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: t('subagent-empty-hint') }) })] })) : (subagents.map((subagent, index) => (_jsxs(Box, { flexDirection: "column", children: [_jsx(SubagentCard, { subagent: subagent, focused: index === focusIndex, onClick: onSelect !== undefined
|
|
53
53
|
// Click = view detail, same as Enter on the focused card.
|
|
54
54
|
? () => onSelect(subagent.agentId)
|
|
55
|
-
: undefined }), index < subagents.length - 1 && (_jsx(Text, { dimColor: true, children: '─'.repeat(Math.max(20, Math.min(72, columns - 6))) }))] }, subagent.agentId)))) }) }), _jsx(Divider, { color: "subtle", title: ""
|
|
55
|
+
: undefined }), index < subagents.length - 1 && (_jsx(Text, { dimColor: true, children: '─'.repeat(Math.max(20, Math.min(72, columns - 6))) }))] }, subagent.agentId)))) }) }), _jsx(Divider, { color: "subtle", title: "" }), _jsx(Box, { marginTop: 0, children: _jsx(Text, { dimColor: true, children: onSelect
|
|
56
56
|
? t('subagent-dashboard-hint-detail')
|
|
57
57
|
: t('subagent-dashboard-hint-basic') }) })] }));
|
|
58
58
|
}
|
|
@@ -138,5 +138,5 @@ export function SubagentDetailScene({ subagent, onBack, onInterrupt, }) {
|
|
|
138
138
|
return (_jsxs(React.Fragment, { children: [_jsx(Box, { onClick: () => setPage(name), backgroundColor: !active ? 'userMessageBackgroundHover' : undefined, children: _jsx(Text, { color: active ? 'claude' : undefined, bold: active, inverse: active, children: ` ${label} ` }) }), _jsx(Text, { dimColor: true, children: name === PAGES[PAGES.length - 1] ? '' : '│' })] }, name));
|
|
139
139
|
};
|
|
140
140
|
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: info.color, bold: true, children: info.glyph }), _jsx(Text, { bold: true, children: `${t('subagent-card-prefix')}${subagent.description}` }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { color: info.color, children: info.label }), _jsx(Box, { flexGrow: 1 }), _jsx(ExitButton, { onClick: onBack })] }), _jsxs(Text, { children: [_jsx(Text, { children: subagent.model ?? subagent.provider ?? 'default' }), _jsx(Text, { dimColor: true, children: ` · ${formatDuration(elapsed)} · ${totalTokens || '—'} tok · ${subagent.toolCalls.length} tools` })] }), _jsxs(Text, { dimColor: true, children: [`${t('subagent-started')} ${formatTimestamp(subagent.startedAt)}`
|
|
141
|
-
+ (subagent.completedAt ? ` · ${t('subagent-completed')} ${formatTimestamp(subagent.completedAt)}` : ''), ` · id ${subagent.agentId}`] }), subagent.error && (_jsx(Box, { marginTop: 0, children: _jsx(Text, { color: "error", wrap: "wrap", children: `${t('subagent-error-label')}: ${subagent.error}` }) })), _jsxs(Box, { flexDirection: "row", gap: 0, marginTop: 1, children: [tab('summary', t('subagent-tab-summary')), tab('output', t('subagent-output-label')), tab('tools', t('subagent-tools')), _jsx(Text, { dimColor: true, children: ` ${pageIndex + 1}/${PAGES.length}` })] }), _jsx(Text, { dimColor: true, children: '─'.repeat(Math.max(20, Math.min(72, columns - 6))) }), _jsx(Box, { flexDirection: "column", paddingX: 1, maxHeight: Math.max(10, rows - 14), children: _jsxs(ScrollBox, { ref: scrollRef, flexDirection: "column", flexGrow: 1, children: [page === 'summary' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatGrid, { subagent: subagent, totalTokens: totalTokens, elapsed: elapsed, statusLabel: info.label, statusColor: info.color }), subagent.summary && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, bold: true, children: '─ summary ' }), _jsx(Text, { wrap: "wrap", children: subagent.summary })] })), !subagent.summary && (_jsx(Text, { dimColor: true, children: isRunning ? t('subagent-no-output') : t('subagent-no-summary') }))] })), page === 'output' && (subagent.outputEvents.length === 0 && subagent.output.length === 0 ? (_jsx(Text, { dimColor: true, children: t('subagent-no-output') })) : (subagent.outputEvents.map((line, index) => (_jsxs(Text, { wrap: "wrap", dimColor: line.kind === 'thinking' || line.kind === 'system', color: line.kind === 'error' ? 'error' : undefined, children: [line.kind === 'thinking' ? ' ⌁ ' : ' ', line.text, !line.settled && isRunning ? ' ▍' : ''] }, index))))), page === 'tools' && (subagent.toolCalls.length === 0 ? (_jsx(Text, { dimColor: true, children: t('subagent-no-tools') })) : (subagent.toolCalls.map((tool, index) => (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: tool.status === 'failed' ? 'error' : tool.status === 'running' ? 'warning' : 'success', children: tool.status === 'running' ? '·' : tool.status === 'failed' ? '×' : '✓' }), _jsx(Text, { color: toolNameColor(tool.name), children: tool.name }), tool.endedAt && _jsx(Text, { dimColor: true, children: formatDuration(tool.endedAt - tool.startedAt) })] }), tool.argsPreview && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(JsonArgsText, { raw: tool.argsPreview }) })), tool.resultPreview && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(Text, { dimColor: true, wrap: "wrap", children: `⎿ ${tool.resultPreview}` }) })), tool.error && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(Text, { color: "error", wrap: "wrap", children: tool.error }) }))] }, tool.id ?? index)))))] }) }), _jsx(Divider, { color: "subtle", title: ""
|
|
141
|
+
+ (subagent.completedAt ? ` · ${t('subagent-completed')} ${formatTimestamp(subagent.completedAt)}` : ''), ` · id ${subagent.agentId}`] }), subagent.error && (_jsx(Box, { marginTop: 0, children: _jsx(Text, { color: "error", wrap: "wrap", children: `${t('subagent-error-label')}: ${subagent.error}` }) })), _jsxs(Box, { flexDirection: "row", gap: 0, marginTop: 1, children: [tab('summary', t('subagent-tab-summary')), tab('output', t('subagent-output-label')), tab('tools', t('subagent-tools')), _jsx(Text, { dimColor: true, children: ` ${pageIndex + 1}/${PAGES.length}` })] }), _jsx(Text, { dimColor: true, children: '─'.repeat(Math.max(20, Math.min(72, columns - 6))) }), _jsx(Box, { flexDirection: "column", paddingX: 1, maxHeight: Math.max(10, rows - 14), children: _jsxs(ScrollBox, { ref: scrollRef, flexDirection: "column", flexGrow: 1, children: [page === 'summary' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatGrid, { subagent: subagent, totalTokens: totalTokens, elapsed: elapsed, statusLabel: info.label, statusColor: info.color }), subagent.summary && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, bold: true, children: '─ summary ' }), _jsx(Text, { wrap: "wrap", children: subagent.summary })] })), !subagent.summary && (_jsx(Text, { dimColor: true, children: isRunning ? t('subagent-no-output') : t('subagent-no-summary') }))] })), page === 'output' && (subagent.outputEvents.length === 0 && subagent.output.length === 0 ? (_jsx(Text, { dimColor: true, children: t('subagent-no-output') })) : (subagent.outputEvents.map((line, index) => (_jsxs(Text, { wrap: "wrap", dimColor: line.kind === 'thinking' || line.kind === 'system', color: line.kind === 'error' ? 'error' : undefined, children: [line.kind === 'thinking' ? ' ⌁ ' : ' ', line.text, !line.settled && isRunning ? ' ▍' : ''] }, index))))), page === 'tools' && (subagent.toolCalls.length === 0 ? (_jsx(Text, { dimColor: true, children: t('subagent-no-tools') })) : (subagent.toolCalls.map((tool, index) => (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: tool.status === 'failed' ? 'error' : tool.status === 'running' ? 'warning' : 'success', children: tool.status === 'running' ? '·' : tool.status === 'failed' ? '×' : '✓' }), _jsx(Text, { color: toolNameColor(tool.name), children: tool.name }), tool.endedAt && _jsx(Text, { dimColor: true, children: formatDuration(tool.endedAt - tool.startedAt) })] }), tool.argsPreview && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(JsonArgsText, { raw: tool.argsPreview }) })), tool.resultPreview && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(Text, { dimColor: true, wrap: "wrap", children: `⎿ ${tool.resultPreview}` }) })), tool.error && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(Text, { color: "error", wrap: "wrap", children: tool.error }) }))] }, tool.id ?? index)))))] }) }), _jsx(Divider, { color: "subtle", title: "" }), _jsxs(Box, { marginTop: 0, flexDirection: "row", children: [_jsx(Text, { dimColor: true, children: `←/→ ${t('subagent-hint-page')} · ↑/↓ ${t('subagent-hint-scroll')}` }), isRunning && onInterrupt && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: ' · ' }), _jsx(Box, { onClick: () => onInterrupt(subagent.agentId), children: _jsx(Text, { dimColor: true, bold: true, color: "warning", children: "X interrupt" }) })] })), _jsx(Text, { dimColor: true, children: ` · Esc ${t('subagent-hint-back')}` })] })] }));
|
|
142
142
|
}
|
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { type SelectOption } from './Select.js';
|
|
3
|
+
import type { TuiThemeHost } from '../dsh-adapter/themes.js';
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* ~/.dsh-tui/themes (sorted by file name). A user theme named `auto` is
|
|
8
|
-
* shadowed by the built-in pseudo-theme (getTheme resolves `auto` first),
|
|
9
|
-
* so it is filtered out to keep the list truthful. Shared by ThemePicker
|
|
10
|
-
* (render) and the /theme command (focus index), so both always see the
|
|
11
|
-
* same ordering.
|
|
5
|
+
* Build the full selectable list from the shared catalog: `auto`, built-ins,
|
|
6
|
+
* static JSON themes, and optional plugin themes. Shared by ThemePicker (render)
|
|
7
|
+
* and the /theme command (focus index), so both always see the same ordering.
|
|
12
8
|
*/
|
|
13
|
-
export declare function getThemeOptions(): SelectOption[];
|
|
9
|
+
export declare function getThemeOptions(themeHost?: TuiThemeHost): SelectOption[];
|
|
14
10
|
/**
|
|
15
11
|
* Color-theme picker in the ActivityPicker style: a permission-colored Pane
|
|
16
|
-
* listing the `auto` pseudo-theme and
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
12
|
+
* listing the `auto` pseudo-theme and built-in palettes first, followed by
|
|
13
|
+
* static JSON and plugin themes — each row shows the display name, base and
|
|
14
|
+
* three key color swatches; `❯` marks focus, `✓` the active theme. Enter
|
|
15
|
+
* applies through the ThemeProvider setter (persists to ~/.dsh-tui/theme.json
|
|
16
|
+
* and hot swaps), Esc cancels.
|
|
21
17
|
*/
|
|
22
|
-
export declare function ThemePicker({ focusIndex, currentTheme, onPick, }: {
|
|
18
|
+
export declare function ThemePicker({ focusIndex, currentTheme, themeHost, onPick, }: {
|
|
23
19
|
focusIndex: number;
|
|
24
20
|
currentTheme: string | undefined;
|
|
21
|
+
/** Optional runtime theme host; static themes work without it. */
|
|
22
|
+
themeHost?: TuiThemeHost;
|
|
25
23
|
/** Mouse pick (fullscreen): clicked row's absolute index (Chat applies
|
|
26
24
|
* the same code path as the keyboard Enter). */
|
|
27
25
|
onPick?: (index: number) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemePicker.d.ts","sourceRoot":"","sources":["../../../src/components/ThemePicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,EAAU,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"ThemePicker.d.ts","sourceRoot":"","sources":["../../../src/components/ThemePicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,EAAU,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AAIvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAqC5D;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,CAAC,EAAE,YAAY,GAAG,YAAY,EAAE,CAYxE;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,EAC1B,UAAU,EACV,YAAY,EACZ,SAAS,EACT,MAAM,GACP,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,kEAAkE;IAClE,SAAS,CAAC,EAAE,YAAY,CAAA;IACxB;qDACiD;IACjD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC,GAAG,KAAK,CAAC,SAAS,CA2BlB"}
|
|
@@ -5,8 +5,8 @@ import { Box, Text } from '../ui.js';
|
|
|
5
5
|
import { Pane } from './design-system/Pane.js';
|
|
6
6
|
import { Select } from './Select.js';
|
|
7
7
|
import { HintLine } from './design-system/HintLine.js';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { listThemeCatalog } from '../themeCatalog.js';
|
|
9
|
+
import { useRuntimeThemeSnapshot } from '../hooks/useRuntimeThemeSnapshot.js';
|
|
10
10
|
/** One double-block swatch character per preview key, in a row. */
|
|
11
11
|
const SWATCH = '██';
|
|
12
12
|
/** Theme keys previewed in the picker, chosen for visual contrast. */
|
|
@@ -23,35 +23,33 @@ function optionFor(name, displayName, theme, description) {
|
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* ~/.dsh-tui/themes (sorted by file name). A user theme named `auto` is
|
|
30
|
-
* shadowed by the built-in pseudo-theme (getTheme resolves `auto` first),
|
|
31
|
-
* so it is filtered out to keep the list truthful. Shared by ThemePicker
|
|
32
|
-
* (render) and the /theme command (focus index), so both always see the
|
|
33
|
-
* same ordering.
|
|
26
|
+
* Build the full selectable list from the shared catalog: `auto`, built-ins,
|
|
27
|
+
* static JSON themes, and optional plugin themes. Shared by ThemePicker (render)
|
|
28
|
+
* and the /theme command (focus index), so both always see the same ordering.
|
|
34
29
|
*/
|
|
35
|
-
export function getThemeOptions() {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
30
|
+
export function getThemeOptions(themeHost) {
|
|
31
|
+
return listThemeCatalog(themeHost).map(item => {
|
|
32
|
+
const base = item.base ?? 'dark';
|
|
33
|
+
const description = item.source === 'auto'
|
|
34
|
+
? t('theme-auto-base')
|
|
35
|
+
: item.source === 'builtin'
|
|
36
|
+
? t('theme-builtin-base', { name: item.name })
|
|
37
|
+
: item.source === 'runtime'
|
|
38
|
+
? t('theme-plugin-base', { base, name: item.name })
|
|
39
|
+
: t('theme-user-base', { base, name: item.name });
|
|
40
|
+
return optionFor(item.name, item.displayName, item.theme, description);
|
|
40
41
|
});
|
|
41
|
-
const custom = listCustomThemes()
|
|
42
|
-
.filter(spec => spec.name !== AUTO_THEME_NAME)
|
|
43
|
-
.map(spec => optionFor(spec.name, spec.displayName, buildTheme(spec), t('theme-user-base', { base: spec.base, name: spec.name })));
|
|
44
|
-
return [auto, ...builtins, ...custom];
|
|
45
42
|
}
|
|
46
43
|
/**
|
|
47
44
|
* Color-theme picker in the ActivityPicker style: a permission-colored Pane
|
|
48
|
-
* listing the `auto` pseudo-theme and
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
45
|
+
* listing the `auto` pseudo-theme and built-in palettes first, followed by
|
|
46
|
+
* static JSON and plugin themes — each row shows the display name, base and
|
|
47
|
+
* three key color swatches; `❯` marks focus, `✓` the active theme. Enter
|
|
48
|
+
* applies through the ThemeProvider setter (persists to ~/.dsh-tui/theme.json
|
|
49
|
+
* and hot swaps), Esc cancels.
|
|
53
50
|
*/
|
|
54
|
-
export function ThemePicker({ focusIndex, currentTheme, onPick, }) {
|
|
55
|
-
const
|
|
51
|
+
export function ThemePicker({ focusIndex, currentTheme, themeHost, onPick, }) {
|
|
52
|
+
const runtimeThemeSnapshot = useRuntimeThemeSnapshot(themeHost);
|
|
53
|
+
const options = React.useMemo(() => getThemeOptions(themeHost), [runtimeThemeSnapshot, themeHost]);
|
|
56
54
|
return (_jsx(Pane, { color: "permission", children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: t('picker-title-theme') }) }), _jsx(Select, { options: options, focusIndex: focusIndex, selectedValue: currentTheme, visibleOptionCount: 6, onPick: onPick ? index => onPick(index) : undefined }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('hint-confirm-exit') }) })] }) }));
|
|
57
55
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { PointerEvent } from '../ink/events/pointer-event.js';
|
|
3
|
+
/** Tooltip content: a fixed string or a getter resolved at show time. */
|
|
4
|
+
export type TooltipContent = string | (() => string);
|
|
5
|
+
export type TooltipOptions = {
|
|
6
|
+
/** Dwell before the tooltip appears. Default 600ms. */
|
|
7
|
+
delayMs?: number;
|
|
8
|
+
};
|
|
9
|
+
/** Hover props returned by {@link useTooltip}; spread onto a Box. */
|
|
10
|
+
export type TooltipHoverProps = {
|
|
11
|
+
onMouseEnter: (event?: PointerEvent) => void;
|
|
12
|
+
onMouseLeave: () => void;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Per-attachment owner: identifies which element's timer is pending and
|
|
16
|
+
* which element owns the shown tooltip (so one element's leave can never
|
|
17
|
+
* hide another's tooltip).
|
|
18
|
+
*/
|
|
19
|
+
type TooltipOwner = {
|
|
20
|
+
timer: ReturnType<typeof setTimeout> | null;
|
|
21
|
+
};
|
|
22
|
+
type TooltipState = {
|
|
23
|
+
owner: TooltipOwner;
|
|
24
|
+
anchorCol: number;
|
|
25
|
+
anchorRow: number;
|
|
26
|
+
content: string;
|
|
27
|
+
};
|
|
28
|
+
/** Subscribe to tooltip show/hide; returns an unsubscribe function. */
|
|
29
|
+
export declare function subscribeTooltip(listener: () => void): () => void;
|
|
30
|
+
/** Stable snapshot for useSyncExternalStore (identity kept until changed). */
|
|
31
|
+
export declare function getTooltipSnapshot(): TooltipState | null;
|
|
32
|
+
/** Hide shown content and invalidate every pending dwell timer. */
|
|
33
|
+
export declare function clearTooltip(): void;
|
|
34
|
+
/**
|
|
35
|
+
* Hover props that pop a tooltip with the full content after a dwell.
|
|
36
|
+
*
|
|
37
|
+
* The pointer position (screen col/row) is taken from the PointerEvent the
|
|
38
|
+
* renderer hands to `onMouseEnter` — the runtime always passes one even
|
|
39
|
+
* though the prop type is `() => void`, which is why the handler's parameter
|
|
40
|
+
* is optional (a `() => void` prop accepts a function with only optional
|
|
41
|
+
* parameters).
|
|
42
|
+
*/
|
|
43
|
+
export declare function useTooltip(content: TooltipContent, opts?: TooltipOptions): TooltipHoverProps;
|
|
44
|
+
/**
|
|
45
|
+
* Convenience wrapper for list rows built from data (a component boundary
|
|
46
|
+
* makes the hook call legal per item). Renders a plain Box carrying the
|
|
47
|
+
* hover props around `children`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function TooltipTarget({ content, children, }: {
|
|
50
|
+
content: TooltipContent;
|
|
51
|
+
children: React.ReactNode;
|
|
52
|
+
}): React.ReactNode;
|
|
53
|
+
/**
|
|
54
|
+
* The singleton floating layer: subscribes to the tooltip store and renders
|
|
55
|
+
* the rounded card above the anchor row (below it when there is no room),
|
|
56
|
+
* horizontally clamped inside the terminal. Mount once, LAST in the tree so
|
|
57
|
+
* it paints over everything else. Terminal resize hides the tooltip — its
|
|
58
|
+
* anchor is screen geometry that just went stale.
|
|
59
|
+
*/
|
|
60
|
+
export declare function TooltipLayer({ invalidationKey, subscribeInvalidation, }?: {
|
|
61
|
+
/** Changes when a modal/screen geometry transition makes the anchor stale. */
|
|
62
|
+
invalidationKey?: unknown;
|
|
63
|
+
/** Scroll/viewport subscription; every notification invalidates the anchor. */
|
|
64
|
+
subscribeInvalidation?: (listener: () => void) => () => void;
|
|
65
|
+
}): React.ReactNode;
|
|
66
|
+
export {};
|
|
67
|
+
//# sourceMappingURL=Tooltip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tooltip.d.ts","sourceRoot":"","sources":["../../../src/components/Tooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAiBlE,yEAAyE;AACzE,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAA;AAEpD,MAAM,MAAM,cAAc,GAAG;IAC3B,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,qEAAqE;AACrE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,YAAY,EAAE,CAAC,KAAK,CAAC,EAAE,YAAY,KAAK,IAAI,CAAA;IAC5C,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB,CAAA;AAED;;;;GAIG;AACH,KAAK,YAAY,GAAG;IAAE,KAAK,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,GAAG,IAAI,CAAA;CAAE,CAAA;AAEnE,KAAK,YAAY,GAAG;IAClB,KAAK,EAAE,YAAY,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAYD,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAKjE;AAED,8EAA8E;AAC9E,wBAAgB,kBAAkB,IAAI,YAAY,GAAG,IAAI,CAExD;AAcD,mEAAmE;AACnE,wBAAgB,YAAY,IAAI,IAAI,CAMnC;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,iBAAiB,CAuC5F;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,cAAc,CAAA;IACvB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B,GAAG,KAAK,CAAC,SAAS,CAGlB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAC3B,eAAe,EACf,qBAAqB,GACtB,GAAE;IACD,8EAA8E;IAC9E,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,+EAA+E;IAC/E,qBAAqB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAA;CACxD,GAAG,KAAK,CAAC,SAAS,CAgEvB"}
|