@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
@@ -0,0 +1,162 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { renderHook, waitFor, cleanup } from "@testing-library/react";
3
+ import { useWorkspaceFileSearch } from "../useWorkspaceFileSearch";
4
+ import { __clearWorkspaceListingCache } from "../workspaceListingCache";
5
+ import type { WorkspaceEntry } from "../useWorkspaceEntries";
6
+ import type { WorkspaceFileEntry, WorkspaceFileLister } from "../WorkspaceFileLister";
7
+
8
+ let entryIdCounter = 0;
9
+
10
+ function makeEntry(overrides?: Partial<WorkspaceEntry>): WorkspaceEntry {
11
+ return {
12
+ id: `ws-${++entryIdCounter}`,
13
+ name: "acme/api",
14
+ type: "git",
15
+ gitUrl: "https://github.com/acme/api",
16
+ gitBranch: "main",
17
+ ...overrides,
18
+ };
19
+ }
20
+
21
+ const FILES_A: WorkspaceFileEntry[] = [
22
+ { path: "src/index.ts", isDirectory: false },
23
+ { path: "src/button.tsx", isDirectory: false },
24
+ { path: "README.md", isDirectory: false },
25
+ ];
26
+
27
+ const FILES_B: WorkspaceFileEntry[] = [
28
+ { path: "lib/button.ts", isDirectory: false },
29
+ { path: "lib/util.ts", isDirectory: false },
30
+ ];
31
+
32
+ describe("useWorkspaceFileSearch", () => {
33
+ beforeEach(() => {
34
+ __clearWorkspaceListingCache();
35
+ });
36
+ afterEach(cleanup);
37
+
38
+ it("is unsupported and never fetches when no lister is injected", () => {
39
+ const lister = vi.fn();
40
+ const entries = [makeEntry()];
41
+ const { result } = renderHook(() =>
42
+ useWorkspaceFileSearch({ entries, lister: undefined, query: "x" }),
43
+ );
44
+ expect(result.current.isUnsupported).toBe(true);
45
+ expect(result.current.groups).toEqual([]);
46
+ expect(lister).not.toHaveBeenCalled();
47
+ });
48
+
49
+ it("aggregates ranked matches across multiple entries", async () => {
50
+ const entryA = makeEntry();
51
+ const entryB = makeEntry();
52
+ const lister: WorkspaceFileLister = vi.fn(async (e) =>
53
+ e.id === entryA.id ? FILES_A : FILES_B,
54
+ );
55
+
56
+ const { result } = renderHook(() =>
57
+ useWorkspaceFileSearch({ entries: [entryA, entryB], lister, query: "button" }),
58
+ );
59
+
60
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
61
+
62
+ expect(result.current.totalMatches).toBe(2);
63
+ const groupPaths = result.current.groups.map((g) => ({
64
+ id: g.entry.id,
65
+ paths: g.matches.map((m) => m.path),
66
+ }));
67
+ expect(groupPaths).toEqual([
68
+ { id: entryA.id, paths: ["src/button.tsx"] },
69
+ { id: entryB.id, paths: ["lib/button.ts"] },
70
+ ]);
71
+ });
72
+
73
+ it("reuses the shared cache — no refetch when warm", async () => {
74
+ const entry = makeEntry();
75
+ const lister: WorkspaceFileLister = vi.fn(async () => FILES_A);
76
+
77
+ const first = renderHook(
78
+ ({ q }) => useWorkspaceFileSearch({ entries: [entry], lister, query: q }),
79
+ { initialProps: { q: "button" } },
80
+ );
81
+ await waitFor(() => expect(first.result.current.isLoading).toBe(false));
82
+ expect(lister).toHaveBeenCalledTimes(1);
83
+ first.unmount();
84
+
85
+ // Remount (re-enter Search) with the same entry → served from cache.
86
+ const second = renderHook(() =>
87
+ useWorkspaceFileSearch({ entries: [entry], lister, query: "index" }),
88
+ );
89
+ await waitFor(() => expect(second.result.current.isLoading).toBe(false));
90
+ expect(lister).toHaveBeenCalledTimes(1);
91
+ expect(second.result.current.groups[0].matches.map((m) => m.path)).toEqual([
92
+ "src/index.ts",
93
+ ]);
94
+ });
95
+
96
+ it("isolates a per-entry error while other entries still resolve", async () => {
97
+ const good = makeEntry();
98
+ const bad = makeEntry();
99
+ const lister: WorkspaceFileLister = vi.fn(async (e) => {
100
+ if (e.id === bad.id) throw new Error("network down");
101
+ return FILES_A;
102
+ });
103
+
104
+ const { result } = renderHook(() =>
105
+ useWorkspaceFileSearch({ entries: [good, bad], lister, query: "button" }),
106
+ );
107
+
108
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
109
+
110
+ const goodGroup = result.current.groups.find((g) => g.entry.id === good.id);
111
+ const badGroup = result.current.groups.find((g) => g.entry.id === bad.id);
112
+ expect(goodGroup?.matches.map((m) => m.path)).toEqual(["src/button.tsx"]);
113
+ expect(badGroup?.error?.message).toBe("network down");
114
+ expect(result.current.error?.message).toBe("network down");
115
+ });
116
+
117
+ it("surfaces per-group truncated from a notice entry", async () => {
118
+ const entry = makeEntry();
119
+ const lister: WorkspaceFileLister = vi.fn(async () => [
120
+ { path: "src/button.tsx", isDirectory: false },
121
+ { path: "... truncated", isDirectory: false, notice: true as const },
122
+ ]);
123
+
124
+ const { result } = renderHook(() =>
125
+ useWorkspaceFileSearch({ entries: [entry], lister, query: "button" }),
126
+ );
127
+
128
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
129
+ expect(result.current.groups[0].truncated).toBe(true);
130
+ expect(result.current.groups[0].matches.map((m) => m.path)).toEqual([
131
+ "src/button.tsx",
132
+ ]);
133
+ });
134
+
135
+ it("reports isUnsupported when every entry's substrate returns null", async () => {
136
+ const entry = makeEntry();
137
+ const lister: WorkspaceFileLister = vi.fn(async () => null);
138
+
139
+ const { result } = renderHook(() =>
140
+ useWorkspaceFileSearch({ entries: [entry], lister, query: "x" }),
141
+ );
142
+
143
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
144
+ expect(result.current.isUnsupported).toBe(true);
145
+ expect(result.current.groups).toEqual([]);
146
+ });
147
+
148
+ it("keeps a stable return reference across rerenders with an unchanged query", async () => {
149
+ const entry = makeEntry();
150
+ const entries = [entry];
151
+ const lister: WorkspaceFileLister = vi.fn(async () => FILES_A);
152
+
153
+ const { result, rerender } = renderHook(() =>
154
+ useWorkspaceFileSearch({ entries, lister, query: "button" }),
155
+ );
156
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
157
+
158
+ const before = result.current;
159
+ rerender();
160
+ expect(result.current).toBe(before);
161
+ });
162
+ });
@@ -0,0 +1,61 @@
1
+ // Correlate an open workspace-file selection with the session file change that
2
+ // touched it, so the read-only Viewer can default a changed file to its diff
3
+ // (Slice 4). This is the change-side peer of `resolveWorkspaceFileSelection`.
4
+ //
5
+ // The hard part is again PATH FORM, and — critically — ENTRY IDENTITY. A
6
+ // `FileChange` carries a capture-side path (`path` is workspace-root-relative,
7
+ // falling back to `absolutePath`), while the selection is the reader's
8
+ // `{ entryId, path }`. Matching by a bare `/`-boundary suffix (as the
9
+ // presentation-only `changeForRowPath` does) is deliberately NOT used here: in
10
+ // a multi-repo session it would happily match `repoA/src/a.ts` to a change in
11
+ // `repoB/src/a.ts` and render the wrong file's diff.
12
+ //
13
+ // Instead we route each change's path through the SAME correlation-grade
14
+ // resolver (`resolveWorkspaceFileSelection`) the tree and transcript use to
15
+ // open files, with the SAME `entries` and `sandboxWorkspaceRoot`. A change is
16
+ // the open file's change iff it resolves to the exact `{ entryId, path }`
17
+ // currently selected. Single-entry sessions match exactly; ambiguous
18
+ // multi-entry / absolute paths degrade to "no match" (never a wrong match),
19
+ // inheriting DD-08's honesty. One resolver, one truth.
20
+
21
+ import type { FileChange } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
22
+ import type { SelectedWorkspaceFile } from "../internal/store/workspace-file-selection-store.js";
23
+ import { resolveWorkspaceFileSelection } from "./resolveWorkspaceFileSelection.js";
24
+ import type { WorkspaceEntry } from "./useWorkspaceEntries.js";
25
+
26
+ /**
27
+ * The session {@link FileChange} that touched the currently-open file, or
28
+ * `null` when the open file was not changed this session (or cannot be
29
+ * correlated with certainty).
30
+ *
31
+ * @param selection - The open file, keyed by `entryId` + repo/root-relative `path`.
32
+ * @param fileChanges - Net-per-file session changes (from `useSessionFileChanges`).
33
+ * @param entries - The client workspace entries — the SAME list the tree and
34
+ * transcript resolve against (`flow.workspace.entries`), so the returned
35
+ * `entryId` is directly comparable. Do not pass the proto `workspaceEntries`.
36
+ * @param sandboxWorkspaceRoot - The sandbox root for git sessions (strips
37
+ * cloud-absolute change paths), identical to the transcript resolver's input.
38
+ * @returns The matching change, or `null`.
39
+ */
40
+ export function findChangeForSelection(
41
+ selection: SelectedWorkspaceFile,
42
+ fileChanges: readonly FileChange[],
43
+ entries: readonly WorkspaceEntry[],
44
+ sandboxWorkspaceRoot?: string,
45
+ ): FileChange | null {
46
+ for (const change of fileChanges) {
47
+ const resolved = resolveWorkspaceFileSelection(
48
+ change.path || change.absolutePath,
49
+ entries,
50
+ sandboxWorkspaceRoot,
51
+ );
52
+ if (
53
+ resolved !== null &&
54
+ resolved.entryId === selection.entryId &&
55
+ resolved.path === selection.path
56
+ ) {
57
+ return change;
58
+ }
59
+ }
60
+ return null;
61
+ }
@@ -7,13 +7,67 @@ export type {
7
7
  WorkspaceFileEntry,
8
8
  WorkspaceFileLister,
9
9
  } from "./WorkspaceFileLister.js";
