@mcp-b/react-components 0.14.2 → 0.16.1

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 (251) hide show
  1. package/dist/AgentActivity-BNUNrP9O.d.ts +36 -0
  2. package/dist/{AgentChat-DQkqt82e.d.ts → AgentChat-CrlpbvQJ.d.ts} +58 -6
  3. package/dist/{CodeBlock-DUh9WOGo.d.ts → CodeBlock-BRajW4QU.d.ts} +1 -1
  4. package/dist/{CodeBlock-BUFXG8HW.js → CodeBlock-BmB4Xaqq.js} +15 -4
  5. package/dist/{FileTree-mpEIcfzA.js → FileTree-CkVbIMWV.js} +82 -89
  6. package/dist/Message-BkMdE3bS.js +375 -0
  7. package/dist/{ModelSelector-CPCpFAUt.js → ModelSelector-SlJNgwKY.js} +2 -2
  8. package/dist/{PromptInput-ClNqBiIw.d.ts → PromptInput-BWTuXMOD.d.ts} +123 -5
  9. package/dist/ResponsiveSidebarShell-Dd9DCcBT.d.ts +67 -0
  10. package/dist/{Select-Dl_vF_Bt.d.ts → Select-BeEsefzh.d.ts} +12 -2
  11. package/dist/{ThinkChat-BPcehdbP.js → ThinkChat-CzC-zNGA.js} +24 -36
  12. package/dist/{ThinkChatActivity-BmwuIigx.d.ts → ThinkChatActivity-BFQnb2ct.d.ts} +3 -10
  13. package/dist/ThinkChatActivitySummary-BPmu9a-c.d.ts +39 -0
  14. package/dist/{ThinkChatToolParts-CDEjqS6A.d.ts → ThinkChatToolParts-CSzVwMXf.d.ts} +13 -3
  15. package/dist/ThinkExecutionRun-ChhWhOoU.d.ts +30 -0
  16. package/dist/Toggle-CAAlNl-M.d.ts +69 -0
  17. package/dist/ToggleGroup-8hPv-b69.d.ts +73 -0
  18. package/dist/Workspace-D39LxGvP.d.ts +62 -0
  19. package/dist/WorkspaceFile-DOZkPwzu.d.ts +106 -0
  20. package/dist/WorkspaceFileView-rSN_yT0L.js +744 -0
  21. package/dist/WorkspaceLink-DIPbXrUM.js +337 -0
  22. package/dist/WorkspaceLink-DK1DWMHe.d.ts +89 -0
  23. package/dist/components/Activity.d.ts +27 -1
  24. package/dist/components/Activity.js +33 -15
  25. package/dist/components/AgentActivity.d.ts +2 -0
  26. package/dist/components/AgentActivity.js +45 -0
  27. package/dist/components/AgentActivityPreview.d.ts +46 -0
  28. package/dist/components/AgentActivityPreview.js +94 -0
  29. package/dist/components/AgentAttention.d.ts +73 -0
  30. package/dist/components/AgentAttention.js +85 -0
  31. package/dist/components/AgentChat.d.ts +2 -2
  32. package/dist/components/AgentChat.js +224 -200
  33. package/dist/components/AgentChatMessageEditor.d.ts +29 -0
  34. package/dist/components/AgentChatMessageEditor.js +92 -0
  35. package/dist/components/AgentChatTurnError.d.ts +17 -0
  36. package/dist/components/AgentChatTurnError.js +40 -0
  37. package/dist/components/AgentConnection.d.ts +12 -17
  38. package/dist/components/AgentConnection.js +99 -91
  39. package/dist/components/AppFrame.d.ts +24 -0
  40. package/dist/components/AppFrame.js +25 -0
  41. package/dist/components/AppTopBar.d.ts +71 -0
  42. package/dist/components/AppTopBar.js +82 -0
  43. package/dist/components/Artifact.js +1 -1
  44. package/dist/components/BrowserLiveView.js +1 -1
  45. package/dist/components/BrowserReplay.js +1 -1
  46. package/dist/components/BrowserRunPanel.js +1 -1
  47. package/dist/components/Card.d.ts +3 -0
  48. package/dist/components/Card.js +2 -1
  49. package/dist/components/ChainOfThought.d.ts +1 -1
  50. package/dist/components/ChainOfThought.js +1 -1
  51. package/dist/components/ChatGptSignInScreen.js +2 -2
  52. package/dist/components/Checkbox.js +5 -13
  53. package/dist/components/CheckboxGroup.d.ts +58 -2
  54. package/dist/components/CheckboxGroup.js +59 -1
  55. package/dist/components/CodeBlock.d.ts +1 -1
  56. package/dist/components/CodeBlock.js +1 -1
  57. package/dist/components/CodexModelSelector.js +1 -1
  58. package/dist/components/Commit.js +2 -2
  59. package/dist/components/ContextMenu.js +1 -1
  60. package/dist/components/Conversation.js +1 -1
  61. package/dist/components/DescriptionList.d.ts +12 -0
  62. package/dist/components/DescriptionList.js +11 -0
  63. package/dist/components/DescriptionListParts.d.ts +37 -0
  64. package/dist/components/DescriptionListParts.js +35 -0
  65. package/dist/components/EnvironmentVariables.js +2 -2
  66. package/dist/components/Fieldset.d.ts +13 -1
  67. package/dist/components/Fieldset.js +11 -2
  68. package/dist/components/FileTree.js +1 -1
  69. package/dist/components/IconButton.d.ts +33 -0
  70. package/dist/components/IconButton.js +27 -0
  71. package/dist/components/McpConnectionRequest.d.ts +44 -0
  72. package/dist/components/McpConnectionRequest.js +122 -0
  73. package/dist/components/McpServerPicker.d.ts +48 -23
  74. package/dist/components/McpServerPicker.js +238 -189
  75. package/dist/components/Menubar.js +1 -1
  76. package/dist/components/Message.d.ts +1 -0
  77. package/dist/components/Message.js +1 -285
  78. package/dist/components/MessageScroller.js +1 -1
  79. package/dist/components/ModelSelector.js +1 -1
  80. package/dist/components/Pricing.d.ts +37 -0
  81. package/dist/components/Pricing.js +17 -0
  82. package/dist/components/PricingBillingToggle.d.ts +23 -0
  83. package/dist/components/PricingBillingToggle.js +86 -0
  84. package/dist/components/PricingConfirmationDialog.d.ts +25 -0
  85. package/dist/components/PricingConfirmationDialog.js +57 -0
  86. package/dist/components/PricingFAQ.d.ts +18 -0
  87. package/dist/components/PricingFAQ.js +27 -0
  88. package/dist/components/PricingPage.d.ts +2 -38
  89. package/dist/components/PricingPage.js +39 -359
  90. package/dist/components/PricingPlanCard.d.ts +22 -0
  91. package/dist/components/PricingPlanCard.js +109 -0
  92. package/dist/components/PricingUsageCalculator.d.ts +22 -0
  93. package/dist/components/PricingUsageCalculator.js +96 -0
  94. package/dist/components/PromptInput.d.ts +2 -2
  95. package/dist/components/PromptInput.js +210 -15
  96. package/dist/components/Queue.js +1 -1
  97. package/dist/components/Reasoning.d.ts +2 -1
  98. package/dist/components/Reasoning.js +14 -29
  99. package/dist/components/ResizableSidePanel.d.ts +57 -0
  100. package/dist/components/ResizableSidePanel.js +151 -0
  101. package/dist/components/ResponsiveSidebarShell.d.ts +2 -0
  102. package/dist/components/ResponsiveSidebarShell.js +129 -0
  103. package/dist/components/Select.d.ts +2 -2
  104. package/dist/components/Select.js +11 -2
  105. package/dist/components/Slider.d.ts +13 -1
  106. package/dist/components/Slider.js +11 -0
  107. package/dist/components/Sources.js +1 -1
  108. package/dist/components/Stepper.d.ts +4 -0
  109. package/dist/components/Stepper.js +5 -3
  110. package/dist/components/SubagentRun.d.ts +18 -19
  111. package/dist/components/SubagentRun.js +25 -18
  112. package/dist/components/SubagentRunIndicator.d.ts +22 -0
  113. package/dist/components/SubagentRunIndicator.js +33 -0
  114. package/dist/components/SubagentRunList.d.ts +25 -0
  115. package/dist/components/SubagentRunList.js +26 -0
  116. package/dist/components/SubagentRunSelector.d.ts +47 -0
  117. package/dist/components/SubagentRunSelector.js +55 -0
  118. package/dist/components/Task.js +1 -1
  119. package/dist/components/Terminal.js +2 -2
  120. package/dist/components/TestResults.js +1 -1
  121. package/dist/components/Textarea.d.ts +21 -0
  122. package/dist/components/Textarea.js +19 -0
  123. package/dist/components/ThemeModeControl.d.ts +24 -0
  124. package/dist/components/ThemeModeControl.js +52 -0
  125. package/dist/components/ThemeProvider.d.ts +38 -0
  126. package/dist/components/ThemeProvider.js +80 -0
  127. package/dist/components/ThinkChat.d.ts +29 -6
  128. package/dist/components/ThinkChat.js +7 -4
  129. package/dist/components/ThinkChatActivity.d.ts +3 -2
  130. package/dist/components/ThinkChatActivity.js +12 -318
  131. package/dist/components/ThinkChatActivitySummary.d.ts +2 -0
  132. package/dist/components/ThinkChatActivitySummary.js +421 -0
  133. package/dist/components/ThinkChatToolPanels.d.ts +7 -5
  134. package/dist/components/ThinkChatToolPanels.js +70 -8
  135. package/dist/components/ThinkChatToolParts.d.ts +2 -2
  136. package/dist/components/ThinkChatToolParts.js +36 -10
  137. package/dist/components/ThinkExecutionDetail.d.ts +20 -0
  138. package/dist/components/ThinkExecutionDetail.js +80 -0
  139. package/dist/components/ThinkExecutionRun.d.ts +2 -0
  140. package/dist/components/ThinkExecutionRun.js +72 -0
  141. package/dist/components/ThinkExecutionStatus.d.ts +22 -0
  142. package/dist/components/ThinkExecutionStatus.js +36 -0
  143. package/dist/components/ThinkExecutionWorkbench.d.ts +7 -0
  144. package/dist/components/ThinkExecutionWorkbench.js +69 -225
  145. package/dist/components/ThinkFullApp.d.ts +58 -14
  146. package/dist/components/ThinkFullApp.js +154 -160
  147. package/dist/components/ThinkFullAppViewSwitch.d.ts +31 -0
  148. package/dist/components/ThinkFullAppViewSwitch.js +34 -0
  149. package/dist/components/ThinkRoutine.d.ts +36 -0
  150. package/dist/components/ThinkRoutine.js +15 -0
  151. package/dist/components/ThinkRoutineBrowser.d.ts +25 -0
  152. package/dist/components/ThinkRoutineBrowser.js +156 -0
  153. package/dist/components/ThinkRoutineDetail.d.ts +24 -0
  154. package/dist/components/ThinkRoutineDetail.js +102 -0
  155. package/dist/components/ThinkRoutineForm.d.ts +32 -0
  156. package/dist/components/ThinkRoutineForm.js +151 -0
  157. package/dist/components/ThinkRoutineLibrary.d.ts +74 -0
  158. package/dist/components/ThinkRoutineLibrary.js +178 -0
  159. package/dist/components/ThinkRoutineRuns.d.ts +24 -0
  160. package/dist/components/ThinkRoutineRuns.js +68 -0
  161. package/dist/components/ThinkThreadPicker.d.ts +18 -1
  162. package/dist/components/ThinkThreadPicker.js +51 -5
  163. package/dist/components/ThinkThreadShell.d.ts +12 -9
  164. package/dist/components/ThinkThreadShell.js +17 -137
  165. package/dist/components/Toggle.d.ts +1 -68
  166. package/dist/components/ToggleGroup.d.ts +1 -72
  167. package/dist/components/Tooltip.d.ts +3 -1
  168. package/dist/components/Tooltip.js +4 -2
  169. package/dist/components/WebPreview.js +1 -1
  170. package/dist/components/Workspace.d.ts +2 -0
  171. package/dist/components/Workspace.js +424 -0
  172. package/dist/components/WorkspaceBrowser.d.ts +32 -0
  173. package/dist/components/WorkspaceBrowser.js +103 -0
  174. package/dist/components/WorkspaceExplorer.d.ts +12 -79
  175. package/dist/components/WorkspaceExplorer.js +116 -330
  176. package/dist/components/WorkspaceFile.d.ts +2 -0
  177. package/dist/components/WorkspaceFile.js +195 -0
  178. package/dist/components/WorkspaceFileView.d.ts +26 -0
  179. package/dist/components/WorkspaceFileView.js +2 -0
  180. package/dist/components/WorkspaceHeader.d.ts +35 -0
  181. package/dist/components/WorkspaceHeader.js +51 -0
  182. package/dist/components/WorkspaceLink.d.ts +2 -0
  183. package/dist/components/WorkspaceLink.js +2 -0
  184. package/dist/components/icons.d.ts +2 -2
  185. package/dist/components/icons.js +2 -2
  186. package/dist/{copy-button-DUMLYdsk.js → copy-button-Dejn5heL.js} +1 -1
  187. package/dist/icons-CpfUer49.js +85 -0
  188. package/dist/{icons-CUCz_Sft.d.ts → icons-GQd1xF4e.d.ts} +51 -1
  189. package/dist/styles/activity-header.css +75 -0
  190. package/dist/styles/activity.css +49 -63
  191. package/dist/styles/agent-activity-preview-overlay.css +80 -0
  192. package/dist/styles/agent-activity-preview.css +233 -0
  193. package/dist/styles/agent-attention.css +128 -0
  194. package/dist/styles/agent-chat.css +133 -4
  195. package/dist/styles/agent-connection.css +9 -9
  196. package/dist/styles/alert-dialog.css +11 -11
  197. package/dist/styles/app-frame.css +15 -0
  198. package/dist/styles/app-top-bar.css +102 -0
  199. package/dist/styles/avatar.css +0 -1
  200. package/dist/styles/browser-control-overlay.css +184 -0
  201. package/dist/styles/card.css +29 -8
  202. package/dist/styles/chain-of-thought.css +5 -5
  203. package/dist/styles/checkbox-group.css +69 -0
  204. package/dist/styles/codex-model-selector.css +3 -3
  205. package/dist/styles/combobox.css +2 -1
  206. package/dist/styles/context-menu.css +1 -1
  207. package/dist/styles/description-list.css +50 -0
  208. package/dist/styles/dialog.css +11 -11
  209. package/dist/styles/field.css +14 -14
  210. package/dist/styles/fieldset.css +15 -4
  211. package/dist/styles/index.css +14 -2
  212. package/dist/styles/mcp-connection-request.css +60 -0
  213. package/dist/styles/mcp-server-picker.css +96 -68
  214. package/dist/styles/menu.css +1 -1
  215. package/dist/styles/message.css +4 -43
  216. package/dist/styles/model-selector.css +18 -12
  217. package/dist/styles/popover.css +9 -9
  218. package/dist/styles/preview-card.css +1 -1
  219. package/dist/styles/pricing-page.css +21 -27
  220. package/dist/styles/prompt-input.css +225 -32
  221. package/dist/styles/reasoning.css +1 -55
  222. package/dist/styles/recipes.css +50 -24
  223. package/dist/styles/resizable-side-panel.css +66 -0
  224. package/dist/styles/responsive-sidebar-shell.css +169 -0
  225. package/dist/styles/select.css +13 -3
  226. package/dist/styles/sources.css +6 -6
  227. package/dist/styles/stepper.css +13 -0
  228. package/dist/styles/streamdown.css +0 -4
  229. package/dist/styles/subagent-run.css +59 -42
  230. package/dist/styles/task.css +17 -10
  231. package/dist/styles/test-results.css +17 -10
  232. package/dist/styles/textarea.css +20 -0
  233. package/dist/styles/theme-mode-control.css +72 -0
  234. package/dist/styles/think-execution-status.css +49 -0
  235. package/dist/styles/think-execution-workbench.css +16 -80
  236. package/dist/styles/think-full-app.css +143 -128
  237. package/dist/styles/think-routine-library.css +491 -0
  238. package/dist/styles/think-thread-picker.css +47 -3
  239. package/dist/styles/toast.css +6 -6
  240. package/dist/styles/toggle-group.css +7 -0
  241. package/dist/styles/tooltip.css +1 -1
  242. package/dist/styles/workspace-explorer.css +516 -64
  243. package/dist/utils/mcp-server-catalog.d.ts +82 -78
  244. package/dist/utils/mcp-server-catalog.js +64 -41
  245. package/dist/utils/structured-code.d.ts +1 -1
  246. package/dist/utils/theme.d.ts +22 -0
  247. package/dist/utils/theme.js +55 -0
  248. package/package.json +34 -28
  249. package/dist/icons-Ba9THQKz.js +0 -64
  250. package/dist/styles/think-resize-handle.css +0 -33
  251. package/dist/styles/think-thread-shell.css +0 -219
