@deepseek-harness-tui/dsh-tui 0.10.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (277) hide show
  1. package/README.md +171 -124
  2. package/bin/dsh-tui.js +748 -22
  3. package/cordis.patch.yml +38 -10
  4. package/cordis.yml +2 -0
  5. package/lib/types/adapter/channel/session-projection.d.ts.map +1 -1
  6. package/lib/types/adapter/channel/session-projection.js +2 -0
  7. package/lib/types/adapter/channel/ui-policy.d.ts +4 -1
  8. package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
  9. package/lib/types/adapter/channel/ui-policy.js +5 -1
  10. package/lib/types/adapter/kernel/host-facade.d.ts.map +1 -1
  11. package/lib/types/adapter/kernel/host-facade.js +1 -0
  12. package/lib/types/adapter/kernel/replay.d.ts.map +1 -1
  13. package/lib/types/adapter/kernel/replay.js +3 -0
  14. package/lib/types/adapter/kernel/runtime.d.ts.map +1 -1
  15. package/lib/types/adapter/kernel/runtime.js +2 -0
  16. package/lib/types/adapter/kernel/slices/workspace.d.ts.map +1 -1
  17. package/lib/types/adapter/kernel/slices/workspace.js +1 -0
  18. package/lib/types/adapter/ports/channel-ui.d.ts +22 -2
  19. package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
  20. package/lib/types/adapter/ports/channel-view.d.ts +66 -0
  21. package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
  22. package/lib/types/adapter/ports/channel-workspace.d.ts +23 -0
  23. package/lib/types/adapter/ports/channel-workspace.d.ts.map +1 -1
  24. package/lib/types/adapter/ports/workspace.d.ts +2 -0
  25. package/lib/types/adapter/ports/workspace.d.ts.map +1 -1
  26. package/lib/types/adapter/upstream/workspace-driver.d.ts.map +1 -1
  27. package/lib/types/adapter/upstream/workspace-driver.js +10 -2
  28. package/lib/types/commands.d.ts.map +1 -1
  29. package/lib/types/commands.js +1 -0
  30. package/lib/types/components/ContextBarView.d.ts +15 -7
  31. package/lib/types/components/ContextBarView.d.ts.map +1 -1
  32. package/lib/types/components/ContextBarView.js +26 -22
  33. package/lib/types/components/ImagePreviewOverlay.d.ts.map +1 -1
  34. package/lib/types/components/ImagePreviewOverlay.js +11 -5
  35. package/lib/types/components/Markdown.d.ts +11 -1
  36. package/lib/types/components/Markdown.d.ts.map +1 -1
  37. package/lib/types/components/Markdown.js +20 -2
  38. package/lib/types/components/MermaidDiagram.d.ts +13 -0
  39. package/lib/types/components/MermaidDiagram.d.ts.map +1 -0
  40. package/lib/types/components/MermaidDiagram.js +61 -0
  41. package/lib/types/components/MessageList.d.ts +26 -1
  42. package/lib/types/components/MessageList.d.ts.map +1 -1
  43. package/lib/types/components/MessageList.js +72 -7
  44. package/lib/types/components/PageMargin.d.ts +1 -1
  45. package/lib/types/components/PromptInput.d.ts +37 -1
  46. package/lib/types/components/PromptInput.d.ts.map +1 -1
  47. package/lib/types/components/PromptInput.js +243 -28
  48. package/lib/types/components/ScrollbarGutter.d.ts +20 -5
  49. package/lib/types/components/ScrollbarGutter.d.ts.map +1 -1
  50. package/lib/types/components/ScrollbarGutter.js +88 -10
  51. package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
  52. package/lib/types/components/StreamingMarkdown.js +30 -28
  53. package/lib/types/components/Tooltip.d.ts.map +1 -1
  54. package/lib/types/components/Tooltip.js +45 -7
  55. package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
  56. package/lib/types/components/messages/UserPromptMessage.js +5 -7
  57. package/lib/types/components/messages/useImageInspection.d.ts.map +1 -1
  58. package/lib/types/components/messages/useImageInspection.js +19 -9
  59. package/lib/types/components/promptDraftCache.d.ts +102 -0
  60. package/lib/types/components/promptDraftCache.d.ts.map +1 -0
  61. package/lib/types/components/promptDraftCache.js +76 -0
  62. package/lib/types/components/questions/AskUserQuestionPanel.d.ts +17 -1
  63. package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
  64. package/lib/types/components/questions/AskUserQuestionPanel.js +40 -7
  65. package/lib/types/components/questions/QuestionMinimizedBar.d.ts +12 -0
  66. package/lib/types/components/questions/QuestionMinimizedBar.d.ts.map +1 -0
  67. package/lib/types/components/questions/QuestionMinimizedBar.js +43 -0
  68. package/lib/types/components/sessions/SessionListRow.d.ts +23 -1
  69. package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
  70. package/lib/types/components/sessions/SessionListRow.js +52 -5
  71. package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts +49 -0
  72. package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts.map +1 -0
  73. package/lib/types/components/workspaces/HomeWorkspaceRow.js +50 -0
  74. package/lib/types/dsh-adapter/bundled-presets.d.ts +10 -0
  75. package/lib/types/dsh-adapter/bundled-presets.d.ts.map +1 -0
  76. package/lib/types/dsh-adapter/bundled-presets.js +80 -0
  77. package/lib/types/dsh-adapter/channel/action-readiness.d.ts +1 -1
  78. package/lib/types/dsh-adapter/channel/action-readiness.d.ts.map +1 -1
  79. package/lib/types/dsh-adapter/channel/action-readiness.js +3 -0
  80. package/lib/types/dsh-adapter/channel/agent-view-projection.d.ts +1 -1
  81. package/lib/types/dsh-adapter/channel/agent-view-projection.d.ts.map +1 -1
  82. package/lib/types/dsh-adapter/channel/agent-view-projection.js +19 -2
  83. package/lib/types/dsh-adapter/channel/background-action.d.ts.map +1 -1
  84. package/lib/types/dsh-adapter/channel/background-action.js +52 -29
  85. package/lib/types/dsh-adapter/channel/binding.d.ts +19 -2
  86. package/lib/types/dsh-adapter/channel/binding.d.ts.map +1 -1
  87. package/lib/types/dsh-adapter/channel/binding.js +78 -18
  88. package/lib/types/dsh-adapter/channel/composer-images.d.ts +5 -0
  89. package/lib/types/dsh-adapter/channel/composer-images.d.ts.map +1 -1
  90. package/lib/types/dsh-adapter/channel/composer-images.js +105 -6
  91. package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts +8 -0
  92. package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts.map +1 -1
  93. package/lib/types/dsh-adapter/channel/context-bookkeeping.js +3 -2
  94. package/lib/types/dsh-adapter/channel/ide-selection.d.ts +58 -0
  95. package/lib/types/dsh-adapter/channel/ide-selection.d.ts.map +1 -0
  96. package/lib/types/dsh-adapter/channel/ide-selection.js +226 -0
  97. package/lib/types/dsh-adapter/channel/input-delivery.d.ts +6 -1
  98. package/lib/types/dsh-adapter/channel/input-delivery.d.ts.map +1 -1
  99. package/lib/types/dsh-adapter/channel/input-delivery.js +9 -1
  100. package/lib/types/dsh-adapter/channel/local-actions.d.ts +3 -19
  101. package/lib/types/dsh-adapter/channel/local-actions.d.ts.map +1 -1
  102. package/lib/types/dsh-adapter/channel/local-actions.js +2 -1
  103. package/lib/types/dsh-adapter/channel/model-actions.d.ts +4 -1
  104. package/lib/types/dsh-adapter/channel/model-actions.d.ts.map +1 -1
  105. package/lib/types/dsh-adapter/channel/model-actions.js +82 -6
  106. package/lib/types/dsh-adapter/channel/model-switch.d.ts.map +1 -1
  107. package/lib/types/dsh-adapter/channel/model-switch.js +49 -31
  108. package/lib/types/dsh-adapter/channel/projection.d.ts +4 -0
  109. package/lib/types/dsh-adapter/channel/projection.d.ts.map +1 -1
  110. package/lib/types/dsh-adapter/channel/projection.js +29 -19
  111. package/lib/types/dsh-adapter/channel/reports.d.ts.map +1 -1
  112. package/lib/types/dsh-adapter/channel/reports.js +4 -12
  113. package/lib/types/dsh-adapter/channel/session-fork.d.ts.map +1 -1
  114. package/lib/types/dsh-adapter/channel/session-fork.js +26 -2
  115. package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts +3 -0
  116. package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts.map +1 -1
  117. package/lib/types/dsh-adapter/channel/session-live-adoption.js +1 -0
  118. package/lib/types/dsh-adapter/channel/session-metadata.d.ts +10 -0
  119. package/lib/types/dsh-adapter/channel/session-metadata.d.ts.map +1 -1
  120. package/lib/types/dsh-adapter/channel/session-metadata.js +2 -2
  121. package/lib/types/dsh-adapter/channel/session-resume.d.ts +11 -2
  122. package/lib/types/dsh-adapter/channel/session-resume.d.ts.map +1 -1
  123. package/lib/types/dsh-adapter/channel/session-resume.js +291 -94
  124. package/lib/types/dsh-adapter/channel/session-rewind.d.ts.map +1 -1
  125. package/lib/types/dsh-adapter/channel/session-rewind.js +34 -16
  126. package/lib/types/dsh-adapter/channel/session-tree-actions.d.ts.map +1 -1
  127. package/lib/types/dsh-adapter/channel/session-tree-actions.js +19 -3
  128. package/lib/types/dsh-adapter/channel/settings-host.d.ts.map +1 -1
  129. package/lib/types/dsh-adapter/channel/settings-host.js +4 -3
  130. package/lib/types/dsh-adapter/channel/state.d.ts +1 -1
  131. package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
  132. package/lib/types/dsh-adapter/channel/state.js +1 -1
  133. package/lib/types/dsh-adapter/channel/transcript.d.ts.map +1 -1
  134. package/lib/types/dsh-adapter/channel/transcript.js +6 -12
  135. package/lib/types/dsh-adapter/channel/types.d.ts +6 -1
  136. package/lib/types/dsh-adapter/channel/types.d.ts.map +1 -1
  137. package/lib/types/dsh-adapter/channel/workspace-actions.d.ts +4 -1
  138. package/lib/types/dsh-adapter/channel/workspace-actions.d.ts.map +1 -1
  139. package/lib/types/dsh-adapter/channel/workspace-actions.js +41 -1
  140. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  141. package/lib/types/dsh-adapter/channel.js +67 -6
  142. package/lib/types/dsh-adapter/compat/messages.d.ts +15 -0
  143. package/lib/types/dsh-adapter/compat/messages.d.ts.map +1 -0
  144. package/lib/types/dsh-adapter/compat/messages.js +15 -0
  145. package/lib/types/dsh-adapter/compat/settings.d.ts +33 -0
  146. package/lib/types/dsh-adapter/compat/settings.d.ts.map +1 -0
  147. package/lib/types/dsh-adapter/compat/settings.js +43 -0
  148. package/lib/types/dsh-adapter/compat/shell.d.ts +25 -0
  149. package/lib/types/dsh-adapter/compat/shell.d.ts.map +1 -0
  150. package/lib/types/dsh-adapter/compat/shell.js +11 -0
  151. package/lib/types/dsh-adapter/contract.d.ts +4 -4
  152. package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
  153. package/lib/types/dsh-adapter/contract.js +15 -6
  154. package/lib/types/dsh-adapter/ide-channel.d.ts +224 -0
  155. package/lib/types/dsh-adapter/ide-channel.d.ts.map +1 -0
  156. package/lib/types/dsh-adapter/ide-channel.js +556 -0
  157. package/lib/types/dsh-adapter/index.d.ts +18 -3
  158. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  159. package/lib/types/dsh-adapter/index.js +32 -5
  160. package/lib/types/dsh-adapter/plugin.d.ts +5 -2
  161. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  162. package/lib/types/dsh-adapter/plugin.js +173 -42
  163. package/lib/types/dsh-adapter/preset-resolution.d.ts +8 -0
  164. package/lib/types/dsh-adapter/preset-resolution.d.ts.map +1 -1
  165. package/lib/types/dsh-adapter/preset-resolution.js +10 -0
  166. package/lib/types/dsh-adapter/session-mount-heartbeat.d.ts +53 -0
  167. package/lib/types/dsh-adapter/session-mount-heartbeat.d.ts.map +1 -0
  168. package/lib/types/dsh-adapter/session-mount-heartbeat.js +100 -0
  169. package/lib/types/dsh-adapter/sessionTree.d.ts.map +1 -1
  170. package/lib/types/dsh-adapter/sessionTree.js +3 -2
  171. package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
  172. package/lib/types/dsh-adapter/subagents.js +6 -5
  173. package/lib/types/dsh-adapter/workspaces.d.ts +46 -3
  174. package/lib/types/dsh-adapter/workspaces.d.ts.map +1 -1
  175. package/lib/types/dsh-adapter/workspaces.js +116 -0
  176. package/lib/types/effortPrefs.d.ts +15 -3
  177. package/lib/types/effortPrefs.d.ts.map +1 -1
  178. package/lib/types/effortPrefs.js +37 -3
  179. package/lib/types/homePrefs.d.ts +15 -0
  180. package/lib/types/homePrefs.d.ts.map +1 -0
  181. package/lib/types/homePrefs.js +53 -0
  182. package/lib/types/i18n.d.ts +257 -296
  183. package/lib/types/i18n.d.ts.map +1 -1
  184. package/lib/types/i18n.js +78 -84
  185. package/lib/types/ink/colorize.d.ts +10 -0
  186. package/lib/types/ink/colorize.d.ts.map +1 -1
  187. package/lib/types/ink/colorize.js +21 -0
  188. package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
  189. package/lib/types/ink/components/ScrollBox.js +12 -1
  190. package/lib/types/ink/dom.d.ts +1 -0
  191. package/lib/types/ink/dom.d.ts.map +1 -1
  192. package/lib/types/ink/hooks/use-copy-on-select.d.ts +5 -1
  193. package/lib/types/ink/hooks/use-copy-on-select.d.ts.map +1 -1
  194. package/lib/types/ink/hooks/use-copy-on-select.js +15 -1
  195. package/lib/types/ink/ink.d.ts.map +1 -1
  196. package/lib/types/ink/ink.js +36 -1
  197. package/lib/types/ink/output.d.ts +10 -1
  198. package/lib/types/ink/output.d.ts.map +1 -1
  199. package/lib/types/ink/output.js +109 -17
  200. package/lib/types/ink/render-border.d.ts +6 -1
  201. package/lib/types/ink/render-border.d.ts.map +1 -1
  202. package/lib/types/ink/render-border.js +25 -9
  203. package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
  204. package/lib/types/ink/render-node-to-output.js +48 -6
  205. package/lib/types/ink/renderer.d.ts +6 -0
  206. package/lib/types/ink/renderer.d.ts.map +1 -1
  207. package/lib/types/ink/renderer.js +2 -1
  208. package/lib/types/ink/selection.d.ts +60 -0
  209. package/lib/types/ink/selection.d.ts.map +1 -1
  210. package/lib/types/ink/selection.js +129 -0
  211. package/lib/types/ink/sixel-graphics.d.ts +19 -1
  212. package/lib/types/ink/sixel-graphics.d.ts.map +1 -1
  213. package/lib/types/ink/sixel-graphics.js +124 -12
  214. package/lib/types/ink/terminal-image.d.ts +18 -1
  215. package/lib/types/ink/terminal-image.d.ts.map +1 -1
  216. package/lib/types/ink/terminal.d.ts +17 -0
  217. package/lib/types/ink/terminal.d.ts.map +1 -1
  218. package/lib/types/ink/terminal.js +20 -1
  219. package/lib/types/screens/Chat.d.ts +17 -1
  220. package/lib/types/screens/Chat.d.ts.map +1 -1
  221. package/lib/types/screens/Chat.js +284 -72
  222. package/lib/types/screens/SessionSupervisor.d.ts +84 -0
  223. package/lib/types/screens/SessionSupervisor.d.ts.map +1 -0
  224. package/lib/types/screens/SessionSupervisor.js +350 -0
  225. package/lib/types/screens/SessionTree.js +2 -2
  226. package/lib/types/screens/Settings.d.ts.map +1 -1
  227. package/lib/types/screens/Settings.js +32 -9
  228. package/lib/types/screens/StatusLine.d.ts +13 -0
  229. package/lib/types/screens/StatusLine.d.ts.map +1 -1
  230. package/lib/types/screens/StatusLine.js +36 -17
  231. package/lib/types/screens/StatusMetrics.d.ts +79 -22
  232. package/lib/types/screens/StatusMetrics.d.ts.map +1 -1
  233. package/lib/types/screens/StatusMetrics.js +153 -88
  234. package/lib/types/screens/sessionSupervisor/model.d.ts +83 -0
  235. package/lib/types/screens/sessionSupervisor/model.d.ts.map +1 -0
  236. package/lib/types/screens/sessionSupervisor/model.js +69 -0
  237. package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts +120 -0
  238. package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts.map +1 -0
  239. package/lib/types/screens/sessionSupervisor/useSessionSupervisor.js +593 -0
  240. package/lib/types/sessionMounts.d.ts +272 -0
  241. package/lib/types/sessionMounts.d.ts.map +1 -0
  242. package/lib/types/sessionMounts.js +706 -0
  243. package/lib/types/sessions/format.d.ts.map +1 -1
  244. package/lib/types/sessions/format.js +4 -1
  245. package/lib/types/sessions/resumeFailure.d.ts +38 -0
  246. package/lib/types/sessions/resumeFailure.d.ts.map +1 -0
  247. package/lib/types/sessions/resumeFailure.js +58 -0
  248. package/lib/types/terminal-utils/mermaid.d.ts +44 -0
  249. package/lib/types/terminal-utils/mermaid.d.ts.map +1 -0
  250. package/lib/types/terminal-utils/mermaid.js +83 -0
  251. package/lib/types/tips.d.ts.map +1 -1
  252. package/lib/types/tips.js +175 -19
  253. package/lib/types/tuiDisplayPrefs.d.ts +7 -9
  254. package/lib/types/tuiDisplayPrefs.d.ts.map +1 -1
  255. package/lib/types/tuiDisplayPrefs.js +31 -28
  256. package/lib/types/update.d.ts +61 -0
  257. package/lib/types/update.d.ts.map +1 -1
  258. package/lib/types/update.js +194 -0
  259. package/lib/types/utils/imageResize.d.ts +90 -0
  260. package/lib/types/utils/imageResize.d.ts.map +1 -0
  261. package/lib/types/utils/imageResize.js +272 -0
  262. package/lib/types/utils/keymap.d.ts +5 -1
  263. package/lib/types/utils/keymap.d.ts.map +1 -1
  264. package/lib/types/utils/keymap.js +23 -0
  265. package/lib/types/workspaces.d.ts +2 -1
  266. package/lib/types/workspaces.d.ts.map +1 -1
  267. package/lib/types/workspaces.js +1 -1
  268. package/node_modules/@deepseek-harness-tui/dsh-auth/package.json +10 -10
  269. package/package.json +120 -103
  270. package/presets/liangshen/.dsh-tui-managed.json +1 -1
  271. package/presets/liangshen/agent.cordis.yml +15 -0
  272. package/lib/types/screens/AgentView.d.ts +0 -23
  273. package/lib/types/screens/AgentView.d.ts.map +0 -1
  274. package/lib/types/screens/AgentView.js +0 -546
  275. package/lib/types/screens/SessionBrowser.d.ts +0 -42
  276. package/lib/types/screens/SessionBrowser.d.ts.map +0 -1
  277. package/lib/types/screens/SessionBrowser.js +0 -911
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../../../src/adapter/ports/workspace.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../../../src/adapter/ports/workspace.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-driver.d.ts","sourceRoot":"","sources":["../../../../src/adapter/upstream/workspace-driver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAqB,MAAM,gBAAgB,CAAA;AAClE,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,aAAa,CAAA;AA0DtE,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,CAqBjE;AA8FD,eAAO,MAAM,eAAe,EAAE,cAY7B,CAAA"}
1
+ {"version":3,"file":"workspace-driver.d.ts","sourceRoot":"","sources":["../../../../src/adapter/upstream/workspace-driver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAqB,MAAM,gBAAgB,CAAA;AAClE,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,aAAa,CAAA;AA2DtE,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,CAqBjE;AAqGD,eAAO,MAAM,eAAe,EAAE,cAY7B,CAAA"}
@@ -22,6 +22,7 @@ const WORKSPACE_FEATURES = Object.freeze([
22
22
  'host.workspaces.commands',
23
23
  'host.workspaces.commandShell',
24
24
  'host.workspaces.rename',
25
+ 'host.workspaces.remove',
25
26
  'host.workspaces.runCommand',
26
27
  ]);
27
28
  function serviceEvidence(id) {
@@ -157,8 +158,14 @@ async function verifyWorkspaceLive(ctx) {
157
158
  out.push(degradedFeature('host.workspaces.commands', [serviceEvidence('tuiWorkspaces'), probeEvidence('tuiWorkspaces.commands()', errorText(error))], 'tuiWorkspaces.commands() live-probe'));
158
159
  }
159
160
  // No safe auto-reversible P3 probe for command-shell execution, renaming,
160
- // or provider command execution. These stay feature-degraded.
161
- for (const feature of ['host.workspaces.commandShell', 'host.workspaces.rename', 'host.workspaces.runCommand']) {
161
+ // ledger registration/removal, or provider command execution: each mutates
162
+ // durable state a probe cannot undo. These stay feature-degraded.
163
+ for (const feature of [
164
+ 'host.workspaces.commandShell',
165
+ 'host.workspaces.rename',
166
+ 'host.workspaces.remove',
167
+ 'host.workspaces.runCommand',
168
+ ]) {
162
169
  out.push(degradedFeature(feature, [serviceEvidence('tuiWorkspaces')], `${feature}.live-probe`));
163
170
  }
164
171
  return out;
@@ -171,6 +178,7 @@ function createWorkspacePort(host) {
171
178
  describe: cwd => host.describe(cwd),
172
179
  commandShell: cwd => host.commandShell(cwd),
173
180
  rename: (cwd, title) => host.rename(cwd, title),
181
+ remove: cwd => host.remove(cwd),
174
182
  commands: () => host.commands(),
175
183
  runCommand: (name, input, cwd, signal) => host.runCommand(name, input, cwd, signal),
176
184
  });
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAA;AAChH,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAA;AAWhH,+EAA+E;AAC/E,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,KAAK,SAAS,qBAAqB,EAAE,CAAA;AAEpG;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,YAAY,EA+DxC,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,YAAY,EAElD,CAAA;AAED,0DAA0D;AAC1D,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAEpD,CAAA;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAG1D;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAIhG;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,GACX;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAIhD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,SAAS,YAAY,EAAmB,GAC7C,OAAO,CAKT;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,SAAS,YAAY,EAAmB,GAC7C,YAAY,EAAE,CAKhB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,SAAS,YAAY,EAAmB,EAC/C,QAAQ,GAAE,eAA0B,GACnC,iBAAiB,EAAE,CAuCrB"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAA;AAChH,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAA;AAWhH,+EAA+E;AAC/E,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,KAAK,SAAS,qBAAqB,EAAE,CAAA;AAEpG;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,YAAY,EAgExC,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,YAAY,EAElD,CAAA;AAED,0DAA0D;AAC1D,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAEpD,CAAA;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAG1D;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAIhG;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,GACX;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAIhD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,SAAS,YAAY,EAAmB,GAC7C,OAAO,CAKT;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,SAAS,YAAY,EAAmB,GAC7C,YAAY,EAAE,CAKhB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,SAAS,YAAY,EAAmB,EAC/C,QAAQ,GAAE,eAA0B,GACnC,iBAAiB,EAAE,CAuCrB"}
@@ -74,6 +74,7 @@ export const LOCAL_COMMANDS = [
74
74
  { name: 'terminal-setup', description: 'Show terminal setup instructions' },
75
75
  { name: 'connect', description: 'Connect to a remote machine' },
76
76
  { name: 'workspace', description: 'Resume, rename, or open a workspace' },
77
+ { name: 'home', description: 'Workspace home: manage workspaces and open their sessions' },
77
78
  // Help / exit
78
79
  { name: 'help', description: 'Show shortcuts and commands' },
79
80
  { name: 'tips', description: 'Show usage tips and shortcuts' },
@@ -3,11 +3,19 @@ import type { Color } from '../ink/styles.js';
3
3
  import { type ContextSegments } from '../screens/StatusMetrics.js';
4
4
  /**
5
5
  * The hoverable JSX twin of `renderContextBar`: the same segmented context
6
- * bar (same largest-remainder column split, same label fallback ladder,
7
- * same free-segment readout), rendered as one Box per segment so each
8
- * carries its own mouse handlers. Hovering a segment reports its key (or
9
- * `free`) upward through `onHover`; the footer turns that into a detail
10
- * readout on its supplemental row.
6
+ * bar (same largest-remainder column split, same free-segment readout),
7
+ * rendered as one Box per segment so the bar can react to the pointer.
8
+ *
9
+ * A used segment is its fill color and nothing else — no label (community
10
+ * feedback on the old `s`/`p`/`t` letters: they read as noise on a row that is
11
+ * already decorative, and they never fit the narrow segments anyway). The only
12
+ * text left is the free segment's right-aligned usage readout.
13
+ *
14
+ * ONE hover target for the whole bar rather than one per segment. The colors
15
+ * are named where there is room for words: hovering anywhere on the bar parks
16
+ * the full per-content-type breakdown on the footer's supplemental row. A
17
+ * per-segment target also made every crossing of the five fills an
18
+ * enter+leave pair, hence a footer re-render per crossing.
11
19
  *
12
20
  * The component path follows the ANSI path's geometry: this exists so the bar
13
21
  * can react to the pointer, not to restyle it.
@@ -26,8 +34,8 @@ export declare function ContextBarView({ segments, usedTokens, contextWindow, wi
26
34
  freeFill: Color;
27
35
  freeText: Color;
28
36
  };
29
- /** Hover signal: a segment key (`system`…`tools`), `free`, or null on
37
+ /** Hover signal: true while the pointer is anywhere on the bar, false on
30
38
  * leave. Absent handlers render a static bar (tests, headless embeds). */
31
- onHover?: (segment: string | null) => void;
39
+ onHover?: (hovered: boolean) => void;
32
40
  }): React.ReactNode;
