@mcp-b/react-components 0.7.0 → 0.8.0

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 (75) hide show
  1. package/dist/AgentChat-403mckDG.d.ts +101 -0
  2. package/dist/BrowserLiveView-ZktFZiMD.d.ts +47 -0
  3. package/dist/BrowserReplay-6G-mnwcn.d.ts +40 -0
  4. package/dist/{CodeBlock-BK0sOAbV.js → CodeBlock-XNQOupQ3.js} +1 -1
  5. package/dist/{FileTree-BWQZ0FSi.js → FileTree-wMyZhhHO.js} +1 -1
  6. package/dist/ModelSelector-9G_-GooI.js +306 -0
  7. package/dist/ThinkChatActivity-CeHzk6-K.d.ts +73 -0
  8. package/dist/ThinkChatContext-BLAAb8J5.d.ts +28 -0
  9. package/dist/components/Activity.js +1 -1
  10. package/dist/components/AgentChat.d.ts +1 -100
  11. package/dist/components/AgentChat.js +12 -7
  12. package/dist/components/AgentConnection.d.ts +43 -2
  13. package/dist/components/AgentConnection.js +154 -3
  14. package/dist/components/Artifact.js +1 -1
  15. package/dist/components/BrowserLiveView.d.ts +1 -46
  16. package/dist/components/BrowserLiveView.js +1 -1
  17. package/dist/components/BrowserReplay.d.ts +1 -39
  18. package/dist/components/BrowserReplay.js +1 -1
  19. package/dist/components/ChainOfThought.d.ts +1 -1
  20. package/dist/components/ChainOfThought.js +1 -1
  21. package/dist/components/ChatGptSignInScreen.d.ts +38 -0
  22. package/dist/components/ChatGptSignInScreen.js +201 -0
  23. package/dist/components/CodeBlock.js +1 -1
  24. package/dist/components/Commit.js +1 -1
  25. package/dist/components/ContextMenu.js +1 -1
  26. package/dist/components/Conversation.js +1 -1
  27. package/dist/components/Dialog.js +1 -1
  28. package/dist/components/EnvironmentVariables.js +1 -1
  29. package/dist/components/FileTree.js +1 -1
  30. package/dist/components/McpServerPicker.js +7 -5
  31. package/dist/components/Menubar.js +1 -1
  32. package/dist/components/Message.js +1 -1
  33. package/dist/components/MessageScroller.js +2 -2
  34. package/dist/components/ModelSelector.js +1 -305
  35. package/dist/components/NavigationMenu.js +1 -1
  36. package/dist/components/PromptInput.js +6 -5
  37. package/dist/components/Queue.js +1 -1
  38. package/dist/components/Reasoning.js +1 -1
  39. package/dist/components/Select.js +1 -1
  40. package/dist/components/Sources.js +1 -1
  41. package/dist/components/Stepper.js +1 -1
  42. package/dist/components/Task.js +1 -1
  43. package/dist/components/Terminal.js +4 -3
  44. package/dist/components/TestResults.js +1 -1
  45. package/dist/components/ThinkChat.d.ts +15 -7
  46. package/dist/components/ThinkChat.js +1 -1
  47. package/dist/components/ThinkChatActivity.d.ts +2 -72
  48. package/dist/components/ThinkChatActivity.js +4 -3
  49. package/dist/components/ThinkChatContext.d.ts +1 -27
  50. package/dist/components/ThinkChatToolPanels.js +1 -1
  51. package/dist/components/ThinkChatToolParts.js +4 -4
  52. package/dist/components/ThinkExecutionWorkbench.d.ts +27 -0
  53. package/dist/components/ThinkExecutionWorkbench.js +450 -0
  54. package/dist/components/ThinkFullApp.d.ts +61 -0
  55. package/dist/components/ThinkFullApp.js +258 -0
  56. package/dist/components/ThinkThreadPicker.d.ts +88 -0
  57. package/dist/components/ThinkThreadPicker.js +282 -0
  58. package/dist/components/WebPreview.js +1 -1
  59. package/dist/components/WorkspaceExplorer.js +5 -4
  60. package/dist/components/icons.d.ts +2 -2
  61. package/dist/components/icons.js +2 -2
  62. package/dist/{copy-button-CB2MWcau.js → copy-button-C6JSKtFE.js} +1 -1
  63. package/dist/{icons-BYpJY6L2.d.ts → icons-CbjtbIJK.d.ts} +3 -1
  64. package/dist/{icons-BCNawx-T.js → icons-D2Q5n5C1.js} +37 -1
  65. package/dist/styles/agent-connection.css +33 -5
  66. package/dist/styles/chatgpt-sign-in-screen.css +224 -0
  67. package/dist/styles/code-block.css +3 -1
  68. package/dist/styles/index.css +3 -0
  69. package/dist/styles/message-scroller.css +24 -1
  70. package/dist/styles/prompt-input.css +1 -0
  71. package/dist/styles/think-full-app.css +731 -0
  72. package/dist/styles/think-thread-picker.css +449 -0
  73. package/dist/utils/mcp-server-catalog.d.ts +28 -3
  74. package/dist/utils/mcp-server-catalog.js +27 -1
  75. package/package.json +7 -8
