@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
@@ -9,29 +9,67 @@ import {
9
9
  } from "react";
10
10
  import { cn } from "@stigmer/theme";
11
11
 
12
+ /** Which pane carries the drag-controlled pixel width; its sibling flexes. */
13
+ export type ResizablePane = "primary" | "secondary";
14
+
15
+ /**
16
+ * Which pane (if any) collapses below the `lg` breakpoint, leaving its sibling
17
+ * full-width. Use this to hide the pixel-sized pane on narrow screens — always
18
+ * pair it with {@link ResizableSplitProps.resizablePane} (hide the fixed pane so
19
+ * the visible sibling is the flexing one and fills the row). `"none"` keeps both
20
+ * panes at every width.
21
+ */
22
+ export type ResizableCollapse = "primary" | "secondary" | "none";
23
+
12
24
  /** Props for {@link ResizableSplit}. */
13
25
  export interface ResizableSplitProps {
14
- /** Content rendered in the flexible (left) region. */
26
+ /** Content rendered in the first (left) region. */
15
27
  readonly primary: ReactNode;
16
- /** Content rendered in the resizable (right) panel. */
28
+ /** Content rendered in the second (right) region. */
17
29
  readonly secondary: ReactNode;
18
30
  /**
19
- * Default width of the secondary panel in pixels.
31
+ * Which pane is pixel-sized and drag-resizable; the other flexes to fill.
32
+ * Child render order is fixed (`primary` then `secondary`) regardless of this
33
+ * value, so flipping it re-flows the layout without remounting either child.
34
+ * @default "secondary"
35
+ */
36
+ readonly resizablePane?: ResizablePane;
37
+ /**
38
+ * Default width of the resizable pane in pixels.
20
39
  * Overridden by a persisted value when `storageKey` is set.
21
40
  * @default 384
22
41
  */
23
42
  readonly defaultSize?: number;
24
- /** Minimum width of the secondary panel in pixels. @default 280 */
43
+ /** Minimum width of the resizable pane in pixels. @default 280 */
25
44
  readonly minSize?: number;
26
- /** Maximum width of the secondary panel in pixels. @default 800 */
45
+ /** Maximum width of the resizable pane in pixels. @default 800 */
27
46
  readonly maxSize?: number;
28
47
  /**
29
- * `localStorage` key for persisting the panel width across sessions.
30
- * When omitted, the panel width resets to `defaultSize` on remount.
48
+ * `localStorage` key for persisting the pane width across sessions.
49
+ * When omitted, the pane width resets to `defaultSize` on remount.
50
+ * Changing this key (or `resizablePane`) re-initializes the width from the
51
+ * new key without remounting — the mechanism the session layout uses to keep
52
+ * a distinct persisted width per mode.
31
53
  */
32
54
  readonly storageKey?: string;
55
+ /** Which pane collapses below the `lg` breakpoint. @default "none" */
56
+ readonly responsiveCollapse?: ResizableCollapse;
57
+ /**
58
+ * Which pane (if any) is collapsed at every width, hiding the drag handle and
59
+ * letting the sibling fill the row. Unlike conditional rendering at the call
60
+ * site, collapsing through this prop keeps both children mounted at stable
61
+ * tree positions — the session panel uses it so opening/closing the panel
62
+ * never remounts the conversation. The persisted width survives a collapse
63
+ * and applies again on expand.
64
+ * @default "none"
65
+ */
66
+ readonly collapsedPane?: ResizableCollapse;
67
+ /**
68
+ * Accessible label for the drag separator. @default "Resize panel"
69
+ */
70
+ readonly ariaLabel?: string;
33
71
  /**
34
- * Called whenever the secondary panel width changes (during drag
72
+ * Called whenever the resizable pane width changes (during drag
35
73
  * and on initial mount from persisted state). Consumers use this
36
74
  * to thread the width into layout-dependent calculations like
37
75
  * `WorkflowExecutionGraph.panelOffsetPx`.
@@ -44,62 +82,111 @@ export interface ResizableSplitProps {
44
82
  const KEYBOARD_STEP_PX = 20;
45
83
 
46
84
  /**
47
- * Horizontal split layout with a drag-resizable right panel.
85
+ * Read the initial pane width for a given storage key, falling back to
86
+ * `defaultSize` when there is no valid persisted value. Pure of React state so
87
+ * it can seed `useState` and re-seed on a key/pane change.
88
+ */
89
+ function readInitialWidth(
90
+ storageKey: string | undefined,
91
+ defaultSize: number,
92
+ minSize: number,
93
+ maxSize: number,
94
+ ): number {
95
+ if (storageKey) {
96
+ try {
97
+ const stored = localStorage.getItem(storageKey);
98
+ if (stored) {
99
+ const parsed = Number(stored);
100
+ if (Number.isFinite(parsed) && parsed >= minSize && parsed <= maxSize) {
101
+ return parsed;
102
+ }
103
+ }
104
+ } catch {
105
+ /* localStorage may be unavailable */
106
+ }
107
+ }
108
+ return defaultSize;
109
+ }
110
+
111
+ /**
112
+ * Horizontal split layout with one drag-resizable pixel-sized pane and one
113
+ * flexible pane.
48
114
  *
49
- * The left region (`primary`) fills remaining space via `flex: 1`.
50
- * The right region (`secondary`) has a pixel-based width controlled
51
- * by a draggable divider.
115
+ * By default the right region (`secondary`) is pixel-sized and the left
116
+ * (`primary`) flexes. Set `resizablePane="primary"` to flip which side carries
117
+ * the width — the session layout uses this to invert between a chat-dominant
118
+ * layout (inspector on the right, fixed) and a workspace-dominant layout (chat
119
+ * on the left, fixed). Render order never changes, so flipping re-flows the row
120
+ * without remounting either child (preserving streaming/scroll state).
52
121
  *
53
122
  * Design:
54
123
  * - Pointer-based drag with `setPointerCapture` for reliable tracking
55
124
  * - `requestAnimationFrame` coalescing during drag (DD-009 pattern)
56
- * - Keyboard accessible: Left/Right arrows nudge by {@link KEYBOARD_STEP_PX}
57
- * - Optional `localStorage` persistence via `storageKey`
125
+ * - Keyboard accessible: arrow keys nudge by {@link KEYBOARD_STEP_PX}, oriented
126
+ * so the pane always grows toward its own side
127
+ * - Optional `localStorage` persistence via `storageKey`, re-initialized when
128
+ * the key or resizable side changes (no remount required)
129
+ * - Optional responsive collapse of the pixel pane below `lg`
58
130
  * - Styled with `--stgm-*` tokens; no hardcoded colors
59
131
  *
60
132
  * @example
61
133
  * ```tsx
134
+ * // Chat-dominant: inspector fixed on the right (default)
135
+ * <ResizableSplit
136
+ * primary={<Conversation />}
137
+ * secondary={<Inspector />}
138
+ * storageKey="stgm-session-inspector-width"
139
+ * responsiveCollapse="secondary"
140
+ * />
141
+ *
142
+ * // Workspace-dominant: chat fixed on the left
62
143
  * <ResizableSplit
63
- * primary={<WorkflowGraph />}
64
- * secondary={<InspectorPanel />}
65
- * defaultSize={384}
66
- * minSize={280}
67
- * maxSize={800}
68
- * storageKey="wf-exec-inspector-width"
69
- * onResize={setPanelWidth}
144
+ * resizablePane="primary"
145
+ * primary={<Conversation />}
146
+ * secondary={<WorkspaceSurface />}
147
+ * storageKey="stgm-session-chat-width"
148
+ * responsiveCollapse="primary"
70
149
  * />
71
150
  * ```
72
151
  */
73
152
  export function ResizableSplit({
74
153
  primary,
75
154
  secondary,
155
+ resizablePane = "secondary",
76
156
  defaultSize = 384,
77
157
  minSize = 280,
78
158
  maxSize = 800,
79
159
  storageKey,
160
+ responsiveCollapse = "none",
161
+ collapsedPane = "none",
162
+ ariaLabel = "Resize panel",
80
163
  onResize,
81
164
  className,
82
165
  }: ResizableSplitProps) {
83
- const [panelWidth, setPanelWidth] = useState(() => {
84
- if (storageKey) {
85
- try {
86
- const stored = localStorage.getItem(storageKey);
87
- if (stored) {
88
- const parsed = Number(stored);
89
- if (Number.isFinite(parsed) && parsed >= minSize && parsed <= maxSize) {
90
- return parsed;
91
- }
92
- }
93
- } catch { /* localStorage may be unavailable */ }
94
- }
95
- return defaultSize;
96
- });
166
+ const isPrimaryResizable = resizablePane === "primary";
167
+ const isCollapsed = collapsedPane !== "none";
168
+
169
+ const [panelWidth, setPanelWidth] = useState(() =>
170
+ readInitialWidth(storageKey, defaultSize, minSize, maxSize),
171
+ );
172
+
173
+ // Re-initialize the width when the storage key or resizable side changes —
174
+ // the session layout swaps both together on a mode flip and expects the width
175
+ // to come from the new key, not carry over. Adjust-state-during-render
176
+ // (matching `useSessionPanel`) so it lands before paint without a remount.
177
+ const [prevStorageKey, setPrevStorageKey] = useState(storageKey);
178
+ const [prevResizablePane, setPrevResizablePane] = useState(resizablePane);
179
+ if (storageKey !== prevStorageKey || resizablePane !== prevResizablePane) {
180
+ setPrevStorageKey(storageKey);
181
+ setPrevResizablePane(resizablePane);
182
+ setPanelWidth(readInitialWidth(storageKey, defaultSize, minSize, maxSize));
183
+ }
97
184
 
98
185
  const containerRef = useRef<HTMLDivElement>(null);
99
186
  const rafRef = useRef(0);
100
187
  const isDraggingRef = useRef(false);
101
188
 
102
- // Notify consumer of initial width on mount
189
+ // Notify consumer of the current width whenever it changes (incl. on re-init).
103
190
  const onResizeRef = useRef(onResize);
104
191
  onResizeRef.current = onResize;
105
192
  useEffect(() => {
@@ -116,7 +203,9 @@ export function ResizableSplit({
116
203
  if (!storageKey) return;
117
204
  try {
118
205
  localStorage.setItem(storageKey, String(width));
119
- } catch { /* quota or security error */ }
206
+ } catch {
207
+ /* quota or security error */
208
+ }
120
209
  },
121
210
  [storageKey],
122
211
  );
@@ -140,11 +229,15 @@ export function ResizableSplit({
140
229
  const container = containerRef.current;
141
230
  if (!container) return;
142
231
  const rect = container.getBoundingClientRect();
143
- const newWidth = clampWidth(rect.right - clientX);
144
- setPanelWidth(newWidth);
232
+ // The resizable pane's width is the distance from its own edge to the
233
+ // pointer: left edge for a primary pane, right edge for a secondary one.
234
+ const raw = isPrimaryResizable
235
+ ? clientX - rect.left
236
+ : rect.right - clientX;
237
+ setPanelWidth(clampWidth(raw));
145
238
  });
146
239
  },
147
- [clampWidth],
240
+ [clampWidth, isPrimaryResizable],
148
241
  );
149
242
 
150
243
  const handlePointerUp = useCallback(
@@ -160,9 +253,11 @@ export function ResizableSplit({
160
253
 
161
254
  const handleKeyDown = useCallback(
162
255
  (e: React.KeyboardEvent) => {
256
+ // Arrow keys grow the pane toward its own side: a primary (left) pane
257
+ // grows on ArrowRight, a secondary (right) pane grows on ArrowLeft.
163
258
  let delta = 0;
164
- if (e.key === "ArrowLeft") delta = KEYBOARD_STEP_PX;
165
- else if (e.key === "ArrowRight") delta = -KEYBOARD_STEP_PX;
259
+ if (e.key === "ArrowLeft") delta = isPrimaryResizable ? -KEYBOARD_STEP_PX : KEYBOARD_STEP_PX;
260
+ else if (e.key === "ArrowRight") delta = isPrimaryResizable ? KEYBOARD_STEP_PX : -KEYBOARD_STEP_PX;
166
261
  if (delta === 0) return;
167
262
 
168
263
  e.preventDefault();
@@ -172,26 +267,57 @@ export function ResizableSplit({
172
267
  return next;
173
268
  });
174
269
  },
175
- [clampWidth, persist],
270
+ [clampWidth, persist, isPrimaryResizable],
176
271
  );
177
272
 
178
273
  // Cleanup rAF on unmount
179
274
  useEffect(() => () => cancelAnimationFrame(rafRef.current), []);
180
275
 
276
+ const fixedPaneClass = "shrink-0 overflow-hidden";
277
+ const flexPaneClass = "min-w-0 flex-1";
278
+
279
+ // A collapsed pane hides entirely and its sibling flexes to fill the row,
280
+ // overriding the fixed/flex split. Width state is untouched, so expanding
281
+ // restores the previous size.
282
+ const primaryPaneClass =
283
+ collapsedPane === "primary"
284
+ ? "hidden"
285
+ : collapsedPane === "secondary" || !isPrimaryResizable
286
+ ? flexPaneClass
287
+ : fixedPaneClass;
288
+ const secondaryPaneClass =
289
+ collapsedPane === "secondary"
290
+ ? "hidden"
291
+ : collapsedPane === "primary" || isPrimaryResizable
292
+ ? flexPaneClass
293
+ : fixedPaneClass;
294
+
181
295
  return (
182
296
  <div ref={containerRef} className={cn("flex min-h-0 flex-1", className)}>
183
- {/* Primary region (flexible) */}
184
- <div className="min-w-0 flex-1">{primary}</div>
297
+ {/* Primary region (first child, order-stable across flips/collapses) */}
298
+ <div
299
+ className={cn(
300
+ primaryPaneClass,
301
+ responsiveCollapse === "primary" && "max-lg:hidden",
302
+ )}
303
+ style={
304
+ isPrimaryResizable && !isCollapsed ? { width: panelWidth } : undefined
305
+ }
306
+ >
307
+ {primary}
308
+ </div>
185
309
 
186
- {/* Drag handle */}
310
+ {/* Drag handle — CSS-hidden (not unmounted) while collapsed so all three
311
+ children keep stable tree positions across a collapse toggle. */}
187
312
  <div
188
313
  role="separator"
189
314
  aria-orientation="vertical"
190
315
  aria-valuenow={panelWidth}
191
316
  aria-valuemin={minSize}
192
317
  aria-valuemax={maxSize}
193
- aria-label="Resize inspector panel"
194
- tabIndex={0}
318
+ aria-label={ariaLabel}
319
+ aria-hidden={isCollapsed || undefined}
320
+ tabIndex={isCollapsed ? -1 : 0}
195
321
  onPointerDown={handlePointerDown}
196
322
  onPointerMove={handlePointerMove}
197
323
  onPointerUp={handlePointerUp}
@@ -203,16 +329,23 @@ export function ResizableSplit({
203
329
  "focus-visible:bg-[var(--stgm-primary,#6366f1)] focus-visible:outline-none",
204
330
  "active:bg-[var(--stgm-primary,#6366f1)]",
205
331
  "transition-colors duration-100",
332
+ responsiveCollapse !== "none" && "max-lg:hidden",
333
+ isCollapsed && "hidden",
206
334
  )}
207
335
  >
208
336
  {/* Wider invisible hit target (12px) for easier grabbing */}
209
337
  <div className="absolute inset-y-0 -left-1.5 -right-1.5" />
210
338
  </div>
211
339
 
212
- {/* Secondary region (resizable, fixed width) */}
340
+ {/* Secondary region (second child, order-stable across flips/collapses) */}
213
341
  <div
214
- className="shrink-0 overflow-hidden"
215
- style={{ width: panelWidth }}
342
+ className={cn(
343
+ secondaryPaneClass,
344
+ responsiveCollapse === "secondary" && "max-lg:hidden",
345
+ )}
346
+ style={
347
+ !isPrimaryResizable && !isCollapsed ? { width: panelWidth } : undefined
348
+ }
216
349
  >
217
350
  {secondary}
218
351
  </div>
@@ -14,7 +14,9 @@ import type { ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution
14
14
  import { ApprovalAction } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
15
15
  import {
16
16
  ThreadItemRenderer,
17
+ threadContentColumnClass,
17
18
  type ThreadItem,
19
+ type ThreadContentColumn,
18
20
  } from "../execution/MessageThread.js";
19
21
  import { FilePathContext, type FilePathContextValue } from "../execution/FilePathContext.js";
20
22
  import { SandboxContext, type SandboxContextValue } from "../execution/SandboxContext.js";
@@ -45,9 +47,11 @@ export interface VirtualizedThreadProps {
45
47
  readonly fileReviewCtx: FileReviewContextValue;
46
48
  readonly unresolvedApprovalCount: number;
47
49
  readonly onBuildFromPlan?: () => void;
50
+ readonly onOpenPlan?: (executionId: string) => void;
48
51
  readonly org?: string;
49
52
  readonly planActionsDisabled?: boolean;
50
- readonly centerContent?: boolean;
53
+ readonly planBuildPending?: boolean;
54
+ readonly contentColumn?: ThreadContentColumn;
51
55
  readonly onRetrySend?: () => void;
52
56
  readonly onRetryExecution?: (message: string) => void;
53
57
  readonly onEditMessage?: (text: string) => void;
@@ -112,9 +116,11 @@ export function VirtualizedThread({
112
116
  fileReviewCtx,
113
117
  unresolvedApprovalCount,
114
118
  onBuildFromPlan,
119
+ onOpenPlan,
115
120
  org,
116
121
  planActionsDisabled,
117
- centerContent,
122
+ planBuildPending,
123
+ contentColumn,
118
124
  onRetrySend,
119
125
  onRetryExecution,
120
126
  onEditMessage,
@@ -148,13 +154,15 @@ export function VirtualizedThread({
148
154
  submittingApprovalIds,
149
155
  approvalErrors,
150
156
  onBuildFromPlan,
157
+ onOpenPlan,
151
158
  org,
152
159
  planActionsDisabled,
160
+ planBuildPending,
153
161
  onRetrySend,
154
162
  onRetryExecution,
155
163
  onEditMessage,
156
164
  }),
157
- [formatToolCallSummary, onApprovalSubmit, submittingApprovalIds, approvalErrors, onBuildFromPlan, org, planActionsDisabled, onRetrySend, onRetryExecution, onEditMessage],
165
+ [formatToolCallSummary, onApprovalSubmit, submittingApprovalIds, approvalErrors, onBuildFromPlan, onOpenPlan, org, planActionsDisabled, planBuildPending, onRetrySend, onRetryExecution, onEditMessage],
158
166
  );
159
167
 
160
168
  return (
@@ -177,7 +185,7 @@ export function VirtualizedThread({
177
185
  className="h-full"
178
186
  components={{ Scroller: ScrollerWithA11y }}
179
187
  itemContent={(index, item) => (
180
- <div className={cn("pb-4 pt-0 first:pt-6", centerContent && "mx-auto w-full max-w-3xl px-4")}>
188
+ <div className={cn("pb-4 pt-0 first:pt-6", threadContentColumnClass(contentColumn))}>
181
189
  <ThreadItemWrapper animate={index >= items.length - 2}>
182
190
  <ThreadItemRenderer
183
191
  item={item}
@@ -72,6 +72,67 @@ describe("ResizableSplit", () => {
72
72
  });
73
73
  });
74
74
 
75
+ describe("collapsedPane", () => {
76
+ /** The pane wrapper div for a rendered probe. */
77
+ function paneOf(testId: string): HTMLElement {
78
+ return screen.getByTestId(testId).parentElement as HTMLElement;
79
+ }
80
+
81
+ it("hides the collapsed pane and the separator; the sibling flexes", () => {
82
+ renderSplit({ collapsedPane: "secondary" });
83
+ expect(paneOf("secondary").className).toContain("hidden");
84
+ expect(paneOf("primary").className).toContain("flex-1");
85
+ const separator = screen.getByRole("separator", { hidden: true });
86
+ expect(separator.className).toContain("hidden");
87
+ expect(separator.getAttribute("tabindex")).toBe("-1");
88
+ });
89
+
90
+ it("overrides the resizable pane's fixed width while collapsed", () => {
91
+ renderSplit({ resizablePane: "primary", collapsedPane: "secondary" });
92
+ // Primary is normally pixel-sized; with its sibling collapsed it flexes.
93
+ expect(paneOf("primary").style.width).toBe("");
94
+ expect(paneOf("primary").className).toContain("flex-1");
95
+ });
96
+
97
+ it("keeps both children mounted (same DOM nodes) across a collapse toggle", () => {
98
+ const { rerender } = render(
99
+ <ResizableSplit
100
+ resizablePane="primary"
101
+ collapsedPane="secondary"
102
+ primary={<div data-testid="primary">Primary</div>}
103
+ secondary={<div data-testid="secondary">Secondary</div>}
104
+ />,
105
+ );
106
+ const primaryBefore = screen.getByTestId("primary");
107
+ const secondaryBefore = screen.getByTestId("secondary");
108
+ rerender(
109
+ <ResizableSplit
110
+ resizablePane="primary"
111
+ collapsedPane="none"
112
+ primary={<div data-testid="primary">Primary</div>}
113
+ secondary={<div data-testid="secondary">Secondary</div>}
114
+ />,
115
+ );
116
+ // Identical node references — expanding never remounted either pane.
117
+ expect(screen.getByTestId("primary")).toBe(primaryBefore);
118
+ expect(screen.getByTestId("secondary")).toBe(secondaryBefore);
119
+ expect(paneOf("primary").style.width).toBe("384px");
120
+ });
121
+
122
+ it("restores the persisted width when expanding after a collapse", () => {
123
+ localStorage.setItem("collapse-key", "451");
124
+ renderSplit({
125
+ resizablePane: "primary",
126
+ collapsedPane: "secondary",
127
+ storageKey: "collapse-key",
128
+ minSize: 300,
129
+ maxSize: 600,
130
+ });
131
+ const separator = screen.getByRole("separator", { hidden: true });
132
+ expect(separator.getAttribute("aria-valuenow")).toBe("451");
133
+ });
134
+ });
135
+
75
136
  describe("keyboard interaction", () => {
76
137
  it("increases panel width on ArrowLeft", () => {
77
138
  renderSplit({ defaultSize: 400 });
@@ -145,11 +206,72 @@ describe("ResizableSplit", () => {
145
206
  expect(separator.getAttribute("tabindex")).toBe("0");
146
207
  });
147
208
 
148
- it("has accessible label", () => {
209
+ it("has a default accessible label", () => {
149
210
  renderSplit();
150
211
  const separator = screen.getByRole("separator");
151
- expect(separator.getAttribute("aria-label")).toBe(
152
- "Resize inspector panel",
212
+ expect(separator.getAttribute("aria-label")).toBe("Resize panel");
213
+ });
214
+
215
+ it("uses a custom accessible label", () => {
216
+ renderSplit({ ariaLabel: "Resize file explorer" });
217
+ const separator = screen.getByRole("separator");
218
+ expect(separator.getAttribute("aria-label")).toBe("Resize file explorer");
219
+ });
220
+ });
221
+
222
+ describe("resizablePane", () => {
223
+ it("grows a primary pane on ArrowRight (toward its own side)", () => {
224
+ renderSplit({ resizablePane: "primary", defaultSize: 400 });
225
+ const separator = screen.getByRole("separator");
226
+
227
+ fireEvent.keyDown(separator, { key: "ArrowRight" });
228
+
229
+ expect(separator.getAttribute("aria-valuenow")).toBe("420");
230
+ });
231
+
232
+ it("shrinks a primary pane on ArrowLeft", () => {
233
+ renderSplit({ resizablePane: "primary", defaultSize: 400 });
234
+ const separator = screen.getByRole("separator");
235
+
236
+ fireEvent.keyDown(separator, { key: "ArrowLeft" });
237
+
238
+ expect(separator.getAttribute("aria-valuenow")).toBe("380");
239
+ });
240
+ });
241
+
242
+ describe("width re-initialization on key change", () => {
243
+ it("reloads the width from the new storageKey without remounting", () => {
244
+ localStorage.setItem("key-a", "300");
245
+ localStorage.setItem("key-b", "520");
246
+ const { rerender } = render(
247
+ <ResizableSplit
248
+ primary={<div data-testid="primary">Primary</div>}
249
+ secondary={<div data-testid="secondary">Secondary</div>}
250
+ storageKey="key-a"
251
+ minSize={200}
252
+ maxSize={600}
253
+ />,
254
+ );
255
+ const primary = screen.getByTestId("primary");
256
+ expect(screen.getByRole("separator").getAttribute("aria-valuenow")).toBe(
257
+ "300",
258
+ );
259
+
260
+ rerender(
261
+ <ResizableSplit
262
+ primary={<div data-testid="primary">Primary</div>}
263
+ secondary={<div data-testid="secondary">Secondary</div>}
264
+ storageKey="key-b"
265
+ resizablePane="primary"
266
+ minSize={200}
267
+ maxSize={600}
268
+ />,
269
+ );
270
+
271
+ // Same DOM node — the child was re-flowed, not remounted.
272
+ expect(screen.getByTestId("primary")).toBe(primary);
273
+ expect(screen.getByRole("separator").getAttribute("aria-valuenow")).toBe(
274
+ "520",
153
275
  );
154
276
  });
155
277
  });
@@ -3,6 +3,7 @@ import { render, cleanup } from "@testing-library/react";
3
3
  import type { ComponentType, ReactNode } from "react";
4
4
  import {
5
5
  MARKDOWN_COMPONENTS,
6
+ extractLeadingH1,
6
7
  unwrapEnclosingMarkdownFence,
7
8
  } from "../markdown-components";
8
9
 
@@ -63,6 +64,73 @@ describe("unwrapEnclosingMarkdownFence", () => {
63
64
  const once = unwrapEnclosingMarkdownFence(wrapped);
64
65
  expect(unwrapEnclosingMarkdownFence(once)).toBe(once);
65
66
  });
67
+
68
+ describe("allowBareFence (plan-document surfaces)", () => {
69
+ it.each([
70
+ ["a bare whole-body fence", "```\n# Plan\n- step\n```", "# Plan\n- step"],
71
+ [
72
+ "a bare fence with surrounding whitespace",
73
+ "\n```\n# Plan\n```\n",
74
+ "# Plan",
75
+ ],
76
+ [
77
+ "still unwraps the tagged form",
78
+ "```markdown\n# Plan\n```",
79
+ "# Plan",
80
+ ],
81
+ ])("unwraps %s", (_label, input, expected) => {
82
+ expect(unwrapEnclosingMarkdownFence(input, true)).toBe(expected);
83
+ });
84
+
85
+ it.each([
86
+ ["a language-tagged code block", "```python\nprint('hi')\n```"],
87
+ ["an unclosed bare fence (mid-stream)", "```\n# Plan in progr"],
88
+ ["trailing content after the fence", "```\n# Plan\n```\nthen more"],
89
+ ["leading content before the fence", "intro\n```\n# Plan\n```"],
90
+ ["plain markdown (no fence)", "# Plan\n\n- step"],
91
+ ])("leaves %s untouched even with allowBareFence", (_label, input) => {
92
+ expect(unwrapEnclosingMarkdownFence(input, true)).toBe(input);
93
+ });
94
+ });
95
+ });
96
+
97
+ describe("extractLeadingH1", () => {
98
+ it("splits a leading # heading into title and body", () => {
99
+ expect(extractLeadingH1("# The Plan\n\nFirst paragraph.")).toEqual({
100
+ title: "The Plan",
101
+ body: "First paragraph.",
102
+ });
103
+ });
104
+
105
+ it("handles a document that is only a heading", () => {
106
+ expect(extractLeadingH1("# Just a Title")).toEqual({
107
+ title: "Just a Title",
108
+ body: "",
109
+ });
110
+ });
111
+
112
+ it("ignores leading whitespace before the heading", () => {
113
+ expect(extractLeadingH1("\n\n# The Plan\nBody")).toEqual({
114
+ title: "The Plan",
115
+ body: "Body",
116
+ });
117
+ });
118
+
119
+ it("returns null title when the document does not open with an H1", () => {
120
+ const doc = "Intro paragraph.\n\n# Later Heading\nBody";
121
+ expect(extractLeadingH1(doc)).toEqual({ title: null, body: doc });
122
+ });
123
+
124
+ it("does not treat deeper headings as the title", () => {
125
+ const doc = "## Section\nBody";
126
+ expect(extractLeadingH1(doc)).toEqual({ title: null, body: doc });
127
+ });
128
+
129
+ it("trims trailing whitespace from the title", () => {
130
+ expect(extractLeadingH1("# Spaced Title \nBody").title).toBe(
131
+ "Spaced Title",
132
+ );
133
+ });
66
134
  });
67
135
 
68
136
  describe("MARKDOWN_COMPONENTS.code (shared highlight seam)", () => {