@mcp-b/react-components 0.8.0 → 0.9.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.
- package/dist/{AgentChat-403mckDG.d.ts → AgentChat-B9LD_WF1.d.ts} +67 -2
- package/dist/{ThinkChatActivity-CeHzk6-K.d.ts → ThinkChatActivity-B2UD95My.d.ts} +1 -1
- package/dist/components/AgentChat.d.ts +1 -1
- package/dist/components/AgentChat.js +95 -1
- package/dist/components/ThinkChat.d.ts +2 -2
- package/dist/components/ThinkChatActivity.d.ts +1 -1
- package/dist/components/ThinkFullApp.d.ts +18 -2
- package/dist/components/ThinkFullApp.js +63 -48
- package/package.json +2 -2
|
@@ -17,7 +17,60 @@ interface AgentChatRenderMessageContext<MessageT extends UIMessage = UIMessage>
|
|
|
17
17
|
}
|
|
18
18
|
interface AgentChatProps<MessageT extends UIMessage = UIMessage> {
|
|
19
19
|
className?: string;
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* AI SDK `UIMessage`s to render, oldest first.
|
|
22
|
+
*
|
|
23
|
+
* `AgentChat` intentionally renders the upstream message objects in place:
|
|
24
|
+
* it does not normalize them into a local transcript model. That keeps AI SDK
|
|
25
|
+
* parts such as `text`, `reasoning`, `file`, `tool-*`, `source-url`,
|
|
26
|
+
* `source-document`, and `step-start` aligned with the types exported from
|
|
27
|
+
* `ai`. Cloudflare Think also returns these same UI messages through
|
|
28
|
+
* `useAgentChat`, with Agents session storage applying compaction overlays to
|
|
29
|
+
* reads before they reach the hook.
|
|
30
|
+
*
|
|
31
|
+
* Backend setup for compaction is owned by Think/Agents, not this component:
|
|
32
|
+
* configure the agent session with `Session.create(this).onCompaction(...)
|
|
33
|
+
* .compactAfter(...)`. Agents' Sessions docs describe that `getHistory()`
|
|
34
|
+
* replaces a compacted range with a synthetic message whose id starts with
|
|
35
|
+
* `compaction_`; this component uses Agents' exported
|
|
36
|
+
* `isCompactionMessage` helper to render that synthetic message as a quiet
|
|
37
|
+
* separator. Think's context-overflow recovery is a separate opt-in layer:
|
|
38
|
+
* set `contextOverflow` and `classifyChatError` on the Think agent if the
|
|
39
|
+
* backend should compact and retry/proactively guard a too-large in-flight
|
|
40
|
+
* turn.
|
|
41
|
+
*
|
|
42
|
+
* @example Think session compaction setup
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { Think, defaultContextOverflowClassifier } from "@cloudflare/think";
|
|
45
|
+
* import { Session } from "agents/experimental/memory/session";
|
|
46
|
+
* import { createCompactFunction } from "agents/experimental/memory/utils";
|
|
47
|
+
* import { generateText } from "ai";
|
|
48
|
+
*
|
|
49
|
+
* export class MyAgent extends Think<Env> {
|
|
50
|
+
* override configureSession(session: Session) {
|
|
51
|
+
* return session
|
|
52
|
+
* .onCompaction(
|
|
53
|
+
* createCompactFunction({
|
|
54
|
+
* summarize: async (prompt) =>
|
|
55
|
+
* (await generateText({ model: this.env.MODEL, prompt })).text,
|
|
56
|
+
* protectHead: 3,
|
|
57
|
+
* tailTokenBudget: 20_000,
|
|
58
|
+
* }),
|
|
59
|
+
* )
|
|
60
|
+
* .compactAfter(100_000);
|
|
61
|
+
* }
|
|
62
|
+
*
|
|
63
|
+
* override contextOverflow = {
|
|
64
|
+
* reactive: true,
|
|
65
|
+
* proactive: { maxInputTokens: 200_000 },
|
|
66
|
+
* };
|
|
67
|
+
* override classifyChatError = defaultContextOverflowClassifier;
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @see https://github.com/cloudflare/agents/blob/main/docs/agents/sessions.md#compaction
|
|
72
|
+
* @see https://github.com/cloudflare/agents/blob/main/docs/think/index.md#context-window-overflow-recovery
|
|
73
|
+
*/
|
|
21
74
|
messages: readonly MessageT[];
|
|
22
75
|
/** Drives the composer's send/stop affordance and streaming indicators. */
|
|
23
76
|
status?: ChatStatus;
|
|
@@ -64,7 +117,19 @@ interface AgentChatProps<MessageT extends UIMessage = UIMessage> {
|
|
|
64
117
|
maxTokens: number;
|
|
65
118
|
modelId?: string;
|
|
66
119
|
};
|
|
67
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* Override rendering per part; return `undefined` to fall back to the default
|
|
122
|
+
* AI SDK/Think-aware part UI.
|
|
123
|
+
*
|
|
124
|
+
* The default renderer uses upstream helpers where available:
|
|
125
|
+
* `isTextUIPart`, `isReasoningUIPart`, `isFileUIPart`, `isToolUIPart`,
|
|
126
|
+
* `getToolName`, and Think's `getToolPartState`/input/output accessors. It
|
|
127
|
+
* also renders native AI SDK `source-url`/`source-document` parts as a single
|
|
128
|
+
* grouped `Sources` block when they appear consecutively, and native
|
|
129
|
+
* `step-start` parts as step separators. If you override source rendering,
|
|
130
|
+
* return a React node for every source part you want to show; returning
|
|
131
|
+
* `undefined` lets `AgentChat` keep the upstream-shaped source-run grouping.
|
|
132
|
+
*/
|
|
68
133
|
renderPart?: (context: AgentChatRenderPartContext<MessageT>) => React.ReactNode | undefined;
|
|
69
134
|
/** Rendered after each message's parts — for actions, citations, and the like. */
|
|
70
135
|
renderMessageAfter?: (context: AgentChatRenderMessageContext<MessageT>) => React.ReactNode;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as AgentChatRenderPartContext, r as AgentChatProps } from "./AgentChat-
|
|
1
|
+
import { a as AgentChatRenderPartContext, r as AgentChatProps } from "./AgentChat-B9LD_WF1.js";
|
|
2
2
|
import { s as ThinkChatPart } from "./ThinkChatToolParts-DnUiZgCC.js";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { DynamicToolUIPart, ToolUIPart, UIMessage } from "ai";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as AgentChatRenderPartContext, i as AgentChatRenderMessageContext, n as AgentChatPart, r as AgentChatProps, t as AgentChat } from "../AgentChat-
|
|
1
|
+
import { a as AgentChatRenderPartContext, i as AgentChatRenderMessageContext, n as AgentChatPart, r as AgentChatProps, t as AgentChat } from "../AgentChat-B9LD_WF1.js";
|
|
2
2
|
export { AgentChat, AgentChatPart, AgentChatProps, AgentChatRenderMessageContext, AgentChatRenderPartContext };
|
|
@@ -5,11 +5,14 @@ import { Context, ContextContent, ContextContentBody, ContextContentHeader, Cont
|
|
|
5
5
|
import { MessageScroller, MessageScrollerButton, MessageScrollerContent, MessageScrollerItem, MessageScrollerProvider, MessageScrollerViewport } from "./MessageScroller.js";
|
|
6
6
|
import { ConversationEmptyState } from "./Conversation.js";
|
|
7
7
|
import { Message, MessageAction, MessageActions, MessageAttachment, MessageContent, MessageResponse } from "./Message.js";
|
|
8
|
+
import { Marker, MarkerContent } from "./Marker.js";
|
|
8
9
|
import { MenuGroup, MenuGroupLabel } from "./Menu.js";
|
|
9
10
|
import { PromptInput, PromptInputActionAddAttachments, PromptInputActionMenu, PromptInputActionMenuContent, PromptInputActionMenuTrigger, PromptInputApproval, PromptInputAttachments, PromptInputBody, PromptInputButton, PromptInputFooter, PromptInputPermissionSelector, PromptInputSubmit, PromptInputTextarea, PromptInputTools, getApprovalRequestId, getApprovalStateKey, getPendingToolApprovals, usePromptInputAttachments } from "./PromptInput.js";
|
|
10
11
|
import { Reasoning, ReasoningContent, ReasoningTrigger } from "./Reasoning.js";
|
|
12
|
+
import { Source, Sources, SourcesContent, SourcesTrigger } from "./Sources.js";
|
|
11
13
|
import * as React from "react";
|
|
12
14
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
15
|
+
import { isCompactionMessage } from "agents/experimental/memory/utils";
|
|
13
16
|
import { getToolInput, getToolOutput, getToolPartState } from "@cloudflare/think/react";
|
|
14
17
|
import { getToolName, isFileUIPart, isReasoningUIPart, isTextUIPart, isToolUIPart } from "ai";
|
|
15
18
|
//#region src/components/AgentChat.tsx
|
|
@@ -33,6 +36,21 @@ function AgentChatAddMenu({ children }) {
|
|
|
33
36
|
children: [/* @__PURE__ */ jsxs(MenuGroup, { children: [/* @__PURE__ */ jsx(MenuGroupLabel, { children: "Files" }), /* @__PURE__ */ jsx(PromptInputActionAddAttachments, {})] }), children]
|
|
34
37
|
})] });
|
|
35
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Per-message row-size compaction marker from `AIChatAgent` persistence.
|
|
41
|
+
*
|
|
42
|
+
* This is distinct from Session history compaction overlays. When a single
|
|
43
|
+
* SQLite row approaches the Workers/SQLite size limit, Agents compacts large
|
|
44
|
+
* tool outputs and, if necessary, truncates text before persistence. The
|
|
45
|
+
* message remains in place and receives metadata keys documented by Agents:
|
|
46
|
+
* `compactedToolOutputs` lists tool-call ids whose outputs were summarized;
|
|
47
|
+
* `compactedTextParts` lists text-part indexes that were truncated. The UI
|
|
48
|
+
* reads those keys structurally because they are persisted metadata rather than
|
|
49
|
+
* exported AI SDK part variants, then adds a local visual note while preserving
|
|
50
|
+
* the original `UIMessage` and part shapes.
|
|
51
|
+
*
|
|
52
|
+
* @see https://github.com/cloudflare/agents/blob/main/docs/agents/chat-agents.md#row-size-protection
|
|
53
|
+
*/
|
|
36
54
|
function isCompactedPart(message, part, partIndex) {
|
|
37
55
|
const metadata = message.metadata;
|
|
38
56
|
if (isTextUIPart(part)) return Array.isArray(metadata?.compactedTextParts) && metadata.compactedTextParts.includes(partIndex);
|
|
@@ -60,6 +78,69 @@ function isToolPartFailed(part) {
|
|
|
60
78
|
const state = getToolPartState(part);
|
|
61
79
|
return state === "error" || state === "denied";
|
|
62
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* AI SDK exports concrete source part types but no public source-part guard.
|
|
83
|
+
* Keep these local guards as exact discriminant checks, matching
|
|
84
|
+
* `ai/src/ui/ui-messages.ts` and `ai/src/ui/process-ui-message-stream.ts`.
|
|
85
|
+
* If AI SDK exports source or step guards later, replace these wrappers rather
|
|
86
|
+
* than widening the local transcript contract.
|
|
87
|
+
*/
|
|
88
|
+
function isSourceUrlPart(part) {
|
|
89
|
+
return part?.type === "source-url";
|
|
90
|
+
}
|
|
91
|
+
function isSourceDocumentPart(part) {
|
|
92
|
+
return part?.type === "source-document";
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* AI SDK emits `step-start` as a native `StepStartUIPart`, but currently exports
|
|
96
|
+
* only the type, not an `isStepStartUIPart` helper. Keep this as the exact
|
|
97
|
+
* upstream discriminant so step separators do not become a local protocol.
|
|
98
|
+
*/
|
|
99
|
+
function isStepStartPart(part) {
|
|
100
|
+
return part.type === "step-start";
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Session compaction overlay marker from Agents Sessions.
|
|
104
|
+
*
|
|
105
|
+
* Agents exports `isCompactionMessage` from
|
|
106
|
+
* `agents/experimental/memory/utils`; it checks the canonical `compaction_`
|
|
107
|
+
* id prefix used when `Session.getHistory()` materializes a summary overlay.
|
|
108
|
+
* `UIMessage` is structurally compatible with the fields the helper reads, so
|
|
109
|
+
* keep this wrapper instead of duplicating the prefix test locally.
|
|
110
|
+
*/
|
|
111
|
+
function isCompactionMessage$1(message) {
|
|
112
|
+
return isCompactionMessage(message);
|
|
113
|
+
}
|
|
114
|
+
function getMessageText(message) {
|
|
115
|
+
return message.parts.filter((part) => isTextUIPart(part) || isReasoningUIPart(part)).map((part) => part.text).join("");
|
|
116
|
+
}
|
|
117
|
+
function formatSourceTitle(part) {
|
|
118
|
+
if (part.title) return part.title;
|
|
119
|
+
if (part.type === "source-document") return part.filename ?? part.mediaType;
|
|
120
|
+
try {
|
|
121
|
+
return new URL(part.url).hostname.replace(/^www\./, "");
|
|
122
|
+
} catch {
|
|
123
|
+
return part.url;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function renderSources(parts) {
|
|
127
|
+
return /* @__PURE__ */ jsxs(Sources, { children: [/* @__PURE__ */ jsx(SourcesTrigger, { count: parts.length }), /* @__PURE__ */ jsx(SourcesContent, { children: parts.map((part) => part.type === "source-url" ? /* @__PURE__ */ jsx(Source, {
|
|
128
|
+
href: part.url,
|
|
129
|
+
title: formatSourceTitle(part)
|
|
130
|
+
}, part.sourceId) : /* @__PURE__ */ jsx(Source, {
|
|
131
|
+
title: formatSourceTitle(part),
|
|
132
|
+
description: part.filename ?? part.mediaType
|
|
133
|
+
}, part.sourceId)) })] });
|
|
134
|
+
}
|
|
135
|
+
function getSourceRun(parts, start) {
|
|
136
|
+
const sources = [];
|
|
137
|
+
for (let index = start; index < parts.length; index++) {
|
|
138
|
+
const candidate = parts[index];
|
|
139
|
+
if (!isSourceUrlPart(candidate) && !isSourceDocumentPart(candidate)) break;
|
|
140
|
+
sources.push(candidate);
|
|
141
|
+
}
|
|
142
|
+
return sources;
|
|
143
|
+
}
|
|
63
144
|
function describeToolPart(part) {
|
|
64
145
|
const name = getToolName(part);
|
|
65
146
|
const verb = isToolPartFailed(part) ? `${name} failed` : isToolPartWorking(part) ? `Running ${name}` : `Ran ${name}`;
|
|
@@ -96,6 +177,10 @@ function renderDefaultPart(part) {
|
|
|
96
177
|
children: [/* @__PURE__ */ jsx(ReasoningTrigger, {}), /* @__PURE__ */ jsx(ReasoningContent, { children: part.text })]
|
|
97
178
|
});
|
|
98
179
|
if (isToolUIPart(part)) return renderToolActivity(part);
|
|
180
|
+
if (isStepStartPart(part)) return /* @__PURE__ */ jsx(Marker, {
|
|
181
|
+
variant: "separator",
|
|
182
|
+
children: /* @__PURE__ */ jsx(MarkerContent, { children: "Next step" })
|
|
183
|
+
});
|
|
99
184
|
return null;
|
|
100
185
|
}
|
|
101
186
|
/**
|
|
@@ -168,7 +253,10 @@ function AgentChat({ className, messages, status = "ready", onSubmit, onStop, pl
|
|
|
168
253
|
}) : null, messages.map((message, messageIndex) => /* @__PURE__ */ jsx(MessageScrollerItem, {
|
|
169
254
|
messageId: message.id,
|
|
170
255
|
scrollAnchor: message.role === "user",
|
|
171
|
-
children: /* @__PURE__ */
|
|
256
|
+
children: isCompactionMessage$1(message) ? /* @__PURE__ */ jsx(Marker, {
|
|
257
|
+
variant: "separator",
|
|
258
|
+
children: /* @__PURE__ */ jsx(MarkerContent, { children: getMessageText(message) || "Conversation compacted" })
|
|
259
|
+
}) : /* @__PURE__ */ jsxs(Message, {
|
|
172
260
|
from: message.role,
|
|
173
261
|
children: [
|
|
174
262
|
message.parts.map((part, partIndex) => {
|
|
@@ -179,6 +267,12 @@ function AgentChat({ className, messages, status = "ready", onSubmit, onStop, pl
|
|
|
179
267
|
partIndex,
|
|
180
268
|
defaultRender
|
|
181
269
|
});
|
|
270
|
+
if (isSourceUrlPart(part) || isSourceDocumentPart(part)) {
|
|
271
|
+
if (custom !== void 0) return custom ? /* @__PURE__ */ jsx(MessageContent, { children: custom }, getPartKey(part, partIndex)) : null;
|
|
272
|
+
const previous = message.parts[partIndex - 1];
|
|
273
|
+
if (isSourceUrlPart(previous) || isSourceDocumentPart(previous)) return null;
|
|
274
|
+
return /* @__PURE__ */ jsx(MessageContent, { children: renderSources(getSourceRun(message.parts, partIndex)) }, getPartKey(part, partIndex));
|
|
275
|
+
}
|
|
182
276
|
const rendered = custom === void 0 ? defaultRender() : custom;
|
|
183
277
|
return rendered ? /* @__PURE__ */ jsxs(MessageContent, { children: [rendered, isCompactedPart(message, part, partIndex) ? /* @__PURE__ */ jsx("div", {
|
|
184
278
|
className: "message__compacted",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { n as ExecutionApprovals } from "../PromptInput-D6-G24p7.js";
|
|
2
|
-
import { a as AgentChatRenderPartContext, r as AgentChatProps } from "../AgentChat-
|
|
2
|
+
import { a as AgentChatRenderPartContext, r as AgentChatProps } from "../AgentChat-B9LD_WF1.js";
|
|
3
3
|
import { n as BrowserLiveViewProps, t as BrowserLiveView } from "../BrowserLiveView-ZktFZiMD.js";
|
|
4
4
|
import { n as BrowserReplayProps, t as BrowserReplay } from "../BrowserReplay-6G-mnwcn.js";
|
|
5
5
|
import { _ as isThinkChatActivityPart } from "../ThinkChatToolParts-DnUiZgCC.js";
|
|
6
|
-
import { a as ThinkChatToolRenderers, c as renderThinkChatActivityPart, i as ThinkChatToolRenderer, n as ThinkChatActivityProps, o as createThinkChatActivityRenderer, r as ThinkChatActivityRendererOptions, s as describeThinkChatActivityPart, t as ThinkChatActivity } from "../ThinkChatActivity-
|
|
6
|
+
import { a as ThinkChatToolRenderers, c as renderThinkChatActivityPart, i as ThinkChatToolRenderer, n as ThinkChatActivityProps, o as createThinkChatActivityRenderer, r as ThinkChatActivityRendererOptions, s as describeThinkChatActivityPart, t as ThinkChatActivity } from "../ThinkChatActivity-B2UD95My.js";
|
|
7
7
|
import { n as ThinkChatValue } from "../ThinkChatContext-BLAAb8J5.js";
|
|
8
8
|
import { FileInfo, UseWorkspaceOptions, WorkspaceExplorer, WorkspaceExplorerProps, WorkspaceFileState, WorkspaceInfo, WorkspaceSource, WorkspaceState, inferWorkspaceLanguage, useWorkspace } from "./WorkspaceExplorer.js";
|
|
9
9
|
import * as React from "react";
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { _ as isThinkChatActivityPart } from "../ThinkChatToolParts-DnUiZgCC.js";
|
|
2
|
-
import { a as ThinkChatToolRenderers, c as renderThinkChatActivityPart, i as ThinkChatToolRenderer, n as ThinkChatActivityProps, o as createThinkChatActivityRenderer, r as ThinkChatActivityRendererOptions, s as describeThinkChatActivityPart, t as ThinkChatActivity } from "../ThinkChatActivity-
|
|
2
|
+
import { a as ThinkChatToolRenderers, c as renderThinkChatActivityPart, i as ThinkChatToolRenderer, n as ThinkChatActivityProps, o as createThinkChatActivityRenderer, r as ThinkChatActivityRendererOptions, s as describeThinkChatActivityPart, t as ThinkChatActivity } from "../ThinkChatActivity-B2UD95My.js";
|
|
3
3
|
export { ThinkChatActivity, ThinkChatActivityProps, ThinkChatActivityRendererOptions, ThinkChatToolRenderer, ThinkChatToolRenderers, createThinkChatActivityRenderer, describeThinkChatActivityPart, isThinkChatActivityPart, renderThinkChatActivityPart };
|
|
@@ -9,7 +9,20 @@ import { Think } from "@cloudflare/think";
|
|
|
9
9
|
|
|
10
10
|
//#region src/components/ThinkFullApp.d.ts
|
|
11
11
|
type ThinkFullAppAgent<State = unknown> = Think<object, State>;
|
|
12
|
-
type ThinkFullAppWorkbenchView = "files" | "execution";
|
|
12
|
+
type ThinkFullAppWorkbenchView = "files" | "execution" | (string & {});
|
|
13
|
+
interface ThinkFullAppPanelContext<AgentT extends ThinkFullAppAgent<State>, State = unknown> {
|
|
14
|
+
agent: ReturnType<typeof useAgent<AgentT, State>>;
|
|
15
|
+
chat: ReturnType<typeof useAgentChat<State, UIMessage>>;
|
|
16
|
+
workspace?: WorkspaceSource;
|
|
17
|
+
workspaceRoot?: string;
|
|
18
|
+
}
|
|
19
|
+
interface ThinkFullAppPanel<AgentT extends ThinkFullAppAgent<State>, State = unknown> {
|
|
20
|
+
/** Unique panel id used by `workbenchView`; custom panels should not reuse built-in ids. */
|
|
21
|
+
id: ThinkFullAppWorkbenchView;
|
|
22
|
+
label: string;
|
|
23
|
+
icon: React.ReactNode;
|
|
24
|
+
render: (context: ThinkFullAppPanelContext<AgentT, State>) => React.ReactNode;
|
|
25
|
+
}
|
|
13
26
|
interface ThinkFullAppProps<AgentT extends ThinkFullAppAgent<State>, State = unknown> extends React.HTMLAttributes<HTMLDivElement> {
|
|
14
27
|
/** The `useAgent` value from `agents/react`; its stub drives approvals and execution records. */
|
|
15
28
|
agent: ReturnType<typeof useAgent<AgentT, State>>;
|
|
@@ -21,6 +34,8 @@ interface ThinkFullAppProps<AgentT extends ThinkFullAppAgent<State>, State = unk
|
|
|
21
34
|
workspaceRoot?: string;
|
|
22
35
|
/** Message rendering labels and approval overrides for ThinkChat. */
|
|
23
36
|
messages?: ThinkChatMessagesProps;
|
|
37
|
+
/** Extra workbench panels appended after the built-in Files and Execution panels. */
|
|
38
|
+
workbenchPanels?: readonly ThinkFullAppPanel<AgentT, State>[];
|
|
24
39
|
/** Optional parent-owned thread picker/sidebar rendered beside the selected child app. */
|
|
25
40
|
threadSidebar?: React.ReactNode;
|
|
26
41
|
/** Whether the thread sidebar is open. Omit for internal state. */
|
|
@@ -50,6 +65,7 @@ declare function ThinkFullApp<AgentT extends ThinkFullAppAgent<State>, State = u
|
|
|
50
65
|
style,
|
|
51
66
|
threadSidebar,
|
|
52
67
|
threadSidebarOpen,
|
|
68
|
+
workbenchPanels,
|
|
53
69
|
workbenchView,
|
|
54
70
|
workspace,
|
|
55
71
|
workspaceRoot,
|
|
@@ -58,4 +74,4 @@ declare function ThinkFullApp<AgentT extends ThinkFullAppAgent<State>, State = u
|
|
|
58
74
|
ref?: React.Ref<HTMLDivElement>;
|
|
59
75
|
}): React.JSX.Element;
|
|
60
76
|
//#endregion
|
|
61
|
-
export { type ThinkExecutionAgent, ThinkExecutionWorkbench, type ThinkExecutionWorkbenchProps, type ThinkExecutionWorkbenchSource, ThinkFullApp, ThinkFullAppAgent, ThinkFullAppProps, ThinkFullAppWorkbenchView };
|
|
77
|
+
export { type ThinkExecutionAgent, ThinkExecutionWorkbench, type ThinkExecutionWorkbenchProps, type ThinkExecutionWorkbenchSource, ThinkFullApp, ThinkFullAppAgent, ThinkFullAppPanel, ThinkFullAppPanelContext, ThinkFullAppProps, ThinkFullAppWorkbenchView };
|
|
@@ -68,8 +68,8 @@ function WorkbenchViewItem({ children, label, value }) {
|
|
|
68
68
|
children: /* @__PURE__ */ jsx(TooltipPopup, { children: label })
|
|
69
69
|
}) })] });
|
|
70
70
|
}
|
|
71
|
-
function WorkbenchViewSwitch({ value, onValueChange }) {
|
|
72
|
-
return /* @__PURE__ */
|
|
71
|
+
function WorkbenchViewSwitch({ panels, value, onValueChange }) {
|
|
72
|
+
return /* @__PURE__ */ jsx(ToggleGroup, {
|
|
73
73
|
className: "think-full-app__workbench-view-switch",
|
|
74
74
|
size: "sm",
|
|
75
75
|
value: [value],
|
|
@@ -78,36 +78,15 @@ function WorkbenchViewSwitch({ value, onValueChange }) {
|
|
|
78
78
|
const [next] = nextValue;
|
|
79
79
|
if (next) onValueChange(next);
|
|
80
80
|
},
|
|
81
|
-
children:
|
|
82
|
-
value:
|
|
83
|
-
label:
|
|
84
|
-
children:
|
|
85
|
-
}
|
|
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"
|
|
81
|
+
children: panels.map((panel) => /* @__PURE__ */ jsx(WorkbenchViewItem, {
|
|
82
|
+
value: panel.id,
|
|
83
|
+
label: panel.label,
|
|
84
|
+
children: panel.icon
|
|
85
|
+
}, panel.id))
|
|
107
86
|
});
|
|
108
87
|
}
|
|
109
88
|
/** 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 }) {
|
|
89
|
+
function ThinkFullApp({ agent, chat, className, defaultWorkbenchView, defaultThreadSidebarOpen, messages, onThreadSidebarOpenChange, onWorkbenchViewChange, ref, style, threadSidebar, threadSidebarOpen, workbenchPanels, workbenchView, workspace, workspaceRoot, ...props }) {
|
|
111
90
|
const approvals = useThinkPendingApprovals(agent.stub, `${chat.messages.length}:${chat.status}`);
|
|
112
91
|
const resolvedMessages = React.useMemo(() => ({
|
|
113
92
|
...messages,
|
|
@@ -118,14 +97,55 @@ function ThinkFullApp({ agent, chat, className, defaultWorkbenchView, defaultThr
|
|
|
118
97
|
approvals.pendingApprovals,
|
|
119
98
|
messages
|
|
120
99
|
]);
|
|
121
|
-
const hasWorkspace = workspace !== void 0;
|
|
122
100
|
const [mobileView, setMobileView] = React.useState("chat");
|
|
123
101
|
const [innerWorkbenchView, setInnerWorkbenchView] = React.useState(defaultWorkbenchView ?? "files");
|
|
124
102
|
const [workbenchPanelWidth, setWorkbenchPanelWidth] = React.useState(workbenchPanelDefaultWidth);
|
|
103
|
+
const hasWorkspace = workspace !== void 0;
|
|
104
|
+
const panelContext = React.useMemo(() => ({
|
|
105
|
+
agent,
|
|
106
|
+
chat,
|
|
107
|
+
workspace,
|
|
108
|
+
workspaceRoot
|
|
109
|
+
}), [
|
|
110
|
+
agent,
|
|
111
|
+
chat,
|
|
112
|
+
workspace,
|
|
113
|
+
workspaceRoot
|
|
114
|
+
]);
|
|
115
|
+
const availableWorkbenchPanels = React.useMemo(() => {
|
|
116
|
+
const builtInPanels = [];
|
|
117
|
+
if (workspace !== void 0) builtInPanels.push({
|
|
118
|
+
id: "files",
|
|
119
|
+
label: "Files",
|
|
120
|
+
icon: /* @__PURE__ */ jsx(FileIcon, {}),
|
|
121
|
+
render: () => /* @__PURE__ */ jsx(WorkspacePanel, {
|
|
122
|
+
source: workspace,
|
|
123
|
+
root: workspaceRoot
|
|
124
|
+
})
|
|
125
|
+
});
|
|
126
|
+
builtInPanels.push({
|
|
127
|
+
id: "execution",
|
|
128
|
+
label: "Execution",
|
|
129
|
+
icon: /* @__PURE__ */ jsx(TerminalIcon, {}),
|
|
130
|
+
render: ({ agent }) => /* @__PURE__ */ jsx(ThinkExecutionWorkbench, {
|
|
131
|
+
source: agent.stub,
|
|
132
|
+
title: "Execution"
|
|
133
|
+
})
|
|
134
|
+
});
|
|
135
|
+
return workbenchPanels ? [...builtInPanels, ...workbenchPanels] : builtInPanels;
|
|
136
|
+
}, [
|
|
137
|
+
workbenchPanels,
|
|
138
|
+
workspace,
|
|
139
|
+
workspaceRoot
|
|
140
|
+
]);
|
|
125
141
|
const activeMobileView = mobileView === "workbench" ? "workbench" : "chat";
|
|
126
142
|
const isMobileWorkbenchView = activeMobileView === "workbench";
|
|
127
|
-
const
|
|
128
|
-
const
|
|
143
|
+
const requestedWorkbenchView = workbenchView ?? innerWorkbenchView;
|
|
144
|
+
const activePanel = availableWorkbenchPanels.find((panel) => panel.id === requestedWorkbenchView) ?? availableWorkbenchPanels[0];
|
|
145
|
+
const activeWorkbenchView = activePanel?.id ?? "execution";
|
|
146
|
+
const mobileWorkbenchLabel = hasWorkspace && activeWorkbenchView === "files" ? "Show workbench" : `Show ${activePanel?.label.toLowerCase() ?? "workbench"}`;
|
|
147
|
+
const showWorkbenchViewSwitch = availableWorkbenchPanels.length > 1;
|
|
148
|
+
const showShellTopbar = showWorkbenchViewSwitch || isMobileWorkbenchView;
|
|
129
149
|
const rootStyle = {
|
|
130
150
|
...style,
|
|
131
151
|
"--think-full-app-workbench-w": `${workbenchPanelWidth}px`
|
|
@@ -134,21 +154,16 @@ function ThinkFullApp({ agent, chat, className, defaultWorkbenchView, defaultThr
|
|
|
134
154
|
setWorkbenchPanelWidth((current) => clampWorkbenchPanelWidth(current + delta));
|
|
135
155
|
};
|
|
136
156
|
const setWorkbenchView = React.useCallback((next) => {
|
|
137
|
-
if (!hasWorkspace) return;
|
|
138
157
|
if (workbenchView === void 0) setInnerWorkbenchView(next);
|
|
139
158
|
onWorkbenchViewChange?.(next);
|
|
140
|
-
}, [
|
|
141
|
-
hasWorkspace,
|
|
142
|
-
onWorkbenchViewChange,
|
|
143
|
-
workbenchView
|
|
144
|
-
]);
|
|
159
|
+
}, [onWorkbenchViewChange, workbenchView]);
|
|
145
160
|
const app = /* @__PURE__ */ jsxs("div", {
|
|
146
161
|
ref,
|
|
147
162
|
className: cx("think-full-app", className),
|
|
148
163
|
"data-has-files": hasWorkspace ? "true" : "false",
|
|
149
164
|
"data-has-thread-sidebar": threadSidebar ? "true" : "false",
|
|
150
165
|
"data-has-workbench": "true",
|
|
151
|
-
"data-has-workbench-topbar":
|
|
166
|
+
"data-has-workbench-topbar": showWorkbenchViewSwitch ? "true" : "false",
|
|
152
167
|
"data-mobile-view": activeMobileView,
|
|
153
168
|
style: rootStyle,
|
|
154
169
|
...props,
|
|
@@ -157,8 +172,9 @@ function ThinkFullApp({ agent, chat, className, defaultWorkbenchView, defaultThr
|
|
|
157
172
|
className: "think-full-app__topbar",
|
|
158
173
|
role: "toolbar",
|
|
159
174
|
"aria-label": "Workbench controls",
|
|
160
|
-
"data-mobile-only":
|
|
161
|
-
children: [/* @__PURE__ */ jsx(WorkbenchBackButton, { onBack: () => setMobileView("chat") }),
|
|
175
|
+
"data-mobile-only": showWorkbenchViewSwitch ? void 0 : "true",
|
|
176
|
+
children: [/* @__PURE__ */ jsx(WorkbenchBackButton, { onBack: () => setMobileView("chat") }), showWorkbenchViewSwitch ? /* @__PURE__ */ jsx(WorkbenchViewSwitch, {
|
|
177
|
+
panels: availableWorkbenchPanels,
|
|
162
178
|
value: activeWorkbenchView,
|
|
163
179
|
onValueChange: setWorkbenchView
|
|
164
180
|
}) : null]
|
|
@@ -173,9 +189,9 @@ function ThinkFullApp({ agent, chat, className, defaultWorkbenchView, defaultThr
|
|
|
173
189
|
color: "neutral",
|
|
174
190
|
size: "icon-sm",
|
|
175
191
|
className: "think-full-app__mobile-action",
|
|
176
|
-
"aria-label":
|
|
192
|
+
"aria-label": mobileWorkbenchLabel,
|
|
177
193
|
onClick: () => setMobileView("workbench"),
|
|
178
|
-
children:
|
|
194
|
+
children: activePanel?.icon ?? /* @__PURE__ */ jsx(TerminalIcon, {})
|
|
179
195
|
})
|
|
180
196
|
}) : null,
|
|
181
197
|
/* @__PURE__ */ jsx("section", {
|
|
@@ -235,11 +251,10 @@ function ThinkFullApp({ agent, chat, className, defaultWorkbenchView, defaultThr
|
|
|
235
251
|
"aria-label": "Workbench",
|
|
236
252
|
children: /* @__PURE__ */ jsx("div", {
|
|
237
253
|
className: "think-full-app__workbench-slot",
|
|
238
|
-
children: /* @__PURE__ */ jsx(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
view: activeWorkbenchView
|
|
254
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
255
|
+
className: "think-full-app__workbench-panel",
|
|
256
|
+
"data-view": activeWorkbenchView,
|
|
257
|
+
children: activePanel?.render(panelContext)
|
|
243
258
|
})
|
|
244
259
|
})
|
|
245
260
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-b/react-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "MCP-B React components built on Base UI and shared design tokens, including Cloudflare Think chat primitives.",
|
|
5
5
|
"homepage": "https://design-system.sigvelo.com",
|
|
6
6
|
"bugs": {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"anser": "^2.3.5",
|
|
51
51
|
"shiki": "^3.23.0",
|
|
52
52
|
"streamdown": "^2.5.0",
|
|
53
|
-
"@mcp-b/design-tokens": "0.
|
|
53
|
+
"@mcp-b/design-tokens": "0.9.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@ai-sdk/react": "^3.0.222",
|