@deepseek-harness-tui/dsh-tui 0.8.7 → 0.9.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.
Files changed (229) hide show
  1. package/README.md +29 -10
  2. package/bin/dsh-tui.js +1 -1
  3. package/lib/types/commands.d.ts.map +1 -1
  4. package/lib/types/commands.js +4 -2
  5. package/lib/types/components/ActivityPicker.d.ts +4 -1
  6. package/lib/types/components/ActivityPicker.d.ts.map +1 -1
  7. package/lib/types/components/ActivityPicker.js +2 -2
  8. package/lib/types/components/BtwPanel.d.ts.map +1 -1
  9. package/lib/types/components/BtwPanel.js +1 -1
  10. package/lib/types/components/Chat/SubagentMessage.d.ts +2 -1
  11. package/lib/types/components/Chat/SubagentMessage.d.ts.map +1 -1
  12. package/lib/types/components/Chat/SubagentMessage.js +11 -3
  13. package/lib/types/components/CommandSuggestions.d.ts +21 -4
  14. package/lib/types/components/CommandSuggestions.d.ts.map +1 -1
  15. package/lib/types/components/CommandSuggestions.js +41 -14
  16. package/lib/types/components/EffortSlider.d.ts +4 -1
  17. package/lib/types/components/EffortSlider.d.ts.map +1 -1
  18. package/lib/types/components/EffortSlider.js +4 -2
  19. package/lib/types/components/ExtensionDialog.js +6 -2
  20. package/lib/types/components/FileSuggestions.d.ts +26 -5
  21. package/lib/types/components/FileSuggestions.d.ts.map +1 -1
  22. package/lib/types/components/FileSuggestions.js +53 -16
  23. package/lib/types/components/GoalTodoPanel.d.ts +26 -2
  24. package/lib/types/components/GoalTodoPanel.d.ts.map +1 -1
  25. package/lib/types/components/GoalTodoPanel.js +86 -17
  26. package/lib/types/components/HelpMenu.d.ts +7 -1
  27. package/lib/types/components/HelpMenu.d.ts.map +1 -1
  28. package/lib/types/components/HelpMenu.js +12 -4
  29. package/lib/types/components/HistorySearchDialog.d.ts +4 -1
  30. package/lib/types/components/HistorySearchDialog.d.ts.map +1 -1
  31. package/lib/types/components/HistorySearchDialog.js +2 -2
  32. package/lib/types/components/LangPicker.d.ts +14 -0
  33. package/lib/types/components/LangPicker.d.ts.map +1 -0
  34. package/lib/types/components/LangPicker.js +17 -0
  35. package/lib/types/components/LoadedContextPanel.d.ts.map +1 -1
  36. package/lib/types/components/LoadedContextPanel.js +3 -1
  37. package/lib/types/components/LogoV2.d.ts +5 -1
  38. package/lib/types/components/LogoV2.d.ts.map +1 -1
  39. package/lib/types/components/LogoV2.js +12 -3
  40. package/lib/types/components/Markdown.d.ts.map +1 -1
  41. package/lib/types/components/Markdown.js +12 -3
  42. package/lib/types/components/MessageList.d.ts +43 -2
  43. package/lib/types/components/MessageList.d.ts.map +1 -1
  44. package/lib/types/components/MessageList.js +553 -41
  45. package/lib/types/components/ModelPicker.d.ts +4 -1
  46. package/lib/types/components/ModelPicker.d.ts.map +1 -1
  47. package/lib/types/components/ModelPicker.js +2 -2
  48. package/lib/types/components/PermissionsPicker.d.ts +21 -0
  49. package/lib/types/components/PermissionsPicker.d.ts.map +1 -0
  50. package/lib/types/components/PermissionsPicker.js +27 -0
  51. package/lib/types/components/PlanPicker.d.ts +15 -0
  52. package/lib/types/components/PlanPicker.d.ts.map +1 -0
  53. package/lib/types/components/PlanPicker.js +18 -0
  54. package/lib/types/components/PresetPicker.d.ts +4 -1
  55. package/lib/types/components/PresetPicker.d.ts.map +1 -1
  56. package/lib/types/components/PresetPicker.js +2 -2
  57. package/lib/types/components/PromptInput.d.ts +4 -3
  58. package/lib/types/components/PromptInput.d.ts.map +1 -1
  59. package/lib/types/components/PromptInput.js +273 -50
  60. package/lib/types/components/RewindPicker.d.ts +12 -1
  61. package/lib/types/components/RewindPicker.d.ts.map +1 -1
  62. package/lib/types/components/RewindPicker.js +6 -4
  63. package/lib/types/components/ScrollbarGutter.d.ts +28 -0
  64. package/lib/types/components/ScrollbarGutter.d.ts.map +1 -0
  65. package/lib/types/components/ScrollbarGutter.js +68 -0
  66. package/lib/types/components/Select.d.ts +8 -1
  67. package/lib/types/components/Select.d.ts.map +1 -1
  68. package/lib/types/components/Select.js +4 -2
  69. package/lib/types/components/SkillsPicker.d.ts +4 -1
  70. package/lib/types/components/SkillsPicker.d.ts.map +1 -1
  71. package/lib/types/components/SkillsPicker.js +2 -2
  72. package/lib/types/components/SubagentCard.d.ts +2 -1
  73. package/lib/types/components/SubagentCard.d.ts.map +1 -1
  74. package/lib/types/components/SubagentCard.js +6 -1
  75. package/lib/types/components/SubagentDashboard.d.ts +4 -0
  76. package/lib/types/components/SubagentDashboard.d.ts.map +1 -1
  77. package/lib/types/components/SubagentDashboard.js +9 -1
  78. package/lib/types/components/SubagentDetailScene.d.ts.map +1 -1
  79. package/lib/types/components/SubagentDetailScene.js +5 -4
  80. package/lib/types/components/SuggestionCard.d.ts +63 -0
  81. package/lib/types/components/SuggestionCard.d.ts.map +1 -0
  82. package/lib/types/components/SuggestionCard.js +77 -0
  83. package/lib/types/components/ThemePicker.d.ts +4 -1
  84. package/lib/types/components/ThemePicker.d.ts.map +1 -1
  85. package/lib/types/components/ThemePicker.js +2 -2
  86. package/lib/types/components/ThinkingToggle.d.ts +4 -1
  87. package/lib/types/components/ThinkingToggle.d.ts.map +1 -1
  88. package/lib/types/components/ThinkingToggle.js +2 -2
  89. package/lib/types/components/TimelineRail.d.ts +53 -0
  90. package/lib/types/components/TimelineRail.d.ts.map +1 -0
  91. package/lib/types/components/TimelineRail.js +190 -0
  92. package/lib/types/components/WorkspaceFlowPicker.d.ts +6 -1
  93. package/lib/types/components/WorkspaceFlowPicker.d.ts.map +1 -1
  94. package/lib/types/components/WorkspaceFlowPicker.js +4 -2
  95. package/lib/types/components/WorkspaceMenuPicker.d.ts +19 -0
  96. package/lib/types/components/WorkspaceMenuPicker.d.ts.map +1 -0
  97. package/lib/types/components/WorkspaceMenuPicker.js +19 -0
  98. package/lib/types/components/WorkspacePicker.d.ts +4 -1
  99. package/lib/types/components/WorkspacePicker.d.ts.map +1 -1
  100. package/lib/types/components/WorkspacePicker.js +2 -2
  101. package/lib/types/components/approvals/ApprovalPanel.d.ts.map +1 -1
  102. package/lib/types/components/approvals/ApprovalPanel.js +5 -1
  103. package/lib/types/components/design-system/ListItem.d.ts +8 -1
  104. package/lib/types/components/design-system/ListItem.d.ts.map +1 -1
  105. package/lib/types/components/design-system/ListItem.js +8 -3
  106. package/lib/types/components/messages/AssistantTextMessage.d.ts +5 -2
  107. package/lib/types/components/messages/AssistantTextMessage.d.ts.map +1 -1
  108. package/lib/types/components/messages/AssistantTextMessage.js +6 -2
  109. package/lib/types/components/messages/AssistantThinkingMessage.d.ts +2 -1
  110. package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
  111. package/lib/types/components/messages/AssistantThinkingMessage.js +10 -4
  112. package/lib/types/components/messages/AssistantToolUseMessage.d.ts +8 -1
  113. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  114. package/lib/types/components/messages/AssistantToolUseMessage.js +4 -2
  115. package/lib/types/components/messages/UserPromptMessage.d.ts +2 -1
  116. package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
  117. package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
  118. package/lib/types/components/questions/AskUserQuestionPanel.js +34 -2
  119. package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -1
  120. package/lib/types/components/questions/PlanReviewPanel.js +13 -2
  121. package/lib/types/components/sessions/SessionListRow.d.ts +4 -1
  122. package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
  123. package/lib/types/components/sessions/SessionListRow.js +4 -2
  124. package/lib/types/components/trajectory/HotspotView.d.ts +7 -1
  125. package/lib/types/components/trajectory/HotspotView.d.ts.map +1 -1
  126. package/lib/types/components/trajectory/HotspotView.js +8 -2
  127. package/lib/types/components/trajectory/Ledger.d.ts +9 -1
  128. package/lib/types/components/trajectory/Ledger.d.ts.map +1 -1
  129. package/lib/types/components/trajectory/Ledger.js +23 -5
  130. package/lib/types/components/trajectory/WaveBand.d.ts +10 -1
  131. package/lib/types/components/trajectory/WaveBand.d.ts.map +1 -1
  132. package/lib/types/components/trajectory/WaveBand.js +7 -2
  133. package/lib/types/dsh-adapter/channel.d.ts +27 -6
  134. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  135. package/lib/types/dsh-adapter/channel.js +432 -66
  136. package/lib/types/dsh-adapter/contract.d.ts +58 -18
  137. package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
  138. package/lib/types/dsh-adapter/contract.js +124 -36
  139. package/lib/types/dsh-adapter/index.d.ts +10 -2
  140. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  141. package/lib/types/dsh-adapter/index.js +10 -13
  142. package/lib/types/dsh-adapter/plugin.d.ts +0 -9
  143. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  144. package/lib/types/dsh-adapter/plugin.js +136 -6
  145. package/lib/types/i18n.d.ts +194 -16
  146. package/lib/types/i18n.d.ts.map +1 -1
  147. package/lib/types/i18n.js +73 -10
  148. package/lib/types/ink/components/AlternateScreen.d.ts.map +1 -1
  149. package/lib/types/ink/components/AlternateScreen.js +13 -1
  150. package/lib/types/ink/components/App.d.ts +12 -1
  151. package/lib/types/ink/components/App.d.ts.map +1 -1
  152. package/lib/types/ink/components/App.js +101 -4
  153. package/lib/types/ink/components/ClockContext.d.ts.map +1 -1
  154. package/lib/types/ink/components/ClockContext.js +2 -0
  155. package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
  156. package/lib/types/ink/components/ScrollBox.js +170 -91
  157. package/lib/types/ink/constants.d.ts +9 -0
  158. package/lib/types/ink/constants.d.ts.map +1 -1
  159. package/lib/types/ink/constants.js +9 -0
  160. package/lib/types/ink/events/click-event.d.ts +7 -16
  161. package/lib/types/ink/events/click-event.d.ts.map +1 -1
  162. package/lib/types/ink/events/click-event.js +16 -19
  163. package/lib/types/ink/events/event-handlers.d.ts +11 -1
  164. package/lib/types/ink/events/event-handlers.d.ts.map +1 -1
  165. package/lib/types/ink/events/event-handlers.js +2 -0
  166. package/lib/types/ink/events/input-event.d.ts +9 -0
  167. package/lib/types/ink/events/input-event.d.ts.map +1 -1
  168. package/lib/types/ink/events/input-event.js +4 -0
  169. package/lib/types/ink/events/pointer-event.d.ts +61 -0
  170. package/lib/types/ink/events/pointer-event.d.ts.map +1 -0
  171. package/lib/types/ink/events/pointer-event.js +61 -0
  172. package/lib/types/ink/events/wheel-event.d.ts +17 -0
  173. package/lib/types/ink/events/wheel-event.d.ts.map +1 -0
  174. package/lib/types/ink/events/wheel-event.js +20 -0
  175. package/lib/types/ink/geometry-trace.d.ts +51 -0
  176. package/lib/types/ink/geometry-trace.d.ts.map +1 -0
  177. package/lib/types/ink/geometry-trace.js +83 -0
  178. package/lib/types/ink/hit-test.d.ts +56 -1
  179. package/lib/types/ink/hit-test.d.ts.map +1 -1
  180. package/lib/types/ink/hit-test.js +146 -8
  181. package/lib/types/ink/ink.d.ts +58 -1
  182. package/lib/types/ink/ink.d.ts.map +1 -1
  183. package/lib/types/ink/ink.js +268 -52
  184. package/lib/types/ink/node-cache.d.ts +5 -0
  185. package/lib/types/ink/node-cache.d.ts.map +1 -1
  186. package/lib/types/ink/parse-keypress.d.ts +8 -0
  187. package/lib/types/ink/parse-keypress.d.ts.map +1 -1
  188. package/lib/types/ink/parse-keypress.js +101 -12
  189. package/lib/types/ink/render-node-to-output.d.ts +16 -3
  190. package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
  191. package/lib/types/ink/render-node-to-output.js +144 -25
  192. package/lib/types/ink/selection.d.ts +29 -4
  193. package/lib/types/ink/selection.d.ts.map +1 -1
  194. package/lib/types/ink/selection.js +41 -5
  195. package/lib/types/ink/timeline-rail.d.ts +138 -0
  196. package/lib/types/ink/timeline-rail.d.ts.map +1 -0
  197. package/lib/types/ink/timeline-rail.js +179 -0
  198. package/lib/types/ink/wrap-text.d.ts.map +1 -1
  199. package/lib/types/ink/wrap-text.js +47 -5
  200. package/lib/types/screens/Chat.d.ts.map +1 -1
  201. package/lib/types/screens/Chat.js +819 -337
  202. package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
  203. package/lib/types/screens/SessionBrowser.js +56 -29
  204. package/lib/types/screens/Settings.d.ts.map +1 -1
  205. package/lib/types/screens/Settings.js +64 -22
  206. package/lib/types/screens/StatusLine.d.ts +2 -1
  207. package/lib/types/screens/StatusLine.d.ts.map +1 -1
  208. package/lib/types/screens/StatusLine.js +17 -1
  209. package/lib/types/screens/TrajectoryScene.d.ts.map +1 -1
  210. package/lib/types/screens/TrajectoryScene.js +81 -14
  211. package/lib/types/screens/chatOverlay.d.ts +221 -0
  212. package/lib/types/screens/chatOverlay.d.ts.map +1 -0
  213. package/lib/types/screens/chatOverlay.js +114 -0
  214. package/lib/types/tips.d.ts.map +1 -1
  215. package/lib/types/tips.js +64 -10
  216. package/lib/types/tuiDisplayPrefs.d.ts +15 -0
  217. package/lib/types/tuiDisplayPrefs.d.ts.map +1 -1
  218. package/lib/types/tuiDisplayPrefs.js +9 -0
  219. package/lib/types/utils/debug.d.ts +10 -0
  220. package/lib/types/utils/debug.d.ts.map +1 -1
  221. package/lib/types/utils/debug.js +24 -0
  222. package/lib/types/utils/fileSuggestions.d.ts +19 -0
  223. package/lib/types/utils/fileSuggestions.d.ts.map +1 -0
  224. package/lib/types/utils/fileSuggestions.js +40 -0
  225. package/lib/types/utils/mentions.d.ts.map +1 -1
  226. package/lib/types/utils/mentions.js +3 -1
  227. package/lib/types/utils/paths.d.ts.map +1 -1
  228. package/lib/types/utils/paths.js +1 -0
  229. package/package.json +69 -66
