@mcp-b/react-components 0.3.0 → 0.4.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/attachment-preview-ChtC3HiU.js +25 -0
- package/dist/components/AgentChat.d.ts +38 -1
- package/dist/components/AgentChat.js +135 -39
- package/dist/components/AgentConnection.js +1 -1
- package/dist/components/BrowserLiveView.js +1 -1
- package/dist/components/Context.d.ts +28 -27
- package/dist/components/Context.js +48 -47
- package/dist/components/Message.d.ts +16 -2
- package/dist/components/Message.js +36 -2
- package/dist/components/PricingPage.js +1 -1
- package/dist/components/PromptInput.d.ts +532 -2
- package/dist/components/PromptInput.js +153 -15
- package/dist/components/Suggestion.d.ts +43 -0
- package/dist/components/Suggestion.js +39 -0
- package/dist/components/ThinkChat.d.ts +22 -1
- package/dist/components/ThinkChat.js +85 -17
- package/dist/styles/agent-chat.css +19 -0
- package/dist/styles/attachment-preview.css +21 -0
- package/dist/styles/context.css +9 -17
- package/dist/styles/index.css +2 -0
- package/dist/styles/message.css +48 -0
- package/dist/styles/prompt-input.css +172 -0
- package/dist/styles/suggestions.css +21 -0
- package/package.json +2 -2
- package/dist/PromptInput-Dzn-fOPc.d.ts +0 -444
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Dialog, DialogContent, DialogTrigger } from "./components/Dialog.js";
|
|
2
|
+
import "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/_internal/attachment-preview.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Click-to-zoom for image attachments: `children` is the thumbnail and
|
|
7
|
+
* becomes the dialog trigger; the dialog shows the image full size. Shared by
|
|
8
|
+
* the composer chips and message-side attachments.
|
|
9
|
+
*/
|
|
10
|
+
function AttachmentPreview({ url, filename, children }) {
|
|
11
|
+
return /* @__PURE__ */ jsxs(Dialog, { children: [/* @__PURE__ */ jsx(DialogTrigger, { render: children }), /* @__PURE__ */ jsxs(DialogContent, {
|
|
12
|
+
className: "attachment-preview",
|
|
13
|
+
"aria-label": filename ?? "Attachment preview",
|
|
14
|
+
children: [/* @__PURE__ */ jsx("img", {
|
|
15
|
+
className: "attachment-preview__image",
|
|
16
|
+
src: url,
|
|
17
|
+
alt: filename ?? "Attachment"
|
|
18
|
+
}), filename ? /* @__PURE__ */ jsx("div", {
|
|
19
|
+
className: "attachment-preview__name",
|
|
20
|
+
children: filename
|
|
21
|
+
}) : null]
|
|
22
|
+
})] });
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { AttachmentPreview as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ExecutionApprovals, PromptInputMessage, PromptInputStatus } from "./PromptInput.js";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { ChatAddToolApproveResponseFunction, UIMessage } from "ai";
|
|
4
4
|
|
|
@@ -30,12 +30,44 @@ interface AgentChatProps<MessageT extends UIMessage = UIMessage> {
|
|
|
30
30
|
emptyState?: React.ReactNode;
|
|
31
31
|
/** Extra controls rendered in the composer toolbar. */
|
|
32
32
|
composerTools?: React.ReactNode;
|
|
33
|
+
/**
|
|
34
|
+
* Prompt suggestions shown as a chip row above the composer; clicking one
|
|
35
|
+
* submits it as the message text. Strings double as React keys — keep them
|
|
36
|
+
* unique.
|
|
37
|
+
*/
|
|
38
|
+
suggestions?: readonly string[];
|
|
39
|
+
/**
|
|
40
|
+
* Transient status rendered between the transcript and the composer —
|
|
41
|
+
* e.g. a "Reconnecting…" pill. `ThinkChat.Messages` fills this from the
|
|
42
|
+
* hook's `isRecovering`/`connectionError` automatically.
|
|
43
|
+
*/
|
|
44
|
+
notice?: React.ReactNode;
|
|
45
|
+
/**
|
|
46
|
+
* When set, the last assistant message gets a Retry action that calls this
|
|
47
|
+
* with its message id — pass to the AI SDK's `regenerate({ messageId })`.
|
|
48
|
+
*/
|
|
49
|
+
onRegenerate?: (messageId: string) => void;
|
|
33
50
|
/**
|
|
34
51
|
* Pass `addToolApprovalResponse` from `useAgentChat`/`useChat` directly.
|
|
35
52
|
* When set and a tool call awaits approval, the composer becomes the
|
|
36
53
|
* confirmation.
|
|
37
54
|
*/
|
|
38
55
|
onApprovalResponse?: ChatAddToolApproveResponseFunction;
|
|
56
|
+
/**
|
|
57
|
+
* When set, paused codemode executions surface in the same composer
|
|
58
|
+
* confirmation as tool approvals. See {@link ExecutionApprovals}.
|
|
59
|
+
*/
|
|
60
|
+
executionApprovals?: ExecutionApprovals;
|
|
61
|
+
/**
|
|
62
|
+
* When set, a Context ring in the composer footer shows how much of the
|
|
63
|
+
* model's window the conversation occupies. Used tokens come from the
|
|
64
|
+
* newest message carrying `metadata.usage` (the AI SDK `messageMetadata`
|
|
65
|
+
* convention; Think merges it into message metadata as turns finish).
|
|
66
|
+
*/
|
|
67
|
+
contextWindow?: {
|
|
68
|
+
maxTokens: number;
|
|
69
|
+
modelId?: string;
|
|
70
|
+
};
|
|
39
71
|
/** Override rendering per part; return `undefined` to fall back to the default part UI. */
|
|
40
72
|
renderPart?: (context: AgentChatRenderPartContext<MessageT>) => React.ReactNode | undefined;
|
|
41
73
|
/** Rendered after each message's parts — for actions, citations, and the like. */
|
|
@@ -60,7 +92,12 @@ declare function AgentChat<MessageT extends UIMessage = UIMessage>({
|
|
|
60
92
|
disabled,
|
|
61
93
|
emptyState,
|
|
62
94
|
composerTools,
|
|
95
|
+
suggestions,
|
|
96
|
+
notice,
|
|
97
|
+
onRegenerate,
|
|
63
98
|
onApprovalResponse,
|
|
99
|
+
executionApprovals,
|
|
100
|
+
contextWindow,
|
|
64
101
|
renderPart,
|
|
65
102
|
renderMessageAfter,
|
|
66
103
|
ref
|
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
import { t as cx } from "../class-names-9UtZwb0J.js";
|
|
2
|
+
import { D as PlusIcon, O as RefreshIcon } from "../icons-DQC4Zw5R.js";
|
|
3
|
+
import { Context, ContextContent, ContextContentBody, ContextContentHeader, ContextTrigger, getContextUsage, getContextUsedTokens } from "./Context.js";
|
|
2
4
|
import { Conversation, ConversationContent, ConversationEmptyState, ConversationScrollButton } from "./Conversation.js";
|
|
3
|
-
import { Message, MessageContent, MessageResponse } from "./Message.js";
|
|
4
|
-
import { PromptInput, PromptInputApproval, PromptInputBody, PromptInputFooter, PromptInputSubmit, PromptInputTextarea, PromptInputTools, getPendingToolApprovals } from "./PromptInput.js";
|
|
5
|
+
import { Message, MessageAction, MessageActions, MessageAttachment, MessageContent, MessageResponse } from "./Message.js";
|
|
6
|
+
import { PromptInput, PromptInputApproval, PromptInputAttachments, PromptInputBody, PromptInputButton, PromptInputFooter, PromptInputPermissionSelector, PromptInputSubmit, PromptInputTextarea, PromptInputTools, getApprovalRequestId, getPendingToolApprovals, usePromptInputAttachments } from "./PromptInput.js";
|
|
5
7
|
import { Reasoning, ReasoningContent, ReasoningTrigger } from "./Reasoning.js";
|
|
8
|
+
import { Suggestion, Suggestions } from "./Suggestion.js";
|
|
6
9
|
import { Tool, ToolContent, ToolHeader, ToolInput, ToolOutput } from "./Tool.js";
|
|
7
10
|
import * as React from "react";
|
|
8
11
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
-
import { isReasoningUIPart, isTextUIPart, isToolOrDynamicToolUIPart } from "ai";
|
|
12
|
+
import { isFileUIPart, isReasoningUIPart, isTextUIPart, isToolOrDynamicToolUIPart } from "ai";
|
|
10
13
|
//#region src/components/AgentChat.tsx
|
|
14
|
+
function AgentChatAttachButton() {
|
|
15
|
+
const attachments = usePromptInputAttachments();
|
|
16
|
+
return /* @__PURE__ */ jsx(PromptInputButton, {
|
|
17
|
+
tooltip: "Add photos or files",
|
|
18
|
+
onClick: () => attachments.openFileDialog(),
|
|
19
|
+
children: /* @__PURE__ */ jsx(PlusIcon, {})
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function isCompactedPart(message, part, partIndex) {
|
|
23
|
+
const metadata = message.metadata;
|
|
24
|
+
if (isTextUIPart(part)) return Array.isArray(metadata?.compactedTextParts) && metadata.compactedTextParts.includes(partIndex);
|
|
25
|
+
if (isToolOrDynamicToolUIPart(part)) return Array.isArray(metadata?.compactedToolOutputs) && metadata.compactedToolOutputs.includes(part.toolCallId);
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
11
28
|
function getPartKey(part, partIndex) {
|
|
12
29
|
if (isToolOrDynamicToolUIPart(part)) return part.toolCallId;
|
|
13
30
|
if ("id" in part && typeof part.id === "string") return `${part.type}-${part.id}`;
|
|
@@ -15,6 +32,7 @@ function getPartKey(part, partIndex) {
|
|
|
15
32
|
}
|
|
16
33
|
function renderDefaultPart(part) {
|
|
17
34
|
if (isTextUIPart(part) && part.text) return /* @__PURE__ */ jsx(MessageResponse, { children: part.text });
|
|
35
|
+
if (isFileUIPart(part)) return /* @__PURE__ */ jsx(MessageAttachment, { part });
|
|
18
36
|
if (isReasoningUIPart(part) && part.text) return /* @__PURE__ */ jsxs(Reasoning, {
|
|
19
37
|
isStreaming: part.state === "streaming",
|
|
20
38
|
children: [/* @__PURE__ */ jsx(ReasoningTrigger, {}), /* @__PURE__ */ jsx(ReasoningContent, { children: part.text })]
|
|
@@ -34,13 +52,40 @@ function renderDefaultPart(part) {
|
|
|
34
52
|
* transport. Use it directly with plain AI SDK `useChat` or a read-only
|
|
35
53
|
* transcript; if you have a Cloudflare Think agent, use `ThinkChat` instead.
|
|
36
54
|
*/
|
|
37
|
-
function AgentChat({ className, messages, status = "ready", onSubmit, onStop, placeholder = "Ask anything...", disabled, emptyState, composerTools, onApprovalResponse, renderPart, renderMessageAfter, ref }) {
|
|
55
|
+
function AgentChat({ className, messages, status = "ready", onSubmit, onStop, placeholder = "Ask anything...", disabled, emptyState, composerTools, suggestions, notice, onRegenerate, onApprovalResponse, executionApprovals, contextWindow, renderPart, renderMessageAfter, ref }) {
|
|
38
56
|
const handleSubmit = React.useCallback((message) => {
|
|
39
57
|
onSubmit?.(message);
|
|
40
58
|
}, [onSubmit]);
|
|
41
59
|
const [skippedApprovalIds, setSkippedApprovalIds] = React.useState([]);
|
|
42
|
-
const pendingApprovals =
|
|
43
|
-
|
|
60
|
+
const pendingApprovals = getPendingToolApprovals(messages, { pausedExecutions: Boolean(executionApprovals) }).filter(({ part }) => {
|
|
61
|
+
if (part.state === "approval-requested" && !onApprovalResponse) return false;
|
|
62
|
+
const id = getApprovalRequestId(part);
|
|
63
|
+
return Boolean(id) && !skippedApprovalIds.includes(id);
|
|
64
|
+
});
|
|
65
|
+
const approvalsWired = Boolean(onApprovalResponse || executionApprovals);
|
|
66
|
+
const [permissionMode, setPermissionMode] = React.useState("ask");
|
|
67
|
+
const autoApprovedIds = React.useRef(/* @__PURE__ */ new Set());
|
|
68
|
+
React.useEffect(() => {
|
|
69
|
+
if (permissionMode !== "bypass") return;
|
|
70
|
+
for (const { part } of pendingApprovals) {
|
|
71
|
+
const id = getApprovalRequestId(part);
|
|
72
|
+
if (!id || autoApprovedIds.current.has(id)) continue;
|
|
73
|
+
autoApprovedIds.current.add(id);
|
|
74
|
+
if (part.state === "approval-requested") onApprovalResponse?.({
|
|
75
|
+
id,
|
|
76
|
+
approved: true
|
|
77
|
+
});
|
|
78
|
+
else executionApprovals?.approve(id);
|
|
79
|
+
}
|
|
80
|
+
}, [
|
|
81
|
+
permissionMode,
|
|
82
|
+
messages,
|
|
83
|
+
onApprovalResponse,
|
|
84
|
+
executionApprovals
|
|
85
|
+
]);
|
|
86
|
+
const pendingApproval = permissionMode === "bypass" ? void 0 : pendingApprovals[0];
|
|
87
|
+
const contextUsage = contextWindow ? getContextUsage(messages) : void 0;
|
|
88
|
+
const lastAssistantIndex = onRegenerate && status !== "streaming" && status !== "submitted" ? messages.findLastIndex((message) => message.role === "assistant") : -1;
|
|
44
89
|
return /* @__PURE__ */ jsxs("div", {
|
|
45
90
|
ref,
|
|
46
91
|
className: cx("agent-chat", className),
|
|
@@ -51,42 +96,93 @@ function AgentChat({ className, messages, status = "ready", onSubmit, onStop, pl
|
|
|
51
96
|
description: "Ask the agent to inspect, plan, or build."
|
|
52
97
|
}) : null, messages.map((message, messageIndex) => /* @__PURE__ */ jsxs(Message, {
|
|
53
98
|
from: message.role,
|
|
54
|
-
children: [
|
|
55
|
-
|
|
56
|
-
|
|
99
|
+
children: [
|
|
100
|
+
message.parts.map((part, partIndex) => {
|
|
101
|
+
const defaultRender = () => renderDefaultPart(part);
|
|
102
|
+
const custom = renderPart?.({
|
|
103
|
+
message,
|
|
104
|
+
part,
|
|
105
|
+
partIndex,
|
|
106
|
+
defaultRender
|
|
107
|
+
});
|
|
108
|
+
const rendered = custom === void 0 ? defaultRender() : custom;
|
|
109
|
+
return rendered ? /* @__PURE__ */ jsxs(MessageContent, { children: [rendered, isCompactedPart(message, part, partIndex) ? /* @__PURE__ */ jsx("div", {
|
|
110
|
+
className: "message__compacted",
|
|
111
|
+
children: "Output truncated for storage"
|
|
112
|
+
}) : null] }, getPartKey(part, partIndex)) : null;
|
|
113
|
+
}),
|
|
114
|
+
renderMessageAfter?.({
|
|
57
115
|
message,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
})]
|
|
116
|
+
messageIndex
|
|
117
|
+
}),
|
|
118
|
+
onRegenerate && messageIndex === lastAssistantIndex ? /* @__PURE__ */ jsx(MessageActions, { children: /* @__PURE__ */ jsx(MessageAction, {
|
|
119
|
+
label: "Retry",
|
|
120
|
+
tooltip: "Regenerate response",
|
|
121
|
+
onClick: () => onRegenerate(message.id),
|
|
122
|
+
children: /* @__PURE__ */ jsx(RefreshIcon, { size: 14 })
|
|
123
|
+
}) }) : null
|
|
124
|
+
]
|
|
68
125
|
}, message.id ?? messageIndex))] }), /* @__PURE__ */ jsx(ConversationScrollButton, {})]
|
|
69
|
-
}), /* @__PURE__ */
|
|
126
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
70
127
|
className: "agent-chat__composer",
|
|
71
|
-
children:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
128
|
+
children: [
|
|
129
|
+
notice ? /* @__PURE__ */ jsx("div", {
|
|
130
|
+
className: "agent-chat__notice",
|
|
131
|
+
children: notice
|
|
132
|
+
}) : null,
|
|
133
|
+
suggestions?.length && !pendingApproval ? /* @__PURE__ */ jsx(Suggestions, {
|
|
134
|
+
className: "agent-chat__suggestions",
|
|
135
|
+
children: suggestions.map((suggestion) => /* @__PURE__ */ jsx(Suggestion, {
|
|
136
|
+
suggestion,
|
|
137
|
+
disabled,
|
|
138
|
+
onClick: (text) => handleSubmit({
|
|
139
|
+
text,
|
|
140
|
+
files: []
|
|
141
|
+
})
|
|
142
|
+
}, suggestion))
|
|
143
|
+
}) : null,
|
|
144
|
+
/* @__PURE__ */ jsx(PromptInput, {
|
|
145
|
+
multiple: true,
|
|
146
|
+
onSubmit: handleSubmit,
|
|
147
|
+
children: pendingApproval ? /* @__PURE__ */ jsx(PromptInputApproval, {
|
|
148
|
+
part: pendingApproval.part,
|
|
149
|
+
count: pendingApprovals.length,
|
|
150
|
+
onRespond: onApprovalResponse,
|
|
151
|
+
executionApprovals,
|
|
152
|
+
onSkip: () => {
|
|
153
|
+
const id = getApprovalRequestId(pendingApproval.part);
|
|
154
|
+
if (id) setSkippedApprovalIds((previous) => [...previous, id]);
|
|
155
|
+
}
|
|
156
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
157
|
+
/* @__PURE__ */ jsx(PromptInputAttachments, {}),
|
|
158
|
+
/* @__PURE__ */ jsx(PromptInputBody, { children: /* @__PURE__ */ jsx(PromptInputTextarea, {
|
|
159
|
+
"aria-label": "Message",
|
|
160
|
+
placeholder,
|
|
161
|
+
disabled
|
|
162
|
+
}) }),
|
|
163
|
+
/* @__PURE__ */ jsxs(PromptInputFooter, { children: [/* @__PURE__ */ jsxs(PromptInputTools, { children: [
|
|
164
|
+
/* @__PURE__ */ jsx(AgentChatAttachButton, {}),
|
|
165
|
+
approvalsWired ? /* @__PURE__ */ jsx(PromptInputPermissionSelector, {
|
|
166
|
+
value: permissionMode,
|
|
167
|
+
onValueChange: setPermissionMode
|
|
168
|
+
}) : null,
|
|
169
|
+
composerTools
|
|
170
|
+
] }), /* @__PURE__ */ jsxs("div", {
|
|
171
|
+
className: "prompt-input__footer-end",
|
|
172
|
+
children: [contextWindow ? /* @__PURE__ */ jsxs(Context, {
|
|
173
|
+
usedTokens: getContextUsedTokens(contextUsage),
|
|
174
|
+
maxTokens: contextWindow.maxTokens,
|
|
175
|
+
usage: contextUsage,
|
|
176
|
+
modelId: contextWindow.modelId,
|
|
177
|
+
children: [/* @__PURE__ */ jsx(ContextTrigger, {}), /* @__PURE__ */ jsxs(ContextContent, { children: [/* @__PURE__ */ jsx(ContextContentHeader, {}), /* @__PURE__ */ jsx(ContextContentBody, {})] })]
|
|
178
|
+
}) : null, /* @__PURE__ */ jsx(PromptInputSubmit, {
|
|
179
|
+
status,
|
|
180
|
+
onStop
|
|
181
|
+
})]
|
|
182
|
+
})] })
|
|
183
|
+
] })
|
|
184
|
+
})
|
|
185
|
+
]
|
|
90
186
|
})]
|
|
91
187
|
});
|
|
92
188
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { t as cx } from "../class-names-9UtZwb0J.js";
|
|
2
2
|
import { b as LinkIcon } from "../icons-DQC4Zw5R.js";
|
|
3
|
+
import { Popover } from "./Popover.js";
|
|
3
4
|
import { Button } from "./Button.js";
|
|
4
5
|
import { a as CodeBlockCopyButton, i as CodeBlockContent, n as CodeBlockActions, p as CodeBlockTitle, r as CodeBlockContainer, s as CodeBlockHeader, t as CodeBlock } from "../CodeBlock-C2Xjxu9M.js";
|
|
5
|
-
import { Popover } from "./Popover.js";
|
|
6
6
|
import * as React from "react";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
//#region src/components/AgentConnection.tsx
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { t as cx } from "../class-names-9UtZwb0J.js";
|
|
2
2
|
import { A as SpinnerIcon, x as MaximizeIcon } from "../icons-DQC4Zw5R.js";
|
|
3
3
|
import { Button } from "./Button.js";
|
|
4
|
-
import { Badge } from "./Badge.js";
|
|
5
4
|
import { Dialog, DialogContent, DialogTitle } from "./Dialog.js";
|
|
5
|
+
import { Badge } from "./Badge.js";
|
|
6
6
|
import * as React from "react";
|
|
7
7
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
//#region src/components/BrowserLiveView.tsx
|
|
@@ -1,31 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { PopoverProps } from "./Popover.js";
|
|
1
|
+
import { PopoverPositionerProps, PopoverProps } from "./Popover.js";
|
|
3
2
|
import * as React from "react";
|
|
3
|
+
import { LanguageModelUsage, UIMessage } from "ai";
|
|
4
4
|
|
|
5
5
|
//#region src/components/Context.d.ts
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
input?: number;
|
|
13
|
-
inputTokens?: number;
|
|
14
|
-
output?: number;
|
|
15
|
-
outputTokens?: number;
|
|
16
|
-
reasoning?: number;
|
|
17
|
-
reasoningTokens?: number;
|
|
18
|
-
cache?: number;
|
|
19
|
-
cachedInputTokens?: number;
|
|
20
|
-
categories?: ContextUsageCategory[];
|
|
21
|
-
}
|
|
6
|
+
/**
|
|
7
|
+
* The AI SDK's `LanguageModelUsage`, taken whole. `Partial` only relaxes the
|
|
8
|
+
* required-but-`undefined`able keys for fixtures/JSX literals — key names and
|
|
9
|
+
* meanings are upstream's.
|
|
10
|
+
*/
|
|
11
|
+
type ContextTokenUsage = Partial<LanguageModelUsage>;
|
|
22
12
|
interface ContextProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
23
13
|
/** Tokens currently used. */
|
|
24
14
|
usedTokens: number;
|
|
25
15
|
/** Maximum context window size. */
|
|
26
16
|
maxTokens: number;
|
|
27
17
|
/** Optional usage breakdown. */
|
|
28
|
-
usage?:
|
|
18
|
+
usage?: ContextTokenUsage;
|
|
29
19
|
/** Optional model identifier shown in the header. */
|
|
30
20
|
modelId?: string;
|
|
31
21
|
/** Optional total cost in USD. */
|
|
@@ -36,8 +26,8 @@ interface ContextProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
36
26
|
}
|
|
37
27
|
/**
|
|
38
28
|
* Displays token / context usage with a hover card breakdown. The trigger is
|
|
39
|
-
* a
|
|
40
|
-
*
|
|
29
|
+
* a bare progress ring; clicking it opens a popover (upward by default) with
|
|
30
|
+
* category rows and total cost.
|
|
41
31
|
*
|
|
42
32
|
* @example
|
|
43
33
|
* ```tsx
|
|
@@ -45,7 +35,7 @@ interface ContextProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
45
35
|
* usedTokens={42_300}
|
|
46
36
|
* maxTokens={200_000}
|
|
47
37
|
* modelId="claude-opus-4-6"
|
|
48
|
-
* usage={{
|
|
38
|
+
* usage={{ inputTokens: 30000, outputTokens: 12300 }}
|
|
49
39
|
* totalCostUsd={0.42}
|
|
50
40
|
* >
|
|
51
41
|
* <ContextTrigger />
|
|
@@ -73,17 +63,19 @@ declare function Context({
|
|
|
73
63
|
}: ContextProps & {
|
|
74
64
|
ref?: React.Ref<HTMLDivElement>;
|
|
75
65
|
}): React.JSX.Element;
|
|
76
|
-
interface ContextTriggerProps extends
|
|
77
|
-
className?: string;
|
|
78
|
-
}
|
|
66
|
+
interface ContextTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {}
|
|
79
67
|
declare function ContextTrigger({
|
|
80
68
|
className,
|
|
81
69
|
children,
|
|
82
70
|
...props
|
|
83
71
|
}: ContextTriggerProps): React.JSX.Element;
|
|
84
|
-
interface ContextContentProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
72
|
+
interface ContextContentProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
73
|
+
/** The trigger usually sits in a composer at the bottom, so default "top". */
|
|
74
|
+
side?: PopoverPositionerProps["side"];
|
|
75
|
+
}
|
|
85
76
|
declare function ContextContent({
|
|
86
77
|
className,
|
|
78
|
+
side,
|
|
87
79
|
children,
|
|
88
80
|
...props
|
|
89
81
|
}: ContextContentProps): React.JSX.Element;
|
|
@@ -125,5 +117,14 @@ declare function ContextInputUsage(props: ContextInputUsageProps): React.JSX.Ele
|
|
|
125
117
|
declare function ContextOutputUsage(props: ContextOutputUsageProps): React.JSX.Element;
|
|
126
118
|
declare function ContextReasoningUsage(props: ContextReasoningUsageProps): React.JSX.Element;
|
|
127
119
|
declare function ContextCacheUsage(props: ContextCacheUsageProps): React.JSX.Element;
|
|
120
|
+
/**
|
|
121
|
+
* Latest `metadata.usage` in a transcript — the AI SDK convention for token
|
|
122
|
+
* accounting (`toUIMessageStreamResponse({ messageMetadata })`), which Think
|
|
123
|
+
* merges into `message.metadata` as turns finish. Structural: any message
|
|
124
|
+
* whose metadata carries a `usage` object qualifies.
|
|
125
|
+
*/
|
|
126
|
+
declare function getContextUsage(messages: readonly UIMessage[]): LanguageModelUsage | undefined;
|
|
127
|
+
/** Tokens occupying the context window: `totalTokens`, or input + output. */
|
|
128
|
+
declare function getContextUsedTokens(usage: LanguageModelUsage | undefined): number;
|
|
128
129
|
//#endregion
|
|
129
|
-
export { Context, ContextCacheUsage, ContextCacheUsageProps, ContextContent, ContextContentBody, ContextContentBodyProps, ContextContentFooter, ContextContentFooterProps, ContextContentHeader, ContextContentHeaderProps, ContextContentProps, ContextInputUsage, ContextInputUsageProps, ContextOutputUsage, ContextOutputUsageProps, ContextProps, ContextReasoningUsage, ContextReasoningUsageProps, ContextTrigger, ContextTriggerProps,
|
|
130
|
+
export { Context, ContextCacheUsage, ContextCacheUsageProps, ContextContent, ContextContentBody, ContextContentBodyProps, ContextContentFooter, ContextContentFooterProps, ContextContentHeader, ContextContentHeaderProps, ContextContentProps, ContextInputUsage, ContextInputUsageProps, ContextOutputUsage, ContextOutputUsageProps, ContextProps, ContextReasoningUsage, ContextReasoningUsageProps, ContextTokenUsage, ContextTrigger, ContextTriggerProps, ContextUsageRowProps, getContextUsage, getContextUsedTokens };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { t as cx } from "../class-names-9UtZwb0J.js";
|
|
2
|
-
import { Button } from "./Button.js";
|
|
3
2
|
import { Popover } from "./Popover.js";
|
|
4
3
|
import { Separator } from "./Separator.js";
|
|
5
4
|
import * as React from "react";
|
|
@@ -13,8 +12,8 @@ function useContextContext() {
|
|
|
13
12
|
}
|
|
14
13
|
/**
|
|
15
14
|
* Displays token / context usage with a hover card breakdown. The trigger is
|
|
16
|
-
* a
|
|
17
|
-
*
|
|
15
|
+
* a bare progress ring; clicking it opens a popover (upward by default) with
|
|
16
|
+
* category rows and total cost.
|
|
18
17
|
*
|
|
19
18
|
* @example
|
|
20
19
|
* ```tsx
|
|
@@ -22,7 +21,7 @@ function useContextContext() {
|
|
|
22
21
|
* usedTokens={42_300}
|
|
23
22
|
* maxTokens={200_000}
|
|
24
23
|
* modelId="claude-opus-4-6"
|
|
25
|
-
* usage={{
|
|
24
|
+
* usage={{ inputTokens: 30000, outputTokens: 12300 }}
|
|
26
25
|
* totalCostUsd={0.42}
|
|
27
26
|
* >
|
|
28
27
|
* <ContextTrigger />
|
|
@@ -68,33 +67,26 @@ function ContextTrigger({ className, children, ...props }) {
|
|
|
68
67
|
const percent = maxTokens > 0 ? usedTokens / maxTokens * 100 : 0;
|
|
69
68
|
if (React.isValidElement(children)) return /* @__PURE__ */ jsx(Popover.Trigger, { render: children });
|
|
70
69
|
if (children) return /* @__PURE__ */ jsx(Popover.Trigger, { children });
|
|
71
|
-
return /* @__PURE__ */ jsx(Popover.Trigger, {
|
|
72
|
-
variant: "ghost",
|
|
73
|
-
color: "neutral",
|
|
74
|
-
size: "sm",
|
|
70
|
+
return /* @__PURE__ */ jsx(Popover.Trigger, {
|
|
75
71
|
className: cx("context__trigger", className),
|
|
76
72
|
"aria-label": `Context usage: ${percent.toFixed(0)}%`,
|
|
77
73
|
...props,
|
|
78
|
-
children:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
stroke: 2.5
|
|
85
|
-
})
|
|
86
|
-
}), /* @__PURE__ */ jsxs("span", {
|
|
87
|
-
className: "context__trigger-label",
|
|
88
|
-
children: [percent.toFixed(0), "%"]
|
|
89
|
-
})]
|
|
90
|
-
}) });
|
|
74
|
+
children: /* @__PURE__ */ jsx(ProgressRing, {
|
|
75
|
+
percent,
|
|
76
|
+
size: 16,
|
|
77
|
+
stroke: 2.5
|
|
78
|
+
})
|
|
79
|
+
});
|
|
91
80
|
}
|
|
92
|
-
function ContextContent({ className, children, ...props }) {
|
|
93
|
-
return /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(Popover.Positioner, {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
81
|
+
function ContextContent({ className, side = "top", children, ...props }) {
|
|
82
|
+
return /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(Popover.Positioner, {
|
|
83
|
+
side,
|
|
84
|
+
children: /* @__PURE__ */ jsx(Popover.Popup, {
|
|
85
|
+
className: cx("context__content", className),
|
|
86
|
+
...props,
|
|
87
|
+
children
|
|
88
|
+
})
|
|
89
|
+
}) });
|
|
98
90
|
}
|
|
99
91
|
function ContextContentHeader({ className, children, ref, ...props }) {
|
|
100
92
|
const { usedTokens, maxTokens, modelId } = useContextContext();
|
|
@@ -173,7 +165,6 @@ function ContextContentBody({ className, children, ref, ...props }) {
|
|
|
173
165
|
label: "Cache",
|
|
174
166
|
tokens: cacheTokens
|
|
175
167
|
});
|
|
176
|
-
if (usage?.categories) rows.push(...usage.categories);
|
|
177
168
|
return /* @__PURE__ */ jsxs("div", {
|
|
178
169
|
ref,
|
|
179
170
|
className: cx("context__body", className),
|
|
@@ -182,20 +173,13 @@ function ContextContentBody({ className, children, ref, ...props }) {
|
|
|
182
173
|
className: "context__rows",
|
|
183
174
|
children: rows.map((row) => /* @__PURE__ */ jsxs("li", {
|
|
184
175
|
className: "context__row",
|
|
185
|
-
children: [
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
children: row.tokens.toLocaleString()
|
|
193
|
-
}),
|
|
194
|
-
row.costUsd !== void 0 ? /* @__PURE__ */ jsxs("span", {
|
|
195
|
-
className: "context__row-cost",
|
|
196
|
-
children: ["$", row.costUsd.toFixed(4)]
|
|
197
|
-
}) : null
|
|
198
|
-
]
|
|
176
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
177
|
+
className: "context__row-label",
|
|
178
|
+
children: row.label
|
|
179
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
180
|
+
className: "context__row-value",
|
|
181
|
+
children: row.tokens.toLocaleString()
|
|
182
|
+
})]
|
|
199
183
|
}, row.label))
|
|
200
184
|
})]
|
|
201
185
|
});
|
|
@@ -269,10 +253,27 @@ function ContextUsageRow({ className, children, label, usageKey, defaultLabel, r
|
|
|
269
253
|
}
|
|
270
254
|
function getUsageTokens(usage, key) {
|
|
271
255
|
if (!usage) return void 0;
|
|
272
|
-
if (key === "input") return usage.
|
|
273
|
-
if (key === "output") return usage.
|
|
274
|
-
if (key === "reasoning") return usage.
|
|
275
|
-
return usage.
|
|
256
|
+
if (key === "input") return usage.inputTokens;
|
|
257
|
+
if (key === "output") return usage.outputTokens;
|
|
258
|
+
if (key === "reasoning") return usage.outputTokenDetails?.reasoningTokens ?? usage.reasoningTokens;
|
|
259
|
+
return usage.inputTokenDetails?.cacheReadTokens ?? usage.cachedInputTokens;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Latest `metadata.usage` in a transcript — the AI SDK convention for token
|
|
263
|
+
* accounting (`toUIMessageStreamResponse({ messageMetadata })`), which Think
|
|
264
|
+
* merges into `message.metadata` as turns finish. Structural: any message
|
|
265
|
+
* whose metadata carries a `usage` object qualifies.
|
|
266
|
+
*/
|
|
267
|
+
function getContextUsage(messages) {
|
|
268
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
269
|
+
const usage = messages[i].metadata?.usage;
|
|
270
|
+
if (usage && typeof usage === "object") return usage;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/** Tokens occupying the context window: `totalTokens`, or input + output. */
|
|
274
|
+
function getContextUsedTokens(usage) {
|
|
275
|
+
if (!usage) return 0;
|
|
276
|
+
return usage.totalTokens ?? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
|
|
276
277
|
}
|
|
277
278
|
function ProgressRing({ percent, size, stroke }) {
|
|
278
279
|
const radius = (size - stroke) / 2;
|
|
@@ -306,4 +307,4 @@ function ProgressRing({ percent, size, stroke }) {
|
|
|
306
307
|
});
|
|
307
308
|
}
|
|
308
309
|
//#endregion
|
|
309
|
-
export { Context, ContextCacheUsage, ContextContent, ContextContentBody, ContextContentFooter, ContextContentHeader, ContextInputUsage, ContextOutputUsage, ContextReasoningUsage, ContextTrigger };
|
|
310
|
+
export { Context, ContextCacheUsage, ContextContent, ContextContentBody, ContextContentFooter, ContextContentHeader, ContextInputUsage, ContextOutputUsage, ContextReasoningUsage, ContextTrigger, getContextUsage, getContextUsedTokens };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { r as ButtonProps } from "../Button-Ct5I6aTu.js";
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import { UIMessage } from "ai";
|
|
3
|
+
import { FileUIPart, UIMessage } from "ai";
|
|
4
4
|
import { StreamdownProps } from "streamdown";
|
|
5
5
|
|
|
6
6
|
//#region src/components/Message.d.ts
|
|
@@ -60,6 +60,20 @@ declare const MessageResponse: React.MemoExoticComponent<({
|
|
|
60
60
|
className,
|
|
61
61
|
...props
|
|
62
62
|
}: MessageResponseProps) => React.JSX.Element>;
|
|
63
|
+
interface MessageAttachmentProps extends Omit<React.HTMLAttributes<HTMLElement>, "part"> {
|
|
64
|
+
/** The AI SDK `file` part, taken whole. */
|
|
65
|
+
part: FileUIPart;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A `file` part inside a message: images render inline (click to zoom),
|
|
69
|
+
* anything else becomes a download chip. Covers both directions — files the
|
|
70
|
+
* user attached and files the agent emitted.
|
|
71
|
+
*/
|
|
72
|
+
declare function MessageAttachment({
|
|
73
|
+
part,
|
|
74
|
+
className,
|
|
75
|
+
...props
|
|
76
|
+
}: MessageAttachmentProps): React.JSX.Element;
|
|
63
77
|
interface MessageActionsProps extends React.HTMLAttributes<HTMLDivElement> {}
|
|
64
78
|
/**
|
|
65
79
|
* Horizontal list of action buttons (copy, retry, thumbs-up, etc.) shown
|
|
@@ -214,4 +228,4 @@ declare function MessageBranchPage({
|
|
|
214
228
|
ref?: React.Ref<HTMLSpanElement>;
|
|
215
229
|
}): React.JSX.Element;
|
|
216
230
|
//#endregion
|
|
217
|
-
export { Message, MessageAction, MessageActionProps, MessageActions, MessageActionsProps, MessageBranch, MessageBranchContent, MessageBranchContentProps, MessageBranchNext, MessageBranchNextProps, MessageBranchPage, MessageBranchPageProps, MessageBranchPrevious, MessageBranchPreviousProps, MessageBranchProps, MessageBranchSelector, MessageBranchSelectorProps, MessageContent, MessageContentProps, MessageProps, MessageResponse, MessageResponseProps, MessageRole, MessageToolbar, MessageToolbarProps };
|
|
231
|
+
export { Message, MessageAction, MessageActionProps, MessageActions, MessageActionsProps, MessageAttachment, MessageAttachmentProps, MessageBranch, MessageBranchContent, MessageBranchContentProps, MessageBranchNext, MessageBranchNextProps, MessageBranchPage, MessageBranchPageProps, MessageBranchPrevious, MessageBranchPreviousProps, MessageBranchProps, MessageBranchSelector, MessageBranchSelectorProps, MessageContent, MessageContentProps, MessageProps, MessageResponse, MessageResponseProps, MessageRole, MessageToolbar, MessageToolbarProps };
|