@stigmer/react 3.1.1 → 3.1.2

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 (426) hide show
  1. package/composer/SessionComposer.d.ts +24 -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/FilePathContext.d.ts +8 -1
  10. package/execution/FilePathContext.d.ts.map +1 -1
  11. package/execution/FilePathContext.js.map +1 -1
  12. package/execution/FilePathLink.d.ts.map +1 -1
  13. package/execution/FilePathLink.js +21 -6
  14. package/execution/FilePathLink.js.map +1 -1
  15. package/execution/MessageEntry.d.ts +29 -0
  16. package/execution/MessageEntry.d.ts.map +1 -1
  17. package/execution/MessageEntry.js +29 -5
  18. package/execution/MessageEntry.js.map +1 -1
  19. package/execution/MessageThread.d.ts +76 -13
  20. package/execution/MessageThread.d.ts.map +1 -1
  21. package/execution/MessageThread.js +178 -24
  22. package/execution/MessageThread.js.map +1 -1
  23. package/execution/PlanArtifactCard.d.ts +27 -18
  24. package/execution/PlanArtifactCard.d.ts.map +1 -1
  25. package/execution/PlanArtifactCard.js +13 -23
  26. package/execution/PlanArtifactCard.js.map +1 -1
  27. package/execution/PlanDocumentMessage.d.ts +32 -0
  28. package/execution/PlanDocumentMessage.d.ts.map +1 -0
  29. package/execution/PlanDocumentMessage.js +37 -0
  30. package/execution/PlanDocumentMessage.js.map +1 -0
  31. package/execution/PlanStreamingCard.d.ts +45 -0
  32. package/execution/PlanStreamingCard.d.ts.map +1 -0
  33. package/execution/PlanStreamingCard.js +35 -0
  34. package/execution/PlanStreamingCard.js.map +1 -0
  35. package/execution/index.d.ts +5 -1
  36. package/execution/index.d.ts.map +1 -1
  37. package/execution/index.js +2 -0
  38. package/execution/index.js.map +1 -1
  39. package/execution/useCreateAgentExecution.d.ts +10 -0
  40. package/execution/useCreateAgentExecution.d.ts.map +1 -1
  41. package/execution/useCreateAgentExecution.js +5 -1
  42. package/execution/useCreateAgentExecution.js.map +1 -1
  43. package/github/decodeGitHubContent.d.ts +29 -0
  44. package/github/decodeGitHubContent.d.ts.map +1 -0
  45. package/github/decodeGitHubContent.js +72 -0
  46. package/github/decodeGitHubContent.js.map +1 -0
  47. package/github/index.d.ts +1 -0
  48. package/github/index.d.ts.map +1 -1
  49. package/github/index.js +1 -0
  50. package/github/index.js.map +1 -1
  51. package/github/useGitHubFileReader.d.ts +26 -0
  52. package/github/useGitHubFileReader.d.ts.map +1 -0
  53. package/github/useGitHubFileReader.js +106 -0
  54. package/github/useGitHubFileReader.js.map +1 -0
  55. package/github/useGitHubTreeLister.d.ts.map +1 -1
  56. package/github/useGitHubTreeLister.js +6 -2
  57. package/github/useGitHubTreeLister.js.map +1 -1
  58. package/index.d.ts +9 -9
  59. package/index.d.ts.map +1 -1
  60. package/index.js +5 -5
  61. package/index.js.map +1 -1
  62. package/internal/ResizableSplit.d.ts +72 -22
  63. package/internal/ResizableSplit.d.ts.map +1 -1
  64. package/internal/ResizableSplit.js +96 -38
  65. package/internal/ResizableSplit.js.map +1 -1
  66. package/internal/VirtualizedThread.d.ts +5 -3
  67. package/internal/VirtualizedThread.d.ts.map +1 -1
  68. package/internal/VirtualizedThread.js +6 -4
  69. package/internal/VirtualizedThread.js.map +1 -1
  70. package/internal/file-icons/index.d.ts +17 -0
  71. package/internal/file-icons/index.d.ts.map +1 -0
  72. package/internal/file-icons/index.js +60 -0
  73. package/internal/file-icons/index.js.map +1 -0
  74. package/internal/file-tree/FileTreeNode.d.ts +18 -1
  75. package/internal/file-tree/FileTreeNode.d.ts.map +1 -1
  76. package/internal/file-tree/FileTreeNode.js +11 -2
  77. package/internal/file-tree/FileTreeNode.js.map +1 -1
  78. package/internal/markdown-components.d.ts +38 -1
  79. package/internal/markdown-components.d.ts.map +1 -1
  80. package/internal/markdown-components.js +86 -11
  81. package/internal/markdown-components.js.map +1 -1
  82. package/{workflow → internal}/motion-preference.d.ts +7 -4
  83. package/internal/motion-preference.d.ts.map +1 -0
  84. package/{workflow → internal}/motion-preference.js +7 -4
  85. package/internal/motion-preference.js.map +1 -0
  86. package/internal/store/index.d.ts +33 -0
  87. package/internal/store/index.d.ts.map +1 -1
  88. package/internal/store/index.js +59 -0
  89. package/internal/store/index.js.map +1 -1
  90. package/internal/store/workspace-editors-store.d.ts +145 -0
  91. package/internal/store/workspace-editors-store.d.ts.map +1 -0
  92. package/internal/store/workspace-editors-store.js +208 -0
  93. package/internal/store/workspace-editors-store.js.map +1 -0
  94. package/internal/store/workspace-file-selection-store.d.ts +38 -0
  95. package/internal/store/workspace-file-selection-store.d.ts.map +1 -0
  96. package/internal/store/workspace-file-selection-store.js +62 -0
  97. package/internal/store/workspace-file-selection-store.js.map +1 -0
  98. package/internal/useRevealLine.d.ts +49 -0
  99. package/internal/useRevealLine.d.ts.map +1 -0
  100. package/internal/useRevealLine.js +43 -0
  101. package/internal/useRevealLine.js.map +1 -0
  102. package/library/detect-plan-artifact.d.ts +21 -0
  103. package/library/detect-plan-artifact.d.ts.map +1 -1
  104. package/library/detect-plan-artifact.js +20 -0
  105. package/library/detect-plan-artifact.js.map +1 -1
  106. package/library/detect-streaming-plan.d.ts +45 -0
  107. package/library/detect-streaming-plan.d.ts.map +1 -0
  108. package/library/detect-streaming-plan.js +83 -0
  109. package/library/detect-streaming-plan.js.map +1 -0
  110. package/library/index.d.ts +4 -1
  111. package/library/index.d.ts.map +1 -1
  112. package/library/index.js +2 -1
  113. package/library/index.js.map +1 -1
  114. package/package.json +4 -4
  115. package/session/NewSessionViewer.d.ts +19 -4
  116. package/session/NewSessionViewer.d.ts.map +1 -1
  117. package/session/NewSessionViewer.js +52 -25
  118. package/session/NewSessionViewer.js.map +1 -1
  119. package/session/PlanEditor.d.ts +55 -0
  120. package/session/PlanEditor.d.ts.map +1 -0
  121. package/session/PlanEditor.js +88 -0
  122. package/session/PlanEditor.js.map +1 -0
  123. package/session/PlanStreamingDocument.d.ts +30 -0
  124. package/session/PlanStreamingDocument.d.ts.map +1 -0
  125. package/session/PlanStreamingDocument.js +37 -0
  126. package/session/PlanStreamingDocument.js.map +1 -0
  127. package/session/SessionPanelChip.d.ts +26 -0
  128. package/session/SessionPanelChip.d.ts.map +1 -0
  129. package/session/SessionPanelChip.js +40 -0
  130. package/session/SessionPanelChip.js.map +1 -0
  131. package/session/SessionViewer.d.ts +34 -7
  132. package/session/SessionViewer.d.ts.map +1 -1
  133. package/session/SessionViewer.js +315 -33
  134. package/session/SessionViewer.js.map +1 -1
  135. package/session/{inspector → facets}/ArtifactsTab.d.ts +10 -2
  136. package/session/facets/ArtifactsTab.d.ts.map +1 -0
  137. package/session/{inspector → facets}/ArtifactsTab.js +8 -3
  138. package/session/facets/ArtifactsTab.js.map +1 -0
  139. package/session/{inspector → facets}/ChangesTab.d.ts +3 -3
  140. package/session/facets/ChangesTab.d.ts.map +1 -0
  141. package/session/{inspector → facets}/ChangesTab.js +3 -3
  142. package/session/facets/ChangesTab.js.map +1 -0
  143. package/session/facets/InspectTab.d.ts.map +1 -0
  144. package/session/facets/InspectTab.js.map +1 -0
  145. package/session/{inspector → facets}/SetupTab.d.ts +11 -2
  146. package/session/facets/SetupTab.d.ts.map +1 -0
  147. package/session/{inspector → facets}/SetupTab.js +4 -3
  148. package/session/facets/SetupTab.js.map +1 -0
  149. package/session/{inspector → facets}/UsageTab.d.ts +1 -1
  150. package/session/facets/UsageTab.d.ts.map +1 -0
  151. package/session/{inspector → facets}/UsageTab.js +1 -1
  152. package/session/facets/UsageTab.js.map +1 -0
  153. package/session/{inspector → facets}/index.d.ts +0 -6
  154. package/session/facets/index.d.ts.map +1 -0
  155. package/session/{inspector → facets}/index.js +0 -3
  156. package/session/facets/index.js.map +1 -0
  157. package/session/index.d.ts +11 -2
  158. package/session/index.d.ts.map +1 -1
  159. package/session/index.js +15 -2
  160. package/session/index.js.map +1 -1
  161. package/session/plan-document.d.ts +9 -0
  162. package/session/plan-document.d.ts.map +1 -0
  163. package/session/plan-document.js +17 -0
  164. package/session/plan-document.js.map +1 -0
  165. package/session/useOpenFileChange.d.ts +20 -0
  166. package/session/useOpenFileChange.d.ts.map +1 -0
  167. package/session/useOpenFileChange.js +31 -0
  168. package/session/useOpenFileChange.js.map +1 -0
  169. package/session/usePlanDraft.d.ts +52 -0
  170. package/session/usePlanDraft.d.ts.map +1 -0
  171. package/session/usePlanDraft.js +63 -0
  172. package/session/usePlanDraft.js.map +1 -0
  173. package/session/useSessionConversation.d.ts +6 -0
  174. package/session/useSessionConversation.d.ts.map +1 -1
  175. package/session/useSessionConversation.js +1 -0
  176. package/session/useSessionConversation.js.map +1 -1
  177. package/session/useSessionPageFlow.d.ts.map +1 -1
  178. package/session/useSessionPageFlow.js +1 -0
  179. package/session/useSessionPageFlow.js.map +1 -1
  180. package/session/useSessionPanel.d.ts +120 -0
  181. package/session/useSessionPanel.d.ts.map +1 -0
  182. package/session/useSessionPanel.js +130 -0
  183. package/session/useSessionPanel.js.map +1 -0
  184. package/session/useSessionRailViews.d.ts +45 -0
  185. package/session/useSessionRailViews.d.ts.map +1 -0
  186. package/session/useSessionRailViews.js +105 -0
  187. package/session/useSessionRailViews.js.map +1 -0
  188. package/src/composer/SessionComposer.tsx +52 -10
  189. package/src/composer/__tests__/SessionComposer-contract.test.tsx +58 -4
  190. package/src/execution/ArtifactContentRenderer.tsx +101 -24
  191. package/src/execution/FilePathContext.tsx +8 -1
  192. package/src/execution/FilePathLink.tsx +22 -6
  193. package/src/execution/MessageEntry.tsx +111 -1
  194. package/src/execution/MessageThread.tsx +291 -36
  195. package/src/execution/PlanArtifactCard.tsx +63 -31
  196. package/src/execution/PlanDocumentMessage.tsx +96 -0
  197. package/src/execution/PlanStreamingCard.tsx +137 -0
  198. package/src/execution/__tests__/ArtifactContentRenderer.test.tsx +80 -1
  199. package/src/execution/__tests__/FilePathLink.test.tsx +87 -0
  200. package/src/execution/__tests__/MessageThread.test.tsx +101 -0
  201. package/src/execution/__tests__/PlanArtifactCard.test.tsx +66 -12
  202. package/src/execution/__tests__/PlanDocumentMessage.test.tsx +55 -0
  203. package/src/execution/__tests__/PlanStreamingCard.test.tsx +49 -0
  204. package/src/execution/__tests__/buildThreadItems-plan-completion.test.ts +504 -22
  205. package/src/execution/__tests__/message-entry.test.tsx +48 -0
  206. package/src/execution/__tests__/useCreateAgentExecution.test.tsx +85 -0
  207. package/src/execution/index.ts +7 -1
  208. package/src/execution/useCreateAgentExecution.ts +16 -1
  209. package/src/github/__tests__/decodeGitHubContent.test.ts +102 -0
  210. package/src/github/__tests__/useGitHubFileReader.test.ts +183 -0
  211. package/src/github/decodeGitHubContent.ts +83 -0
  212. package/src/github/index.ts +1 -0
  213. package/src/github/useGitHubFileReader.ts +152 -0
  214. package/src/github/useGitHubTreeLister.ts +6 -2
  215. package/src/index.ts +63 -9
  216. package/src/internal/ResizableSplit.tsx +184 -51
  217. package/src/internal/VirtualizedThread.tsx +12 -4
  218. package/src/internal/__tests__/ResizableSplit.test.tsx +125 -3
  219. package/src/internal/__tests__/markdown-components.test.tsx +68 -0
  220. package/src/internal/__tests__/useRevealLine.test.tsx +97 -0
  221. package/src/internal/file-icons/__tests__/file-icons.test.tsx +35 -0
  222. package/src/internal/file-icons/index.tsx +137 -0
  223. package/src/internal/file-tree/FileTreeNode.tsx +43 -3
  224. package/src/internal/file-tree/__tests__/FileTreeNode-explorer.test.tsx +86 -0
  225. package/src/internal/markdown-components.tsx +116 -11
  226. package/src/{workflow → internal}/motion-preference.ts +7 -4
  227. package/src/internal/store/__tests__/workspace-editors-store.test.ts +216 -0
  228. package/src/internal/store/__tests__/workspace-file-selection-store.test.ts +118 -0
  229. package/src/internal/store/index.ts +94 -0
  230. package/src/internal/store/workspace-editors-store.ts +315 -0
  231. package/src/internal/store/workspace-file-selection-store.ts +88 -0
  232. package/src/internal/useRevealLine.ts +81 -0
  233. package/src/library/__tests__/detect-plan-artifact.test.ts +48 -0
  234. package/src/library/__tests__/detect-streaming-plan.test.ts +234 -0
  235. package/src/library/detect-plan-artifact.ts +35 -0
  236. package/src/library/detect-streaming-plan.ts +108 -0
  237. package/src/library/index.ts +5 -0
  238. package/src/session/NewSessionViewer.tsx +123 -55
  239. package/src/session/PlanEditor.tsx +361 -0
  240. package/src/session/PlanStreamingDocument.tsx +97 -0
  241. package/src/session/SessionPanelChip.tsx +103 -0
  242. package/src/session/SessionViewer.tsx +617 -90
  243. package/src/session/__tests__/NewSessionViewer.test.tsx +148 -0
  244. package/src/session/__tests__/PlanEditor.test.tsx +217 -0
  245. package/src/session/__tests__/PlanStreamingDocument.test.tsx +50 -0
  246. package/src/session/__tests__/SessionPanelChip.test.tsx +42 -0
  247. package/src/session/__tests__/SessionViewer-fileReviewDock.test.tsx +9 -4
  248. package/src/session/__tests__/SessionViewer-transcriptFileOpen.test.tsx +205 -0
  249. package/src/session/__tests__/audienceWiring.test.tsx +55 -19
  250. package/src/session/__tests__/useOpenFileChange.test.tsx +82 -0
  251. package/src/session/__tests__/usePlanDraft.test.ts +102 -0
  252. package/src/session/__tests__/useSessionPanel.test.ts +298 -0
  253. package/src/session/__tests__/useSessionRailViews.test.tsx +124 -0
  254. package/src/session/{inspector → facets}/ArtifactsTab.tsx +27 -5
  255. package/src/session/{inspector → facets}/ChangesTab.tsx +3 -3
  256. package/src/session/{inspector → facets}/SetupTab.tsx +13 -1
  257. package/src/session/{inspector → facets}/UsageTab.tsx +1 -1
  258. package/src/session/{inspector → facets}/__tests__/ArtifactsTab.test.tsx +35 -1
  259. package/src/session/facets/__tests__/SetupTab.test.tsx +44 -0
  260. package/src/session/{inspector → facets}/index.ts +0 -16
  261. package/src/session/index.ts +30 -13
  262. package/src/session/plan-document.ts +19 -0
  263. package/src/session/useOpenFileChange.ts +53 -0
  264. package/src/session/usePlanDraft.ts +109 -0
  265. package/src/session/useSessionConversation.ts +7 -0
  266. package/src/session/useSessionPageFlow.ts +1 -0
  267. package/src/session/useSessionPanel.ts +288 -0
  268. package/src/session/useSessionRailViews.tsx +200 -0
  269. package/src/workflow/WorkflowExecutionGraph.tsx +1 -1
  270. package/src/workflow/index.ts +1 -1
  271. package/src/workflow/useFollowExecution.ts +1 -1
  272. package/src/workspace/EditorTabs.tsx +145 -0
  273. package/src/workspace/ExplorerTree.tsx +247 -0
  274. package/src/workspace/FileViewer.tsx +516 -0
  275. package/src/workspace/WorkspaceContentSearch.tsx +478 -0
  276. package/src/workspace/WorkspaceContentSearcher.ts +70 -0
  277. package/src/workspace/WorkspaceFileLister.ts +11 -0
  278. package/src/workspace/WorkspaceFileReader.ts +57 -0
  279. package/src/workspace/WorkspaceFileSearch.tsx +429 -0
  280. package/src/workspace/WorkspaceSurface.tsx +781 -0
  281. package/src/workspace/__tests__/EditorTabs.test.tsx +100 -0
  282. package/src/workspace/__tests__/ExplorerTree.test.tsx +152 -0
  283. package/src/workspace/__tests__/FileViewer.test.tsx +421 -0
  284. package/src/workspace/__tests__/WorkspaceContentSearch.test.tsx +196 -0
  285. package/src/workspace/__tests__/WorkspaceFileSearch.test.tsx +206 -0
  286. package/src/workspace/__tests__/WorkspaceSurface.test.tsx +348 -0
  287. package/src/workspace/__tests__/findChangeForSelection.test.ts +132 -0
  288. package/src/workspace/__tests__/matchContentPreview.test.ts +55 -0
  289. package/src/workspace/__tests__/matchWorkspaceFiles.test.ts +104 -0
  290. package/src/workspace/__tests__/resolveWorkspaceFileSelection.test.ts +135 -0
  291. package/src/workspace/__tests__/useWorkspaceContentSearch.test.tsx +196 -0
  292. package/src/workspace/__tests__/useWorkspaceFileContent.test.tsx +190 -0
  293. package/src/workspace/__tests__/useWorkspaceFileSearch.test.tsx +162 -0
  294. package/src/workspace/findChangeForSelection.ts +61 -0
  295. package/src/workspace/index.ts +56 -2
  296. package/src/workspace/matchContentPreview.ts +51 -0
  297. package/src/workspace/matchWorkspaceFiles.ts +86 -0
  298. package/src/workspace/resolveWorkspaceFileSelection.ts +119 -0
  299. package/src/workspace/useWorkspaceContentSearch.ts +228 -0
  300. package/src/workspace/useWorkspaceFileContent.ts +129 -0
  301. package/src/workspace/useWorkspaceFileSearch.ts +184 -0
  302. package/src/workspace/useWorkspaceFiles.ts +27 -27
  303. package/src/workspace/workspaceListingCache.ts +93 -0
  304. package/styles.css +1 -1
  305. package/workflow/WorkflowExecutionGraph.js +1 -1
  306. package/workflow/WorkflowExecutionGraph.js.map +1 -1
  307. package/workflow/index.d.ts +1 -1
  308. package/workflow/index.d.ts.map +1 -1
  309. package/workflow/index.js +1 -1
  310. package/workflow/index.js.map +1 -1
  311. package/workflow/useFollowExecution.js +1 -1
  312. package/workflow/useFollowExecution.js.map +1 -1
  313. package/workspace/EditorTabs.d.ts +29 -0
  314. package/workspace/EditorTabs.d.ts.map +1 -0
  315. package/workspace/EditorTabs.js +66 -0
  316. package/workspace/EditorTabs.js.map +1 -0
  317. package/workspace/ExplorerTree.d.ts +32 -0
  318. package/workspace/ExplorerTree.d.ts.map +1 -0
  319. package/workspace/ExplorerTree.js +45 -0
  320. package/workspace/ExplorerTree.js.map +1 -0
  321. package/workspace/FileViewer.d.ts +93 -0
  322. package/workspace/FileViewer.d.ts.map +1 -0
  323. package/workspace/FileViewer.js +182 -0
  324. package/workspace/FileViewer.js.map +1 -0
  325. package/workspace/WorkspaceContentSearch.d.ts +41 -0
  326. package/workspace/WorkspaceContentSearch.d.ts.map +1 -0
  327. package/workspace/WorkspaceContentSearch.js +195 -0
  328. package/workspace/WorkspaceContentSearch.js.map +1 -0
  329. package/workspace/WorkspaceContentSearcher.d.ts +59 -0
  330. package/workspace/WorkspaceContentSearcher.d.ts.map +1 -0
  331. package/workspace/WorkspaceContentSearcher.js +7 -0
  332. package/workspace/WorkspaceContentSearcher.js.map +1 -0
  333. package/workspace/WorkspaceFileLister.d.ts +11 -0
  334. package/workspace/WorkspaceFileLister.d.ts.map +1 -1
  335. package/workspace/WorkspaceFileReader.d.ts +48 -0
  336. package/workspace/WorkspaceFileReader.d.ts.map +1 -0
  337. package/workspace/WorkspaceFileReader.js +11 -0
  338. package/workspace/WorkspaceFileReader.js.map +1 -0
  339. package/workspace/WorkspaceFileSearch.d.ts +32 -0
  340. package/workspace/WorkspaceFileSearch.d.ts.map +1 -0
  341. package/workspace/WorkspaceFileSearch.js +155 -0
  342. package/workspace/WorkspaceFileSearch.js.map +1 -0
  343. package/workspace/WorkspaceSurface.d.ts +151 -0
  344. package/workspace/WorkspaceSurface.d.ts.map +1 -0
  345. package/workspace/WorkspaceSurface.js +184 -0
  346. package/workspace/WorkspaceSurface.js.map +1 -0
  347. package/workspace/findChangeForSelection.d.ts +19 -0
  348. package/workspace/findChangeForSelection.d.ts.map +1 -0
  349. package/workspace/findChangeForSelection.js +46 -0
  350. package/workspace/findChangeForSelection.js.map +1 -0
  351. package/workspace/index.d.ts +24 -2
  352. package/workspace/index.d.ts.map +1 -1
  353. package/workspace/index.js +13 -1
  354. package/workspace/index.js.map +1 -1
  355. package/workspace/matchContentPreview.d.ts +21 -0
  356. package/workspace/matchContentPreview.d.ts.map +1 -0
  357. package/workspace/matchContentPreview.js +36 -0
  358. package/workspace/matchContentPreview.js.map +1 -0
  359. package/workspace/matchWorkspaceFiles.d.ts +30 -0
  360. package/workspace/matchWorkspaceFiles.d.ts.map +1 -0
  361. package/workspace/matchWorkspaceFiles.js +58 -0
  362. package/workspace/matchWorkspaceFiles.js.map +1 -0
  363. package/workspace/resolveWorkspaceFileSelection.d.ts +19 -0
  364. package/workspace/resolveWorkspaceFileSelection.d.ts.map +1 -0
  365. package/workspace/resolveWorkspaceFileSelection.js +115 -0
  366. package/workspace/resolveWorkspaceFileSelection.js.map +1 -0
  367. package/workspace/useWorkspaceContentSearch.d.ts +69 -0
  368. package/workspace/useWorkspaceContentSearch.d.ts.map +1 -0
  369. package/workspace/useWorkspaceContentSearch.js +154 -0
  370. package/workspace/useWorkspaceContentSearch.js.map +1 -0
  371. package/workspace/useWorkspaceFileContent.d.ts +73 -0
  372. package/workspace/useWorkspaceFileContent.d.ts.map +1 -0
  373. package/workspace/useWorkspaceFileContent.js +57 -0
  374. package/workspace/useWorkspaceFileContent.js.map +1 -0
  375. package/workspace/useWorkspaceFileSearch.d.ts +61 -0
  376. package/workspace/useWorkspaceFileSearch.d.ts.map +1 -0
  377. package/workspace/useWorkspaceFileSearch.js +119 -0
  378. package/workspace/useWorkspaceFileSearch.js.map +1 -0
  379. package/workspace/useWorkspaceFiles.d.ts +13 -5
  380. package/workspace/useWorkspaceFiles.d.ts.map +1 -1
  381. package/workspace/useWorkspaceFiles.js +18 -19
  382. package/workspace/useWorkspaceFiles.js.map +1 -1
  383. package/workspace/workspaceListingCache.d.ts +47 -0
  384. package/workspace/workspaceListingCache.d.ts.map +1 -0
  385. package/workspace/workspaceListingCache.js +55 -0
  386. package/workspace/workspaceListingCache.js.map +1 -0
  387. package/session/inspector/ArtifactsTab.d.ts.map +0 -1
  388. package/session/inspector/ArtifactsTab.js.map +0 -1
  389. package/session/inspector/ChangesTab.d.ts.map +0 -1
  390. package/session/inspector/ChangesTab.js.map +0 -1
  391. package/session/inspector/InspectTab.d.ts.map +0 -1
  392. package/session/inspector/InspectTab.js.map +0 -1
  393. package/session/inspector/SessionInspector.d.ts +0 -64
  394. package/session/inspector/SessionInspector.d.ts.map +0 -1
  395. package/session/inspector/SessionInspector.js +0 -53
  396. package/session/inspector/SessionInspector.js.map +0 -1
  397. package/session/inspector/SetupTab.d.ts.map +0 -1
  398. package/session/inspector/SetupTab.js.map +0 -1
  399. package/session/inspector/UsageTab.d.ts.map +0 -1
  400. package/session/inspector/UsageTab.js.map +0 -1
  401. package/session/inspector/WorkspaceTab.d.ts +0 -35
  402. package/session/inspector/WorkspaceTab.d.ts.map +0 -1
  403. package/session/inspector/WorkspaceTab.js +0 -75
  404. package/session/inspector/WorkspaceTab.js.map +0 -1
  405. package/session/inspector/index.d.ts.map +0 -1
  406. package/session/inspector/index.js.map +0 -1
  407. package/session/inspector/useSessionInspector.d.ts +0 -49
  408. package/session/inspector/useSessionInspector.d.ts.map +0 -1
  409. package/session/inspector/useSessionInspector.js +0 -122
  410. package/session/inspector/useSessionInspector.js.map +0 -1
  411. package/src/session/inspector/SessionInspector.tsx +0 -162
  412. package/src/session/inspector/WorkspaceTab.tsx +0 -245
  413. package/src/session/inspector/__tests__/useSessionInspector.test.ts +0 -251
  414. package/src/session/inspector/useSessionInspector.ts +0 -178
  415. package/src/workspace/WorkspaceEntryFiles.tsx +0 -196
  416. package/workflow/motion-preference.d.ts.map +0 -1
  417. package/workflow/motion-preference.js.map +0 -1
  418. package/workspace/WorkspaceEntryFiles.d.ts +0 -22
  419. package/workspace/WorkspaceEntryFiles.d.ts.map +0 -1
  420. package/workspace/WorkspaceEntryFiles.js +0 -58
  421. package/workspace/WorkspaceEntryFiles.js.map +0 -1
  422. /package/session/{inspector → facets}/InspectTab.d.ts +0 -0
  423. /package/session/{inspector → facets}/InspectTab.js +0 -0
  424. /package/src/{workflow → internal}/__tests__/motion-preference.test.ts +0 -0
  425. /package/src/session/{inspector → facets}/InspectTab.tsx +0 -0
  426. /package/src/session/{inspector → facets}/__tests__/ChangesTab.test.tsx +0 -0