@@ -0,0 +1,68 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { Box, Text, NoSelect } from '../ui.js';
4
+ import { RAIL_MIN_TERMINAL_WIDTH, RAIL_WIDTH } from '../ink/timeline-rail.js';
5
+ /** Thumb glyph across the 2-col gutter: solid, clearly positional —
6
+ * deliberately distinct from the timeline's ━━ active tick. */
7
+ const THUMB = '██';
8
+ /**
9
+ * Proportional scrollbar for the fullscreen transcript's gutter — the
10
+ * `scrollbar` option of the `dsh-tui.scrollGutter` setting (the timeline
11
+ * rail's sibling; same 2-column slot, same chrome rules):
12
+ *
13
+ * - the thumb (██) shows the visible window's position AND size over the
14
+ * whole content (viewport²/content tall, positioned by scrollTop);
15
+ * - clicking the track scrolls the clicked position to the viewport top
16
+ * (classic scrollbar semantics — the thumb centers under the click
17
+ * through the follow-up renders);
18
+ * - the gutter is permanent while scrollable (Qwen's rule: an
19
+ * auto-hiding gutter that changes content width rewraps everything);
20
+ * hidden below 60 terminal columns or when the content fits (inline
21
+ * mode keeps the terminal's native scrollback);
22
+ * - NoSelect fences the glyphs out of click-drag text selection; the
23
+ * wheel over the gutter scrolls the transcript.
24
+ *
25
+ * Thumb dragging is intentionally NOT implemented (Grok's MVP rule): a
26
+ * proportional thumb encodes position, and the tick rail remains the
27
+ * semantic navigator. Click-to-position covers the same need.
28
+ */
29
+ export function ScrollbarGutter({ handle, terminalWidth, }) {
30
+ const [, setTick] = React.useState(0);
31
+ React.useEffect(() => {
32
+ if (!handle)
33
+ return;
34
+ return handle.subscribe(() => setTick(t => t + 1));
35
+ }, [handle]);
36
+ if (!handle)
37
+ return null;
38
+ const viewport = handle.getViewportHeight();
39
+ const content = handle.getScrollHeight();
40
+ const maxScroll = Math.max(0, content - viewport);
41
+ if (viewport < 2 || content <= viewport || terminalWidth < RAIL_MIN_TERMINAL_WIDTH)
42
+ return null;
43
+ // Thumb: the visible window mapped onto the gutter. Height proportional
44
+ // to the visible fraction (≥2 rows so it is always grabbable-looking);
45
+ // top follows scrollTop over the scroll range.
46
+ const thumbH = Math.max(2, Math.round((viewport * viewport) / content));
47
+ const trackH = Math.max(1, viewport - thumbH);
48
+ const thumbTop = Math.round((handle.getScrollTop() / Math.max(1, maxScroll)) * trackH);
49
+ const thumbBottom = Math.min(viewport, thumbTop + thumbH);
50
+ // Clicking the track maps the clicked row's position on the track back
51
+ // to a scrollTop and scrolls that content position to the viewport top.
52
+ const trackScrollTop = (y) => {
53
+ if (y <= 0)
54
+ return 0;
55
+ if (y >= trackH)
56
+ return maxScroll;
57
+ return Math.round((y / trackH) * maxScroll);
58
+ };
59
+ const rows = [];
60
+ for (let y = 0; y < viewport; y++) {
61
+ const inThumb = y >= thumbTop && y < thumbBottom;
62
+ rows.push(_jsx(Box, { height: 1, flexShrink: 0, onClick: () => handle.scrollTo(trackScrollTop(y)), children: _jsx(Text, { color: inThumb ? 'inactive' : undefined, children: inThumb ? THUMB : ' ' }) }, y));
63
+ }
64
+ return (_jsx(NoSelect, { fromLeftEdge: true, children: _jsx("ink-box", { onWheel: e => {
65
+ if (e.deltaY !== 0)
66
+ handle.scrollBy(e.deltaY);
67
+ }, style: { flexDirection: 'column', flexShrink: 0, width: RAIL_WIDTH }, children: rows }) }));
68
+ }
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import type { ClickEvent } from '../ink/events/click-event.js';
2
3
  export type SelectOption = {
3
4
  value: string;
4
5
  /** Row content; plain strings render inline, richer rows may carry color swatches. */
@@ -11,12 +12,18 @@ export type SelectOption = {
11
12
  * scroll arrows). Keyboard navigation is owned by the parent dialog, which
12
13
  * passes focus/selection indices back in.
13
14
  */
14
- export declare function Select({ options, focusIndex, selectedValue, visibleOptionCount, }: {
15
+ export declare function Select({ options, focusIndex, selectedValue, visibleOptionCount, onPick, }: {
15
16
  options: readonly SelectOption[];
16
17
  /** Index of the keyboard-focused row (shows the ❯ pointer). */
17
18
  focusIndex: number;
18
19
  /** Value of the chosen row (shows the ✓ checkmark). */
19
20
  selectedValue: string | undefined;
20
21
  visibleOptionCount?: number;
22
+ /**
23
+ * Mouse pick handler (fullscreen). Clicking a row reports the row's
24
+ * absolute index — the parent typically applies it with the same code
25
+ * path as the keyboard Enter. Absent → rows are not clickable.
26
+ */
27
+ onPick?: (index: number, value: string, event: ClickEvent) => void;
21
28
  }): React.ReactNode;
22
29
  //# sourceMappingURL=Select.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../../src/components/Select.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,sFAAsF;IACtF,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,EACrB,OAAO,EACP,UAAU,EACV,aAAa,EACb,kBAAsB,GACvB,EAAE;IACD,OAAO,EAAE,SAAS,YAAY,EAAE,CAAA;IAChC,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAA;IAClB,uDAAuD;IACvD,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,GAAG,KAAK,CAAC,SAAS,CAiClB"}
1
+ {"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../../src/components/Select.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAA;AAG9D,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,sFAAsF;IACtF,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,EACrB,OAAO,EACP,UAAU,EACV,aAAa,EACb,kBAAsB,EACtB,MAAM,GACP,EAAE;IACD,OAAO,EAAE,SAAS,YAAY,EAAE,CAAA;IAChC,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAA;IAClB,uDAAuD;IACvD,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,KAAK,IAAI,CAAA;CACnE,GAAG,KAAK,CAAC,SAAS,CAsClB"}
@@ -7,13 +7,15 @@ import { ListItem } from './design-system/ListItem.js';
7
7
  * scroll arrows). Keyboard navigation is owned by the parent dialog, which
8
8
  * passes focus/selection indices back in.
9
9
  */
10
- export function Select({ options, focusIndex, selectedValue, visibleOptionCount = 5, }) {
10
+ export function Select({ options, focusIndex, selectedValue, visibleOptionCount = 5, onPick, }) {
11
11
  // Window around the focus row, with scroll hints at the edges (CC style).
12
12
  const startIndex = Math.max(0, Math.min(focusIndex - Math.floor(visibleOptionCount / 2), options.length - visibleOptionCount));
13
13
  const endIndex = Math.min(startIndex + visibleOptionCount, options.length);
14
14
  const visible = options.slice(startIndex, endIndex);
15
15
  return (_jsx(Box, { flexDirection: "column", children: visible.map((option, index) => {
16
16
  const absoluteIndex = startIndex + index;
17
- return (_jsx(ListItem, { isFocused: absoluteIndex === focusIndex, isSelected: option.value === selectedValue, description: option.description, showScrollUp: absoluteIndex === startIndex && startIndex > 0, showScrollDown: absoluteIndex === endIndex - 1 && endIndex < options.length, children: option.label }, option.value));
17
+ return (_jsx(ListItem, { isFocused: absoluteIndex === focusIndex, isSelected: option.value === selectedValue, description: option.description, showScrollUp: absoluteIndex === startIndex && startIndex > 0, showScrollDown: absoluteIndex === endIndex - 1 && endIndex < options.length, onClick: onPick
18
+ ? (event) => onPick(absoluteIndex, option.value, event)
19
+ : undefined, children: option.label }, option.value));
18
20
  }) }));
19
21
  }
@@ -9,9 +9,12 @@ import type { SkillInfo } from '../dsh-adapter/channel.js';
9
9
  * 长列表按焦点窗口化(ModelPicker 同款):picker 经 OverlayAbove 浮层挂载后
10
10
  * 有 maxHeight 裁剪,全量渲染会让焦点行被裁掉(看不到焦点按 Enter)。
11
11
  */
12
- export declare function SkillsPicker({ skills, focusIndex, }: {
12
+ export declare function SkillsPicker({ skills, focusIndex, onPick, }: {
13
13
  skills: readonly SkillInfo[];
14
14
  focusIndex: number;
15
+ /** Mouse pick (fullscreen): clicked row's absolute index (Chat applies
16
+ * the same code path as the keyboard Enter). */
17
+ onPick?: (index: number) => void;
15
18
  }): React.ReactNode;
16
19
  /** `/skills` while the registry snapshot is still in flight (ModelPickerLoading 同款). */
17
20
  export declare function SkillsPickerLoading(): React.ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"SkillsPicker.d.ts","sourceRoot":"","sources":["../../../src/components/SkillsPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AA2B1D;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,UAAU,GACX,EAAE;IACD,MAAM,EAAE,SAAS,SAAS,EAAE,CAAA;IAC5B,UAAU,EAAE,MAAM,CAAA;CACnB,GAAG,KAAK,CAAC,SAAS,CAyClB;AAED,wFAAwF;AACxF,wBAAgB,mBAAmB,IAAI,KAAK,CAAC,SAAS,CAerD"}
1
+ {"version":3,"file":"SkillsPicker.d.ts","sourceRoot":"","sources":["../../../src/components/SkillsPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AA2B1D;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,UAAU,EACV,MAAM,GACP,EAAE;IACD,MAAM,EAAE,SAAS,SAAS,EAAE,CAAA;IAC5B,UAAU,EAAE,MAAM,CAAA;IAClB;qDACiD;IACjD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC,GAAG,KAAK,CAAC,SAAS,CA0ClB;AAED,wFAAwF;AACxF,wBAAgB,mBAAmB,IAAI,KAAK,CAAC,SAAS,CAerD"}
@@ -34,14 +34,14 @@ function sourceLabel(source) {
34
34
  * 长列表按焦点窗口化(ModelPicker 同款):picker 经 OverlayAbove 浮层挂载后
35
35
  * 有 maxHeight 裁剪,全量渲染会让焦点行被裁掉(看不到焦点按 Enter)。
36
36
  */
37
- export function SkillsPicker({ skills, focusIndex, }) {
37
+ export function SkillsPicker({ skills, focusIndex, onPick, }) {
38
38
  const { rows: terminalRows } = useTerminalSize();
39
39
  // 每项恒占 2 行(正文 + 来源/简述描述行,均 truncate 成单行)。
40
40
  // 框架行:浮层预留 8 + Pane 2 + 标题 2 + 页脚 1 + 挂载包裹 marginTop 1 = 14(ModelPicker 同款)。
41
41
  const { start, end } = listWindow(skills.map(() => 2), focusIndex, Math.max(terminalRows - 14, 2));
42
42
  return (_jsxs(Pane, { color: "permission", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: t('picker-title-skills') }) }), skills.length === 0 ? (_jsx(Text, { dimColor: true, children: t('skills-empty') })) : (skills.slice(start, end).map((skill, index) => {
43
43
  const absoluteIndex = start + index;
44
- return (_jsx(ListItem, { isFocused: absoluteIndex === focusIndex, description: `${sourceLabel(skill.source)}${skill.description === '' ? '' : ` · ${skill.description}`}`, showScrollUp: absoluteIndex === start && start > 0, showScrollDown: absoluteIndex === end - 1 && end < skills.length, children: skill.userInvocable ? `/${skill.name}` : skill.name }, skill.name));
44
+ return (_jsx(ListItem, { isFocused: absoluteIndex === focusIndex, description: `${sourceLabel(skill.source)}${skill.description === '' ? '' : ` · ${skill.description}`}`, showScrollUp: absoluteIndex === start && start > 0, showScrollDown: absoluteIndex === end - 1 && end < skills.length, onClick: onPick ? () => onPick(absoluteIndex) : undefined, children: skill.userInvocable ? `/${skill.name}` : skill.name }, skill.name));
45
45
  }))] }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('hint-fill-exit') }) })] }));