33
41
  //# sourceMappingURL=ContextBarView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ContextBarView.d.ts","sourceRoot":"","sources":["../../../src/components/ContextBarView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAOL,KAAK,eAAe,EACrB,MAAM,6BAA6B,CAAA;AAOpC;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,UAAU,EACV,aAAa,EACb,KAAK,EACL,MAAM,EACN,OAAO,GACR,EAAE;IACD,oCAAoC;IACpC,QAAQ,EAAE,eAAe,CAAA;IACzB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAA;IAClB,yCAAyC;IACzC,aAAa,EAAE,MAAM,CAAA;IACrB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,MAAM,CAAC,EAAE;QAAE,QAAQ,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,KAAK,CAAA;KAAE,CAAA;IAC7C;+EAC2E;IAC3E,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;CAC3C,GAAG,KAAK,CAAC,SAAS,CAgElB"}
1
+ {"version":3,"file":"ContextBarView.d.ts","sourceRoot":"","sources":["../../../src/components/ContextBarView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAQL,KAAK,eAAe,EACrB,MAAM,6BAA6B,CAAA;AAEpC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,UAAU,EACV,aAAa,EACb,KAAK,EACL,MAAM,EACN,OAAO,GACR,EAAE;IACD,oCAAoC;IACpC,QAAQ,EAAE,eAAe,CAAA;IACzB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAA;IAClB,yCAAyC;IACzC,aAAa,EAAE,MAAM,CAAA;IACrB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,MAAM,CAAC,EAAE;QAAE,QAAQ,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,KAAK,CAAA;KAAE,CAAA;IAC7C;+EAC2E;IAC3E,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;CACrC,GAAG,KAAK,CAAC,SAAS,CA2DlB"}
@@ -1,17 +1,21 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Box, Text } from '../ui.js';
3
- import { USED_SEGMENTS, allocateBarColumns, centeredText, chooseLabel, composeFreeSegmentText, formatTokens, } from '../screens/StatusMetrics.js';
4
- /** Free-segment colors matching StatusMetrics' dark-theme defaults. */
5
- const FREE_FILL = '#E8E8E8';
6
- const FREE_TEXT = '#4A4A4A';
7
- const USED_TEXT = '#FFFFFF';
3
+ import { FREE_SEGMENT_FILL, FREE_SEGMENT_TEXT, USED_SEGMENTS, allocateBarColumns, contextBarReadout, contextPressureStep, rightAlignBarText, } from '../screens/StatusMetrics.js';
8
4
  /**
9
5
  * The hoverable JSX twin of `renderContextBar`: the same segmented context
10
- * bar (same largest-remainder column split, same label fallback ladder,
11
- * same free-segment readout), rendered as one Box per segment so each
12
- * carries its own mouse handlers. Hovering a segment reports its key (or
13
- * `free`) upward through `onHover`; the footer turns that into a detail
14
- * readout on its supplemental row.
6
+ * bar (same largest-remainder column split, same free-segment readout),
7
+ * rendered as one Box per segment so the bar can react to the pointer.
8
+ *
9
+ * A used segment is its fill color and nothing else — no label (community
10
+ * feedback on the old `s`/`p`/`t` letters: they read as noise on a row that is
11
+ * already decorative, and they never fit the narrow segments anyway). The only
12
+ * text left is the free segment's right-aligned usage readout.
13
+ *
14
+ * ONE hover target for the whole bar rather than one per segment. The colors
15
+ * are named where there is room for words: hovering anywhere on the bar parks
16
+ * the full per-content-type breakdown on the footer's supplemental row. A
17
+ * per-segment target also made every crossing of the five fills an
18
+ * enter+leave pair, hence a footer re-render per crossing.
15
19
  *
16
20
  * The component path follows the ANSI path's geometry: this exists so the bar
17
21
  * can react to the pointer, not to restyle it.
@@ -22,25 +26,25 @@ export function ContextBarView({ segments, usedTokens, contextWindow, width, col
22
26
  const freeTokens = Math.max(0, contextWindow - usedTokens);
23
27
  const values = [...USED_SEGMENTS.map(segment => segments[segment.key]), freeTokens];
24
28
  const columns = allocateBarColumns(values, width);
25
- const percent = `${((usedTokens / contextWindow) * 100).toFixed(1)}%`;
26
- const total = `${formatTokens(usedTokens)}/${formatTokens(contextWindow)}`;
27
- const free = formatTokens(contextWindow - usedTokens);
29
+ // The readout is the bar's only text, and its one pressure signal: amber
30
+ // from 80% occupancy, red from 95% (the footer's shared thresholds). The
31
+ // theme key wins over the free-text color, which stays for comfortable
32
+ // occupancy.
33
+ const readout = contextBarReadout(usedTokens, contextWindow);
34
+ const pressure = contextPressureStep((usedTokens / contextWindow) * 100);
28
35
  const nodes = [];
29
36
  for (const [index, segment] of USED_SEGMENTS.entries()) {
30
37
  const segmentWidth = columns[index] ?? 0;
31
38
  if (segmentWidth <= 0)
32
39
  continue;
33
- const label = chooseLabel(segment.labels, segmentWidth);
34
- nodes.push(_jsx(Box, { width: segmentWidth, height: 1, flexShrink: 0, backgroundColor: segment.color, onMouseEnter: onHover === undefined ? undefined : () => onHover(segment.key), onMouseLeave: onHover === undefined ? undefined : () => onHover(null), children: _jsx(Text, { color: USED_TEXT, children: label.length > 0 ? centeredText(label, segmentWidth) : ' '.repeat(segmentWidth) }) }, segment.key));
40
+ // Childless on purpose: the renderer fills a node's own rect with its
41
+ // backgroundColor, so the segment is a pure colored block (render-node-to-
42
+ // output's ownBackgroundColor fill).
43
+ nodes.push(_jsx(Box, { width: segmentWidth, height: 1, flexShrink: 0, backgroundColor: segment.color }, segment.key));
35
44
  }
36
45
  const freeWidth = columns[USED_SEGMENTS.length] ?? 0;
37
46
  if (freeWidth > 0) {
38
- nodes.push(_jsx(Box, { width: freeWidth, height: 1, flexShrink: 0, backgroundColor: colors?.freeFill ?? FREE_FILL, onMouseEnter: onHover === undefined ? undefined : () => onHover('free'), onMouseLeave: onHover === undefined ? undefined : () => onHover(null), children: _jsx(Text, { color: colors?.freeText ?? FREE_TEXT, children: composeFreeSegmentText([
39
- `ctx ${total} ${percent} ${free}`,
40
- `${total} ${percent} ${free}`,
41
- `${total} ${percent}`,
42
- percent,
43
- ], freeWidth) }) }, "free"));
47
+ nodes.push(_jsx(Box, { width: freeWidth, height: 1, flexShrink: 0, backgroundColor: colors?.freeFill ?? FREE_SEGMENT_FILL, children: _jsx(Text, { color: pressure ?? colors?.freeText ?? FREE_SEGMENT_TEXT, wrap: "truncate", children: rightAlignBarText(readout, freeWidth) }) }, "free"));
44
48
  }
45
- return (_jsx(Box, { flexDirection: "row", flexShrink: 0, width: width, children: nodes }));
49
+ return (_jsx(Box, { flexDirection: "row", flexShrink: 0, width: width, onMouseEnter: onHover === undefined ? undefined : () => onHover(true), onMouseLeave: onHover === undefined ? undefined : () => onHover(false), children: nodes }));
46
50
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ImagePreviewOverlay.d.ts","sourceRoot":"","sources":["../../../src/components/ImagePreviewOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAQzB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAA;AAiC1E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,OAAO,EACP,MAAM,EACN,KAAK,EACL,UAAU,GACX,EAAE;IACD,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAA;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACrE;mCAC+B;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE;QACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;QACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;QACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,IAAI,CAAA;QAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,CAAA;KAC5B,CAAA;CACF,GAAG,KAAK,CAAC,SAAS,CAkNlB"}
1
+ {"version":3,"file":"ImagePreviewOverlay.d.ts","sourceRoot":"","sources":["../../../src/components/ImagePreviewOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAQzB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAA;AAiC1E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,OAAO,EACP,MAAM,EACN,KAAK,EACL,UAAU,GACX,EAAE;IACD,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAA;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACrE;mCAC+B;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE;QACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;QACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;QACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,IAAI,CAAA;QAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,CAAA;KAC5B,CAAA;CACF,GAAG,KAAK,CAAC,SAAS,CAwNlB"}
@@ -106,10 +106,16 @@ export function ImagePreviewOverlay({ image, onClose, region, title, navigation,
106
106
  const graphicsFit = columns >= MIN_GRAPHICS_COLUMNS && rows >= MIN_GRAPHICS_ROWS;
107
107
  const graphicsAvailable = useTerminalImages(graphicsFit);
108
108
  const cell = useTerminalImageCellSize();
109
- const [view, setView] = React.useState({ image, zoom: 0 });
110
- React.useEffect(() => { setView(previous => previous.image === image ? previous : { image, zoom: 0 }); }, [image]);
111
- const zoom = view.image === image && cell && graphicsAvailable ? view.zoom : 0;
112
- const setZoom = (value) => setView({ image, zoom: value });
109
+ // Zoom belongs to the attachment (`image.id`), not to the facade object:
110
+ // a caller may hand a fresh facade for the same attachment on every
111
+ // render, and keying on the object would reset the view each commit (#885).
112
+ const imageId = image.id;
113
+ const [view, setView] = React.useState({ imageId, zoom: 0 });
114
+ React.useEffect(() => {
115
+ setView(previous => previous.imageId === imageId ? previous : { imageId, zoom: 0 });
116
+ }, [imageId]);
117
+ const zoom = view.imageId === imageId && cell && graphicsAvailable ? view.zoom : 0;
118
+ const setZoom = (value) => setView({ imageId, zoom: value });
113
119
  const maxImageColumns = Math.max(1, Math.min(columns - 2, Math.floor(columns * PREVIEW_MAX_WIDTH_RATIO)) - CARD_CHROME_COLS);
114
120
  const navigationRows = navigation && navigation.total > 1 && rows >= 6 ? 1 : 0;
115
121
  const maxImageRows = Math.max(1, Math.min(rows - 2, Math.floor(rows * PREVIEW_MAX_HEIGHT_RATIO)) - CARD_CHROME_ROWS - 1 - navigationRows);
@@ -239,7 +245,7 @@ function OriginalImageLink({ image, label }) {
239
245
  React.useEffect(() => {
240
246
  setStatus('idle');
241
247
  return () => { pending.current?.abort(); pending.current = null; };
242
- }, [image]);
248
+ }, [image.id]);
243
249
  const tooltip = useTooltip(image.path ?? image.name ?? t('image-preview-open-original'));
244
250
  return _jsx(Box, { height: 1, flexShrink: 0, ...tooltip, onDragStart: stopControlDrag, onClick: event => {
245
251
  event.stopImmediatePropagation();
@@ -1,8 +1,11 @@
1
1
  import React from 'react';
2
+ import { type Token } from 'marked';
2
3
  /**
3
4
  * Markdown 渲染组件:marked 分词 + ANSI 格式化。
4
5
  *
5
- * 表格 token 交给 MarkdownTable 渲染为带边框的 flexbox 布局;
6
+ * 表格 token 交给 MarkdownTable 渲染为带边框的 flexbox 布局,mermaid
7
+ * 代码块交给 MermaidDiagram 画成 box-drawing 图(两者都需要终端宽度,
8
+ * 所以是独立节点而不是 ANSI 字符串);
6
9
  * 其余块级内容由 formatToken 转成 ANSI 字符串,按块边界分批放进
7
10
  * Text(只去整段首尾空白)。代码块高亮由 cli-highlight 异步提供,
8
11
  * 加载完成后自动触发一次重渲染。无 markdown 语法的纯文本走快速
@@ -15,6 +18,13 @@ type Props = {
15
18
  /** 为 false 时跳过 token 缓存(流式尾部的内容逐帧变化,缓存必然失效) */
