@stigmer/react 0.0.91 → 0.0.93

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 (328) hide show
  1. package/README.md +57 -12
  2. package/agent/AgentDetailView.js +7 -7
  3. package/agent/AgentDetailView.js.map +1 -1
  4. package/agent/AgentPicker.js +3 -3
  5. package/agent/AgentPicker.js.map +1 -1
  6. package/api-key/ApiKeyCreatedAlert.js +1 -1
  7. package/api-key/ApiKeyCreatedAlert.js.map +1 -1
  8. package/api-key/ApiKeyListPanel.js +4 -4
  9. package/api-key/ApiKeyListPanel.js.map +1 -1
  10. package/api-key/CreateApiKeyForm.js +1 -1
  11. package/api-key/CreateApiKeyForm.js.map +1 -1
  12. package/attachment/AttachmentChipList.js +2 -2
  13. package/attachment/AttachmentChipList.js.map +1 -1
  14. package/color-mode.d.ts +48 -0
  15. package/color-mode.d.ts.map +1 -0
  16. package/color-mode.js +53 -0
  17. package/color-mode.js.map +1 -0
  18. package/composer/ComposerToolbar.d.ts +6 -2
  19. package/composer/ComposerToolbar.d.ts.map +1 -1
  20. package/composer/ComposerToolbar.js +5 -3
  21. package/composer/ComposerToolbar.js.map +1 -1
  22. package/composer/ConfigureMenu.js +3 -3
  23. package/composer/ConfigureMenu.js.map +1 -1
  24. package/composer/ContextChip.js +1 -1
  25. package/composer/ContextChip.js.map +1 -1
  26. package/composer/ContextPopover.js +1 -1
  27. package/composer/ContextPopover.js.map +1 -1
  28. package/composer/SessionComposer.d.ts +24 -3
  29. package/composer/SessionComposer.d.ts.map +1 -1
  30. package/composer/SessionComposer.js +5 -4
  31. package/composer/SessionComposer.js.map +1 -1
  32. package/environment/CreateEnvironmentForm.js +1 -1
  33. package/environment/CreateEnvironmentForm.js.map +1 -1
  34. package/environment/EnvVarForm.js +2 -2
  35. package/environment/EnvVarForm.js.map +1 -1
  36. package/environment/EnvironmentListPanel.js +2 -2
  37. package/environment/EnvironmentListPanel.js.map +1 -1
  38. package/environment/EnvironmentVariableEditor.js +6 -6
  39. package/environment/EnvironmentVariableEditor.js.map +1 -1
  40. package/error/ErrorMessage.js +1 -1
  41. package/error/ErrorMessage.js.map +1 -1
  42. package/execution/ApprovalCard.js +4 -4
  43. package/execution/ApprovalCard.js.map +1 -1
  44. package/execution/ArtifactCard.js +1 -1
  45. package/execution/ArtifactCard.js.map +1 -1
  46. package/execution/ArtifactPreviewModal.js +4 -4
  47. package/execution/ArtifactPreviewModal.js.map +1 -1
  48. package/execution/FollowUpInput.js +1 -1
  49. package/execution/FollowUpInput.js.map +1 -1
  50. package/execution/McpToolDetail.js +4 -4
  51. package/execution/McpToolDetail.js.map +1 -1
  52. package/execution/MessageEntry.js +1 -1
  53. package/execution/MessageEntry.js.map +1 -1
  54. package/execution/MessageThread.js +1 -1
  55. package/execution/MessageThread.js.map +1 -1
  56. package/execution/SessionVariablesInput.js +4 -4
  57. package/execution/SessionVariablesInput.js.map +1 -1
  58. package/execution/SubAgentSection.js +2 -2
  59. package/execution/SubAgentSection.js.map +1 -1
  60. package/execution/ToolCallDetail.js +2 -2
  61. package/execution/ToolCallDetail.js.map +1 -1
  62. package/execution/ToolCallGroup.js +1 -1
  63. package/execution/ToolCallGroup.js.map +1 -1
  64. package/execution/ToolCallItem.js +2 -2
  65. package/execution/ToolCallItem.js.map +1 -1
  66. package/execution/WriteBackCard.js +3 -3
  67. package/execution/WriteBackCard.js.map +1 -1
  68. package/execution/tool-rendering-primitives.js +3 -3
  69. package/execution/tool-rendering-primitives.js.map +1 -1
  70. package/github/GitHubRepoPicker.js +5 -5
  71. package/github/GitHubRepoPicker.js.map +1 -1
  72. package/iam-policy/GrantAccessForm.js +1 -1
  73. package/iam-policy/GrantAccessForm.js.map +1 -1
  74. package/iam-policy/OrgMembersPanel.js +5 -5
  75. package/iam-policy/OrgMembersPanel.js.map +1 -1
  76. package/identity-provider/CreateIdentityProviderForm.js +3 -3
  77. package/identity-provider/CreateIdentityProviderForm.js.map +1 -1
  78. package/identity-provider/IdentityProviderDetailPanel.js +5 -5
  79. package/identity-provider/IdentityProviderDetailPanel.js.map +1 -1
  80. package/identity-provider/IdentityProviderListPanel.js +3 -3
  81. package/identity-provider/IdentityProviderListPanel.js.map +1 -1
  82. package/identity-provider/IdentityProviderWizard.js +7 -7
  83. package/identity-provider/IdentityProviderWizard.js.map +1 -1
  84. package/identity-provider/ProviderPicker.js +2 -2
  85. package/identity-provider/ProviderPicker.js.map +1 -1
  86. package/index.d.ts +8 -4
  87. package/index.d.ts.map +1 -1
  88. package/index.js +7 -3
  89. package/index.js.map +1 -1
  90. package/internal/CloudFeatureNotice.js +1 -1
  91. package/internal/CloudFeatureNotice.js.map +1 -1
  92. package/internal/Tabs.js +1 -1
  93. package/internal/Tabs.js.map +1 -1
  94. package/internal/markdown-components.js +2 -2
  95. package/internal/markdown-components.js.map +1 -1
  96. package/invitation/InvitationCreatedAlert.js +1 -1
  97. package/invitation/InvitationCreatedAlert.js.map +1 -1
  98. package/invitation/InvitationManager.js +5 -5
  99. package/invitation/InvitationManager.js.map +1 -1
  100. package/invitation/InvitationRedemption.js +4 -4
  101. package/invitation/InvitationRedemption.js.map +1 -1
  102. package/library/ResourceCountCard.js +1 -1
  103. package/library/ResourceCountCard.js.map +1 -1
  104. package/library/ResourceListView.js +5 -5
  105. package/library/ResourceListView.js.map +1 -1
  106. package/mcp-server/McpServerConfigPanel.js +5 -5
  107. package/mcp-server/McpServerConfigPanel.js.map +1 -1
  108. package/mcp-server/McpServerConnectDialog.js +4 -4
  109. package/mcp-server/McpServerConnectDialog.js.map +1 -1
  110. package/mcp-server/McpServerDetailView.js +14 -14
  111. package/mcp-server/McpServerDetailView.js.map +1 -1
  112. package/mcp-server/McpServerPicker.js +4 -4
  113. package/mcp-server/McpServerPicker.js.map +1 -1
  114. package/mcp-server/McpToolSelector.js +3 -3
  115. package/mcp-server/McpToolSelector.js.map +1 -1
  116. package/mcp-server/OAuthAppForm.js +1 -1
  117. package/mcp-server/OAuthAppForm.js.map +1 -1
  118. package/models/ModelSelector.js +1 -1
  119. package/models/ModelSelector.js.map +1 -1
  120. package/oauth-app/CreateOAuthAppForm.js +1 -1
  121. package/oauth-app/CreateOAuthAppForm.js.map +1 -1
  122. package/oauth-app/OAuthAppDetailPanel.js +3 -3
  123. package/oauth-app/OAuthAppDetailPanel.js.map +1 -1
  124. package/oauth-app/OAuthAppListPanel.js +2 -2
  125. package/oauth-app/OAuthAppListPanel.js.map +1 -1
  126. package/organization/CreateOrganizationForm.js +1 -1
  127. package/organization/CreateOrganizationForm.js.map +1 -1
  128. package/organization/OrgProfilePanel.js +3 -3
  129. package/organization/OrgProfilePanel.js.map +1 -1
  130. package/package.json +4 -4
  131. package/platform-client/CreatePlatformClientForm.js +2 -2
  132. package/platform-client/CreatePlatformClientForm.js.map +1 -1
  133. package/platform-client/PlatformClientDetailPanel.js +4 -4
  134. package/platform-client/PlatformClientDetailPanel.js.map +1 -1
  135. package/platform-client/PlatformClientListPanel.js +3 -3
  136. package/platform-client/PlatformClientListPanel.js.map +1 -1
  137. package/platform-client/PlatformClientSecretAlert.js +2 -2
  138. package/platform-client/PlatformClientSecretAlert.js.map +1 -1
  139. package/provider.d.ts +39 -2
  140. package/provider.d.ts.map +1 -1
  141. package/provider.js +11 -3
  142. package/provider.js.map +1 -1
  143. package/runner/RunnerListPanel.d.ts +65 -0
  144. package/runner/RunnerListPanel.d.ts.map +1 -0
  145. package/runner/RunnerListPanel.js +237 -0
  146. package/runner/RunnerListPanel.js.map +1 -0
  147. package/runner/RunnerPicker.d.ts +54 -0
  148. package/runner/RunnerPicker.d.ts.map +1 -0
  149. package/runner/RunnerPicker.js +133 -0
  150. package/runner/RunnerPicker.js.map +1 -0
  151. package/runner/__tests__/useDeleteRunner.test.d.ts +2 -0
  152. package/runner/__tests__/useDeleteRunner.test.d.ts.map +1 -0
  153. package/runner/__tests__/useDeleteRunner.test.js +108 -0
  154. package/runner/__tests__/useDeleteRunner.test.js.map +1 -0
  155. package/runner/__tests__/useLaunchLocalRunner.test.d.ts +2 -0
  156. package/runner/__tests__/useLaunchLocalRunner.test.d.ts.map +1 -0
  157. package/runner/__tests__/useLaunchLocalRunner.test.js +143 -0
  158. package/runner/__tests__/useLaunchLocalRunner.test.js.map +1 -0
  159. package/runner/__tests__/useStopRunner.test.d.ts +2 -0
  160. package/runner/__tests__/useStopRunner.test.d.ts.map +1 -0
  161. package/runner/__tests__/useStopRunner.test.js +114 -0
  162. package/runner/__tests__/useStopRunner.test.js.map +1 -0
  163. package/runner/index.d.ts +14 -0
  164. package/runner/index.d.ts.map +1 -0
  165. package/runner/index.js +8 -0
  166. package/runner/index.js.map +1 -0
  167. package/runner/phase.d.ts +30 -0
  168. package/runner/phase.d.ts.map +1 -0
  169. package/runner/phase.js +58 -0
  170. package/runner/phase.js.map +1 -0
  171. package/runner/useDeleteRunner.d.ts +36 -0
  172. package/runner/useDeleteRunner.d.ts.map +1 -0
  173. package/runner/useDeleteRunner.js +42 -0
  174. package/runner/useDeleteRunner.js.map +1 -0
  175. package/runner/useLaunchLocalRunner.d.ts +84 -0
  176. package/runner/useLaunchLocalRunner.d.ts.map +1 -0
  177. package/runner/useLaunchLocalRunner.js +75 -0
  178. package/runner/useLaunchLocalRunner.js.map +1 -0
  179. package/runner/useRunnerList.d.ts +49 -0
  180. package/runner/useRunnerList.d.ts.map +1 -0
  181. package/runner/useRunnerList.js +70 -0
  182. package/runner/useRunnerList.js.map +1 -0
  183. package/runner/useStopRunner.d.ts +53 -0
  184. package/runner/useStopRunner.d.ts.map +1 -0
  185. package/runner/useStopRunner.js +50 -0
  186. package/runner/useStopRunner.js.map +1 -0
  187. package/session/draft.d.ts +53 -0
  188. package/session/draft.d.ts.map +1 -0
  189. package/session/draft.js +45 -0
  190. package/session/draft.js.map +1 -0
  191. package/session/index.d.ts +10 -0
  192. package/session/index.d.ts.map +1 -1
  193. package/session/index.js +5 -0
  194. package/session/index.js.map +1 -1
  195. package/session/useCreateSession.d.ts +8 -0
  196. package/session/useCreateSession.d.ts.map +1 -1
  197. package/session/useCreateSession.js +1 -0
  198. package/session/useCreateSession.js.map +1 -1
  199. package/session/useEditSessionPrep.d.ts +26 -0
  200. package/session/useEditSessionPrep.d.ts.map +1 -0
  201. package/session/useEditSessionPrep.js +83 -0
  202. package/session/useEditSessionPrep.js.map +1 -0
  203. package/session/useNewSessionFlow.d.ts +110 -0
  204. package/session/useNewSessionFlow.d.ts.map +1 -0
  205. package/session/useNewSessionFlow.js +184 -0
  206. package/session/useNewSessionFlow.js.map +1 -0
  207. package/session/usePersistedModel.d.ts +18 -0
  208. package/session/usePersistedModel.d.ts.map +1 -0
  209. package/session/usePersistedModel.js +31 -0
  210. package/session/usePersistedModel.js.map +1 -0
  211. package/session/useSessionPageFlow.d.ts +104 -0
  212. package/session/useSessionPageFlow.d.ts.map +1 -0
  213. package/session/useSessionPageFlow.js +172 -0
  214. package/session/useSessionPageFlow.js.map +1 -0
  215. package/skill/SkillDetailView.js +3 -3
  216. package/skill/SkillDetailView.js.map +1 -1
  217. package/skill/SkillPicker.js +3 -3
  218. package/skill/SkillPicker.js.map +1 -1
  219. package/src/agent/AgentDetailView.tsx +8 -8
  220. package/src/agent/AgentPicker.tsx +3 -3
  221. package/src/api-key/ApiKeyCreatedAlert.tsx +2 -2
  222. package/src/api-key/ApiKeyListPanel.tsx +6 -6
  223. package/src/api-key/CreateApiKeyForm.tsx +2 -2
  224. package/src/attachment/AttachmentChipList.tsx +3 -3
  225. package/src/color-mode.ts +75 -0
  226. package/src/composer/ComposerToolbar.tsx +29 -7
  227. package/src/composer/ConfigureMenu.tsx +6 -6
  228. package/src/composer/ContextChip.tsx +1 -1
  229. package/src/composer/ContextPopover.tsx +2 -2
  230. package/src/composer/SessionComposer.tsx +34 -5
  231. package/src/environment/CreateEnvironmentForm.tsx +3 -3
  232. package/src/environment/EnvVarForm.tsx +6 -6
  233. package/src/environment/EnvironmentListPanel.tsx +3 -3
  234. package/src/environment/EnvironmentVariableEditor.tsx +7 -7
  235. package/src/error/ErrorMessage.tsx +5 -5
  236. package/src/execution/ApprovalCard.tsx +5 -5
  237. package/src/execution/ArtifactCard.tsx +2 -2
  238. package/src/execution/ArtifactPreviewModal.tsx +4 -4
  239. package/src/execution/FollowUpInput.tsx +2 -2
  240. package/src/execution/McpToolDetail.tsx +4 -4
  241. package/src/execution/MessageEntry.tsx +1 -1
  242. package/src/execution/MessageThread.tsx +1 -1
  243. package/src/execution/SessionVariablesInput.tsx +7 -7
  244. package/src/execution/SubAgentSection.tsx +5 -5
  245. package/src/execution/ToolCallDetail.tsx +2 -2
  246. package/src/execution/ToolCallGroup.tsx +3 -3
  247. package/src/execution/ToolCallItem.tsx +4 -4
  248. package/src/execution/WriteBackCard.tsx +5 -5
  249. package/src/execution/tool-rendering-primitives.tsx +5 -5
  250. package/src/github/GitHubRepoPicker.tsx +5 -5
  251. package/src/iam-policy/GrantAccessForm.tsx +2 -2
  252. package/src/iam-policy/OrgMembersPanel.tsx +11 -11
  253. package/src/identity-provider/CreateIdentityProviderForm.tsx +4 -4
  254. package/src/identity-provider/IdentityProviderDetailPanel.tsx +7 -7
  255. package/src/identity-provider/IdentityProviderListPanel.tsx +7 -7
  256. package/src/identity-provider/IdentityProviderWizard.tsx +8 -8
  257. package/src/identity-provider/ProviderPicker.tsx +2 -2
  258. package/src/index.ts +46 -7
  259. package/src/internal/CloudFeatureNotice.tsx +1 -1
  260. package/src/internal/Tabs.tsx +1 -1
  261. package/src/internal/markdown-components.tsx +2 -2
  262. package/src/invitation/InvitationCreatedAlert.tsx +2 -2
  263. package/src/invitation/InvitationManager.tsx +9 -9
  264. package/src/invitation/InvitationRedemption.tsx +11 -11
  265. package/src/library/ResourceCountCard.tsx +1 -1
  266. package/src/library/ResourceListView.tsx +7 -7
  267. package/src/mcp-server/McpServerConfigPanel.tsx +7 -7
  268. package/src/mcp-server/McpServerConnectDialog.tsx +5 -5
  269. package/src/mcp-server/McpServerDetailView.tsx +19 -19
  270. package/src/mcp-server/McpServerPicker.tsx +6 -6
  271. package/src/mcp-server/McpToolSelector.tsx +4 -4
  272. package/src/mcp-server/OAuthAppForm.tsx +3 -3
  273. package/src/models/ModelSelector.tsx +1 -1
  274. package/src/oauth-app/CreateOAuthAppForm.tsx +3 -3
  275. package/src/oauth-app/OAuthAppDetailPanel.tsx +7 -7
  276. package/src/oauth-app/OAuthAppListPanel.tsx +3 -3
  277. package/src/organization/CreateOrganizationForm.tsx +3 -3
  278. package/src/organization/OrgProfilePanel.tsx +4 -5
  279. package/src/platform-client/CreatePlatformClientForm.tsx +6 -6
  280. package/src/platform-client/PlatformClientDetailPanel.tsx +19 -19
  281. package/src/platform-client/PlatformClientListPanel.tsx +7 -7
  282. package/src/platform-client/PlatformClientSecretAlert.tsx +2 -2
  283. package/src/provider.tsx +52 -2
  284. package/src/runner/RunnerListPanel.tsx +725 -0
  285. package/src/runner/RunnerPicker.tsx +319 -0
  286. package/src/runner/__tests__/useDeleteRunner.test.tsx +150 -0
  287. package/src/runner/__tests__/useLaunchLocalRunner.test.tsx +223 -0
  288. package/src/runner/__tests__/useStopRunner.test.tsx +154 -0
  289. package/src/runner/index.ts +34 -0
  290. package/src/runner/phase.ts +62 -0
  291. package/src/runner/useDeleteRunner.ts +67 -0
  292. package/src/runner/useLaunchLocalRunner.ts +139 -0
  293. package/src/runner/useRunnerList.ts +114 -0
  294. package/src/runner/useStopRunner.ts +92 -0
  295. package/src/session/draft.ts +82 -0
  296. package/src/session/index.ts +28 -0
  297. package/src/session/useCreateSession.ts +9 -0
  298. package/src/session/useEditSessionPrep.ts +111 -0
  299. package/src/session/useNewSessionFlow.ts +283 -0
  300. package/src/session/usePersistedModel.ts +41 -0
  301. package/src/session/useSessionPageFlow.ts +280 -0
  302. package/src/skill/SkillDetailView.tsx +5 -5
  303. package/src/skill/SkillPicker.tsx +3 -3
  304. package/src/styles.css +25 -1
  305. package/src/usage/OrgUsagePanel.tsx +4 -4
  306. package/src/workspace/WorkspaceEditor.tsx +78 -66
  307. package/src/workspace/index.ts +0 -8
  308. package/styles.css +1 -1
  309. package/usage/OrgUsagePanel.js +2 -2
  310. package/usage/OrgUsagePanel.js.map +1 -1
  311. package/workspace/WorkspaceEditor.d.ts +28 -3
  312. package/workspace/WorkspaceEditor.d.ts.map +1 -1
  313. package/workspace/WorkspaceEditor.js +24 -25
  314. package/workspace/WorkspaceEditor.js.map +1 -1
  315. package/workspace/index.d.ts +0 -4
  316. package/workspace/index.d.ts.map +1 -1
  317. package/workspace/index.js +0 -2
  318. package/workspace/index.js.map +1 -1
  319. package/src/workspace/FolderBrowser.tsx +0 -579
  320. package/src/workspace/useFolderListing.ts +0 -164
  321. package/workspace/FolderBrowser.d.ts +0 -37
  322. package/workspace/FolderBrowser.d.ts.map +0 -1
  323. package/workspace/FolderBrowser.js +0 -188
  324. package/workspace/FolderBrowser.js.map +0 -1
  325. package/workspace/useFolderListing.d.ts +0 -73
  326. package/workspace/useFolderListing.d.ts.map +0 -1
  327. package/workspace/useFolderListing.js +0 -110
  328. package/workspace/useFolderListing.js.map +0 -1
