@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,190 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { Box, Text, NoSelect } from '../ui.js';
4
+ import { stringWidth } from '../ink/stringWidth.js';
5
+ import { RAIL_WIDTH, railEligible, computeRailGeometry, wrapPreviewLines, } from '../ink/timeline-rail.js';
6
+ /** ▴ / ▾ (U+25B4 / U+25BE — the small triangles; CP437-safe on ConHost too). */
7
+ const CHEVRON_UP = ' ▴';
8
+ const CHEVRON_DOWN = ' ▾';
9
+ /** Tick glyphs, right-aligned across the 2-col rail (Grok Build spec):
10
+ * active = heavy stroke, hover = wide light stroke, idle = short dim
11
+ * stroke in the rightmost cell. */
12
+ const TICK_ACTIVE = '━━';
13
+ const TICK_HOVER = '──';
14
+ const TICK_IDLE = ' ─';
15
+ /** Pointer rest time before the hover preview card pops (ms). Sweeps
16
+ * never mount a card; only a deliberate pause does. */
17
+ const HOVER_DWELL_MS = 120;
18
+ /**
19
+ * Timeline rail: the fullscreen transcript's turn navigator — a 2-column
20
+ * gutter REPLACING the classic scrollbar (Grok Build's timeline sidebar,
21
+ * ported to the dsh Ink tree).
22
+ *
23
+ * - one tick per user turn; tick position encodes conversation order,
24
+ * not scroll proportion (a long answer must not skew the timeline);
25
+ * - the tick of the turn owning the viewport top row (the turn being
26
+ * read — same anchor the sticky prompt header pins) renders as `━━`;
27
+ * hovering a tick widens it to `──` and pops a preview card left of
28
+ * the rail; idle ticks are a short dim ` ─`;
29
+ * - ▲ / ▾ step between turns by STRICT geometry (nearest prompt above /
30
+ * below the viewport top — never `active ± 1`, which can name a turn
31
+ * no scroll reaches), so a dim chevron is always a guaranteed no-op;
32
+ * - clicking a tick (or chevron) jumps by content coordinate — the
33
+ * prompt's measured top — no element seek, no force-mount race;
34
+ * - more turns than rows: the window slides around the active tick, and
35
+ * pins to the tail while at the bottom (never excluding active);
36
+ * - hidden below 60 terminal columns, under 2 turns, under 3 viewport
37
+ * rows, or when the content fits (inline mode keeps the terminal's
38
+ * native scrollback).
39
+ *
40
+ * The whole 2-column width is the hit target; the rail is fenced with
41
+ * NoSelect so click-drag text selection never picks up its glyphs, and
42
+ * the wheel scrolls the transcript (the rail has no scroll of its own).
43
+ */
44
+ export function TimelineRail({ handle, turns, activeId, upId, downId, terminalWidth, hoverEnabled, onRevealTurn, }) {
45
+ const [, setTick] = React.useState(0);
46
+ const [hover, setHover] = React.useState(null);
47
+ // Dwell-gated preview card: the card pops only after the pointer RESTS
48
+ // on a tick for HOVER_DWELL_MS. Sweeping the rail fires enter/leave per
49
+ // cell — without the gate each crossing mounts an absolute-positioned
50
+ // card at a new spot (flapping previews, absolute-rect churn, and the
51
+ // fast-sweep residue users reported). The glyph highlight stays instant
52
+ // (cheap, no overlay); only the card waits for intent.
53
+ const [cardIndex, setCardIndex] = React.useState(null);
54
+ const dwellTimerRef = React.useRef(null);
55
+ const clearDwell = React.useCallback(() => {
56
+ if (dwellTimerRef.current !== null) {
57
+ clearTimeout(dwellTimerRef.current);
58
+ dwellTimerRef.current = null;
59
+ }
60
+ setCardIndex(null);
61
+ }, []);
62
+ const armDwell = React.useCallback((index) => {
63
+ if (dwellTimerRef.current !== null)
64
+ clearTimeout(dwellTimerRef.current);
65
+ dwellTimerRef.current = setTimeout(() => {
66
+ dwellTimerRef.current = null;
67
+ setCardIndex(index);
68
+ }, HOVER_DWELL_MS);
69
+ }, []);
70
+ React.useEffect(() => () => {
71
+ if (dwellTimerRef.current !== null)
72
+ clearTimeout(dwellTimerRef.current);
73
+ }, []);
74
+ React.useEffect(() => {
75
+ if (!handle)
76
+ return;
77
+ return handle.subscribe(() => {
78
+ setTick(t => t + 1);
79
+ // A scroll (wheel over the rail, tick/chevron jump, header click)
80
+ // slides the tick window under a STATIONARY pointer — without this,
81
+ // the hover card keeps re-narrating whatever turn now sits under the
82
+ // pointer cell, flapping previews across turns every frame (reads as
83
+ // ghosting). OpenCode's modality rule: only a real mouse move may
84
+ // re-enter hover; Grok likewise clears the hover popup on tick click.
85
+ // setHover(null) on an already-null hover is a React no-op.
86
+ setHover(null);
87
+ clearDwell();
88
+ });
89
+ }, [handle, clearDwell]);
90
+ if (!handle)
91
+ return null;
92
+ const viewport = handle.getViewportHeight();
93
+ const content = handle.getScrollHeight();
94
+ const maxScroll = Math.max(0, content - viewport);
95
+ if (!railEligible({
96
+ turnCount: turns.length,
97
+ terminalWidth,
98
+ viewportRows: viewport,
99
+ scrollable: content > viewport,
100
+ }))
101
+ return null;
102
+ const activeIndex = turns.findIndex(t => t.id === activeId);
103
+ // Tail-pin the tick window while the viewport sits at the bottom
104
+ // (sticky flag OR positional) — newest ticks stay on screen.
105
+ const atBottom = handle.isSticky() || handle.getScrollTop() >= maxScroll;
106
+ const geo = computeRailGeometry(turns.length, viewport, activeIndex === -1 ? null : activeIndex, atBottom);
107
+ if (!geo)
108
+ return null;
109
+ // Jump by CONTENT COORDINATE, not by element: the target row is usually
110
+ // unmounted (virtualization), and the element-based seek's force-mount
111
+ // path races React's synchronous re-render (the renderer's deferred
112
+ // Yoga read can see a detached anchor and silently no-op). The turn's
113
+ // reported top is the measured text position — scrollTo() lands the
114
+ // prompt exactly at the viewport top; the renderer clamps unreachable
115
+ // tail tops to maxScroll (their turns stay on screen, just short of
116
+ // owning the top row — and ▼ never names them, see timeline-rail.ts).
117
+ // FOLDDED turns have no measured top (-1): their click (and ▲ naming
118
+ // one) routes through onRevealTurn instead — expand the fold, then the
119
+ // existing force-mount seek lands the row once it measures.
120
+ const jumpToIndex = (index) => {
121
+ const turn = turns[index];
122
+ if (!turn)
123
+ return;
124
+ if (turn.folded)
125
+ onRevealTurn(turn.id);
126
+ else
127
+ handle.scrollTo(turn.top);
128
+ };
129
+ const jumpToId = (id) => {
130
+ if (id === null)
131
+ return;
132
+ const index = turns.findIndex(t => t.id === id);
133
+ if (index !== -1)
134
+ jumpToIndex(index);
135
+ };
136
+ const shown = geo.windowEnd - geo.windowStart;
137
+ const chevron = (kind) => {
138
+ const enabled = (kind === 'up' ? upId : downId) !== null;
139
+ const hovered = hover?.kind === kind;
140
+ const glyph = kind === 'up' ? CHEVRON_UP : CHEVRON_DOWN;
141
+ const color = !enabled ? 'subtle' : hovered ? 'text' : 'inactive';
142
+ return (_jsx(Box, { height: 1, flexShrink: 0, onClick: () => jumpToId(kind === 'up' ? upId : downId), onMouseEnter: () => setHover({ kind }), onMouseLeave: () => setHover(null), children: _jsx(Text, { color: color, children: glyph }) }, kind));
143
+ };
144
+ const tickRows = [];
145
+ for (let k = 0; k < shown; k++) {
146
+ const index = geo.windowStart + k;
147
+ const turn = turns[index];
148
+ const isActive = index === activeIndex;
149
+ const isHovered = hover?.kind === 'tick' && hover.index === index;
150
+ const glyph = isActive ? TICK_ACTIVE : isHovered ? TICK_HOVER : TICK_IDLE;
151
+ const color = isActive || isHovered ? 'text' : 'subtle';
152
+ tickRows.push(_jsx(Box, { height: 1, flexShrink: 0, onClick: () => jumpToIndex(index), onMouseEnter: () => {
153
+ setHover({ kind: 'tick', index });
154
+ armDwell(index);
155
+ }, onMouseLeave: () => {
156
+ setHover(null);
157
+ clearDwell();
158
+ }, children: _jsx(Text, { color: color, children: glyph }) }, turn.id));
159
+ }
160
+ // Hover preview card, anchored left of the rail and vertically centered
161
+ // on the hovered tick (shrink-to-fit, rounded chrome). Dwell-gated: only
162
+ // cardIndex (armed after HOVER_DWELL_MS of rest) draws — a sweep keeps
163
+ // hover flashing on glyphs but never mounts the overlay. Suppressed
164
+ // while an overlay owns the screen — the rail stops narrating over
165
+ // dialogs.
166
+ let card = null;
167
+ if (hoverEnabled && cardIndex !== null) {
168
+ const turn = turns[cardIndex];
169
+ if (turn && turn.preview.length > 0) {
170
+ const tickRow = geo.tickTop + cardIndex - geo.windowStart;
171
+ if (tickRow >= geo.tickTop && tickRow < geo.downRow) {
172
+ const budget = Math.min(32, Math.max(16, Math.floor((terminalWidth - RAIL_WIDTH) / 2)));
173
+ const lines = wrapPreviewLines(turn.preview, budget);
174
+ if (lines.length > 0 && lines.some(l => l.length > 0)) {
175
+ const widest = Math.max(...lines.map(l => stringWidth(l)));
176
+ const cardW = widest + 4; // rounded border ×2 + padding ×2
177
+ const cardH = lines.length + 2;
178
+ if (cardH <= viewport) {
179
+ const top = Math.max(0, Math.min(tickRow - Math.floor(cardH / 2), viewport - cardH));
180
+ card = (_jsx(Box, { position: "absolute", top: top, right: RAIL_WIDTH + 1, width: cardW, height: cardH, flexDirection: "column", flexShrink: 0, borderStyle: "round", borderColor: "inactive", backgroundColor: "toolCardBackgroundDim", paddingX: 1, children: lines.map((line, i) => (_jsx(Text, { color: "text", wrap: "truncate-end", children: line }, i))) }));
181
+ }
182
+ }
183
+ }
184
+ }
185
+ }
186
+ return (_jsx(NoSelect, { fromLeftEdge: true, children: _jsxs("ink-box", { onWheel: e => {
187
+ if (e.deltaY !== 0)
188
+ handle.scrollBy(e.deltaY);
189
+ }, style: { flexDirection: 'column', flexShrink: 0, width: RAIL_WIDTH }, children: [_jsx(Box, { height: geo.upRow, flexShrink: 0 }), chevron('up'), tickRows, chevron('down'), card] }) }));
190
+ }
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { TuiWorkspaceChoice } from '../workspaces.js';
3
3
  /** Generic nested choice surface returned by a workspace provider command. */