@@ -1,100 +1,33 @@
1
- import { A as CodeBlockLanguage } from "../CodeBlock-DUh9WOGo.js";
1
+ import { a as WorkspaceFileLinkProps, c as WorkspaceLinkResolution, d as WorkspaceNavigationProvider, f as WorkspaceNavigationProviderProps, i as WorkspaceFileLink, l as WorkspaceNavigationAdapter, n as WorkspaceDestination, o as WorkspaceInvalidDestination, p as resolveWorkspaceLink, r as WorkspaceExternalDestination, s as WorkspaceLinkBase, t as ResolveWorkspaceLinkOptions, u as WorkspaceNavigationEntry } from "../WorkspaceLink-DK1DWMHe.js";
2
+ import { a as WorkspaceMediaInfo, c as WorkspaceMediaTrackKind, d as inferWorkspaceLanguage, f as inferWorkspaceMedia, g as parseWorkspaceSkill, h as isWorkspaceSkillPath, i as WorkspaceFileState, l as WorkspaceSkillDocument, m as isWorkspaceMediaPath, n as WorkspaceFileAssetRequest, o as WorkspaceMediaKind, p as isWorkspaceMarkdownPath, r as WorkspaceFileAssetResolver, s as WorkspaceMediaTrack, t as WorkspaceFileAsset } from "../WorkspaceFile-DOZkPwzu.js";
3
+ import { a as useWorkspace, i as WorkspaceState, n as WorkspaceInfo, r as WorkspaceSource, t as UseWorkspaceOptions } from "../Workspace-D39LxGvP.js";
2
4
  import * as React from "react";
