@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,97 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { render, cleanup } from "@testing-library/react";
3
+ import { useRevealLine, type RevealTarget } from "../useRevealLine";
4
+ import { prefersReducedMotion } from "../motion-preference";
5
+
6
+ // Mock the motion utility directly: it caches at module scope, so driving it
7
+ // through `matchMedia` would leak state across cases. Mocking gives each test a
8
+ // deterministic reduced-motion answer.
9
+ vi.mock("../motion-preference", () => ({
10
+ prefersReducedMotion: vi.fn(() => false),
11
+ }));
12
+
13
+ // A tiny harness that renders addressable line rows and drives the hook, so we
14
+ // exercise the real `[data-line]` query + scroll effect against a real DOM.
15
+ function LineView({
16
+ reveal,
17
+ lineCount = 5,
18
+ }: {
19
+ readonly reveal: RevealTarget | undefined;
20
+ readonly lineCount?: number;
21
+ }) {
22
+ const { containerRef, isRevealed } = useRevealLine<HTMLDivElement>(reveal);
23
+ return (
24
+ <div ref={containerRef}>
25
+ {Array.from({ length: lineCount }, (_, i) => {
26
+ const line = i + 1;
27
+ return (
28
+ <span key={line} data-line={line} data-revealed={isRevealed(line)}>
29
+ line {line}
30
+ </span>
31
+ );
32
+ })}
33
+ </div>
34
+ );
35
+ }
36
+
37
+ describe("useRevealLine", () => {
38
+ let scrollSpy: ReturnType<typeof vi.fn>;
39
+
40
+ beforeEach(() => {
41
+ vi.mocked(prefersReducedMotion).mockReturnValue(false);
42
+ // happy-dom has no real scrollIntoView — stub it to assert calls + options.
43
+ scrollSpy = vi.fn();
44
+ Element.prototype.scrollIntoView =
45
+ scrollSpy as unknown as typeof Element.prototype.scrollIntoView;
46
+ });
47
+
48
+ afterEach(() => {
49
+ cleanup();
50
+ });
51
+
52
+ it("scrolls the target line into view on mount", () => {
53
+ render(<LineView reveal={{ line: 3, nonce: 1 }} />);
54
+ expect(scrollSpy).toHaveBeenCalledTimes(1);
55
+ expect(scrollSpy).toHaveBeenCalledWith({ block: "center", behavior: "smooth" });
56
+ });
57
+
58
+ it("re-scrolls when the nonce changes, even for the same line", () => {
59
+ const { rerender } = render(<LineView reveal={{ line: 3, nonce: 1 }} />);
60
+ expect(scrollSpy).toHaveBeenCalledTimes(1);
61
+
62
+ rerender(<LineView reveal={{ line: 3, nonce: 2 }} />);
63
+ expect(scrollSpy).toHaveBeenCalledTimes(2);
64
+ });
65
+
66
+ it("does not re-scroll when the nonce is unchanged", () => {
67
+ const { rerender } = render(<LineView reveal={{ line: 3, nonce: 7 }} />);
68
+ expect(scrollSpy).toHaveBeenCalledTimes(1);
69
+
70
+ // Same nonce, different (irrelevant) prop → no additional scroll.
71
+ rerender(<LineView reveal={{ line: 3, nonce: 7 }} lineCount={6} />);
72
+ expect(scrollSpy).toHaveBeenCalledTimes(1);
73
+ });
74
+
75
+ it("is a no-op when the target line is out of range", () => {
76
+ render(<LineView reveal={{ line: 99, nonce: 1 }} lineCount={5} />);
77
+ expect(scrollSpy).not.toHaveBeenCalled();
78
+ });
79
+
80
+ it("is a no-op when there is no reveal target", () => {
81
+ render(<LineView reveal={undefined} />);
82
+ expect(scrollSpy).not.toHaveBeenCalled();
83
+ });
84
+
85
+ it("uses instant scroll under prefers-reduced-motion", () => {
86
+ vi.mocked(prefersReducedMotion).mockReturnValue(true);
87
+ render(<LineView reveal={{ line: 2, nonce: 1 }} />);
88
+ expect(scrollSpy).toHaveBeenCalledWith({ block: "center", behavior: "auto" });
89
+ });
90
+
91
+ it("reports the revealed line via isRevealed", () => {
92
+ const { container } = render(<LineView reveal={{ line: 4, nonce: 1 }} />);
93
+ const revealed = container.querySelector('[data-revealed="true"]');
94
+ expect(revealed?.getAttribute("data-line")).toBe("4");
95
+ expect(container.querySelectorAll('[data-revealed="true"]')).toHaveLength(1);
96
+ });
97
+ });
@@ -0,0 +1,35 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { fileIconCategory } from "../index";
3
+
4
+ describe("fileIconCategory", () => {
5
+ it("classifies common code extensions", () => {
6
+ for (const name of ["a.ts", "b.tsx", "c.js", "m.py", "s.go", "r.rs"]) {
7
+ expect(fileIconCategory(name)).toBe("code");
8
+ }
9
+ });
10
+
11
+ it("classifies markup, style, data, markdown, image", () => {
12
+ expect(fileIconCategory("index.html")).toBe("markup");
13
+ expect(fileIconCategory("app.css")).toBe("style");
14
+ expect(fileIconCategory("tsconfig.json")).toBe("data");
15
+ expect(fileIconCategory("config.yaml")).toBe("data");
16
+ expect(fileIconCategory("README.md")).toBe("markdown");
17
+ expect(fileIconCategory("logo.png")).toBe("image");
18
+ });
19
+
20
+ it("is case-insensitive on the extension", () => {
21
+ expect(fileIconCategory("Main.TS")).toBe("code");
22
+ expect(fileIconCategory("PHOTO.PNG")).toBe("image");
23
+ });
24
+
25
+ it("falls back to generic for unknown or extension-less names", () => {
26
+ expect(fileIconCategory("Makefile")).toBe("generic");
27
+ expect(fileIconCategory("data.unknownext")).toBe("generic");
28
+ expect(fileIconCategory("trailingdot.")).toBe("generic");
29
+ });
30
+
31
+ it("uses the last dotted segment for multi-dot names", () => {
32
+ expect(fileIconCategory("app.test.ts")).toBe("code");
33
+ expect(fileIconCategory("archive.tar.gz")).toBe("generic");
34
+ });
35
+ });
@@ -0,0 +1,137 @@
1
+ "use client";
2
+
3
+ import type { ReactNode } from "react";
4
+
5
+ // Monochrome, shape-differentiated file icons. Deliberately NOT colored with
6
+ // language brand colors: DD-005 forbids hardcoded colors, and every glyph must
7
+ // tint from `currentColor` so it inherits the row's `--stgm-*` token in every
8
+ // preset and color mode (like VS Code's "Minimal" icon theme). Recognition
9
+ // comes from distinct shapes per broad category, not from color.
10
+
11
+ /** Broad file categories, chosen so one glyph serves many related extensions. */
12
+ export type FileIconCategory =
13
+ | "code"
14
+ | "markup"
15
+ | "style"
16
+ | "data"
17
+ | "markdown"
18
+ | "image"
19
+ | "generic";
20
+
21
+ /**
22
+ * Extension → category. Kept intentionally coarse: adding a language usually
23
+ * means adding one entry here, not a new glyph. Unknown extensions fall back to
24
+ * the generic document icon.
25
+ */
26
+ const EXTENSION_CATEGORY: Readonly<Record<string, FileIconCategory>> = {
27
+ // code
28
+ js: "code", jsx: "code", mjs: "code", cjs: "code",
29
+ ts: "code", tsx: "code",
30
+ py: "code", rb: "code", go: "code", rs: "code",
31
+ java: "code", kt: "code", c: "code", h: "code",
32
+ cpp: "code", cc: "code", hpp: "code", cs: "code",
33
+ php: "code", swift: "code", sh: "code", bash: "code", zsh: "code",
34
+ lua: "code", dart: "code", scala: "code", proto: "code",
35
+ // markup
36
+ html: "markup", htm: "markup", xml: "markup", vue: "markup", svelte: "markup",
37
+ // style
38
+ css: "style", scss: "style", sass: "style", less: "style",
39
+ // data / config
40
+ json: "data", jsonc: "data", yaml: "data", yml: "data",
41
+ toml: "data", ini: "data", env: "data", csv: "data", tsv: "data",
42
+ // markdown
43
+ md: "markdown", mdx: "markdown", markdown: "markdown",
44
+ // image
45
+ png: "image", jpg: "image", jpeg: "image", gif: "image",
46
+ webp: "image", svg: "image", ico: "image", bmp: "image",
47
+ };
48
+
49
+ /** Classify a file name into a {@link FileIconCategory} by its extension. */
50
+ export function fileIconCategory(fileName: string): FileIconCategory {
51
+ const dot = fileName.lastIndexOf(".");
52
+ if (dot < 0 || dot === fileName.length - 1) return "generic";
53
+ const ext = fileName.slice(dot + 1).toLowerCase();
54
+ return EXTENSION_CATEGORY[ext] ?? "generic";
55
+ }
56
+
57
+ /**
58
+ * A monochrome icon for a file, chosen by extension category. Tints from
59
+ * `currentColor`; size via the surrounding font/box.
60
+ */
61
+ export function FileTypeIcon({ fileName }: { readonly fileName: string }): ReactNode {
62
+ const category = fileIconCategory(fileName);
63
+ return (
64
+ <svg
65
+ width="14"
66
+ height="14"
67
+ viewBox="0 0 16 16"
68
+ fill="none"
69
+ stroke="currentColor"
70
+ strokeWidth="1.3"
71
+ strokeLinecap="round"
72
+ strokeLinejoin="round"
73
+ className="shrink-0"
74
+ aria-hidden="true"
75
+ >
76
+ {/* Every category shares a document outline for a consistent silhouette. */}
77
+ <path d="M9.5 1.5H4.5C3.95 1.5 3.5 1.95 3.5 2.5V13.5C3.5 14.05 3.95 14.5 4.5 14.5H11.5C12.05 14.5 12.5 14.05 12.5 13.5V4.5L9.5 1.5Z" />
78
+ <path d="M9.3 1.7V4.5H12.3" />
79
+ {CATEGORY_GLYPH[category]}
80
+ </svg>
81
+ );
82
+ }
83
+
84
+ /** The inner glyph that distinguishes each category, drawn in the doc body. */
85
+ const CATEGORY_GLYPH: Readonly<Record<FileIconCategory, ReactNode>> = {
86
+ code: (
87
+ <>
88
+ <path d="M7 8.5L5.75 9.75L7 11" />
89
+ <path d="M9 8.5L10.25 9.75L9 11" />
90
+ </>
91
+ ),
92
+ markup: (
93
+ <>
94
+ <path d="M6.5 8.5L5.5 9.9L6.5 11.3" />
95
+ <path d="M9.5 8.5L10.5 9.9L9.5 11.3" />
96
+ </>
97
+ ),
98
+ style: <path d="M5.5 9.3H10.5M5.5 11.1H8.5" />,
99
+ data: (
100
+ <>
101
+ <path d="M7 8.4C6.2 8.4 6.2 9.3 6.2 9.9C6.2 10.5 6.2 11.4 5.4 11.4" />
102
+ <path d="M9 8.4C9.8 8.4 9.8 9.3 9.8 9.9C9.8 10.5 9.8 11.4 10.6 11.4" />
103
+ </>
104
+ ),
105
+ markdown: <path d="M5.4 11.3V8.6L6.9 10.1L8.4 8.6V11.3M10.4 8.7V11.1M9.5 10.2L10.4 11.2L11.3 10.2" />,
106
+ image: (
107
+ <>
108
+ <circle cx="6.4" cy="9" r="0.7" />
109
+ <path d="M5.2 11.4L7.3 9.9L10.6 11.6" />
110
+ </>
111
+ ),
112
+ generic: <path d="M5.5 9.3H10.5M5.5 11.1H10.5" />,
113
+ };
114
+
115
+ /** A folder icon; `open` swaps to the opened-folder silhouette. */
116
+ export function FolderTypeIcon({ open = false }: { readonly open?: boolean }): ReactNode {
117
+ return (
118
+ <svg
119
+ width="14"
120
+ height="14"
121
+ viewBox="0 0 16 16"
122
+ fill="none"
123
+ stroke="currentColor"
124
+ strokeWidth="1.3"
125
+ strokeLinecap="round"
126
+ strokeLinejoin="round"
127
+ className="shrink-0"
128
+ aria-hidden="true"
129
+ >
130
+ {open ? (
131
+ <path d="M1.8 5.5V12A1 1 0 002.8 13H12.6L14.5 7.5H4.2L2.8 11" />
132
+ ) : (
133
+ <path d="M1.8 4.5A1 1 0 012.8 3.5H6L7.3 5H13.2A1 1 0 0114.2 6V12A1 1 0 0113.2 13H2.8A1 1 0 011.8 12V4.5Z" />
134
+ )}
135
+ </svg>
136
+ );
137
+ }
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useCallback, useState, type DragEvent } from "react";
4
4
  import { cn } from "@stigmer/theme";