46
46
  }
47
47
  /** `/skills` while the registry snapshot is still in flight (ModelPickerLoading 同款). */
@@ -1,9 +1,10 @@
1
1
  import React from 'react';
2
2
  import type { SubagentState } from '../dsh-adapter/subagents.js';
3
+ import type { ClickEvent } from '../ink/events/click-event.js';
3
4
  export interface SubagentCardProps {
4
5
  subagent: SubagentState;
5
6
  focused?: boolean;
6
- onClick?: () => void;
7
+ onClick?(event: ClickEvent): void;
7
8
  }
8
9
  export declare function SubagentCard({ subagent, focused, onClick }: SubagentCardProps): React.ReactNode;
9
10
  //# sourceMappingURL=SubagentCard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SubagentCard.d.ts","sourceRoot":"","sources":["../../../src/components/SubagentCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAShE,MAAM,WAAW,iBAAiB;IAAG,QAAQ,EAAE,aAAa,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE;AAEvG,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,iBAAiB,GAAG,KAAK,CAAC,SAAS,CA4B/F"}
1
+ {"version":3,"file":"SubagentCard.d.ts","sourceRoot":"","sources":["../../../src/components/SubagentCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAEvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAGhE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAA;AAO9D,MAAM,WAAW,iBAAiB;IAAG,QAAQ,EAAE,aAAa,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;CAAE;AAEpH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,iBAAiB,GAAG,KAAK,CAAC,SAAS,CAwC/F"}
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
2
3
  import { Box, Text } from '../ui.js';
3
4
  import { t } from '../i18n.js';
4
5
  import { isMinimalMode } from '../minimalMode.js';
@@ -22,5 +23,9 @@ export function SubagentCard({ subagent, focused, onClick }) {
22
23
  : running ? 'warning'
23
24
  : subagent.status === 'failed' || subagent.status === 'cancelled' ? 'error'
24
25
  : 'success';
25
- return _jsxs(Box, { flexDirection: "column", paddingLeft: 1, marginBottom: 1, onClick: onClick, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: glyphColor, children: glyph }), _jsx(Text, { bold: true, color: focused ? 'claude' : undefined, children: `${t('subagent-card-prefix')}${subagent.description}` }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: ' · ' }), _jsx(Text, { children: subagent.model ?? subagent.provider ?? 'default' }), _jsx(Text, { dimColor: true, children: ` · ${formatDuration(elapsed)} · ${total || '—'} tok · ${subagent.toolCalls.length} tools` })] })] }), liveLine && _jsx(Text, { dimColor: true, wrap: "truncate", children: ` │ ${liveLine}` })] });
26
+ // Mouse affordance: clickable cards tint on hover; the keyboard-focused
27
+ // card keeps its brand-color header (no double highlight).
28
+ const [hovered, setHovered] = useState(false);
29
+ const hoverTint = onClick !== undefined && hovered && !focused;
30
+ return _jsxs(Box, { flexDirection: "column", paddingLeft: 1, marginBottom: 1, onClick: onClick, onMouseEnter: onClick !== undefined ? () => setHovered(true) : undefined, onMouseLeave: onClick !== undefined ? () => setHovered(false) : undefined, backgroundColor: hoverTint ? 'userMessageBackgroundHover' : undefined, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: glyphColor, children: glyph }), _jsx(Text, { bold: true, color: focused ? 'claude' : undefined, children: `${t('subagent-card-prefix')}${subagent.description}` }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: ' · ' }), _jsx(Text, { children: subagent.model ?? subagent.provider ?? 'default' }), _jsx(Text, { dimColor: true, children: ` · ${formatDuration(elapsed)} · ${total || '—'} tok · ${subagent.toolCalls.length} tools` })] })] }), liveLine && _jsx(Text, { dimColor: true, wrap: "truncate", children: ` │ ${liveLine}` })] });
26
31
  }