@@ -0,0 +1,258 @@
1
+ import { t as cx } from "../class-names-9UtZwb0J.js";
2
+ import { P as TerminalIcon, h as FileIcon, n as ArrowLeftIcon } from "../icons-D2Q5n5C1.js";
3
+ import { Button } from "./Button.js";
4
+ import { Tooltip, TooltipPopup, TooltipPortal, TooltipPositioner, TooltipTrigger } from "./Tooltip.js";
5
+ import { ToggleGroup, ToggleGroupItem } from "./ToggleGroup.js";
6
+ import { WorkspaceExplorer, useWorkspace } from "./WorkspaceExplorer.js";
7
+ import { ThinkChat, useThinkPendingApprovals } from "./ThinkChat.js";
8
+ import { ThinkExecutionWorkbench } from "./ThinkExecutionWorkbench.js";
9
+ import { ThinkThreadShell } from "./ThinkThreadPicker.js";
10
+ import * as React from "react";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ //#region src/components/ThinkFullApp.tsx
13
+ const workbenchPanelDefaultWidth = 340;
14
+ const workbenchPanelMinWidth = 300;
15
+ const workbenchPanelMaxWidth = 600;
16
+ const workbenchPanelKeyboardStep = 24;
17
+ function clampWorkbenchPanelWidth(width) {
18
+ return Math.min(workbenchPanelMaxWidth, Math.max(workbenchPanelMinWidth, width));
19
+ }
20
+ function beginWorkbenchPanelResize(event, current, apply) {
21
+ event.preventDefault();
22
+ const startX = event.clientX;
23
+ const onMove = (move) => {
24
+ apply(clampWorkbenchPanelWidth(current - (move.clientX - startX)));
25
+ };
26
+ const onUp = () => {
27
+ window.removeEventListener("pointermove", onMove);
28
+ window.removeEventListener("pointerup", onUp);
29
+ window.removeEventListener("pointercancel", onUp);
30
+ document.body.classList.remove("is-resizing-columns");
31
+ };
32
+ document.body.classList.add("is-resizing-columns");
33
+ window.addEventListener("pointermove", onMove);
34
+ window.addEventListener("pointerup", onUp);
35
+ window.addEventListener("pointercancel", onUp);
36
+ }
37
+ function WorkbenchBackButton({ onBack }) {
38
+ return /* @__PURE__ */ jsx(Button, {
39
+ type: "button",
40
+ variant: "ghost",
41
+ color: "neutral",
42
+ size: "icon-sm",
43
+ className: "think-full-app__workspace-back",
44
+ "aria-label": "Show chat",
45
+ onClick: onBack,
46
+ children: /* @__PURE__ */ jsx(ArrowLeftIcon, {})
47
+ });
48
+ }
49
+ function WorkspacePanel({ source, root }) {
50
+ return /* @__PURE__ */ jsx(WorkspaceExplorer, {
51
+ className: "think-full-app__workbench",
52
+ workspace: useWorkspace({
53
+ source,
54
+ root
55
+ })
56
+ });
57
+ }
58
+ function WorkbenchViewItem({ children, label, value }) {
59
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx(ToggleGroupItem, {
60
+ type: "button",
61
+ className: "think-full-app__workbench-view-item",
62
+ value,
63
+ "aria-label": label,
64
+ children
65
+ }) }), /* @__PURE__ */ jsx(TooltipPortal, { children: /* @__PURE__ */ jsx(TooltipPositioner, {
66
+ side: "bottom",
67
+ sideOffset: 6,
68
+ children: /* @__PURE__ */ jsx(TooltipPopup, { children: label })
69
+ }) })] });
70
+ }
71
+ function WorkbenchViewSwitch({ value, onValueChange }) {
72
+ return /* @__PURE__ */ jsxs(ToggleGroup, {
73
+ className: "think-full-app__workbench-view-switch",
74
+ size: "sm",
75
+ value: [value],
76
+ "aria-label": "Workbench views",
77
+ onValueChange: (nextValue) => {
78
+ const [next] = nextValue;
79
+ if (next) onValueChange(next);
80
+ },
81
+ children: [/* @__PURE__ */ jsx(WorkbenchViewItem, {
82
+ value: "files",
83
+ label: "Files",
84
+ children: /* @__PURE__ */ jsx(FileIcon, {})
85
+ }), /* @__PURE__ */ jsx(WorkbenchViewItem, {
86
+ value: "execution",
87
+ label: "Execution",
88
+ children: /* @__PURE__ */ jsx(TerminalIcon, {})
89
+ })]
90
+ });
91
+ }
92
+ function WorkbenchPanel({ executionSource, view, workspace, workspaceRoot }) {
93
+ if (workspace) return /* @__PURE__ */ jsx("div", {
94
+ className: "think-full-app__workbench-panel",
95
+ "data-view": view,
96
+ children: view === "files" ? /* @__PURE__ */ jsx(WorkspacePanel, {
97
+ source: workspace,
98
+ root: workspaceRoot
99
+ }) : /* @__PURE__ */ jsx(ThinkExecutionWorkbench, {
100
+ source: executionSource,
101
+ title: "Execution"
102
+ })
103
+ });
104
+ return /* @__PURE__ */ jsx(ThinkExecutionWorkbench, {
105
+ source: executionSource,
106
+ title: "Execution"
107
+ });
108
+ }
109
+ /** Combines ThinkChat with an optional resizable workbench shell. */
110
+ function ThinkFullApp({ agent, chat, className, defaultWorkbenchView, defaultThreadSidebarOpen, messages, onThreadSidebarOpenChange, onWorkbenchViewChange, ref, style, threadSidebar, threadSidebarOpen, workbenchView, workspace, workspaceRoot, ...props }) {
111
+ const approvals = useThinkPendingApprovals(agent.stub, `${chat.messages.length}:${chat.status}`);
112
+ const resolvedMessages = React.useMemo(() => ({
113
+ ...messages,
114
+ executionApprovals: messages?.executionApprovals ?? approvals.executionApprovals,
115
+ pendingApprovals: messages?.pendingApprovals ?? approvals.pendingApprovals
116
+ }), [
117
+ approvals.executionApprovals,
118
+ approvals.pendingApprovals,
119
+ messages
120
+ ]);
121
+ const hasWorkspace = workspace !== void 0;
122
+ const [mobileView, setMobileView] = React.useState("chat");
123
+ const [innerWorkbenchView, setInnerWorkbenchView] = React.useState(defaultWorkbenchView ?? "files");
124
+ const [workbenchPanelWidth, setWorkbenchPanelWidth] = React.useState(workbenchPanelDefaultWidth);
125
+ const activeMobileView = mobileView === "workbench" ? "workbench" : "chat";
126
+ const isMobileWorkbenchView = activeMobileView === "workbench";
127
+ const activeWorkbenchView = hasWorkspace ? workbenchView ?? innerWorkbenchView : "execution";
128
+ const showShellTopbar = hasWorkspace || isMobileWorkbenchView;
129
+ const rootStyle = {
130
+ ...style,
131
+ "--think-full-app-workbench-w": `${workbenchPanelWidth}px`
132
+ };
133
+ const resizeWorkbenchPanelBy = (delta) => {
134
+ setWorkbenchPanelWidth((current) => clampWorkbenchPanelWidth(current + delta));
135
+ };
136
+ const setWorkbenchView = React.useCallback((next) => {
137
+ if (!hasWorkspace) return;
138
+ if (workbenchView === void 0) setInnerWorkbenchView(next);
139
+ onWorkbenchViewChange?.(next);
140
+ }, [
141
+ hasWorkspace,
142
+ onWorkbenchViewChange,
143
+ workbenchView
144
+ ]);
145
+ const app = /* @__PURE__ */ jsxs("div", {
146
+ ref,
147
+ className: cx("think-full-app", className),
148
+ "data-has-files": hasWorkspace ? "true" : "false",
149
+ "data-has-thread-sidebar": threadSidebar ? "true" : "false",
150
+ "data-has-workbench": "true",
151
+ "data-has-workbench-topbar": hasWorkspace ? "true" : "false",
152
+ "data-mobile-view": activeMobileView,
153
+ style: rootStyle,
154
+ ...props,
155
+ children: [
156
+ showShellTopbar ? /* @__PURE__ */ jsxs("div", {
157
+ className: "think-full-app__topbar",
158
+ role: "toolbar",
159
+ "aria-label": "Workbench controls",
160
+ "data-mobile-only": hasWorkspace ? void 0 : "true",
161
+ children: [/* @__PURE__ */ jsx(WorkbenchBackButton, { onBack: () => setMobileView("chat") }), hasWorkspace ? /* @__PURE__ */ jsx(WorkbenchViewSwitch, {
162
+ value: activeWorkbenchView,
163
+ onValueChange: setWorkbenchView
164
+ }) : null]
165
+ }) : null,
166
+ !isMobileWorkbenchView ? /* @__PURE__ */ jsx("div", {
167
+ className: "think-full-app__mobile-header",
168
+ role: "toolbar",
169
+ "aria-label": "Workbench",
170
+ children: /* @__PURE__ */ jsx(Button, {
171
+ type: "button",
172
+ variant: "ghost",
173
+ color: "neutral",
174
+ size: "icon-sm",
175
+ className: "think-full-app__mobile-action",
176
+ "aria-label": hasWorkspace ? "Show workbench" : "Show execution",
177
+ onClick: () => setMobileView("workbench"),
178
+ children: hasWorkspace ? /* @__PURE__ */ jsx(FileIcon, {}) : /* @__PURE__ */ jsx(TerminalIcon, {})
179
+ })
180
+ }) : null,
181
+ /* @__PURE__ */ jsx("section", {
182
+ className: "think-full-app__chat-surface",
183
+ "aria-label": "Chat",
184
+ children: /* @__PURE__ */ jsx("div", {
185
+ className: "think-full-app__chat",
186
+ children: /* @__PURE__ */ jsxs("div", {
187
+ className: "think-full-app__chat-inner",
188
+ children: [/* @__PURE__ */ jsx(ThinkChat.Provider, {
189
+ chat,
190
+ children: /* @__PURE__ */ jsx(ThinkChat.Messages, { ...resolvedMessages })
191
+ }), chat.error ? /* @__PURE__ */ jsxs("div", {
192
+ className: "think-full-app__error",
193
+ role: "alert",
194
+ children: [chat.error.message, /* @__PURE__ */ jsx(Button, {
195
+ type: "button",
196
+ variant: "link",
197
+ color: "neutral",
198
+ size: "xs",
199
+ className: "think-full-app__error-dismiss",
200
+ onClick: () => chat.clearError(),
201
+ children: "Dismiss"
202
+ })]
203
+ }) : null]
204
+ })
205
+ })
206
+ }),
207
+ /* @__PURE__ */ jsx("button", {
208
+ type: "button",
209
+ role: "separator",
210
+ "aria-label": "Resize workbench panel",
211
+ "aria-orientation": "vertical",
212
+ "aria-valuemin": workbenchPanelMinWidth,
213
+ "aria-valuemax": workbenchPanelMaxWidth,
214
+ "aria-valuenow": workbenchPanelWidth,
215
+ className: "think-full-app__resizer",
216
+ onPointerDown: (event) => beginWorkbenchPanelResize(event, workbenchPanelWidth, setWorkbenchPanelWidth),
217
+ onKeyDown: (event) => {
218
+ if (event.key === "ArrowLeft") {
219
+ event.preventDefault();
220
+ resizeWorkbenchPanelBy(workbenchPanelKeyboardStep);
221
+ } else if (event.key === "ArrowRight") {
222
+ event.preventDefault();
223
+ resizeWorkbenchPanelBy(-24);
224
+ } else if (event.key === "Home") {
225
+ event.preventDefault();
226
+ setWorkbenchPanelWidth(workbenchPanelMinWidth);
227
+ } else if (event.key === "End") {
228
+ event.preventDefault();
229
+ setWorkbenchPanelWidth(workbenchPanelMaxWidth);
230
+ }
231
+ }
232
+ }),
233
+ /* @__PURE__ */ jsx("aside", {
234
+ className: "think-full-app__aside",
235
+ "aria-label": "Workbench",
236
+ children: /* @__PURE__ */ jsx("div", {
237
+ className: "think-full-app__workbench-slot",
238
+ children: /* @__PURE__ */ jsx(WorkbenchPanel, {
239
+ executionSource: agent.stub,
240
+ workspace,
241
+ workspaceRoot,
242
+ view: activeWorkbenchView
243
+ })
244
+ })
245
+ })
246
+ ]
247
+ });
248
+ if (!threadSidebar) return app;
249
+ return /* @__PURE__ */ jsx(ThinkThreadShell, {
250
+ sidebar: threadSidebar,
251
+ sidebarOpen: threadSidebarOpen,
252
+ defaultSidebarOpen: defaultThreadSidebarOpen,
253
+ onSidebarOpenChange: onThreadSidebarOpenChange,
254
+ children: app
255
+ });
256
+ }
257
+ //#endregion
258
+ export { ThinkExecutionWorkbench, ThinkFullApp };
@@ -0,0 +1,88 @@
1
+ import * as React from "react";
2
+
3
+ //#region src/components/ThinkThreadPicker.d.ts
4
+ interface ThinkThreadItem {
5
+ /** Stable child-agent/thread id passed back to the app's route state. */
6
+ id: string;
7
+ /** Human-readable thread title, usually generated by the parent Think agent. */
8
+ title: string;
9
+ /** Optional parent-owned preview for apps that render their own richer sidebar. */
10
+ lastMessagePreview?: string;
11
+ /** Timestamp from the parent registry or metadata row. `updatedAt` wins over `createdAt`. */
12
+ createdAt?: Date | number;
13
+ /** Timestamp from parent-owned thread metadata when available. */
14
+ updatedAt?: Date | number;
15
+ /** Preformatted timestamp label when the app already has one, such as "14m". */
16
+ updatedAtLabel?: string;
17
+ /** Optional upstream/app status string. The picker only displays it; it does not derive it. */
18
+ status?: string;
19
+ /** Human label for `status` when the raw upstream value is not user-facing. */
20
+ statusLabel?: string;
21
+ }
22
+ interface ThinkThreadPickerProps extends Omit<React.HTMLAttributes<HTMLElement>, "onSelect" | "title"> {
23
+ /** Threads from the parent agent, already sorted in the order the app wants to show. */
24
+ threads: readonly ThinkThreadItem[];
25
+ /** Selected child-agent/thread id. */
26
+ selectedId?: string;
27
+ /** Called when a thread row is selected; callers route this into `useAgent({ sub })`. */
28
+ onSelect: (id: string) => void;
29
+ /** Optional parent-agent create action, usually `parent.stub.createThread()`. */
30
+ onCreate?: () => void | Promise<void>;
31
+ /** Visible heading for the roster. */
32
+ title?: React.ReactNode;
33
+ /** Accessible label for the list navigation. */
34
+ "aria-label"?: string;
35
+ }
36
+ interface ThinkThreadShellProps extends React.HTMLAttributes<HTMLDivElement> {
37
+ /** Thread picker or app-owned sidebar shown beside the selected child shell. */
38
+ sidebar: React.ReactNode;
39
+ /** Whether the thread drawer is open. Omit for internal state. */
40
+ sidebarOpen?: boolean;
41
+ /** Initial thread drawer state when `sidebarOpen` is uncontrolled. */
42
+ defaultSidebarOpen?: boolean;
43
+ /** Called when the thread drawer is opened or closed. */
44
+ onSidebarOpenChange?: (open: boolean) => void;
45
+ }
46
+ /**
47
+ * Thread roster UI for sub-agent chat apps.
48
+ *
49
+ * This component does not call Think or Cloudflare RPC methods directly. Feed
50
+ * it from the parent agent's roster method, then route `onSelect` into the
51
+ * child `useAgent({ sub: [...] })` name. Keep backend contract notes beside the
52
+ * parent Think agent because `listThreads()`/`createThread()` are app-owned.
53
+ */
54
+ declare function ThinkThreadPicker({
55
+ threads,
56
+ selectedId,
57
+ onSelect,
58
+ onCreate,
59
+ className,
60
+ title,
61
+ "aria-label": ariaLabel,
62
+ ref,
63
+ ...props
64
+ }: ThinkThreadPickerProps & {
65
+ ref?: React.Ref<HTMLElement>;
66
+ }): React.JSX.Element;
67
+ /**
68
+ * Layout wrapper for a thread picker beside one selected `ThinkFullApp`.
69
+ *
70
+ * Mobile keeps the drawer at about 80% width so the selected chat remains
71
+ * visible as a preview. It only owns layout; callers still own the parent agent,
72
+ * selected child agent, chat hook, and shared workspace.
73
+ */
74
+ declare function ThinkThreadShell({
75
+ sidebar,
76
+ sidebarOpen,
77
+ defaultSidebarOpen,
78
+ onSidebarOpenChange,
79
+ className,
80
+ children,
81
+ ref,
82
+ style,
83
+ ...props
84
+ }: ThinkThreadShellProps & {
85
+ ref?: React.Ref<HTMLDivElement>;
86
+ }): React.JSX.Element;
87
+ //#endregion
88
+ export { ThinkThreadItem, ThinkThreadPicker, ThinkThreadPickerProps, ThinkThreadShell, ThinkThreadShellProps };
@@ -0,0 +1,282 @@
1
+ import { t as cx } from "../class-names-9UtZwb0J.js";
2
+ import { M as SpinnerIcon, O as PlusIcon, j as SidebarIcon, u as DotIcon } from "../icons-D2Q5n5C1.js";
3
+ import { Button } from "./Button.js";
4
+ import { Tooltip, TooltipPopup, TooltipPortal, TooltipPositioner, TooltipTrigger } from "./Tooltip.js";
5
+ import { ScrollArea } from "./ScrollArea.js";
6
+ import * as React from "react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/components/ThinkThreadPicker.tsx
9
+ const threadSidebarDefaultWidth = 256;
10
+ const threadSidebarMinWidth = 224;
11
+ const threadSidebarMaxWidth = 384;
12
+ const threadSidebarKeyboardStep = 24;
13
+ function useControllableSidebar({ sidebarOpen, defaultSidebarOpen, onSidebarOpenChange }) {
14
+ const [innerOpen, setInnerOpen] = React.useState(defaultSidebarOpen ?? true);
15
+ return [sidebarOpen ?? innerOpen, React.useCallback((next) => {
16
+ if (sidebarOpen === void 0) setInnerOpen(next);
17
+ onSidebarOpenChange?.(next);
18
+ }, [sidebarOpen, onSidebarOpenChange])];
19
+ }
20
+ function clampThreadSidebarWidth(width) {
21
+ return Math.min(threadSidebarMaxWidth, Math.max(threadSidebarMinWidth, width));
22
+ }
23
+ function beginThreadSidebarResize(event, current, apply) {
24
+ event.preventDefault();
25
+ const startX = event.clientX;
26
+ const onMove = (move) => {
27
+ apply(clampThreadSidebarWidth(current + move.clientX - startX));
28
+ };
29
+ const onUp = () => {
30
+ window.removeEventListener("pointermove", onMove);
31
+ window.removeEventListener("pointerup", onUp);
32
+ window.removeEventListener("pointercancel", onUp);
33
+ document.body.classList.remove("is-resizing-columns");
34
+ };
35
+ document.body.classList.add("is-resizing-columns");
36
+ window.addEventListener("pointermove", onMove);
37
+ window.addEventListener("pointerup", onUp);
38
+ window.addEventListener("pointercancel", onUp);
39
+ }
40
+ function toThreadDate(value) {
41
+ if (value === void 0) return void 0;
42
+ const date = value instanceof Date ? value : new Date(value);
43
+ return Number.isNaN(date.getTime()) ? void 0 : date;
44
+ }
45
+ function formatThreadTimestamp(thread) {
46
+ const date = toThreadDate(thread.updatedAt ?? thread.createdAt);
47
+ if (thread.updatedAtLabel) return {
48
+ date,
49
+ label: thread.updatedAtLabel
50
+ };
51
+ if (!date) return void 0;
52
+ const elapsed = Math.max(0, Date.now() - date.getTime());
53
+ const minute = 6e4;
54
+ const hour = 60 * minute;
55
+ const day = 24 * hour;
56
+ if (elapsed < hour) return {
57
+ date,
58
+ label: `${Math.max(1, Math.round(elapsed / minute))}m`
59
+ };
60
+ if (elapsed < day) return {
61
+ date,
62
+ label: `${Math.round(elapsed / hour)}h`
63
+ };
64
+ if (elapsed < 7 * day) return {
65
+ date,
66
+ label: `${Math.round(elapsed / day)}d`
67
+ };
68
+ return {
69
+ date,
70
+ label: date.toLocaleDateString(void 0, {
71
+ month: "short",
72
+ day: "numeric"
73
+ })
74
+ };
75
+ }
76
+ function formatThreadStatus(thread) {
77
+ if (thread.statusLabel) return thread.statusLabel;
78
+ if (!thread.status || thread.status === "idle" || thread.status === "ready") return void 0;
79
+ return thread.status.replaceAll("-", " ");
80
+ }
81
+ function isThreadBusy(status) {
82
+ return status === "pending" || status === "running" || status === "starting" || status === "streaming" || status === "submitted" || status === "recovering";
83
+ }
84
+ /**
85
+ * Thread roster UI for sub-agent chat apps.
86
+ *
87
+ * This component does not call Think or Cloudflare RPC methods directly. Feed
88
+ * it from the parent agent's roster method, then route `onSelect` into the
89
+ * child `useAgent({ sub: [...] })` name. Keep backend contract notes beside the
90
+ * parent Think agent because `listThreads()`/`createThread()` are app-owned.
91
+ */
92
+ function ThinkThreadPicker({ threads, selectedId, onSelect, onCreate, className, title = "Recents", "aria-label": ariaLabel = "Threads", ref, ...props }) {
93
+ return /* @__PURE__ */ jsxs("aside", {
94
+ ref,
95
+ className: cx("think-thread-picker", className),
96
+ ...props,
97
+ children: [
98
+ /* @__PURE__ */ jsx("header", {
99
+ className: "think-thread-picker__header",
100
+ children: /* @__PURE__ */ jsx("h2", {
101
+ className: "think-thread-picker__title",
102
+ children: title
103
+ })
104
+ }),
105
+ /* @__PURE__ */ jsx("nav", {
106
+ className: "think-thread-picker__nav",
107
+ "aria-label": ariaLabel,
108
+ children: /* @__PURE__ */ jsxs(ScrollArea.Root, {
109
+ className: "think-thread-picker__scroll",
110
+ children: [/* @__PURE__ */ jsx(ScrollArea.Viewport, {
111
+ className: "think-thread-picker__viewport",
112
+ "aria-label": "Thread list",
113
+ children: threads.length ? /* @__PURE__ */ jsx("ul", {
114
+ className: "think-thread-picker__list",
115
+ children: threads.map((thread) => {
116
+ const timestamp = formatThreadTimestamp(thread);
117
+ const statusLabel = formatThreadStatus(thread);
118
+ const busy = isThreadBusy(thread.status);
119
+ return /* @__PURE__ */ jsx("li", {
120
+ className: "think-thread-picker__item",
121
+ children: /* @__PURE__ */ jsxs(Button, {
122
+ type: "button",
123
+ variant: "ghost",
124
+ color: "neutral",
125
+ size: "sm",
126
+ className: "think-thread-picker__thread",
127
+ "data-selected": thread.id === selectedId ? "true" : "false",
128
+ "data-status": thread.status,
129
+ "aria-current": thread.id === selectedId ? "page" : void 0,
130
+ onClick: () => onSelect(thread.id),
131
+ children: [/* @__PURE__ */ jsx("span", {
132
+ className: "think-thread-picker__thread-title",
133
+ children: thread.title
134
+ }), statusLabel ? /* @__PURE__ */ jsx("span", {
135
+ className: "think-thread-picker__thread-status",
136
+ role: "img",
137
+ "aria-label": statusLabel,
138
+ title: statusLabel,
139
+ children: busy ? /* @__PURE__ */ jsx(SpinnerIcon, {}) : /* @__PURE__ */ jsx(DotIcon, {})
140
+ }) : timestamp?.date ? /* @__PURE__ */ jsx("time", {
141
+ className: "think-thread-picker__thread-time",
142
+ dateTime: timestamp.date.toISOString(),
143
+ title: timestamp.date.toLocaleString(),
144
+ children: timestamp.label
145
+ }) : timestamp ? /* @__PURE__ */ jsx("span", {
146
+ className: "think-thread-picker__thread-time",
147
+ children: timestamp.label
148
+ }) : null]
149
+ })
150
+ }, thread.id);
151
+ })
152
+ }) : /* @__PURE__ */ jsx("p", {
153
+ className: "think-thread-picker__empty",
154
+ children: "No threads yet."
155
+ })
156
+ }), /* @__PURE__ */ jsx(ScrollArea.Scrollbar, {
157
+ orientation: "vertical",
158
+ children: /* @__PURE__ */ jsx(ScrollArea.Thumb, {})
159
+ })]
160
+ })
161
+ }),
162
+ onCreate ? /* @__PURE__ */ jsx("div", {
163
+ className: "think-thread-picker__footer",
164
+ children: /* @__PURE__ */ jsxs(Button, {
165
+ type: "button",
166
+ variant: "normal",
167
+ color: "neutral",
168
+ size: "sm",
169
+ className: "think-thread-picker__create",
170
+ onClick: () => void onCreate(),
171
+ children: [/* @__PURE__ */ jsx(PlusIcon, {}), /* @__PURE__ */ jsx("span", { children: "New thread" })]
172
+ })
173
+ }) : null
174
+ ]
175
+ });
176
+ }
177
+ /**
178
+ * Layout wrapper for a thread picker beside one selected `ThinkFullApp`.
179
+ *
180
+ * Mobile keeps the drawer at about 80% width so the selected chat remains
181
+ * visible as a preview. It only owns layout; callers still own the parent agent,
182
+ * selected child agent, chat hook, and shared workspace.
183
+ */
184
+ function ThinkThreadShell({ sidebar, sidebarOpen, defaultSidebarOpen, onSidebarOpenChange, className, children, ref, style, ...props }) {
185
+ const sidebarId = React.useId();
186
+ const [isSidebarOpen, setSidebarOpen] = useControllableSidebar({
187
+ sidebarOpen,
188
+ defaultSidebarOpen,
189
+ onSidebarOpenChange
190
+ });
191
+ const [sidebarWidth, setSidebarWidth] = React.useState(threadSidebarDefaultWidth);
192
+ const toggleLabel = isSidebarOpen ? "Hide threads" : "Show threads";
193
+ const rootStyle = {
194
+ ...style,
195
+ "--think-thread-sidebar-w": `${sidebarWidth}px`
196
+ };
197
+ const resizeThreadSidebarBy = (delta) => {
198
+ setSidebarWidth((current) => clampThreadSidebarWidth(current + delta));
199
+ };
200
+ return /* @__PURE__ */ jsx("div", {
201
+ ref,
202
+ className: cx("think-thread-shell", className),
203
+ style: rootStyle,
204
+ ...props,
205
+ children: /* @__PURE__ */ jsxs("div", {
206
+ className: "think-thread-shell__frame",
207
+ "data-sidebar-open": isSidebarOpen ? "true" : "false",
208
+ children: [
209
+ /* @__PURE__ */ jsx("div", {
210
+ className: "think-thread-shell__toolbar",
211
+ role: "toolbar",
212
+ "aria-label": "Thread controls",
213
+ children: /* @__PURE__ */ jsx("div", {
214
+ className: "think-thread-shell__panel-toggle",
215
+ children: /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx(Button, {
216
+ type: "button",
217
+ variant: "ghost",
218
+ color: "neutral",
219
+ size: "icon",
220
+ "aria-label": toggleLabel,
221
+ "aria-controls": sidebarId,
222
+ "aria-expanded": isSidebarOpen,
223
+ "aria-pressed": isSidebarOpen,
224
+ "data-selected": isSidebarOpen,
225
+ onClick: () => setSidebarOpen(!isSidebarOpen),
226
+ children: /* @__PURE__ */ jsx(SidebarIcon, {})
227
+ }) }), /* @__PURE__ */ jsx(TooltipPortal, { children: /* @__PURE__ */ jsx(TooltipPositioner, {
228
+ side: "bottom",
229
+ sideOffset: 6,
230
+ children: /* @__PURE__ */ jsx(TooltipPopup, { children: toggleLabel })
231
+ }) })] })
232
+ })
233
+ }),
234
+ isSidebarOpen ? /* @__PURE__ */ jsx("button", {
235
+ type: "button",
236
+ role: "separator",
237
+ "aria-label": "Resize threads panel",
238
+ "aria-orientation": "vertical",
239
+ "aria-valuemin": threadSidebarMinWidth,
240
+ "aria-valuemax": threadSidebarMaxWidth,
241
+ "aria-valuenow": sidebarWidth,
242
+ className: "think-thread-shell__resizer",
243
+ onPointerDown: (event) => beginThreadSidebarResize(event, sidebarWidth, setSidebarWidth),
244
+ onKeyDown: (event) => {
245
+ if (event.key === "ArrowLeft") {
246
+ event.preventDefault();
247
+ resizeThreadSidebarBy(-24);
248
+ } else if (event.key === "ArrowRight") {
249
+ event.preventDefault();
250
+ resizeThreadSidebarBy(threadSidebarKeyboardStep);
251
+ } else if (event.key === "Home") {
252
+ event.preventDefault();
253
+ setSidebarWidth(threadSidebarMinWidth);
254
+ } else if (event.key === "End") {
255
+ event.preventDefault();
256
+ setSidebarWidth(threadSidebarMaxWidth);
257
+ }
258
+ }
259
+ }) : null,
260
+ /* @__PURE__ */ jsx("div", {
261
+ id: sidebarId,
262
+ className: "think-thread-shell__sidebar",
263
+ "aria-hidden": isSidebarOpen ? void 0 : true,
264
+ inert: isSidebarOpen ? void 0 : true,
265
+ children: sidebar
266
+ }),
267
+ /* @__PURE__ */ jsx("div", {
268
+ className: "think-thread-shell__main",
269
+ children
270
+ }),
271
+ isSidebarOpen ? /* @__PURE__ */ jsx("button", {
272
+ type: "button",
273
+ className: "think-thread-shell__peek",
274
+ "aria-label": "Show selected thread",
275
+ onClick: () => setSidebarOpen(false)
276
+ }) : null
277
+ ]
278
+ })
279
+ });
280
+ }
281
+ //#endregion
282
+ export { ThinkThreadPicker, ThinkThreadShell };
@@ -1,5 +1,5 @@
1
1
  import { t as cx } from "../class-names-9UtZwb0J.js";