16
19
  cacheTokens?: boolean;
17
20
  };
21
+ /**
22
+ * Tokens that render as their own layout node (a width-aware component)
23
+ * instead of joining the ANSI text run. StreamingMarkdown consults the same
24
+ * predicate: a standalone node has a fixed one-row gap to its neighbours
25
+ * rather than the newline-derived spacing of text blocks.
26
+ */
27
+ export declare function isStandaloneToken(token: Token): boolean;
18
28
  /**
19
29
  * 混合渲染 Markdown 内容:表格用带边框的 flexbox 组件,其余内容由
20
30
  * formatToken 生成 ANSI 字符串放入 Text。高亮对象异步就绪后自动刷新。
@@ -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;AA4ID;;;;;;;;;;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;AACzB,OAAO,EAAU,KAAK,KAAK,EAAe,MAAM,QAAQ,CAAA;AAQxD;;;;;;;;;;GAUG;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;AA4ED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAEvD;AAgFD;;;;;;;;;;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"}
@@ -4,7 +4,9 @@ import { marked } from 'marked';
4
4
  import { Box, Text } from '../ui.js';
5
5
  import { configureMarked, formatToken, stripPromptXMLTags } from '../terminal-utils/markdown.js';
6
6
  import { getCliHighlightPromise } from '../terminal-utils/cliHighlight.js';
7
+ import { isMermaidLang } from '../terminal-utils/mermaid.js';
7
8
  import { MarkdownTable } from './MarkdownTable.js';
9
+ import { MermaidDiagram } from './MermaidDiagram.js';
8
10
  // ---- token 缓存 ----
9
11
  //
10
12
  // marked.lexer 在组件重挂载时是最贵的开销;消息内容不可变,相同文本
@@ -73,8 +75,20 @@ function lexWithCache(content, allowCache) {
73
75
  return tokens;
74
76
  }
75
77
  /**
76
- * 把 lexer 产出的 token 列表转成 React 节点序列:table 独立渲染,
77
- * 其余 token 的 ANSI 文本按完整块分批拼接,只去整段首尾空白。
78
+ * Tokens that render as their own layout node (a width-aware component)
79
+ * instead of joining the ANSI text run. StreamingMarkdown consults the same
80
+ * predicate: a standalone node has a fixed one-row gap to its neighbours
81
+ * rather than the newline-derived spacing of text blocks.
82
+ */
83
+ export function isStandaloneToken(token) {
84
+ return token.type === 'table' || isMermaidToken(token);
85
+ }
86
+ function isMermaidToken(token) {
87
+ return token.type === 'code' && isMermaidLang(token.lang);
88
+ }
89
+ /**
90
+ * 把 lexer 产出的 token 列表转成 React 节点序列:table 与 mermaid 块独立
91
+ * 渲染,其余 token 的 ANSI 文本按完整块分批拼接,只去整段首尾空白。
78
92
  */