@@ -5,6 +5,10 @@ export interface SubagentDashboardProps {
5
5
  onClose: () => void;
6
6
  onSelect?: (agentId: string) => void;
7
7
  }
8
+ /** 可点击 ✕ 退出按钮:整屏/浮层场景的鼠标退出通道(Esc 等价)。 */
9
+ export declare function ExitButton({ onClick }: {
10
+ onClick: () => void;
11
+ }): React.ReactNode;
8
12
  /**
9
13
  * SubagentDashboard — overlay panel showing all active/recent subagents.
10
14
  * Keyboard: up/down to navigate, Enter to view detail, Esc to close.
@@ -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;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,SAAS,EACT,OAAO,EACP,QAAQ,EACT,EAAE,sBAAsB,GAAG,KAAK,CAAC,SAAS,CAoG1C"}
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,CA2G1C"}
@@ -5,6 +5,11 @@ import { SubagentCard } from './SubagentCard.js';
5
5
  import { t } from '../i18n.js';
6
6
  import { Divider } from './design-system/Divider.js';
7
7
  import { isPlainReturnInput } from '../utils/modifiers.js';
8
+ /** 可点击 ✕ 退出按钮:整屏/浮层场景的鼠标退出通道(Esc 等价)。 */
9
+ export function ExitButton({ onClick }) {
10
+ const [hovered, setHovered] = React.useState(false);
11
+ return (_jsx(Box, { onClick: onClick, onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), children: _jsx(Text, { color: hovered ? 'text' : 'subtle', children: ' ✕' }) }));
12
+ }
8
13
  /**
9
14
  * SubagentDashboard — overlay panel showing all active/recent subagents.
10
15
  * Keyboard: up/down to navigate, Enter to view detail, Esc to close.
@@ -44,7 +49,10 @@ export function SubagentDashboard({ subagents, onClose, onSelect }) {
44
49
  const running = subagents.filter(s => s.status === 'running').length;
45
50
  const completed = subagents.filter(s => s.status === 'completed').length;
46
51
  const failed = subagents.filter(s => s.status === 'failed').length;
47
- return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsx(Divider, { color: "claude", title: t('subagent-dashboard-title'), padding: 4 }), _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, { 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 }), 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: "", padding: 4 }), _jsx(Box, { marginTop: 0, children: _jsx(Text, { dimColor: true, children: onSelect
52
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsx(Divider, { color: "claude", title: t('subagent-dashboard-title'), padding: 4 }), _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
+ // Click = view detail, same as Enter on the focused card.
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: "", padding: 4 }), _jsx(Box, { marginTop: 0, children: _jsx(Text, { dimColor: true, children: onSelect
48
56
  ? t('subagent-dashboard-hint-detail')
49
57
  : t('subagent-dashboard-hint-basic') }) })] }));
50
58
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SubagentDetailScene.d.ts","sourceRoot":"","sources":["../../../src/components/SubagentDetailScene.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAsGhE,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,aAAa,CAAA;IACvB,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,MAAM,EACN,WAAW,GACZ,EAAE,wBAAwB,GAAG,KAAK,CAAC,SAAS,CA+L5C"}
1
+ {"version":3,"file":"SubagentDetailScene.d.ts","sourceRoot":"","sources":["../../../src/components/SubagentDetailScene.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAuGhE,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,aAAa,CAAA;IACvB,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,MAAM,EACN,WAAW,GACZ,EAAE,wBAAwB,GAAG,KAAK,CAAC,SAAS,CA8M5C"}
@@ -1,8 +1,9 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  import { Box, Text, useInput, ScrollBox, useTerminalSize } from '../ui.js';
4
4
  import { t } from '../i18n.js';
5
5
  import { Divider } from './design-system/Divider.js';
6
+ import { ExitButton } from './SubagentDashboard.js';
6
7
  import { isPlainReturnInput } from '../utils/modifiers.js';
7
8
  import { toolNameColor } from './messages/AssistantToolUseMessage.js';
8
9
  import { getCliHighlightPromise } from '../cc/cliHighlight.js';
@@ -134,8 +135,8 @@ export function SubagentDetailScene({ subagent, onBack, onInterrupt, }) {
134
135
  });
135
136
  const tab = (name, label) => {
136
137
  const active = page === name;
137
- return (_jsxs(React.Fragment, { children: [_jsx(Text, { color: active ? 'claude' : undefined, bold: active, inverse: active, children: ` ${label} ` }), _jsx(Text, { dimColor: true, children: name === PAGES[PAGES.length - 1] ? '' : '│' })] }, name));
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));
138
139
  };
139
- 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 })] }), _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)}`
140
- + (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: "", padding: 4 }), _jsx(Box, { marginTop: 0, children: _jsxs(Text, { dimColor: true, children: [`←/→ ${t('subagent-hint-page')} · ↑/↓ ${t('subagent-hint-scroll')}`, isRunning && onInterrupt ? ' · X interrupt' : '', ` · Esc ${t('subagent-hint-back')}`] }) })] }));
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: "", padding: 4 }), _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')}` })] })] }));
141
142
  }
@@ -0,0 +1,63 @@
1
+ import React from 'react';
2
+ /**
3
+ * `/` 命令菜单与 `@` 文件菜单共用的圆角卡片外壳(与输入框 EffortInputBorder
4
+ * 同款 ╭╮╰╯ 视觉语言):
5
+ *
6
+ * ╭─ 标题 ─────────────────────╮
7
+ * │ ❯ 行内容 … │
8
+ * │ 行内容 … │
9
+ * │ ↑2 · ↓3 │ ← 仅当列表被裁剪
10
+ * ╰────────────────────────────╯
11
+ *
12
+ * 底边 ╰╯ 直接坐在输入框顶边 ╭╮ 的上一行(PromptInput 的浮层包装去掉了
13
+ * 底部留白),卡片与输入框连成一体,读作"挂在输入框上的下拉"。边框色
14
+ * 跟随输入框 idle 色(plan 模式下整套面板一起变 sage 绿)。
15
+ *
16
+ * 每一行的左右 │ 由本组件包在行外——侧边框若是内容列旁的单行 Text,只
17
+ * 会画在自己那一行(行高的首行),多行列表的中段行会裸奔无边框。
18
+ * 卡片左右各占 2 列(│ + 1 空格),行内容的可用宽度由
19
+ * {@link cardContentWidth} 统一计算,CJK 截断契约(verify-cjk-truncate)
20
+ * 按该宽度钉住。
21
+ */
22
+ export declare function SuggestionCard({ title, columns, accent, footer, rows, onRowPick, onWheelStep, }: {
23
+ /** 嵌在顶边框里的标题(已本地化、含计数)。 */
24
+ title: string;
25
+ columns: number;
26
+ /** 边框色(主题 token);缺省 promptBorder。 */
27
+ accent?: 'promptBorder' | 'planMode';
28
+ /** 底部 dim 提示行(滚动指示);null/undefined 时不渲染。 */
29
+ footer?: string | null;
30
+ /** 已渲染的行内容(每行一个节点),本组件为各行补上左右边框。 */
31
+ rows: readonly React.ReactNode[];
32
+ /**
33
+ * 鼠标点击行(fullscreen):上报行索引——命令/文件补全用它接受该项
34
+ * (与 Tab/Enter 同路径)。未提供时行为不变。
35
+ */
36
+ onRowPick?: (index: number) => void;
37
+ /**
38
+ * 滚轮在菜单上滚动(fullscreen):每次滚动上报 ±1 步——补全菜单用它
39
+ * 移动选中行(窗口随之滚动)。位置路由保证只有菜单下的滚轮到达这里。
40
+ */
41
+ onWheelStep?: (step: 1 | -1) => void;
42
+ }): React.ReactNode;
43
+ /**
44
+ * 卡片内一行内容的可用显示宽度:总宽减去两侧 │ + 各 1 空格的内边距。
45
+ * CommandSuggestions / FileSuggestions 的截断数学共用这一口径。
46
+ */
47
+ export declare function cardContentWidth(columns: number): number;
48
+ /**
49
+ * 把补全名按「命中的查询前缀」拆成三段(用于前缀高亮)。三级尝试,
50
+ * 均大小写不敏感,与 completeCommands / 文件候选的过滤语义对齐:
51
+ * 1. 整名前缀(文件查询是路径前缀,`src/re` 命中 `src/render`);
52
+ * 2. 最后一个空格 token 的前缀(嵌套命令 `model deepseek/…` 的
53
+ * `deepseek/` 查询——补全名带 `model ` 路径前缀);
54
+ * 3. 最后一个 `/` 段的前缀(`/model deepseek-v` 命中段
55
+ * `deepseek-v4-flash` 的开头)。
56
+ * 查询为空、或都不命中(别名命中、过期候选)返回 null——渲染方整体 dim。
57
+ */
58
+ export declare function splitQueryMatch(name: string, query: string): {
59
+ before: string;
60
+ match: string;
61
+ after: string;
62
+ } | null;
63
+ //# sourceMappingURL=SuggestionCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SuggestionCard.d.ts","sourceRoot":"","sources":["../../../src/components/SuggestionCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,OAAO,EACP,MAAM,EACN,MAAM,EACN,IAAI,EACJ,SAAS,EACT,WAAW,GACZ,EAAE;IACD,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,qCAAqC;IACrC,MAAM,CAAC,EAAE,cAAc,GAAG,UAAU,CAAA;IACpC,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,oCAAoC;IACpC,IAAI,EAAE,SAAS,KAAK,CAAC,SAAS,EAAE,CAAA;IAChC;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAA;CACrC,GAAG,KAAK,CAAC,SAAS,CAwDlB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAmBzD"}
@@ -0,0 +1,77 @@
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 { stringWidth } from '../ink/stringWidth.js';
5
+ /**
6
+ * `/` 命令菜单与 `@` 文件菜单共用的圆角卡片外壳(与输入框 EffortInputBorder
7
+ * 同款 ╭╮╰╯ 视觉语言):
8
+ *
9
+ * ╭─ 标题 ─────────────────────╮
10
+ * │ ❯ 行内容 … │
11
+ * │ 行内容 … │
12
+ * │ ↑2 · ↓3 │ ← 仅当列表被裁剪
13
+ * ╰────────────────────────────╯
14
+ *
15
+ * 底边 ╰╯ 直接坐在输入框顶边 ╭╮ 的上一行(PromptInput 的浮层包装去掉了
16
+ * 底部留白),卡片与输入框连成一体,读作"挂在输入框上的下拉"。边框色
17
+ * 跟随输入框 idle 色(plan 模式下整套面板一起变 sage 绿)。
18
+ *
19
+ * 每一行的左右 │ 由本组件包在行外——侧边框若是内容列旁的单行 Text,只
20
+ * 会画在自己那一行(行高的首行),多行列表的中段行会裸奔无边框。
21
+ * 卡片左右各占 2 列(│ + 1 空格),行内容的可用宽度由
22
+ * {@link cardContentWidth} 统一计算,CJK 截断契约(verify-cjk-truncate)
23
+ * 按该宽度钉住。
24
+ */
25
+ export function SuggestionCard({ title, columns, accent, footer, rows, onRowPick, onWheelStep, }) {
26
+ const inner = Math.max(0, columns - 2);
27
+ const lead = `─ ${title} `;
28
+ // 标题放不下(极窄终端)时退化为素边框,不做半截标题。
29
+ const titleFits = stringWidth(lead) + 1 <= inner;
30
+ const top = titleFits
31
+ ? `╭${lead}${'─'.repeat(inner - stringWidth(lead))}╮`
32
+ : `╭${'─'.repeat(inner)}╮`;
33
+ const borderColor = accent ?? 'promptBorder';
34
+ const [hoveredRow, setHoveredRow] = React.useState(-1);
35
+ const handleWheel = React.useCallback((e) => {
36
+ if (e.deltaY !== 0)
37
+ onWheelStep?.(e.deltaY > 0 ? 1 : -1);
38
+ }, [onWheelStep]);
39
+ return (_jsxs("ink-box", { style: { flexDirection: 'column', width: '100%', flexShrink: 0 }, onWheel: onWheelStep !== undefined ? handleWheel : undefined, children: [_jsx(Text, { color: borderColor, wrap: "truncate-end", children: top }), rows.map((row, index) => (_jsxs(Box, { flexDirection: "row", width: "100%", onClick: onRowPick ? () => onRowPick(index) : undefined, onMouseEnter: onRowPick ? () => setHoveredRow(index) : undefined, onMouseLeave: onRowPick ? () => setHoveredRow(current => (current === index ? -1 : current)) : undefined, children: [_jsx(Text, { color: borderColor, children: "\u2502" }), _jsx(Box, { flexDirection: "column", flexGrow: 1, minWidth: 0, backgroundColor: onRowPick !== undefined && hoveredRow === index ? 'userMessageBackgroundHover' : undefined, children: row }), _jsx(Text, { color: borderColor, children: "\u2502" })] }, index))), footer ? (_jsxs(Box, { flexDirection: "row", width: "100%", children: [_jsx(Text, { color: borderColor, children: "\u2502" }), _jsx(Box, { flexGrow: 1, minWidth: 0, children: _jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [" ", footer] }) }), _jsx(Text, { color: borderColor, children: "\u2502" })] })) : null, _jsx(Text, { color: borderColor, wrap: "truncate-end", children: `╰${'─'.repeat(inner)}╯` })] }));
40
+ }
41
+ /**
42
+ * 卡片内一行内容的可用显示宽度:总宽减去两侧 │ + 各 1 空格的内边距。
43
+ * CommandSuggestions / FileSuggestions 的截断数学共用这一口径。
44
+ */
45
+ export function cardContentWidth(columns) {
46
+ return Math.max(0, columns - 4);
47
+ }
48
+ /**
49
+ * 把补全名按「命中的查询前缀」拆成三段(用于前缀高亮)。三级尝试,
50
+ * 均大小写不敏感,与 completeCommands / 文件候选的过滤语义对齐:
51
+ * 1. 整名前缀(文件查询是路径前缀,`src/re` 命中 `src/render`);
52
+ * 2. 最后一个空格 token 的前缀(嵌套命令 `model deepseek/…` 的
53
+ * `deepseek/` 查询——补全名带 `model ` 路径前缀);
54
+ * 3. 最后一个 `/` 段的前缀(`/model deepseek-v` 命中段
55
+ * `deepseek-v4-flash` 的开头)。
56
+ * 查询为空、或都不命中(别名命中、过期候选)返回 null——渲染方整体 dim。
57
+ */
58
+ export function splitQueryMatch(name, query) {
59
+ if (query === '')
60
+ return null;
61
+ const lower = query.toLowerCase();
62
+ const startsWith = (start) => {
63
+ const segment = name.slice(start);
64
+ if (!segment.toLowerCase().startsWith(lower))
65
+ return null;
66
+ const matched = segment.slice(0, Math.min(query.length, segment.length));
67
+ return {
68
+ before: name.slice(0, start),
69
+ match: matched,
70
+ after: segment.slice(matched.length),
71
+ };
72
+ };
73
+ const lastSpace = name.lastIndexOf(' ');
74
+ return (startsWith(0)
75
+ ?? (lastSpace >= 0 ? startsWith(lastSpace + 1) : null)
76
+ ?? startsWith(name.lastIndexOf('/') + 1));
77
+ }
@@ -19,8 +19,11 @@ export declare function getThemeOptions(): SelectOption[];
19
19
  * active theme. Enter applies through the ThemeProvider setter (persists to
20
20
  * ~/.dsh-tui/theme.json and hot swaps), Esc cancels.