5
+ import { FileTypeIcon, FolderTypeIcon } from "../file-icons/index.js";
5
6
  import type { TreeNode } from "./tree-node.js";
6
7
 
7
8
  /**
@@ -15,6 +16,12 @@ export interface FileTreeNodeProps {
15
16
  readonly node: TreeNode;
16
17
  readonly selectedPath: string;
17
18
  readonly onSelect: (path: string) => void;
19
+ /**
20
+ * Optional double-click handler for file nodes. When set, a double-click on a
21
+ * file calls `onActivate(path)` (used by the workspace surface to pin a
22
+ * previewed tab); the single click still fires `onSelect`. Folders ignore it.
23
+ */
24
+ readonly onActivate?: (path: string) => void;
18
25
  readonly depth: number;
19
26
  /**
20
27
  * When `true`, file nodes (not folders) become draggable and emit
@@ -24,6 +31,17 @@ export interface FileTreeNodeProps {
24
31
  * where file referencing is not applicable (e.g., SkillFileBrowser).
25
32
  */
26
33
  readonly enableDrag?: boolean;
34
+ /**
35
+ * When `true`, render monochrome file-type / folder icons before each name
36
+ * (VS Code explorer feel). Opt-in so drag/skill contexts stay icon-free.
37
+ */
38
+ readonly showIcons?: boolean;
39
+ /**
40
+ * When `true`, draw vertical indent guides down each nested group and indent
41
+ * via nested margins rather than per-row left padding. Opt-in; the default
42
+ * padding-based indentation is preserved for existing consumers.
43
+ */
44
+ readonly indentGuides?: boolean;
27
45
  /**
28
46
  * Maximum depth at which folders start collapsed instead of expanded.
29
47
  * Folders at depth >= maxInitialDepth render collapsed by default,
@@ -49,8 +67,11 @@ export function FileTreeNode({
49
67
  node,
50
68
  selectedPath,
51
69
  onSelect,
70
+ onActivate,
52
71
  depth,
53
72
  enableDrag = false,
73
+ showIcons = false,
74
+ indentGuides = false,
54
75
  maxInitialDepth,
55
76
  }: FileTreeNodeProps) {
56
77
  const [expanded, setExpanded] = useState(
@@ -68,6 +89,10 @@ export function FileTreeNode({
68
89
  }
69
90
  }, [isFolder, node.path, onSelect]);
70
91
 
92
+ const handleDoubleClick = useCallback(() => {
93
+ if (!isFolder) onActivate?.(node.path);
94
+ }, [isFolder, node.path, onActivate]);
95
+
71
96
  const handleDragStart = useCallback(
72
97
  (e: DragEvent) => {
73
98
  e.dataTransfer.setData(FILE_REF_MIME, JSON.stringify({ path: node.path }));
@@ -77,42 +102,68 @@ export function FileTreeNode({
77
102
  );
78
103
 
79
104
  return (
80
- <li role="treeitem" aria-expanded={isFolder ? expanded : undefined}>
105
+ <li
106
+ role="treeitem"
107
+ // Depth conveys tree nesting to assistive tech (indentation is visual
108
+ // only). aria-level is 1-based; `depth` is 0-based. aria-expanded stays on
109
+ // the treeitem (the <li>) — its child group is nested here, so this is the
110
+ // element a tree-aware screen reader reads expandability from.
111
+ aria-level={depth + 1}
112
+ aria-expanded={isFolder ? expanded : undefined}
113
+ >
81
114
  <button
82
115
  type="button"
83
116
  onClick={handleClick}
117
+ onDoubleClick={onActivate ? handleDoubleClick : undefined}
84
118
  draggable={isDraggable}
85
119
  onDragStart={isDraggable ? handleDragStart : undefined}
86
120
  className={cn(
87
- "flex w-full items-center gap-1.5 px-3 py-1 text-left text-xs transition-colors",
121
+ "flex w-full items-center gap-1.5 py-1 text-left text-xs transition-colors",
88
122
  "hover:bg-muted",
89
123
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
90
124
  isSelected && !isFolder && "bg-muted text-foreground font-medium",
91
125
  !isSelected && "text-muted-foreground",
92
126
  isDraggable && "cursor-grab active:cursor-grabbing",
93
127
  )}
94
- style={{ paddingLeft: `${12 + depth * 12}px` }}
128
+ // Indent-guide mode indents via the nested group's margin, so rows need
129
+ // only a small constant inset; padding mode keeps the per-depth inset.
130
+ style={{ paddingLeft: indentGuides ? "8px" : `${12 + depth * 12}px`, paddingRight: "8px" }}
95
131
  aria-current={isSelected ? "true" : undefined}
96
132
  >
97
133
  {isFolder && (
98
- <span className="text-[10px] text-muted-foreground-subtle">
134
+ <span
135
+ aria-hidden="true"
136
+ className="text-[10px] text-muted-foreground-subtle"
137
+ >
99
138
  {expanded ? "▼" : "▶"}
100
139
  </span>
101
140
  )}
141
+ {showIcons &&
142
+ (isFolder ? (
143
+ <FolderTypeIcon open={expanded} />
144
+ ) : (
145
+ <FileTypeIcon fileName={node.name} />
146
+ ))}
102
147
  <span className={cn("truncate", isFolder && "font-medium text-foreground")}>
103
148
  {node.name}
104
149
  </span>
105
150
  </button>
106
151
  {isFolder && expanded && node.children && (
107
- <ul role="group">
152
+ <ul
153
+ role="group"
154
+ className={cn(indentGuides && "ml-3 border-l border-border")}
155
+ >
108
156
  {node.children.map((child) => (
109
157
  <FileTreeNode
110
158
  key={child.path}
111
159
  node={child}
112
160
  selectedPath={selectedPath}
113
161
  onSelect={onSelect}
162
+ onActivate={onActivate}
114
163
  depth={depth + 1}
115
164
  enableDrag={enableDrag}
165
+ showIcons={showIcons}
166
+ indentGuides={indentGuides}
116
167
  maxInitialDepth={maxInitialDepth}
117
168
  />
118
169
  ))}
@@ -0,0 +1,86 @@
1
+ import { describe, it, expect, vi, afterEach } from "vitest";
2
+ import { render, screen, fireEvent, cleanup } from "@testing-library/react";
3
+ import { FileTreeNode } from "../FileTreeNode";
4
+ import type { TreeNode } from "../tree-node";
5
+
6
+ afterEach(cleanup);
7
+
8
+ const fileNode: TreeNode = { name: "a.ts", path: "src/a.ts" };
9
+ const folderNode: TreeNode = {
10
+ name: "src",
11
+ path: "src/",
12
+ children: [fileNode],
13
+ };
14
+
15
+ describe("FileTreeNode — explorer props", () => {
16
+ it("renders an icon before the name when showIcons is set", () => {
17
+ render(
18
+ <ul>
19
+ <FileTreeNode node={fileNode} selectedPath="" onSelect={vi.fn()} depth={0} showIcons />
20
+ </ul>,
21
+ );
22
+ const button = screen.getByText("a.ts").closest("button")!;
23
+ expect(button.querySelector("svg")).toBeTruthy();
24
+ });
25
+
26
+ it("renders no icon by default (unchanged for existing consumers)", () => {
27
+ render(
28
+ <ul>
29
+ <FileTreeNode node={fileNode} selectedPath="" onSelect={vi.fn()} depth={0} />
30
+ </ul>,
31
+ );
32
+ const button = screen.getByText("a.ts").closest("button")!;
33
+ expect(button.querySelector("svg")).toBeNull();
34
+ });
35
+
36
+ it("calls onActivate on double-click of a file", () => {
37
+ const onActivate = vi.fn();
38
+ render(
39
+ <ul>
40
+ <FileTreeNode
41
+ node={fileNode}
42
+ selectedPath=""
43
+ onSelect={vi.fn()}
44
+ onActivate={onActivate}
45
+ depth={0}
46
+ />
47
+ </ul>,
48
+ );
49
+ fireEvent.doubleClick(screen.getByText("a.ts"));
50
+ expect(onActivate).toHaveBeenCalledWith("src/a.ts");
51
+ });
52
+
53
+ it("does not call onActivate on a folder double-click", () => {
54
+ const onActivate = vi.fn();
55
+ render(
56
+ <ul>
57
+ <FileTreeNode
58
+ node={folderNode}
59
+ selectedPath=""
60
+ onSelect={vi.fn()}
61
+ onActivate={onActivate}
62
+ depth={0}
63
+ />
64
+ </ul>,
65
+ );
66
+ fireEvent.doubleClick(screen.getByText("src"));
67
+ expect(onActivate).not.toHaveBeenCalled();
68
+ });
69
+
70
+ it("draws an indent guide on nested groups when indentGuides is set", () => {
71
+ const { container } = render(
72
+ <ul>
73
+ <FileTreeNode
74
+ node={folderNode}
75
+ selectedPath=""
76
+ onSelect={vi.fn()}
77
+ depth={0}
78
+ indentGuides
79
+ />
80
+ </ul>,
81
+ );
82
+ // The nested group carries the guide border class.
83
+ const group = container.querySelector('ul[role="group"]');
84
+ expect(group?.className).toContain("border-l");
85
+ });
86
+ });