@@ -14,6 +14,12 @@ export interface PlanArtifactCardProps {
14
14
  readonly executionId: string;
15
15
  /** The published `plan.md` artifact (from `findPlanArtifact`). */
16
16
  readonly artifact: ExecutionArtifact;
17
+ /**
18
+ * The plan's title (its leading `# H1`). The card is the plan's compact
19
+ * stand-in in the thread, so the title is what makes it recognizable.
20
+ * Falls back to "Plan" when omitted.
21
+ */
22
+ readonly title?: string;
17
23
  /**
18
24
  * Organization slug. Required for the "Open full" modal (the shared
19
25
  * {@link ArtifactPreviewModal} uses it for its detection/apply pipeline and
@@ -23,33 +29,36 @@ export interface PlanArtifactCardProps {
23
29
  readonly org?: string;
24
30
  /** Called when the user clicks "Build from plan". Hidden when omitted. */
25
31
  readonly onImplement?: () => void;
32
+ /**
33
+ * Opens the plan in the session panel's plan document tab — the
34
+ * side-by-side review/refine surface. When provided it REPLACES the
35
+ * modal-based "Open full" secondary with "Open plan" (one review
36
+ * affordance, not two). When omitted (hosts without a session panel) the
37
+ * modal remains.
38
+ */
39
+ readonly onOpenPlan?: () => void;
26
40
  /** Disables the primary CTA (e.g., while an execution is active). */
27
41
  readonly disabled?: boolean;
42
+ /** When true, the primary button reads "Starting build…" (upload in flight). */
43
+ readonly buildPending?: boolean;
28
44
  /** Additional CSS classes for the root element. */
29
45
  readonly className?: string;
30
46
  }