79
93
  function renderTokensToNodes(tokens, highlight, dimColor) {
80
94
  const nodes = [];
@@ -108,6 +122,10 @@ function renderTokensToNodes(tokens, highlight, dimColor) {
108
122
  flushAnsiText();
109
123
  nodes.push(_jsx(MarkdownTable, { token: token, highlight: highlight }, nodes.length));
110
124
  }
125
+ else if (isMermaidToken(token)) {
126
+ flushAnsiText();
127
+ nodes.push(_jsx(MermaidDiagram, { token: token, highlight: highlight, dimColor: dimColor }, nodes.length));
128
+ }
111
129
  else {
112
130
  ansiText += formatToken(token, 0, null, null, highlight);
113
131
  // A top-level token boundary keeps inline formatting and code fences
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import type { Tokens } from 'marked';
3
+ import type { CliHighlight } from '../terminal-utils/cliHighlight.js';
4
+ type Props = {
5
+ token: Tokens.Code;
6
+ highlight: CliHighlight | null;
7
+ dimColor: boolean;
8
+ /** Override terminal width (useful for testing). */
9
+ forceWidth?: number;
10
+ };
11
+ export declare function MermaidDiagram({ token, highlight, dimColor, forceWidth }: Props): React.ReactNode;
12
+ export {};
13
+ //# sourceMappingURL=MermaidDiagram.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MermaidDiagram.d.ts","sourceRoot":"","sources":["../../../src/components/MermaidDiagram.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAOpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAA;AA6BrE,KAAK,KAAK,GAAG;IACX,KAAK,EAAE,MAAM,CAAC,IAAI,CAAA;IAClB,SAAS,EAAE,YAAY,GAAG,IAAI,CAAA;IAC9B,QAAQ,EAAE,OAAO,CAAA;IACjB,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAmBD,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CAgCjG"}
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { Box, Text } from '../ui.js';
4
+ import { useTerminalSize } from '../ink/hooks/use-terminal-size.js';
5
+ import { stringWidth } from '../ink/stringWidth.js';
6
+ import { getTheme } from '../theme.js';
7
+ import { useTheme } from './design-system/ThemeProvider.js';
8
+ import { formatToken } from '../terminal-utils/markdown.js';
9
+ import { getMermaidEnginePromise, paintMermaidArt, peekMermaidEngine, renderMermaid, } from '../terminal-utils/mermaid.js';
10
+ import { getMermaidDiagrams, subscribeMermaidDiagrams } from '../tuiDisplayPrefs.js';
11
+ import { t } from '../i18n.js';
12
+ /**
13
+ * A ```mermaid fence rendered as Unicode box-drawing art.
14
+ *
15
+ * The art is shown on its own, indented like a code-block body; the
16
+ * ```mermaid fence line is a code-block affordance and a diagram needs none.
17
+ * Whenever there is no art to show (setting off, engine still loading or
18
+ * unavailable, unsupported diagram type, nothing parsed, or the layout is
19
+ * wider than the viewport) the block falls back to the ordinary code-block
20
+ * rendering — fence line included, because that IS a code block — and a
21
+ * too-wide diagram adds a caption with the width it needs.
22
+ */
23
+ /** Same viewport slack MarkdownTable keeps for gutters and message insets. */
24
+ const SAFETY_MARGIN = 4;
25
+ const INDENT = ' ';
26
+ /** Column budget the indent takes: display width, not string length. */
27
+ const INDENT_WIDTH = stringWidth(INDENT);
28
+ /** The lazily loaded engine: available on first render once any earlier
29
+ * diagram loaded it, otherwise resolved after mount. */
30
+ function useMermaidEngine() {
31
+ const [engine, setEngine] = React.useState(() => peekMermaidEngine() ?? null);
32
+ React.useEffect(() => {
33
+ if (engine !== null)
34
+ return;
35
+ let mounted = true;
36
+ void getMermaidEnginePromise().then(loaded => {
37
+ if (mounted && loaded !== null)
38
+ setEngine(loaded);
39
+ });
40
+ return () => {
41
+ mounted = false;
42
+ };
43
+ }, [engine]);
44
+ return engine;
45
+ }
46
+ export function MermaidDiagram({ token, highlight, dimColor, forceWidth }) {
47
+ const enabled = React.useSyncExternalStore(subscribeMermaidDiagrams, getMermaidDiagrams);
48
+ const engine = useMermaidEngine();
49
+ const { columns } = useTerminalSize();
50
+ const [themeName] = useTheme();
51
+ const theme = getTheme(themeName);
52
+ const width = Math.max(0, forceWidth ?? columns);
53
+ // Layout is width-independent (the engine draws at natural size), so a
54
+ // resize or theme switch only re-paints; streaming changes token.text
55
+ // every frame and re-lays out — sub-millisecond for a warm engine.
56
+ const art = React.useMemo(() => (enabled && engine !== null ? renderMermaid(engine, token.text) : null), [enabled, engine, token.text]);
57
+ if (art === null || art.width > width - INDENT_WIDTH - SAFETY_MARGIN) {
58
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: dimColor, children: formatToken(token, 0, null, null, highlight).trimEnd() }), art !== null && (_jsx(Text, { color: "inactive", children: INDENT + t('mermaid-too-wide', { width: art.width }) }))] }));
59
+ }
60
+ return (_jsx(Text, { dimColor: dimColor, children: paintMermaidArt(art, theme).map(line => INDENT + line).join('\n') }));
61
+ }
@@ -5,7 +5,26 @@ import type { TranscriptImage } from '../dsh-adapter/transcript-images.js';
5
5
  import type { DOMElement } from '../ink/dom.js';
