@deepseek-harness-tui/dsh-tui 0.8.8 → 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 (215) hide show
  1. package/README.md +27 -8
  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 +24 -7
  21. package/lib/types/components/FileSuggestions.d.ts.map +1 -1
  22. package/lib/types/components/FileSuggestions.js +39 -12
  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/Markdown.d.ts.map +1 -1
  38. package/lib/types/components/Markdown.js +12 -3
  39. package/lib/types/components/MessageList.d.ts +43 -2
  40. package/lib/types/components/MessageList.d.ts.map +1 -1
  41. package/lib/types/components/MessageList.js +553 -41
  42. package/lib/types/components/ModelPicker.d.ts +4 -1
  43. package/lib/types/components/ModelPicker.d.ts.map +1 -1
  44. package/lib/types/components/ModelPicker.js +2 -2
  45. package/lib/types/components/PermissionsPicker.d.ts +21 -0
  46. package/lib/types/components/PermissionsPicker.d.ts.map +1 -0
  47. package/lib/types/components/PermissionsPicker.js +27 -0
  48. package/lib/types/components/PlanPicker.d.ts +15 -0
  49. package/lib/types/components/PlanPicker.d.ts.map +1 -0
  50. package/lib/types/components/PlanPicker.js +18 -0
  51. package/lib/types/components/PresetPicker.d.ts +4 -1
  52. package/lib/types/components/PresetPicker.d.ts.map +1 -1
  53. package/lib/types/components/PresetPicker.js +2 -2
  54. package/lib/types/components/PromptInput.d.ts +4 -3
  55. package/lib/types/components/PromptInput.d.ts.map +1 -1
  56. package/lib/types/components/PromptInput.js +248 -29
  57. package/lib/types/components/RewindPicker.d.ts +12 -1
  58. package/lib/types/components/RewindPicker.d.ts.map +1 -1
  59. package/lib/types/components/RewindPicker.js +6 -4
  60. package/lib/types/components/ScrollbarGutter.d.ts +28 -0
  61. package/lib/types/components/ScrollbarGutter.d.ts.map +1 -0
  62. package/lib/types/components/ScrollbarGutter.js +68 -0
  63. package/lib/types/components/Select.d.ts +8 -1
  64. package/lib/types/components/Select.d.ts.map +1 -1
  65. package/lib/types/components/Select.js +4 -2
  66. package/lib/types/components/SkillsPicker.d.ts +4 -1
  67. package/lib/types/components/SkillsPicker.d.ts.map +1 -1
  68. package/lib/types/components/SkillsPicker.js +2 -2
  69. package/lib/types/components/SubagentCard.d.ts +2 -1
  70. package/lib/types/components/SubagentCard.d.ts.map +1 -1
  71. package/lib/types/components/SubagentCard.js +6 -1
  72. package/lib/types/components/SubagentDashboard.d.ts +4 -0
  73. package/lib/types/components/SubagentDashboard.d.ts.map +1 -1
  74. package/lib/types/components/SubagentDashboard.js +9 -1
  75. package/lib/types/components/SubagentDetailScene.d.ts.map +1 -1
  76. package/lib/types/components/SubagentDetailScene.js +5 -4
  77. package/lib/types/components/SuggestionCard.d.ts +63 -0
  78. package/lib/types/components/SuggestionCard.d.ts.map +1 -0
  79. package/lib/types/components/SuggestionCard.js +77 -0
  80. package/lib/types/components/ThemePicker.d.ts +4 -1
  81. package/lib/types/components/ThemePicker.d.ts.map +1 -1
  82. package/lib/types/components/ThemePicker.js +2 -2
  83. package/lib/types/components/ThinkingToggle.d.ts +4 -1
  84. package/lib/types/components/ThinkingToggle.d.ts.map +1 -1
  85. package/lib/types/components/ThinkingToggle.js +2 -2
  86. package/lib/types/components/TimelineRail.d.ts +53 -0
  87. package/lib/types/components/TimelineRail.d.ts.map +1 -0
  88. package/lib/types/components/TimelineRail.js +190 -0
  89. package/lib/types/components/WorkspaceFlowPicker.d.ts +6 -1
  90. package/lib/types/components/WorkspaceFlowPicker.d.ts.map +1 -1
  91. package/lib/types/components/WorkspaceFlowPicker.js +4 -2
  92. package/lib/types/components/WorkspaceMenuPicker.d.ts +19 -0
  93. package/lib/types/components/WorkspaceMenuPicker.d.ts.map +1 -0
  94. package/lib/types/components/WorkspaceMenuPicker.js +19 -0
  95. package/lib/types/components/WorkspacePicker.d.ts +4 -1
  96. package/lib/types/components/WorkspacePicker.d.ts.map +1 -1
  97. package/lib/types/components/WorkspacePicker.js +2 -2
  98. package/lib/types/components/approvals/ApprovalPanel.d.ts.map +1 -1
  99. package/lib/types/components/approvals/ApprovalPanel.js +5 -1
  100. package/lib/types/components/design-system/ListItem.d.ts +8 -1
  101. package/lib/types/components/design-system/ListItem.d.ts.map +1 -1
  102. package/lib/types/components/design-system/ListItem.js +8 -3
  103. package/lib/types/components/messages/AssistantTextMessage.d.ts +5 -2
  104. package/lib/types/components/messages/AssistantTextMessage.d.ts.map +1 -1
  105. package/lib/types/components/messages/AssistantTextMessage.js +6 -2
  106. package/lib/types/components/messages/AssistantThinkingMessage.d.ts +2 -1
  107. package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
  108. package/lib/types/components/messages/AssistantThinkingMessage.js +10 -4
  109. package/lib/types/components/messages/AssistantToolUseMessage.d.ts +8 -1
  110. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  111. package/lib/types/components/messages/AssistantToolUseMessage.js +4 -2
  112. package/lib/types/components/messages/UserPromptMessage.d.ts +2 -1
  113. package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
  114. package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
  115. package/lib/types/components/questions/AskUserQuestionPanel.js +34 -2
  116. package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -1
  117. package/lib/types/components/questions/PlanReviewPanel.js +13 -2
  118. package/lib/types/components/sessions/SessionListRow.d.ts +4 -1
  119. package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
  120. package/lib/types/components/sessions/SessionListRow.js +4 -2
  121. package/lib/types/components/trajectory/HotspotView.d.ts +7 -1
  122. package/lib/types/components/trajectory/HotspotView.d.ts.map +1 -1
  123. package/lib/types/components/trajectory/HotspotView.js +8 -2
  124. package/lib/types/components/trajectory/Ledger.d.ts +9 -1
  125. package/lib/types/components/trajectory/Ledger.d.ts.map +1 -1
  126. package/lib/types/components/trajectory/Ledger.js +23 -5
  127. package/lib/types/components/trajectory/WaveBand.d.ts +10 -1
  128. package/lib/types/components/trajectory/WaveBand.d.ts.map +1 -1
  129. package/lib/types/components/trajectory/WaveBand.js +7 -2
  130. package/lib/types/dsh-adapter/channel.d.ts +16 -5
  131. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  132. package/lib/types/dsh-adapter/channel.js +332 -29
  133. package/lib/types/dsh-adapter/index.d.ts +10 -2
  134. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  135. package/lib/types/dsh-adapter/index.js +4 -1
  136. package/lib/types/dsh-adapter/plugin.d.ts +0 -9
  137. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  138. package/lib/types/dsh-adapter/plugin.js +136 -6
  139. package/lib/types/i18n.d.ts +176 -15
  140. package/lib/types/i18n.d.ts.map +1 -1
  141. package/lib/types/i18n.js +51 -8
  142. package/lib/types/ink/components/AlternateScreen.d.ts.map +1 -1
  143. package/lib/types/ink/components/AlternateScreen.js +13 -1
  144. package/lib/types/ink/components/App.d.ts +12 -1
  145. package/lib/types/ink/components/App.d.ts.map +1 -1
  146. package/lib/types/ink/components/App.js +101 -4
  147. package/lib/types/ink/components/ClockContext.d.ts.map +1 -1
  148. package/lib/types/ink/components/ClockContext.js +2 -0
  149. package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
  150. package/lib/types/ink/components/ScrollBox.js +170 -91
  151. package/lib/types/ink/constants.d.ts +9 -0
  152. package/lib/types/ink/constants.d.ts.map +1 -1
  153. package/lib/types/ink/constants.js +9 -0
  154. package/lib/types/ink/events/click-event.d.ts +7 -16
  155. package/lib/types/ink/events/click-event.d.ts.map +1 -1
  156. package/lib/types/ink/events/click-event.js +16 -19
  157. package/lib/types/ink/events/event-handlers.d.ts +11 -1
  158. package/lib/types/ink/events/event-handlers.d.ts.map +1 -1
  159. package/lib/types/ink/events/event-handlers.js +2 -0
  160. package/lib/types/ink/events/input-event.d.ts +9 -0
  161. package/lib/types/ink/events/input-event.d.ts.map +1 -1
  162. package/lib/types/ink/events/input-event.js +4 -0
  163. package/lib/types/ink/events/pointer-event.d.ts +61 -0
  164. package/lib/types/ink/events/pointer-event.d.ts.map +1 -0
  165. package/lib/types/ink/events/pointer-event.js +61 -0
  166. package/lib/types/ink/events/wheel-event.d.ts +17 -0
  167. package/lib/types/ink/events/wheel-event.d.ts.map +1 -0
  168. package/lib/types/ink/events/wheel-event.js +20 -0
  169. package/lib/types/ink/geometry-trace.d.ts +51 -0
  170. package/lib/types/ink/geometry-trace.d.ts.map +1 -0
  171. package/lib/types/ink/geometry-trace.js +83 -0
  172. package/lib/types/ink/hit-test.d.ts +56 -1
  173. package/lib/types/ink/hit-test.d.ts.map +1 -1
  174. package/lib/types/ink/hit-test.js +146 -8
  175. package/lib/types/ink/ink.d.ts +58 -1
  176. package/lib/types/ink/ink.d.ts.map +1 -1
  177. package/lib/types/ink/ink.js +230 -29
  178. package/lib/types/ink/node-cache.d.ts +5 -0
  179. package/lib/types/ink/node-cache.d.ts.map +1 -1
  180. package/lib/types/ink/parse-keypress.d.ts +8 -0
  181. package/lib/types/ink/parse-keypress.d.ts.map +1 -1
  182. package/lib/types/ink/parse-keypress.js +101 -12
  183. package/lib/types/ink/render-node-to-output.d.ts +10 -0
  184. package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
  185. package/lib/types/ink/render-node-to-output.js +111 -18
  186. package/lib/types/ink/timeline-rail.d.ts +138 -0
  187. package/lib/types/ink/timeline-rail.d.ts.map +1 -0
  188. package/lib/types/ink/timeline-rail.js +179 -0
  189. package/lib/types/ink/wrap-text.d.ts.map +1 -1
  190. package/lib/types/ink/wrap-text.js +47 -5
  191. package/lib/types/screens/Chat.d.ts.map +1 -1
  192. package/lib/types/screens/Chat.js +819 -337
  193. package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
  194. package/lib/types/screens/SessionBrowser.js +56 -29
  195. package/lib/types/screens/Settings.d.ts.map +1 -1
  196. package/lib/types/screens/Settings.js +64 -22
  197. package/lib/types/screens/StatusLine.d.ts +2 -1
  198. package/lib/types/screens/StatusLine.d.ts.map +1 -1
  199. package/lib/types/screens/StatusLine.js +17 -1
  200. package/lib/types/screens/TrajectoryScene.d.ts.map +1 -1
  201. package/lib/types/screens/TrajectoryScene.js +81 -14
  202. package/lib/types/screens/chatOverlay.d.ts +221 -0
  203. package/lib/types/screens/chatOverlay.d.ts.map +1 -0
  204. package/lib/types/screens/chatOverlay.js +114 -0
  205. package/lib/types/tips.d.ts.map +1 -1
  206. package/lib/types/tips.js +42 -6
  207. package/lib/types/tuiDisplayPrefs.d.ts +15 -0
  208. package/lib/types/tuiDisplayPrefs.d.ts.map +1 -1
  209. package/lib/types/tuiDisplayPrefs.js +9 -0
  210. package/lib/types/utils/debug.d.ts +10 -0
  211. package/lib/types/utils/debug.d.ts.map +1 -1
  212. package/lib/types/utils/debug.js +24 -0
  213. package/lib/types/utils/paths.d.ts.map +1 -1
  214. package/lib/types/utils/paths.js +1 -0
  215. package/package.json +4 -2