3
- import { FileInfo, Workspace } from "@cloudflare/shell";
4
5
 
5
6
  //#region src/components/WorkspaceExplorer.d.ts
6
- /** Workspace summary, as returned by `Workspace.getWorkspaceInfo()` upstream. */
7
- type WorkspaceInfo = Awaited<ReturnType<Workspace["getWorkspaceInfo"]>>;
8
- /**
9
- * What the explorer actually calls: `readDir` and `readFile`, plus
10
- * `getWorkspaceInfo` for the header's file-count chip when available. A full
11
- * `Workspace` from `@cloudflare/shell`, Think's `WorkspaceLike`, or any
12
- * cross-DO RPC proxy exposing these methods satisfies it.
13
- */
14
- type WorkspaceSource = Pick<Workspace, "readDir" | "readFile"> & Partial<Pick<Workspace, "getWorkspaceInfo">>;
15
- interface UseWorkspaceOptions {
16
- /** Must be referentially stable (memoize in the app) — a new source resets the workspace. */
17
- source: WorkspaceSource;
18
- /** Root directory to list first. Defaults to "/". */
19
- root?: string;
20
- }
21
- interface WorkspaceFileState {
22
- path: string;
23
- name: string;
24
- /** `null` means the file is missing or binary, matching upstream `readFile`. */
25
- content: string | null;
26
- error: string | null;
27
- }
28
- interface WorkspaceState {
29
- root: string;
30
- /** Loaded directory listings, keyed by directory path. */
31
- entriesByDirectory: Readonly<Record<string, readonly FileInfo[]>>;
32
- /** Every loaded entry, keyed by its path. */
33
- entryByPath: ReadonlyMap<string, FileInfo>;
34
- expandedPaths: ReadonlySet<string>;
35
- /** Update expansion; newly expanded directories load lazily. */
36
- setExpandedPaths: (next: Set<string>) => void;
37
- selectedPath: string | null;
38
- /** Select a file and load its content (cached after the first read). */
39
- selectFile: (path: string | null) => void;
40
- /** The selected file once its read settles; null while loading. */
41
- selectedFile: WorkspaceFileState | null;
42
- /** Root listing in flight. */
43
- isLoading: boolean;
44
- /** Directory and file paths with a request in flight. */
45
- loadingPaths: ReadonlySet<string>;
46
- /** Root/directory listing failure. Per-file read errors live on the file state. */
47
- error: string | null;
48
- /** Workspace summary, when the source provides `getWorkspaceInfo`. */
49
- info: WorkspaceInfo | null;
50
- /** Reload the root, every expanded directory, and the selected file. */
51
- refresh: () => void;
52
- }
53
- /**
54
- * Headless state for browsing a Think workspace: lazy directory loading,
55
- * cached file reads, expansion/selection, and refresh. Feed the result to
56
- * `WorkspaceExplorer`, or render your own UI from it.
57
- */
58
- declare function useWorkspace({
59
- source,
60
- root
61
- }: UseWorkspaceOptions): WorkspaceState;
62
- /** Best-effort syntax-highlighting language from a file path; unknown extensions render as plain text. */
63
- declare function inferWorkspaceLanguage(path: string): CodeBlockLanguage;
64
7
  interface WorkspaceExplorerProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
