@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
@@ -5,13 +5,19 @@ import { cn } from "@stigmer/theme";
5
5
  import type { ResourceRef } from "@stigmer/sdk";
6
6
  import type { UseGitHubConnectionReturn } from "../github/useGitHubConnection.js";
7
7
  import type { WorkspaceFileLister } from "../workspace/WorkspaceFileLister.js";
8
+ import type { WorkspaceFileReader } from "../workspace/WorkspaceFileReader.js";
9
+ import type { WorkspaceContentSearcher } from "../workspace/WorkspaceContentSearcher.js";
8
10
  import type { InteractionModeOption } from "../composer/index.js";
9
11
  import { SessionComposer } from "../composer/index.js";
10
12
  import type { HarnessOption } from "../models/harness.js";
11
13
  import { ResizableSplit } from "../internal/ResizableSplit.js";
12
- import { SessionInspector } from "./inspector/SessionInspector.js";
13
- import type { SetupTabProps } from "./inspector/SetupTab.js";
14
+ import { useWorkspaceEditors } from "../internal/store/index.js";
15
+ import { WorkspaceSurface } from "../workspace/WorkspaceSurface.js";
16
+ import type { SetupTabProps } from "./facets/SetupTab.js";
14
17
  import { useNewSessionFlow } from "./useNewSessionFlow.js";
18
+ import { useSessionPanel } from "./useSessionPanel.js";
19
+ import { useSessionRailViews } from "./useSessionRailViews.js";
20
+ import { SessionPanelChip } from "./SessionPanelChip.js";
15
21
  import type { RuntimeEnvProvider } from "./runtime-env.js";
16
22
  import type { SessionAudience } from "./audience.js";
17
23
 