31
47
 
32
48
  /**
33
- * Action surface for a plan that a completed Plan-mode execution published as a
34
- * `plan.md` artifact.
35
- *
36
- * It is rendered immediately below the plan message in the thread (which already
37
- * shows the plan as rich markdown — the message renderer unwraps a model's
38
- * enclosing markdown fence), so this card deliberately holds **no** plan
39
- * content: the message is the document and this is its action bar. Rendering
40
- * the plan a second time here would duplicate the same text the message and the
41
- * "Open full" modal already show.
49
+ * The compact plan card a completed Plan turn's sole representation in the
50
+ * thread. The plan document itself lives in the session panel's plan tab
51
+ * (`PlanEditor`), Cursor-style: chat carries a recognizable, actionable card;
52
+ * the panel carries the document.
42
53
  *
43
- * The hierarchy is the point: one prominent, themeable primary action
44
- * **Build from plan** with **Open full** and **Download** as subdued
45
- * secondaries, so the next step is unmistakable. A small negative top margin
46
- * pulls the bar against the plan message so the two read as a single
47
- * first-class plan block.
54
+ * Anatomy: a document-style identity block (plan icon, the plan's title, a
55
+ * `plan.md · size` meta line) with the actions trailing — **Open plan** and
56
+ * **Download** as subdued secondaries and one prominent, themeable primary,
57
+ * **Build from plan** so the next step is unmistakable.
48
58
  *
49
- * Kept as its own thread item (not merged into the message) on purpose: merging
50
- * would change the streamed message item's identity at completion and remount
51
- * the just-streamed plan. Adjacent-and-attached gets the unified look with no
52
- * remount.
59
+ * Only the LATEST plan's card carries `onImplement`; a superseded plan's card
60
+ * is review-only (its `onOpenPlan` opens the document read-only), so a stale
61
+ * plan can never be implemented by accident.
53
62
  *
54
63
  * All visual properties flow through `--stgm-*` tokens; the component is
55
64
  * self-contained and embeddable.
@@ -57,9 +66,12 @@ export interface PlanArtifactCardProps {
57
66
  export const PlanArtifactCard = memo(function PlanArtifactCard({
58
67
  executionId,
59
68
  artifact,
69
+ title,
60
70
  org,
61
71
  onImplement,
72
+ onOpenPlan,
62
73
  disabled,
74
+ buildPending,
63
75
  className,
64
76
  }: PlanArtifactCardProps) {
65
77
  const [previewOpen, setPreviewOpen] = useState(false);
@@ -69,35 +81,55 @@ export const PlanArtifactCard = memo(function PlanArtifactCard({
69
81
  return (
70
82
  <div
71
83
  role="region"
72
- aria-label="Plan actions"
84
+ aria-label="Plan"
73
85
  onKeyDown={handleKeyDown}
74
86
  className={cn(
75
- // `-mt-2` tightens the thread's `gap-4` so the bar attaches to the
76
- // plan message above it, reading as one first-class plan block.
77
- "mx-4 -mt-2 flex flex-wrap items-center gap-x-3 gap-y-2 rounded-md",
78
- "border border-border-muted bg-muted-faint px-3 py-2",
87
+ "mx-4 flex flex-wrap items-center gap-x-3 gap-y-2 rounded-lg",
88
+ "border border-border-muted bg-card px-3 py-2.5",
79
89
  className,
80
90
  )}
81
91
  >
82
92
  <PlanIcon />
83
- <span className="text-xs font-medium text-foreground">Plan</span>
84
- <span className="text-xs tabular-nums text-muted-foreground-faint">
85
- {formatArtifactSize(artifact.sizeBytes)}
86
- </span>
93
+ <div className="min-w-0 flex-1 basis-48">
94
+ <div className="truncate text-sm font-medium text-foreground">
95
+ {title ?? "Plan"}
96
+ </div>
97
+ <div className="text-[0.65rem] text-muted-foreground-faint">
98
+ {artifact.name}
99
+ <span aria-hidden="true"> · </span>
100
+ <span className="tabular-nums">
101
+ {formatArtifactSize(artifact.sizeBytes)}
102
+ </span>
103
+ </div>
104
+ </div>
87
105
 
88
106
  <div className="ml-auto flex flex-wrap items-center gap-x-3 gap-y-1.5">
89
- {org && (
107
+ {onOpenPlan ? (
90
108
  <button
91
109
  type="button"
92
- onClick={() => setPreviewOpen(true)}
110
+ onClick={onOpenPlan}
93
111
  className={cn(
94
112
  "inline-flex items-center gap-1 text-xs font-medium text-muted-foreground transition-colors hover:text-foreground",
95
113
  FOCUS_RING,
96
114
  )}
97
115
  >
98
116
  <ExpandIcon />
99
- Open full
117
+ Open plan
100
118
  </button>
119
+ ) : (
120
+ org && (
121
+ <button
122
+ type="button"
123
+ onClick={() => setPreviewOpen(true)}
124
+ className={cn(
125
+ "inline-flex items-center gap-1 text-xs font-medium text-muted-foreground transition-colors hover:text-foreground",
126
+ FOCUS_RING,
127
+ )}
128
+ >
129
+ <ExpandIcon />
130
+ Open full
131
+ </button>
132
+ )
101
133
  )}
102
134
  <button
103
135
  type="button"
@@ -125,7 +157,7 @@ export const PlanArtifactCard = memo(function PlanArtifactCard({
125
157
  )}
126
158
  >
127
159
  <ImplementIcon />
128
- Build from plan
160
+ {buildPending ? "Starting build…" : "Build from plan"}
129
161
  </button>
130
162
  )}
131
163
  </div>
@@ -0,0 +1,96 @@
1
+ "use client";
2
+
3
+ import { memo, useMemo } from "react";
4
+ import { Streamdown } from "streamdown";
5
+ import { cn } from "@stigmer/theme";
6
+ import {
7
+ PLAN_DOCUMENT_MARKDOWN_COMPONENTS,
8
+ extractLeadingH1,
9
+ unwrapEnclosingMarkdownFence,
10
+ } from "../internal/markdown-components.js";
11
+
12
+ /** Props for {@link PlanDocumentMessage}. */
13
+ export interface PlanDocumentMessageProps {
14
+ /** The plan message's markdown content (the agent's final Plan-mode reply). */
15
+ readonly content: string;
16
+ /** Additional CSS class names for the root container. */
17
+ readonly className?: string;
18
+ }
19
+
20
+ /**
21
+ * Renders a Plan-mode turn's plan as a first-class document instead of a chat
22
+ * bubble: a bordered card with a title header (the plan's leading `# H1`,
23
+ * lifted out of the prose) and document-grade typography
24
+ * ({@link PLAN_DOCUMENT_MARKDOWN_COMPONENTS}).
25
+ *
26
+ * This is the thread's NO-ARTIFACT fallback: a completed Plan turn that never
27
+ * published `plan.md` (older executions, a failed upload) keeps its plan
28
+ * inline via this component — the message is the only copy of the plan, so it
29
+ * must stay readable in place. A turn that DID publish the artifact collapses
30
+ * its plan message into the compact `PlanArtifactCard` instead, and the
31
+ * document renders in the session panel's plan tab (`PlanEditor`) with the
32
+ * same typography.
33
+ *
34
+ * Fence handling is plan-scoped: a plan wrapped in a whole-body fence is
35
+ * unwrapped even when the fence carries no language tag (`allowBareFence`) —
36
+ * a Plan turn's output is a markdown document by contract, so an enclosing
37
+ * bare fence is wrapping, not code. Render-time only; the transcript and the
38
+ * published artifact stay byte-faithful.
39
+ *
40
+ * Purely presentational — no data fetching, no state.
41
+ * All visual properties flow through `--stgm-*` tokens.
42
+ */
43
+ export const PlanDocumentMessage = memo(function PlanDocumentMessage({
44
+ content,
45
+ className,
46
+ }: PlanDocumentMessageProps) {
47
+ const { title, body } = useMemo(
48
+ () => extractLeadingH1(unwrapEnclosingMarkdownFence(content, true)),
49
+ [content],
50
+ );
51
+
52
+ return (
53
+ <div
54
+ role="article"
55
+ aria-label="Plan document"
56
+ className={cn("mx-4", className)}
57
+ >
58
+ <div className="overflow-hidden rounded-lg border border-border-muted bg-card">
59
+ <header className="flex items-center gap-2 border-b border-border-muted bg-muted-faint px-4 py-2.5">
60
+ <PlanDocIcon />
61
+ <span className="min-w-0 flex-1 truncate text-sm font-semibold text-foreground">
62
+ {title ?? "Plan"}
63
+ </span>
64
+ </header>
65
+ <div className="stgm-prose px-4 py-4">
66
+ <Streamdown
67
+ components={PLAN_DOCUMENT_MARKDOWN_COMPONENTS}
68
+ isAnimating={false}
69
+ >
70
+ {body}
71
+ </Streamdown>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ );
76
+ });
77
+
78
+ function PlanDocIcon() {
79
+ return (
80
+ <svg
81
+ width="14"
82
+ height="14"
83
+ viewBox="0 0 16 16"
84
+ fill="none"
85
+ stroke="currentColor"
86
+ strokeWidth="1.5"
87
+ strokeLinecap="round"
88
+ strokeLinejoin="round"
89
+ className="shrink-0 text-muted-foreground"
90
+ aria-hidden="true"
91
+ >
92
+ <path d="M3 4h10M3 8h7M3 12h8" />
93
+ <path d="M12.5 10.5l1.5 1.5-1.5 1.5" />
94
+ </svg>
95
+ );
96
+ }
@@ -0,0 +1,137 @@
1
+ "use client";
2
+
3
+ import { memo } from "react";
4
+ import { cn } from "@stigmer/theme";
5
+ import { PLAN_ARTIFACT_NAME } from "../library/detect-plan-artifact.js";
6
+ import { formatArtifactSize } from "./artifact-utils.js";
7
+
8
+ /** Props for {@link PlanStreamingCard}. */
9
+ export interface PlanStreamingCardProps {
10
+ /**
11
+ * The plan's title (its leading `# H1`), streaming in live. Falls back to
12
+ * "Writing plan…" until the title has arrived.
13
+ */
14
+ readonly title?: string;
15
+ /**
16
+ * Live size of the plan text streamed so far. An approximation (UTF-16
17
+ * length, not encoded bytes) — it exists to show growth while the agent
18
+ * writes, not to state the artifact's final size.
19
+ */
20
+ readonly sizeBytes: number;
21
+ /**
22
+ * Opens the panel's plan document tab, where the plan is rendering live.
23
+ * Always provided in practice: the thread only collapses a streaming plan
24
+ * behind this card when the host wired `onOpenPlan` (see `MessageThread`'s
25
+ * `collapseStreamingPlan` gate) — without a document surface there is
26
+ * nothing for the card to open, and the plan streams inline instead.
27
+ */
28
+ readonly onOpenPlan?: () => void;
29
+ /** Additional CSS classes for the root element. */
30
+ readonly className?: string;
31
+ }
32
+
33
+ /**
34
+ * The live counterpart of {@link PlanArtifactCard} — a streaming Plan turn's
35
+ * compact stand-in in the thread while the plan document renders live in the
36
+ * session panel's plan tab. Same anatomy as the completed card (icon, title,
37
+ * `plan.md · size` meta, trailing actions) so the completion handoff reads as
38
+ * the same card settling, not a new element appearing.
39
+ *
40
+ * Deliberately action-light: "Open plan" is the only affordance. Download and
41
+ * Build require the published artifact, which does not exist until the turn
42
+ * completes — at which point `buildThreadItems` swaps this item for the
43
+ * `plan-completion` item under the same list key.
44
+ *
45
+ * The pulsing icon is the progress affordance: a CSS animation inside `.stgm`,
46
+ * covered by the stylesheet's global `prefers-reduced-motion` rule (DD-015),
47
+ * and safe under virtualization (no `@starting-style`).
48
+ *
49
+ * All visual properties flow through `--stgm-*` tokens; the component is
50
+ * self-contained and embeddable.
51
+ */
52
+ export const PlanStreamingCard = memo(function PlanStreamingCard({
53
+ title,
54
+ sizeBytes,
55
+ onOpenPlan,
56
+ className,
57
+ }: PlanStreamingCardProps) {
58
+ return (
59
+ <div
60
+ role="region"
61
+ aria-label="Plan being written"
62
+ aria-busy="true"
63
+ className={cn(
64
+ "mx-4 flex flex-wrap items-center gap-x-3 gap-y-2 rounded-lg",
65
+ "border border-border-muted bg-card px-3 py-2.5",
66
+ className,
67
+ )}
68
+ >
69
+ <PlanIcon className="animate-pulse" />
70
+ <div className="min-w-0 flex-1 basis-48">
71
+ <div className="truncate text-sm font-medium text-foreground">
72
+ {title ?? "Writing plan…"}
73
+ </div>
74
+ <div className="text-[0.65rem] text-muted-foreground-faint">
75
+ {PLAN_ARTIFACT_NAME}
76
+ <span aria-hidden="true"> · </span>
77
+ <span role="status">Writing…</span>
78
+ <span aria-hidden="true"> · </span>
79
+ <span className="tabular-nums">{formatArtifactSize(sizeBytes)}</span>
80
+ </div>
81
+ </div>
82
+
83
+ {onOpenPlan && (
84
+ <button
85
+ type="button"
86
+ onClick={onOpenPlan}
87
+ className={cn(
88
+ "ml-auto inline-flex items-center gap-1 text-xs font-medium text-muted-foreground transition-colors hover:text-foreground",
89
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:rounded-sm",
90
+ )}
91
+ >
92
+ <ExpandIcon />
93
+ Open plan
94
+ </button>
95
+ )}
96
+ </div>
97
+ );
98
+ });
99
+
100
+ function PlanIcon({ className }: { readonly className?: string }) {
101
+ return (
102
+ <svg
103
+ width="16"
104
+ height="16"
105
+ viewBox="0 0 16 16"
106
+ fill="none"
107
+ stroke="currentColor"
108
+ strokeWidth="1.5"
109
+ strokeLinecap="round"
110
+ strokeLinejoin="round"
111
+ className={cn("shrink-0 text-muted-foreground-faint", className)}
112
+ aria-hidden="true"
113
+ >
114
+ <path d="M3 4h10M3 8h7M3 12h8" />
115
+ <path d="M12.5 10.5l1.5 1.5-1.5 1.5" />
116
+ </svg>
117
+ );
118
+ }
119
+
120
+ function ExpandIcon() {
121
+ return (
122
+ <svg
123
+ width="10"
124
+ height="10"
125
+ viewBox="0 0 12 12"
126
+ fill="none"
127
+ stroke="currentColor"
128
+ strokeWidth="1.5"
129
+ strokeLinecap="round"
130
+ strokeLinejoin="round"
131
+ className="shrink-0"
132
+ aria-hidden="true"
133
+ >
134
+ <path d="M7 1.5h3.5V5M10.5 1.5L6.5 5.5M5 10.5H1.5V7M1.5 10.5l4-4" />
135
+ </svg>
136
+ );
137
+ }
@@ -1,4 +1,4 @@
1
- import { describe, it, expect, afterEach } from "vitest";
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
2
  import { render, screen, fireEvent, cleanup } from "@testing-library/react";