6
6
  import { type TimelineSnapshot } from '../ink/timeline-rail.js';
7
7
  import type { ToolBackground } from '../tuiDisplayPrefs.js';
8
- export declare function MessageList({ rows, expanded, expandedRows, selectedId, onToggleRow, streamViewToggledRows, onToggleStreamView, model, diffLayout, thinkingFold, toolBackground, foldTerminalCommand, smoothStreaming, activityFrames, showAll, onToggleAll, onLoadOlder, thinkingVisible, historyPaintEnabled, registerRowRef, scrollHandle, forceMountRowId, newSinceRowId, onUnseenCount, onTimeline, failureHintRowId, failureHint, onOpenSubagent, onOpenJobs, onOpenFile, onPreviewImage, suppressImageGraphics, }: {
8
+ /**
9
+ * Display form of an IDE-selection path (T-FIX-01): when the path lives
10
+ * under the session cwd, strip that prefix so the indicator line reads
11
+ * `src/a.ts` instead of a long absolute path (UAT finding — the extension
12
+ * anchors at its workspace root while the TUI session cwd is the git
13
+ * worktree root, so raw paths are long and redundant). Pure display layer:
14
+ * the `<attached-file>` block keeps its absolute path for the model.
15
+ *
16
+ * Comparison reuses normalizeIdePath (the ide-channel lock matcher's
17
+ * normalizer: backslashes folded to forward slashes, trailing slashes
18
+ * stripped, case folded on case-insensitive filesystems) so Windows forms
19
+ * like `d:/x` vs `D:\X` still match. Prefix hits return the remainder
20
+ * (`src/a.ts`); everything else — outside cwd, empty/undefined cwd, or the
21
+ * path being the cwd itself — returns the input unchanged. NOT basename:
22
+ * that would drop the directory context and collide on same-named files.
23
+ * `caseInsensitive` is parameterized so verifiers can pin either mode on
24
+ * any host.
25
+ */
26
+ export declare function displaySelectionPath(path: string, sessionCwd: string | undefined, caseInsensitive?: boolean): string;
27
+ export declare function MessageList({ rows, expanded, expandedRows, selectedId, onToggleRow, streamViewToggledRows, onToggleStreamView, model, diffLayout, thinkingFold, toolBackground, foldTerminalCommand, smoothStreaming, activityFrames, showAll, onToggleAll, onLoadOlder, thinkingVisible, historyPaintEnabled, registerRowRef, scrollHandle, forceMountRowId, newSinceRowId, onUnseenCount, onTimeline, failureHintRowId, failureHint, onOpenSubagent, onOpenJobs, onOpenFile, sessionCwd, onPreviewImage, suppressImageGraphics, }: {
9
28
  rows: readonly ChatRow[];
10
29
  expanded: boolean;
11
30
  expandedRows: ReadonlySet<number>;
@@ -93,6 +112,12 @@ export declare function MessageList({ rows, expanded, expandedRows, selectedId,
93
112
  onOpenJobs?: () => void;
94
113
  /** 点击工具卡内的文件路径(打开文件操作菜单)。 */
95
114
  onOpenFile?: (path: string) => void;
115
+ /** Session working directory (fs path, `channel.cwd`): the IDE-selection
116
+ * indicator line strips this prefix for display (T-FIX-01). Optional —
117
+ * repro/verify harnesses that predate the field render unchanged with
118
+ * raw paths. The real fs cwd, NOT displayCwd: remote URI display forms
119
+ * can't prefix-match selection paths. */
120
+ sessionCwd?: string | undefined;
96
121
  /** 点击 transcript 缩略图(打开共享的大图预览 overlay)。 */
97
122
  onPreviewImage?: (image: TranscriptImage) => void;
98
123
  /** Modal preview owns the terminal-image frame budget while open. */
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/MessageList.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/MessageList.tsx"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  import React, { useState } from 'react';
3
3
  import { getLang, subscribeLang, t } from '../i18n.js';
4
4
  import { Box, Text, useTerminalSize } from '../ui.js';
5
+ import { normalizeIdePath } from '../dsh-adapter/ide-channel.js';
5
6
  import { Divider } from './design-system/Divider.js';
6
7
  import { UserPromptMessage } from './messages/UserPromptMessage.js';
7
8
  import { AssistantTextMessage } from './messages/AssistantTextMessage.js';
@@ -92,6 +93,48 @@ function revealDisplayLen(row, enabled) {
92
93
  }
93
94
  return row.text.length;
94
95
  }
96
+ /**
97
+ * Display form of an IDE-selection path (T-FIX-01): when the path lives
98
+ * under the session cwd, strip that prefix so the indicator line reads
99
+ * `src/a.ts` instead of a long absolute path (UAT finding — the extension
100
+ * anchors at its workspace root while the TUI session cwd is the git
101
+ * worktree root, so raw paths are long and redundant). Pure display layer:
102
+ * the `<attached-file>` block keeps its absolute path for the model.
103
+ *
104
+ * Comparison reuses normalizeIdePath (the ide-channel lock matcher's
105
+ * normalizer: backslashes folded to forward slashes, trailing slashes
106
+ * stripped, case folded on case-insensitive filesystems) so Windows forms
107
+ * like `d:/x` vs `D:\X` still match. Prefix hits return the remainder
108
+ * (`src/a.ts`); everything else — outside cwd, empty/undefined cwd, or the
109
+ * path being the cwd itself — returns the input unchanged. NOT basename:
110
+ * that would drop the directory context and collide on same-named files.
111
+ * `caseInsensitive` is parameterized so verifiers can pin either mode on
112
+ * any host.
113
+ */
114
+ export function displaySelectionPath(path, sessionCwd, caseInsensitive = process.platform === 'win32' || process.platform === 'darwin') {
115
+ if (sessionCwd === undefined || sessionCwd === '')
116
+ return path;
117
+ // Shared normalizer (lock matching uses the same): backslashes fold to
118
+ // forward slashes, trailing slashes stripped — transforms that preserve
119
+ // character positions, so slicing the canonical path at the cwd's length
120
+ // keeps the file's own casing in the displayed relative string. Folding
121
+ // decides only WHETHER the prefix matches, never what gets sliced off.
122
+ const pathNorm = normalizeIdePath(path, false);
123
+ const cwdNorm = normalizeIdePath(sessionCwd, false);
124
+ if (cwdNorm === '' || cwdNorm.length >= pathNorm.length)
125
+ return path;
126
+ // The POSIX root `/` is a prefix of every absolute path without a further
127
+ // separator — `/repo/file.ts` under cwd `/` displays as `repo/file.ts`.
128
+ // (A `startsWith('/' + '/')` check would never match; coderabbit review.)
129
+ if (cwdNorm === '/') {
130
+ return pathNorm.startsWith('/') ? pathNorm.slice(1) : path;
131
+ }
132
+ const foldedPath = normalizeIdePath(pathNorm, caseInsensitive);
133
+ const foldedCwd = normalizeIdePath(cwdNorm, caseInsensitive);
134
+ if (!foldedPath.startsWith(`${foldedCwd}/`))
135
+ return path;
136
+ return pathNorm.slice(cwdNorm.length + 1);
137
+ }
95
138
  /**
96
139
  * Per-kind layout signature PARTS: the O(1) identity of every input that
97
140
  * decides a row's rendered HEIGHT (see sigRef in MessageList). Fields are
@@ -113,7 +156,7 @@ function revealDisplayLen(row, enabled) {
113
156
  * (parts.slice()).
114
157
  */
115
158
  const signatureScratch = [];
116
- function signatureParts(row, columns, expanded, expandedRows, streamViewToggledRows, thinkingVisible, thinkingFold, diffLayout, foldTerminalCommand, model, failureHintRowId, failureHint, displayTextLen) {
159
+ function signatureParts(row, columns, expanded, expandedRows, streamViewToggledRows, thinkingVisible, thinkingFold, diffLayout, foldTerminalCommand, model, failureHintRowId, failureHint, displayTextLen, sessionCwd) {
117
160
  signatureScratch.length = 0;
118
161
  // Universal height inputs: width reflows every row; kind switches height
119
162
  // semantics wholesale; text length drives wrapping. `displayTextLen` is the
@@ -156,8 +199,26 @@ function signatureParts(row, columns, expanded, expandedRows, streamViewToggledR
156
199
  // Folded one-liner vs full summary text.
157
200
  signatureScratch.push(expanded, expandedRows.has(row.id));
158
201
  break;
202
+ case 'user':
203
+ // T06: the selection indicator line above the bubble adds one rendered
204
+ // row whose text wraps with width — its presence and content length
205
+ // are height inputs. Missing this would leave offscreen spacer heights
206
+ // stale when the field arrives (scroll jump, DESIGN R4).
207
+ // T-FIX-01: the indicator renders the cwd-relative display path, so
208
+ // the signature hashes THAT string — a cwd switch (/workspace) that
209
+ // changes the display form must invalidate the cached height too.
210
+ // width is the display-CELL width (emoji / CJK / combining / ANSI
211
+ // differ from JS length), not the JS string length — two paths with
212
+ // equal length but different terminal widths must not share a cached
213
+ // height (coderabbit review, repo width convention).
214
+ signatureScratch.push(row.selectionAttached === undefined ? 0 : 1, stringWidth(displaySelectionPath(row.selectionAttached?.path ?? '', sessionCwd)), String(row.selectionAttached?.lines ?? ''));
215
+ // Long-line fold: expanding a folded row swaps the folded preview for
216
+ // the raw line (thousands of rows), so both expansion switches are
217
+ // height inputs on user rows too — same rationale as the default arm.
218
+ signatureScratch.push(expanded, expandedRows.has(row.id));
219
+ break;
159
220
  default:
160
- // user / notice / interrupt / local / local-output: height follows
221
+ // notice / interrupt / local / local-output: height follows
161
222
  // text + columns alone (selection/background never change height) —
162
223
  // EXCEPT the long-line fold: expanding a folded row swaps ~10 rows of
163
224
  // folded text for the raw line (thousands), so both expansion switches
@@ -169,7 +230,7 @@ function signatureParts(row, columns, expanded, expandedRows, streamViewToggledR
169
230
  }
170
231
  return signatureScratch;
171
232
  }
172
- export function MessageList({ rows, expanded, expandedRows, selectedId, onToggleRow, streamViewToggledRows = NO_STREAM_VIEW_TOGGLED, onToggleStreamView = NOOP_TOGGLE_STREAM_VIEW, model, diffLayout = 'auto', thinkingFold = 'preview', toolBackground = 'none', foldTerminalCommand = false, smoothStreaming = false, activityFrames, showAll, onToggleAll, onLoadOlder, thinkingVisible = true, historyPaintEnabled = true, registerRowRef, scrollHandle, forceMountRowId, newSinceRowId, onUnseenCount, onTimeline, failureHintRowId, failureHint, onOpenSubagent, onOpenJobs, onOpenFile, onPreviewImage, suppressImageGraphics = false, }) {
233
+ export function MessageList({ rows, expanded, expandedRows, selectedId, onToggleRow, streamViewToggledRows = NO_STREAM_VIEW_TOGGLED, onToggleStreamView = NOOP_TOGGLE_STREAM_VIEW, model, diffLayout = 'auto', thinkingFold = 'preview', toolBackground = 'none', foldTerminalCommand = false, smoothStreaming = false, activityFrames, showAll, onToggleAll, onLoadOlder, thinkingVisible = true, historyPaintEnabled = true, registerRowRef, scrollHandle, forceMountRowId, newSinceRowId, onUnseenCount, onTimeline, failureHintRowId, failureHint, onOpenSubagent, onOpenJobs, onOpenFile, sessionCwd, onPreviewImage, suppressImageGraphics = false, }) {
173
234
  const lang = React.useSyncExternalStore(subscribeLang, getLang);
174
235
  const hiddenCount = rows.length - RENDERED_ROW_CAP;
175
236
  // The thinking filter runs BEFORE virtualization so window indices line up.
@@ -396,7 +457,7 @@ export function MessageList({ rows, expanded, expandedRows, selectedId, onToggle
396
457
  // reasoning height at once, remounting the widened window over rows
397
458
  // whose rendering never changed (Yoga spike + measure churn for
398
459
  // nothing). Base parts cover the universal height inputs.
399
- const parts = signatureParts(row, columns, expanded, expandedRows, streamViewToggledRows, thinkingVisible, thinkingFold, diffLayout, foldTerminalCommand, model, failureHintRowId, failureHint, revealDisplayLen(row, smoothStreaming));
460
+ const parts = signatureParts(row, columns, expanded, expandedRows, streamViewToggledRows, thinkingVisible, thinkingFold, diffLayout, foldTerminalCommand, model, failureHintRowId, failureHint, revealDisplayLen(row, smoothStreaming), sessionCwd);
400
461
  const cachedParts = sigs.get(row.id);
401
462
  let same = false;
402
463
  if (cachedParts !== undefined && cachedParts.length === parts.length) {
@@ -959,7 +1020,7 @@ export function MessageList({ rows, expanded, expandedRows, selectedId, onToggle
959
1020
  else if (row.kind === 'reasoning' && smoothStreaming) {
960
1021
  displayText = revealTextOf(`r${row.id}`, row.text, { enabled: true, active: displayStreaming });
961
1022
  }
962
- return (_jsx(MemoRow, { rowId: row.id, kind: row.kind, text: displayText, images: row.images, textFull: row.kind === 'reasoning' ? row.text : undefined, executionTarget: row.executionTarget, streaming: displayStreaming, durationMs: row.durationMs, time: row.time, marginTopOnTurn: marginTopOnTurn, isSelected: selectedId === row.id, isExpanded: expandedRows.has(row.id), expanded: expanded, model: model, diffLayout: diffLayout, thinkingFold: thinkingFold, toolBackground: toolBackground, foldTerminalCommand: foldTerminalCommand, smoothStreaming: smoothStreaming, fresh: row.fresh === true, revealVersion: revealVersion, activityFrames: activityFrames, background: rowBackground(row.id), toolCallId: tool?.callId, toolName: tool?.name, toolArgsText: tool?.argsText, toolArgsFull: tool?.argsFull, toolStatus: tool?.status, toolResultText: tool?.resultText, toolResultFull: tool?.resultFull, toolErrorText: tool?.errorText, toolFootnote: failureHintRowId === row.id ? failureHint : undefined, toolCallView: tool?.callView, toolResultView: tool?.resultView, toolStartedAt: tool?.startedAt, toolDurationMs: tool?.durationMs, subagent: subagent, job: job, onToggleRow: onToggleRow, onToggleStreamView: onToggleStreamView, streamViewToggled: streamViewToggledRows.has(row.id), onOpenSubagent: onOpenSubagent, onOpenJobs: onOpenJobs, onOpenFile: onOpenFile, onPreviewImage: onPreviewImage, suppressImageGraphics: suppressImageGraphics, setRowRef: setRowRef }, row.id));
1023
+ return (_jsx(MemoRow, { rowId: row.id, kind: row.kind, text: displayText, images: row.images, textFull: row.kind === 'reasoning' ? row.text : undefined, executionTarget: row.executionTarget, selectionAttached: row.selectionAttached, streaming: displayStreaming, durationMs: row.durationMs, time: row.time, marginTopOnTurn: marginTopOnTurn, isSelected: selectedId === row.id, isExpanded: expandedRows.has(row.id), expanded: expanded, model: model, diffLayout: diffLayout, thinkingFold: thinkingFold, toolBackground: toolBackground, foldTerminalCommand: foldTerminalCommand, smoothStreaming: smoothStreaming, fresh: row.fresh === true, revealVersion: revealVersion, activityFrames: activityFrames, background: rowBackground(row.id), toolCallId: tool?.callId, toolName: tool?.name, toolArgsText: tool?.argsText, toolArgsFull: tool?.argsFull, toolStatus: tool?.status, toolResultText: tool?.resultText, toolResultFull: tool?.resultFull, toolErrorText: tool?.errorText, toolFootnote: failureHintRowId === row.id ? failureHint : undefined, toolCallView: tool?.callView, toolResultView: tool?.resultView, toolStartedAt: tool?.startedAt, toolDurationMs: tool?.durationMs, subagent: subagent, job: job, onToggleRow: onToggleRow, onToggleStreamView: onToggleStreamView, streamViewToggled: streamViewToggledRows.has(row.id), onOpenSubagent: onOpenSubagent, onOpenJobs: onOpenJobs, onOpenFile: onOpenFile, sessionCwd: sessionCwd, onPreviewImage: onPreviewImage, suppressImageGraphics: suppressImageGraphics, setRowRef: setRowRef }, row.id));
963
1024
  }), bottomPad > 0 && _jsx(Box, { height: bottomPad, flexShrink: 0 })] }));
964
1025
  }
965
1026
  /** Load-earlier / show-previous divider row with a mouse hover tint — the
@@ -968,7 +1029,7 @@ function ClickableDivider({ title, onClick }) {
968
1029
  const [hovered, setHovered] = useState(false);
969
1030
  return (_jsx(Box, { marginTop: 1, onClick: onClick, onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), backgroundColor: hovered ? 'userMessageBackgroundHover' : undefined, children: _jsx(Divider, { title: title }) }));
970
1031
  }
971
- function TranscriptRow({ rowId, kind, text, images, textFull, executionTarget, streaming, durationMs, time, marginTopOnTurn, isSelected, isExpanded, expanded, model, diffLayout, smoothStreaming, fresh, revealVersion, thinkingFold, toolBackground, foldTerminalCommand, activityFrames, background, toolCallId, toolName, toolArgsText, toolArgsFull, toolStatus, toolResultText, toolResultFull, toolErrorText, toolFootnote, toolCallView, toolResultView, toolStartedAt, toolDurationMs, subagent, job, onToggleRow, onToggleStreamView, streamViewToggled, onOpenSubagent, onOpenJobs, onOpenFile, onPreviewImage, suppressImageGraphics, setRowRef, }) {
1032
+ function TranscriptRow({ rowId, kind, text, images, textFull, executionTarget, selectionAttached, sessionCwd, streaming, durationMs, time, marginTopOnTurn, isSelected, isExpanded, expanded, model, diffLayout, smoothStreaming, fresh, revealVersion, thinkingFold, toolBackground, foldTerminalCommand, activityFrames, background, toolCallId, toolName, toolArgsText, toolArgsFull, toolStatus, toolResultText, toolResultFull, toolErrorText, toolFootnote, toolCallView, toolResultView, toolStartedAt, toolDurationMs, subagent, job, onToggleRow, onToggleStreamView, streamViewToggled, onOpenSubagent, onOpenJobs, onOpenFile, onPreviewImage, suppressImageGraphics, setRowRef, }) {
972
1033
  const ref = React.useCallback((el) => {
973
1034
  setRowRef(rowId, el);
974
1035
  }, [setRowRef, rowId]);
@@ -1018,7 +1079,11 @@ function TranscriptRow({ rowId, kind, text, images, textFull, executionTarget, s
1018
1079
  const foldClickable = foldable && folded.hiddenChars > 0;
1019
1080
  switch (kind) {
1020
1081
  case 'user':
1021
- return (_jsxs(Box, { flexDirection: "column", ref: ref, onClick: foldClickable ? foldOnClick : undefined, children: [text !== '' && (_jsx(UserPromptMessage, { text: displayText, marginTopOnTurn: marginTopOnTurn, isSelected: isSelected })), images !== undefined && (_jsx(Box, { marginTop: text === '' && marginTopOnTurn ? 1 : 0, children: _jsx(TranscriptImages, { images: images, onPreview: onPreviewImage, suppressGraphics: suppressImageGraphics }) }))] }));
1082
+ return (_jsxs(Box, { flexDirection: "column", ref: ref, onClick: foldClickable ? foldOnClick : undefined, children: [selectionAttached && (_jsxs(Text, { dimColor: true, children: ['⧉ ', t('selection-attached', {
1083
+ lines: selectionAttached.lines,
1084
+ count: selectionAttached.lines,
1085
+ path: displaySelectionPath(selectionAttached.path, sessionCwd),
1086
+ })] })), text !== '' && (_jsx(UserPromptMessage, { text: displayText, marginTopOnTurn: marginTopOnTurn, isSelected: isSelected })), images !== undefined && (_jsx(Box, { marginTop: text === '' && marginTopOnTurn ? 1 : 0, children: _jsx(TranscriptImages, { images: images, onPreview: onPreviewImage, suppressGraphics: suppressImageGraphics }) }))] }));
1022
1087
  case 'assistant':
1023
1088
  return streaming ? (_jsxs(Box, { alignItems: "flex-start", flexDirection: "row", marginTop: marginTopOnTurn ? 1 : 0, width: "100%", backgroundColor: background, ref: ref, onClick: foldClickable ? foldOnClick : undefined, children: [_jsx(Box, { minWidth: 2, children: _jsx(Text, { color: "text", children: "\u25CF" }) }), _jsxs(Box, { flexDirection: "column", children: [_jsx(StreamingMarkdown, { children: stripNarration(displayText) }), images !== undefined && _jsx(TranscriptImages, { images: images, indent: 0, onPreview: onPreviewImage, suppressGraphics: suppressImageGraphics })] })] })) : (_jsxs(Box, { width: "100%", flexDirection: "column", backgroundColor: background, ref: ref, onClick: foldClickable ? foldOnClick : undefined, children: [expanded && (_jsx(Box, { flexDirection: "row", justifyContent: "flex-end", gap: 1, marginTop: 1, children: _jsx(MessageMetadata, { timestamp: time, model: model }) })), _jsx(AssistantTextMessage, { text: stripNarration(displayText), marginTopOnTurn: marginTopOnTurn, isSelected: isSelected, isExpanded: isExpanded }), images !== undefined && _jsx(TranscriptImages, { images: images, onPreview: onPreviewImage, suppressGraphics: suppressImageGraphics })] }));
1024
1089
  case 'reasoning': {
@@ -17,7 +17,7 @@ import React from 'react';
17
17
  * 组件(换行宽度、虚拟化、输入区预算、表格布局……)无需各自知道
18
18
  * 边距存在——它们拿到的就是内容区尺寸;
19
19
  * 3. 坐标层:`PageInsetContext` 报告内容区相对屏幕原点的偏移,供少数
20
- * 以「屏幕坐标」做 absolute 定位的浮层(Tooltip、SessionBrowser
20
+ * 以「屏幕坐标」做 absolute 定位的浮层(Tooltip、会话列表
21
21
  * 右键菜单)补偿——它们的锚点来自指针事件(屏幕坐标),而 absolute
22
22
  * 盒子相对的是内缩后的内容区原点。
23
23
  *