65
8
  workspace: WorkspaceState;
9
+ /** Host-owned URL/history adapter shared by chat links and the file explorer. */
10
+ navigation: WorkspaceNavigationAdapter;
66
11
  /** Header label. Defaults to "Workspace". */
67
12
  title?: React.ReactNode;
13
+ /** Leading workspace control, such as a host-owned Back to chat button. */
14
+ leading?: React.ReactNode;
68
15
  /** Extra header actions, rendered next to the built-in refresh button. */
69
16
  actions?: React.ReactNode;
70
- /** Hide the file filter input. */
17
+ /** Show the file filter input. Hidden by default to keep the browser compact. */
71
18
  showFilter?: boolean;
72
19
  filterPlaceholder?: string;
73
20
  emptyState?: React.ReactNode;
74
21
  }
75
22
  /**
76
- * A Think workspace file browser with a preview pane: lazy directory tree,
77
- * filter, refresh, and syntax-highlighted file view with copy. Drive it with
78
- * `useWorkspace(source)`.
79
- *
80
- * @example
81
- * ```tsx
82
- * const workspace = useWorkspace({ source: agent.workspace, root: "/workspace" });
83
- * <WorkspaceExplorer workspace={workspace} />
84
- * ```
23
+ * Convenience workspace application composed from `WorkspaceBrowser` and
24
+ * `WorkspaceFileView`. The host adapter owns URLs and history; the explorer
25
+ * only restores presentation state for stable navigation entry keys.
85
26
  */
86
27
  declare function WorkspaceExplorer({
87
- workspace,
88
- className,
89
- title,
90
- actions,
91
- showFilter,
92
- filterPlaceholder,
93
- emptyState,
94
- ref,
95
28
  ...props
96
29
  }: WorkspaceExplorerProps & {
97
30
  ref?: React.Ref<HTMLDivElement>;
98
31
  }): React.JSX.Element;
99
32
  //#endregion
100
- export { type FileInfo, UseWorkspaceOptions, WorkspaceExplorer, WorkspaceExplorerProps, WorkspaceFileState, WorkspaceInfo, WorkspaceSource, WorkspaceState, inferWorkspaceLanguage, useWorkspace };
33
+ export { type ResolveWorkspaceLinkOptions, type UseWorkspaceOptions, type WorkspaceDestination, WorkspaceExplorer, WorkspaceExplorerProps, type WorkspaceExternalDestination, type WorkspaceFileAsset, type WorkspaceFileAssetRequest, type WorkspaceFileAssetResolver, WorkspaceFileLink, type WorkspaceFileLinkProps, type WorkspaceFileState, type WorkspaceInfo, type WorkspaceInvalidDestination, type WorkspaceLinkBase, type WorkspaceLinkResolution, type WorkspaceMediaInfo, type WorkspaceMediaKind, type WorkspaceMediaTrack, type WorkspaceMediaTrackKind, type WorkspaceNavigationAdapter, type WorkspaceNavigationEntry, WorkspaceNavigationProvider, type WorkspaceNavigationProviderProps, type WorkspaceSkillDocument, type WorkspaceSource, type WorkspaceState, inferWorkspaceLanguage, inferWorkspaceMedia, isWorkspaceMarkdownPath, isWorkspaceMediaPath, isWorkspaceSkillPath, parseWorkspaceSkill, resolveWorkspaceLink, useWorkspace };
@@ -1,355 +1,141 @@
1
1
  import { t as cx } from "../class-names-9UtZwb0J.js";
