@stigmer/react 3.1.1 → 3.1.3

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 (483) hide show
  1. package/composer/SessionComposer.d.ts +25 -1
  2. package/composer/SessionComposer.d.ts.map +1 -1
  3. package/composer/SessionComposer.js +19 -8
  4. package/composer/SessionComposer.js.map +1 -1
  5. package/execution/ArtifactContentRenderer.d.ts +11 -1
  6. package/execution/ArtifactContentRenderer.d.ts.map +1 -1
  7. package/execution/ArtifactContentRenderer.js +39 -15
  8. package/execution/ArtifactContentRenderer.js.map +1 -1
  9. package/execution/ArtifactPreviewModal.d.ts +5 -5
  10. package/execution/ArtifactPreviewModal.js +4 -4
  11. package/execution/ArtifactPreviewModal.js.map +1 -1
  12. package/execution/FileChangeProgressBar.d.ts +47 -0
  13. package/execution/FileChangeProgressBar.d.ts.map +1 -0
  14. package/execution/FileChangeProgressBar.js +49 -0
  15. package/execution/FileChangeProgressBar.js.map +1 -0
  16. package/execution/FileChangesView.d.ts.map +1 -1
  17. package/execution/FileChangesView.js +19 -4
  18. package/execution/FileChangesView.js.map +1 -1
  19. package/execution/FilePathContext.d.ts +8 -1
  20. package/execution/FilePathContext.d.ts.map +1 -1
  21. package/execution/FilePathContext.js.map +1 -1
  22. package/execution/FilePathLink.d.ts.map +1 -1
  23. package/execution/FilePathLink.js +21 -6
  24. package/execution/FilePathLink.js.map +1 -1
  25. package/execution/FileReviewAtoms.d.ts +27 -0
  26. package/execution/FileReviewAtoms.d.ts.map +1 -0
  27. package/execution/FileReviewAtoms.js +64 -0
  28. package/execution/FileReviewAtoms.js.map +1 -0
  29. package/execution/FileReviewCard.d.ts +2 -2
  30. package/execution/FileReviewCard.d.ts.map +1 -1
  31. package/execution/FileReviewCard.js +32 -62
  32. package/execution/FileReviewCard.js.map +1 -1
  33. package/execution/MessageEntry.d.ts +20 -0
  34. package/execution/MessageEntry.d.ts.map +1 -1
  35. package/execution/MessageEntry.js +19 -5
  36. package/execution/MessageEntry.js.map +1 -1
  37. package/execution/MessageThread.d.ts +75 -13
  38. package/execution/MessageThread.d.ts.map +1 -1
  39. package/execution/MessageThread.js +195 -27
  40. package/execution/MessageThread.js.map +1 -1
  41. package/execution/PlanArtifactCard.d.ts +36 -21
  42. package/execution/PlanArtifactCard.d.ts.map +1 -1
  43. package/execution/PlanArtifactCard.js +39 -25
  44. package/execution/PlanArtifactCard.js.map +1 -1
  45. package/execution/PlanCompletionCard.d.ts +4 -4
  46. package/execution/PlanCompletionCard.d.ts.map +1 -1
  47. package/execution/PlanCompletionCard.js +4 -4
  48. package/execution/PlanCompletionCard.js.map +1 -1
  49. package/execution/PlanDocumentMessage.d.ts +32 -0
  50. package/execution/PlanDocumentMessage.d.ts.map +1 -0
  51. package/execution/PlanDocumentMessage.js +37 -0
  52. package/execution/PlanDocumentMessage.js.map +1 -0
  53. package/execution/PlanStreamingCard.d.ts +50 -0
  54. package/execution/PlanStreamingCard.d.ts.map +1 -0
  55. package/execution/PlanStreamingCard.js +40 -0
  56. package/execution/PlanStreamingCard.js.map +1 -0
  57. package/execution/index.d.ts +9 -1
  58. package/execution/index.d.ts.map +1 -1
  59. package/execution/index.js +4 -0
  60. package/execution/index.js.map +1 -1
  61. package/execution/use-build-from-plan-hotkey.d.ts +1 -1
  62. package/execution/use-build-from-plan-hotkey.js +1 -1
  63. package/execution/useArtifactCopy.d.ts +51 -0
  64. package/execution/useArtifactCopy.d.ts.map +1 -0
  65. package/execution/useArtifactCopy.js +75 -0
  66. package/execution/useArtifactCopy.js.map +1 -0
  67. package/execution/useArtifactDownload.d.ts.map +1 -1
  68. package/execution/useArtifactDownload.js +17 -5
  69. package/execution/useArtifactDownload.js.map +1 -1
  70. package/execution/useCreateAgentExecution.d.ts +12 -0
  71. package/execution/useCreateAgentExecution.d.ts.map +1 -1
  72. package/execution/useCreateAgentExecution.js +5 -1
  73. package/execution/useCreateAgentExecution.js.map +1 -1
  74. package/github/decodeGitHubContent.d.ts +29 -0
  75. package/github/decodeGitHubContent.d.ts.map +1 -0
  76. package/github/decodeGitHubContent.js +72 -0
  77. package/github/decodeGitHubContent.js.map +1 -0
  78. package/github/index.d.ts +1 -0
  79. package/github/index.d.ts.map +1 -1
  80. package/github/index.js +1 -0
  81. package/github/index.js.map +1 -1
  82. package/github/useGitHubFileReader.d.ts +26 -0
  83. package/github/useGitHubFileReader.d.ts.map +1 -0
  84. package/github/useGitHubFileReader.js +106 -0
  85. package/github/useGitHubFileReader.js.map +1 -0
  86. package/github/useGitHubTreeLister.d.ts.map +1 -1
  87. package/github/useGitHubTreeLister.js +6 -2
  88. package/github/useGitHubTreeLister.js.map +1 -1
  89. package/index.d.ts +9 -9
  90. package/index.d.ts.map +1 -1
  91. package/index.js +5 -5
  92. package/index.js.map +1 -1
  93. package/internal/ResizableSplit.d.ts +72 -22
  94. package/internal/ResizableSplit.d.ts.map +1 -1
  95. package/internal/ResizableSplit.js +96 -38
  96. package/internal/ResizableSplit.js.map +1 -1
  97. package/internal/VirtualizedThread.d.ts +5 -3
  98. package/internal/VirtualizedThread.d.ts.map +1 -1
  99. package/internal/VirtualizedThread.js +6 -4
  100. package/internal/VirtualizedThread.js.map +1 -1
  101. package/internal/file-icons/index.d.ts +17 -0
  102. package/internal/file-icons/index.d.ts.map +1 -0
  103. package/internal/file-icons/index.js +60 -0
  104. package/internal/file-icons/index.js.map +1 -0
  105. package/internal/file-tree/FileTreeNode.d.ts +18 -1
  106. package/internal/file-tree/FileTreeNode.d.ts.map +1 -1
  107. package/internal/file-tree/FileTreeNode.js +11 -2
  108. package/internal/file-tree/FileTreeNode.js.map +1 -1
  109. package/internal/markdown-components.d.ts +38 -1
  110. package/internal/markdown-components.d.ts.map +1 -1
  111. package/internal/markdown-components.js +86 -11
  112. package/internal/markdown-components.js.map +1 -1
  113. package/{workflow → internal}/motion-preference.d.ts +7 -4
  114. package/internal/motion-preference.d.ts.map +1 -0
  115. package/{workflow → internal}/motion-preference.js +7 -4
  116. package/internal/motion-preference.js.map +1 -0
  117. package/internal/store/index.d.ts +33 -0
  118. package/internal/store/index.d.ts.map +1 -1
  119. package/internal/store/index.js +59 -0
  120. package/internal/store/index.js.map +1 -1
  121. package/internal/store/workspace-editors-store.d.ts +145 -0
  122. package/internal/store/workspace-editors-store.d.ts.map +1 -0
  123. package/internal/store/workspace-editors-store.js +208 -0
  124. package/internal/store/workspace-editors-store.js.map +1 -0
  125. package/internal/store/workspace-file-selection-store.d.ts +38 -0
  126. package/internal/store/workspace-file-selection-store.d.ts.map +1 -0
  127. package/internal/store/workspace-file-selection-store.js +62 -0
  128. package/internal/store/workspace-file-selection-store.js.map +1 -0
  129. package/internal/useRevealLine.d.ts +49 -0
  130. package/internal/useRevealLine.d.ts.map +1 -0
  131. package/internal/useRevealLine.js +43 -0
  132. package/internal/useRevealLine.js.map +1 -0
  133. package/library/detect-plan-artifact.d.ts +43 -4
  134. package/library/detect-plan-artifact.d.ts.map +1 -1
  135. package/library/detect-plan-artifact.js +45 -5
  136. package/library/detect-plan-artifact.js.map +1 -1
  137. package/library/detect-streaming-plan.d.ts +45 -0
  138. package/library/detect-streaming-plan.d.ts.map +1 -0
  139. package/library/detect-streaming-plan.js +83 -0
  140. package/library/detect-streaming-plan.js.map +1 -0
  141. package/library/index.d.ts +4 -1
  142. package/library/index.d.ts.map +1 -1
  143. package/library/index.js +2 -1
  144. package/library/index.js.map +1 -1
  145. package/package.json +4 -4
  146. package/session/NewSessionViewer.d.ts +19 -4
  147. package/session/NewSessionViewer.d.ts.map +1 -1
  148. package/session/NewSessionViewer.js +52 -25
  149. package/session/NewSessionViewer.js.map +1 -1
  150. package/session/PlanEditor.d.ts +55 -0
  151. package/session/PlanEditor.d.ts.map +1 -0
  152. package/session/PlanEditor.js +88 -0
  153. package/session/PlanEditor.js.map +1 -0
  154. package/session/PlanStreamingDocument.d.ts +30 -0
  155. package/session/PlanStreamingDocument.d.ts.map +1 -0
  156. package/session/PlanStreamingDocument.js +37 -0
  157. package/session/PlanStreamingDocument.js.map +1 -0
  158. package/session/SessionPanelChip.d.ts +26 -0
  159. package/session/SessionPanelChip.d.ts.map +1 -0
  160. package/session/SessionPanelChip.js +40 -0
  161. package/session/SessionPanelChip.js.map +1 -0
  162. package/session/SessionViewer.d.ts +34 -7
  163. package/session/SessionViewer.d.ts.map +1 -1
  164. package/session/SessionViewer.js +334 -33
  165. package/session/SessionViewer.js.map +1 -1
  166. package/session/{inspector → facets}/ArtifactsTab.d.ts +10 -2
  167. package/session/facets/ArtifactsTab.d.ts.map +1 -0
  168. package/session/{inspector → facets}/ArtifactsTab.js +8 -3
  169. package/session/facets/ArtifactsTab.js.map +1 -0
  170. package/session/{inspector → facets}/ChangesTab.d.ts +3 -3
  171. package/session/facets/ChangesTab.d.ts.map +1 -0
  172. package/session/{inspector → facets}/ChangesTab.js +3 -3
  173. package/session/facets/ChangesTab.js.map +1 -0
  174. package/session/facets/InspectTab.d.ts.map +1 -0
  175. package/session/facets/InspectTab.js.map +1 -0
  176. package/session/{inspector → facets}/SetupTab.d.ts +11 -2
  177. package/session/facets/SetupTab.d.ts.map +1 -0
  178. package/session/{inspector → facets}/SetupTab.js +4 -3
  179. package/session/facets/SetupTab.js.map +1 -0
  180. package/session/{inspector → facets}/UsageTab.d.ts +1 -1
  181. package/session/facets/UsageTab.d.ts.map +1 -0
  182. package/session/{inspector → facets}/UsageTab.js +1 -1
  183. package/session/facets/UsageTab.js.map +1 -0
  184. package/session/{inspector → facets}/index.d.ts +0 -6
  185. package/session/facets/index.d.ts.map +1 -0
  186. package/session/{inspector → facets}/index.js +0 -3
  187. package/session/facets/index.js.map +1 -0
  188. package/session/index.d.ts +11 -2
  189. package/session/index.d.ts.map +1 -1
  190. package/session/index.js +15 -2
  191. package/session/index.js.map +1 -1
  192. package/session/plan-document.d.ts +21 -0
  193. package/session/plan-document.d.ts.map +1 -0
  194. package/session/plan-document.js +28 -0
  195. package/session/plan-document.js.map +1 -0
  196. package/session/useOpenFileChange.d.ts +20 -0
  197. package/session/useOpenFileChange.d.ts.map +1 -0
  198. package/session/useOpenFileChange.js +31 -0
  199. package/session/useOpenFileChange.js.map +1 -0
  200. package/session/usePlanDraft.d.ts +52 -0
  201. package/session/usePlanDraft.d.ts.map +1 -0
  202. package/session/usePlanDraft.js +63 -0
  203. package/session/usePlanDraft.js.map +1 -0
  204. package/session/useSessionConversation.d.ts +14 -1
  205. package/session/useSessionConversation.d.ts.map +1 -1
  206. package/session/useSessionConversation.js +20 -2
  207. package/session/useSessionConversation.js.map +1 -1
  208. package/session/useSessionPageFlow.d.ts.map +1 -1
  209. package/session/useSessionPageFlow.js +1 -0
  210. package/session/useSessionPageFlow.js.map +1 -1
  211. package/session/useSessionPanel.d.ts +120 -0
  212. package/session/useSessionPanel.d.ts.map +1 -0
  213. package/session/useSessionPanel.js +130 -0
  214. package/session/useSessionPanel.js.map +1 -0
  215. package/session/useSessionRailViews.d.ts +45 -0
  216. package/session/useSessionRailViews.d.ts.map +1 -0
  217. package/session/useSessionRailViews.js +105 -0
  218. package/session/useSessionRailViews.js.map +1 -0
  219. package/src/composer/SessionComposer.tsx +53 -10
  220. package/src/composer/__tests__/SessionComposer-contract.test.tsx +58 -4
  221. package/src/execution/ArtifactContentRenderer.tsx +101 -24
  222. package/src/execution/ArtifactPreviewModal.tsx +9 -9
  223. package/src/execution/FileChangeProgressBar.tsx +121 -0
  224. package/src/execution/FileChangesView.tsx +18 -4
  225. package/src/execution/FilePathContext.tsx +8 -1
  226. package/src/execution/FilePathLink.tsx +22 -6
  227. package/src/execution/FileReviewAtoms.tsx +89 -0
  228. package/src/execution/FileReviewCard.tsx +57 -90
  229. package/src/execution/MessageEntry.tsx +49 -1
  230. package/src/execution/MessageThread.tsx +308 -39
  231. package/src/execution/PlanArtifactCard.tsx +174 -64
  232. package/src/execution/PlanCompletionCard.tsx +5 -5
  233. package/src/execution/PlanDocumentMessage.tsx +96 -0
  234. package/src/execution/PlanStreamingCard.tsx +141 -0
  235. package/src/execution/__tests__/ArtifactContentRenderer.test.tsx +80 -1
  236. package/src/execution/__tests__/ArtifactPreviewModal.test.tsx +6 -6
  237. package/src/execution/__tests__/FileChangeProgressBar.test.tsx +125 -0
  238. package/src/execution/__tests__/FilePathLink.test.tsx +87 -0
  239. package/src/execution/__tests__/FileReviewCard.test.tsx +66 -7
  240. package/src/execution/__tests__/MessageThread.test.tsx +102 -1
  241. package/src/execution/__tests__/PlanArtifactCard.test.tsx +114 -26
  242. package/src/execution/__tests__/PlanCompletionCard.test.tsx +2 -2
  243. package/src/execution/__tests__/PlanDocumentMessage.test.tsx +55 -0
  244. package/src/execution/__tests__/PlanStreamingCard.test.tsx +56 -0
  245. package/src/execution/__tests__/buildThreadItems-plan-completion.test.ts +600 -22
  246. package/src/execution/__tests__/message-entry.test.tsx +17 -0
  247. package/src/execution/__tests__/useArtifactCopy.test.tsx +90 -0
  248. package/src/execution/__tests__/useArtifactDownload.test.tsx +2 -0
  249. package/src/execution/__tests__/useCreateAgentExecution.test.tsx +85 -0
  250. package/src/execution/index.ts +13 -1
  251. package/src/execution/use-build-from-plan-hotkey.ts +1 -1
  252. package/src/execution/useArtifactCopy.ts +109 -0
  253. package/src/execution/useArtifactDownload.ts +17 -5
  254. package/src/execution/useCreateAgentExecution.ts +18 -1
  255. package/src/github/__tests__/decodeGitHubContent.test.ts +102 -0
  256. package/src/github/__tests__/useGitHubFileReader.test.ts +183 -0
  257. package/src/github/decodeGitHubContent.ts +83 -0
  258. package/src/github/index.ts +1 -0
  259. package/src/github/useGitHubFileReader.ts +152 -0
  260. package/src/github/useGitHubTreeLister.ts +6 -2
  261. package/src/index.ts +69 -9
  262. package/src/internal/ResizableSplit.tsx +184 -51
  263. package/src/internal/VirtualizedThread.tsx +12 -4
  264. package/src/internal/__tests__/ResizableSplit.test.tsx +125 -3
  265. package/src/internal/__tests__/markdown-components.test.tsx +68 -0
  266. package/src/internal/__tests__/useRevealLine.test.tsx +97 -0
  267. package/src/internal/file-icons/__tests__/file-icons.test.tsx +35 -0
  268. package/src/internal/file-icons/index.tsx +137 -0
  269. package/src/internal/file-tree/FileTreeNode.tsx +56 -5
  270. package/src/internal/file-tree/__tests__/FileTreeNode-explorer.test.tsx +86 -0
  271. package/src/internal/markdown-components.tsx +116 -11
  272. package/src/{workflow → internal}/motion-preference.ts +7 -4
  273. package/src/internal/store/__tests__/workspace-editors-store.test.ts +216 -0
  274. package/src/internal/store/__tests__/workspace-file-selection-store.test.ts +118 -0
  275. package/src/internal/store/index.ts +94 -0
  276. package/src/internal/store/workspace-editors-store.ts +315 -0
  277. package/src/internal/store/workspace-file-selection-store.ts +88 -0
  278. package/src/internal/useRevealLine.ts +81 -0
  279. package/src/library/__tests__/detect-plan-artifact.test.ts +69 -0
  280. package/src/library/__tests__/detect-streaming-plan.test.ts +234 -0
  281. package/src/library/detect-plan-artifact.ts +62 -5
  282. package/src/library/detect-streaming-plan.ts +108 -0
  283. package/src/library/index.ts +7 -0
  284. package/src/session/NewSessionViewer.tsx +123 -55
  285. package/src/session/PlanEditor.tsx +361 -0
  286. package/src/session/PlanStreamingDocument.tsx +97 -0
  287. package/src/session/SessionPanelChip.tsx +103 -0
  288. package/src/session/SessionViewer.tsx +655 -96
  289. package/src/session/__tests__/NewSessionViewer.test.tsx +148 -0
  290. package/src/session/__tests__/PlanEditor.test.tsx +217 -0
  291. package/src/session/__tests__/PlanStreamingDocument.test.tsx +50 -0
  292. package/src/session/__tests__/SessionPanelChip.test.tsx +42 -0
  293. package/src/session/__tests__/SessionViewer-fileReviewDock.test.tsx +66 -4
  294. package/src/session/__tests__/SessionViewer-transcriptFileOpen.test.tsx +205 -0
  295. package/src/session/__tests__/audienceWiring.test.tsx +55 -19
  296. package/src/session/__tests__/useOpenFileChange.test.tsx +82 -0
  297. package/src/session/__tests__/usePlanDraft.test.ts +102 -0
  298. package/src/session/__tests__/useSessionConversation.test.tsx +44 -0
  299. package/src/session/__tests__/useSessionPanel.test.ts +298 -0
  300. package/src/session/__tests__/useSessionRailViews.test.tsx +124 -0
  301. package/src/session/{inspector → facets}/ArtifactsTab.tsx +27 -5
  302. package/src/session/{inspector → facets}/ChangesTab.tsx +3 -3
  303. package/src/session/{inspector → facets}/SetupTab.tsx +13 -1
  304. package/src/session/{inspector → facets}/UsageTab.tsx +1 -1
  305. package/src/session/{inspector → facets}/__tests__/ArtifactsTab.test.tsx +42 -8
  306. package/src/session/facets/__tests__/SetupTab.test.tsx +44 -0
  307. package/src/session/{inspector → facets}/index.ts +0 -16
  308. package/src/session/index.ts +30 -13
  309. package/src/session/plan-document.ts +30 -0
  310. package/src/session/useOpenFileChange.ts +53 -0
  311. package/src/session/usePlanDraft.ts +109 -0
  312. package/src/session/useSessionConversation.ts +38 -3
  313. package/src/session/useSessionPageFlow.ts +1 -0
  314. package/src/session/useSessionPanel.ts +288 -0
  315. package/src/session/useSessionRailViews.tsx +200 -0
  316. package/src/workflow/WorkflowExecutionGraph.tsx +1 -1
  317. package/src/workflow/index.ts +1 -1
  318. package/src/workflow/useFollowExecution.ts +1 -1
  319. package/src/workspace/EditorTabs.tsx +174 -0
  320. package/src/workspace/ExplorerTree.tsx +253 -0
  321. package/src/workspace/FileViewer.tsx +537 -0
  322. package/src/workspace/WorkspaceContentSearch.tsx +502 -0
  323. package/src/workspace/WorkspaceContentSearcher.ts +70 -0
  324. package/src/workspace/WorkspaceEditor.tsx +19 -16
  325. package/src/workspace/WorkspaceFileLister.ts +11 -0
  326. package/src/workspace/WorkspaceFileReader.ts +57 -0
  327. package/src/workspace/WorkspaceFileSearch.tsx +452 -0
  328. package/src/workspace/WorkspaceSurface.tsx +839 -0
  329. package/src/workspace/__tests__/EditorTabs.test.tsx +102 -0
  330. package/src/workspace/__tests__/ExplorerTree.test.tsx +204 -0
  331. package/src/workspace/__tests__/FileViewer.test.tsx +439 -0
  332. package/src/workspace/__tests__/WorkspaceContentSearch.test.tsx +229 -0
  333. package/src/workspace/__tests__/WorkspaceEditor-initialPanel.test.tsx +19 -0
  334. package/src/workspace/__tests__/WorkspaceFileSearch.test.tsx +227 -0
  335. package/src/workspace/__tests__/WorkspaceSurface.test.tsx +418 -0
  336. package/src/workspace/__tests__/a11y/harness.tsx +140 -0
  337. package/src/workspace/__tests__/a11y/mock-capabilities.ts +138 -0
  338. package/src/workspace/__tests__/a11y/workspace-editor.a11y.test.tsx +65 -0
  339. package/src/workspace/__tests__/a11y/workspace-explorer-search.a11y.test.tsx +112 -0
  340. package/src/workspace/__tests__/a11y/workspace-viewer-tabs.a11y.test.tsx +69 -0
  341. package/src/workspace/__tests__/findChangeForSelection.test.ts +132 -0
  342. package/src/workspace/__tests__/matchContentPreview.test.ts +55 -0
  343. package/src/workspace/__tests__/matchWorkspaceFiles.test.ts +104 -0
  344. package/src/workspace/__tests__/resolveWorkspaceFileSelection.test.ts +135 -0
  345. package/src/workspace/__tests__/useWorkspaceContentSearch.test.tsx +196 -0
  346. package/src/workspace/__tests__/useWorkspaceFileContent.test.tsx +190 -0
  347. package/src/workspace/__tests__/useWorkspaceFileSearch.test.tsx +162 -0
  348. package/src/workspace/findChangeForSelection.ts +61 -0
  349. package/src/workspace/index.ts +56 -2
  350. package/src/workspace/matchContentPreview.ts +51 -0
  351. package/src/workspace/matchWorkspaceFiles.ts +86 -0
  352. package/src/workspace/resolveWorkspaceFileSelection.ts +119 -0
  353. package/src/workspace/useWorkspaceContentSearch.ts +228 -0
  354. package/src/workspace/useWorkspaceFileContent.ts +129 -0
  355. package/src/workspace/useWorkspaceFileSearch.ts +184 -0
  356. package/src/workspace/useWorkspaceFiles.ts +27 -27
  357. package/src/workspace/workspaceListingCache.ts +93 -0
  358. package/styles.css +1 -1
  359. package/workflow/WorkflowExecutionGraph.js +1 -1
  360. package/workflow/WorkflowExecutionGraph.js.map +1 -1
  361. package/workflow/index.d.ts +1 -1
  362. package/workflow/index.d.ts.map +1 -1
  363. package/workflow/index.js +1 -1
  364. package/workflow/index.js.map +1 -1
  365. package/workflow/useFollowExecution.js +1 -1
  366. package/workflow/useFollowExecution.js.map +1 -1
  367. package/workspace/EditorTabs.d.ts +47 -0
  368. package/workspace/EditorTabs.d.ts.map +1 -0
  369. package/workspace/EditorTabs.js +78 -0
  370. package/workspace/EditorTabs.js.map +1 -0
  371. package/workspace/ExplorerTree.d.ts +32 -0
  372. package/workspace/ExplorerTree.d.ts.map +1 -0
  373. package/workspace/ExplorerTree.js +49 -0
  374. package/workspace/ExplorerTree.js.map +1 -0
  375. package/workspace/FileViewer.d.ts +93 -0
  376. package/workspace/FileViewer.d.ts.map +1 -0
  377. package/workspace/FileViewer.js +193 -0
  378. package/workspace/FileViewer.js.map +1 -0
  379. package/workspace/WorkspaceContentSearch.d.ts +41 -0
  380. package/workspace/WorkspaceContentSearch.d.ts.map +1 -0
  381. package/workspace/WorkspaceContentSearch.js +216 -0
  382. package/workspace/WorkspaceContentSearch.js.map +1 -0
  383. package/workspace/WorkspaceContentSearcher.d.ts +59 -0
  384. package/workspace/WorkspaceContentSearcher.d.ts.map +1 -0
  385. package/workspace/WorkspaceContentSearcher.js +7 -0
  386. package/workspace/WorkspaceContentSearcher.js.map +1 -0
  387. package/workspace/WorkspaceEditor.d.ts.map +1 -1
  388. package/workspace/WorkspaceEditor.js +8 -10
  389. package/workspace/WorkspaceEditor.js.map +1 -1
  390. package/workspace/WorkspaceFileLister.d.ts +11 -0
  391. package/workspace/WorkspaceFileLister.d.ts.map +1 -1
  392. package/workspace/WorkspaceFileReader.d.ts +48 -0
  393. package/workspace/WorkspaceFileReader.d.ts.map +1 -0
  394. package/workspace/WorkspaceFileReader.js +11 -0
  395. package/workspace/WorkspaceFileReader.js.map +1 -0
  396. package/workspace/WorkspaceFileSearch.d.ts +32 -0
  397. package/workspace/WorkspaceFileSearch.d.ts.map +1 -0
  398. package/workspace/WorkspaceFileSearch.js +175 -0
  399. package/workspace/WorkspaceFileSearch.js.map +1 -0
  400. package/workspace/WorkspaceSurface.d.ts +151 -0
  401. package/workspace/WorkspaceSurface.d.ts.map +1 -0
  402. package/workspace/WorkspaceSurface.js +220 -0
  403. package/workspace/WorkspaceSurface.js.map +1 -0
  404. package/workspace/findChangeForSelection.d.ts +19 -0
  405. package/workspace/findChangeForSelection.d.ts.map +1 -0
  406. package/workspace/findChangeForSelection.js +46 -0
  407. package/workspace/findChangeForSelection.js.map +1 -0
  408. package/workspace/index.d.ts +24 -2
  409. package/workspace/index.d.ts.map +1 -1
  410. package/workspace/index.js +13 -1
  411. package/workspace/index.js.map +1 -1
  412. package/workspace/matchContentPreview.d.ts +21 -0
  413. package/workspace/matchContentPreview.d.ts.map +1 -0
  414. package/workspace/matchContentPreview.js +36 -0
  415. package/workspace/matchContentPreview.js.map +1 -0
  416. package/workspace/matchWorkspaceFiles.d.ts +30 -0
  417. package/workspace/matchWorkspaceFiles.d.ts.map +1 -0
  418. package/workspace/matchWorkspaceFiles.js +58 -0
  419. package/workspace/matchWorkspaceFiles.js.map +1 -0
  420. package/workspace/resolveWorkspaceFileSelection.d.ts +19 -0
  421. package/workspace/resolveWorkspaceFileSelection.d.ts.map +1 -0
  422. package/workspace/resolveWorkspaceFileSelection.js +115 -0
  423. package/workspace/resolveWorkspaceFileSelection.js.map +1 -0
  424. package/workspace/useWorkspaceContentSearch.d.ts +69 -0
  425. package/workspace/useWorkspaceContentSearch.d.ts.map +1 -0
  426. package/workspace/useWorkspaceContentSearch.js +154 -0
  427. package/workspace/useWorkspaceContentSearch.js.map +1 -0
  428. package/workspace/useWorkspaceFileContent.d.ts +73 -0
  429. package/workspace/useWorkspaceFileContent.d.ts.map +1 -0
  430. package/workspace/useWorkspaceFileContent.js +57 -0
  431. package/workspace/useWorkspaceFileContent.js.map +1 -0
  432. package/workspace/useWorkspaceFileSearch.d.ts +61 -0
  433. package/workspace/useWorkspaceFileSearch.d.ts.map +1 -0
  434. package/workspace/useWorkspaceFileSearch.js +119 -0
  435. package/workspace/useWorkspaceFileSearch.js.map +1 -0
  436. package/workspace/useWorkspaceFiles.d.ts +13 -5
  437. package/workspace/useWorkspaceFiles.d.ts.map +1 -1
  438. package/workspace/useWorkspaceFiles.js +18 -19
  439. package/workspace/useWorkspaceFiles.js.map +1 -1
  440. package/workspace/workspaceListingCache.d.ts +47 -0
  441. package/workspace/workspaceListingCache.d.ts.map +1 -0
  442. package/workspace/workspaceListingCache.js +55 -0
  443. package/workspace/workspaceListingCache.js.map +1 -0
  444. package/session/inspector/ArtifactsTab.d.ts.map +0 -1
  445. package/session/inspector/ArtifactsTab.js.map +0 -1
  446. package/session/inspector/ChangesTab.d.ts.map +0 -1
  447. package/session/inspector/ChangesTab.js.map +0 -1
  448. package/session/inspector/InspectTab.d.ts.map +0 -1
  449. package/session/inspector/InspectTab.js.map +0 -1
  450. package/session/inspector/SessionInspector.d.ts +0 -64
  451. package/session/inspector/SessionInspector.d.ts.map +0 -1
  452. package/session/inspector/SessionInspector.js +0 -53
  453. package/session/inspector/SessionInspector.js.map +0 -1
  454. package/session/inspector/SetupTab.d.ts.map +0 -1
  455. package/session/inspector/SetupTab.js.map +0 -1
  456. package/session/inspector/UsageTab.d.ts.map +0 -1
  457. package/session/inspector/UsageTab.js.map +0 -1
  458. package/session/inspector/WorkspaceTab.d.ts +0 -35
  459. package/session/inspector/WorkspaceTab.d.ts.map +0 -1
  460. package/session/inspector/WorkspaceTab.js +0 -75
  461. package/session/inspector/WorkspaceTab.js.map +0 -1
  462. package/session/inspector/index.d.ts.map +0 -1
  463. package/session/inspector/index.js.map +0 -1
  464. package/session/inspector/useSessionInspector.d.ts +0 -49
  465. package/session/inspector/useSessionInspector.d.ts.map +0 -1
  466. package/session/inspector/useSessionInspector.js +0 -122
  467. package/session/inspector/useSessionInspector.js.map +0 -1
  468. package/src/session/inspector/SessionInspector.tsx +0 -162
  469. package/src/session/inspector/WorkspaceTab.tsx +0 -245
  470. package/src/session/inspector/__tests__/useSessionInspector.test.ts +0 -251
  471. package/src/session/inspector/useSessionInspector.ts +0 -178
  472. package/src/workspace/WorkspaceEntryFiles.tsx +0 -196
  473. package/workflow/motion-preference.d.ts.map +0 -1
  474. package/workflow/motion-preference.js.map +0 -1
  475. package/workspace/WorkspaceEntryFiles.d.ts +0 -22
  476. package/workspace/WorkspaceEntryFiles.d.ts.map +0 -1
  477. package/workspace/WorkspaceEntryFiles.js +0 -58
  478. package/workspace/WorkspaceEntryFiles.js.map +0 -1
  479. /package/session/{inspector → facets}/InspectTab.d.ts +0 -0
  480. /package/session/{inspector → facets}/InspectTab.js +0 -0
  481. /package/src/{workflow → internal}/__tests__/motion-preference.test.ts +0 -0
  482. /package/src/session/{inspector → facets}/InspectTab.tsx +0 -0
  483. /package/src/session/{inspector → facets}/__tests__/ChangesTab.test.tsx +0 -0