@@ -47,6 +53,20 @@ export interface NewSessionViewerProps {
47
53
  */
48
54
  readonly workspaceFileLister?: WorkspaceFileLister;
49
55
 
56
+ /**
57
+ * Platform-injected content reader for the read-only file viewer. When set,
58
+ * clicking a file in the Workspace tree opens it in a contextual "Viewer"
59
+ * tab — the same capability `SessionViewer` accepts (DD-016 parity).
60
+ */
61
+ readonly workspaceFileReader?: WorkspaceFileReader;
62
+
63
+ /**
64
+ * Platform-injected content (text) searcher — the same capability
65
+ * `SessionViewer` accepts (DD-016 parity). Desktop injects a native
66
+ * ripgrep-backed searcher; web leaves it undefined (DD-09).
67
+ */
68
+ readonly workspaceContentSearcher?: WorkspaceContentSearcher;
69
+
50
70
  /**
51
71
  * Supplies host-app environment variables for the session's first
52
72
  * execution (e.g. short-lived credentials for MCP tools, minted as
@@ -119,9 +139,10 @@ export interface NewSessionViewerProps {
119
139
  * Owns `useNewSessionFlow` internally and composes:
120
140
  * - **Centered composer** (primary pane): `SessionComposer` with all
121
141
  * context pickers (agent, workspace, MCP servers, skills)
122
- * - **SessionInspector** (secondary pane): progressively revealed when
123
- * context is attached (workspace/agent/MCP/skills/vars non-empty),
124
- * showing only the Setup tab with interactive workspace actions.
142
+ * - **Session panel** (secondary pane): the unified `WorkspaceSurface` with a
143
+ * Config rail view collapsed by default behind a persistent top-right chip
144
+ * and homing on the Config facet when opened. Same layout model as
145
+ * `SessionViewer` (DD-016).
125
146
  *
126
147
  * Framework-agnostic — no Next.js, no Tauri, no routing deps. Host
127
148
  * apps inject platform-specific values via props (DD-004/DD-016).
@@ -156,6 +177,8 @@ export function NewSessionViewer({
156
177
  enableLocal = false,
157
178
  onBrowseLocalFolder,
158
179
  workspaceFileLister,
180
+ workspaceFileReader,
181
+ workspaceContentSearcher,
159
182
  getRuntimeEnv,
160
183
  audience = "integrator",
161
184
  defaultHarness,
@@ -179,12 +202,25 @@ export function NewSessionViewer({
179
202
  const [interactionMode, setInteractionMode] = useState<InteractionModeOption>("agent");
180
203
  const isEndUser = audience === "endUser";
181
204
 
182
- const hasContext =
183
- flow.workspace.hasEntries ||
184
- flow.agentRef !== null ||
185
- flow.mcpServerUsages.length > 0 ||
186
- flow.skillRefs.length > 0 ||
187
- (flow.sessionVariables != null && !flow.sessionVariables.isEmpty);
205
+ // The unified-panel controller (shared with SessionViewer, DD-016). The
206
+ // launcher has no execution yet, so the FSM inputs are static. It has no
207
+ // streaming column to isolate either, so subscribing to the editor group in
208
+ // the body is harmless — unlike `SessionViewer`, which subscribes one level
209
+ // down. Homes on Config: pre-session the Explorer is empty, while Config
210
+ // carries the run defaults (harness/model) worth seeing before starting.
211
+ const panel = useSessionPanel({
212
+ phase: null,
213
+ hasChanges: false,
214
+ defaultView: "configure",
215
+ });
216
+ const { editors, activeKey, activeFile, reveal } = useWorkspaceEditors(
217
+ panel.editorsStore,
218
+ );
219
+
220
+ // Honor a jump-to-line reveal only while it targets the active editor
221
+ // (DD-016 parity with SessionViewer).
222
+ const activeReveal =
223
+ reveal && reveal.key === activeKey ? reveal : undefined;
188
224
 
189
225
  const handleRemoveAgent = useCallback(() => {
190
226
  flow.setAgentRef(null);
@@ -240,26 +276,34 @@ export function NewSessionViewer({
240
276
  ],
241
277
  );
242
278
 
243
- const workspaceConfig = useMemo(
244
- () => ({
245
- actions: {
246
- workspace: flow.workspace,
247
- enableGitHub,
248
- enableLocal,
249
- gitHubConnection,
250
- onBrowseLocalFolder,
251
- workspaceFileLister,
252
- },
253
- }),
254
- [flow.workspace, enableGitHub, enableLocal, gitHubConnection, onBrowseLocalFolder, workspaceFileLister],
255
- );
279
+ // Launcher facets: Config only — no executions exist yet, so the
280
+ // execution-derived facets (Changes/Artifacts/Usage) don't apply.
281
+ const railViews = useSessionRailViews({
282
+ allExecutions: [],
283
+ org,
284
+ sessionConfig,
285
+ selectedItem: null,
286
+ includeExecutionFacets: false,
287
+ });
288
+
289
+ // Explorer-footer folder attach (desktop only — needs the native picker).
290
+ const canAddLocalFolder = enableLocal && !!onBrowseLocalFolder;
291
+ const handleAddLocalFolder = useCallback(async () => {
292
+ const path = await onBrowseLocalFolder?.();
293
+ if (path) flow.workspace.addLocalPath(path);
294
+ }, [onBrowseLocalFolder, flow.workspace.addLocalPath]);
256
295
 
257
296
  const composerNode = (
258
- <div className="flex h-full flex-col items-center overflow-y-auto px-4">
259
- <div className={cn(
260
- "w-full max-w-2xl space-y-6",
261
- hasContext ? "my-6" : "my-auto",
262
- )}>
297
+ // `my-auto` centers the composer vertically whether or not context is
298
+ // attached — safe-centering, not a special case: auto margins absorb free
299
+ // space when the composer is shorter than the pane and collapse to zero
300
+ // when it outgrows it, degrading to a normal top-aligned scroll. The
301
+ // scroll container's `py-6` keeps breathing room in that overflow case.
302
+ // Do NOT reintroduce a context-driven position flip: the composer must not
303
+ // move as context is attached (DD-16's layout-stability rationale — chrome
304
+ // that shifts with attached context reads as instability).
305
+ <div className="flex h-full flex-col items-center overflow-y-auto px-4 py-6">
306
+ <div className="my-auto w-full max-w-2xl space-y-6">
263
307
  <h1 className="text-center text-lg font-medium text-foreground">
264
308
  {heading}
265
309
  </h1>
@@ -308,38 +352,62 @@ export function NewSessionViewer({
308
352
  </div>
309
353
  );
310
354
 
311
- if (!hasContext) {
312
- return (
313
- <div className={cn("flex h-full w-full flex-col", className)}>
314
- {composerNode}
355
+ return (
356
+ <div className={cn("relative flex h-full w-full flex-col", className)}>
357
+ {/* Persistent chrome: the chip is always mounted, matching SessionViewer
358
+ (DD-016) rather than the launcher's earlier progressively-revealed
359
+ inspector. A toggle that appears/disappears with attached context
360
+ reads as instability and, worse, unmounts the open panel's only
361
+ collapse control when the last context item is removed. Always-on is
362
+ the predictable, discoverable shape; opening homes on the Config
363
+ facet, which carries useful defaults (harness/model) pre-session. */}
364
+ <div className="absolute top-2 right-6 z-10">
365
+ <SessionPanelChip
366
+ isOpen={panel.isOpen}
367
+ onToggle={panel.isOpen ? panel.closePanel : panel.openPanel}
368
+ />
315
369
  </div>