2
- import { A as RefreshIcon, h as FileIcon, j as SearchIcon, n as ArrowLeftIcon } from "../icons-Ba9THQKz.js";
3
- import { Button } from "./Button.js";
4
- import { a as CodeBlockCopyButton, i as CodeBlockContent, n as CodeBlockActions, o as CodeBlockFilename, p as CodeBlockTitle, r as CodeBlockContainer, s as CodeBlockHeader, t as CodeBlock } from "../CodeBlock-BUFXG8HW.js";
5
- import { Input } from "./Input.js";
6
- import { i as FileTreeFolder, r as FileTreeFile, t as FileTree } from "../FileTree-mpEIcfzA.js";
2
+ import { a as WorkspaceNavigationRestorationBoundary, n as WorkspaceNavigationProvider, o as useRequiredWorkspaceNavigationRestoration, r as resolveWorkspaceLink, t as WorkspaceFileLink } from "../WorkspaceLink-DIPbXrUM.js";
3
+ import { n as mergeRefs } from "../merge-refs-DF_7w2x_.js";
4
+ import { WorkspaceBrowser } from "./WorkspaceBrowser.js";
5
+ import { inferWorkspaceLanguage, inferWorkspaceMedia, isWorkspaceMarkdownPath, isWorkspaceMediaPath, isWorkspaceSkillPath, parseWorkspaceSkill } from "./WorkspaceFile.js";
6
+ import { t as WorkspaceFileView } from "../WorkspaceFileView-rSN_yT0L.js";
7
+ import { useWorkspace } from "./Workspace.js";
7
8
  import * as React from "react";
8
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
+ import { jsx } from "react/jsx-runtime";
9
10
  //#region src/components/WorkspaceExplorer.tsx