2
- import { O as RefreshIcon, n as ArrowLeftIcon, o as ChevronDownIcon, r as ArrowRightIcon } from "../icons-BCNawx-T.js";
2
+ import { k as RefreshIcon, n as ArrowLeftIcon, o as ChevronDownIcon, r as ArrowRightIcon } from "../icons-D2Q5n5C1.js";
3
3
  import { Button } from "./Button.js";
4
4
  import { Tooltip, TooltipPopup, TooltipPortal, TooltipPositioner, TooltipTrigger } from "./Tooltip.js";
5
5
  import { Badge } from "./Badge.js";
@@ -1,8 +1,8 @@
1
1
  import { t as cx } from "../class-names-9UtZwb0J.js";
2
- import { O as RefreshIcon, k as SearchIcon, m as FileIcon, n as ArrowLeftIcon } from "../icons-BCNawx-T.js";
2
+ import { A as SearchIcon, h as FileIcon, k as RefreshIcon, n as ArrowLeftIcon } from "../icons-D2Q5n5C1.js";
3
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-BK0sOAbV.js";
5
- import { i as FileTreeFolder, r as FileTreeFile, t as FileTree } from "../FileTree-BWQZ0FSi.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-XNQOupQ3.js";
5
+ import { i as FileTreeFolder, r as FileTreeFile, t as FileTree } from "../FileTree-wMyZhhHO.js";
6
6
  import * as React from "react";