316
- );
317
- }
318
370
 
319
- return (
320
- <div className={cn("flex h-full w-full flex-col", className)}>
371
+ {/* Same unified-panel layout as SessionViewer (DD-016): collapsed by
372
+ default (composer fills the row); opening makes the composer the
373
+ fixed narrow pane and hands the flexible region to the surface.
374
+ Collapse goes through the split's `collapsedPane` (CSS, not
375
+ conditional structure), so the composer never remounts across an
376
+ open/close toggle. */}
321
377
  <ResizableSplit
322
- defaultSize={320}
323
- minSize={240}
324
- maxSize={480}
325
- storageKey="stgm-new-session-inspector-width"
326
- className={cn(
327
- "min-h-0 flex-1",
328
- "[&>*:nth-child(2)]:max-lg:hidden [&>*:nth-child(3)]:max-lg:hidden",
329
- )}
378
+ resizablePane="primary"
379
+ collapsedPane={panel.isOpen ? "none" : "secondary"}
380
+ defaultSize={420}
381
+ minSize={320}
382
+ maxSize={640}
383
+ storageKey="stgm-new-session-chat-width"
384
+ responsiveCollapse={panel.isOpen ? "primary" : "none"}
385
+ ariaLabel="Resize composer panel"
386
+ className="min-h-0 flex-1"
330
387
  primary={composerNode}
331
388
  secondary={
332
- <aside className="flex h-full flex-col overflow-hidden">
333
- <SessionInspector
334
- displayExecution={null}
335
- allExecutions={[]}
336
- org={org}
337
- selectedItem={null}
338
- sessionConfig={sessionConfig}
339
- workspaceConfig={workspaceConfig}
340
- className="min-h-0 flex-1"
389
+ panel.isOpen ? (
390
+ <WorkspaceSurface
391
+ entries={flow.workspace.entries}
392
+ lister={workspaceFileLister}
393
+ reader={workspaceFileReader}
394
+ searcher={workspaceContentSearcher}
395
+ view={panel.view}
396
+ onViewChange={panel.setView}
397
+ extraViews={railViews}
398
+ onRemoveEntry={flow.workspace.remove}
399
+ onAddLocalFolder={canAddLocalFolder ? handleAddLocalFolder : undefined}
400
+ editors={editors}
401
+ selectedFile={activeFile}
402
+ onOpenFile={panel.openFile}
403
+ onActivateEditor={panel.activateEditor}
404
+ onPinEditor={panel.pinEditor}
405
+ onCloseEditor={panel.closeEditor}
406
+ onCollapse={panel.closePanel}
407
+ reveal={activeReveal}
408
+ className="h-full"
341
409
  />
342
- </aside>
410
+ ) : null
343
411
  }
344
412
  />
345
413
  </div>
@@ -0,0 +1,361 @@
1
+ "use client";
2
+
3
+ import { useMemo, useState } from "react";
4
+ import Markdown from "react-markdown";
5
+ import { cn } from "@stigmer/theme";
6
+ import {
7
+ PLAN_DOCUMENT_MARKDOWN_COMPONENTS,
8
+ REMARK_PLUGINS,
9
+ extractLeadingH1,
10
+ unwrapEnclosingMarkdownFence,
11
+ } from "../internal/markdown-components.js";
12
+ import { useArtifactContent } from "../execution/useArtifactContent.js";
13
+ import { useBuildFromPlanHotkey } from "../execution/use-build-from-plan-hotkey.js";
14
+ import { formatArtifactSize } from "../execution/artifact-utils.js";
15
+ import type { SessionPlan } from "../library/detect-plan-artifact.js";
16
+ import type { PlanDraftController } from "./usePlanDraft.js";
17
+
18
+ /** Props for {@link PlanEditor}. */
19
+ export interface PlanEditorProps {
20
+ /** The plan to display (latest, or a superseded plan opened from its card). */
21
+ readonly plan: SessionPlan;
22
+ /**
23
+ * Draft controller owned by the viewer (survives panel collapse/unmounts).
24
+ * Consulted only when editable — a read-only (superseded) plan renders its
25
+ * own artifact text, never the current plan's draft.
26
+ */
27
+ readonly draft?: PlanDraftController;
28
+ /** Builds from the plan — the same action as the thread card's primary. */
29
+ readonly onBuildFromPlan?: () => void;
30
+ /** Disables the Build action (execution in flight, or build submitting). */
31
+ readonly buildDisabled?: boolean;
32
+ /**
33
+ * Renders the plan as a historical, read-only document: no Edit view, no
34
+ * Build action, and a "superseded" notice. Used when the user opens a plan
35
+ * that a newer plan has since replaced.
36
+ */
37
+ readonly readOnly?: boolean;
38
+ }
39
+
40
+ type PlanEditorView = "rendered" | "source" | "edit";
41
+
42
+ /**
43
+ * The plan document for the workspace surface's editor area — the wide-pane
44
+ * successor of the retired Plan sidebar facet, mounted as a virtual document
45
+ * (`SurfaceVirtualDocument`) under the {@link PLAN_DOCUMENT_ENTRY_ID} tab.
46
+ *
47
+ * Three views over one text:
48
+ * - **Rendered** — the plan as a document (same typography as the thread's
49
+ * plan rendering, title lifted from the leading H1).
50
+ * - **Source** — the raw markdown.
51
+ * - **Edit** — a plain-textarea editor over a LOCAL draft
52
+ * ({@link PlanDraftController}); the published artifact is never mutated.
53
+ * Building from an edited plan delivers the draft to the implement
54
+ * execution (edit-as-input).
55
+ *
56
+ * All views show the *effective* plan — the draft when one exists, the
57
+ * published artifact otherwise — so what the user reviews is exactly what
58
+ * Build will implement. An "Edited" indicator plus Revert makes the overlay
59
+ * visible and reversible.
60
+ *
61
+ * Editing is disabled for truncated content (the content RPC caps at 512 KB):
62
+ * editing a truncated plan would silently drop its tail on handoff. The
63
+ * document still renders read-only with the truncation notice.
64
+ *
65
+ * Holds no must-survive local state: the panel region unmounts wholesale on
66
+ * collapse, so anything that must outlive it (the draft, the fetched content
67
+ * cache) is owned above. The view pick resetting to Rendered on reopen is
68
+ * deliberate.
69
+ *
70
+ * All visual properties flow through `--stgm-*` tokens.
71
+ */
72
+ export function PlanEditor({
73
+ plan,
74
+ draft,
75
+ onBuildFromPlan,
76
+ buildDisabled,
77
+ readOnly,
78
+ }: PlanEditorProps) {
79
+ const [view, setView] = useState<PlanEditorView>("rendered");
80
+ const canBuild = !readOnly && onBuildFromPlan !== undefined;
81
+ const handleKeyDown = useBuildFromPlanHotkey(
82
+ canBuild ? onBuildFromPlan : undefined,
83
+ buildDisabled,
84
+ );
85
+
86
+ const { content, isTruncated, isLoading, error, refetch } =
87
+ useArtifactContent(
88
+ plan.executionId,
89
+ plan.artifact.storageKey,
90
+ undefined,
91
+ plan.artifact.contentHash,
92
+ );
93
+
94
+ const activeDraft = readOnly ? undefined : draft;
95
+ const effectiveText = activeDraft?.draftText ?? content;
96
+ // A read-only plan (or one without a draft controller) never offers Edit;
97
+ // a truncated plan only keeps it while an existing draft needs rescuing.
98
+ const canEdit = activeDraft !== undefined && (!isTruncated || activeDraft.isEdited);
99
+ const activeView: PlanEditorView = view === "edit" && !canEdit ? "rendered" : view;
100
+
101
+ if (isLoading) {
102
+ return (
103
+ <PlanEditorFrame>
104
+ <PlanEditorSkeleton />
105
+ </PlanEditorFrame>
106
+ );
107
+ }
108
+
109
+ if (error || effectiveText === null) {
110
+ return (
111
+ <PlanEditorFrame>
112
+ <div className="flex flex-col items-center gap-2 px-4 py-8 text-center">
113
+ <p className="text-xs text-muted-foreground">
114
+ Couldn&rsquo;t load the plan{error ? `: ${error.message}` : "."}
115
+ </p>
116
+ <button
117
+ type="button"
118
+ onClick={refetch}
119
+ className="rounded text-xs font-medium text-foreground underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
120
+ >
121
+ Retry
122
+ </button>
123
+ </div>
124
+ </PlanEditorFrame>
125
+ );
126
+ }
127
+
128
+ return (
129
+ <PlanEditorFrame onKeyDown={handleKeyDown}>
130
+ {/* Toolbar: view tabs lead; state + the one primary action trail.
131
+ Narrow panes are first-class: rows wrap (flex-wrap) and every item
132
+ can shrink (min-w-0 + truncating labels), so the toolbar reflows
133
+ instead of forcing the document pane to scroll sideways. */}
134
+ <div className="flex min-w-0 flex-wrap items-center gap-2">
135
+ <div
136
+ className="inline-flex shrink-0 rounded-md bg-muted p-0.5"
137
+ role="tablist"
138
+ aria-label="Plan view"
139
+ >
140
+ <ViewTab
141
+ active={activeView === "rendered"}
142
+ onClick={() => setView("rendered")}
143
+ label="Rendered"
144
+ />
145
+ <ViewTab
146
+ active={activeView === "source"}
147
+ onClick={() => setView("source")}
148
+ label="Source"
149
+ />
150
+ {activeDraft !== undefined && (
151
+ <ViewTab
152
+ active={activeView === "edit"}
153
+ onClick={() => setView("edit")}
154
+ label="Edit"
155
+ disabled={!canEdit}
156
+ title={
157
+ !canEdit
158
+ ? "This plan is too large to edit in place — download it instead."
159
+ : undefined
160
+ }
161
+ />
162
+ )}
163
+ </div>
164
+ <span className="text-[0.65rem] tabular-nums text-muted-foreground-faint">
165
+ {formatArtifactSize(plan.artifact.sizeBytes)}
166
+ </span>
167
+
168
+ <div className="ml-auto flex min-w-0 flex-wrap items-center gap-3">
169
+ {activeDraft?.isEdited && (
170
+ <span className="inline-flex items-center gap-2">
171
+ <span className="rounded-md bg-accent px-1.5 py-0.5 text-[0.65rem] font-medium text-muted-foreground">
172
+ Edited
173
+ </span>
174
+ <button
175
+ type="button"
176
+ onClick={() => activeDraft.setDraft(null)}
177
+ className="rounded text-[0.65rem] font-medium text-muted-foreground underline-offset-2 hover:text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
178
+ >
179
+ Revert
180
+ </button>
181
+ </span>
182
+ )}
183
+ {readOnly ? (
184
+ <span
185
+ role="status"
186
+ className="rounded-md bg-muted px-2 py-0.5 text-[0.65rem] font-medium text-muted-foreground"
187
+ >
188
+ Superseded by a newer plan
189
+ </span>
190
+ ) : (
191
+ onBuildFromPlan && (
192
+ <button
193
+ type="button"
194
+ disabled={buildDisabled}
195
+ onClick={onBuildFromPlan}
196
+ className={cn(
197
+ "inline-flex min-w-0 items-center gap-1.5 rounded-md px-3 py-1.5",
198
+ "text-xs font-medium transition-colors",
199
+ "bg-primary text-primary-foreground hover:bg-primary-hover",
200
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
201
+ "disabled:pointer-events-none disabled:opacity-50",
202
+ )}
203
+ >
204
+ <ImplementIcon />
205
+ <span className="truncate">
206
+ {buildDisabled ? "Starting build…" : "Build"}
207
+ </span>
208
+ </button>
209
+ )
210
+ )}
211
+ </div>
212
+ </div>
213
+
214
+ {activeView === "rendered" && <RenderedPlan text={effectiveText} />}
215
+ {activeView === "source" && (
216
+ <pre className="overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs whitespace-pre-wrap text-foreground">
217
+ {effectiveText}
218
+ </pre>
219
+ )}
220
+ {activeView === "edit" && activeDraft !== undefined && (
221
+ <textarea
222
+ value={effectiveText}
223
+ onChange={(e) => activeDraft.setDraft(e.target.value)}
224
+ aria-label="Edit plan"
225
+ spellCheck={false}
226
+ rows={28}
227
+ className={cn(
228
+ "w-full flex-1 resize-y rounded-md border border-border bg-card p-3",
229
+ "font-mono text-xs leading-relaxed text-foreground",
230
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
231
+ )}
232
+ />
233
+ )}
234
+
235
+ {isTruncated && (
236
+ <p role="status" className="text-[0.65rem] text-muted-foreground">
237
+ Preview truncated — download the artifact for the full plan.
238
+ </p>
239
+ )}
240
+ </PlanEditorFrame>
241
+ );
242
+ }
243
+
244
+ /**
245
+ * The editor-area envelope: a centered document column so the plan reads as a
246
+ * page rather than stretching across the full pane width.
247
+ */
248
+ function PlanEditorFrame({
249
+ children,
250
+ onKeyDown,
251
+ }: {
252
+ readonly children: React.ReactNode;
253
+ readonly onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;
254
+ }) {
255
+ return (
256
+ <div
257
+ role="article"
258
+ aria-label="Plan document"
259
+ onKeyDown={onKeyDown}
260
+ className="mx-auto flex w-full max-w-3xl flex-col gap-3 px-4 py-3"
261
+ >
262
+ {children}
263
+ </div>
264
+ );
265
+ }
266
+
267
+ /**
268
+ * The plan document: title lifted from the leading H1, body in the shared
269
+ * plan-document typography — identical treatment to the thread's plan
270
+ * rendering, so the plan reads the same on every surface.
271
+ */
272
+ function RenderedPlan({ text }: { readonly text: string }) {
273
+ const { title, body } = useMemo(
274
+ () => extractLeadingH1(unwrapEnclosingMarkdownFence(text, true)),
275
+ [text],
276
+ );
277
+
278
+ return (
279
+ <div className="overflow-hidden rounded-lg border border-border-muted bg-card">
280
+ {title && (
281
+ <header className="border-b border-border-muted bg-muted-faint px-4 py-2.5">
282
+ <span className="block truncate text-sm font-semibold text-foreground">
283
+ {title}
284
+ </span>
285
+ </header>
286
+ )}
287
+ <div className="stgm-prose px-4 py-4">
288
+ <Markdown
289
+ remarkPlugins={REMARK_PLUGINS}
290
+ components={PLAN_DOCUMENT_MARKDOWN_COMPONENTS}
291
+ >
292
+ {body}
293
+ </Markdown>
294
+ </div>
295
+ </div>
296
+ );
297
+ }
298
+
299
+ function PlanEditorSkeleton() {
300
+ return (
301
+ <div className="space-y-2 py-2" aria-hidden="true">
302
+ <div className="h-8 w-full animate-pulse rounded-md bg-muted" />
303
+ <div className="h-4 w-2/3 animate-pulse rounded bg-muted" />
304
+ <div className="h-4 w-full animate-pulse rounded bg-muted" />
305
+ <div className="h-4 w-5/6 animate-pulse rounded bg-muted" />
306
+ </div>
307
+ );
308
+ }
309
+
310
+ function ViewTab({
311
+ active,
312
+ onClick,
313
+ label,
314
+ disabled,
315
+ title,
316
+ }: {
317
+ readonly active: boolean;
318
+ readonly onClick: () => void;
319
+ readonly label: string;
320
+ readonly disabled?: boolean;
321
+ readonly title?: string;
322
+ }) {
323
+ return (
324
+ <button
325
+ type="button"
326
+ role="tab"
327
+ aria-selected={active}
328
+ onClick={onClick}
329
+ disabled={disabled}
330
+ title={title}
331
+ className={cn(
332
+ "rounded px-2 py-0.5 text-[0.65rem] font-medium transition-colors",
333
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
334
+ active
335
+ ? "bg-card text-foreground shadow-sm"
336
+ : "text-muted-foreground hover:text-foreground",
337
+ "disabled:pointer-events-none disabled:opacity-50",
338
+ )}
339
+ >
340
+ {label}
341
+ </button>
342
+ );
343
+ }
344
+
345
+ function ImplementIcon() {
346
+ return (
347
+ <svg
348
+ width="12"
349
+ height="12"
350
+ viewBox="0 0 12 12"
351
+ fill="none"
352
+ stroke="currentColor"
353
+ strokeWidth="1.5"
354
+ strokeLinecap="round"
355
+ strokeLinejoin="round"
356
+ aria-hidden="true"
357
+ >
358
+ <path d="M2 6h8M7 3l3 3-3 3" />
359
+ </svg>
360
+ );
361
+ }