10
+ export { MAX_WORKSPACE_FILE_READ_BYTES } from "./WorkspaceFileReader.js";
11
+ export type {
12
+ WorkspaceFileContent,
13
+ WorkspaceFileReader,
14
+ } from "./WorkspaceFileReader.js";
10
15
  export { useWorkspaceFiles } from "./useWorkspaceFiles.js";
11
16
  export type {
12
17
  UseWorkspaceFilesOptions,
13
18
  UseWorkspaceFilesReturn,
14
19
  } from "./useWorkspaceFiles.js";
15
- export { WorkspaceEntryFiles } from "./WorkspaceEntryFiles.js";
16
- export type { WorkspaceEntryFilesProps } from "./WorkspaceEntryFiles.js";
20
+ export { useWorkspaceFileContent } from "./useWorkspaceFileContent.js";
21
+ export type {
22
+ UseWorkspaceFileContentOptions,
23
+ UseWorkspaceFileContentReturn,
24
+ } from "./useWorkspaceFileContent.js";
25
+ export { useWorkspaceFileSearch } from "./useWorkspaceFileSearch.js";
26
+ export type {
27
+ UseWorkspaceFileSearchOptions,
28
+ UseWorkspaceFileSearchReturn,
29
+ WorkspaceFileSearchGroup,
30
+ } from "./useWorkspaceFileSearch.js";
31
+ // The match type is part of the public search return; the matcher itself stays @internal.
32
+ export type { WorkspaceFileMatch } from "./matchWorkspaceFiles.js";
33
+ export { WorkspaceFileSearch } from "./WorkspaceFileSearch.js";
34
+ export type { WorkspaceFileSearchProps } from "./WorkspaceFileSearch.js";
35
+ export type {
36
+ WorkspaceContentMatch,
37
+ WorkspaceContentSearchResult,
38
+ WorkspaceContentSearcher,
39
+ } from "./WorkspaceContentSearcher.js";
40
+ export { useWorkspaceContentSearch } from "./useWorkspaceContentSearch.js";
41
+ export type {
42
+ UseWorkspaceContentSearchOptions,
43
+ UseWorkspaceContentSearchReturn,
44
+ WorkspaceContentSearchGroup,
45
+ } from "./useWorkspaceContentSearch.js";
46
+ export { WorkspaceContentSearch } from "./WorkspaceContentSearch.js";
47
+ export type { WorkspaceContentSearchProps } from "./WorkspaceContentSearch.js";
48
+ // The highlight helper (findHighlightRanges) stays @internal, like matchWorkspaceFiles.
49
+ export { FileViewer } from "./FileViewer.js";
50
+ export type { FileViewerProps, FileViewerHandle } from "./FileViewer.js";
51
+ export { WorkspaceSurface } from "./WorkspaceSurface.js";
52
+ export type {
53
+ WorkspaceSurfaceProps,
54
+ SurfaceRailView,
55
+ SurfaceVirtualDocument,
56
+ } from "./WorkspaceSurface.js";
57
+ // Virtual-document identity helpers: hosts mint collision-proof entry ids for
58
+ // SurfaceVirtualDocument tabs and detect them in file-only code paths.
59
+ export { virtualEntryId, isVirtualEntryId } from "../internal/store/index.js";
60
+ export { ExplorerTree } from "./ExplorerTree.js";
61
+ export type { ExplorerTreeProps } from "./ExplorerTree.js";
62
+ // The open-editor tab shape + open-file options are part of the public
63
+ // WorkspaceSurface API.
64
+ export type { OpenEditor, OpenFileOptions } from "../internal/store/index.js";
65
+ // The jump-to-line reveal target rides on FileViewer / WorkspaceSurface /
66
+ // ArtifactContentRenderer props; export the shape (the hook stays @internal).
67
+ export type { RevealTarget } from "../internal/useRevealLine.js";
68
+ // The viewer's selection shape is part of the public FileViewer API; re-export
69
+ // the type (not the store) so consumers can construct/inspect a selection.
70
+ export type { SelectedWorkspaceFile } from "../internal/store/workspace-file-selection-store.js";
17
71
  export { WorkspaceEditor } from "./WorkspaceEditor.js";