4
- export declare function WorkspaceFlowPicker({ title, choices, focusIndex, busy, input, }: {
4
+ export declare function WorkspaceFlowPicker({ title, choices, focusIndex, busy, input, onPick, }: {
5
5
  title: string;
6
6
  choices: readonly TuiWorkspaceChoice[];
7
7
  focusIndex: number;
@@ -11,5 +11,10 @@ export declare function WorkspaceFlowPicker({ title, choices, focusIndex, busy,
11
11
  cursor: number;
12
12
  placeholder?: string;
13
13
  } | null;
14
+ /** Mouse pick (fullscreen): reports the clicked row's absolute index —
15
+ * Chat applies it with the same code path as the keyboard Enter. Rows
16
+ * are inert while busy (keyboard is swallowed too) and while the inline
17
+ * text input owns the interaction. */
18
+ onPick?: (index: number) => void;
14
19
  }): React.ReactNode;
15
20
  //# sourceMappingURL=WorkspaceFlowPicker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkspaceFlowPicker.d.ts","sourceRoot":"","sources":["../../../src/components/WorkspaceFlowPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAO1D,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,OAAO,EACP,UAAU,EACV,IAAY,EACZ,KAAY,GACb,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CACvE,GAAG,KAAK,CAAC,SAAS,CAoDlB"}
1
+ {"version":3,"file":"WorkspaceFlowPicker.d.ts","sourceRoot":"","sources":["../../../src/components/WorkspaceFlowPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAO1D,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,OAAO,EACP,UAAU,EACV,IAAY,EACZ,KAAY,EACZ,MAAM,GACP,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACtE;;;2CAGuC;IACvC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC,GAAG,KAAK,CAAC,SAAS,CA0DlB"}
@@ -6,10 +6,12 @@ import { ListItem } from './design-system/ListItem.js';
6
6
  import { HintLine } from './design-system/HintLine.js';
7
7
  const WINDOW = 8;
8
8
  /** Generic nested choice surface returned by a workspace provider command. */
9
- export function WorkspaceFlowPicker({ title, choices, focusIndex, busy = false, input = null, }) {
9
+ export function WorkspaceFlowPicker({ title, choices, focusIndex, busy = false, input = null, onPick, }) {
10
10
  const start = Math.max(0, Math.min(focusIndex - Math.floor(WINDOW / 2), choices.length - WINDOW));
11
11
  const visible = choices.slice(start, start + WINDOW);
12
- return (_jsxs(Pane, { color: "permission", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: title }) }), visible.map((choice, index) => (_jsxs(ListItem, { isFocused: start + index === focusIndex, isSelected: false, description: choice.description, showScrollUp: index === 0 && start > 0, showScrollDown: index === visible.length - 1 && start + visible.length < choices.length, children: [choice.badge ? `${choice.badge} · ` : '', choice.label] }, choice.id))), input !== null && (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "remember", children: "\u276F " }), input.value.length === 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { inverse: true, children: " " }), _jsx(Text, { dimColor: true, children: input.placeholder ?? '' })] })) : (_jsxs(_Fragment, { children: [_jsx(Text, { children: input.value.slice(0, input.cursor) }), _jsx(Text, { inverse: true, children: input.value[input.cursor] ?? ' ' }), _jsx(Text, { children: input.value.slice(input.cursor + 1) })] }))] }))] }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: busy
12
+ return (_jsxs(Pane, { color: "permission", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: title }) }), visible.map((choice, index) => (_jsxs(ListItem, { isFocused: start + index === focusIndex, isSelected: false, description: choice.description, disabled: busy, showScrollUp: index === 0 && start > 0, showScrollDown: index === visible.length - 1 && start + visible.length < choices.length, onClick: onPick !== undefined && !busy && input === null
13
+ ? () => onPick(start + index)
14
+ : undefined, children: [choice.badge ? `${choice.badge} · ` : '', choice.label] }, choice.id))), input !== null && (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "remember", children: "\u276F " }), input.value.length === 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { inverse: true, children: " " }), _jsx(Text, { dimColor: true, children: input.placeholder ?? '' })] })) : (_jsxs(_Fragment, { children: [_jsx(Text, { children: input.value.slice(0, input.cursor) }), _jsx(Text, { inverse: true, children: input.value[input.cursor] ?? ' ' }), _jsx(Text, { children: input.value.slice(input.cursor + 1) })] }))] }))] }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: busy
13
15
  ? t('workspace-flow-loading')