21
21
  */
22
- export declare function ThemePicker({ focusIndex, currentTheme, }: {
22
+ export declare function ThemePicker({ focusIndex, currentTheme, onPick, }: {
23
23
  focusIndex: number;
24
24
  currentTheme: string | undefined;
25
+ /** Mouse pick (fullscreen): clicked row's absolute index (Chat applies
26
+ * the same code path as the keyboard Enter). */
27
+ onPick?: (index: number) => void;
25
28
  }): React.ReactNode;
26
29
  //# sourceMappingURL=ThemePicker.d.ts.map
@@ -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;AAuCvD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,IAAI,YAAY,EAAE,CAsBhD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,EAC1B,UAAU,EACV,YAAY,GACb,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;CACjC,GAAG,KAAK,CAAC,SAAS,CAsBlB"}
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;AAuCvD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,IAAI,YAAY,EAAE,CAsBhD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,EAC1B,UAAU,EACV,YAAY,EACZ,MAAM,GACP,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC;qDACiD;IACjD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC,GAAG,KAAK,CAAC,SAAS,CAuBlB"}
@@ -51,7 +51,7 @@ export function getThemeOptions() {
51
51
  * active theme. Enter applies through the ThemeProvider setter (persists to
52
52
  * ~/.dsh-tui/theme.json and hot swaps), Esc cancels.
53
53
  */
54
- export function ThemePicker({ focusIndex, currentTheme, }) {
54
+ export function ThemePicker({ focusIndex, currentTheme, onPick, }) {
55
55
  const options = React.useMemo(() => getThemeOptions(), []);
56
- 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 }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('hint-confirm-exit') }) })] }) }));
56
+ 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
57
  }