7
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
8
  //#region src/components/WorkspaceExplorer.tsx
@@ -193,7 +193,8 @@ const LANGUAGE_BY_EXTENSION = {
193
193
  };
194
194
  /** Best-effort syntax-highlighting language from a file path; unknown extensions render as plain text. */
195
195
  function inferWorkspaceLanguage(path) {
196
- return LANGUAGE_BY_EXTENSION[basename(path).split(".").at(-1)?.toLowerCase() ?? ""] ?? "text";
196
+ const extension = basename(path).split(".").at(-1)?.toLowerCase() ?? "";
197
+ return LANGUAGE_BY_EXTENSION[extension] ?? "text";
197
198
  }
198
199
  /**
199
200
  * A Think workspace file browser with a preview pane: lazy directory tree,
@@ -1,2 +1,2 @@
1
- import { A as SearchIcon, C as MonitorIcon, D as PlayIcon, E as PencilIcon, F as WrenchIcon, I as XIcon, M as SquareIcon, N as TerminalIcon, O as PlusIcon, P as WarningIcon, S as MinusIcon, T as PauseIcon, _ as IconProps, a as CheckIcon, b as LinkIcon, c as ChevronRightIcon, d as DownloadIcon, f as EyeIcon, g as FolderOpenIcon, h as FolderIcon, i as ArrowUpIcon, j as SpinnerIcon, k as RefreshIcon, l as CopyIcon, m as FileIcon, n as ArrowLeftIcon, o as ChevronDownIcon, p as EyeOffIcon, r as ArrowRightIcon, s as ChevronLeftIcon, t as ArrowDownIcon, u as DotIcon, v as ImageIcon, w as PaperclipIcon, x as MaximizeIcon, y as InfoIcon } from "../icons-BYpJY6L2.js";
2
- export { ArrowDownIcon, ArrowLeftIcon, ArrowRightIcon, ArrowUpIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, CopyIcon, DotIcon, DownloadIcon, EyeIcon, EyeOffIcon, FileIcon, FolderIcon, FolderOpenIcon, IconProps, ImageIcon, InfoIcon, LinkIcon, MaximizeIcon, MinusIcon, MonitorIcon, PaperclipIcon, PauseIcon, PencilIcon, PlayIcon, PlusIcon, RefreshIcon, SearchIcon, SpinnerIcon, SquareIcon, TerminalIcon, WarningIcon, WrenchIcon, XIcon };
1
+ import { A as RefreshIcon, C as MinusIcon, D as PencilIcon, E as PauseIcon, F as TerminalIcon, I as WarningIcon, L as WrenchIcon, M as SidebarIcon, N as SpinnerIcon, O as PlayIcon, P as SquareIcon, R as XIcon, S as MaximizeIcon, T as PaperclipIcon, _ as FolderOpenIcon, a as CheckIcon, b as InfoIcon, c as ChevronRightIcon, d as DownloadIcon, f as EllipsisIcon, g as FolderIcon, h as FileIcon, i as ArrowUpIcon, j as SearchIcon, k as PlusIcon, l as CopyIcon, m as EyeOffIcon, n as ArrowLeftIcon, o as ChevronDownIcon, p as EyeIcon, r as ArrowRightIcon, s as ChevronLeftIcon, t as ArrowDownIcon, u as DotIcon, v as IconProps, w as MonitorIcon, x as LinkIcon, y as ImageIcon } from "../icons-CbjtbIJK.js";
2
+ export { ArrowDownIcon, ArrowLeftIcon, ArrowRightIcon, ArrowUpIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, CopyIcon, DotIcon, DownloadIcon, EllipsisIcon, EyeIcon, EyeOffIcon, FileIcon, FolderIcon, FolderOpenIcon, IconProps, ImageIcon, InfoIcon, LinkIcon, MaximizeIcon, MinusIcon, MonitorIcon, PaperclipIcon, PauseIcon, PencilIcon, PlayIcon, PlusIcon, RefreshIcon, SearchIcon, SidebarIcon, SpinnerIcon, SquareIcon, TerminalIcon, WarningIcon, WrenchIcon, XIcon };
@@ -1,2 +1,2 @@
1
- import { A as SpinnerIcon, C as PaperclipIcon, D as PlusIcon, E as PlayIcon, F as XIcon, M as TerminalIcon, N as WarningIcon, O as RefreshIcon, P as WrenchIcon, S as MonitorIcon, T as PencilIcon, _ as ImageIcon, a as CheckIcon, b as MaximizeIcon, c as ChevronRightIcon, d as DownloadIcon, f as EyeIcon, g as FolderOpenIcon, h as FolderIcon, i as ArrowUpIcon, j as SquareIcon, k as SearchIcon, l as CopyIcon, m as FileIcon, n as ArrowLeftIcon, o as ChevronDownIcon, p as EyeOffIcon, r as ArrowRightIcon, s as ChevronLeftIcon, t as ArrowDownIcon, u as DotIcon, v as InfoIcon, w as PauseIcon, x as MinusIcon, y as LinkIcon } from "../icons-BCNawx-T.js";
2
- export { ArrowDownIcon, ArrowLeftIcon, ArrowRightIcon, ArrowUpIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, CopyIcon, DotIcon, DownloadIcon, EyeIcon, EyeOffIcon, FileIcon, FolderIcon, FolderOpenIcon, ImageIcon, InfoIcon, LinkIcon, MaximizeIcon, MinusIcon, MonitorIcon, PaperclipIcon, PauseIcon, PencilIcon, PlayIcon, PlusIcon, RefreshIcon, SearchIcon, SpinnerIcon, SquareIcon, TerminalIcon, WarningIcon, WrenchIcon, XIcon };
1
+ import { A as SearchIcon, C as MonitorIcon, D as PlayIcon, E as PencilIcon, F as WarningIcon, I as WrenchIcon, L as XIcon, M as SpinnerIcon, N as SquareIcon, O as PlusIcon, P as TerminalIcon, S as MinusIcon, T as PauseIcon, _ as FolderOpenIcon, a as CheckIcon, b as LinkIcon, c as ChevronRightIcon, d as DownloadIcon, f as EllipsisIcon, g as FolderIcon, h as FileIcon, i as ArrowUpIcon, j as SidebarIcon, k as RefreshIcon, l as CopyIcon, m as EyeOffIcon, n as ArrowLeftIcon, o as ChevronDownIcon, p as EyeIcon, r as ArrowRightIcon, s as ChevronLeftIcon, t as ArrowDownIcon, u as DotIcon, v as ImageIcon, w as PaperclipIcon, x as MaximizeIcon, y as InfoIcon } from "../icons-D2Q5n5C1.js";
2
+ export { ArrowDownIcon, ArrowLeftIcon, ArrowRightIcon, ArrowUpIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, CopyIcon, DotIcon, DownloadIcon, EllipsisIcon, EyeIcon, EyeOffIcon, FileIcon, FolderIcon, FolderOpenIcon, ImageIcon, InfoIcon, LinkIcon, MaximizeIcon, MinusIcon, MonitorIcon, PaperclipIcon, PauseIcon, PencilIcon, PlayIcon, PlusIcon, RefreshIcon, SearchIcon, SidebarIcon, SpinnerIcon, SquareIcon, TerminalIcon, WarningIcon, WrenchIcon, XIcon };