14
16
  : input !== null
15
17
  ? t('workspace-flow-input-hint')
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ /**
3
+ * Bare `/workspace` action menu: the built-in subcommands (resume / rename /
4
+ * open) plus any dynamically registered workspace extensions. Enter runs the
5
+ * focused action through the same paths a hand-typed subcommand takes;
6
+ * actions needing free text (rename/open) fall back to their usage notice.
7
+ */
8
+ export declare function WorkspaceMenuPicker({ options, focusIndex, onPick, }: {
9
+ options: readonly {
10
+ id: string;
11
+ label: string;
12
+ description: string;
13
+ }[];
14
+ focusIndex: number;
15
+ /** Mouse pick (fullscreen): reports the clicked row's index — Chat
16
+ * applies it with the same code path as the keyboard Enter. */
17
+ onPick?: (index: number) => void;
18
+ }): React.ReactNode;
19
+ //# sourceMappingURL=WorkspaceMenuPicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkspaceMenuPicker.d.ts","sourceRoot":"","sources":["../../../src/components/WorkspaceMenuPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,UAAU,EACV,MAAM,GACP,EAAE;IACD,OAAO,EAAE,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACtE,UAAU,EAAE,MAAM,CAAA;IAClB;oEACgE;IAChE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC,GAAG,KAAK,CAAC,SAAS,CAyBlB"}
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { t } from '../i18n.js';
3
+ import { Box, Text } from '../ui.js';
4
+ import { Pane } from './design-system/Pane.js';
5
+ import { Select } from './Select.js';
6
+ import { HintLine } from './design-system/HintLine.js';
7
+ /**
8
+ * Bare `/workspace` action menu: the built-in subcommands (resume / rename /
9
+ * open) plus any dynamically registered workspace extensions. Enter runs the
10
+ * focused action through the same paths a hand-typed subcommand takes;
11
+ * actions needing free text (rename/open) fall back to their usage notice.
12
+ */
13
+ export function WorkspaceMenuPicker({ options, focusIndex, onPick, }) {
14
+ return (_jsx(Pane, { color: "permission", children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: t('workspace-menu-title') }) }), _jsx(Select, { options: options.map(option => ({
15
+ value: option.id,
16
+ label: option.label,
17
+ description: option.description,
18
+ })), focusIndex: focusIndex, selectedValue: undefined, onPick: onPick === undefined ? undefined : (index) => onPick(index) }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('hint-confirm-exit') }) })] }) }));
19
+ }
@@ -1,9 +1,12 @@
1
1
  import React from 'react';
2
2
  import type { TuiWorkspaceTarget } from '../workspaces.js';
3
3
  /** `/workspace` target picker contributed by local and optional providers. */
