@deepseek-harness-tui/dsh-tui 0.10.2 → 0.11.0
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 +171 -124
- package/bin/dsh-tui.js +748 -22
- package/cordis.patch.yml +38 -10
- package/cordis.yml +2 -0
- package/lib/types/adapter/channel/session-projection.d.ts.map +1 -1
- package/lib/types/adapter/channel/session-projection.js +2 -0
- package/lib/types/adapter/channel/ui-policy.d.ts +4 -1
- package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
- package/lib/types/adapter/channel/ui-policy.js +5 -1
- package/lib/types/adapter/kernel/host-facade.d.ts.map +1 -1
- package/lib/types/adapter/kernel/host-facade.js +1 -0
- package/lib/types/adapter/kernel/replay.d.ts.map +1 -1
- package/lib/types/adapter/kernel/replay.js +3 -0
- package/lib/types/adapter/kernel/runtime.d.ts.map +1 -1
- package/lib/types/adapter/kernel/runtime.js +2 -0
- package/lib/types/adapter/kernel/slices/workspace.d.ts.map +1 -1
- package/lib/types/adapter/kernel/slices/workspace.js +1 -0
- package/lib/types/adapter/ports/channel-ui.d.ts +22 -2
- package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-view.d.ts +66 -0
- package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-workspace.d.ts +23 -0
- package/lib/types/adapter/ports/channel-workspace.d.ts.map +1 -1
- package/lib/types/adapter/ports/workspace.d.ts +2 -0
- package/lib/types/adapter/ports/workspace.d.ts.map +1 -1
- package/lib/types/adapter/upstream/workspace-driver.d.ts.map +1 -1
- package/lib/types/adapter/upstream/workspace-driver.js +10 -2
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +1 -0
- package/lib/types/components/ContextBarView.d.ts +15 -7
- package/lib/types/components/ContextBarView.d.ts.map +1 -1
- package/lib/types/components/ContextBarView.js +26 -22
- package/lib/types/components/ImagePreviewOverlay.d.ts.map +1 -1
- package/lib/types/components/ImagePreviewOverlay.js +11 -5
- package/lib/types/components/Markdown.d.ts +11 -1
- package/lib/types/components/Markdown.d.ts.map +1 -1
- package/lib/types/components/Markdown.js +20 -2
- package/lib/types/components/MermaidDiagram.d.ts +13 -0
- package/lib/types/components/MermaidDiagram.d.ts.map +1 -0
- package/lib/types/components/MermaidDiagram.js +61 -0
- package/lib/types/components/MessageList.d.ts +26 -1
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +72 -7
- package/lib/types/components/PageMargin.d.ts +1 -1
- package/lib/types/components/PromptInput.d.ts +37 -1
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +243 -28
- package/lib/types/components/ScrollbarGutter.d.ts +20 -5
- package/lib/types/components/ScrollbarGutter.d.ts.map +1 -1
- package/lib/types/components/ScrollbarGutter.js +88 -10
- package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
- package/lib/types/components/StreamingMarkdown.js +30 -28
- package/lib/types/components/Tooltip.d.ts.map +1 -1
- package/lib/types/components/Tooltip.js +45 -7
- package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
- package/lib/types/components/messages/UserPromptMessage.js +5 -7
- package/lib/types/components/messages/useImageInspection.d.ts.map +1 -1
- package/lib/types/components/messages/useImageInspection.js +19 -9
- package/lib/types/components/promptDraftCache.d.ts +102 -0
- package/lib/types/components/promptDraftCache.d.ts.map +1 -0
- package/lib/types/components/promptDraftCache.js +76 -0
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts +17 -1
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.js +40 -7
- package/lib/types/components/questions/QuestionMinimizedBar.d.ts +12 -0
- package/lib/types/components/questions/QuestionMinimizedBar.d.ts.map +1 -0
- package/lib/types/components/questions/QuestionMinimizedBar.js +43 -0
- package/lib/types/components/sessions/SessionListRow.d.ts +23 -1
- package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
- package/lib/types/components/sessions/SessionListRow.js +52 -5
- package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts +49 -0
- package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts.map +1 -0
- package/lib/types/components/workspaces/HomeWorkspaceRow.js +50 -0
- package/lib/types/dsh-adapter/bundled-presets.d.ts +10 -0
- package/lib/types/dsh-adapter/bundled-presets.d.ts.map +1 -0
- package/lib/types/dsh-adapter/bundled-presets.js +80 -0
- package/lib/types/dsh-adapter/channel/action-readiness.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/action-readiness.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/action-readiness.js +3 -0
- package/lib/types/dsh-adapter/channel/agent-view-projection.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/agent-view-projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/agent-view-projection.js +19 -2
- package/lib/types/dsh-adapter/channel/background-action.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/background-action.js +52 -29
- package/lib/types/dsh-adapter/channel/binding.d.ts +19 -2
- package/lib/types/dsh-adapter/channel/binding.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/binding.js +78 -18
- package/lib/types/dsh-adapter/channel/composer-images.d.ts +5 -0
- package/lib/types/dsh-adapter/channel/composer-images.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/composer-images.js +105 -6
- package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts +8 -0
- package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/context-bookkeeping.js +3 -2
- package/lib/types/dsh-adapter/channel/ide-selection.d.ts +58 -0
- package/lib/types/dsh-adapter/channel/ide-selection.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/ide-selection.js +226 -0
- package/lib/types/dsh-adapter/channel/input-delivery.d.ts +6 -1
- package/lib/types/dsh-adapter/channel/input-delivery.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/input-delivery.js +9 -1
- package/lib/types/dsh-adapter/channel/local-actions.d.ts +3 -19
- package/lib/types/dsh-adapter/channel/local-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/local-actions.js +2 -1
- package/lib/types/dsh-adapter/channel/model-actions.d.ts +4 -1
- package/lib/types/dsh-adapter/channel/model-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/model-actions.js +82 -6
- package/lib/types/dsh-adapter/channel/model-switch.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/model-switch.js +49 -31
- package/lib/types/dsh-adapter/channel/projection.d.ts +4 -0
- package/lib/types/dsh-adapter/channel/projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/projection.js +29 -19
- package/lib/types/dsh-adapter/channel/reports.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/reports.js +4 -12
- package/lib/types/dsh-adapter/channel/session-fork.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-fork.js +26 -2
- package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts +3 -0
- package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-live-adoption.js +1 -0
- package/lib/types/dsh-adapter/channel/session-metadata.d.ts +10 -0
- package/lib/types/dsh-adapter/channel/session-metadata.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-metadata.js +2 -2
- package/lib/types/dsh-adapter/channel/session-resume.d.ts +11 -2
- package/lib/types/dsh-adapter/channel/session-resume.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-resume.js +291 -94
- package/lib/types/dsh-adapter/channel/session-rewind.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-rewind.js +34 -16
- package/lib/types/dsh-adapter/channel/session-tree-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-tree-actions.js +19 -3
- package/lib/types/dsh-adapter/channel/settings-host.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/settings-host.js +4 -3
- package/lib/types/dsh-adapter/channel/state.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/state.js +1 -1
- package/lib/types/dsh-adapter/channel/transcript.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/transcript.js +6 -12
- package/lib/types/dsh-adapter/channel/types.d.ts +6 -1
- package/lib/types/dsh-adapter/channel/types.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/workspace-actions.d.ts +4 -1
- package/lib/types/dsh-adapter/channel/workspace-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/workspace-actions.js +41 -1
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +67 -6
- package/lib/types/dsh-adapter/compat/messages.d.ts +15 -0
- package/lib/types/dsh-adapter/compat/messages.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/messages.js +15 -0
- package/lib/types/dsh-adapter/compat/settings.d.ts +33 -0
- package/lib/types/dsh-adapter/compat/settings.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/settings.js +43 -0
- package/lib/types/dsh-adapter/compat/shell.d.ts +25 -0
- package/lib/types/dsh-adapter/compat/shell.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/shell.js +11 -0
- package/lib/types/dsh-adapter/contract.d.ts +4 -4
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +15 -6
- package/lib/types/dsh-adapter/ide-channel.d.ts +224 -0
- package/lib/types/dsh-adapter/ide-channel.d.ts.map +1 -0
- package/lib/types/dsh-adapter/ide-channel.js +556 -0
- package/lib/types/dsh-adapter/index.d.ts +18 -3
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +32 -5
- package/lib/types/dsh-adapter/plugin.d.ts +5 -2
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +173 -42
- package/lib/types/dsh-adapter/preset-resolution.d.ts +8 -0
- package/lib/types/dsh-adapter/preset-resolution.d.ts.map +1 -1
- package/lib/types/dsh-adapter/preset-resolution.js +10 -0
- package/lib/types/dsh-adapter/session-mount-heartbeat.d.ts +53 -0
- package/lib/types/dsh-adapter/session-mount-heartbeat.d.ts.map +1 -0
- package/lib/types/dsh-adapter/session-mount-heartbeat.js +100 -0
- package/lib/types/dsh-adapter/sessionTree.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessionTree.js +3 -2
- package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
- package/lib/types/dsh-adapter/subagents.js +6 -5
- package/lib/types/dsh-adapter/workspaces.d.ts +46 -3
- package/lib/types/dsh-adapter/workspaces.d.ts.map +1 -1
- package/lib/types/dsh-adapter/workspaces.js +116 -0
- package/lib/types/effortPrefs.d.ts +15 -3
- package/lib/types/effortPrefs.d.ts.map +1 -1
- package/lib/types/effortPrefs.js +37 -3
- package/lib/types/homePrefs.d.ts +15 -0
- package/lib/types/homePrefs.d.ts.map +1 -0
- package/lib/types/homePrefs.js +53 -0
- package/lib/types/i18n.d.ts +257 -296
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +78 -84
- package/lib/types/ink/colorize.d.ts +10 -0
- package/lib/types/ink/colorize.d.ts.map +1 -1
- package/lib/types/ink/colorize.js +21 -0
- package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
- package/lib/types/ink/components/ScrollBox.js +12 -1
- package/lib/types/ink/dom.d.ts +1 -0
- package/lib/types/ink/dom.d.ts.map +1 -1
- package/lib/types/ink/hooks/use-copy-on-select.d.ts +5 -1
- package/lib/types/ink/hooks/use-copy-on-select.d.ts.map +1 -1
- package/lib/types/ink/hooks/use-copy-on-select.js +15 -1
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +36 -1
- package/lib/types/ink/output.d.ts +10 -1
- package/lib/types/ink/output.d.ts.map +1 -1
- package/lib/types/ink/output.js +109 -17
- package/lib/types/ink/render-border.d.ts +6 -1
- package/lib/types/ink/render-border.d.ts.map +1 -1
- package/lib/types/ink/render-border.js +25 -9
- package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
- package/lib/types/ink/render-node-to-output.js +48 -6
- package/lib/types/ink/renderer.d.ts +6 -0
- package/lib/types/ink/renderer.d.ts.map +1 -1
- package/lib/types/ink/renderer.js +2 -1
- package/lib/types/ink/selection.d.ts +60 -0
- package/lib/types/ink/selection.d.ts.map +1 -1
- package/lib/types/ink/selection.js +129 -0
- package/lib/types/ink/sixel-graphics.d.ts +19 -1
- package/lib/types/ink/sixel-graphics.d.ts.map +1 -1
- package/lib/types/ink/sixel-graphics.js +124 -12
- package/lib/types/ink/terminal-image.d.ts +18 -1
- package/lib/types/ink/terminal-image.d.ts.map +1 -1
- package/lib/types/ink/terminal.d.ts +17 -0
- package/lib/types/ink/terminal.d.ts.map +1 -1
- package/lib/types/ink/terminal.js +20 -1
- package/lib/types/screens/Chat.d.ts +17 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +284 -72
- package/lib/types/screens/SessionSupervisor.d.ts +84 -0
- package/lib/types/screens/SessionSupervisor.d.ts.map +1 -0
- package/lib/types/screens/SessionSupervisor.js +350 -0
- package/lib/types/screens/SessionTree.js +2 -2
- package/lib/types/screens/Settings.d.ts.map +1 -1
- package/lib/types/screens/Settings.js +32 -9
- package/lib/types/screens/StatusLine.d.ts +13 -0
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +36 -17
- package/lib/types/screens/StatusMetrics.d.ts +79 -22
- package/lib/types/screens/StatusMetrics.d.ts.map +1 -1
- package/lib/types/screens/StatusMetrics.js +153 -88
- package/lib/types/screens/sessionSupervisor/model.d.ts +83 -0
- package/lib/types/screens/sessionSupervisor/model.d.ts.map +1 -0
- package/lib/types/screens/sessionSupervisor/model.js +69 -0
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts +120 -0
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts.map +1 -0
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.js +593 -0
- package/lib/types/sessionMounts.d.ts +272 -0
- package/lib/types/sessionMounts.d.ts.map +1 -0
- package/lib/types/sessionMounts.js +706 -0
- package/lib/types/sessions/format.d.ts.map +1 -1
- package/lib/types/sessions/format.js +4 -1
- package/lib/types/sessions/resumeFailure.d.ts +38 -0
- package/lib/types/sessions/resumeFailure.d.ts.map +1 -0
- package/lib/types/sessions/resumeFailure.js +58 -0
- package/lib/types/terminal-utils/mermaid.d.ts +44 -0
- package/lib/types/terminal-utils/mermaid.d.ts.map +1 -0
- package/lib/types/terminal-utils/mermaid.js +83 -0
- package/lib/types/tips.d.ts.map +1 -1
- package/lib/types/tips.js +175 -19
- package/lib/types/tuiDisplayPrefs.d.ts +7 -9
- package/lib/types/tuiDisplayPrefs.d.ts.map +1 -1
- package/lib/types/tuiDisplayPrefs.js +31 -28
- package/lib/types/update.d.ts +61 -0
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +194 -0
- package/lib/types/utils/imageResize.d.ts +90 -0
- package/lib/types/utils/imageResize.d.ts.map +1 -0
- package/lib/types/utils/imageResize.js +272 -0
- package/lib/types/utils/keymap.d.ts +5 -1
- package/lib/types/utils/keymap.d.ts.map +1 -1
- package/lib/types/utils/keymap.js +23 -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/@deepseek-harness-tui/dsh-auth/package.json +10 -10
- package/package.json +120 -103
- package/presets/liangshen/.dsh-tui-managed.json +1 -1
- package/presets/liangshen/agent.cordis.yml +15 -0
- package/lib/types/screens/AgentView.d.ts +0 -23
- package/lib/types/screens/AgentView.d.ts.map +0 -1
- package/lib/types/screens/AgentView.js +0 -546
- package/lib/types/screens/SessionBrowser.d.ts +0 -42
- package/lib/types/screens/SessionBrowser.d.ts.map +0 -1
- package/lib/types/screens/SessionBrowser.js +0 -911
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The composer draft that a screen hands to its owner on the way out.
|
|
3
|
+
*
|
|
4
|
+
* Every screen that REPLACES the conversation (`/resume`'s session screen, the
|
|
5
|
+
* session tree, `/settings`, the jobs panel, the trajectory scene) is an early
|
|
6
|
+
* return in `Chat`, so `PromptInput` is unmounted while the user is away and
|
|
7
|
+
* its text, caret and staged-image bindings live in component state. The owner
|
|
8
|
+
* therefore holds one slot of this type and the composer fills it as it
|
|
9
|
+
* unmounts; on the way back it is read, checked and consumed.
|
|
10
|
+
*
|
|
11
|
+
* Deliberately dependency-free (it does not import `PromptInput`, which would
|
|
12
|
+
* be a cycle), so the component and the headless regressions can both assert
|
|
13
|
+
* against the same rules.
|
|
14
|
+
*
|
|
15
|
+
* The snapshot shape, the image-liveness rule and the carried edit state
|
|
16
|
+
* (fold block, fullscreen editor, vim mode) are taken from PR #847
|
|
17
|
+
* (`promptDraftCache.ts`, branch `fix/composer-draft-retention`); the owner
|
|
18
|
+
* check here adds the agent id, because a generation alone cannot tell two
|
|
19
|
+
* channels with a fixed generation apart.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* The current binding generation, mirroring the channel expression: the agent
|
|
23
|
+
* binding generation advances on every agent replacement (`/resume` to another
|
|
24
|
+
* session, `/new`, `/bg`, attach, rewind) and wins; a simplified or embedded
|
|
25
|
+
* channel without it falls back to the staged-image generation, and 0 when it
|
|
26
|
+
* has neither.
|
|
27
|
+
* @param source - The channel, or anything carrying the same two members.
|
|
28
|
+
* @returns The generation the snapshot is fenced against.
|
|
29
|
+
*/
|
|
30
|
+
export function resolveBindingGeneration(source) {
|
|
31
|
+
return source.agentBindingGeneration ?? source.stagedImageGeneration?.() ?? 0;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Whether a stored draft belongs to the conversation in front of us now.
|
|
35
|
+
*
|
|
36
|
+
* Both halves are required. The generation catches a replaced binding; the
|
|
37
|
+
* agent id catches the case a generation cannot describe — a test or embedded
|
|
38
|
+
* channel whose generation is a constant, where a stale draft would otherwise
|
|
39
|
+
* be adopted by a different conversation.
|
|
40
|
+
* @param snapshot - The stored draft, if any.
|
|
41
|
+
* @param ownerAgentId - Agent id of the composer that would restore it.
|
|
42
|
+
* @param generation - Current binding generation.
|
|
43
|
+
*/
|
|
44
|
+
export function isUsableDraftSnapshot(snapshot, ownerAgentId, generation) {
|
|
45
|
+
return snapshot !== null
|
|
46
|
+
&& snapshot !== undefined
|
|
47
|
+
&& snapshot.ownerAgentId === ownerAgentId
|
|
48
|
+
&& snapshot.bindingGeneration === generation;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Drop the image bindings whose capability is gone.
|
|
52
|
+
*
|
|
53
|
+
* A visible `[Image #N]` is only a label; what makes it an attachment is the
|
|
54
|
+
* stageId behind it. A session switch (or a discard) revokes the capability and
|
|
55
|
+
* the label must not be re-bound by reading the number back out of the text.
|
|
56
|
+
* Entries that fail the check are dropped silently and the caller keeps the
|
|
57
|
+
* visible token as ordinary text, which is the behaviour an edit already has.
|
|
58
|
+
* @param images - Stored pairs.
|
|
59
|
+
* @param hasStagedImage - Channel capability probe; absent means "none live".
|
|
60
|
+
*/
|
|
61
|
+
export function filterLiveImageBindings(images, hasStagedImage) {
|
|
62
|
+
if (images === null || images === undefined)
|
|
63
|
+
return [];
|
|
64
|
+
const live = [];
|
|
65
|
+
for (const entry of images) {
|
|
66
|
+
if (!Array.isArray(entry) || entry.length !== 2)
|
|
67
|
+
continue;
|
|
68
|
+
const [token, stageId] = entry;
|
|
69
|
+
if (typeof token !== 'string' || typeof stageId !== 'string')
|
|
70
|
+
continue;
|
|
71
|
+
if (hasStagedImage?.(stageId) !== true)
|
|
72
|
+
continue;
|
|
73
|
+
live.push([token, stageId]);
|
|
74
|
+
}
|
|
75
|
+
return live;
|
|
76
|
+
}
|
|
@@ -72,6 +72,22 @@ export type AskUserQuestionPanelProps = {
|
|
|
72
72
|
* verification injects a fake so paste outcomes are deterministic.
|
|
73
73
|
*/
|
|
74
74
|
readonly readClipboardOverride?: () => Promise<ClipboardRead>;
|
|
75
|
+
/**
|
|
76
|
+
* Manual collapse (question panel fold, default Ctrl+K): while
|
|
77
|
+
* collapsed the panel STAYS MOUNTED — draft refs survive, the fold
|
|
78
|
+
* branch sits after every hook — and renders the two-line minimized
|
|
79
|
+
* bar instead, with editing/caret layers gated off. Its own input
|
|
80
|
+
* handler keeps Esc/Ctrl+C as "expand", never cancel, while folded.
|
|
81
|
+
*/
|
|
82
|
+
readonly collapsed?: boolean;
|
|
83
|
+
/** Expand back from the minimized bar (fold key / Esc / mouse click). */
|
|
84
|
+
readonly onExpand?: () => void;
|
|
85
|
+
/** Fold from the expanded panel (fold key / header-row click). */
|
|
86
|
+
readonly onToggleFold?: () => void;
|
|
87
|
+
/** True when mouse tracking is on: mouse-only affordances (the
|
|
88
|
+
* multi-select submit row) render only then — inline hosts have no
|
|
89
|
+
* pointer, so an unclickable row would just eat a line. */
|
|
90
|
+
readonly fullscreen?: boolean;
|
|
75
91
|
};
|
|
76
|
-
export declare function AskUserQuestionPanel({ question, position, total, answered, initialDraft, onAnswer, onCancel, onBack, readClipboardOverride, }: AskUserQuestionPanelProps): React.ReactNode;
|
|
92
|
+
export declare function AskUserQuestionPanel({ question, position, total, answered, initialDraft, onAnswer, onCancel, onBack, readClipboardOverride, collapsed, onExpand, onToggleFold, fullscreen, }: AskUserQuestionPanelProps): React.ReactNode;
|
|
77
93
|
//# sourceMappingURL=AskUserQuestionPanel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AskUserQuestionPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/AskUserQuestionPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;
|
|
1
|
+
{"version":3,"file":"AskUserQuestionPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/AskUserQuestionPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAMtF,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAkB5E,MAAM,MAAM,yBAAyB,GAAG;IACtC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;QACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;YAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC3F,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;QAC9B;;oEAE4D;QAC5D,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;QAClC;;qEAE6D;QAC7D,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;QAC5C;0EACkE;QAClE,QAAQ,CAAC,MAAM,CAAC,EAAE;YAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;YAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAA;KAC7E,CAAA;IACD,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,iDAAiD;IACjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,8EAA8E;IAC9E,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAA;IACrC,QAAQ,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACzD,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAChD;;;;OAIG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,CAAA;IAC7D;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IAC9B,kEAAkE;IAClE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,IAAI,CAAA;IAClC;;gEAE4D;IAC5D,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAC9B,CAAA;AAED,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,qBAAqB,EACrB,SAAiB,EACjB,QAAQ,EACR,YAAY,EACZ,UAAkB,GACnB,EAAE,yBAAyB,GAAG,KAAK,CAAC,SAAS,CAsmB7C"}
|
|
@@ -32,8 +32,9 @@ import { useDeclaredCursor } from '../../ink/hooks/use-declared-cursor.js';
|
|
|
32
32
|
import { Divider } from '../design-system/Divider.js';
|
|
33
33
|
import { POINTER } from '../../terminal-utils/figures.js';
|
|
34
34
|
import { PlanReviewPanel } from './PlanReviewPanel.js';
|
|
35
|
+
import { QuestionMinimizedBar } from './QuestionMinimizedBar.js';
|
|
35
36
|
import { isPlainReturnInput } from '../../utils/modifiers.js';
|
|
36
|
-
import { actionMatches } from '../../utils/keymap.js';
|
|
37
|
+
import { actionMatches, effectiveComboDisplay } from '../../utils/keymap.js';
|
|
37
38
|
import { flattenPasteInline } from '../../dsh-adapter/sanitize.js';
|
|
38
39
|
import { readClipboard } from '../../utils/clipboard.js';
|
|
39
40
|
import { listWindow } from '../listWindow.js';
|
|
@@ -50,7 +51,7 @@ const PENCIL = '✎';
|
|
|
50
51
|
* PlanReviewPanel (the two panels share the same input contract).
|
|
51
52
|
*/
|
|
52
53
|
const ANSWER_PASTE_MAX_POINTS = 8000;
|
|
53
|
-
export function AskUserQuestionPanel({ question, position, total, answered, initialDraft, onAnswer, onCancel, onBack, readClipboardOverride, }) {
|
|
54
|
+
export function AskUserQuestionPanel({ question, position, total, answered, initialDraft, onAnswer, onCancel, onBack, readClipboardOverride, collapsed = false, onExpand, onToggleFold, fullscreen = false, }) {
|
|
54
55
|
// Plan-mode's exit_plan_mode ask carries a presentation intent: render
|
|
55
56
|
// the plan decision card instead of the generic questionnaire. The
|
|
56
57
|
// branch precedes every hook so hook order stays stable per remount key.
|
|
@@ -134,7 +135,7 @@ export function AskUserQuestionPanel({ question, position, total, answered, init
|
|
|
134
135
|
// three visual variants): its nodeCache rect IS the caret cell, so (0, 0)
|
|
135
136
|
// stays exact under CJK widths and line wrapping without any
|
|
136
137
|
// layout-affecting wrapper Box.
|
|
137
|
-
const caretRef = useDeclaredCursor({ line: 0, column: 0, active: !hideCustomInput });
|
|
138
|
+
const caretRef = useDeclaredCursor({ line: 0, column: 0, active: !hideCustomInput && !collapsed });
|
|
138
139
|
const moveFocus = (delta) => {
|
|
139
140
|
if (rowCount <= 1)
|
|
140
141
|
return;
|
|
@@ -277,7 +278,22 @@ export function AskUserQuestionPanel({ question, position, total, answered, init
|
|
|
277
278
|
...(textRef.current !== '' ? { custom: textRef.current } : {}),
|
|
278
279
|
};
|
|
279
280
|
};
|
|
280
|
-
useInput((input, key) => {
|
|
281
|
+
useInput((input, key, event) => {
|
|
282
|
+
// The mounted panel owns folding, just like answering: hidden asks
|
|
283
|
+
// cannot steal approval/dialog keys, and an interrupting questionnaire
|
|
284
|
+
// still works when Chat yields to an underlying screen's open flag.
|
|
285
|
+
if (onToggleFold !== undefined && actionMatches('questionFold', input, key)) {
|
|
286
|
+
onToggleFold();
|
|
287
|
+
event.stopImmediatePropagation();
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if (collapsed) {
|
|
291
|
+
if (key.escape || (key.ctrl && input === 'c')) {
|
|
292
|
+
onExpand?.();
|
|
293
|
+
event.stopImmediatePropagation();
|
|
294
|
+
}
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
281
297
|
if (key.ctrl && input === 'c') {
|
|
282
298
|
onCancel();
|
|
283
299
|
return;
|
|
@@ -410,9 +426,14 @@ export function AskUserQuestionPanel({ question, position, total, answered, init
|
|
|
410
426
|
if (!multiSelect)
|
|
411
427
|
setAttached(options[focusIndex]?.label ?? null);
|
|
412
428
|
}
|
|
413
|
-
}
|
|
429
|
+
});
|
|
414
430
|
const remaining = total - answered;
|
|
431
|
+
/** Fold shortcut label for the hint row (follows /settings remaps). */
|
|
432
|
+
const foldCombo = effectiveComboDisplay('questionFold');
|
|
415
433
|
const headerTitle = ` ${t('question-header-progress', { position, total, remaining: remaining > 1 ? t('question-remaining-more', { n: remaining }) : '' })} `;
|
|
434
|
+
/** First line of the question body for the minimized bar (whitespace
|
|
435
|
+
* flattened the same way windowedOptions flattens labels). */
|
|
436
|
+
const questionText = question.question.split('\n')[0]?.replace(/\s+/gu, ' ').trim() ?? '';
|
|
416
437
|
// The caret counts code points (see the module header), so the caret
|
|
417
438
|
// char and the visual split index into the point array — never raw
|
|
418
439
|
// UTF-16 offsets, which could land inside a surrogate pair.
|
|
@@ -450,6 +471,10 @@ export function AskUserQuestionPanel({ question, position, total, answered, init
|
|
|
450
471
|
onAnswer({ selected: [label], ...(text !== '' ? { custom: text } : {}) });
|
|
451
472
|
};
|
|
452
473
|
const [hoverIndex, setHoverIndex] = React.useState(-1);
|
|
474
|
+
/** Header row hover — the row doubles as the mouse fold/unfold affordance. */
|
|
475
|
+
const [headerHovered, setHeaderHovered] = React.useState(false);
|
|
476
|
+
/** Mouse submit row hover (multi-select: clicking submits the checked set). */
|
|
477
|
+
const [submitHovered, setSubmitHovered] = React.useState(false);
|
|
453
478
|
const renderInputRow = () => (_jsxs(Box, { flexDirection: "row", marginTop: inputFocused ? 1 : 0, onClick: focusInputRow, onMouseEnter: () => setHoverIndex(options.length), onMouseLeave: () => setHoverIndex(current => (current === options.length ? -1 : current)), backgroundColor: hoverIndex === options.length && !inputFocused ? 'userMessageBackgroundHover' : undefined, children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: inputFocused ? 'accent' : undefined, bold: inputFocused, children: inputFocused ? POINTER : ' ' }) }), _jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: inputFocused ? 'accent' : 'suggestion', children: PENCIL }) }), _jsxs(Box, { flexDirection: "row", marginLeft: 1, children: [_jsx(Text, { bold: inputFocused, color: inputFocused ? 'accent' : 'suggestion', children: t('question-custom-tab') }), attached !== null && (_jsx(Text, { color: "suggestion", children: t('question-attached-label', { label: attached }) })), _jsx(Text, { dimColor: true, children: "\uFF1A" }), customText === '' && !inputFocused ? (_jsx(Text, { ref: caretRef, dimColor: true, children: t('question-direct-input') })) : (_jsxs(_Fragment, { children: [_jsx(Text, { wrap: "wrap", children: textPoints.slice(0, customCursor).join('') }), inputFocused
|
|
454
479
|
? _jsx(Text, { ref: caretRef, inverse: true, children: cursorChar })
|
|
455
480
|
: _jsx(Text, { ref: caretRef, color: "suggestion", children: "\u258F" }), _jsx(Text, { wrap: "wrap", children: textPoints.slice(inputFocused ? customCursor + 1 : customCursor).join('') })] }))] })] }));
|
|
@@ -469,7 +494,7 @@ export function AskUserQuestionPanel({ question, position, total, answered, init
|
|
|
469
494
|
? option.description?.replace(/\s+/gu, ' ').trim()
|
|
470
495
|
: option.description;
|
|
471
496
|
return (_jsxs(Box, { flexDirection: "row", marginTop: !windowedOptions && focused ? 1 : 0, onClick: () => clickOption(absoluteIndex), onMouseEnter: () => setHoverIndex(absoluteIndex), onMouseLeave: () => setHoverIndex(current => (current === absoluteIndex ? -1 : current)), backgroundColor: hoverIndex === absoluteIndex && !focused ? 'userMessageBackgroundHover' : undefined, children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: focused ? 'accent' : undefined, bold: focused, children: pointer }) }), _jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: focused ? 'accent' : undefined, bold: selected, children: selected ? (multiSelect ? CHECKED : '●') : UNCHECKED }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [_jsx(Text, { bold: focused || selected, color: focused ? 'accent' : undefined, wrap: windowedOptions ? 'truncate' : 'wrap', children: label }), description !== undefined && (_jsx(Text, { dimColor: true, wrap: windowedOptions ? 'truncate' : 'wrap', children: description }))] })] }, `${absoluteIndex}:${option.label}`));
|
|
472
|
-
}), hideCustomInput ? null : renderInputRow()] }));
|
|
497
|
+
}), hideCustomInput ? null : renderInputRow(), multiSelect && fullscreen && (checked.size > 0 || textRef.current !== '') && (_jsxs(Box, { flexDirection: "row", height: 1, marginTop: 1, onClick: submitOptions, onMouseEnter: () => setSubmitHovered(true), onMouseLeave: () => setSubmitHovered(false), backgroundColor: submitHovered ? 'userMessageBackgroundHover' : undefined, children: [_jsx(Text, { color: "accent", children: "\u2713 " }), _jsx(Text, { dimColor: true, children: t('question-submit-selection') })] }))] }));
|
|
473
498
|
const hintParts = inputFocused
|
|
474
499
|
? [
|
|
475
500
|
t('question-hint-type'),
|
|
@@ -479,6 +504,7 @@ export function AskUserQuestionPanel({ question, position, total, answered, init
|
|
|
479
504
|
onBack === undefined ? t('question-hint-esc') : t('question-hint-previous'),
|
|
480
505
|
...(onBack === undefined ? [] : [t('question-hint-cancel')]),
|
|
481
506
|
...(multiSelect && checked.size > 0 ? [t('question-hint-selected', { n: checked.size })] : []),
|
|
507
|
+
t('question-fold-hint', { combo: foldCombo }),
|
|
482
508
|
]
|
|
483
509
|
: [
|
|
484
510
|
t('question-hint-select'),
|
|
@@ -488,6 +514,13 @@ export function AskUserQuestionPanel({ question, position, total, answered, init
|
|
|
488
514
|
onBack === undefined ? t('question-hint-esc') : t('question-hint-previous'),
|
|
489
515
|
...(onBack === undefined ? [] : [t('question-hint-cancel')]),
|
|
490
516
|
...(multiSelect && checked.size > 0 ? [t('question-hint-selected', { n: checked.size })] : []),
|
|
517
|
+
t('question-fold-hint', { combo: foldCombo }),
|
|
491
518
|
];
|
|
492
|
-
|
|
519
|
+
// Folded: render only the minimized bar. This branch sits AFTER every
|
|
520
|
+
// hook (and after the intent early-return), so folding never reorders
|
|
521
|
+
// hooks and the draft refs above stay alive for the expand.
|
|
522
|
+
if (collapsed) {
|
|
523
|
+
return _jsx(QuestionMinimizedBar, { progress: headerTitle, questionText: questionText, onExpand: onExpand ?? (() => { }) });
|
|
524
|
+
}
|
|
525
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, paddingRight: 2, width: "100%", children: [_jsx(Box, { flexDirection: "column", onClick: onToggleFold, onMouseEnter: () => setHeaderHovered(true), onMouseLeave: () => setHeaderHovered(false), backgroundColor: headerHovered ? 'userMessageBackgroundHover' : undefined, children: _jsx(Divider, { color: "permission", title: `▾${headerTitle}` }) }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [question.header !== undefined && (_jsxs(Text, { color: "suggestion", bold: true, children: ["\u25C8 ", question.header] })), _jsx(Text, { bold: true, wrap: "wrap", children: question.question }), question.detail !== undefined && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: question.detail.split('\n').map((line, index) => (_jsx(Text, { dimColor: true, italic: true, wrap: "wrap", children: line }, index))) }))] }), renderOptions(), error !== null && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "error", children: error }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: hintParts.join(' · ') }) })] }));
|
|
493
526
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type QuestionMinimizedBarProps = {
|
|
3
|
+
/** Progress title, same text as the expanded panel's Divider header
|
|
4
|
+
* (e.g. ` 📋 提问 · 第 2/5 题 · 还剩 3 题 `). */
|
|
5
|
+
readonly progress: string;
|
|
6
|
+
/** First line of the question body, truncated by the layout. */
|
|
7
|
+
readonly questionText: string;
|
|
8
|
+
/** Expand back to the full panel (Ctrl+K / Esc / click). */
|
|
9
|
+
readonly onExpand: () => void;
|
|
10
|
+
};
|
|
11
|
+
export declare function QuestionMinimizedBar({ progress, questionText, onExpand, }: QuestionMinimizedBarProps): React.ReactNode;
|
|
12
|
+
//# sourceMappingURL=QuestionMinimizedBar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QuestionMinimizedBar.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/QuestionMinimizedBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AA0BzB,MAAM,MAAM,yBAAyB,GAAG;IACtC;+CAC2C;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,gEAAgE;IAChE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,4DAA4D;IAC5D,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;CAC9B,CAAA;AAED,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,YAAY,EACZ,QAAQ,GACT,EAAE,yBAAyB,GAAG,KAAK,CAAC,SAAS,CAwC7C"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Box, Text } from '../../ui.js';
|
|
4
|
+
import { t } from '../../i18n.js';
|
|
5
|
+
import { effectiveComboDisplay } from '../../utils/keymap.js';
|
|
6
|
+
/**
|
|
7
|
+
* 提问面板挂起条 — the two-line stand-in for a folded questionnaire
|
|
8
|
+
* (manual collapse; the ask stays parked and the panel stays mounted, so
|
|
9
|
+
* drafts survive).
|
|
10
|
+
*
|
|
11
|
+
* Line 1 mirrors the GoalTodoPanel fold-header language: a dim `▸` (folded
|
|
12
|
+
* state), the same progress title as the expanded header, then the first
|
|
13
|
+
* line of the question truncated to whatever width remains. The whole row
|
|
14
|
+
* is clickable (fullscreen mouse) and hover-highlighted to expand.
|
|
15
|
+
*
|
|
16
|
+
* Line 2 is the permanent "answer pending" affordance — a blinking pause
|
|
17
|
+
* glyph (`⏸` toggled dim/bright on a local interval that lives only while
|
|
18
|
+
* the bar is mounted) plus the waiting text and the fold key hint. The key
|
|
19
|
+
* label follows keymap remaps via effectiveComboString, like the /settings
|
|
20
|
+
* shortcut rows.
|
|
21
|
+
*
|
|
22
|
+
* The component is deliberately dumb: the parent formats the strings, so
|
|
23
|
+
* the bar owns only its local hover/blink state.
|
|
24
|
+
*/
|
|
25
|
+
const BLINK_MS = 500;
|
|
26
|
+
export function QuestionMinimizedBar({ progress, questionText, onExpand, }) {
|
|
27
|
+
const [blinkOn, setBlinkOn] = React.useState(true);
|
|
28
|
+
const [hovered, setHovered] = React.useState(false);
|
|
29
|
+
// The pause glyph blinks by APPEARING and DISAPPEARING (a visible→gone
|
|
30
|
+
// beat reads as a true blink; dim↔normal is too subtle). The off phase
|
|
31
|
+
// must occupy exactly the glyph's width or the waiting text shifts left
|
|
32
|
+
// and right every beat: '⏸ ' measures 2 columns (⏸ is a narrow 1-col
|
|
33
|
+
// glyph here), so it is replaced by two spaces. Blink only while the
|
|
34
|
+
// bar exists; unmounting clears the interval.
|
|
35
|
+
React.useEffect(() => {
|
|
36
|
+
const id = setInterval(() => setBlinkOn(previous => !previous), BLINK_MS);
|
|
37
|
+
return () => clearInterval(id);
|
|
38
|
+
}, []);
|
|
39
|
+
const waiting = `${t('question-fold-waiting')} — ${t('question-fold-expand', {
|
|
40
|
+
combo: effectiveComboDisplay('questionFold'),
|
|
41
|
+
})}`;
|
|
42
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, paddingRight: 2, width: "100%", children: [_jsxs(Box, { flexDirection: "row", height: 1, onClick: onExpand, onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), backgroundColor: hovered ? 'userMessageBackgroundHover' : undefined, children: [_jsx(Text, { dimColor: true, children: '▸ ' }), _jsx(Text, { dimColor: true, children: progress }), _jsx(Box, { flexGrow: 1, flexShrink: 1, marginLeft: 1, children: _jsx(Text, { wrap: "truncate", children: questionText }) })] }), _jsxs(Box, { flexDirection: "row", height: 1, children: [_jsx(Text, { children: blinkOn ? '⏸ ' : ' ' }), _jsx(Text, { dimColor: true, wrap: "truncate", children: waiting })] })] }));
|
|
43
|
+
}
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import type { ClickEvent } from '../../ink/events/click-event.js';
|
|
3
3
|
import type { ContextMenuEvent } from '../../ink/events/context-menu-event.js';
|
|
4
4
|
import type { SessionSummary } from '../../dsh-adapter/sessions/index.js';
|
|
5
|
+
import type { AgentViewStatus } from '../../adapter/ports/channel-view.js';
|
|
5
6
|
/**
|
|
6
7
|
* One session in the browser's list: a title line and a metadata line.
|
|
7
8
|
*
|
|
@@ -15,7 +16,7 @@ import type { SessionSummary } from '../../dsh-adapter/sessions/index.js';
|
|
|
15
16
|
* stay exactly two lines at every terminal width, and a row that wraps
|
|
16
17
|
* destroys the alignment that lets the eye scan a list at all.
|
|
17
18
|
*/
|
|
18
|
-
export declare function SessionListRow({ session, width, depth, focused, pinned, now, onClick, onContextMenu, onTogglePin, }: {
|
|
19
|
+
export declare function SessionListRow({ session, width, depth, focused, pinned, now, onClick, onContextMenu, onTogglePin, liveStatus, current, occupiedPid, spinner, }: {
|
|
19
20
|
session: SessionSummary;
|
|
20
21
|
/** Columns available to the row, indentation included. */
|
|
21
22
|
width: number;
|
|
@@ -32,5 +33,26 @@ export declare function SessionListRow({ session, width, depth, focused, pinned,
|
|
|
32
33
|
onContextMenu?(event: ContextMenuEvent): void;
|
|
33
34
|
/** 点击行内 ★/☆(fullscreen):切换固定状态,不冒泡成"打开会话"。 */
|
|
34
35
|
onTogglePin?(): void;
|
|
36
|
+
/**
|
|
37
|
+
* Live status of an agent THIS terminal is running, when there is one.
|
|
38
|
+
* `working` animates instead of holding a static glyph, so a busy session
|
|
39
|
+
* is legible at a glance — the same vocabulary the session overview uses,
|
|
40
|
+
* which is what lets the two surfaces read as one feature.
|
|
41
|
+
*/
|
|
42
|
+
liveStatus?: AgentViewStatus;
|
|
43
|
+
/** True when this is the session the terminal is attached to. */
|
|
44
|
+
current?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* The pid of ANOTHER TUI terminal holding this session, when one does. The
|
|
47
|
+
* row turns red and says so: the session stays visible but cannot be
|
|
48
|
+
* entered, which is the honest presentation of a claim this process must
|
|
49
|
+
* not break. Undefined means free, or already ours.
|
|
50
|
+
*/
|
|
51
|
+
occupiedPid?: number;
|
|
52
|
+
/** Shared animation clock for the working glyph. */
|
|
53
|
+
spinner?: {
|
|
54
|
+
frame: number;
|
|
55
|
+
time: number;
|
|
56
|
+
};
|
|
35
57
|
}): React.ReactNode;
|
|
36
58
|
//# sourceMappingURL=SessionListRow.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionListRow.d.ts","sourceRoot":"","sources":["../../../../src/components/sessions/SessionListRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAGvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAA;
|
|
1
|
+
{"version":3,"file":"SessionListRow.d.ts","sourceRoot":"","sources":["../../../../src/components/sessions/SessionListRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAGvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAA;AAY9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAA;AA0B1E;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,KAAK,EACL,KAAK,EACL,OAAO,EACP,MAAM,EACN,GAAG,EACH,OAAO,EACP,aAAa,EACb,WAAW,EACX,UAAU,EACV,OAAO,EACP,WAAW,EACX,OAAO,GACR,EAAE;IACD,OAAO,EAAE,cAAc,CAAA;IACvB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAA;IACb,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;IAChB,sEAAsE;IACtE,MAAM,EAAE,OAAO,CAAA;IACf,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAA;IACX,4CAA4C;IAC5C,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IACjC,gDAAgD;IAChD,aAAa,CAAC,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAA;IAC7C,8CAA8C;IAC9C,WAAW,CAAC,IAAI,IAAI,CAAA;IACpB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oDAAoD;IACpD,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAC1C,GAAG,KAAK,CAAC,SAAS,CA6HlB"}
|
|
@@ -3,7 +3,31 @@ import { useState } from 'react';
|
|
|
3
3
|
import { Box, Text } from '../../ui.js';
|
|
4
4
|
import { t } from '../../i18n.js';
|
|
5
5
|
import { useTooltip } from '../Tooltip.js';
|
|
6
|
+
import { SpinnerGlyph } from '../Spinner/SpinnerGlyph.js';
|
|
7
|
+
import { stringWidth } from '../../ink/stringWidth.js';
|
|
6
8
|
import { formatAbsolute, formatBytes, formatWhen, kindMark, titleColor, truncateWidth, } from '../../sessions/format.js';
|
|
9
|
+
/**
|
|
10
|
+
* State presentation for a session this terminal is running: one glyph and one
|
|
11
|
+
* theme colour per status, matching the session overview's own vocabulary so
|
|
12
|
+
* the same session never looks like two different things on two screens.
|
|
13
|
+
* `stopped` is the "not running here" spelling and draws dim.
|
|
14
|
+
*/
|
|
15
|
+
const LIVE_STATUS_GLYPH = {
|
|
16
|
+
'needs-input': '✻',
|
|
17
|
+
working: '✽',
|
|
18
|
+
completed: '✓',
|
|
19
|
+
failed: '✕',
|
|
20
|
+
idle: '∙',
|
|
21
|
+
stopped: '∙',
|
|
22
|
+
};
|
|
23
|
+
const LIVE_STATUS_COLOR = {
|
|
24
|
+
'needs-input': 'warning',
|
|
25
|
+
working: 'suggestion',
|
|
26
|
+
completed: 'success',
|
|
27
|
+
failed: 'error',
|
|
28
|
+
idle: undefined,
|
|
29
|
+
stopped: undefined,
|
|
30
|
+
};
|
|
7
31
|
/**
|
|
8
32
|
* One session in the browser's list: a title line and a metadata line.
|
|
9
33
|
*
|
|
@@ -17,7 +41,7 @@ import { formatAbsolute, formatBytes, formatWhen, kindMark, titleColor, truncate
|
|
|
17
41
|
* stay exactly two lines at every terminal width, and a row that wraps
|
|
18
42
|
* destroys the alignment that lets the eye scan a list at all.
|
|
19
43
|
*/
|
|
20
|
-
export function SessionListRow({ session, width, depth, focused, pinned, now, onClick, onContextMenu, onTogglePin, }) {
|
|
44
|
+
export function SessionListRow({ session, width, depth, focused, pinned, now, onClick, onContextMenu, onTogglePin, liveStatus, current, occupiedPid, spinner, }) {
|
|
21
45
|
const indent = depth * 2;
|
|
22
46
|
// Two cells for the focus marker, plus the indent for a nested run.
|
|
23
47
|
const body = Math.max(8, width - 2 - indent);
|
|
@@ -30,10 +54,28 @@ export function SessionListRow({ session, width, depth, focused, pinned, now, on
|
|
|
30
54
|
// and never the working directory, so that pair is always new information
|
|
31
55
|
// for telling look-alike sessions apart.
|
|
32
56
|
const titleText = session.label ?? session.title.text;
|
|
33
|
-
|
|
34
|
-
|
|
57
|
+
/**
|
|
58
|
+
* The state cell holds EXACTLY two columns on every row, occupied or not,
|
|
59
|
+
* for the same reason the pin slot does: a column that appears only when a
|
|
60
|
+
* session is busy would shift every title on screen each time one starts or
|
|
61
|
+
* stops working.
|
|
62
|
+
*/
|
|
63
|
+
const occupied = occupiedPid !== undefined;
|
|
64
|
+
const stateGlyph = occupied ? '⊘' : LIVE_STATUS_GLYPH[liveStatus ?? 'stopped'];
|
|
65
|
+
const stateColor = occupied
|
|
66
|
+
? 'error'
|
|
67
|
+
: liveStatus === undefined
|
|
68
|
+
? undefined
|
|
69
|
+
: LIVE_STATUS_COLOR[liveStatus];
|
|
70
|
+
const occupiedText = occupied ? ` ${t('supervisor-occupied-badge', { pid: occupiedPid })}` : '';
|
|
71
|
+
const currentText = current === true ? ` ${t('supervisor-current')}` : '';
|
|
72
|
+
const titleBudget = body - 2 - (mark === undefined ? 0 : 2) - 2
|
|
73
|
+
- stringWidth(occupiedText) - stringWidth(currentText);
|
|
74
|
+
const shownTitle = truncateWidth(titleText, Math.max(4, titleBudget));
|
|
35
75
|
const titleTooltip = useTooltip(() => {
|
|
36
76
|
const parts = [];
|
|
77
|
+
if (occupied)
|
|
78
|
+
parts.push(t('session-mount-occupied-short', { pid: occupiedPid }));
|
|
37
79
|
if (shownTitle !== titleText)
|
|
38
80
|
parts.push(titleText);
|
|
39
81
|
parts.push(formatAbsolute(session.updatedAt));
|
|
@@ -52,10 +94,15 @@ export function SessionListRow({ session, width, depth, focused, pinned, now, on
|
|
|
52
94
|
if (session.childCount > 0 && depth === 0) {
|
|
53
95
|
facts.push(t('session-children', { n: session.childCount }));
|
|
54
96
|
}
|
|
55
|
-
return (_jsxs(Box, { flexDirection: "column", flexShrink: 0, onClick: onClick, onContextMenu: onContextMenu, onMouseEnter: onClick !== undefined || onContextMenu !== undefined ? () => setHovered(true) : undefined, onMouseLeave: onClick !== undefined || onContextMenu !== undefined ? () => setHovered(false) : undefined,
|
|
97
|
+
return (_jsxs(Box, { flexDirection: "column", flexShrink: 0, onClick: onClick, onContextMenu: onContextMenu, onMouseEnter: onClick !== undefined || onContextMenu !== undefined ? () => setHovered(true) : undefined, onMouseLeave: onClick !== undefined || onContextMenu !== undefined ? () => setHovered(false) : undefined,
|
|
98
|
+
// Hover is the BLUE prompt — pointer feedback and nothing else. Selection
|
|
99
|
+
// is green and never blue: a row the keyboard cursor is on must not look
|
|
100
|
+
// like a row the mouse happens to be over, so the two never share a
|
|
101
|
+
// background and a selected row keeps its own colour while hovered.
|
|
102
|
+
backgroundColor: hovered && !focused ? 'userMessageBackgroundHover' : undefined, children: [_jsxs(Box, { children: [_jsx(Text, { color: focused ? 'success' : 'subtle', children: `${' '.repeat(indent)}${focused ? '❯ ' : ' '}` }), _jsx(Box, { ...pinTooltip, onClick: onTogglePin === undefined ? undefined : (event) => {
|
|
56
103
|
// The star is a control on the row, not the row: a click here
|
|
57
104
|
// toggles the pin and must never fall through to resume.
|
|
58
105
|
event.stopImmediatePropagation();
|
|
59
106
|
onTogglePin();
|
|
60
|
-
}, children: _jsx(Text, { color: pinned ? 'remember' : undefined, dimColor: !pinned, children: pinned ? '★ ' : '☆ ' }) }), mark !== undefined && _jsx(Text, { color: mark.color, children: `${mark.glyph} ` }), _jsx(Box, { ...titleTooltip, children: _jsx(Text, { color: titleColor(session.title.source, focused), bold: focused, children: shownTitle }) })] }), _jsx(Box, { children: _jsx(Text, { dimColor:
|
|
107
|
+
}, children: _jsx(Text, { color: pinned ? 'remember' : undefined, dimColor: !pinned, children: pinned ? '★ ' : '☆ ' }) }), _jsx(Box, { children: liveStatus === 'working' && !occupied && spinner !== undefined ? (_jsx(SpinnerGlyph, { frame: spinner.frame, messageColor: "suggestion", reducedMotion: false, time: spinner.time })) : (_jsx(Text, { color: stateColor, dimColor: liveStatus === undefined && !occupied, children: `${stateGlyph} ` })) }), mark !== undefined && _jsx(Text, { color: mark.color, children: `${mark.glyph} ` }), _jsx(Box, { ...titleTooltip, children: _jsx(Text, { color: occupied ? 'error' : titleColor(session.title.source, focused), bold: focused, children: shownTitle }) }), occupiedText !== '' && _jsx(Text, { color: "error", children: occupiedText }), currentText !== '' && _jsx(Text, { color: "success", children: currentText })] }), _jsx(Box, { children: _jsx(Text, { color: focused ? 'success' : undefined, dimColor: !focused, children: `${' '.repeat(indent + 2)}${truncateWidth(facts.join(' · '), body)}` }) })] }));
|
|
61
108
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ClickEvent } from '../../ink/events/click-event.js';
|
|
3
|
+
import type { ContextMenuEvent } from '../../ink/events/context-menu-event.js';
|
|
4
|
+
/**
|
|
5
|
+
* One workspace in the home screen's left rail.
|
|
6
|
+
*
|
|
7
|
+
* Two lines, like every other navigable row in this app: the name line is what
|
|
8
|
+
* the user named it, the detail line is where it actually is and how much is in
|
|
9
|
+
* it. The path is not decoration — two workspaces can carry the same basename
|
|
10
|
+
* (`.../a/work` and `.../b/work`), and the ledger allows duplicate titles, so
|
|
11
|
+
* the path is the only thing that tells them apart.
|
|
12
|
+
*
|
|
13
|
+
* Widths are resolved here rather than left to flexbox for the same reason the
|
|
14
|
+
* session rows resolve them: a row must be exactly two lines at every terminal
|
|
15
|
+
* width, and a wrapped row destroys the alignment a list is scanned by.
|
|
16
|
+
*/
|
|
17
|
+
export declare function HomeWorkspaceRow({ title, path, home, sessionCount, present, selected, focused, width, onSelect, onMenu, }: {
|
|
18
|
+
title: string;
|
|
19
|
+
/** Canonical directory path recorded in the ledger. */
|
|
20
|
+
path: string;
|
|
21
|
+
/** Home directory, for collapsing the path to `~`. */
|
|
22
|
+
home: string;
|
|
23
|
+
sessionCount: number;
|
|
24
|
+
/** False when the recorded directory no longer exists. */
|
|
25
|
+
present: boolean;
|
|
26
|
+
/** Whether this workspace's sessions are the ones on the right. */
|
|
27
|
+
selected: boolean;
|
|
28
|
+
/** Keyboard cursor. */
|
|
29
|
+
focused: boolean;
|
|
30
|
+
/** Columns available to the row. */
|
|
31
|
+
width: number;
|
|
32
|
+
/** Left click: show this workspace's sessions (same path as Enter). */
|
|
33
|
+
onSelect?(event: ClickEvent): void;
|
|
34
|
+
/** Right click: open the workspace action menu. */
|
|
35
|
+
onMenu?(event: ContextMenuEvent): void;
|
|
36
|
+
}): React.ReactNode;
|
|
37
|
+
/**
|
|
38
|
+
* The `+` row that opens the directory picker.
|
|
39
|
+
*
|
|
40
|
+
* Deliberately the FIRST row of the rail and always clickable: adding a
|
|
41
|
+
* workspace is the rail's only creation action, and a control that is only
|
|
42
|
+
* reachable by keyboard is a control most users never find in a terminal.
|
|
43
|
+
*/
|
|
44
|
+
export declare function HomeAddWorkspaceRow({ focused, width, onOpen, }: {
|
|
45
|
+
focused: boolean;
|
|
46
|
+
width: number;
|
|
47
|
+
onOpen?(event: ClickEvent): void;
|
|
48
|
+
}): React.ReactNode;
|
|
49
|
+
//# sourceMappingURL=HomeWorkspaceRow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HomeWorkspaceRow.d.ts","sourceRoot":"","sources":["../../../../src/components/workspaces/HomeWorkspaceRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAGvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAA;AAG9E;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,YAAY,EACZ,OAAO,EACP,QAAQ,EACR,OAAO,EACP,KAAK,EACL,QAAQ,EACR,MAAM,GACP,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAA;IACZ,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAA;IAChB,mEAAmE;IACnE,QAAQ,EAAE,OAAO,CAAA;IACjB,uBAAuB;IACvB,OAAO,EAAE,OAAO,CAAA;IAChB,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,uEAAuE;IACvE,QAAQ,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IAClC,mDAAmD;IACnD,MAAM,CAAC,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAA;CACvC,GAAG,KAAK,CAAC,SAAS,CAuClB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,KAAK,EACL,MAAM,GACP,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;CACjC,GAAG,KAAK,CAAC,SAAS,CAoBlB"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Box, Text } from '../../ui.js';
|
|
4
|
+
import { t } from '../../i18n.js';
|
|
5
|
+
import { formatProject, spreadRow, truncateWidth } from '../../sessions/format.js';
|
|
6
|
+
/**
|
|
7
|
+
* One workspace in the home screen's left rail.
|
|
8
|
+
*
|
|
9
|
+
* Two lines, like every other navigable row in this app: the name line is what
|
|
10
|
+
* the user named it, the detail line is where it actually is and how much is in
|
|
11
|
+
* it. The path is not decoration — two workspaces can carry the same basename
|
|
12
|
+
* (`.../a/work` and `.../b/work`), and the ledger allows duplicate titles, so
|
|
13
|
+
* the path is the only thing that tells them apart.
|
|
14
|
+
*
|
|
15
|
+
* Widths are resolved here rather than left to flexbox for the same reason the
|
|
16
|
+
* session rows resolve them: a row must be exactly two lines at every terminal
|
|
17
|
+
* width, and a wrapped row destroys the alignment a list is scanned by.
|
|
18
|
+
*/
|
|
19
|
+
export function HomeWorkspaceRow({ title, path, home, sessionCount, present, selected, focused, width, onSelect, onMenu, }) {
|
|
20
|
+
const [hovered, setHovered] = useState(false);
|
|
21
|
+
const body = Math.max(8, width - 4);
|
|
22
|
+
// The presence marker leads the badge on purpose: `spreadRow` may truncate
|
|
23
|
+
// the right-hand segment, and "this directory is gone" is the one fact that
|
|
24
|
+
// must never be the part that gets cut (the session count is also visible in
|
|
25
|
+
// the pane, the missing directory is not).
|
|
26
|
+
const count = t('home-sessions-count', { n: sessionCount });
|
|
27
|
+
const badge = present ? count : `${t('home-workspace-missing')} · ${count}`;
|
|
28
|
+
const heading = spreadRow(`${selected ? '▣' : '▢'} ${title}`, `${selected ? '✓ ' : ''}${badge}`, body);
|
|
29
|
+
const detail = formatProject(path, home);
|
|
30
|
+
// The cursor (green) outranks the selection ring (green); both outrank the
|
|
31
|
+
// idle colour. Blue is not part of this ladder at all — see the background.
|
|
32
|
+
const headingColor = (focused || selected) ? 'success' : present ? 'text' : 'inactive';
|
|
33
|
+
return (_jsxs(Box, { flexDirection: "column", flexShrink: 0, paddingX: 1, onClick: onSelect, onContextMenu: onMenu, onMouseEnter: onSelect === undefined && onMenu === undefined ? undefined : () => setHovered(true), onMouseLeave: onSelect === undefined && onMenu === undefined ? undefined : () => setHovered(false),
|
|
34
|
+
// Hover is the BLUE prompt — pointer feedback and nothing else, so it
|
|
35
|
+
// never marks the focused or the selected row. Selection itself is green
|
|
36
|
+
// in the text, which is why this row draws no box while selected.
|
|
37
|
+
backgroundColor: hovered && !focused ? 'userMessageBackgroundHover' : undefined, children: [_jsxs(Box, { height: 1, flexShrink: 0, overflow: "hidden", children: [_jsx(Text, { color: focused ? 'success' : 'subtle', children: focused ? '❯ ' : ' ' }), _jsx(Text, { color: headingColor, bold: focused || selected, children: heading.left }), _jsx(Text, { dimColor: !focused, children: `${' '.repeat(heading.gap)}${heading.right}` })] }), _jsx(Text, { color: (focused || selected) ? 'success' : undefined, dimColor: !(focused || selected), wrap: "truncate-end", children: ` ${truncateWidth(detail, body - 2)}` })] }));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The `+` row that opens the directory picker.
|
|
41
|
+
*
|
|
42
|
+
* Deliberately the FIRST row of the rail and always clickable: adding a
|
|
43
|
+
* workspace is the rail's only creation action, and a control that is only
|
|
44
|
+
* reachable by keyboard is a control most users never find in a terminal.
|
|
45
|
+
*/
|
|
46
|
+
export function HomeAddWorkspaceRow({ focused, width, onOpen, }) {
|
|
47
|
+
const [hovered, setHovered] = useState(false);
|
|
48
|
+
const body = Math.max(8, width - 4);
|
|
49
|
+
return (_jsxs(Box, { flexDirection: "column", flexShrink: 0, paddingX: 1, onClick: onOpen, onMouseEnter: onOpen === undefined ? undefined : () => setHovered(true), onMouseLeave: onOpen === undefined ? undefined : () => setHovered(false), backgroundColor: focused || hovered ? 'userMessageBackgroundHover' : undefined, children: [_jsxs(Box, { height: 1, flexShrink: 0, overflow: "hidden", children: [_jsx(Text, { color: focused ? 'suggestion' : 'subtle', children: focused ? '❯ ' : ' ' }), _jsx(Text, { color: "remember", bold: true, children: `+ ${t('home-add-workspace')}` })] }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: ` ${truncateWidth(t('home-add-hint'), body - 2)}` })] }));
|
|
50
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
/**
|
|
3
|
+
* 0.1.7 removed directory discovery. Consume the official bundle definitions
|
|
4
|
+
* through its registry, without copying or reimplementing their tool sets.
|
|
5
|
+
* Web/profile declarations own their seats even while still activating.
|
|
6
|
+
* Returns false on the legacy directory-backed roster or while absent. An
|
|
7
|
+
* absent service is watched through Cordis so a late registry is not missed.
|
|
8
|
+
*/
|
|
9
|
+
export declare function registerBundledPresets(ctx: Context): Promise<boolean>;
|
|
10
|
+
//# sourceMappingURL=bundled-presets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundled-presets.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/bundled-presets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAwClD;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CA0C3E"}
|