3
3
  import { ArtifactContentRenderer } from "../ArtifactContentRenderer";
4
4
 
@@ -60,3 +60,82 @@ describe("ArtifactContentRenderer — markdown", () => {
60
60
  ).toBeGreaterThan(0);
61
61
  });
62
62
  });
63
+
64
+ describe("ArtifactContentRenderer — reveal (jump-to-line)", () => {
65
+ let scrollIntoView: ReturnType<typeof vi.fn>;
66
+ beforeEach(() => {
67
+ // happy-dom lacks a real scrollIntoView; the hook calls it optionally.
68
+ scrollIntoView = vi.fn();
69
+ Element.prototype.scrollIntoView =
70
+ scrollIntoView as unknown as typeof Element.prototype.scrollIntoView;
71
+ });
72
+
73
+ it("renders addressable lines and highlights the target row for plain text", () => {
74
+ const { container } = render(
75
+ <ArtifactContentRenderer
76
+ content={"one\ntwo\nthree"}
77
+ fileName="notes.txt"
78
+ reveal={{ line: 2, nonce: 1 }}
79
+ />,
80
+ );
81
+
82
+ // Every line is addressable; exactly the target row carries the highlight.
83
+ expect(container.querySelectorAll("[data-line]").length).toBe(3);
84
+ const target = container.querySelector('[data-line="2"]');
85
+ expect(target?.className).toContain("bg-primary-subtle");
86
+ expect(container.querySelectorAll(".bg-primary-subtle").length).toBe(1);
87
+ });
88
+
89
+ it("does not highlight anything when the target line is out of range", () => {
90
+ const { container } = render(
91
+ <ArtifactContentRenderer
92
+ content={"one\ntwo"}
93
+ fileName="notes.txt"
94
+ reveal={{ line: 99, nonce: 1 }}
95
+ />,
96
+ );
97
+ expect(container.querySelectorAll(".bg-primary-subtle").length).toBe(0);
98
+ });
99
+
100
+ it("forces Markdown to its Source (line-faithful) view when revealing", () => {
101
+ const { container } = render(
102
+ <ArtifactContentRenderer
103
+ content={"# Heading\n\ntext"}
104
+ fileName="doc.md"
105
+ reveal={{ line: 1, nonce: 1 }}
106
+ />,
107
+ );
108
+ // Source is line-numbered <pre>, not the rendered <h1>.
109
+ expect(container.querySelector("h1")).toBeNull();
110
+ expect(container.querySelector("pre")).not.toBeNull();
111
+ expect(container.querySelector('[data-line="1"]')).not.toBeNull();
112
+ // The Rendered/Source toggle is still offered.
113
+ expect(screen.getByRole("tab", { name: "Source" })).toBeTruthy();
114
+ });
115
+
116
+ it("renders JSON raw (not reformatted) when revealing, so line numbers hold", () => {
117
+ // Minified single-line JSON: pretty-printing would expand it to many lines
118
+ // and shift line 1. Raw rendering keeps it one addressable row.
119
+ const { container } = render(
120
+ <ArtifactContentRenderer
121
+ content={'{"a":1,"b":2}'}
122
+ fileName="data.json"
123
+ reveal={{ line: 1, nonce: 1 }}
124
+ />,
125
+ );
126
+ const rows = container.querySelectorAll("[data-line]");
127
+ expect(rows.length).toBe(1);
128
+ expect(rows[0].textContent).toContain('{"a":1,"b":2}');
129
+ });
130
+
131
+ it("scrolls the target line into view", () => {
132
+ render(
133
+ <ArtifactContentRenderer
134
+ content={"one\ntwo\nthree"}
135
+ fileName="notes.txt"
136
+ reveal={{ line: 3, nonce: 1 }}
137
+ />,
138
+ );
139
+ expect(scrollIntoView).toHaveBeenCalled();
140
+ });
141
+ });
@@ -5,6 +5,7 @@ import {
5
5
  WorkspaceEntrySchema,
6
6
  WorkspaceSourceSchema,
7
7
  LocalPathSourceSchema,
8
+ GitRepoSourceSchema,
8
9
  } from "@stigmer/protos/ai/stigmer/agentic/session/v1/workspace_pb";