4
- export declare function WorkspacePicker({ targets, focusIndex, currentCwd, }: {
4
+ export declare function WorkspacePicker({ targets, focusIndex, currentCwd, onPick, }: {
5
5
  targets: readonly TuiWorkspaceTarget[];
6
6
  focusIndex: number;
7
7
  currentCwd: string;
8
+ /** Mouse pick (fullscreen): reports the clicked row's absolute index —
9
+ * Chat applies it with the same code path as the keyboard Enter. */
10
+ onPick?: (index: number) => void;
8
11
  }): React.ReactNode;
9
12
  //# sourceMappingURL=WorkspacePicker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkspacePicker.d.ts","sourceRoot":"","sources":["../../../src/components/WorkspacePicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAO1D,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EACP,UAAU,EACV,UAAU,GACX,EAAE;IACD,OAAO,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;CACnB,GAAG,KAAK,CAAC,SAAS,CAyBlB"}
1
+ {"version":3,"file":"WorkspacePicker.d.ts","sourceRoot":"","sources":["../../../src/components/WorkspacePicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAO1D,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EACP,UAAU,EACV,UAAU,EACV,MAAM,GACP,EAAE;IACD,OAAO,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB;yEACqE;IACrE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC,GAAG,KAAK,CAAC,SAAS,CA0BlB"}
@@ -6,8 +6,8 @@ import { ListItem } from './design-system/ListItem.js';
6
6
  import { HintLine } from './design-system/HintLine.js';
7
7
  const WINDOW = 8;
8
8
  /** `/workspace` target picker contributed by local and optional providers. */
9
- export function WorkspacePicker({ targets, focusIndex, currentCwd, }) {
9
+ export function WorkspacePicker({ targets, focusIndex, currentCwd, onPick, }) {
10
10
  const start = Math.max(0, Math.min(focusIndex - Math.floor(WINDOW / 2), targets.length - WINDOW));
11
11
  const visible = targets.slice(start, start + WINDOW);
12
- return (_jsxs(Pane, { color: "permission", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: t('workspace-picker-title') }) }), visible.map((target, index) => (_jsxs(ListItem, { isFocused: start + index === focusIndex, isSelected: target.cwd === currentCwd, description: target.description ?? target.uri, showScrollUp: index === 0 && start > 0, showScrollDown: index === visible.length - 1 && start + visible.length < targets.length, children: [target.badge, " \u00B7 ", target.label] }, target.uri)))] }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('workspace-picker-hint') }) })] }));
12
+ return (_jsxs(Pane, { color: "permission", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: t('workspace-picker-title') }) }), visible.map((target, index) => (_jsxs(ListItem, { isFocused: start + index === focusIndex, isSelected: target.cwd === currentCwd, description: target.description ?? target.uri, showScrollUp: index === 0 && start > 0, showScrollDown: index === visible.length - 1 && start + visible.length < targets.length, onClick: onPick === undefined ? undefined : () => onPick(start + index), children: [target.badge, " \u00B7 ", target.label] }, target.uri)))] }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('workspace-picker-hint') }) })] }));
13
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ApprovalPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/approvals/ApprovalPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AAEtE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAA;IACnC,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,cAAc,GAAG,UAAU,KAAK,IAAI,CAAA;CAClE,CAAA;AAID,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,kBAAkB,GAAG,KAAK,CAAC,SAAS,CAmEzF"}
1
+ {"version":3,"file":"ApprovalPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/approvals/ApprovalPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AAEtE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAA;IACnC,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,cAAc,GAAG,UAAU,KAAK,IAAI,CAAA;CAClE,CAAA;AAID,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,kBAAkB,GAAG,KAAK,CAAC,SAAS,CA+EzF"}
@@ -20,6 +20,9 @@ import { POINTER } from '../../cc/figures.js';
20
20
  const OUTCOMES = ['allowed-once', 'rejected'];
21
21
  export function ApprovalPanel({ approval, onDecide }) {
22
22
  const [focusIndex, setFocusIndex] = React.useState(0);
23
+ // Hover highlight per decision row (mouse affordance; the click handler
24
+ // below mirrors the keyboard Enter on the focused row).
25
+ const [hoverIndex, setHoverIndex] = React.useState(-1);
23
26
  useInput((input, key) => {
24
27
  if (key.escape || (key.ctrl && input === 'c')) {
25
28
  onDecide('rejected');
@@ -44,6 +47,7 @@ export function ApprovalPanel({ approval, onDecide }) {
44
47
  const optionLabels = [t('approval-yes'), t('approval-no')];
45
48
  return (_jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, paddingRight: 2, width: "100%", children: [_jsx(Divider, { color: "permission", title: t('approval-waiting', { tool: approval.toolName }), padding: 4 }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [approval.command !== undefined && (_jsx(Box, { flexDirection: "column", paddingX: 2, children: _jsx(Text, { dimColor: true, wrap: "wrap", children: approval.command }) })), approval.reason !== undefined && (_jsx(Text, { dimColor: true, wrap: "wrap", children: approval.reason })), _jsx(Text, { dimColor: true, children: t('approval-proceed') })] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: optionLabels.map((label, index) => {
46
49
  const focused = index === focusIndex;
47
- return (_jsxs(Box, { flexDirection: "row", marginTop: focused ? 1 : 0, children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: focused ? 'claude' : undefined, bold: focused, children: focused ? POINTER : ' ' }) }), _jsxs(Text, { bold: focused, color: focused ? 'claude' : undefined, wrap: "wrap", children: [index + 1, ". ", label] })] }, label));
50
+ const hovered = index === hoverIndex;
51
+ return (_jsxs(Box, { flexDirection: "row", marginTop: focused ? 1 : 0, onClick: () => onDecide(OUTCOMES[index]), onMouseEnter: () => setHoverIndex(index), onMouseLeave: () => setHoverIndex(current => (current === index ? -1 : current)), backgroundColor: hovered && !focused ? 'userMessageBackgroundHover' : undefined, children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: focused ? 'claude' : undefined, bold: focused, children: focused ? POINTER : ' ' }) }), _jsxs(Text, { bold: focused, color: focused ? 'claude' : undefined, wrap: "wrap", children: [index + 1, ". ", label] })] }, label));
48
52
  }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: t('approval-hint') }) })] }));
49
53
  }
@@ -1,4 +1,5 @@
1
1
  import React, { type ReactNode } from 'react';