18
72
  export type { WorkspaceEditorProps } from "./WorkspaceEditor.js";
19
73
  export { WorkspaceSummary } from "./WorkspaceSummary.js";
@@ -0,0 +1,51 @@
1
+ // Pure highlight helper behind content search. Kept separate from the hook and
2
+ // component so the offset math is exhaustively unit-testable in isolation (no
3
+ // React, no async).
4
+ //
5
+ // The backend returns each hit's `preview` line but no column offset (a
6
+ // cross-language byte/UTF-16 offset would be brittle — see WorkspaceContentMatch).
7
+ // Since matching is case-insensitive substring, the client re-derives the
8
+ // highlight span(s) from the preview text itself, identically to how
9
+ // `matchWorkspaceFiles` derives the filename highlight range. `@internal`.
10
+
11
+ /**
12
+ * A half-open `[start, end)` range of code-unit offsets into a preview string,
13
+ * marking one occurrence of the query to emphasize.
14
+ */
15
+ export interface HighlightRange {
16
+ /** Inclusive start offset within the preview. */
17
+ readonly start: number;
18
+ /** Exclusive end offset within the preview. */
19
+ readonly end: number;
20
+ }
21
+
22
+ /**
23
+ * Finds every case-insensitive occurrence of `query` within `preview`,
24
+ * best-effort for rendering emphasis.
25
+ *
26
+ * - Empty/whitespace query → `[]` (nothing to highlight).
27
+ * - Overlapping matches cannot occur (search advances past each match end).
28
+ * - Offsets are JS string code units, so a consumer highlights with
29
+ * `preview.slice(start, end)`.
30
+ */
31
+ export function findHighlightRanges(
32
+ preview: string,
33
+ query: string,
34
+ ): HighlightRange[] {
35
+ const needle = query.trim().toLowerCase();
36
+ if (needle.length === 0) return [];
37
+
38
+ const haystack = preview.toLowerCase();
39
+ const ranges: HighlightRange[] = [];
40
+
41
+ let from = 0;
42
+ for (;;) {
43
+ const start = haystack.indexOf(needle, from);
44
+ if (start === -1) break;
45
+ const end = start + needle.length;
46
+ ranges.push({ start, end });
47
+ from = end;
48
+ }
49
+
50
+ return ranges;
51
+ }
@@ -0,0 +1,86 @@
1
+ // Pure ranking function behind workspace search. Kept separate from the hook and
2
+ // component so the matching/ranking rules are exhaustively unit-testable in
3
+ // isolation (no React, no async).
4
+ //
5
+ // Match semantics intentionally mirror `internal/file-tree/filterFileTree` —
6
+ // case-insensitive substring — but widened from a node's basename to the full
7
+ // relative path, so `foo/bar` is findable and directory context is searchable
8
+ // (DD-10). The trivial predicate is not extracted into a shared micro-module; a
9
+ // cross-reference comment is the lower-coupling, honest choice.
10
+
11
+ import type { WorkspaceFileEntry } from "./WorkspaceFileLister.js";
12
+
13
+ /**
14
+ * A workspace file that matched a search query.
15
+ *
16
+ * `matchStart`/`matchEnd` are code-unit offsets into `path` (a half-open
17
+ * `[start, end)` range) for the single contiguous substring hit, so a consumer
18
+ * can highlight exactly the matched span with `path.slice(...)`.
19
+ */
20
+ export interface WorkspaceFileMatch {
21
+ /** Repo-relative (git) or root-relative (local) path of the matched file. */
22
+ readonly path: string;
23
+ /** Inclusive start offset of the matched substring within `path`. */
24
+ readonly matchStart: number;
25
+ /** Exclusive end offset of the matched substring within `path`. */
26
+ readonly matchEnd: number;
27
+ }
28
+
29
+ /** A match plus the ranking signal used only for sorting. */
30
+ interface RankedMatch extends WorkspaceFileMatch {
31
+ /** `true` when the hit falls within the basename (after the last `/`). */
32
+ readonly isBasenameHit: boolean;
33
+ }
34
+
35
+ /**
36
+ * Ranks a flat listing against a query, returning matched files best-first.
37
+ *
38
+ * - Case-insensitive substring on the full relative path.
39
+ * - Directories and advisory `notice` entries are never matched (a defensive
40
+ * guard even though the shared cache already strips notices — DD-11).
41
+ * - Empty/whitespace query returns `[]` (search shows a hint, not everything).
42
+ *
43
+ * Ranking, in order: a basename hit outranks a path-only hit; then an earlier
44
+ * match offset; then a shorter path; then lexicographic path for a stable,
45
+ * deterministic order.
46
+ */
47
+ export function matchWorkspaceFiles(
48
+ files: readonly WorkspaceFileEntry[],
49
+ query: string,
50
+ ): WorkspaceFileMatch[] {
51
+ const trimmed = query.trim();
52
+ if (trimmed.length === 0) return [];
53
+
54
+ const needle = trimmed.toLowerCase();
55
+ const ranked: RankedMatch[] = [];
56
+
57
+ for (const file of files) {
58
+ if (file.isDirectory || file.notice) continue;
59
+
60
+ const matchStart = file.path.toLowerCase().indexOf(needle);
61
+ if (matchStart === -1) continue;
62
+
63
+ const lastSlash = file.path.lastIndexOf("/");
64
+ ranked.push({
65
+ path: file.path,
66
+ matchStart,
67
+ matchEnd: matchStart + needle.length,
68
+ isBasenameHit: matchStart >= lastSlash + 1,
69
+ });
70
+ }
71
+
72
+ ranked.sort(compareMatches);
73
+
74
+ return ranked.map(({ path, matchStart, matchEnd }) => ({
75
+ path,
76
+ matchStart,
77
+ matchEnd,
78
+ }));
79
+ }
80
+
81
+ function compareMatches(a: RankedMatch, b: RankedMatch): number {
82
+ if (a.isBasenameHit !== b.isBasenameHit) return a.isBasenameHit ? -1 : 1;
83
+ if (a.matchStart !== b.matchStart) return a.matchStart - b.matchStart;
84
+ if (a.path.length !== b.path.length) return a.path.length - b.path.length;
85
+ return a.path.localeCompare(b.path);
86
+ }
@@ -0,0 +1,119 @@
1
+ // Resolve a transcript tool-call file path to an openable workspace-file
2
+ // selection. This is the seam that lets a click on a path in the message thread
3
+ // route to the same read-only Viewer a file-tree click drives (Slice 3).
4
+ //
5
+ // The hard part is NOT identity ("which entry") but PATH FORM. The reader and
6
+ // lister contracts are strict: `WorkspaceFileReader` / the GitHub Trees lister
7
+ // speak repo-root-relative (git) or root-relative (local) paths — the exact
8
+ // strings a file-tree node carries. Transcript tool-call paths are not
9
+ // guaranteed to be in that form: depending on harness and workspace layout they
10
+ // arrive workspace-relative, subdir-prefixed, or absolute (see
11
+ // `execution/file-review-status.ts` `changeForRowPath`, which documents the
12
+ // same variance and treats path matching as presentation-only).
13
+ //
14
+ // Rather than fuzzy-match, this resolver normalizes deterministically using only
15
+ // anchors the client already holds, grounded in how the runner provisions the
16
+ // workspace (`backend/.../workspace/provisioner.ts`):
17
+ // - Single entry -> cloned at the workspace root, so a (humanized) tool-call
18
+ // path is already repo/root-relative. Identity transform.
19
+ // - Multiple git -> each cloned into `<root>/<entry.name>/`, so the path is
20
+ // prefixed by the subdir name; strip it.
21
+ // - Local (desktop) -> the native reader joins `entry.localPath` + a
22
+ // root-relative path; local tool paths may be absolute, so
23
+ // strip the known `entry.localPath` prefix.
24
+ // - Cloud absolute -> strip the known `sandboxWorkspaceRoot` first.
25
+ // Anything that does not resolve to a definite relative path returns `null` so
26
+ // the caller keeps its existing copy/GitHub-link behavior — a wrong file can
27
+ // never open. Extending this to arbitrary/absolute paths belongs to a later
28
+ // slice that resolves against the authoritative file-tree index.
29
+
30
+ import { classifyPath } from "../execution/file-path-resolver.js";
31
+ import type { SelectedWorkspaceFile } from "../internal/store/workspace-file-selection-store.js";
32
+ import type { WorkspaceEntry } from "./useWorkspaceEntries.js";
33
+
34
+ /** Forward-slash-normalize and drop any leading slashes for prefix comparison. */
35
+ function normalize(path: string): string {
36
+ return path.replace(/\\/g, "/").replace(/^\/+/, "");
37
+ }
38
+
39
+ /**
40
+ * Returns the portion of `path` beneath `anchor`, or `null` when `path` does
41
+ * not sit under `anchor`. An exact match (the anchor itself, a directory) yields
42
+ * `""` — the caller rejects it, since a directory is not a viewable file.
43
+ */
44
+ function relativeTo(path: string, anchor: string): string | null {
45
+ if (!anchor) return null;
46
+ if (path === anchor) return "";
47
+ if (path.startsWith(anchor + "/")) return path.slice(anchor.length + 1);
48
+ return null;
49
+ }
50
+
51
+ /**
52
+ * Resolves a transcript tool-call file path to the {@link SelectedWorkspaceFile}
53
+ * that opens it in the read-only Viewer, or `null` when it cannot be resolved to
54
+ * a definite repo/root-relative file (caller keeps copy/GitHub-link fallback).
55
+ *
56
+ * `entries` are the client-side workspace entries (`useWorkspaceEntries`), the
57
+ * same list the Viewer resolves against, so the returned `entryId` is directly
58
+ * usable. `sandboxWorkspaceRoot` (when the session has git-repo entries, e.g.
59
+ * `/home/daytona/workspace`) is stripped first so cloud-absolute paths reduce to
60
+ * workspace-relative before per-entry anchoring.
61
+ *
62
+ * The result `path` is guaranteed to match what the file tree / lister emits for
63
+ * the same file, so opening from the transcript and from the tree produce an
64
+ * identical selection (and hit the same content fetch/cache).
65
+ */
66
+ export function resolveWorkspaceFileSelection(
67
+ path: string,
68
+ entries: readonly WorkspaceEntry[],
69
+ sandboxWorkspaceRoot?: string,
70
+ ): SelectedWorkspaceFile | null {
71
+ if (!path || entries.length === 0) return null;
72
+
73
+ // 1. Reduce a cloud-absolute path to workspace-relative using the known root.
74
+ let work = normalize(path);
75
+ if (sandboxWorkspaceRoot) {
76
+ const root = normalize(sandboxWorkspaceRoot);
77
+ const relToRoot = relativeTo(work, root);
78
+ if (relToRoot !== null) work = relToRoot;
79
+ }
80
+ // The bare workspace root ("" after stripping, or the humanized ".") is not a
81
+ // file.
82
+ if (!work || work === ".") return null;
83
+
84
+ // 2. The platform mount (`.stigmer/…`) is a virtual namespace, not a workspace
85
+ // file — checked after the root strip so an absolute `<root>/.stigmer/…`
86
+ // is caught too. Keep copy behavior for it.
87
+ if (classifyPath(work).kind === "platform") return null;
88
+
89
+ // 3. Anchor on the exact per-entry roots the client holds. Local entries
90
+ // anchor on their absolute `localPath`; multi-entry git repos anchor on the
91
+ // provisioning subdir (`entry.name`). A single git entry is cloned at the
92
+ // root, so it falls through to step 4.
93
+ const isMulti = entries.length > 1;
94
+ for (const entry of entries) {
95
+ let anchor: string | null = null;
96
+ if (entry.type === "local" && entry.localPath) {
97
+ anchor = normalize(entry.localPath);
98
+ } else if (entry.type === "git" && isMulti) {
99
+ anchor = normalize(entry.name);
100
+ }
101
+ if (anchor === null) continue;
102
+
103
+ const rel = relativeTo(work, anchor);
104
+ // Exact match is the entry's own root directory, not a viewable file — stop
105
+ // rather than fall through to the single-entry fallback (which would return
106
+ // the raw root path).
107
+ if (rel === "") return null;
108
+ if (rel) return { entryId: entry.id, path: rel };
109
+ }
110
+
111
+ // 4. A single entry whose anchor did not apply (e.g. a git repo at the root, or
112
+ // a local path already root-relative): the path is already repo/root-relative.
113
+ if (entries.length === 1) {
114
+ return { entryId: entries[0].id, path: work };
115
+ }
116
+
117
+ // 5. Multi-entry with no anchor match: refuse to guess which entry owns it.
118
+ return null;
119
+ }