10
- function basename(path) {
11
- return path.split("/").filter(Boolean).at(-1) ?? path;
11
+ function encodeWorkspaceDestination(destination) {
12
+ const path = destination.path.split("/").map((segment) => encodeURIComponent(segment)).join("/");
13
+ return destination.fragment === null ? path : `${path}#${encodeURIComponent(destination.fragment)}`;
12
14
  }
13
- function sortEntries(entries) {
14
- return [...entries].sort((left, right) => {
15
- if (left.type !== right.type) return left.type === "directory" ? -1 : 1;
16
- return left.name.localeCompare(right.name);
15
+ function getValidatedFileDestination(destination, root) {
16
+ if (destination.kind !== "file") return null;
17
+ const resolved = resolveWorkspaceLink({
18
+ href: encodeWorkspaceDestination(destination),
19
+ root,
20
+ base: {
21
+ kind: "directory",
22
+ path: root
23
+ }
17
24
  });
25
+ if (resolved.kind !== "file" || resolved.path !== destination.path || resolved.fragment !== destination.fragment) return null;
26
+ return resolved;
18
27
  }
19
- function toErrorMessage(error) {
20
- return error instanceof Error ? error.message : String(error);
28
+ function getDirectoryAncestors(path, root) {
29
+ const ancestors = [];
30
+ let parent = path.slice(0, path.lastIndexOf("/")) || "/";
31
+ while (parent !== root && (root === "/" || parent.startsWith(`${root}/`))) {
32
+ ancestors.unshift(parent);
33
+ parent = parent.slice(0, parent.lastIndexOf("/")) || "/";
34
+ }
35
+ return ancestors;
21
36
  }
22
37
  /**
23
- * Headless state for browsing a Think workspace: lazy directory loading,
24
- * cached file reads, expansion/selection, and refresh. Feed the result to
25
- * `WorkspaceExplorer`, or render your own UI from it.
38
+ * Convenience workspace application composed from `WorkspaceBrowser` and
39
+ * `WorkspaceFileView`. The host adapter owns URLs and history; the explorer
40
+ * only restores presentation state for stable navigation entry keys.
26
41
  */
27
- function useWorkspace({ source, root = "/" }) {
28
- const [entriesByDirectory, setEntriesByDirectory] = React.useState({});
29
- const [filesByPath, setFilesByPath] = React.useState({});
30
- const [expandedPaths, setExpandedPathsState] = React.useState(/* @__PURE__ */ new Set());
31
- const [selectedPath, setSelectedPath] = React.useState(null);
32
- const [isLoading, setIsLoading] = React.useState(false);
33
- const [loadingPaths, setLoadingPaths] = React.useState(/* @__PURE__ */ new Set());
34
- const [error, setError] = React.useState(null);
35
- const [info, setInfo] = React.useState(null);
36
- const loadedDirectoriesRef = React.useRef(/* @__PURE__ */ new Set());
37
- const markLoading = React.useCallback((path, loading) => {
38
- setLoadingPaths((current) => {
39
- const next = new Set(current);
40
- if (loading) next.add(path);
41
- else next.delete(path);
42
- return next;
43
- });
44
- }, []);
45
- const loadDirectory = React.useCallback(async (path, { force = false } = {}) => {
46
- if (!force && loadedDirectoriesRef.current.has(path)) return;
47
- markLoading(path, true);
48
- try {
49
- const entries = sortEntries(await source.readDir(path));
50
- loadedDirectoriesRef.current.add(path);
51
- setEntriesByDirectory((current) => ({
52
- ...current,
53
- [path]: entries
54
- }));
55
- setError(null);
56
- } catch (listError) {
57
- setError(toErrorMessage(listError));
58
- } finally {
59
- markLoading(path, false);
60
- }
61
- }, [source, markLoading]);
62
- const loadFile = React.useCallback(async (path, { force = false } = {}) => {
63
- if (!force && Object.hasOwn(filesByPath, path)) return;
64
- markLoading(path, true);
65
- try {
66
- const content = await source.readFile(path);
67
- setFilesByPath((current) => ({
68
- ...current,
69
- [path]: {
70
- path,
71
- name: basename(path),
72
- content,
73
- error: null
74
- }
75
- }));
76
- } catch (readError) {
77
- setFilesByPath((current) => ({
78
- ...current,
79
- [path]: {
80
- path,
81
- name: basename(path),
82
- content: null,
83
- error: toErrorMessage(readError)
84
- }
85
- }));
86
- } finally {
87
- markLoading(path, false);
88
- }
42
+ function WorkspaceExplorer({ ...props }) {
43
+ return /* @__PURE__ */ jsx(WorkspaceNavigationRestorationBoundary, { children: /* @__PURE__ */ jsx(WorkspaceExplorerContent, { ...props }) });
44
+ }
45
+ function WorkspaceExplorerContent({ workspace, navigation, className, title = "Workspace", leading, actions, showFilter = false, filterPlaceholder = "Filter files...", emptyState, ref, ...props }) {
46
+ const explorerRef = React.useRef(null);
47
+ const pendingTreeFocusRef = React.useRef(null);
48
+ const previousDestinationRef = React.useRef(navigation.current.destination);
49
+ const restoration = useRequiredWorkspaceNavigationRestoration();
50
+ const { entriesByDirectory, expandedPaths, selectedPath, selectedFile, isLoading, loadingPaths, refresh, selectFile, setExpandedPaths } = workspace;
51
+ const fileDestination = getValidatedFileDestination(navigation.current.destination, workspace.root);
52
+ React.useEffect(() => {
53
+ if (fileDestination && selectedPath !== fileDestination.path) selectFile(fileDestination.path);
89
54
  }, [
90
- source,
91
- filesByPath,
92
- markLoading
55
+ fileDestination?.path,
56
+ selectFile,
57
+ selectedPath
93
58
  ]);
94
- const loadInfo = React.useCallback(async () => {
95
- if (!source.getWorkspaceInfo) return;
96
- try {
97
- setInfo(await source.getWorkspaceInfo());
98
- } catch {
99
- setInfo(null);
100
- }
101
- }, [source]);
102
59
  React.useEffect(() => {
103
- setEntriesByDirectory({});
104
- setFilesByPath({});
105
- setExpandedPathsState(/* @__PURE__ */ new Set());
106
- setSelectedPath(null);
107
- setError(null);
108
- setInfo(null);
109
- loadedDirectoriesRef.current = /* @__PURE__ */ new Set();
110
- setIsLoading(true);
111
- Promise.all([loadDirectory(root, { force: true }), loadInfo()]).finally(() => setIsLoading(false));
60
+ const previous = previousDestinationRef.current;
61
+ if (navigation.current.destination.kind === "files" && previous.kind === "file") {
62
+ const saved = restoration.get(navigation.current.key);
63
+ pendingTreeFocusRef.current = saved?.focusDestination ? null : {
64
+ entryKey: navigation.current.key,
65
+ path: previous.path
66
+ };
67
+ }
68
+ previousDestinationRef.current = navigation.current.destination;
112
69
  }, [
113
- loadDirectory,
114
- loadInfo,
115
- root
70
+ navigation.current.key,
71
+ navigation.current.destination,
72
+ restoration
116
73
  ]);
117
- const setExpandedPaths = React.useCallback((next) => {
118
- setExpandedPathsState((current) => {
119
- for (const path of next) if (!current.has(path)) loadDirectory(path);
120
- return next;
121
- });
122
- }, [loadDirectory]);
123
- const selectFile = React.useCallback((path) => {
124
- setSelectedPath(path);
125
- if (path !== null) loadFile(path);
126
- }, [loadFile]);
127
- const refresh = React.useCallback(() => {
128
- (async () => {
129
- setIsLoading(true);
130
- try {
131
- await Promise.all([...[root, ...expandedPaths].map((path) => loadDirectory(path, { force: true })), loadInfo()]);
132
- if (selectedPath) await loadFile(selectedPath, { force: true });
133
- } finally {
134
- setIsLoading(false);
135
- }
136
- })();
74
+ React.useEffect(() => {
75
+ const pending = pendingTreeFocusRef.current;
76
+ if (!pending || pending.entryKey !== navigation.current.key || navigation.current.destination.kind !== "files") return;
77
+ const ancestors = getDirectoryAncestors(pending.path, workspace.root);
78
+ if (ancestors.some((path) => !expandedPaths.has(path))) {
79
+ setExpandedPaths(/* @__PURE__ */ new Set([...expandedPaths, ...ancestors]));
80
+ return;
81
+ }
82
+ const surface = explorerRef.current;
83
+ const target = [...surface?.querySelectorAll(".file-tree__node") ?? []].find((candidate) => candidate.dataset.workspacePath === pending.path);
84
+ if (target) {
85
+ target.focus();
86
+ pendingTreeFocusRef.current = null;
87
+ return;
88
+ }
89
+ if (isLoading || ancestors.some((path) => loadingPaths.has(path))) return;
90
+ (surface?.querySelector(".file-tree__node") ?? surface?.querySelector("[data-workspace-browser-focus-target]"))?.focus();
91
+ pendingTreeFocusRef.current = null;
137
92
  }, [
138
- expandedPaths,
139
- loadDirectory,
140
- loadFile,
141
- loadInfo,
142
- root,
143
- selectedPath
144
- ]);
145
- return {
146
- root,
147
93
  entriesByDirectory,
148
- entryByPath: React.useMemo(() => {
149
- const map = /* @__PURE__ */ new Map();
150
- for (const entries of Object.values(entriesByDirectory)) for (const entry of entries) map.set(entry.path, entry);
151
- return map;
152
- }, [entriesByDirectory]),
153
94
  expandedPaths,
154
- setExpandedPaths,
155
- selectedPath,
156
- selectFile,
157
- selectedFile: selectedPath ? filesByPath[selectedPath] ?? null : null,
158
95
  isLoading,
159
96
  loadingPaths,
160
- error,
161
- info,
162
- refresh
163
- };
164
- }
165
- const LANGUAGE_BY_EXTENSION = {
166
- ts: "ts",
167
- mts: "ts",
168
- cts: "ts",
169
- tsx: "tsx",
170
- js: "js",
171
- mjs: "js",
172
- cjs: "js",
173
- jsx: "jsx",
174
- json: "json",
175
- jsonc: "jsonc",
176
- css: "css",
177
- html: "html",
178
- md: "md",
179
- mdx: "mdx",
180
- sh: "bash",
181
- bash: "bash",
182
- zsh: "bash",
183
- yml: "yaml",
184
- yaml: "yaml",
185
- toml: "toml",
186
- py: "python",
187
- rs: "rust",
188
- go: "go",
189
- sql: "sql",
190
- xml: "xml",
191
- svg: "xml",
192
- vue: "vue",
193
- svelte: "svelte"
194
- };
195
- /** Best-effort syntax-highlighting language from a file path; unknown extensions render as plain text. */
196
- function inferWorkspaceLanguage(path) {
197
- const extension = basename(path).split(".").at(-1)?.toLowerCase() ?? "";
198
- return LANGUAGE_BY_EXTENSION[extension] ?? "text";
199
- }
200
- /**
201
- * A Think workspace file browser with a preview pane: lazy directory tree,
202
- * filter, refresh, and syntax-highlighted file view with copy. Drive it with
203
- * `useWorkspace(source)`.
204
- *
205
- * @example
206
- * ```tsx
207
- * const workspace = useWorkspace({ source: agent.workspace, root: "/workspace" });
208
- * <WorkspaceExplorer workspace={workspace} />
209
- * ```
210
- */
211
- function WorkspaceExplorer({ workspace, className, title = "Workspace", actions, showFilter = true, filterPlaceholder = "Filter files...", emptyState, ref, ...props }) {
212
- const [view, setView] = React.useState("tree");
213
- const [filter, setFilter] = React.useState("");
214
- const filterQuery = filter.trim().toLowerCase();
215
- const { root, entriesByDirectory, entryByPath, expandedPaths, setExpandedPaths, selectedPath, selectFile, selectedFile, isLoading, loadingPaths, error, info, refresh } = workspace;
216
- const handleSelect = React.useCallback((path) => {
217
- if (entryByPath.get(path)?.type === "directory") return;
218
- selectFile(path);
219
- setView("file");
220
- }, [entryByPath, selectFile]);
221
- const filteredFiles = React.useMemo(() => {
222
- if (!filterQuery) return [];
223
- return sortEntries([...entryByPath.values()].filter((entry) => entry.type !== "directory" && entry.path.toLowerCase().includes(filterQuery)));
224
- }, [entryByPath, filterQuery]);
225
- const renderEntry = (entry) => {
226
- if (entry.type === "directory") return /* @__PURE__ */ jsx(FileTreeFolder, {
227
- path: entry.path,
228
- name: entry.name,
229
- children: (entriesByDirectory[entry.path] ?? []).map(renderEntry)
230
- }, entry.path);
231
- return /* @__PURE__ */ jsx(FileTreeFile, {
232
- path: entry.path,
233
- name: entry.name
234
- }, entry.path);
235
- };
236
- const rootEntries = entriesByDirectory[root] ?? [];
237
- const showFileView = view === "file" && selectedPath !== null;
238
- const selectedFileLoading = selectedPath !== null && loadingPaths.has(selectedPath);
239
- return /* @__PURE__ */ jsxs("div", {
240
- ref,
97
+ navigation.current.destination,
98
+ navigation.current.key,
99
+ setExpandedPaths,
100
+ workspace.root
101
+ ]);
102
+ return /* @__PURE__ */ jsx("div", {
103
+ ref: mergeRefs(explorerRef, ref),
241
104
  className: cx("workspace-explorer", className),
242
105
  ...props,
243
- children: [/* @__PURE__ */ jsxs("div", {
244
- className: "workspace-explorer__header",
245
- children: [showFileView ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Button, {
246
- variant: "ghost",
247
- color: "neutral",
248
- size: "icon-xs",
249
- onClick: () => setView("tree"),
250
- "aria-label": "Back to files",
251
- children: /* @__PURE__ */ jsx(ArrowLeftIcon, { "aria-hidden": "true" })
252
- }), /* @__PURE__ */ jsx("span", {
253
- className: "workspace-explorer__path",
254
- title: selectedPath,
255
- children: selectedPath
256
- })] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
257
- className: "workspace-explorer__title",
258
- children: title
259
- }), info ? /* @__PURE__ */ jsxs("span", {
260
- className: "workspace-explorer__count",
261
- children: [info.fileCount.toLocaleString(), " files"]
262
- }) : null] }), /* @__PURE__ */ jsxs("div", {
263
- className: "workspace-explorer__actions",
264
- children: [actions, /* @__PURE__ */ jsx(Button, {
265
- variant: "ghost",
266
- color: "neutral",
267
- size: "icon-xs",
268
- onClick: refresh,
269
- disabled: isLoading,
270
- "aria-label": "Refresh workspace",
271
- children: /* @__PURE__ */ jsx(RefreshIcon, { "aria-hidden": "true" })
272
- })]
273
- })]
274
- }), showFileView ? /* @__PURE__ */ jsx(WorkspaceFileView, {
275
- file: selectedFile,
276
- isLoading: selectedFileLoading
277
- }) : /* @__PURE__ */ jsxs("div", {
278
- className: "workspace-explorer__body",
279
- children: [showFilter ? /* @__PURE__ */ jsxs("label", {
280
- className: "workspace-explorer__filter",
281
- children: [/* @__PURE__ */ jsx(SearchIcon, { "aria-hidden": "true" }), /* @__PURE__ */ jsx(Input, {
282
- type: "search",
283
- value: filter,
284
- onChange: (event) => setFilter(event.currentTarget.value),
285
- placeholder: filterPlaceholder,
286
- className: "workspace-explorer__filter-input",
287
- "aria-label": "Filter workspace files",
288
- autoComplete: "off"
289
- })]
290
- }) : null, error ? /* @__PURE__ */ jsx("div", {
291
- className: "workspace-explorer__empty",
292
- role: "alert",
293
- children: error
294
- }) : filterQuery ? filteredFiles.length > 0 ? /* @__PURE__ */ jsx(FileTree, {
295
- className: "workspace-explorer__tree",
296
- selectedPath,
297
- onSelect: handleSelect,
298
- "aria-label": "Workspace files",
299
- children: filteredFiles.map((entry) => /* @__PURE__ */ jsx(FileTreeFile, {
300
- path: entry.path,
301
- name: entry.path
302
- }, entry.path))
303
- }) : /* @__PURE__ */ jsx("div", {
304
- className: "workspace-explorer__empty",
305
- children: "No matching files"
306
- }) : rootEntries.length > 0 ? /* @__PURE__ */ jsx(FileTree, {
307
- className: "workspace-explorer__tree",
308
- expanded: new Set(expandedPaths),
309
- onExpandedChange: setExpandedPaths,
310
- selectedPath,
311
- onSelect: handleSelect,
312
- "aria-label": "Workspace files",
313
- children: rootEntries.map(renderEntry)
314
- }) : /* @__PURE__ */ jsx("div", {
315
- className: "workspace-explorer__empty",
316
- children: isLoading ? "Loading workspace..." : emptyState ?? "No workspace files"
317
- })]
318
- })]
319
- });
320
- }
321
- function WorkspaceFileView({ file, isLoading }) {
322
- if (isLoading) return /* @__PURE__ */ jsx("div", {
323
- className: "workspace-explorer__empty",
324
- children: "Loading file..."
325
- });
326
- if (!file) return /* @__PURE__ */ jsx("div", {
327
- className: "workspace-explorer__empty",
328
- children: "File preview is unavailable."
329
- });
330
- if (file.error) return /* @__PURE__ */ jsxs("div", {
331
- className: "workspace-explorer__empty",
332
- role: "alert",
333
- children: ["Could not preview this file: ", file.error]
334
- });
335
- if (file.content === null) return /* @__PURE__ */ jsx("div", {
336
- className: "workspace-explorer__empty",
337
- children: "This file has no text preview."
338
- });
339
- if (file.content.length === 0) return /* @__PURE__ */ jsx("div", {
340
- className: "workspace-explorer__empty",
341
- children: "This file is empty."
342
- });
343
- return /* @__PURE__ */ jsx("div", {
344
- className: "workspace-explorer__file",
345
- children: /* @__PURE__ */ jsxs(CodeBlock, {
346
- className: "workspace-explorer__code",
347
- code: file.content,
348
- language: inferWorkspaceLanguage(file.path),
349
- showLineNumbers: true,
350
- children: [/* @__PURE__ */ jsxs(CodeBlockHeader, { children: [/* @__PURE__ */ jsxs(CodeBlockTitle, { children: [/* @__PURE__ */ jsx(FileIcon, { "aria-hidden": "true" }), /* @__PURE__ */ jsx(CodeBlockFilename, { children: file.name })] }), /* @__PURE__ */ jsx(CodeBlockActions, { children: /* @__PURE__ */ jsx(CodeBlockCopyButton, {}) })] }), /* @__PURE__ */ jsx(CodeBlockContainer, { children: /* @__PURE__ */ jsx(CodeBlockContent, {}) })]
106
+ children: fileDestination ? /* @__PURE__ */ jsx(WorkspaceNavigationProvider, {
107
+ root: workspace.root,
108
+ base: {
109
+ kind: "file",
110
+ path: fileDestination.path
111
+ },
112
+ navigation,
113
+ children: /* @__PURE__ */ jsx(WorkspaceFileView, {
114
+ file: selectedFile?.path === fileDestination.path ? selectedFile : null,
115
+ path: fileDestination.path,
116
+ isLoading: selectedPath !== fileDestination.path || loadingPaths.has(fileDestination.path),
117
+ isRefreshing: isLoading,
118
+ leading,
119
+ actions,
120
+ onRefresh: refresh
121
+ })
122
+ }) : /* @__PURE__ */ jsx(WorkspaceBrowser, {
123
+ workspace,
124
+ title,
125
+ leading,
126
+ actions,
127
+ showFilter,
128
+ filterPlaceholder,
129
+ emptyState,
130
+ onSelectFile: (path) => navigation.navigate({
131
+ kind: "file",
132
+ path,
133
+ fragment: null
134
+ }),
135
+ onBack: navigation.canGoBack ? () => navigation.goBack() : void 0,
136
+ onForward: navigation.canGoForward ? () => navigation.goForward() : void 0
351
137
  })
352
138
  });
353
139
  }
354
140
  //#endregion
355
- export { WorkspaceExplorer, inferWorkspaceLanguage, useWorkspace };
141
+ export { WorkspaceExplorer, WorkspaceFileLink, WorkspaceNavigationProvider, inferWorkspaceLanguage, inferWorkspaceMedia, isWorkspaceMarkdownPath, isWorkspaceMediaPath, isWorkspaceSkillPath, parseWorkspaceSkill, resolveWorkspaceLink, useWorkspace };
@@ -0,0 +1,2 @@
1
+ import { a as WorkspaceMediaInfo, c as WorkspaceMediaTrackKind, d as inferWorkspaceLanguage, f as inferWorkspaceMedia, g as parseWorkspaceSkill, h as isWorkspaceSkillPath, i as WorkspaceFileState, l as WorkspaceSkillDocument, m as isWorkspaceMediaPath, n as WorkspaceFileAssetRequest, o as WorkspaceMediaKind, p as isWorkspaceMarkdownPath, r as WorkspaceFileAssetResolver, s as WorkspaceMediaTrack, t as WorkspaceFileAsset, u as getWorkspaceBasename } from "../WorkspaceFile-DOZkPwzu.js";
2
+ export { WorkspaceFileAsset, WorkspaceFileAssetRequest, WorkspaceFileAssetResolver, WorkspaceFileState, WorkspaceMediaInfo, WorkspaceMediaKind, WorkspaceMediaTrack, WorkspaceMediaTrackKind, WorkspaceSkillDocument, getWorkspaceBasename, inferWorkspaceLanguage, inferWorkspaceMedia, isWorkspaceMarkdownPath, isWorkspaceMediaPath, isWorkspaceSkillPath, parseWorkspaceSkill };