@@ -1,4 +1,4 @@
1
- import { describe, it, expect, afterEach } from "vitest";
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
2
  import { render, screen, fireEvent, cleanup } from "@testing-library/react";
3
3
  import { ArtifactContentRenderer } from "../ArtifactContentRenderer";
4
4
 
@@ -60,3 +60,82 @@ describe("ArtifactContentRenderer — markdown", () => {
60
60
  ).toBeGreaterThan(0);
61
61
  });
62
62
  });
63
+
64
+ describe("ArtifactContentRenderer — reveal (jump-to-line)", () => {
65
+ let scrollIntoView: ReturnType<typeof vi.fn>;
66
+ beforeEach(() => {
67
+ // happy-dom lacks a real scrollIntoView; the hook calls it optionally.
68
+ scrollIntoView = vi.fn();
69
+ Element.prototype.scrollIntoView =
70
+ scrollIntoView as unknown as typeof Element.prototype.scrollIntoView;
71
+ });
72
+
73
+ it("renders addressable lines and highlights the target row for plain text", () => {
74
+ const { container } = render(
75
+ <ArtifactContentRenderer
76
+ content={"one\ntwo\nthree"}
77
+ fileName="notes.txt"
78
+ reveal={{ line: 2, nonce: 1 }}
79
+ />,
80
+ );
81
+
82
+ // Every line is addressable; exactly the target row carries the highlight.
83
+ expect(container.querySelectorAll("[data-line]").length).toBe(3);
84
+ const target = container.querySelector('[data-line="2"]');
85
+ expect(target?.className).toContain("bg-primary-subtle");
86
+ expect(container.querySelectorAll(".bg-primary-subtle").length).toBe(1);
87
+ });
88
+
89
+ it("does not highlight anything when the target line is out of range", () => {
90
+ const { container } = render(
91
+ <ArtifactContentRenderer
92
+ content={"one\ntwo"}
93
+ fileName="notes.txt"
94
+ reveal={{ line: 99, nonce: 1 }}
95
+ />,
96
+ );
97
+ expect(container.querySelectorAll(".bg-primary-subtle").length).toBe(0);
98
+ });
99
+
100
+ it("forces Markdown to its Source (line-faithful) view when revealing", () => {
101
+ const { container } = render(
102
+ <ArtifactContentRenderer
103
+ content={"# Heading\n\ntext"}
104
+ fileName="doc.md"
105
+ reveal={{ line: 1, nonce: 1 }}
106
+ />,
107
+ );
108
+ // Source is line-numbered <pre>, not the rendered <h1>.
109
+ expect(container.querySelector("h1")).toBeNull();
110
+ expect(container.querySelector("pre")).not.toBeNull();
111
+ expect(container.querySelector('[data-line="1"]')).not.toBeNull();
112
+ // The Rendered/Source toggle is still offered.
113
+ expect(screen.getByRole("tab", { name: "Source" })).toBeTruthy();
114
+ });
115
+
116
+ it("renders JSON raw (not reformatted) when revealing, so line numbers hold", () => {
117
+ // Minified single-line JSON: pretty-printing would expand it to many lines
118
+ // and shift line 1. Raw rendering keeps it one addressable row.
119
+ const { container } = render(
120
+ <ArtifactContentRenderer
121
+ content={'{"a":1,"b":2}'}
122
+ fileName="data.json"
123
+ reveal={{ line: 1, nonce: 1 }}
124
+ />,
125
+ );
126
+ const rows = container.querySelectorAll("[data-line]");
127
+ expect(rows.length).toBe(1);
128
+ expect(rows[0].textContent).toContain('{"a":1,"b":2}');
129
+ });
130
+
131
+ it("scrolls the target line into view", () => {
132
+ render(
133
+ <ArtifactContentRenderer
134
+ content={"one\ntwo\nthree"}
135
+ fileName="notes.txt"
136
+ reveal={{ line: 3, nonce: 1 }}
137
+ />,
138
+ );
139
+ expect(scrollIntoView).toHaveBeenCalled();
140
+ });
141
+ });
@@ -26,8 +26,8 @@ function withStigmer(children: ReactNode) {
26
26
 
27
27
  afterEach(cleanup);
28
28
 
29
- describe("ArtifactPreviewContent — Build from plan action", () => {
30
- it("renders a 'Build from plan' button only when onImplement is provided", () => {
29
+ describe("ArtifactPreviewContent — Build action", () => {
30
+ it("renders a 'Build' button only when onImplement is provided", () => {
31
31
  const { rerender } = render(
32
32
  withStigmer(
33
33
  <ArtifactPreviewContent
@@ -39,7 +39,7 @@ describe("ArtifactPreviewContent — Build from plan action", () => {
39
39
  />,
40
40
  ),
41
41
  );
42
- expect(screen.queryByText("Build from plan")).toBeNull();
42
+ expect(screen.queryByText("Build")).toBeNull();
43
43
 
44
44
  rerender(
45
45
  withStigmer(
@@ -53,10 +53,10 @@ describe("ArtifactPreviewContent — Build from plan action", () => {
53
53
  />,
54
54
  ),
55
55
  );
56
- expect(screen.getByText("Build from plan")).toBeTruthy();
56
+ expect(screen.getByText("Build")).toBeTruthy();
57
57
  });
58
58
 
59
- it("calls onImplement then closes the modal when 'Build from plan' is clicked", () => {
59
+ it("calls onImplement then closes the modal when 'Build' is clicked", () => {
60
60
  const calls: string[] = [];
61
61
  const onImplement = vi.fn(() => calls.push("implement"));
62
62
  const onClose = vi.fn(() => calls.push("close"));
@@ -74,7 +74,7 @@ describe("ArtifactPreviewContent — Build from plan action", () => {
74
74
  ),
75
75
  );
76
76
 
77
- fireEvent.click(screen.getByText("Build from plan"));
77
+ fireEvent.click(screen.getByText("Build"));
78
78
 
79
79
  expect(onImplement).toHaveBeenCalledTimes(1);
80
80
  expect(onClose).toHaveBeenCalledTimes(1);
@@ -0,0 +1,125 @@
1
+ /**
2
+ * FileChangeProgressBar — the live "N files changed so far" strip for a running
3
+ * turn (DD-32). These tests cover its own responsibilities: emptiness, the
4
+ * summary count + aggregate stat, the expandable per-file list with its cap
5
+ * overflow, zero-count hiding, and that only the count is an aria-live region.
6
+ */
7
+
8
+ import { describe, it, expect, afterEach } from "vitest";
9
+ import { render, screen, fireEvent, cleanup } from "@testing-library/react";
10
+ import { create } from "@bufbuild/protobuf";
11
+ import {
12
+ FileChangeProgressSchema,
13
+ FileChangeProgressEntrySchema,
14
+ type FileChangeProgress,
15
+ } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
16
+ import { FileChangeKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
17
+ import { FileChangeProgressBar } from "../FileChangeProgressBar";
18
+
19
+ function entry(path: string, kind: FileChangeKind, added: number, removed: number) {
20
+ return create(FileChangeProgressEntrySchema, {
21
+ pathBefore: kind === FileChangeKind.ADD ? "" : path,
22
+ pathAfter: kind === FileChangeKind.DELETE ? "" : path,
23
+ kind,
24
+ linesAdded: added,
25
+ linesRemoved: removed,
26
+ });
27
+ }
28
+
29
+ function progress(overrides: Partial<FileChangeProgress> = {}): FileChangeProgress {
30
+ return create(FileChangeProgressSchema, {
31
+ changeSetId: "exec:0",
32
+ filesChanged: 2,
33
+ linesAdded: 7,
34
+ linesRemoved: 3,
35
+ entries: [
36
+ entry("src/a.ts", FileChangeKind.ADD, 5, 0),
37
+ entry("src/b.ts", FileChangeKind.MODIFY, 2, 3),
38
+ ],
39
+ capturedAt: "2026-07-05T00:00:00Z",
40
+ ...overrides,
41
+ });
42
+ }
43
+
44
+ afterEach(() => cleanup());
45
+
46
+ describe("FileChangeProgressBar", () => {
47
+ it("renders nothing when progress is undefined", () => {
48
+ const { container } = render(<FileChangeProgressBar progress={undefined} />);
49
+ expect(container.firstChild).toBeNull();
50
+ });
51
+
52
+ it("renders nothing when zero files have changed (revert-to-clean)", () => {
53
+ const { container } = render(
54
+ <FileChangeProgressBar
55
+ progress={progress({ filesChanged: 0, entries: [], linesAdded: 0, linesRemoved: 0 })}
56
+ />,
57
+ );
58
+ expect(container.firstChild).toBeNull();
59
+ });
60
+
61
+ it("shows the summary count and aggregate +N −M", () => {
62
+ render(<FileChangeProgressBar progress={progress()} />);
63
+ expect(screen.getByText(/2 files changing/)).toBeTruthy();
64
+ expect(screen.getByText("+7")).toBeTruthy();
65
+ expect(screen.getByText("-3")).toBeTruthy();
66
+ });
67
+
68
+ it("uses the singular noun for a single file", () => {
69
+ render(
70
+ <FileChangeProgressBar
71
+ progress={progress({ filesChanged: 1, entries: [entry("only.ts", FileChangeKind.ADD, 1, 0)] })}
72
+ />,
73
+ );
74
+ expect(screen.getByText(/1 file changing/)).toBeTruthy();
75
+ });
76
+
77
+ it("puts only the count in an aria-live region, not the per-file list", () => {
78
+ render(<FileChangeProgressBar progress={progress()} />);
79
+ const live = screen.getByText(/2 files changing/);
80
+ expect(live.getAttribute("aria-live")).toBe("polite");
81
+ // The expandable list is not part of any live region.
82
+ fireEvent.click(screen.getByRole("button", { name: /2 files changing/ }));
83
+ const link = screen.getByText("a.ts");
84
+ expect(link.closest("[aria-live]")).toBeNull();
85
+ });
86
+
87
+ it("expands to a per-file list on click and collapses again", () => {
88
+ render(<FileChangeProgressBar progress={progress()} />);
89
+ expect(screen.queryByText("a.ts")).toBeNull();
90
+
91
+ const toggle = screen.getByRole("button", { name: /2 files changing/ });
92
+ fireEvent.click(toggle);
93
+ expect(screen.getByText("a.ts")).toBeTruthy();
94
+ expect(screen.getByText("b.ts")).toBeTruthy();
95
+ expect(toggle.getAttribute("aria-expanded")).toBe("true");
96
+
97
+ fireEvent.click(toggle);
98
+ expect(screen.queryByText("a.ts")).toBeNull();
99
+ });
100
+
101
+ it("shows an honest overflow line when the entry list is capped", () => {
102
+ // 5 files changed but only 2 entries carried → "… and 3 more".
103
+ render(<FileChangeProgressBar progress={progress({ filesChanged: 5 })} />);
104
+ fireEvent.click(screen.getByRole("button", { name: /5 files changing/ }));
105
+ expect(screen.getByText(/and 3 more/)).toBeTruthy();
106
+ });
107
+
108
+ it("hides the per-file stat when both counts are zero (binary/secret)", () => {
109
+ render(
110
+ <FileChangeProgressBar
111
+ progress={progress({
112
+ filesChanged: 1,
113
+ linesAdded: 0,
114
+ linesRemoved: 0,
115
+ entries: [entry(".env", FileChangeKind.MODIFY, 0, 0)],
116
+ })}
117
+ />,
118
+ );
119
+ fireEvent.click(screen.getByRole("button", { name: /1 file changing/ }));
120
+ // The secret path is present…
121
+ expect(screen.getByText(".env")).toBeTruthy();
122
+ // …but no +0 −0 stat is rendered anywhere.
123
+ expect(screen.queryByText("+0")).toBeNull();
124
+ });
125
+ });
@@ -5,6 +5,7 @@ import {
5
5
  WorkspaceEntrySchema,
6
6
  WorkspaceSourceSchema,
7
7
  LocalPathSourceSchema,
8
+ GitRepoSourceSchema,
8
9
  } from "@stigmer/protos/ai/stigmer/agentic/session/v1/workspace_pb";
9
10
  import type { WorkspaceEntry } from "@stigmer/protos/ai/stigmer/agentic/session/v1/workspace_pb";
10
11
  import { FilePathLink } from "../FilePathLink";
@@ -89,4 +90,90 @@ describe("FilePathLink interaction", () => {
89
90
  expect(onFilePathClick.mock.calls[0][0]).toBe("src/main.go");
90
91
  expect(onFilePathClick.mock.calls[0][1].action).toBe("copy");
91
92
  });
93
+
94
+ it("falls back to copy when a copy-action handler declines (returns false)", async () => {
95
+ const writeText = vi.fn().mockResolvedValue(undefined);
96
+ vi.stubGlobal("navigator", { clipboard: { writeText } });
97
+ const onFilePathClick = vi.fn().mockReturnValue(false);
98
+ render(
99
+ <FilePathContext.Provider value={{ workspaceEntries: [], onFilePathClick }}>
100
+ <FilePathLink path="src/main.go" />
101
+ </FilePathContext.Provider>,
102
+ );
103
+ fireEvent.click(screen.getByRole("button"));
104
+ expect(onFilePathClick).toHaveBeenCalledTimes(1);
105
+ expect(writeText).toHaveBeenCalledWith("src/main.go");
106
+ vi.unstubAllGlobals();
107
+ });
108
+
109
+ it("suppresses the copy default when a copy-action handler returns void", () => {
110
+ const writeText = vi.fn().mockResolvedValue(undefined);
111
+ vi.stubGlobal("navigator", { clipboard: { writeText } });
112
+ const onFilePathClick = vi.fn(); // returns undefined -> handled
113
+ render(
114
+ <FilePathContext.Provider value={{ workspaceEntries: [], onFilePathClick }}>
115
+ <FilePathLink path="src/main.go" />
116
+ </FilePathContext.Provider>,
117
+ );
118
+ fireEvent.click(screen.getByRole("button"));
119
+ expect(onFilePathClick).toHaveBeenCalledTimes(1);
120
+ expect(writeText).not.toHaveBeenCalled();
121
+ vi.unstubAllGlobals();
122
+ });
123
+ });
124
+
125
+ describe("FilePathLink link-action with an injected handler", () => {
126
+ const gitEntries = [
127
+ create(WorkspaceEntrySchema, {
128
+ name: "acme/app",
129
+ source: create(WorkspaceSourceSchema, {
130
+ source: {
131
+ case: "gitRepo",
132
+ value: create(GitRepoSourceSchema, {
133
+ url: "https://github.com/acme/app.git",
134
+ branch: "main",
135
+ }),
136
+ },
137
+ }),
138
+ }),
139
+ ];
140
+
141
+ it("renders a real <a href> even when a handler is present (native link semantics)", () => {
142
+ const onFilePathClick = vi.fn().mockReturnValue(true);
143
+ render(
144
+ <FilePathContext.Provider value={{ workspaceEntries: gitEntries, onFilePathClick }}>
145
+ <FilePathLink path="src/main.go" />
146
+ </FilePathContext.Provider>,
147
+ );
148
+ const anchor = screen.getByRole("link");
149
+ expect(anchor.getAttribute("href")).toBe(
150
+ "https://github.com/acme/app/blob/main/src/main.go",
151
+ );
152
+ });
153
+
154
+ it("prevents native navigation when the handler opens it in-app (returns true)", () => {
155
+ const onFilePathClick = vi.fn().mockReturnValue(true);
156
+ render(
157
+ <FilePathContext.Provider value={{ workspaceEntries: gitEntries, onFilePathClick }}>
158
+ <FilePathLink path="src/main.go" />
159
+ </FilePathContext.Provider>,
160
+ );
161
+ const evt = new MouseEvent("click", { bubbles: true, cancelable: true });
162
+ screen.getByRole("link").dispatchEvent(evt);
163
+ expect(onFilePathClick).toHaveBeenCalledTimes(1);
164
+ expect(evt.defaultPrevented).toBe(true);
165
+ });
166
+
167
+ it("leaves native navigation intact when the handler declines (returns false)", () => {
168
+ const onFilePathClick = vi.fn().mockReturnValue(false);
169
+ render(
170
+ <FilePathContext.Provider value={{ workspaceEntries: gitEntries, onFilePathClick }}>
171
+ <FilePathLink path="src/main.go" />
172
+ </FilePathContext.Provider>,
173
+ );
174
+ const evt = new MouseEvent("click", { bubbles: true, cancelable: true });
175
+ screen.getByRole("link").dispatchEvent(evt);
176
+ expect(onFilePathClick).toHaveBeenCalledTimes(1);
177
+ expect(evt.defaultPrevented).toBe(false);
178
+ });
92
179
  });
@@ -248,6 +248,16 @@ describe("FileReviewCard", () => {
248
248
  });
249
249
 
250
250
  describe("multi-file set (per-file controls)", () => {
251
+ it("starts a complete multi-file set collapsed in diff mode — auto-expansion is list-mode only", () => {
252
+ // Diff mode's expanded body is every full diff with NO height cap (the
253
+ // workflow file-review list); auto-expanding a multi-file set there
254
+ // would dump every diff open on mount. Only the compact list mode
255
+ // (showDiffs=false, the dock) starts multi-file sets expanded.
256
+ render(<FileReviewCard fileChangeSet={multiChangeSet()} onSubmit={noop} />);
257
+ expect(document.querySelector('[data-cursor-target="file-diff"]')).toBeNull();
258
+ expect(screen.getByRole("button", { name: "Review" })).toBeTruthy();
259
+ });
260
+
251
261
  it("renders a Keep/Discard radio per file plus whole-set controls", () => {
252
262
  render(<FileReviewCard fileChangeSet={multiChangeSet()} onSubmit={noop} />);
253
263
  expandCard();
@@ -848,11 +858,12 @@ describe("FileReviewCard", () => {
848
858
  // cases mirror the decision cases above without the diff DOM. The default
849
859
  // (showDiffs unset) stays covered by every other suite in this file.
850
860
  describe("list mode (showDiffs=false)", () => {
861
+ /** Open a still-collapsed card's file inventory (single complete file). */
851
862
  function expandFiles() {
852
863
  fireEvent.click(screen.getByRole("button", { name: "Files" }));
853
864
  }
854
865
 
855
- it("labels the expander 'Files' and renders inert list rows with no diff DOM", () => {
866
+ it("starts a multi-file set expanded the per-file controls are the honest decision surface", () => {
856
867
  render(
857
868
  <FileReviewCard
858
869
  fileChangeSet={multiChangeSet()}
@@ -860,17 +871,65 @@ describe("FileReviewCard", () => {
860
871
  showDiffs={false}
861
872
  />,
862
873
  );
863
- expandFiles();
874
+ // No expander click: the list rows and per-file controls are immediately
875
+ // visible, with no diff DOM (the transcript's stamped rows own the diffs).
864
876
  expect(document.querySelector('[data-cursor-target="file-diff"]')).toBeNull();
865
877
  expect(
866
878
  document.querySelectorAll('[data-cursor-target="file-review-list-row"]'),
867
879
  ).toHaveLength(2);
868
- // Filename-first path text, full path available on hover — and no
869
- // link/copy affordance (the list is an inventory, not navigation).
880
+ expect(screen.getByRole("button", { name: "Hide" })).toBeTruthy();
881
+ // Filename-first linked path (FilePathLink: copy fallback without a
882
+ // provider — an accessible button, never a bare span), full path on hover.
870
883
  expect(screen.getByText("a.ts")).toBeTruthy();
871
884
  expect(screen.getByText("b.ts")).toBeTruthy();
885
+ expect(screen.getByRole("button", { name: /Copy path: src\/a\.ts/ })).toBeTruthy();
872
886
  expect(screen.getByTitle("src/a.ts")).toBeTruthy();
873
- expect(screen.queryByRole("link")).toBeNull();
887
+ });
888
+
889
+ it("keeps a single complete file collapsed behind the 'Files' expander", () => {
890
+ render(
891
+ <FileReviewCard fileChangeSet={changeSet()} onSubmit={noop} showDiffs={false} />,
892
+ );
893
+ expect(
894
+ document.querySelector('[data-cursor-target="file-review-list-row"]'),
895
+ ).toBeNull();
896
+ fireEvent.click(screen.getByRole("button", { name: "Files" }));
897
+ expect(
898
+ document.querySelector('[data-cursor-target="file-review-list-row"]'),
899
+ ).toBeTruthy();
900
+ });
901
+
902
+ it("shows per-file +N −M from the capture-time counts, hiding the stat when no count exists", () => {
903
+ const counted = create(CapturedFileChangeSchema, {
904
+ id: "fc1",
905
+ pathBefore: "src/a.ts",
906
+ pathAfter: "src/a.ts",
907
+ kind: FileChangeKind.MODIFY,
908
+ captureClass: FileCaptureClass.GIT_TRACKED,
909
+ before: inline("old\n"),
910
+ after: inline("new\n"),
911
+ fileDigest: "d-fc1",
912
+ diffComplete: true,
913
+ linesAdded: 37,
914
+ linesRemoved: 2,
915
+ });
916
+ render(
917
+ <FileReviewCard
918
+ fileChangeSet={multiChangeSet({ fc1: counted })}
919
+ onSubmit={noop}
920
+ showDiffs={false}
921
+ />,
922
+ );
923
+ const rows = document.querySelectorAll('[data-cursor-target="file-review-list-row"]');
924
+ // fc1 carries counts; fc2 (legacy fixture, zero counts) shows no stat —
925
+ // never a misleading "+0 -0". The bar also aggregates the set's counts,
926
+ // so exactly two stats render: the bar's and fc1's row.
927
+ const stats = document.querySelectorAll('[data-cursor-target="file-review-line-stats"]');
928
+ expect(rows).toHaveLength(2);
929
+ expect(stats).toHaveLength(2);
930
+ expect(stats[0].textContent).toBe("+37 -2");
931
+ expect(stats[1].textContent).toBe("+37 -2");
932
+ expect(screen.getAllByLabelText("37 added, 2 removed")).toHaveLength(2);
874
933
  });
875
934
 
876
935
  it("marks each file with its change-kind letter (A/M/D)", () => {
@@ -899,7 +958,7 @@ describe("FileReviewCard", () => {
899
958
  showDiffs={false}
900
959
  />,
901
960
  );
902
- expandFiles();
961
+ // A multi-file list-mode set starts expanded — the letters are visible.
903
962
  expect(screen.getByLabelText("added").textContent).toBe("A");
904
963
  expect(screen.getByLabelText("deleted").textContent).toBe("D");
905
964
  });
@@ -937,7 +996,7 @@ describe("FileReviewCard", () => {
937
996
  showDiffs={false}
938
997
  />,
939
998
  );
940
- expandFiles();
999
+ // Starts expanded (multi-file list mode) — the row control is live.
941
1000
  fireEvent.click(screen.getByRole("radio", { name: "Keep src/a.ts" }));
942
1001
  expect(onSubmit).toHaveBeenCalledWith(FileDecisionAction.APPROVE, {
943
1002
  scope: FileDecisionScope.FILE,
@@ -784,6 +784,50 @@ describe("MessageThread", () => {
784
784
  });
785
785
  });
786
786
 
787
+ describe("live plan collapse (streaming Plan turn)", () => {
788
+ function streamingPlanExecution() {
789
+ return makeExecution({
790
+ id: "exec-live",
791
+ phase: ExecutionPhase.EXECUTION_IN_PROGRESS,
792
+ interactionMode: InteractionMode.PLAN,
793
+ aiContent: "# Rollout Plan\n\n1. First step",
794
+ });
795
+ }
796
+
797
+ it("collapses the streaming plan behind a live card when onOpenPlan is wired", () => {
798
+ const onOpenPlan = vi.fn();
799
+ render(
800
+ <MessageThread
801
+ executions={[]}
802
+ activeStreamExecution={streamingPlanExecution()}
803
+ onOpenPlan={onOpenPlan}
804
+ />,
805
+ );
806
+
807
+ const card = screen.getByRole("region", { name: "Plan being written" });
808
+ expect(card.textContent).toContain("Rollout Plan");
809
+ // The plan body lives in the panel's plan tab, not the thread.
810
+ expect(screen.queryByText(/First step/)).toBeNull();
811
+
812
+ fireEvent.click(screen.getByText("Open plan"));
813
+ expect(onOpenPlan).toHaveBeenCalledWith("exec-live");
814
+ });
815
+
816
+ it("keeps the plan streaming inline for hosts without a plan surface (DD-011)", () => {
817
+ render(
818
+ <MessageThread
819
+ executions={[]}
820
+ activeStreamExecution={streamingPlanExecution()}
821
+ />,
822
+ );
823
+
824
+ expect(
825
+ screen.queryByRole("region", { name: "Plan being written" }),
826
+ ).toBeNull();
827
+ expect(screen.getByText(/First step/)).toBeTruthy();
828
+ });
829
+ });
830
+
787
831
  it("renders plan-completion card when last execution is completed Plan mode", () => {
788
832
  const exec = makeExecution({
789
833
  id: "exec-plan",
@@ -800,10 +844,67 @@ describe("MessageThread", () => {
800
844
  />,
801
845
  );
802
846
 
803
- const buildBtn = screen.getByRole("button", { name: /build from plan/i });
847
+ const buildBtn = screen.getByRole("button", { name: "Build" });
804
848
  expect(buildBtn).toBeTruthy();
805
849
 
806
850
  fireEvent.click(buildBtn);
807
851
  expect(onBuildFromPlan).toHaveBeenCalledOnce();
808
852
  });
853
+
854
+ describe("plan-turn identity", () => {
855
+ it("marks a Plan turn's prompt bubble with the Plan badge", () => {
856
+ const exec = makeExecution({
857
+ id: "exec-plan",
858
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
859
+ interactionMode: InteractionMode.PLAN,
860
+ });
861
+
862
+ render(<MessageThread executions={[exec]} />);
863
+
864
+ expect(screen.getByRole("status", { name: "Plan mode" })).toBeTruthy();
865
+ });
866
+
867
+ it("shows no mode badge on Agent turns", () => {
868
+ const exec = makeExecution({
869
+ id: "exec-agent",
870
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
871
+ interactionMode: InteractionMode.AGENT,
872
+ });
873
+
874
+ render(<MessageThread executions={[exec]} />);
875
+
876
+ expect(screen.queryByRole("status", { name: "Plan mode" })).toBeNull();
877
+ });
878
+
879
+ it("renders a completed Plan turn's plan as a document", () => {
880
+ const exec = makeExecution({
881
+ id: "exec-plan",
882
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
883
+ interactionMode: InteractionMode.PLAN,
884
+ aiContent: "# Rollout Plan\n\n1. First step",
885
+ });
886
+
887
+ render(<MessageThread executions={[exec]} />);
888
+
889
+ const doc = screen.getByRole("article", { name: "Plan document" });
890
+ expect(doc.textContent).toContain("Rollout Plan");
891
+ expect(doc.textContent).toContain("First step");
892
+ });
893
+
894
+ it("renders a streaming Plan turn's text as an ordinary chat message", () => {
895
+ const exec = makeExecution({
896
+ id: "exec-live",
897
+ phase: ExecutionPhase.EXECUTION_IN_PROGRESS,
898
+ interactionMode: InteractionMode.PLAN,
899
+ aiContent: "Draft so far",
900
+ });
901
+
902
+ render(<MessageThread executions={[]} activeStreamExecution={exec} />);
903
+
904
+ expect(
905
+ screen.queryByRole("article", { name: "Plan document" }),
906
+ ).toBeNull();
907
+ expect(screen.getByRole("article", { name: "AI response" })).toBeTruthy();
908
+ });
909
+ });
809
910
  });