@@ -1,5 +1,8 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
2
3
  import { Box, Text } from '../ui.js';
4
+ import { t } from '../i18n.js';
5
+ import { modLabel } from '../utils/modifiers.js';
3
6
  /** Maximum todo rows shown before the overflow line. */
4
7
  const MAX_TODOS = 8;
5
8
  const PHASE_LABEL = {
@@ -8,15 +11,40 @@ const PHASE_LABEL = {
8
11
  blocked: '⛔ blocked',
9
12
  complete: '✓ complete',
10
13
  };
11
- function PhaseBadge({ phase, roundsStarted, maxGoalRounds, }) {
12
- const color = phase === 'active'
13
- ? 'success'
14
- : phase === 'paused'
15
- ? 'warning'
16
- : phase === 'blocked'
17
- ? 'error'
18
- : undefined;
19
- return (_jsxs(Text, { color: color, dimColor: phase === 'complete', children: [PHASE_LABEL[phase], " \u00B7 ", roundsStarted, "/", maxGoalRounds] }));
14
+ /** Compact phase marker for the status-footer chip. */
15
+ const PHASE_GLYPH = {
16
+ active: '●',
17
+ paused: '⏸',
18
+ blocked: '⛔',
19
+ complete: '✓',
20
+ };
21
+ function phaseColor(phase) {
22
+ if (phase === 'active')
23
+ return 'success';
24
+ if (phase === 'paused')
25
+ return 'warning';
26
+ if (phase === 'blocked')
27
+ return 'error';
28
+ return undefined;
29
+ }
30
+ /** `47s` under a minute, `3m12s` after — same shape as the subagent cards. */
31
+ function formatDuration(ms) {
32
+ const seconds = Math.max(0, Math.floor(ms / 1000));
33
+ return seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m${seconds % 60}s`;
34
+ }
35
+ /**
36
+ * Compact goal chip for the status footer: phase glyph + rounds, colored by
37
+ * phase. `minimal` swaps the glyph for a text form, per the minimal-mode
38
+ * no-emoji contract.
39
+ */
40
+ export function GoalStatusChip({ goal, minimal = false }) {
41
+ return (_jsx(Text, { color: phaseColor(goal.phase), dimColor: goal.phase === 'complete', children: minimal
42
+ ? `goal ${goal.roundsStarted}/${goal.maxGoalRounds}`
43
+ : `${PHASE_GLYPH[goal.phase]} ${goal.roundsStarted}/${goal.maxGoalRounds}` }));
44
+ }
45
+ function PhaseBadge({ phase, roundsStarted, maxGoalRounds, elapsed, }) {
46
+ const color = phaseColor(phase);
47
+ return (_jsxs(Text, { color: color, dimColor: phase === 'complete', children: [PHASE_LABEL[phase], " \u00B7 ", roundsStarted, "/", maxGoalRounds, elapsed !== undefined ? ` · ${elapsed}` : ''] }));
20
48
  }
21
49
  function TodoGlyph({ status }) {
22
50
  switch (status) {
@@ -42,21 +70,62 @@ function BranchPrefix({ last }) {
42
70
  * `channel.todos` from `todo/write` whole-list snapshots, so every model
43
71
  * update re-renders this panel in real time (no polling). Renders nothing
44
72
  * while both slots are empty.
73
+ *
74
+ * The todo section folds two ways: completed rows fold automatically once
75
+ * the agent goes idle (the header's `✓ done/total` count keeps the
76
+ * summary), and `collapsed` folds the whole section to that single header
77
+ * line any time — including mid-turn, where the line still previews the
78
+ * in-progress task. `onToggle` is the click affordance for the header row;
79
+ * the ctrl/cmd+q hotkey in Chat drives the same state.
80
+ *
81
+ * Goal elapsed time is the panel's own wall clock (started when the goal
82
+ * id first appears, frozen on completion) — deliberately not derived from
83
+ * session-event timestamps.
45
84
  */
46
- export function GoalTodoPanel({ channel }) {
85
+ export function GoalTodoPanel({ channel, collapsed = false, onToggle, }) {
47
86
  const goal = channel.goal;
48
87
  const allTodos = channel.todos ?? [];
88
+ const doneCount = allTodos.filter(todo => todo.status === 'completed').length;
49
89
  // Completed rows are useful progress while a turn is running, but become
50
- // stale footer noise once the agent is idle. Keep unfinished work visible.
90
+ // stale footer noise once the agent is idle. Keep unfinished work visible;
91
+ // the header count carries the done summary either way.
51
92
  const todos = channel.working
52
93
  ? allTodos
53
94
  : allTodos.filter(todo => todo.status !== 'completed');
54
- if (goal === undefined && todos.length === 0)
95
+ // Local goal timer: remember when this goal id first rendered. Written in
96
+ // render (idempotent lazy ref init) so a fresh mount with a live goal
97
+ // starts counting immediately.
98
+ const startRef = React.useRef(undefined);
99
+ if (goal !== undefined && startRef.current?.id !== goal.id) {
100
+ startRef.current = { id: goal.id, at: Date.now() };
101
+ }
102
+ const [now, setNow] = React.useState(() => Date.now());
103
+ // Hover tint for the clickable todo fold header (mouse affordance).
104
+ const [headerHovered, setHeaderHovered] = React.useState(false);
105
+ React.useEffect(() => {
106
+ // Tick only while the goal is open; a complete goal freezes the last
107
+ // elapsed reading instead of counting past the finish line.
108
+ if (goal === undefined || goal.phase === 'complete')
109
+ return;
110
+ const timer = setInterval(() => setNow(Date.now()), 1000);
111
+ return () => clearInterval(timer);
112
+ }, [goal]);
113
+ const elapsed = goal !== undefined && startRef.current !== undefined
114
+ ? formatDuration(now - startRef.current.at)
115
+ : undefined;
116
+ // All-completed idle snapshot with no goal: nothing left to narrate —
117
+ // the whole panel folds away (historical behavior).
118
+ const anyUnfinished = allTodos.some(todo => todo.status !== 'completed');
119
+ const showTodoSection = allTodos.length > 0 && (channel.working || anyUnfinished || goal !== undefined);
120
+ if (goal === undefined && !showTodoSection)
55
121
  return null;
56
122
  const visible = todos.slice(0, MAX_TODOS);
57
123
  const hidden = todos.length - visible.length;
58
- return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: [goal !== undefined && (_jsxs(Box, { flexDirection: "column", marginBottom: todos.length > 0 ? 1 : 0, children: [_jsxs(Box, { flexDirection: "row", width: "100%", children: [_jsx(Text, { color: "suggestion", children: "\uD83C\uDFAF " }), _jsx(Box, { flexGrow: 1, flexShrink: 1, children: _jsx(Text, { bold: true, wrap: "truncate", children: goal.objective }) }), _jsx(Box, { flexShrink: 0, marginLeft: 1, children: _jsx(PhaseBadge, { phase: goal.phase, roundsStarted: goal.roundsStarted, maxGoalRounds: goal.maxGoalRounds }) })] }), goal.phase === 'blocked' && goal.blockedReason !== undefined && (_jsxs(Box, { flexDirection: "row", marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "\u2502 " }), _jsx(Text, { color: "error", wrap: "truncate", children: goal.blockedReason.message })] }))] })), todos.length > 0 && (_jsxs(Box, { flexDirection: "column", children: [visible.map((todo, index) => {
59
- const last = index === visible.length - 1 && hidden === 0;
60
- return (_jsxs(Box, { flexDirection: "row", children: [_jsx(BranchPrefix, { last: last }), _jsx(TodoGlyph, { status: todo.status }), _jsx(Text, { wrap: "truncate", dimColor: todo.status === 'completed', children: todo.content })] }, index));
61
- }), hidden > 0 && (_jsxs(Box, { flexDirection: "row", children: [_jsx(BranchPrefix, { last: true }), _jsxs(Text, { dimColor: true, children: ["\u2026 ", hidden, " more"] })] }))] }))] }));
124
+ // Collapsed preview: the live task when one runs, else the next open row.
125
+ const preview = allTodos.find(todo => todo.status === 'in_progress')
126
+ ?? allTodos.find(todo => todo.status !== 'completed');
127
+ return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, paddingRight: 2, paddingTop: 1, children: [goal !== undefined && (_jsxs(Box, { flexDirection: "column", marginBottom: showTodoSection ? 1 : 0, children: [_jsxs(Box, { flexDirection: "row", width: "100%", children: [_jsx(Text, { color: "suggestion", children: "\uD83C\uDFAF " }), _jsx(Box, { flexGrow: 1, flexShrink: 1, children: _jsx(Text, { bold: true, wrap: "truncate", children: goal.objective }) }), _jsx(Box, { flexShrink: 0, marginLeft: 1, children: _jsx(PhaseBadge, { phase: goal.phase, roundsStarted: goal.roundsStarted, maxGoalRounds: goal.maxGoalRounds, elapsed: elapsed }) })] }), goal.phase === 'blocked' && goal.blockedReason !== undefined && (_jsxs(Box, { flexDirection: "row", marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "\u2502 " }), _jsx(Text, { color: "error", wrap: "truncate", children: goal.blockedReason.message })] }))] })), showTodoSection && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", onClick: onToggle, onMouseEnter: () => setHeaderHovered(true), onMouseLeave: () => setHeaderHovered(false), backgroundColor: headerHovered ? 'userMessageBackgroundHover' : undefined, children: [_jsxs(Text, { dimColor: true, children: [collapsed ? '▸' : '▾', " "] }), _jsxs(Text, { dimColor: true, children: ["\u2713 ", doneCount, "/", allTodos.length] }), collapsed && preview !== undefined && (_jsx(Box, { flexGrow: 1, flexShrink: 1, marginLeft: 1, children: preview.status === 'in_progress' ? (_jsxs(Text, { wrap: "truncate", children: [_jsx(Text, { color: "suggestion", children: "\u25CF " }), preview.content] })) : (_jsxs(Text, { wrap: "truncate", dimColor: true, children: ["\u25CB ", preview.content] })) }))] }), !collapsed && (_jsxs(Box, { flexDirection: "column", children: [visible.map((todo, index) => {
128
+ const last = index === visible.length - 1 && hidden === 0;
129
+ return (_jsxs(Box, { flexDirection: "row", children: [_jsx(BranchPrefix, { last: last }), _jsx(TodoGlyph, { status: todo.status }), _jsx(Text, { wrap: "truncate", dimColor: todo.status === 'completed', children: todo.content })] }, index));
130
+ }), hidden > 0 && (_jsxs(Box, { flexDirection: "row", children: [_jsx(BranchPrefix, { last: true }), _jsxs(Text, { dimColor: true, children: ["\u2026 ", hidden, " more"] })] })), _jsxs(Text, { dimColor: true, children: [" ", t('goal-todo-fold-hint', { mod: modLabel })] })] }))] }))] }));
62
131
  }
@@ -11,7 +11,7 @@ import type { LocalCommand } from '../commands.js';
11
11
  * menu is for chrome commands. Modifier labels follow the platform
12
12
  * convention: ⌘ on macOS, ctrl elsewhere.
13
13
  */
14
- export declare function HelpMenu({ commands, viewportHeight, viewportWidth, scrollRef, }: {
14
+ export declare function HelpMenu({ commands, viewportHeight, viewportWidth, scrollRef, onCommandPick, }: {
15
15
  commands: readonly LocalCommand[];
16
16
  /** Fixed viewport supplied by the prompt overlay; unset for standalone renders. */
17
17
  viewportHeight?: number;
@@ -19,5 +19,11 @@ export declare function HelpMenu({ commands, viewportHeight, viewportWidth, scro
19
19
  viewportWidth?: number;
20
20
  /** PromptInput owns keyboard routing and drives this scroll viewport. */
21
21
  scrollRef?: React.Ref<ScrollBoxHandle>;
22
+ /**
23
+ * Mouse pick on a command row (fullscreen): fills `/<name> ` into the
24
+ * prompt — the Tab-completion's mouse equivalent. Absent for standalone
25
+ * renders (i18n verifier): rows render exactly as before.
26
+ */
27
+ onCommandPick?: (name: string) => void;
22
28
  }): React.ReactNode;
23
29
  //# sourceMappingURL=HelpMenu.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"HelpMenu.d.ts","sourceRoot":"","sources":["../../../src/components/HelpMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,UAAU,CAAA;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAKlD;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,cAAc,EACd,aAAa,EACb,SAAS,GACV,EAAE;IACD,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAA;IACjC,mFAAmF;IACnF,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,yEAAyE;IACzE,SAAS,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;CACvC,GAAG,KAAK,CAAC,SAAS,CAyHlB"}
1
+ {"version":3,"file":"HelpMenu.d.ts","sourceRoot":"","sources":["../../../src/components/HelpMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,UAAU,CAAA;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAKlD;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,cAAc,EACd,aAAa,EACb,SAAS,EACT,aAAa,GACd,EAAE;IACD,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAA;IACjC,mFAAmF;IACnF,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,yEAAyE;IACzE,SAAS,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;IACtC;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACvC,GAAG,KAAK,CAAC,SAAS,CAoHlB"}
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
2
3
  import { Box, ScrollBox, Text } from '../ui.js';
3
4
  import { localizedDescription } from '../commands.js';
4
5
  import { t } from '../i18n.js';
@@ -13,12 +14,12 @@ import { modLabel } from '../utils/modifiers.js';
13
14
  * menu is for chrome commands. Modifier labels follow the platform
14
15
  * convention: ⌘ on macOS, ctrl elsewhere.
15
16
  */
16
- export function HelpMenu({ commands, viewportHeight, viewportWidth, scrollRef, }) {
17
+ export function HelpMenu({ commands, viewportHeight, viewportWidth, scrollRef, onCommandPick, }) {
17
18
  const chrome = commands.filter(command => !command.skill);
18
19
  const primaryShortcuts = (_jsxs(Box, { flexDirection: "column", width: 26, flexShrink: 0, children: [_jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-for-commands') }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-this-help') }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-verbose-output', { mod: modLabel }) }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-open-trajectory', { mod: modLabel }) }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-search-history', { mod: modLabel }) }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-interrupt') }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-exit') }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-redraw', { mod: modLabel }) }) })] }));
19
- const secondaryShortcuts = (_jsxs(Box, { flexDirection: "column", width: 24, flexShrink: 0, children: [_jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-clear-input') }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-history-nav') }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-move-cursor') }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-word-jumps', { mod: modLabel }) }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-complete-command') }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-cycle-mode') }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-open-editor') }) })] }));
20
- const commandRows = chrome.map(command => (_jsx(Box, { flexShrink: 0, children: _jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["/", command.name, " \u2014 ", localizedDescription(command)] }) }, command.name)));
21
- const compactCommandRows = chrome.map(command => (_jsx(Box, { flexShrink: 0, children: _jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["/", command.name, " \u2014 ", localizedDescription(command)] }) }, command.name)));
20
+ const secondaryShortcuts = (_jsxs(Box, { flexDirection: "column", width: 24, flexShrink: 0, children: [_jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-clear-input') }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-history-nav') }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-move-cursor') }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-word-jumps', { mod: modLabel }) }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-complete-command') }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-cycle-mode') }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-open-editor') }) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: t('help-fold-todos', { mod: modLabel }) }) })] }));
21
+ const commandRows = chrome.map(command => (_jsx(HelpCommandRow, { command: command, onPick: onCommandPick }, command.name)));
22
+ const compactCommandRows = chrome.map(command => (_jsx(HelpCommandRow, { command: command, onPick: onCommandPick }, command.name)));
22
23
  const commandList = (_jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [_jsx(Text, { dimColor: true, children: t('help-commands-title') }), commandRows] }));
23
24
  const content = (_jsxs(Box, { paddingX: 2, flexDirection: "row", gap: 4, children: [primaryShortcuts, secondaryShortcuts, _jsx(Box, { flexDirection: "column", flexShrink: 1, children: commandList })] }));
24
25
  // Standalone renders (notably the i18n verifier) keep the historical
@@ -29,3 +30,10 @@ export function HelpMenu({ commands, viewportHeight, viewportWidth, scrollRef, }
29
30
  const compact = viewportWidth !== undefined && viewportWidth < 72;
30
31
  return (_jsxs(Box, { height: viewportHeight, flexDirection: "column", children: [compact ? (_jsxs(ScrollBox, { ref: scrollRef, flexDirection: "column", flexGrow: 1, flexShrink: 1, paddingX: 2, children: [primaryShortcuts, secondaryShortcuts, _jsx(Text, { dimColor: true, children: t('help-commands-title') }), compactCommandRows] })) : (_jsxs(Box, { paddingX: 2, flexDirection: "row", gap: 4, flexGrow: 1, children: [primaryShortcuts, secondaryShortcuts, _jsx(ScrollBox, { ref: scrollRef, flexDirection: "column", flexGrow: 1, flexShrink: 1, children: commandList })] })), _jsx(Box, { paddingX: 2, flexShrink: 0, children: _jsx(Text, { dimColor: true, wrap: "truncate-end", children: t('help-scroll-hint') }) })] }));
31
32
  }
33
+ /** One `/name — description` row; clickable (hover background) when the
34
+ * host wired a pick handler — the menu IS a control surface. */
35
+ function HelpCommandRow({ command, onPick, }) {
36
+ const [hovered, setHovered] = React.useState(false);
37
+ const clickable = onPick !== undefined;
38
+ return (_jsx(Box, { flexShrink: 0, onClick: clickable ? () => onPick(command.name) : undefined, onMouseEnter: clickable ? () => setHovered(true) : undefined, onMouseLeave: clickable ? () => setHovered(false) : undefined, backgroundColor: clickable && hovered ? 'userMessageBackgroundHover' : undefined, children: _jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ["/", command.name, " \u2014 ", localizedDescription(command)] }) }));
39
+ }
@@ -7,10 +7,13 @@ import { type HistoryEntry } from '../history.js';
7
7
  * first), and the ↑/↓ · Enter · Esc hint line. Keyboard handling lives in
8
8
  * the caller (Chat).
9
9
  */
10
- export declare function HistorySearchDialog({ query, cursorOffset, matches, focusIndex, }: {
10
+ export declare function HistorySearchDialog({ query, cursorOffset, matches, focusIndex, onPick, }: {
11
11
  query: string;
12
12
  cursorOffset: number;
13
13
  matches: readonly HistoryEntry[];
14
14
  focusIndex: number;
15
+ /** 鼠标点击行(fullscreen):上报绝对索引——Chat 用与 Enter 相同的
16
+ * 填入/提交路径处理。 */
17
+ onPick?: (index: number) => void;
15
18
  }): React.ReactNode;
16
19
  //# sourceMappingURL=HistorySearchDialog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"HistorySearchDialog.d.ts","sourceRoot":"","sources":["../../../src/components/HistorySearchDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjE;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,YAAY,EACZ,OAAO,EACP,UAAU,GACX,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,SAAS,YAAY,EAAE,CAAA;IAChC,UAAU,EAAE,MAAM,CAAA;CACnB,GAAG,KAAK,CAAC,SAAS,CAuDlB"}
1
+ {"version":3,"file":"HistorySearchDialog.d.ts","sourceRoot":"","sources":["../../../src/components/HistorySearchDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjE;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,YAAY,EACZ,OAAO,EACP,UAAU,EACV,MAAM,GACP,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,SAAS,YAAY,EAAE,CAAA;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB;qBACiB;IACjB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC,GAAG,KAAK,CAAC,SAAS,CAwDlB"}
@@ -15,7 +15,7 @@ import { historyEntryId } from '../history.js';
15
15
  * first), and the ↑/↓ · Enter · Esc hint line. Keyboard handling lives in
16
16
  * the caller (Chat).
17
17
  */
18
- export function HistorySearchDialog({ query, cursorOffset, matches, focusIndex, }) {
18
+ export function HistorySearchDialog({ query, cursorOffset, matches, focusIndex, onPick, }) {
19
19
  const isTerminalFocused = useTerminalFocus();
20
20
  const { rows: terminalRows } = useTerminalSize();
21
21
  // 焦点窗口化按行预算:每项恒 2 行(命令 + age 描述,ListItem 保证单行
@@ -30,7 +30,7 @@ export function HistorySearchDialog({ query, cursorOffset, matches, focusIndex,
30
30
  // The SearchBox owns the native-cursor declaration while this
31
31
  // dialog is open — result rows must not park the cursor on
32
32
  // themselves, or IME preedit lands on a list row.
33
- declareCursor: false, description: formatRelativeAge(entry.ts), showScrollUp: absoluteIndex === start && start > 0, showScrollDown: absoluteIndex === end - 1 && end < matches.length, children: entry.text }, historyEntryId(entry)));
33
+ declareCursor: false, description: formatRelativeAge(entry.ts), showScrollUp: absoluteIndex === start && start > 0, showScrollDown: absoluteIndex === end - 1 && end < matches.length, onClick: onPick ? () => onPick(absoluteIndex) : undefined, children: entry.text }, historyEntryId(entry)));
34
34
  })), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('hint-history-search') }) })] }) }));
35
35
  }
36
36
  /** Relative age like CC's formatRelativeTimeAgo ("now" / "5m ago" / …), localized. */
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import { type Lang } from '../i18n.js';
3
+ /**
4
+ * `/lang` en/zh picker: bare `/lang` opens this picker (the current language
5
+ * marked) and Enter applies the choice through the same path as `/lang <id>`.
6
+ */
7
+ export declare function LangPicker({ focusIndex, currentLang, onPick, }: {
8
+ focusIndex: number;
9
+ currentLang: Lang;
10
+ /** Mouse pick (fullscreen): clicked row's absolute index (Chat applies
11
+ * the same code path as the keyboard Enter). */
12
+ onPick?: (index: number) => void;
13
+ }): React.ReactNode;
14
+ //# sourceMappingURL=LangPicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LangPicker.d.ts","sourceRoot":"","sources":["../../../src/components/LangPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAY,KAAK,IAAI,EAAE,MAAM,YAAY,CAAA;AAMhD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EACzB,UAAU,EACV,WAAW,EACX,MAAM,GACP,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,IAAI,CAAA;IACjB;qDACiD;IACjD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC,GAAG,KAAK,CAAC,SAAS,CAyBlB"}
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { LANGS, 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
+ * `/lang` en/zh picker: bare `/lang` opens this picker (the current language
9
+ * marked) and Enter applies the choice through the same path as `/lang <id>`.
10
+ */
11
+ export function LangPicker({ focusIndex, currentLang, onPick, }) {
12
+ return (_jsx(Pane, { color: "permission", children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: t('lang-picker-title') }) }), _jsx(Select, { options: LANGS.map(lang => ({
13
+ value: lang,
14
+ label: lang === 'zh' ? '中文' : 'English',
15
+ description: t(lang === 'zh' ? 'lang-zh-desc' : 'lang-en-desc'),
16
+ })), focusIndex: focusIndex, selectedValue: currentLang, onPick: onPick ? index => onPick(index) : undefined }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('hint-confirm-exit') }) })] }) }));
17
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"LoadedContextPanel.d.ts","sourceRoot":"","sources":["../../../src/components/LoadedContextPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,2BAA2B,CAAA;AA+BlF;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,OAAO,EACP,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,aAAa,CAAA;IACtB,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,EAAE,MAAM,IAAI,CAAA;CACrB,GAAG,KAAK,CAAC,SAAS,CAoElB"}
1
+ {"version":3,"file":"LoadedContextPanel.d.ts","sourceRoot":"","sources":["../../../src/components/LoadedContextPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,2BAA2B,CAAA;AA+BlF;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,OAAO,EACP,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,aAAa,CAAA;IACtB,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,EAAE,MAAM,IAAI,CAAA;CACrB,GAAG,KAAK,CAAC,SAAS,CA2ElB"}
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
2
3
  import { t } from '../i18n.js';
3
4
  import { Box, Text } from '../ui.js';
4
5
  import { summarizeLoadedContext, truncateContextText } from '../utils/loaded-context.js';
@@ -24,7 +25,8 @@ function Group({ title, children }) {
24
25
  */
25
26
  export function LoadedContextPanel({ context, open, onToggle, }) {
26
27
  const summary = summarizeLoadedContext(context);
28
+ const [hovered, setHovered] = React.useState(false);
27
29
  if (summary === '')
28
30
  return null;
29
- return (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [_jsx(Box, { paddingX: 1, backgroundColor: open ? 'userMessageBackgroundHover' : undefined, children: _jsxs(Text, { bold: open, wrap: "truncate", children: [open ? '▼' : '▶', " ", _jsxs(Text, { dimColor: true, children: ["\uFF08Ctrl+P", open ? t('context-panel-collapse') : t('context-panel-expand'), "\uFF09"] }), " ", t('context-loaded'), " \u00B7 ", summary] }) }), open && (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [context.sections.length > 0 && (_jsx(Group, { title: t('context-panel-sections', { n: context.sections.length }), children: context.sections.map(section => (_jsx(Entry, { entry: section }, section.name))) })), context.files.length > 0 && (_jsx(Group, { title: t('context-panel-files', { n: context.files.length }), children: context.files.map(file => (_jsx(Text, { dimColor: true, children: file.displayPath }, file.displayPath))) })), context.contexts.length > 0 && (_jsx(Group, { title: t('context-panel-runtime', { n: context.contexts.length }), children: context.contexts.map(entry => (_jsx(Entry, { entry: entry }, entry.name))) })), context.skills.length > 0 && (_jsx(Group, { title: t('context-panel-skills', { n: context.skills.length }), children: context.skills.map(skill => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, dimColor: true, children: skill.name }), _jsx(Text, { dimColor: true, wrap: "wrap", children: skill.description })] }, skill.name))) })), context.tools.length > 0 && (_jsx(Group, { title: t('context-panel-tools', { n: context.tools.length }), children: context.tools.map(tool => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, dimColor: true, children: tool.name }), _jsx(Text, { dimColor: true, wrap: "wrap", children: truncateContextText(tool.description, 160) })] }, tool.name))) })), _jsx(Text, { dimColor: true, children: "\u00B7 /context" })] }))] }));
31
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [_jsx(Box, { paddingX: 1, backgroundColor: open || hovered ? 'userMessageBackgroundHover' : undefined, onClick: onToggle, onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), children: _jsxs(Text, { bold: open, wrap: "truncate", children: [open ? '▼' : '▶', " ", _jsxs(Text, { dimColor: true, children: ["\uFF08Ctrl+P", open ? t('context-panel-collapse') : t('context-panel-expand'), "\uFF09"] }), " ", t('context-loaded'), " \u00B7 ", summary] }) }), open && (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [context.sections.length > 0 && (_jsx(Group, { title: t('context-panel-sections', { n: context.sections.length }), children: context.sections.map(section => (_jsx(Entry, { entry: section }, section.name))) })), context.files.length > 0 && (_jsx(Group, { title: t('context-panel-files', { n: context.files.length }), children: context.files.map(file => (_jsx(Text, { dimColor: true, children: file.displayPath }, file.displayPath))) })), context.contexts.length > 0 && (_jsx(Group, { title: t('context-panel-runtime', { n: context.contexts.length }), children: context.contexts.map(entry => (_jsx(Entry, { entry: entry }, entry.name))) })), context.skills.length > 0 && (_jsx(Group, { title: t('context-panel-skills', { n: context.skills.length }), children: context.skills.map(skill => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, dimColor: true, children: skill.name }), _jsx(Text, { dimColor: true, wrap: "wrap", children: skill.description })] }, skill.name))) })), context.tools.length > 0 && (_jsx(Group, { title: t('context-panel-tools', { n: context.tools.length }), children: context.tools.map(tool => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, dimColor: true, children: tool.name }), _jsx(Text, { dimColor: true, wrap: "wrap", children: truncateContextText(tool.description, 160) })] }, tool.name))) })), _jsx(Text, { dimColor: true, children: "\u00B7 /context" })] }))] }));
30
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Markdown.d.ts","sourceRoot":"","sources":["../../../src/components/Markdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB;;;;;;;;GAQG;AAEH,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AA4GD;;;;;;;;;;GAUG;AACH,iBAAS,YAAY,CAAC,EAAE,QAAQ,EAAE,QAAgB,EAAE,WAAkB,EAAE,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CA6BhG;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,gDAMpB,CAAA"}
1
+ {"version":3,"file":"Markdown.d.ts","sourceRoot":"","sources":["../../../src/components/Markdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB;;;;;;;;GAQG;AAEH,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAoHD;;;;;;;;;;GAUG;AACH,iBAAS,YAAY,CAAC,EAAE,QAAQ,EAAE,QAAgB,EAAE,WAAkB,EAAE,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CA6BhG;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,gDAMpB,CAAA"}
@@ -53,10 +53,19 @@ function lexWithCache(content, allowCache) {
53
53
  const tokens = marked.lexer(content);
54
54
  if (content.length > TOKEN_CACHE_MAX_SOURCE_LENGTH)
55
55
  return tokens;
56
- if (tokenCache.size >= TOKEN_CACHE_CAPACITY ||
56
+ // Evict oldest entries (Map preserves insertion order) until both the
57
+ // count and char budgets fit. The previous full clear() nuked the whole
58
+ // cache every time a long session crossed 200 blocks — every subsequent
59
+ // row remount then re-ran the lexer (scroll-through-a-long-session
60
+ // stutter); evicting only what the newcomer displaces keeps the working
61
+ // set warm.
62
+ while (tokenCache.size >= TOKEN_CACHE_CAPACITY ||
57
63
  tokenCacheChars + content.length > TOKEN_CACHE_CHAR_BUDGET) {
58
- tokenCache.clear();
59
- tokenCacheChars = 0;
64
+ const oldest = tokenCache.keys().next().value;
65
+ if (oldest === undefined)
66
+ break;
67
+ tokenCache.delete(oldest);
68
+ tokenCacheChars -= oldest.length;
60
69
  }
61
70
  tokenCache.set(content, tokens);
62
71
  tokenCacheChars += content.length;
@@ -2,13 +2,17 @@ import React from 'react';
2
2
  import { type ScrollBoxHandle } from '../ui.js';
3
3
  import type { ChatRow } from '../dsh-adapter/channel.js';
4
4
  import type { DOMElement } from '../ink/dom.js';
5
+ import { type TimelineSnapshot } from '../ink/timeline-rail.js';
5
6
  import type { ToolBackground } from '../tuiDisplayPrefs.js';
6
- export declare function MessageList({ rows, expanded, expandedRows, selectedId, onToggleRow, model, diffLayout, thinkingFold, toolBackground, activityFrames, showAll, onToggleAll, onLoadOlder, thinkingVisible, registerRowRef, scrollHandle, forceMountRowId, newSinceRowId, onUnseenCount, failureHintRowId, failureHint, }: {
7
+ export declare function MessageList({ rows, expanded, expandedRows, selectedId, onToggleRow, streamFoldedRows, onToggleStreamFold, model, diffLayout, thinkingFold, toolBackground, activityFrames, showAll, onToggleAll, onLoadOlder, thinkingVisible, historyPaintEnabled, registerRowRef, scrollHandle, forceMountRowId, newSinceRowId, onUnseenCount, onTimeline, failureHintRowId, failureHint, onOpenSubagent, }: {
7
8
  rows: readonly ChatRow[];
8
9
  expanded: boolean;
9
10
  expandedRows: ReadonlySet<number>;
10
11
  selectedId: number | null;
11
12
  onToggleRow: (rowId: number) => void;
13
+ /** 流式 reasoning 行被用户折叠(点击展开/折叠对流式行同样有效)。 */
14
+ streamFoldedRows?: ReadonlySet<number>;
15
+ onToggleStreamFold?: (rowId: number) => void;
12
16
  model: string;
13
17
  /** Edit/Write diff presentation preference (forwarded to tool cards). */
14
18
  diffLayout?: 'auto' | 'split' | 'unified';
@@ -25,6 +29,15 @@ export declare function MessageList({ rows, expanded, expandedRows, selectedId,
25
29
  * earlier messages" affordance; shown only when rows were folded). */
26
30
  onLoadOlder?: () => void;
27
31
  thinkingVisible?: boolean;
32
+ /**
33
+ * Whether rows outside the virtualization window must still be painted
34
+ * once (main-screen mode: unpainted rows leave NO copy in the terminal
35
+ * scrollback, so preset history would vanish). The alt-screen has no
36
+ * scrollback — passing false skips the mount-everything-on-open
37
+ * expansion there (a 300-row fold window of markdown otherwise costs
38
+ * seconds of lex/highlight/layout before first paint).
39
+ */
40
+ historyPaintEnabled?: boolean;
28
41
  /** Transcript search: register each row's DOM element for scroll-to-match. */
29
42
  registerRowRef?: (rowId: number, el: DOMElement | null) => void;
30
43
  /** Scroll viewport the list virtualizes against. */
@@ -36,6 +49,29 @@ export declare function MessageList({ rows, expanded, expandedRows, selectedId,
36
49
  newSinceRowId?: number | null;
37
50
  /** Reports how many new rows still sit below the viewport bottom edge. */
38
51
  onUnseenCount?: (count: number) => void;
52
+ /**
53
+ * Reports the conversation timeline snapshot for the sticky prompt
54
+ * header AND the transcript's turn rail: one entry per user turn
55
+ * (stable row id + content-space text top + preview line), plus the
56
+ * viewport-derived navigation targets, all computed from the same
57
+ * offsets[]/base geometry the mount window uses:
58
+ *
59
+ * - active: the LAST turn whose prompt top is at-or-above the viewport
60
+ * top (the turn whose content owns the top row — the one being
61
+ * read); the FIRST turn stands in while pre-turn content (logo /
62
+ * loaded-context) owns the top. Never null while any turn exists.
63
+ * Top-anchored on purpose (Grok timeline semantics), not
64
+ * "topmost visible prompt": the highlight moves only when a turn
65
+ * boundary crosses the viewport top, so it never leaps when nudging
66
+ * off the bottom, and the header/rail can never disagree.
67
+ * - upId: nearest turn STRICTLY above the viewport top (▲ target).
68
+ * - downId: nearest turn below the top whose top ≤ maxScroll — turns
69
+ * past maxScroll can never own the top row (the renderer clamps
70
+ * there), so naming them would make ▼ repeat itself forever.
71
+ *
72
+ * Reported post-commit, only when the signature changes.
73
+ */
74
+ onTimeline?: (state: TimelineSnapshot) => void;
39
75
  /**
40
76
  * Row id that should carry the trajectory footnote — the newest unseen
41
77
  * failure, or null. Exactly one row ever carries it: repeating the pointer
@@ -44,6 +80,8 @@ export declare function MessageList({ rows, expanded, expandedRows, selectedId,
44
80
  failureHintRowId?: number | null;
45
81
  /** Footnote text, e.g. `ctrl+t for the full trajectory`. */
46
82
  failureHint?: string;
83
+ /** 打开子代理详情场景(transcript 内点击子代理卡)。 */
84
+ onOpenSubagent?: (agentId: string) => void;
47
85
  }): React.JSX.Element;
48
86
  /**
49
87
  * The header block pinned above the transcript: the DeepSeek pixel whale
@@ -51,10 +89,13 @@ export declare function MessageList({ rows, expanded, expandedRows, selectedId,
51
89
  * welcome line. It scrolls away with the transcript once the conversation
52
90
  * fills the viewport (Claude Code shows its ✦ logo in the same slot).
53
91
  */
54
- export declare function LogoHeader({ model, effort, cwd, whale, }: {
92
+ export declare function LogoHeader({ model, effort, cwd, whale, skipIntro, }: {
55
93
  model: string;
56
94
  effort?: string | undefined;
57
95
  cwd: string;
58
96
  whale?: boolean;
97
+ /** Jump straight to the settled header (long-session resume: the ~3.4s
98
+ * opening animation competes with transcript mount batches). */
99
+ skipIntro?: boolean;
59
100
  }): React.ReactNode;
60
101
  //# sourceMappingURL=MessageList.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAA8B,KAAK,eAAe,EAAE,MAAM,UAAU,CAAA;AAC3E,OAAO,KAAK,EAAE,OAAO,EAAsD,MAAM,2BAA2B,CAAA;AAC5G,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAe/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AA2B3D,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,WAAW,EACX,KAAK,EACL,UAAmB,EACnB,YAAwB,EACxB,cAAuB,EACvB,cAAc,EACd,OAAO,EACP,WAAW,EACX,WAAW,EACX,eAAsB,EACtB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,WAAW,GACZ,EAAE;IACD,IAAI,EAAE,SAAS,OAAO,EAAE,CAAA;IACxB,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACjC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IACzC,mEAAmE;IACnE,YAAY,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;IACjC,qEAAqE;IACrE,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B;sEACkE;IAClE,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,MAAM,IAAI,CAAA;IACvB;2EACuE;IACvE,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAA;IAC/D,oDAAoD;IACpD,YAAY,CAAC,EAAE,eAAe,GAAG,IAAI,CAAA;IACrC,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B;8EAC0E;IAC1E,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,0EAA0E;IAC1E,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,qBAiWA;AA2SD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,MAAM,EACN,GAAG,EACH,KAAY,GACb,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,GAAG,KAAK,CAAC,SAAS,CASlB"}
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/MessageList.tsx"],"names":[],"mappings":"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"}