@@ -3,8 +3,11 @@ import React from 'react';
3
3
  * The `/thinking` display dialog: a permission-colored Pane with a bold
4
4
  * title, the Shown/Hidden select, and the Enter/Esc hint line.
5
5
  */
6
- export declare function ThinkingToggle({ currentValue, focusIndex, }: {
6
+ export declare function ThinkingToggle({ currentValue, focusIndex, onPick, }: {
7
7
  currentValue: boolean;
8
8
  focusIndex: number;
9
+ /** Mouse pick (fullscreen): reports the clicked row's index — Chat
10
+ * applies it with the same code path as the keyboard Enter. */
11
+ onPick?: (index: number) => void;
9
12
  }): React.ReactNode;
10
13
  //# sourceMappingURL=ThinkingToggle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ThinkingToggle.d.ts","sourceRoot":"","sources":["../../../src/components/ThinkingToggle.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,YAAY,EACZ,UAAU,GACX,EAAE;IACD,YAAY,EAAE,OAAO,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;CACnB,GAAG,KAAK,CAAC,SAAS,CAsClB"}
1
+ {"version":3,"file":"ThinkingToggle.d.ts","sourceRoot":"","sources":["../../../src/components/ThinkingToggle.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,YAAY,EACZ,UAAU,EACV,MAAM,GACP,EAAE;IACD,YAAY,EAAE,OAAO,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB;oEACgE;IAChE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC,GAAG,KAAK,CAAC,SAAS,CAuClB"}
@@ -8,7 +8,7 @@ import { HintLine } from './design-system/HintLine.js';
8
8
  * The `/thinking` display dialog: a permission-colored Pane with a bold
9
9
  * title, the Shown/Hidden select, and the Enter/Esc hint line.
10
10
  */
11
- export function ThinkingToggle({ currentValue, focusIndex, }) {
11
+ export function ThinkingToggle({ currentValue, focusIndex, onPick, }) {
12
12
  const options = [
13
13
  {
14
14
  value: 'true',
@@ -21,5 +21,5 @@ export function ThinkingToggle({ currentValue, focusIndex, }) {
21
21
  description: t('thinking-disabled-desc'),
22
22
  },
23
23
  ];
24
- 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('thinking-title') }), _jsx(Text, { dimColor: true, children: t('thinking-subtitle') })] }), _jsx(Box, { flexDirection: "column", marginBottom: 1, children: _jsx(Select, { options: options, focusIndex: focusIndex, selectedValue: currentValue ? 'true' : 'false', visibleOptionCount: 2 }) })] }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('hint-confirm-exit') }) })] }));
24
+ 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('thinking-title') }), _jsx(Text, { dimColor: true, children: t('thinking-subtitle') })] }), _jsx(Box, { flexDirection: "column", marginBottom: 1, children: _jsx(Select, { options: options, focusIndex: focusIndex, selectedValue: currentValue ? 'true' : 'false', visibleOptionCount: 2, onPick: onPick === undefined ? undefined : (index) => onPick(index) }) })] }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('hint-confirm-exit') }) })] }));
25
25
  }
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+ import { type ScrollBoxHandle } from '../ui.js';
3
+ import { type TimelineTurn } from '../ink/timeline-rail.js';
4
+ /**
5
+ * Timeline rail: the fullscreen transcript's turn navigator — a 2-column
6
+ * gutter REPLACING the classic scrollbar (Grok Build's timeline sidebar,
7
+ * ported to the dsh Ink tree).
8
+ *
9
+ * - one tick per user turn; tick position encodes conversation order,
10
+ * not scroll proportion (a long answer must not skew the timeline);
11
+ * - the tick of the turn owning the viewport top row (the turn being
12
+ * read — same anchor the sticky prompt header pins) renders as `━━`;
13
+ * hovering a tick widens it to `──` and pops a preview card left of
14
+ * the rail; idle ticks are a short dim ` ─`;
15
+ * - ▲ / ▾ step between turns by STRICT geometry (nearest prompt above /
16
+ * below the viewport top — never `active ± 1`, which can name a turn
17
+ * no scroll reaches), so a dim chevron is always a guaranteed no-op;
18
+ * - clicking a tick (or chevron) jumps by content coordinate — the
19
+ * prompt's measured top — no element seek, no force-mount race;
20
+ * - more turns than rows: the window slides around the active tick, and
21
+ * pins to the tail while at the bottom (never excluding active);
22
+ * - hidden below 60 terminal columns, under 2 turns, under 3 viewport
23
+ * rows, or when the content fits (inline mode keeps the terminal's
24
+ * native scrollback).
25
+ *
26
+ * The whole 2-column width is the hit target; the rail is fenced with
27
+ * NoSelect so click-drag text selection never picks up its glyphs, and
28
+ * the wheel scrolls the transcript (the rail has no scroll of its own).
29
+ */
30
+ export declare function TimelineRail({ handle, turns, activeId, upId, downId, terminalWidth, hoverEnabled, onRevealTurn, }: {
31
+ handle: ScrollBoxHandle | null;
32
+ /** Turns in conversation order (MessageList's measured snapshot). */
33
+ turns: ReadonlyArray<TimelineTurn>;
34
+ /** Turn owning the viewport top row (rail highlight target). */
35
+ activeId: number | null;
36
+ /** ▲ target: nearest turn strictly above the viewport top. */
37
+ upId: number | null;
38
+ /** ▼ target: nearest turn below the viewport top that a scroll can
39
+ * still bring to the top row. */
40
+ downId: number | null;
41
+ terminalWidth: number;
42
+ /** False while a modal overlay owns the screen — suppress the hover
43
+ * card (the rail itself stays, but stops narrating). */
44
+ hoverEnabled: boolean;
45
+ /**
46
+ * Reveal-and-seek for FOLDED turns (older than the recent-rows window):
47
+ * Chat expands the fold (showAll) and seeks through the force-mount
48
+ * path — the row's top is unknown until it mounts, so the coordinate
49
+ * jump below does not apply to it.
50
+ */
51
+ onRevealTurn: (rowId: number) => void;
52
+ }): React.ReactNode;
53
+ //# sourceMappingURL=TimelineRail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimelineRail.d.ts","sourceRoot":"","sources":["../../../src/components/TimelineRail.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,UAAU,CAAA;AAEpE,OAAO,EAKL,KAAK,YAAY,EAClB,MAAM,yBAAyB,CAAA;AAwBhC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,aAAa,EACb,YAAY,EACZ,YAAY,GACb,EAAE;IACD,MAAM,EAAE,eAAe,GAAG,IAAI,CAAA;IAC9B,qEAAqE;IACrE,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,CAAA;IAClC,gEAAgE;IAChE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB;sCACkC;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;IACrB;6DACyD;IACzD,YAAY,EAAE,OAAO,CAAA;IACrB;;;;;OAKG;IACH,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACtC,GAAG,KAAK,CAAC,SAAS,CA0MlB"}