@@ -1,6 +1,4 @@
1
1
  export { useWorkspaceEntries } from "./useWorkspaceEntries";
2
2
  export { WorkspaceEditor } from "./WorkspaceEditor";
3
- export { useFolderListing } from "./useFolderListing";
4
- export { FolderBrowser } from "./FolderBrowser";
5
3
  export { WorkspaceSummary } from "./WorkspaceSummary";
6
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workspace/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAK5D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAMtD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workspace/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAK5D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -1,579 +0,0 @@
1
- "use client";
2
-
3
- import {
4
- useState,
5
- useCallback,
6
- useRef,
7
- useEffect,
8
- type KeyboardEvent,
9
- } from "react";
10
- import { useFolderListing } from "./useFolderListing";
11
-
12
- /** Props for {@link FolderBrowser}. */
13
- export interface FolderBrowserProps {
14
- /** Called when the user confirms the selected directory. */
15
- readonly onSelect: (path: string) => void;
16
- /** Called when the user dismisses the browser. */
17
- readonly onCancel: () => void;
18
- /** Starting directory path. Defaults to CWD from the API. */
19
- readonly initialPath?: string;
20
- /** Additional CSS class names for the root container. */
21
- readonly className?: string;
22
- }
23
-
24
- /**
25
- * Styled component that renders a navigable filesystem browser for
26
- * selecting a local directory as a workspace entry.
27
- *
28
- * Features:
29
- * - Breadcrumb path bar (clickable segments, editable as text input)
30
- * - Directory listing with folder-first sorting
31
- * - Quick navigation: Home and CWD buttons
32
- * - Hidden files toggle
33
- * - Keyboard navigation (Arrow keys, Enter, Escape)
34
- * - Graceful degradation when the /api/fs/list endpoint is unavailable
35
- *
36
- * All visual properties flow through `--stgm-*` tokens.
37
- * No Console-specific dependencies.
38
- *
39
- * @example
40
- * ```tsx
41
- * <FolderBrowser
42
- * onSelect={(path) => workspace.addLocalPath(path)}
43
- * onCancel={() => setShowBrowser(false)}
44
- * initialPath="/Users/dev/projects"
45
- * />
46
- * ```
47
- */
48
- export function FolderBrowser({
49
- onSelect,
50
- onCancel,
51
- initialPath,
52
- className,
53
- }: FolderBrowserProps) {
54
- const { listing, isLoading, error, isAvailable, browse, clearError } =
55
- useFolderListing(initialPath);
56
-
57
- const [showHidden, setShowHidden] = useState(false);
58
- const [isEditingPath, setIsEditingPath] = useState(false);
59
- const [editPath, setEditPath] = useState("");
60
- const [focusIndex, setFocusIndex] = useState(-1);
61
-
62
- const listRef = useRef<HTMLDivElement>(null);
63
- const pathInputRef = useRef<HTMLInputElement>(null);
64
- const fallbackInputRef = useRef<HTMLInputElement>(null);
65
- const [fallbackPath, setFallbackPath] = useState("");
66
-
67
- const currentPath = listing?.path ?? initialPath ?? "";
68
-
69
- const visibleEntries = listing
70
- ? listing.entries.filter((e) => showHidden || !e.hidden)
71
- : [];
72
- const directories = visibleEntries.filter((e) => e.isDir);
73
-
74
- const navigateTo = useCallback(
75
- (path: string) => {
76
- setFocusIndex(-1);
77
- browse(path);
78
- },
79
- [browse],
80
- );
81
-
82
- const handleBreadcrumbClick = useCallback(
83
- (segmentIndex: number) => {
84
- const segments = currentPath.split("/").filter(Boolean);
85
- const targetPath = "/" + segments.slice(0, segmentIndex + 1).join("/");
86
- navigateTo(targetPath);
87
- },
88
- [currentPath, navigateTo],
89
- );
90
-
91
- const startPathEdit = useCallback(() => {
92
- setEditPath(currentPath);
93
- setIsEditingPath(true);
94
- }, [currentPath]);
95
-
96
- const commitPathEdit = useCallback(() => {
97
- setIsEditingPath(false);
98
- const trimmed = editPath.trim();
99
- if (trimmed && trimmed !== currentPath) {
100
- navigateTo(trimmed);
101
- }
102
- }, [editPath, currentPath, navigateTo]);
103
-
104
- const handlePathKeyDown = useCallback(
105
- (e: KeyboardEvent<HTMLInputElement>) => {
106
- if (e.key === "Enter") {
107
- e.preventDefault();
108
- commitPathEdit();
109
- } else if (e.key === "Escape") {
110
- e.preventDefault();
111
- setIsEditingPath(false);
112
- }
113
- },
114
- [commitPathEdit],
115
- );
116
-
117
- const handleListKeyDown = useCallback(
118
- (e: KeyboardEvent<HTMLDivElement>) => {
119
- if (e.key === "Escape") {
120
- e.preventDefault();
121
- onCancel();
122
- return;
123
- }
124
-
125
- if (e.key === "ArrowDown") {
126
- e.preventDefault();
127
- setFocusIndex((prev) =>
128
- prev < directories.length - 1 ? prev + 1 : prev,
129
- );
130
- return;
131
- }
132
-
133
- if (e.key === "ArrowUp") {
134
- e.preventDefault();
135
- setFocusIndex((prev) => (prev > 0 ? prev - 1 : prev));
136
- return;
137
- }
138
-
139
- if (e.key === "Enter" && focusIndex >= 0 && focusIndex < directories.length) {
140
- e.preventDefault();
141
- const entry = directories[focusIndex];
142
- navigateTo(joinPath(currentPath, entry.name));
143
- return;
144
- }
145
-
146
- if (e.key === "Backspace" && currentPath !== "/") {
147
- e.preventDefault();
148
- navigateTo(parentPath(currentPath));
149
- }
150
- },
151
- [onCancel, directories, focusIndex, currentPath, navigateTo],
152
- );
153
-
154
- useEffect(() => {
155
- if (focusIndex >= 0) {
156
- const el = listRef.current?.querySelector(
157
- `[data-index="${focusIndex}"]`,
158
- );
159
- el?.scrollIntoView({ block: "nearest" });
160
- }
161
- }, [focusIndex]);
162
-
163
- useEffect(() => {
164
- if (isEditingPath) {
165
- pathInputRef.current?.focus();
166
- pathInputRef.current?.select();
167
- }
168
- }, [isEditingPath]);
169
-
170
- // Graceful fallback: if the endpoint is not available, show a text input.
171
- if (isAvailable === false) {
172
- return (
173
- <div className={["space-y-2", className].filter(Boolean).join(" ")}>
174
- <input
175
- ref={fallbackInputRef}
176
- type="text"
177
- placeholder="/path/to/project"
178
- value={fallbackPath}
179
- onChange={(e) => setFallbackPath(e.target.value)}
180
- onKeyDown={(e) => {
181
- if (e.key === "Enter" && fallbackPath.trim()) {
182
- e.preventDefault();
183
- onSelect(fallbackPath.trim());
184
- } else if (e.key === "Escape") {
185
- e.preventDefault();
186
- onCancel();
187
- }
188
- }}
189
- className="w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
190
- autoFocus
191
- />
192
- <div className="flex justify-end gap-2">
193
- <button
194
- type="button"
195
- onClick={onCancel}
196
- className="rounded-md px-2.5 py-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
197
- >
198
- Cancel
199
- </button>
200
- <button
201
- type="button"
202
- onClick={() => fallbackPath.trim() && onSelect(fallbackPath.trim())}
203
- disabled={!fallbackPath.trim()}
204
- className="rounded-md bg-primary px-2.5 py-1 text-xs text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-40"
205
- >
206
- Add
207
- </button>
208
- </div>
209
- </div>
210
- );
211
- }
212
-
213
- const pathSegments = currentPath.split("/").filter(Boolean);
214
-
215
- return (
216
- <div
217
- className={["space-y-2", className].filter(Boolean).join(" ")}
218
- onKeyDown={!isEditingPath ? handleListKeyDown : undefined}
219
- >
220
- {/* Breadcrumb / path editor */}
221
- <div className="flex items-center gap-1">
222
- {/* Quick nav: Home + CWD */}
223
- {listing && (
224
- <div className="flex shrink-0 items-center gap-0.5">
225
- <button
226
- type="button"
227
- onClick={() => navigateTo(listing.home)}
228
- className="rounded p-1 text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors"
229
- aria-label="Go to home directory"
230
- title="Home"
231
- >
232
- <HomeIcon />
233
- </button>
234
- <button
235
- type="button"
236
- onClick={() => navigateTo(listing.cwd)}
237
- className="rounded p-1 text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors"
238
- aria-label="Go to working directory"
239
- title="Working directory"
240
- >
241
- <TerminalIcon />
242
- </button>
243
- </div>
244
- )}
245
-
246
- {/* Breadcrumb path or text input */}
247
- <div className="min-w-0 flex-1">
248
- {isEditingPath ? (
249
- <input
250
- ref={pathInputRef}
251
- type="text"
252
- value={editPath}
253
- onChange={(e) => setEditPath(e.target.value)}
254
- onKeyDown={handlePathKeyDown}
255
- onBlur={commitPathEdit}
256
- className="w-full rounded-md border border-input bg-background px-2 py-1 text-xs text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
257
- />
258
- ) : (
259
- <button
260
- type="button"
261
- onClick={startPathEdit}
262
- className="flex w-full min-w-0 items-center gap-0.5 rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent/50 transition-colors"
263
- aria-label="Edit path"
264
- title="Click to type a path"
265
- >
266
- <span
267
- className="cursor-pointer hover:text-foreground"
268
- onClick={(e) => {
269
- e.stopPropagation();
270
- navigateTo("/");
271
- }}
272
- >
273
- /
274
- </span>
275
- {pathSegments.map((seg, i) => (
276
- <span key={i} className="flex items-center gap-0.5 min-w-0">
277
- <ChevronRightIcon />
278
- <span
279
- className={[
280
- "truncate cursor-pointer hover:text-foreground",
281
- i === pathSegments.length - 1
282
- ? "text-foreground font-medium"
283
- : "",
284
- ]
285
- .filter(Boolean)
286
- .join(" ")}
287
- onClick={(e) => {
288
- e.stopPropagation();
289
- handleBreadcrumbClick(i);
290
- }}
291
- >
292
- {seg}
293
- </span>
294
- </span>
295
- ))}
296
- </button>
297
- )}
298
- </div>
299
-
300
- {/* Hidden files toggle */}
301
- <button
302
- type="button"
303
- onClick={() => setShowHidden((prev) => !prev)}
304
- className={[
305
- "shrink-0 rounded p-1 transition-colors",
306
- showHidden
307
- ? "text-foreground bg-accent"
308
- : "text-muted-foreground hover:text-foreground hover:bg-accent/50",
309
- ].join(" ")}
310
- aria-label={showHidden ? "Hide hidden files" : "Show hidden files"}
311
- title={showHidden ? "Hide hidden files" : "Show hidden files"}
312
- >
313
- <EyeIcon />
314
- </button>
315
- </div>
316
-
317
- {/* Error state */}
318
- {error && (
319
- <div className="flex items-center justify-between rounded-md border border-destructive/30 bg-destructive/5 px-2.5 py-2 text-xs text-destructive">
320
- <span>{error}</span>
321
- <button
322
- type="button"
323
- onClick={clearError}
324
- className="shrink-0 rounded-md px-2 py-0.5 text-[0.65rem] hover:bg-destructive/10 transition-colors"
325
- >
326
- Dismiss
327
- </button>
328
- </div>
329
- )}
330
-
331
- {/* Directory list */}
332
- <div
333
- ref={listRef}
334
- className="max-h-52 overflow-y-auto"
335
- role="listbox"
336
- aria-label="Directory contents"
337
- tabIndex={0}
338
- >
339
- {/* Parent directory link */}
340
- {currentPath !== "/" && !isLoading && (
341
- <button
342
- type="button"
343
- onClick={() => navigateTo(parentPath(currentPath))}
344
- className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-xs text-muted-foreground hover:bg-accent/50 hover:text-foreground transition-colors"
345
- role="option"
346
- aria-selected={false}
347
- >
348
- <ParentDirIcon />
349
- <span>..</span>
350
- </button>
351
- )}
352
-
353
- {isLoading ? (
354
- <div className="space-y-1 py-1">
355
- {Array.from({ length: 5 }).map((_, i) => (
356
- <div
357
- key={i}
358
- className="flex items-center gap-2 rounded-md px-2 py-1.5"
359
- >
360
- <div className="h-3.5 w-3.5 rounded bg-muted animate-pulse" />
361
- <div
362
- className="h-3 rounded bg-muted animate-pulse"
363
- style={{ width: `${40 + Math.random() * 40}%` }}
364
- />
365
- </div>
366
- ))}
367
- </div>
368
- ) : (
369
- <>
370
- {directories.map((entry, i) => (
371
- <button
372
- key={entry.name}
373
- type="button"
374
- data-index={i}
375
- onClick={() => navigateTo(joinPath(currentPath, entry.name))}
376
- onDoubleClick={() =>
377
- onSelect(joinPath(currentPath, entry.name))
378
- }
379
- className={[
380
- "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-xs transition-colors",
381
- i === focusIndex
382
- ? "bg-accent text-foreground"
383
- : "text-foreground hover:bg-accent/50",
384
- ].join(" ")}
385
- role="option"
386
- aria-selected={i === focusIndex}
387
- >
388
- <FolderIcon />
389
- <span className="min-w-0 flex-1 truncate text-left">
390
- {entry.name}
391
- </span>
392
- </button>
393
- ))}
394
-
395
- {/* Non-directory entries (dimmed) */}
396
- {visibleEntries
397
- .filter((e) => !e.isDir)
398
- .map((entry) => (
399
- <div
400
- key={entry.name}
401
- className="flex items-center gap-2 rounded-md px-2 py-1.5 text-xs text-muted-foreground/50"
402
- >
403
- <FileIcon />
404
- <span className="min-w-0 flex-1 truncate">{entry.name}</span>
405
- </div>
406
- ))}
407
-
408
- {!isLoading &&
409
- directories.length === 0 &&
410
- visibleEntries.length === 0 && (
411
- <div className="py-4 text-center text-xs text-muted-foreground">
412
- Empty directory
413
- </div>
414
- )}
415
- </>
416
- )}
417
- </div>
418
-
419
- {/* Actions */}
420
- <div className="flex items-center justify-between border-t border-border/50 pt-2">
421
- <span className="truncate text-[0.6rem] text-muted-foreground">
422
- {currentPath}
423
- </span>
424
- <div className="flex shrink-0 items-center gap-2">
425
- <button
426
- type="button"
427
- onClick={onCancel}
428
- className="rounded-md px-2.5 py-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
429
- >
430
- Cancel
431
- </button>
432
- <button
433
- type="button"
434
- onClick={() => onSelect(currentPath)}
435
- disabled={!currentPath || isLoading}
436
- className="rounded-md bg-primary px-2.5 py-1 text-xs text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-40"
437
- >
438
- Select
439
- </button>
440
- </div>
441
- </div>
442
- </div>
443
- );
444
- }
445
-
446
- function joinPath(base: string, name: string): string {
447
- if (base === "/") return "/" + name;
448
- return base + "/" + name;
449
- }
450
-
451
- function parentPath(p: string): string {
452
- const segments = p.split("/").filter(Boolean);
453
- if (segments.length <= 1) return "/";
454
- return "/" + segments.slice(0, -1).join("/");
455
- }
456
-
457
- function HomeIcon() {
458
- return (
459
- <svg
460
- width="14"
461
- height="14"
462
- viewBox="0 0 14 14"
463
- fill="none"
464
- stroke="currentColor"
465
- strokeWidth="1.5"
466
- strokeLinecap="round"
467
- strokeLinejoin="round"
468
- >
469
- <path d="M2 7L7 2.5L12 7" />
470
- <path d="M3.5 8V11.5H5.5V9H8.5V11.5H10.5V8" />
471
- </svg>
472
- );
473
- }
474
-
475
- function TerminalIcon() {
476
- return (
477
- <svg
478
- width="14"
479
- height="14"
480
- viewBox="0 0 14 14"
481
- fill="none"
482
- stroke="currentColor"
483
- strokeWidth="1.5"
484
- strokeLinecap="round"
485
- strokeLinejoin="round"
486
- >
487
- <path d="M3.5 5L6 7L3.5 9" />
488
- <path d="M7.5 9.5H10.5" />
489
- </svg>
490
- );
491
- }
492
-
493
- function EyeIcon() {
494
- return (
495
- <svg
496
- width="14"
497
- height="14"
498
- viewBox="0 0 14 14"
499
- fill="none"
500
- stroke="currentColor"
501
- strokeWidth="1.5"
502
- strokeLinecap="round"
503
- strokeLinejoin="round"
504
- >
505
- <path d="M1.5 7C2.5 4.5 4.5 3 7 3C9.5 3 11.5 4.5 12.5 7C11.5 9.5 9.5 11 7 11C4.5 11 2.5 9.5 1.5 7Z" />
506
- <circle cx="7" cy="7" r="1.5" />
507
- </svg>
508
- );
509
- }
510
-
511
- function ChevronRightIcon() {
512
- return (
513
- <svg
514
- width="10"
515
- height="10"
516
- viewBox="0 0 10 10"
517
- fill="none"
518
- stroke="currentColor"
519
- strokeWidth="1.5"
520
- strokeLinecap="round"
521
- strokeLinejoin="round"
522
- >
523
- <path d="M3.5 2L6.5 5L3.5 8" />
524
- </svg>
525
- );
526
- }
527
-
528
- function FolderIcon() {
529
- return (
530
- <svg
531
- width="14"
532
- height="14"
533
- viewBox="0 0 14 14"
534
- fill="none"
535
- stroke="currentColor"
536
- strokeWidth="1.5"
537
- strokeLinecap="round"
538
- strokeLinejoin="round"
539
- >
540
- <path d="M1.5 3.5V11a1 1 0 001 1h9a1 1 0 001-1V5.5a1 1 0 00-1-1H7L5.5 3H2.5a1 1 0 00-1 .5z" />
541
- </svg>
542
- );
543
- }
544
-
545
- function FileIcon() {
546
- return (
547
- <svg
548
- width="14"
549
- height="14"
550
- viewBox="0 0 14 14"
551
- fill="none"
552
- stroke="currentColor"
553
- strokeWidth="1.5"
554
- strokeLinecap="round"
555
- strokeLinejoin="round"
556
- >
557
- <path d="M3.5 1.5H8.5L11 4V12a.5.5 0 01-.5.5H3.5a.5.5 0 01-.5-.5V2a.5.5 0 01.5-.5z" />
558
- <path d="M8.5 1.5V4H11" />
559
- </svg>
560
- );
561
- }
562
-
563
- function ParentDirIcon() {
564
- return (
565
- <svg
566
- width="14"
567
- height="14"
568
- viewBox="0 0 14 14"
569
- fill="none"
570
- stroke="currentColor"
571
- strokeWidth="1.5"
572
- strokeLinecap="round"
573
- strokeLinejoin="round"
574
- >
575
- <path d="M7 10V4" />
576
- <path d="M4 6.5L7 3.5L10 6.5" />
577
- </svg>
578
- );
579
- }