2
+ import type { ClickEvent } from '../../ink/events/click-event.js';
2
3
  export type ListItemProps = {
3
4
  /** Whether this item is currently focused (keyboard selection).
4
5
  * Shows the pointer indicator (❯) when true. */
@@ -24,6 +25,12 @@ export type ListItemProps = {
24
25
  * @default true
25
26
  */
26
27
  declareCursor?: boolean;
28
+ /**
29
+ * Mouse click handler (fullscreen mode). When provided the row becomes
30
+ * clickable and gains a subtle hover background so the affordance is
31
+ * visible; when absent the row renders exactly as before.
32
+ */
33
+ onClick?: (event: ClickEvent) => void;
27
34
  };
28
35
  /**
29
36
  * A list item for selection UIs, mirroring Claude Code's
@@ -32,5 +39,5 @@ export type ListItemProps = {
32
39
  * and CC's color states (focused = suggestion blue, selected = success
33
40
  * green).
34
41
  */
35
- export declare function ListItem({ isFocused, isSelected, children, description, showScrollDown, showScrollUp, styled, disabled, declareCursor, }: ListItemProps): React.ReactNode;
42
+ export declare function ListItem({ isFocused, isSelected, children, description, showScrollDown, showScrollUp, styled, disabled, declareCursor, onClick, }: ListItemProps): React.ReactNode;
36
43
  //# sourceMappingURL=ListItem.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ListItem.d.ts","sourceRoot":"","sources":["../../../../src/components/design-system/ListItem.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAK7C,MAAM,MAAM,aAAa,GAAG;IAC1B;qDACiD;IACjD,SAAS,EAAE,OAAO,CAAA;IAClB;uDACmD;IACnD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,4CAA4C;IAC5C,QAAQ,EAAE,SAAS,CAAA;IACnB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,qEAAqE;IACrE,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,oEAAoE;IACpE,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,EACvB,SAAS,EACT,UAAkB,EAClB,QAAQ,EACR,WAAW,EACX,cAAc,EACd,YAAY,EACZ,MAAa,EACb,QAAgB,EAChB,aAAa,GACd,EAAE,aAAa,GAAG,KAAK,CAAC,SAAS,CAwEjC"}
1
+ {"version":3,"file":"ListItem.d.ts","sourceRoot":"","sources":["../../../../src/components/design-system/ListItem.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAGvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAGjE,MAAM,MAAM,aAAa,GAAG;IAC1B;qDACiD;IACjD,SAAS,EAAE,OAAO,CAAA;IAClB;uDACmD;IACnD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,4CAA4C;IAC5C,QAAQ,EAAE,SAAS,CAAA;IACnB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,qEAAqE;IACrE,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,oEAAoE;IACpE,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAA;CACtC,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,EACvB,SAAS,EACT,UAAkB,EAClB,QAAQ,EACR,WAAW,EACX,cAAc,EACd,YAAY,EACZ,MAAa,EACb,QAAgB,EAChB,aAAa,EACb,OAAO,GACR,EAAE,aAAa,GAAG,KAAK,CAAC,SAAS,CAoFjC"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React from 'react';
2
+ import React, { useState } from 'react';
3
3
  import { Box, Text } from '../../ui.js';
4
4
  import { useDeclaredCursor } from '../../ink/hooks/use-declared-cursor.js';
5
5
  import { POINTER, DOWN_ARROW, UP_ARROW, TICK } from '../../cc/figures.js';
@@ -10,7 +10,7 @@ import { POINTER, DOWN_ARROW, UP_ARROW, TICK } from '../../cc/figures.js';
10
10
  * and CC's color states (focused = suggestion blue, selected = success
11
11
  * green).
12
12
  */
13
- export function ListItem({ isFocused, isSelected = false, children, description, showScrollDown, showScrollUp, styled = true, disabled = false, declareCursor, }) {
13
+ export function ListItem({ isFocused, isSelected = false, children, description, showScrollDown, showScrollUp, styled = true, disabled = false, declareCursor, onClick, }) {
14
14
  // Park the native terminal cursor on the pointer indicator so screen
15
15
  // readers / magnifiers track the focused item (CC behavior). (0,0) is the
16
16
  // top-left of this Box, where the pointer renders.
@@ -19,6 +19,11 @@ export function ListItem({ isFocused, isSelected = false, children, description,
19
19
  column: 0,
20
20
  active: isFocused && !disabled && declareCursor !== false,
21
21
  });
22
+ // Hover highlight only when the row is actually clickable — the extra
23
+ // background is the mouse affordance (there is no cursor-shape feedback
24
+ // in a terminal).
25
+ const [hovered, setHovered] = useState(false);
26
+ const clickable = Boolean(onClick) && !disabled;
22
27
  function renderIndicator() {
23
28
  if (disabled) {
24
29
  return _jsx(Text, { children: " " });
@@ -54,7 +59,7 @@ export function ListItem({ isFocused, isSelected = false, children, description,
54
59
  // 非 Fragment 的 element(如调用方自己的 <Text>)保留原样——其子树高度
55
60
  // 由调用方负责。
56
61
  const flatChildren = flattenDeep(children);
57
- return (_jsxs(Box, { ref: cursorRef, flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", gap: 1, height: 1, flexShrink: 0, overflow: "hidden", width: "100%", children: [renderIndicator(), styled ? (_jsx(Text, { color: getTextColor(), dimColor: disabled, wrap: "truncate-end", children: flatChildren })) : (flatChildren), isSelected && !disabled && _jsx(Text, { color: "success", children: TICK })] }), description && (_jsx(Box, { paddingLeft: 2, children: _jsx(Text, { color: "inactive", wrap: "truncate", children: flattenLine(description) }) }))] }));
62
+ return (_jsxs(Box, { ref: cursorRef, flexDirection: "column", onClick: clickable ? onClick : undefined, onMouseEnter: clickable ? () => setHovered(true) : undefined, onMouseLeave: clickable ? () => setHovered(false) : undefined, backgroundColor: clickable && hovered ? 'userMessageBackgroundHover' : undefined, children: [_jsxs(Box, { flexDirection: "row", gap: 1, height: 1, flexShrink: 0, overflow: "hidden", width: "100%", children: [renderIndicator(), styled ? (_jsx(Text, { color: getTextColor(), dimColor: disabled, wrap: "truncate-end", children: flatChildren })) : (flatChildren), isSelected && !disabled && _jsx(Text, { color: "success", children: TICK })] }), description && (_jsx(Box, { paddingLeft: 2, children: _jsx(Text, { color: "inactive", wrap: "truncate", children: flattenLine(description) }) }))] }));
58
63
  }
59
64
  /** 单行化:内嵌换行折叠为空格(行尾/行首换行随之消除)。 */
60
65
  function flattenLine(s) {
@@ -7,11 +7,14 @@ type Props = {
7
7
  isSelected?: boolean;
8
8
  /** Row expanded on its own (persistent hover-grey background, CC). */
9
9
  isExpanded?: boolean;
10
- onClick?(): void;
11
10
  };
12
11
  /**
13
12
  * Assistant text message: bullet + markdown body (mirroring Claude Code's default branch).
13
+ *
14
+ * Deliberately not clickable: the transcript is reading material and the
15
+ * mouse's job there is text selection (user feedback — row hover tints and
16
+ * fold toggling were noise, not affordance).
14
17
  */
15
- export declare function AssistantTextMessage({ text, addMargin, isSelected, isExpanded, onClick, }: Props): React.ReactNode;
18
+ export declare function AssistantTextMessage({ text, addMargin, isSelected, isExpanded, }: Props): React.ReactNode;
16
19
  export {};
17
20
  //# sourceMappingURL=AssistantTextMessage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantTextMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantTextMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,4DAA4D;IAC5D,SAAS,EAAE,OAAO,CAAA;IAClB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,sEAAsE;IACtE,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,IAAI,IAAI,CAAA;CACjB,CAAA;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,EACJ,SAAS,EACT,UAAkB,EAClB,UAAkB,EAClB,OAAO,GACR,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CA2BzB"}
1
+ {"version":3,"file":"AssistantTextMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantTextMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,4DAA4D;IAC5D,SAAS,EAAE,OAAO,CAAA;IAClB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,sEAAsE;IACtE,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,EACJ,SAAS,EACT,UAAkB,EAClB,UAAkB,GACnB,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CA0BzB"}
@@ -4,11 +4,15 @@ import { BLACK_CIRCLE } from '../../cc/figures.js';
4
4
  import { Markdown } from '../Markdown.js';
5
5
  /**
6
6
  * Assistant text message: bullet + markdown body (mirroring Claude Code's default branch).
7
+ *
8
+ * Deliberately not clickable: the transcript is reading material and the
9
+ * mouse's job there is text selection (user feedback — row hover tints and
10
+ * fold toggling were noise, not affordance).
7
11
  */
8
- export function AssistantTextMessage({ text, addMargin, isSelected = false, isExpanded = false, onClick, }) {
12
+ export function AssistantTextMessage({ text, addMargin, isSelected = false, isExpanded = false, }) {
9
13
  return (_jsx(Box, { alignItems: "flex-start", flexDirection: "row", justifyContent: "space-between", marginTop: addMargin ? 1 : 0, width: "100%", backgroundColor: isSelected
10
14
  ? 'messageActionsBackground'
11
15
  : isExpanded
12
16
  ? 'userMessageBackgroundHover'
13
- : undefined, onClick: onClick, children: _jsxs(Box, { flexDirection: "row", children: [_jsx(NoSelect, { fromLeftEdge: true, minWidth: 2, children: _jsx(Text, { color: isSelected ? 'suggestion' : 'text', children: BLACK_CIRCLE }) }), _jsx(Box, { flexDirection: "column", children: _jsx(Markdown, { children: text }) })] }) }));
17
+ : undefined, children: _jsxs(Box, { flexDirection: "row", children: [_jsx(NoSelect, { fromLeftEdge: true, minWidth: 2, children: _jsx(Text, { color: isSelected ? 'suggestion' : 'text', children: BLACK_CIRCLE }) }), _jsx(Box, { flexDirection: "column", children: _jsx(Markdown, { children: text }) })] }) }));
14
18
  }
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import type { ClickEvent } from '../../ink/events/click-event.js';
2
3
  type Props = {
3
4
  thinking: string;
4
5
  /** Adds the top margin between messages (CC: addMargin). */
@@ -17,7 +18,7 @@ type Props = {
17
18
  durationMs?: number;
18
19
  /** Message-selection mode highlight. */
19
20
  isSelected?: boolean;
20
- onClick?(): void;
21
+ onClick?(event: ClickEvent): void;
21
22
  };
22
23
  /**
23
24
  * Thinking block: folded `⚓ Thinking (ctrl+o to expand)`, expanded shows the
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantThinkingMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantThinkingMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAuBzB,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,4DAA4D;IAC5D,SAAS,EAAE,OAAO,CAAA;IAClB,2EAA2E;IAC3E,OAAO,EAAE,OAAO,CAAA;IAChB;;4CAEwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;8EAE0E;IAC1E,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,IAAI,IAAI,CAAA;CACjB,CAAA;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,SAAS,EACT,OAAO,EACP,SAAiB,EACjB,OAAe,EACf,UAAU,EACV,UAAkB,EAClB,OAAO,GACR,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CAwHzB"}
1
+ {"version":3,"file":"AssistantThinkingMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantThinkingMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAczB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAUjE,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,4DAA4D;IAC5D,SAAS,EAAE,OAAO,CAAA;IAClB,2EAA2E;IAC3E,OAAO,EAAE,OAAO,CAAA;IAChB;;4CAEwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;8EAE0E;IAC1E,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;CAClC,CAAA;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,SAAS,EACT,OAAO,EACP,SAAiB,EACjB,OAAe,EACf,UAAU,EACV,UAAkB,EAClB,OAAO,GACR,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CAkIzB"}
@@ -49,7 +49,13 @@ export function AssistantThinkingMessage({ thinking, addMargin, verbose, streami
49
49
  const pulse = (Math.sin(frame * 0.9) + 1) / 2;
50
50
  const pulseColor = interpolateColor(BRAND, ICE, pulse);
51
51
  const frameText = THINKING_SPINNER_FRAMES[frame % THINKING_SPINNER_FRAMES.length];
52
- const header = streaming ? (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { children: minimal ? frameText : chalk.rgb(pulseColor.r, pulseColor.g, pulseColor.b).bold(frameText) }), _jsx(Text, { dimColor: true, italic: true, children: ` ${label}` })] })) : (_jsx(Text, { dimColor: true, italic: true, children: `${minimal ? '*' : THINKING_SETTLED_MARKER} ${label}` }));
52
+ // Hover 轻指示:可点击折叠时折叠头从 dim 提亮为正常色(不刷整行背景,
53
+ // 转录视觉保持安静)。
54
+ const [hovered, setHovered] = React.useState(false);
55
+ const hoverProps = onClick !== undefined
56
+ ? { onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false) }
57
+ : {};
58
+ const header = streaming ? (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { children: minimal ? frameText : chalk.rgb(pulseColor.r, pulseColor.g, pulseColor.b).bold(frameText) }), _jsx(Text, { dimColor: !hovered, color: hovered ? 'text' : undefined, italic: true, children: ` ${label}` })] })) : (_jsx(Text, { italic: true, dimColor: !hovered, color: hovered ? 'text' : undefined, children: `${minimal ? '*' : THINKING_SETTLED_MARKER} ${label}` }));
53
59
  if (preview) {
54
60
  // Live ticker: the model's last few reasoning lines, dimmed, one Text
55
61
  // per row so each truncates to the width independently, padded to a
@@ -64,10 +70,10 @@ export function AssistantThinkingMessage({ thinking, addMargin, verbose, streami
64
70
  // Pad with single spaces — an empty-string Text renders with zero
65
71
  // height in ink, so '' padding would not hold the row open.
66
72
  const rows = Array.from({ length: PREVIEW_ROWS }, (_, i) => visible[i] ?? ' ');
67
- return (_jsxs(Box, { flexDirection: "column", marginTop: addMargin ? 1 : 0, backgroundColor: isSelected ? 'messageActionsBackground' : undefined, onClick: onClick, children: [header, _jsx(Box, { flexDirection: "column", paddingLeft: 2, height: PREVIEW_ROWS, flexShrink: 0, overflow: "hidden", children: rows.map((line, i) => (_jsxs(Box, { flexDirection: "row", height: 1, children: [_jsx(Text, { dimColor: true, italic: true, children: '│ ' }), _jsx(Box, { flexDirection: "row", flexGrow: 1, children: _jsx(Text, { dimColor: true, italic: true, wrap: i === rows.length - 1 ? 'truncate-start' : 'truncate', children: i === 0 && clipped ? `…${line}` : line }) })] }, i))) })] }));
73
+ return (_jsxs(Box, { flexDirection: "column", marginTop: addMargin ? 1 : 0, backgroundColor: isSelected ? 'messageActionsBackground' : undefined, onClick: onClick, ...hoverProps, children: [header, _jsx(Box, { flexDirection: "column", paddingLeft: 2, height: PREVIEW_ROWS, flexShrink: 0, overflow: "hidden", children: rows.map((line, i) => (_jsxs(Box, { flexDirection: "row", height: 1, children: [_jsx(Text, { dimColor: true, italic: true, children: '│ ' }), _jsx(Box, { flexDirection: "row", flexGrow: 1, children: _jsx(Text, { dimColor: true, italic: true, wrap: i === rows.length - 1 ? 'truncate-start' : 'truncate', children: i === 0 && clipped ? `…${line}` : line }) })] }, i))) })] }));
68
74
  }
69
75
  if (!verbose) {
70
- return (_jsx(Box, { marginTop: addMargin ? 1 : 0, backgroundColor: isSelected ? 'messageActionsBackground' : undefined, onClick: onClick, children: header }));
76
+ return (_jsx(Box, { marginTop: addMargin ? 1 : 0, backgroundColor: isSelected ? 'messageActionsBackground' : undefined, onClick: onClick, ...hoverProps, children: header }));
71
77
  }
72
- return (_jsxs(Box, { flexDirection: "column", gap: 1, marginTop: addMargin ? 1 : 0, width: "100%", backgroundColor: isSelected ? 'messageActionsBackground' : undefined, onClick: onClick, children: [header, _jsx(Box, { paddingLeft: 2, children: _jsx(StreamingMarkdown, { dimColor: true, children: thinking }) })] }));
78
+ return (_jsxs(Box, { flexDirection: "column", gap: 1, marginTop: addMargin ? 1 : 0, width: "100%", backgroundColor: isSelected ? 'messageActionsBackground' : undefined, onClick: onClick, ...hoverProps, children: [header, _jsx(Box, { paddingLeft: 2, children: _jsx(StreamingMarkdown, { dimColor: true, children: thinking }) })] }));
73
79
  }
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import type { ToolRow } from '../../dsh-adapter/channel.js';
3
3
  import type { ToolBackground } from '../../tuiDisplayPrefs.js';
4
4
  import type { Theme } from '../../theme.js';
5
+ import type { ClickEvent } from '../../ink/events/click-event.js';
5
6
  type Props = {
6
7
  tool: ToolRow;
7
8
  /** Adds the top margin between messages (CC: addMargin). */
@@ -12,6 +13,12 @@ type Props = {
12
13
  isSelected?: boolean;
13
14
  /** Row expanded on its own (persistent hover-grey background, CC). */
14
15
  isExpanded?: boolean;
16
+ /**
17
+ * Mouse click (fullscreen): toggles the row's expansion — same action as
18
+ * clicking other transcript rows. Also makes the `(ctrl+o to expand)`
19
+ * hint actionable with the mouse.
20
+ */
21
+ onClick?(event: ClickEvent): void;
15
22
  /**
16
23
  * Trajectory pointer, rendered as one more `⎿` line under a failed call.
17
24
  *
@@ -33,6 +40,6 @@ export declare function toolNameColor(raw: string): keyof Theme;
33
40
  * output, read content — instead of the raw result dump (mirroring Claude Code's `AssistantToolUseMessage.tsx` + the dsh-tools presentation views the
34
41
  * channel captures per call).
35
42
  */
36
- export declare function AssistantToolUseMessage({ tool, addMargin, verbose, isSelected, isExpanded, footnote, diffLayout, toolBackground, }: Props): React.ReactNode;
43
+ export declare function AssistantToolUseMessage({ tool, addMargin, verbose, isSelected, isExpanded, onClick, footnote, diffLayout, toolBackground, }: Props): React.ReactNode;
37
44
  export {};
38
45
  //# sourceMappingURL=AssistantToolUseMessage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantToolUseMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantToolUseMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,KAAK,EAA8C,OAAO,EAAE,MAAM,8BAA8B,CAAA;AAKvG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAE3C,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAA;IACb,4DAA4D;IAC5D,SAAS,EAAE,OAAO,CAAA;IAClB,iEAAiE;IACjE,OAAO,EAAE,OAAO,CAAA;IAChB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,sEAAsE;IACtE,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IACzC,2EAA2E;IAC3E,cAAc,CAAC,EAAE,cAAc,CAAA;CAChC,CAAA;AAgFD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,KAAK,CAKtD;AAwKD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,IAAI,EACJ,SAAS,EACT,OAAO,EACP,UAAkB,EAClB,UAAkB,EAClB,QAAQ,EACR,UAAmB,EACnB,cAAuB,GACxB,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CAmKzB"}
1
+ {"version":3,"file":"AssistantToolUseMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantToolUseMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,KAAK,EAA8C,OAAO,EAAE,MAAM,8BAA8B,CAAA;AAKvG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAEjE,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAA;IACb,4DAA4D;IAC5D,SAAS,EAAE,OAAO,CAAA;IAClB,iEAAiE;IACjE,OAAO,EAAE,OAAO,CAAA;IAChB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,sEAAsE;IACtE,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;OAIG;IACH,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IACjC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IACzC,2EAA2E;IAC3E,cAAc,CAAC,EAAE,cAAc,CAAA;CAChC,CAAA;AAgFD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,KAAK,CAKtD;AAwKD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,IAAI,EACJ,SAAS,EACT,OAAO,EACP,UAAkB,EAClB,UAAkB,EAClB,OAAO,EACP,QAAQ,EACR,UAAmB,EACnB,cAAuB,GACxB,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CAsKzB"}
@@ -221,7 +221,7 @@ function HeaderTitle({ name, title, isTerminal, displayArgs, argsLanguage, nameC
221
221
  * output, read content — instead of the raw result dump (mirroring Claude Code's `AssistantToolUseMessage.tsx` + the dsh-tools presentation views the
222
222
  * channel captures per call).
223
223
  */
224
- export function AssistantToolUseMessage({ tool, addMargin, verbose, isSelected = false, isExpanded = false, footnote, diffLayout = 'auto', toolBackground = 'none', }) {
224
+ export function AssistantToolUseMessage({ tool, addMargin, verbose, isSelected = false, isExpanded = false, onClick, footnote, diffLayout = 'auto', toolBackground = 'none', }) {
225
225
  const isRunning = tool.status === 'running';
226
226
  const isError = tool.status === 'error';
227
227
  const displayArgs = verbose ? tool.argsFull ?? tool.argsText : tool.argsText;
@@ -286,9 +286,11 @@ export function AssistantToolUseMessage({ tool, addMargin, verbose, isSelected =
286
286
  : ordinaryToolBackground === 'strong'
287
287
  ? 'toolCardBackground'
288
288
  : undefined;
289
- return (_jsx(Box, { ref: viewportRef, flexDirection: "row", justifyContent: "space-between", marginTop: addMargin ? 1 : 0, width: "100%",
289
+ return (_jsx(Box, { ref: viewportRef, flexDirection: "row", justifyContent: "space-between", marginTop: addMargin ? 1 : 0, width: "100%", onClick: onClick,
290
290
  // Only selection paints a highlight; the configured treatment applies
291
291
  // to an ordinary card. Diff line tints stay - they are content, not chrome.
292
+ // No hover tint: the card stays visually quiet until clicked (user
293
+ // feedback — row-hover color changes read as noise in the transcript).
292
294
  backgroundColor: isSelected ? 'messageActionsBackground' : ordinaryBackground, children: _jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { flexDirection: "row", flexWrap: "nowrap", minWidth: minWidth, children: [_jsx(ToolUseLoader, { shouldAnimate: isRunning, isUnresolved: isRunning, isError: isError, toolName: tool.name }), _jsx(HeaderTitle, { name: name, title: headerTitle, isTerminal: headerIsTerminal, displayArgs: displayArgs, argsLanguage: argsLanguage, nameColor: toolNameColor(tool.name) }), !isRunning && (_jsx(Box, { flexWrap: "nowrap", children: _jsx(Text, { dimColor: true, children: elapsedText }) }))] }), useSplitDiff && view?.card === 'diff' ? (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 3, flexShrink: 0, children: _jsx(Text, { dimColor: true, children: GUTTER_FIRST }) }), _jsx(SplitDiffView, { diffs: view.diffs, width: columns - 4, maxRows: DIFF_BODY_MAX_LINES, verbose: verbose, toolBackground: ordinaryToolBackground })] })) : (rendered.map((line, index) => (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 3, flexShrink: 0, children: _jsx(Text, { color: line.tone === 'add'
293
295
  ? 'diffAddedWord'
294
296
  : line.tone === 'del'
@@ -1,11 +1,12 @@
1
1
  import React from 'react';
2
+ import type { ClickEvent } from '../../ink/events/click-event.js';
2
3
  type Props = {
3
4
  text: string;
4
5
  /** Adds the top margin between turns (CC: addMargin). */
5
6
  addMargin: boolean;
6
7
  /** Message-selection mode highlight. */
7
8
  isSelected?: boolean;
8
- onClick?(): void;
9
+ onClick?(event: ClickEvent): void;
9
10
  };
10
11
  /**
11
12
  * User prompt bubble: `❯ text` in bold briefLabelYou gold with no background
@@ -1 +1 @@
1
- {"version":3,"file":"UserPromptMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/UserPromptMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,SAAS,EAAE,OAAO,CAAA;IAClB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,IAAI,IAAI,CAAA;CACjB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,SAAS,EACT,UAAkB,EAClB,OAAO,GACR,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CA6BzB"}
1
+ {"version":3,"file":"UserPromptMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/UserPromptMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAEjE,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,SAAS,EAAE,OAAO,CAAA;IAClB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;CAClC,CAAA;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,SAAS,EACT,UAAkB,EAClB,OAAO,GACR,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CA6BzB"}