9
10
  import type { WorkspaceEntry } from "@stigmer/protos/ai/stigmer/agentic/session/v1/workspace_pb";
10
11
  import { FilePathLink } from "../FilePathLink";
@@ -89,4 +90,90 @@ describe("FilePathLink interaction", () => {
89
90
  expect(onFilePathClick.mock.calls[0][0]).toBe("src/main.go");
90
91
  expect(onFilePathClick.mock.calls[0][1].action).toBe("copy");
91
92
  });
93
+
94
+ it("falls back to copy when a copy-action handler declines (returns false)", async () => {
95
+ const writeText = vi.fn().mockResolvedValue(undefined);
96
+ vi.stubGlobal("navigator", { clipboard: { writeText } });
97
+ const onFilePathClick = vi.fn().mockReturnValue(false);
98
+ render(
99
+ <FilePathContext.Provider value={{ workspaceEntries: [], onFilePathClick }}>
100
+ <FilePathLink path="src/main.go" />
101
+ </FilePathContext.Provider>,
102
+ );
103
+ fireEvent.click(screen.getByRole("button"));
104
+ expect(onFilePathClick).toHaveBeenCalledTimes(1);
105
+ expect(writeText).toHaveBeenCalledWith("src/main.go");
106
+ vi.unstubAllGlobals();
107
+ });
108
+
109
+ it("suppresses the copy default when a copy-action handler returns void", () => {
110
+ const writeText = vi.fn().mockResolvedValue(undefined);
111
+ vi.stubGlobal("navigator", { clipboard: { writeText } });
112
+ const onFilePathClick = vi.fn(); // returns undefined -> handled
113
+ render(
114
+ <FilePathContext.Provider value={{ workspaceEntries: [], onFilePathClick }}>
115
+ <FilePathLink path="src/main.go" />
116
+ </FilePathContext.Provider>,
117
+ );
118
+ fireEvent.click(screen.getByRole("button"));
119
+ expect(onFilePathClick).toHaveBeenCalledTimes(1);
120
+ expect(writeText).not.toHaveBeenCalled();
121
+ vi.unstubAllGlobals();
122
+ });
123
+ });
124
+
125
+ describe("FilePathLink link-action with an injected handler", () => {
126
+ const gitEntries = [
127
+ create(WorkspaceEntrySchema, {
128
+ name: "acme/app",
129
+ source: create(WorkspaceSourceSchema, {
130
+ source: {
131
+ case: "gitRepo",
132
+ value: create(GitRepoSourceSchema, {
133
+ url: "https://github.com/acme/app.git",
134
+ branch: "main",
135
+ }),
136
+ },
137
+ }),
138
+ }),
139
+ ];
140
+
141
+ it("renders a real <a href> even when a handler is present (native link semantics)", () => {
142
+ const onFilePathClick = vi.fn().mockReturnValue(true);
143
+ render(
144
+ <FilePathContext.Provider value={{ workspaceEntries: gitEntries, onFilePathClick }}>
145
+ <FilePathLink path="src/main.go" />
146
+ </FilePathContext.Provider>,
147
+ );
148
+ const anchor = screen.getByRole("link");
149
+ expect(anchor.getAttribute("href")).toBe(
150
+ "https://github.com/acme/app/blob/main/src/main.go",
151
+ );
152
+ });
153
+
154
+ it("prevents native navigation when the handler opens it in-app (returns true)", () => {
155
+ const onFilePathClick = vi.fn().mockReturnValue(true);
156
+ render(
157
+ <FilePathContext.Provider value={{ workspaceEntries: gitEntries, onFilePathClick }}>
158
+ <FilePathLink path="src/main.go" />
159
+ </FilePathContext.Provider>,
160
+ );
161
+ const evt = new MouseEvent("click", { bubbles: true, cancelable: true });
162
+ screen.getByRole("link").dispatchEvent(evt);
163
+ expect(onFilePathClick).toHaveBeenCalledTimes(1);
164
+ expect(evt.defaultPrevented).toBe(true);
165
+ });
166
+
167
+ it("leaves native navigation intact when the handler declines (returns false)", () => {
168
+ const onFilePathClick = vi.fn().mockReturnValue(false);
169
+ render(
170
+ <FilePathContext.Provider value={{ workspaceEntries: gitEntries, onFilePathClick }}>
171
+ <FilePathLink path="src/main.go" />
172
+ </FilePathContext.Provider>,
173
+ );
174
+ const evt = new MouseEvent("click", { bubbles: true, cancelable: true });
175
+ screen.getByRole("link").dispatchEvent(evt);
176
+ expect(onFilePathClick).toHaveBeenCalledTimes(1);
177
+ expect(evt.defaultPrevented).toBe(false);
178
+ });
92
179
  });