@springbrand/message-panel 0.1.0 → 0.1.2

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 (99) hide show
  1. package/dist/demo/camel-chat-showcase.d.ts +1 -0
  2. package/dist/demo/camel-chat-showcase.js +1007 -0
  3. package/dist/demo/fixtures.d.ts +32 -0
  4. package/dist/demo/fixtures.js +250 -0
  5. package/dist/demo/index.js +4 -0
  6. package/dist/demo/message-panel-chat-showcase.d.ts +1 -0
  7. package/dist/demo/message-panel-chat-showcase.js +114 -0
  8. package/dist/demo/message-panel-gallery.d.ts +1 -0
  9. package/dist/demo/message-panel-gallery.js +130 -0
  10. package/dist/src/adapters/ai-sdk.d.ts +4 -0
  11. package/dist/src/adapters/ai-sdk.js +25 -0
  12. package/dist/src/camel/camel-chat-messages.d.ts +44 -0
  13. package/dist/src/camel/camel-chat-messages.js +421 -0
  14. package/dist/src/camel/camel-model-select.d.ts +13 -0
  15. package/dist/src/camel/camel-model-select.js +7 -0
  16. package/dist/src/camel/camel-plan-bar.d.ts +5 -0
  17. package/dist/src/camel/camel-plan-bar.js +24 -0
  18. package/dist/src/camel/camel-prompt-input.d.ts +32 -0
  19. package/dist/src/camel/camel-prompt-input.js +182 -0
  20. package/dist/src/camel/camel-tool-presentation.d.ts +21 -0
  21. package/dist/src/camel/camel-tool-presentation.js +784 -0
  22. package/dist/src/camel/camel-turn.d.ts +57 -0
  23. package/dist/src/camel/camel-turn.js +203 -0
  24. package/dist/src/camel/index.js +7 -0
  25. package/dist/src/camel/turn-summary-bar.d.ts +8 -0
  26. package/dist/src/camel/turn-summary-bar.js +12 -0
  27. package/dist/src/chat-summary-panel.d.ts +22 -0
  28. package/dist/src/chat-summary-panel.js +26 -0
  29. package/dist/src/composer/chat-composer.d.ts +4 -0
  30. package/dist/src/composer/chat-composer.js +90 -0
  31. package/dist/src/composer/composer-attachments.d.ts +13 -0
  32. package/dist/src/composer/composer-attachments.js +47 -0
  33. package/dist/src/composer/composer-trigger-popover.d.ts +31 -0
  34. package/dist/src/composer/composer-trigger-popover.js +29 -0
  35. package/dist/src/composer/composer.d.ts +15 -0
  36. package/dist/src/composer/composer.js +180 -0
  37. package/dist/src/composer/drop-controller.d.ts +8 -0
  38. package/dist/src/composer/drop-controller.js +69 -0
  39. package/dist/src/composer/index.js +6 -0
  40. package/dist/src/composer/types.d.ts +62 -0
  41. package/dist/src/composer/types.js +1 -0
  42. package/dist/src/contracts.d.ts +94 -0
  43. package/dist/src/contracts.js +1 -0
  44. package/dist/src/conversation.d.ts +13 -0
  45. package/dist/src/conversation.js +18 -0
  46. package/dist/src/index.js +12 -0
  47. package/dist/src/internal/cn.d.ts +1 -0
  48. package/dist/src/internal/cn.js +3 -0
  49. package/dist/src/message-panel.d.ts +4 -0
  50. package/dist/src/message-panel.js +144 -0
  51. package/dist/src/message.d.ts +36 -0
  52. package/dist/src/message.js +83 -0
  53. package/dist/src/parts/index.d.ts +187 -0
  54. package/dist/src/parts/index.js +332 -0
  55. package/dist/src/parts/kind.d.ts +3 -0
  56. package/dist/src/parts/kind.js +69 -0
  57. package/dist/src/parts/plan.d.ts +10 -0
  58. package/dist/src/parts/plan.js +40 -0
  59. package/dist/src/parts/primitives.d.ts +70 -0
  60. package/dist/src/parts/primitives.js +153 -0
  61. package/dist/src/registry.d.ts +4 -0
  62. package/dist/src/registry.js +31 -0
  63. package/dist/src/status.d.ts +26 -0
  64. package/dist/src/status.js +74 -0
  65. package/package.json +17 -8
  66. package/demo/camel-chat-showcase.tsx +0 -1265
  67. package/demo/fixtures.ts +0 -292
  68. package/demo/message-panel-chat-showcase.tsx +0 -250
  69. package/demo/message-panel-gallery.tsx +0 -389
  70. package/src/adapters/ai-sdk.ts +0 -32
  71. package/src/camel/camel-chat-messages.tsx +0 -1420
  72. package/src/camel/camel-model-select.tsx +0 -88
  73. package/src/camel/camel-plan-bar.tsx +0 -115
  74. package/src/camel/camel-prompt-input.tsx +0 -540
  75. package/src/camel/camel-tool-presentation.tsx +0 -1270
  76. package/src/camel/camel-turn.ts +0 -315
  77. package/src/camel/turn-summary-bar.tsx +0 -81
  78. package/src/chat-summary-panel.tsx +0 -229
  79. package/src/composer/chat-composer.tsx +0 -216
  80. package/src/composer/composer-attachments.tsx +0 -138
  81. package/src/composer/composer-trigger-popover.tsx +0 -236
  82. package/src/composer/composer.tsx +0 -425
  83. package/src/composer/drop-controller.ts +0 -80
  84. package/src/composer/types.ts +0 -79
  85. package/src/contracts.ts +0 -139
  86. package/src/conversation.tsx +0 -76
  87. package/src/internal/cn.ts +0 -3
  88. package/src/message-panel.tsx +0 -368
  89. package/src/message.tsx +0 -254
  90. package/src/parts/index.tsx +0 -1149
  91. package/src/parts/kind.ts +0 -67
  92. package/src/parts/plan.ts +0 -63
  93. package/src/parts/primitives.tsx +0 -439
  94. package/src/registry.ts +0 -44
  95. package/src/status.tsx +0 -132
  96. /package/{demo/index.ts → dist/demo/index.d.ts} +0 -0
  97. /package/{src/camel/index.ts → dist/src/camel/index.d.ts} +0 -0
  98. /package/{src/composer/index.ts → dist/src/composer/index.d.ts} +0 -0
  99. /package/{src/index.ts → dist/src/index.d.ts} +0 -0
@@ -0,0 +1,332 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { AlertCircleIcon, ArrowRightIcon, AtomIcon, BarChart3Icon, BellIcon, BlocksIcon, BotIcon, BrainCircuitIcon, CheckCircle2Icon, CheckIcon, ChevronDownIcon, ChevronRightIcon, CircleDotIcon, CircleIcon, Code2Icon, DownloadIcon, ExternalLinkIcon, EyeIcon, FileAudioIcon, FileCodeIcon, FileImageIcon, FileSpreadsheetIcon, FileTextIcon, GlobeIcon, ImageIcon, Loader2Icon, ListTodoIcon, Maximize2Icon, NewspaperIcon, OrbitIcon, RefreshCcwIcon, SparklesIcon, TerminalIcon, TriangleAlertIcon, WrenchIcon, XIcon, } from "lucide-react";
3
+ import { useMemo, useState, } from "react";
4
+ import { cn } from "../internal/cn";
5
+ import { MessageResponse } from "../message";
6
+ import { ThinkingIndicator } from "../status";
7
+ import { resolvePartKind, toolNameOfPart } from "./kind";
8
+ import { planStepsOfPart, } from "./plan";
9
+ import { Confirmation, ConfirmationAccepted, ConfirmationAction, ConfirmationActions, ConfirmationRejected, ConfirmationRequest, ConfirmationTitle, Plan, PlanContent, PlanHeader, PlanTitle, Reasoning, ReasoningContent, ReasoningTrigger, } from "./primitives";
10
+ export { resolvePartKind, toolNameOfPart } from "./kind";
11
+ export { planStepsOfPart, projectLatestPlan, } from "./plan";
12
+ export const OUT_OF_BAND_PREFIX = "[Delivered to the user out of band]";
13
+ export function stripNoticePrefix(text) {
14
+ const trimmed = text.trimStart();
15
+ return trimmed.startsWith(OUT_OF_BAND_PREFIX)
16
+ ? trimmed.slice(OUT_OF_BAND_PREFIX.length).trim()
17
+ : text.trim();
18
+ }
19
+ export function TextPart({ text, streaming = false }) {
20
+ if (!text.trim())
21
+ return null;
22
+ if (text.trimStart().startsWith(OUT_OF_BAND_PREFIX)) {
23
+ return _jsx("div", { className: "sb-notice-line", children: stripNoticePrefix(text) });
24
+ }
25
+ return (_jsx("div", { "data-part-type": "text", children: _jsx(MessageResponse, { streaming: streaming, children: text }) }));
26
+ }
27
+ export function ReasoningPart({ text, label, streaming = false, duration, }) {
28
+ if (!text.trim())
29
+ return null;
30
+ return (_jsx("div", { "data-part-type": "reasoning", children: _jsxs(Reasoning, { isStreaming: streaming, defaultOpen: streaming, duration: duration, children: [_jsx(ReasoningTrigger, { children: label }), _jsx(ReasoningContent, { children: text })] }) }));
31
+ }
32
+ const toolIcons = {
33
+ web: GlobeIcon,
34
+ news: NewspaperIcon,
35
+ code: Code2Icon,
36
+ image: ImageIcon,
37
+ file: FileTextIcon,
38
+ terminal: TerminalIcon,
39
+ };
40
+ function humanizeToolName(name) {
41
+ return name
42
+ .replace(/^tool-/, "")
43
+ .replace(/[_-]+/g, " ")
44
+ .replace(/\b\w/g, (character) => character.toUpperCase());
45
+ }
46
+ export function ToolPart({ name, state, input, output, errorText, icon, label, meta, results, command, activityId, onNavigate, }) {
47
+ const Icon = icon ? toolIcons[icon] : WrenchIcon;
48
+ const [open, setOpen] = useState(false);
49
+ const title = label ?? humanizeToolName(name);
50
+ const running = state === "input-streaming" || state === "input-available";
51
+ const hasDetails = input != null ||
52
+ output != null ||
53
+ Boolean(errorText || command || results?.length);
54
+ if (state === "interrupted") {
55
+ return (_jsxs("div", { className: "sb-tool-failed", "data-part-type": "tool", children: [_jsx(TriangleAlertIcon, { size: 16 }), _jsx("span", { children: "Interrupted" }), _jsx("small", { children: title })] }));
56
+ }
57
+ if (state === "output-error") {
58
+ return (_jsxs("div", { className: "sb-tool-failed", "data-part-type": "tool", children: [_jsx(TriangleAlertIcon, { size: 16 }), _jsx("span", { children: "Failed" }), _jsx("small", { children: errorText || title })] }));
59
+ }
60
+ return (_jsxs("div", { className: "sb-tool-line", "data-part-type": "tool", children: [_jsxs("button", { type: "button", className: "sb-tool-line__trigger", "aria-expanded": open, onClick: () => {
61
+ if (hasDetails)
62
+ setOpen((current) => !current);
63
+ if (activityId)
64
+ onNavigate?.(activityId);
65
+ }, children: [_jsx(Icon, { size: 16 }), _jsx("span", { children: running ? `${title}…` : title }), meta && _jsx("small", { children: meta }), running ? (_jsx(Loader2Icon, { size: 14, className: "sb-spin" })) : (hasDetails && (_jsx(ChevronRightIcon, { size: 15, className: cn("sb-tool-line__chevron", open && "is-open") })))] }), open && (_jsxs("div", { className: "sb-tool-line__details", children: [results?.length ? (_jsx("ul", { className: "sb-tool-results", children: results.map((result, index) => (_jsxs("li", { children: [_jsx("i", { "data-index": index % 2 }), _jsx("span", { children: result.title }), _jsx("small", { children: result.domain })] }, `${result.domain}:${result.title}`))) })) : null, command && (_jsxs("div", { className: "sb-terminal", children: [_jsxs("code", { children: ["$ ", command] }), typeof output === "string" && _jsx("pre", { children: output })] })), !command && input != null && (_jsx("pre", { children: JSON.stringify(input, null, 2) })), !command && output != null && (_jsx("pre", { children: typeof output === "string"
66
+ ? output
67
+ : JSON.stringify(output, null, 2) }))] }))] }));
68
+ }
69
+ export function ErrorPart({ title = "Error", body, onRetry, }) {
70
+ return (_jsxs("div", { className: "sb-error-part", "data-part-type": "error", role: "alert", children: [_jsx(AlertCircleIcon, { size: 18 }), _jsxs("div", { children: [_jsx("strong", { children: title }), _jsx("p", { children: body }), onRetry && (_jsxs("button", { type: "button", className: "sb-link-button", onClick: onRetry, children: [_jsx(RefreshCcwIcon, { size: 14 }), "\u91CD\u8BD5"] }))] })] }));
71
+ }
72
+ export function ImagePart({ src, alt, onPreview, }) {
73
+ return (_jsxs("figure", { className: "sb-image-part", "data-part-type": "image", children: [_jsxs("button", { type: "button", onClick: onPreview, "aria-label": `预览图片:${alt}`, children: [_jsx("img", { src: src, alt: alt }), onPreview && _jsx(Maximize2Icon, { size: 17 })] }), _jsx("figcaption", { children: alt })] }));
74
+ }
75
+ export function ChartPart({ title, unit, bars, expandable, onPreview, }) {
76
+ const max = Math.max(...bars.map((bar) => bar.value), 1);
77
+ return (_jsxs("section", { className: "sb-chart", "data-part-type": "chart", "aria-label": title, children: [_jsxs("header", { children: [_jsxs("span", { children: [_jsx(BarChart3Icon, { size: 17 }), _jsx("strong", { children: title })] }), expandable && (_jsx("button", { type: "button", onClick: onPreview, "aria-label": "\u5C55\u5F00\u56FE\u8868", children: _jsx(Maximize2Icon, { size: 16 }) }))] }), _jsx("div", { className: "sb-chart__plot", children: bars.map((bar) => (_jsxs("div", { className: "sb-chart__bar-group", children: [_jsxs("span", { children: [bar.value, unit] }), _jsx("i", { style: { "--sb-bar-height": `${(bar.value / max) * 100}%` } }), _jsx("small", { children: bar.label })] }, bar.label))) })] }));
78
+ }
79
+ export function AgentCardPart({ title, agentName, agentDesc, onNavigate, }) {
80
+ return (_jsxs("section", { className: "sb-agent-card", "data-part-type": "agent_card", children: [_jsx("small", { children: title }), _jsxs("div", { children: [_jsx("span", { className: "sb-agent-card__icon", children: _jsx(BotIcon, { size: 20 }) }), _jsxs("div", { children: [_jsx("strong", { children: agentName }), _jsx("p", { children: agentDesc })] }), onNavigate && (_jsx("button", { type: "button", onClick: onNavigate, "aria-label": `打开 ${agentName}`, children: _jsx(ArrowRightIcon, { size: 17 }) }))] })] }));
81
+ }
82
+ export function StepStatusIcon({ status, animated = false, }) {
83
+ if (status === "done")
84
+ return _jsx(CheckCircle2Icon, { size: 16, "data-tone": "success" });
85
+ if (status === "in_progress") {
86
+ return animated ? (_jsx(Loader2Icon, { size: 16, className: "sb-spin", "data-tone": "active" })) : (_jsx(CircleDotIcon, { size: 16, "data-tone": "muted" }));
87
+ }
88
+ return _jsx(CircleIcon, { size: 16, "data-tone": "muted" });
89
+ }
90
+ export function PlanPart({ title, steps, streaming = false, }) {
91
+ const done = steps.filter((step) => step.status === "done").length;
92
+ const allDone = steps.length > 0 && done === steps.length;
93
+ const animated = streaming && !allDone;
94
+ return (_jsxs(Plan, { className: "sb-plan-card", "data-part-type": "plan", isStreaming: animated, children: [_jsxs(PlanHeader, { children: [_jsxs("div", { className: "sb-plan__heading", children: [_jsx(ListTodoIcon, { size: 18 }), _jsx(PlanTitle, { children: title })] }), steps.length > 0 && (_jsxs("span", { className: "sb-plan__badge", "data-state": allDone ? "done" : animated ? "running" : "paused", children: [allDone && _jsx(CheckCircle2Icon, { size: 13 }), allDone
95
+ ? "Done"
96
+ : `${animated ? "Running · " : ""}${done}/${steps.length}`] }))] }), _jsx(PlanContent, { children: steps.length > 0 ? (_jsx("ol", { className: "sb-plan__steps", children: steps.map((step, index) => {
97
+ const status = step.status ??
98
+ (animated && index === done ? "in_progress" : "pending");
99
+ return (_jsxs("li", { "data-status": status, children: [_jsx(StepStatusIcon, { status: status, animated: animated && status === "in_progress" }), _jsx("span", { children: step.text })] }, `${index}:${step.text}`));
100
+ }) })) : animated ? (_jsx("div", { className: "sb-plan__skeleton", role: "status", "aria-label": "\u6B63\u5728\u751F\u6210\u8BA1\u5212", children: [72, 56, 40].map((width) => (_jsxs("span", { children: [_jsx("i", {}), _jsx("b", { style: { width: `${width}%` } })] }, width))) })) : (_jsx("p", { className: "sb-plan__empty", children: "(\u7A7A\u8BA1\u5212)" })) })] }));
101
+ }
102
+ export function SchedulePart({ title, cadence, nextRun, scheduled = false, onSchedule, }) {
103
+ return (_jsxs("section", { className: "sb-schedule", "data-part-type": "schedule", children: [_jsx(BellIcon, { size: 18 }), _jsxs("div", { children: [_jsx("strong", { children: title }), _jsx("p", { children: cadence }), _jsx("small", { children: nextRun })] }), _jsx("button", { type: "button", className: "sb-button", onClick: onSchedule, disabled: scheduled, children: scheduled ? "已安排" : "安排" })] }));
104
+ }
105
+ export function RunningRow({ tool }) {
106
+ const Icon = tool.icon ? toolIcons[tool.icon] : WrenchIcon;
107
+ return (_jsxs("div", { className: "sb-running-row", children: [_jsx(Icon, { size: 15 }), _jsx("span", { children: tool.label }), _jsx(Loader2Icon, { size: 14, className: "sb-spin" })] }));
108
+ }
109
+ export function CollapsedRow({ tool }) {
110
+ const Icon = tool.icon ? toolIcons[tool.icon] : WrenchIcon;
111
+ return (_jsxs("div", { className: "sb-collapsed-row", children: [_jsx(Icon, { size: 15 }), _jsx("span", { children: tool.label }), _jsx("small", { children: tool.meta }), tool.status === "failed" ? _jsx(XIcon, { size: 14 }) : _jsx(CheckIcon, { size: 14 })] }));
112
+ }
113
+ export function ParallelPart({ label = "Parallel tasks", tools, isActive = true, }) {
114
+ const active = isActive && tools.some((tool) => tool.status === "running");
115
+ return (_jsxs("details", { className: "sb-parallel", "data-part-type": "parallel", open: active, children: [_jsxs("summary", { children: [_jsx("strong", { children: label }), _jsxs("span", { children: [tools.filter((tool) => tool.status === "done").length, "/", tools.length] }), _jsx(ChevronDownIcon, { size: 16 })] }), _jsx("div", { children: tools.map((tool, index) => tool.status === "running" && isActive ? (_jsx(RunningRow, { tool: tool }, `${index}:${tool.label}`)) : (_jsx(CollapsedRow, { tool: tool.status === "running"
116
+ ? { ...tool, status: "failed" }
117
+ : tool }, `${index}:${tool.label}`))) })] }));
118
+ }
119
+ const SUBAGENT_VISUALS = [
120
+ { icon: SparklesIcon, name: "sparkles", color: "#e59a45" },
121
+ { icon: AtomIcon, name: "atom", color: "#9b7bd1" },
122
+ { icon: BrainCircuitIcon, name: "brain", color: "#9b7bd1" },
123
+ { icon: OrbitIcon, name: "orbit", color: "#e59a45" },
124
+ { icon: BlocksIcon, name: "blocks", color: "#e59a45" },
125
+ ];
126
+ function subAgentVisual(name) {
127
+ let hash = 0;
128
+ for (const character of name) {
129
+ hash = (hash * 31 + (character.codePointAt(0) ?? 0)) >>> 0;
130
+ }
131
+ return SUBAGENT_VISUALS[hash % SUBAGENT_VISUALS.length] ?? SUBAGENT_VISUALS[0];
132
+ }
133
+ export function SubAgentsPart({ agents, isActive = true, }) {
134
+ return (_jsx("div", { className: "sb-subagents", "data-part-type": "subagents", children: agents.map((agent, index) => {
135
+ const visual = subAgentVisual(agent.name);
136
+ const AgentIcon = visual.icon;
137
+ return (_jsxs("article", { "data-state": agent.status ?? "done", children: [_jsx("span", { "data-subagent-icon": visual.name, style: { color: agent.tint ?? visual.color }, "aria-hidden": "true", children: _jsx(AgentIcon, { size: 14, strokeWidth: 2.25 }) }), _jsxs("div", { children: [_jsx("strong", { children: agent.name }), agent.status === "running" && isActive ? (_jsxs("small", { children: [_jsx(Loader2Icon, { size: 12, className: "sb-spin" }), "Running\u2026"] })) : (_jsx("p", { children: agent.summary || (agent.status === "running" ? "Interrupted" : "") }))] })] }, `${index}:${agent.name}`));
138
+ }) }));
139
+ }
140
+ export function ConfirmationPart({ id = "confirmation", title, body, confirmLabel = "确认", cancelLabel = "取消", state, onConfirm, }) {
141
+ const [internalState, setInternalState] = useState(state ?? "pending");
142
+ const resolvedState = state ?? internalState;
143
+ const decide = async (confirmed) => {
144
+ await onConfirm?.(confirmed);
145
+ if (state === undefined)
146
+ setInternalState(confirmed ? "confirmed" : "cancelled");
147
+ };
148
+ return (_jsx("div", { "data-part-type": "confirmation", children: _jsxs(Confirmation, { state: resolvedState, approval: { id, approved: resolvedState === "pending" ? undefined : resolvedState === "confirmed" }, children: [_jsxs(ConfirmationTitle, { children: [_jsx(AlertCircleIcon, { size: 18 }), _jsxs("span", { children: [_jsx(ConfirmationRequest, { children: title }), _jsxs(ConfirmationAccepted, { children: ["\u5DF2\u786E\u8BA4\uFF1A", title] }), _jsxs(ConfirmationRejected, { children: ["\u5DF2\u53D6\u6D88\uFF1A", title] })] })] }), body && _jsx("p", { children: body }), _jsxs(ConfirmationActions, { children: [_jsx(ConfirmationAction, { className: "sb-button--secondary", onClick: () => void decide(false), children: cancelLabel }), _jsx(ConfirmationAction, { onClick: () => void decide(true), children: confirmLabel })] })] }) }));
149
+ }
150
+ function ToolApprovalPresentation({ children, toolName, state, context, }) {
151
+ const approval = context.approval;
152
+ if (!approval)
153
+ return _jsx(_Fragment, { children: children });
154
+ return (_jsxs("div", { className: "sb-tool-stack", children: [children, _jsxs(Confirmation, { approval: approval, state: state, children: [_jsxs(ConfirmationTitle, { children: [_jsxs(ConfirmationRequest, { children: [_jsx("strong", { children: humanizeToolName(toolName) }), " \u8BF7\u6C42\u6267\u884C\uFF0C\u9700\u8981\u4F60\u7684\u6279\u51C6\u3002"] }), _jsxs(ConfirmationAccepted, { children: ["\u5DF2\u6279\u51C6 ", humanizeToolName(toolName), "\uFF0C\u7EE7\u7EED\u6267\u884C\u3002"] }), _jsxs(ConfirmationRejected, { children: ["\u5DF2\u62D2\u7EDD ", humanizeToolName(toolName), "\u3002"] })] }), _jsxs(ConfirmationActions, { children: [_jsx(ConfirmationAction, { className: "sb-button--secondary", onClick: () => void context.actions.onApprove?.(approval.id, "deny"), children: "\u62D2\u7EDD" }), _jsx(ConfirmationAction, { className: "sb-button--secondary", onClick: () => void context.actions.onApprove?.(approval.id, "allow_once"), children: "\u4EC5\u672C\u6B21\u5141\u8BB8" }), _jsx(ConfirmationAction, { onClick: () => void context.actions.onApprove?.(approval.id, "allow_level"), children: "\u5141\u8BB8\u8BE5\u6863\u4F4D" })] })] })] }));
155
+ }
156
+ const fileIcons = {
157
+ doc: FileTextIcon,
158
+ image: FileImageIcon,
159
+ html: Code2Icon,
160
+ audio: FileAudioIcon,
161
+ csv: FileSpreadsheetIcon,
162
+ code: FileCodeIcon,
163
+ };
164
+ export function FilePart({ name, kind = "doc", note, url, artifact, onPreview, onDownload, }) {
165
+ const Icon = fileIcons[kind];
166
+ const payload = artifact ?? { name, kind, note, url };
167
+ return (_jsxs("div", { className: "sb-file-part", "data-part-type": "file", children: [_jsx("span", { children: _jsx(Icon, { size: 19 }) }), _jsxs("div", { children: [_jsx("strong", { children: name }), note && _jsx("small", { children: note })] }), _jsxs("div", { children: [onPreview && (_jsx("button", { type: "button", onClick: () => onPreview(payload), "aria-label": `预览 ${name}`, children: _jsx(EyeIcon, { size: 16 }) })), onDownload ? (_jsx("button", { type: "button", onClick: () => onDownload(payload), "aria-label": `下载 ${name}`, children: _jsx(DownloadIcon, { size: 16 }) })) : url ? (_jsx("a", { href: url, target: "_blank", rel: "noreferrer", download: name, "aria-label": `打开 ${name}`, children: _jsx(ExternalLinkIcon, { size: 16 }) })) : null] })] }));
168
+ }
169
+ export function DashboardPart(data) {
170
+ return (_jsxs("section", { className: "sb-dashboard", "data-part-type": "dashboard", children: [_jsx("h3", { children: data.title }), data.tiles?.length ? (_jsx("div", { className: "sb-dashboard__tiles", children: data.tiles.map((tile) => (_jsxs("article", { children: [_jsx("strong", { children: tile.value }), _jsx("small", { children: tile.label })] }, tile.label))) })) : null, data.cards?.length ? (_jsx("div", { className: "sb-dashboard__cards", children: data.cards.map((card) => (_jsxs("article", { children: [_jsx("span", { children: card.icon }), _jsx("strong", { children: card.title }), _jsx("p", { children: card.body })] }, card.title))) })) : null, data.badges?.length ? _jsx("div", { className: "sb-dashboard__badges", children: data.badges.map((badge) => _jsx("span", { children: badge }, badge)) }) : null, data.fileNote && _jsxs("aside", { children: [_jsx("strong", { children: data.fileNote.title }), _jsx("p", { children: data.fileNote.body })] }), data.table && (_jsx("div", { className: "sb-table-scroll", children: _jsxs("table", { children: [_jsx("thead", { children: _jsx("tr", { children: data.table.columns.map((column) => _jsx("th", { children: column }, column)) }) }), _jsx("tbody", { children: data.table.rows.map((row, index) => _jsx("tr", { children: row.map((cell, cellIndex) => _jsx("td", { children: cell }, cellIndex)) }, index)) })] }) }))] }));
171
+ }
172
+ export function AskUserPart({ questions, freeTextPlaceholder, submittedAnswer, onSubmit, }) {
173
+ const [selections, setSelections] = useState(() => questions.map(() => []));
174
+ const [text, setText] = useState("");
175
+ const [submitted, setSubmitted] = useState(false);
176
+ const externalAnswer = submittedAnswer?.trim() ?? "";
177
+ const externalSelections = useMemo(() => new Set(externalAnswer
178
+ ? externalAnswer.split("、").map((item) => item.trim()).filter(Boolean)
179
+ : []), [externalAnswer]);
180
+ const resolvedSubmitted = submitted || Boolean(externalAnswer);
181
+ const valid = questions.every((question, index) => question.kind !== "single" || selections[index].length === 1);
182
+ const toggle = (questionIndex, option) => {
183
+ if (resolvedSubmitted)
184
+ return;
185
+ setSelections((current) => current.map((selected, index) => {
186
+ if (index !== questionIndex)
187
+ return selected;
188
+ if (questions[index].kind === "single")
189
+ return [option];
190
+ return selected.includes(option)
191
+ ? selected.filter((item) => item !== option)
192
+ : [...selected, option];
193
+ }));
194
+ };
195
+ const submit = async () => {
196
+ if (!valid || resolvedSubmitted)
197
+ return;
198
+ await onSubmit?.({ selections, text: text.trim() });
199
+ setSubmitted(true);
200
+ };
201
+ const resolvedAnswer = externalAnswer ||
202
+ [...selections.flat(), text.trim()].filter(Boolean).join("、");
203
+ return (_jsxs("section", { className: "sb-ask-user", "data-part-type": "ask_user", "data-state": resolvedSubmitted ? "submitted" : "pending", children: [questions.map((question, questionIndex) => (_jsxs("fieldset", { disabled: resolvedSubmitted, children: [_jsx("legend", { children: question.prompt }), _jsx("div", { children: question.options.map((option) => {
204
+ const selected = externalAnswer
205
+ ? externalSelections.has(option)
206
+ : selections[questionIndex].includes(option);
207
+ return (_jsxs("button", { type: "button", "aria-pressed": selected, onClick: () => toggle(questionIndex, option), children: [question.kind === "multi" && _jsx("span", { children: selected && _jsx(CheckIcon, { size: 12 }) }), selected && question.kind === "single" && _jsx(CheckIcon, { size: 14 }), option] }, option));
208
+ }) })] }, `${questionIndex}:${question.prompt}`))), freeTextPlaceholder && !resolvedSubmitted && (_jsx("textarea", { value: text, onChange: (event) => setText(event.currentTarget.value), placeholder: freeTextPlaceholder })), resolvedSubmitted ? (_jsxs("div", { className: "sb-ask-user__submitted", children: [_jsx(CheckIcon, { size: 15 }), _jsx("span", { children: "Submitted" }), resolvedAnswer && _jsx("small", { children: resolvedAnswer })] })) : (_jsxs("button", { type: "button", className: "sb-button", disabled: !valid, onClick: () => void submit(), children: ["\u63D0\u4EA4", _jsx(ArrowRightIcon, { size: 15 })] }))] }));
209
+ }
210
+ export function Suggestions({ items, onPick, }) {
211
+ return (_jsx("div", { className: "sb-suggestions", "data-part-type": "suggestions", children: items.map((item) => (_jsxs("button", { type: "button", onClick: () => void onPick?.(item), children: [_jsx(ArrowRightIcon, { size: 15 }), _jsx("span", { children: item })] }, item))) }));
212
+ }
213
+ export function InlineText({ children }) {
214
+ return _jsx("span", { className: "sb-inline-text", children: children });
215
+ }
216
+ export function PartLoading({ kind, label, }) {
217
+ if (kind === "reasoning")
218
+ return _jsx(ThinkingIndicator, {});
219
+ if (kind === "tool")
220
+ return _jsx(RunningRow, { tool: { label: label ?? "Running tool", status: "running" } });
221
+ return (_jsxs("div", { className: "sb-part-loading", "data-kind": kind, role: "status", children: [_jsx("span", {}), _jsx("span", {}), _jsx("span", {})] }));
222
+ }
223
+ export function UnknownPart({ part }) {
224
+ const type = part && typeof part === "object" && typeof part.type === "string"
225
+ ? part.type
226
+ : "unknown";
227
+ return (_jsxs("div", { className: "sb-unknown-part", "data-part-type": "unknown", "data-unknown-type": type, children: [_jsx(AlertCircleIcon, { size: 15 }), _jsxs("span", { children: ["\u6682\u4E0D\u652F\u6301\u7684\u6D88\u606F\u7EC4\u4EF6\uFF1A", type] })] }));
228
+ }
229
+ function objectOf(value) {
230
+ return value != null && typeof value === "object"
231
+ ? value
232
+ : {};
233
+ }
234
+ function strings(value) {
235
+ return Array.isArray(value)
236
+ ? value.filter((item) => typeof item === "string")
237
+ : [];
238
+ }
239
+ function dataOf(part) {
240
+ const record = objectOf(part);
241
+ return objectOf(record.data ?? record.input ?? record.arguments ?? record);
242
+ }
243
+ function fileKind(mediaType, filename) {
244
+ const value = `${String(mediaType ?? "")} ${String(filename ?? "")}`;
245
+ if (/image/i.test(value))
246
+ return "image";
247
+ if (/audio/i.test(value))
248
+ return "audio";
249
+ if (/csv|sheet|excel/i.test(value))
250
+ return "csv";
251
+ if (/html/i.test(value))
252
+ return "html";
253
+ if (/code|javascript|typescript|json|xml/i.test(value))
254
+ return "code";
255
+ return "doc";
256
+ }
257
+ export function DefaultPartRenderer({ part, context, }) {
258
+ const record = objectOf(part);
259
+ if (record.type === "step-start")
260
+ return null;
261
+ const data = dataOf(part);
262
+ const kind = resolvePartKind(part);
263
+ const toolName = toolNameOfPart(part);
264
+ const toolState = String(record.state ?? data.state ?? "output-available");
265
+ const withToolApproval = (children) => toolName ? (_jsx(ToolApprovalPresentation, { toolName: toolName, state: toolState, context: context, children: children })) : (children);
266
+ if (kind === "text") {
267
+ return _jsx(TextPart, { text: String(record.text ?? data.text ?? ""), streaming: context.isActive });
268
+ }
269
+ if (kind === "reasoning") {
270
+ return (_jsx(ReasoningPart, { text: String(record.thinking ?? record.text ?? data.detail ?? data.text ?? ""), label: typeof data.label === "string" ? data.label : undefined, streaming: context.isActive && record.state === "streaming" }));
271
+ }
272
+ if (kind === "tool") {
273
+ const name = toolName ?? String(data.name ?? "tool");
274
+ const state = toolState;
275
+ const pending = state === "input-available" || state === "input-streaming";
276
+ return withToolApproval(_jsx(ToolPart, { name: name, label: typeof data.label === "string" ? data.label : undefined, icon: typeof data.icon === "string" && data.icon in toolIcons ? data.icon : undefined, state: pending && !context.isActive ? "interrupted" : state, input: record.input ?? record.arguments, output: record.output ?? data.output, errorText: typeof record.errorText === "string" ? record.errorText : undefined, command: typeof data.command === "string" ? data.command : undefined, meta: typeof data.meta === "string" ? data.meta : undefined, results: Array.isArray(data.results) ? data.results : undefined, activityId: context.activityId, onNavigate: context.actions.onNavigate }));
277
+ }
278
+ if (kind === "error") {
279
+ return _jsx(ErrorPart, { title: String(data.title ?? "Error"), body: String(data.body ?? data.message ?? "请求失败") });
280
+ }
281
+ if (kind === "image") {
282
+ return _jsx(ImagePart, { src: String(record.src ?? record.url ?? data.src ?? data.url ?? ""), alt: String(record.alt ?? record.filename ?? data.alt ?? data.filename ?? "图片"), onPreview: () => context.actions.onPreview?.(part) });
283
+ }
284
+ if (kind === "chart") {
285
+ return _jsx(ChartPart, { title: String(data.title ?? "Chart"), unit: typeof data.unit === "string" ? data.unit : undefined, bars: Array.isArray(data.bars) ? data.bars : [], expandable: Boolean(data.expandable), onPreview: () => context.actions.onPreview?.(part) });
286
+ }
287
+ if (kind === "agent_card") {
288
+ return _jsx(AgentCardPart, { title: String(data.title ?? "Agent"), agentName: String(data.agentName ?? data.name ?? "Agent"), agentDesc: String(data.agentDesc ?? data.description ?? ""), onNavigate: () => context.actions.onNavigate?.(String(data.target ?? "agents")) });
289
+ }
290
+ if (kind === "plan") {
291
+ if (!context.isLatestOfKind)
292
+ return null;
293
+ const steps = planStepsOfPart(part);
294
+ return withToolApproval(_jsx(PlanPart, { title: String(data.title ?? "任务计划"), steps: steps, streaming: context.isActive && context.status === "streaming" }));
295
+ }
296
+ if (kind === "schedule") {
297
+ return withToolApproval(_jsx(SchedulePart, { title: String(data.title ?? "安排任务"), cadence: String(data.cadence ?? data.schedule ?? ""), nextRun: String(data.nextRun ?? data.next_run ?? ""), scheduled: Boolean(data.scheduled), onSchedule: () => context.actions.onAnswer?.({ type: "schedule", data }) }));
298
+ }
299
+ if (kind === "parallel") {
300
+ return withToolApproval(_jsx(ParallelPart, { label: typeof data.label === "string" ? data.label : undefined, tools: Array.isArray(data.tools) ? data.tools : [], isActive: context.isActive }));
301
+ }
302
+ if (kind === "subagents") {
303
+ const agents = Array.isArray(data.agents)
304
+ ? data.agents
305
+ : [{ name: String(data.name ?? "Sub-agent"), summary: String(data.summary ?? ""), status: context.isActive ? "running" : "done" }];
306
+ return withToolApproval(_jsx(SubAgentsPart, { agents: agents, isActive: context.isActive }));
307
+ }
308
+ if (kind === "confirmation") {
309
+ return _jsx(ConfirmationPart, { id: String(data.id ?? context.activityId), title: String(data.title ?? "确认操作?"), body: typeof data.body === "string" ? data.body : undefined, confirmLabel: String(data.confirmLabel ?? "确认"), cancelLabel: String(data.cancelLabel ?? "取消"), onConfirm: (confirmed) => context.actions.onApprove?.(String(data.id ?? context.activityId), confirmed ? "allow_once" : "deny") });
310
+ }
311
+ if (kind === "file") {
312
+ const filename = String(record.filename ?? data.name ?? data.filename ?? record.mediaType ?? "附件");
313
+ return _jsx(FilePart, { name: filename, kind: fileKind(record.mediaType ?? data.mediaType, filename), note: typeof data.note === "string" ? data.note : undefined, url: typeof record.url === "string" ? record.url : typeof data.url === "string" ? data.url : undefined, artifact: part, onPreview: context.actions.onPreview, onDownload: context.actions.onDownload });
314
+ }
315
+ if (kind === "dashboard") {
316
+ return _jsx(DashboardPart, { ...data, title: String(data.title ?? "Dashboard") });
317
+ }
318
+ if (kind === "ask_user") {
319
+ const questions = Array.isArray(data.questions)
320
+ ? data.questions
321
+ : [{
322
+ prompt: String(data.question ?? "请选择"),
323
+ kind: data.multiSelect === true ? "multi" : "single",
324
+ options: strings(data.options),
325
+ }];
326
+ return withToolApproval(_jsx(AskUserPart, { questions: questions, freeTextPlaceholder: typeof data.freeTextPlaceholder === "string" ? data.freeTextPlaceholder : undefined, submittedAnswer: context.replyText, onSubmit: (payload) => context.actions.onAnswer?.(payload) }));
327
+ }
328
+ if (kind === "suggestions") {
329
+ return withToolApproval(_jsx(Suggestions, { items: strings(data.items), onPick: context.actions.onSuggestion }));
330
+ }
331
+ return _jsx(UnknownPart, { part: part });
332
+ }
@@ -0,0 +1,3 @@
1
+ import type { RichPartKind } from "../contracts";
2
+ export declare function toolNameOfPart(part: unknown): string | null;
3
+ export declare function resolvePartKind(part: unknown): RichPartKind | null;
@@ -0,0 +1,69 @@
1
+ function objectOf(value) {
2
+ return value != null && typeof value === "object"
3
+ ? value
4
+ : {};
5
+ }
6
+ export function toolNameOfPart(part) {
7
+ const record = objectOf(part);
8
+ const type = typeof record.type === "string" ? record.type : "";
9
+ if (type === "toolCall") {
10
+ return typeof record.name === "string" ? record.name : "tool";
11
+ }
12
+ if (type === "dynamic-tool") {
13
+ return typeof record.toolName === "string" ? record.toolName : "dynamic";
14
+ }
15
+ return type.startsWith("tool-") ? type.slice(5) : null;
16
+ }
17
+ export function resolvePartKind(part) {
18
+ const record = objectOf(part);
19
+ const type = typeof record.type === "string" ? record.type : "";
20
+ const toolName = toolNameOfPart(part);
21
+ if (type === "thinking")
22
+ return "reasoning";
23
+ if (type === "tool")
24
+ return "tool";
25
+ if (toolName === "update_plan")
26
+ return "plan";
27
+ if (toolName === "ask_user")
28
+ return "ask_user";
29
+ if (toolName === "suggest_followups")
30
+ return "suggestions";
31
+ if (toolName === "schedule")
32
+ return "schedule";
33
+ if (toolName === "parallel")
34
+ return "parallel";
35
+ if (toolName === "subagents" ||
36
+ toolName === "run_agent" ||
37
+ toolName === "run_temporary_agent" ||
38
+ toolName === "fanout" ||
39
+ toolName === "extract" ||
40
+ toolName === "dispatch_background") {
41
+ return "subagents";
42
+ }
43
+ if (toolName)
44
+ return "tool";
45
+ const normalized = type.startsWith("data-")
46
+ ? type.slice(5).replaceAll("-", "_")
47
+ : type;
48
+ return [
49
+ "text",
50
+ "reasoning",
51
+ "error",
52
+ "image",
53
+ "chart",
54
+ "agent_card",
55
+ "plan",
56
+ "schedule",
57
+ "parallel",
58
+ "subagents",
59
+ "confirmation",
60
+ "file",
61
+ "dashboard",
62
+ "ask_user",
63
+ "suggestions",
64
+ ].includes(normalized)
65
+ ? normalized
66
+ : type === "reasoning-file"
67
+ ? "file"
68
+ : null;
69
+ }
@@ -0,0 +1,10 @@
1
+ export type PlanStepStatus = "pending" | "in_progress" | "done";
2
+ export interface PlanStep {
3
+ text: string;
4
+ status?: PlanStepStatus;
5
+ }
6
+ export declare function planStepsOfPart(part: unknown): PlanStep[];
7
+ export declare function projectLatestPlan(messages: readonly {
8
+ role: string;
9
+ parts: readonly unknown[];
10
+ }[]): PlanStep[] | null;
@@ -0,0 +1,40 @@
1
+ import { resolvePartKind } from "./kind";
2
+ function recordOf(value) {
3
+ return value != null && typeof value === "object"
4
+ ? value
5
+ : {};
6
+ }
7
+ export function planStepsOfPart(part) {
8
+ const record = recordOf(part);
9
+ const data = recordOf(record.data ?? record.input ?? record.arguments ?? record);
10
+ if (!Array.isArray(data.steps))
11
+ return [];
12
+ return data.steps.flatMap((step) => {
13
+ const value = recordOf(step);
14
+ const text = typeof value.text === "string" ? value.text.trim() : "";
15
+ if (!text)
16
+ return [];
17
+ const status = value.status === "in_progress" || value.status === "done"
18
+ ? value.status
19
+ : "pending";
20
+ return [{ text, status }];
21
+ });
22
+ }
23
+ export function projectLatestPlan(messages) {
24
+ for (let messageIndex = messages.length - 1; messageIndex >= 0; messageIndex -= 1) {
25
+ const message = messages[messageIndex];
26
+ if (message.role !== "assistant")
27
+ continue;
28
+ for (let partIndex = message.parts.length - 1; partIndex >= 0; partIndex -= 1) {
29
+ const part = message.parts[partIndex];
30
+ if (resolvePartKind(part) !== "plan")
31
+ continue;
32
+ const steps = planStepsOfPart(part);
33
+ if (steps.length > 0)
34
+ return steps;
35
+ if (recordOf(part).state !== "input-streaming")
36
+ return null;
37
+ }
38
+ }
39
+ return null;
40
+ }
@@ -0,0 +1,70 @@
1
+ import { type ButtonHTMLAttributes, type DetailsHTMLAttributes, type HTMLAttributes, type ReactNode } from "react";
2
+ import type { AIToolPart } from "../contracts";
3
+ export interface ReasoningProps extends HTMLAttributes<HTMLDivElement> {
4
+ isStreaming?: boolean;
5
+ open?: boolean;
6
+ defaultOpen?: boolean;
7
+ onOpenChange?: (open: boolean) => void;
8
+ duration?: number;
9
+ }
10
+ export declare function Reasoning({ isStreaming, open, defaultOpen, onOpenChange, duration, className, children, ...props }: ReasoningProps): import("react").JSX.Element;
11
+ export declare function ReasoningTrigger({ children, className, ...props }: ButtonHTMLAttributes<HTMLButtonElement>): import("react").JSX.Element;
12
+ export declare function ReasoningContent({ children, className, ...props }: Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
13
+ children: string;
14
+ }): import("react").JSX.Element | null;
15
+ type ToolPart = AIToolPart;
16
+ export type ToolState = ToolPart["state"];
17
+ export declare function getStatusBadge(status: ToolState): import("react").JSX.Element;
18
+ export declare const Tool: import("react").ForwardRefExoticComponent<DetailsHTMLAttributes<HTMLDetailsElement> & import("react").RefAttributes<HTMLDetailsElement>>;
19
+ type ToolHeaderProps = HTMLAttributes<HTMLElement> & {
20
+ title?: string;
21
+ type: ToolPart["type"];
22
+ state: ToolState;
23
+ toolName?: string;
24
+ };
25
+ export declare function ToolHeader({ className, title, type, state, toolName, ...props }: ToolHeaderProps): import("react").JSX.Element;
26
+ export declare const ToolContent: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
27
+ export declare function ToolInput({ input, className, ...props }: HTMLAttributes<HTMLPreElement> & {
28
+ input: unknown;
29
+ }): import("react").JSX.Element;
30
+ export declare function ToolOutput({ output, errorText, className, ...props }: HTMLAttributes<HTMLPreElement> & {
31
+ output: unknown;
32
+ errorText?: string;
33
+ }): import("react").JSX.Element | null;
34
+ export interface ConfirmationProps extends HTMLAttributes<HTMLDivElement> {
35
+ state: ToolState | "pending" | "confirmed" | "cancelled";
36
+ approval?: {
37
+ id: string;
38
+ approved?: boolean;
39
+ };
40
+ }
41
+ export declare function Confirmation({ state, approval, className, ...props }: ConfirmationProps): import("react").JSX.Element;
42
+ export declare const ConfirmationTitle: ({ className, ...props }: HTMLAttributes<HTMLDivElement>) => import("react").JSX.Element;
43
+ export declare function ConfirmationRequest({ children }: {
44
+ children?: ReactNode;
45
+ }): ReactNode;
46
+ export declare function ConfirmationAccepted({ children }: {
47
+ children?: ReactNode;
48
+ }): ReactNode;
49
+ export declare function ConfirmationRejected({ children }: {
50
+ children?: ReactNode;
51
+ }): ReactNode;
52
+ export declare function ConfirmationActions({ className, ...props }: HTMLAttributes<HTMLDivElement>): import("react").JSX.Element | null;
53
+ export declare const ConfirmationAction: import("react").ForwardRefExoticComponent<ButtonHTMLAttributes<HTMLButtonElement> & import("react").RefAttributes<HTMLButtonElement>>;
54
+ export declare const Plan: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
55
+ isStreaming?: boolean;
56
+ } & import("react").RefAttributes<HTMLDivElement>>;
57
+ export declare const PlanHeader: ({ className, ...props }: HTMLAttributes<HTMLDivElement>) => import("react").JSX.Element;
58
+ export declare const PlanTitle: ({ className, ...props }: HTMLAttributes<HTMLHeadingElement>) => import("react").JSX.Element;
59
+ export declare const PlanDescription: ({ className, ...props }: HTMLAttributes<HTMLParagraphElement>) => import("react").JSX.Element;
60
+ export declare const PlanContent: ({ className, ...props }: HTMLAttributes<HTMLDivElement>) => import("react").JSX.Element;
61
+ export declare const PlanFooter: ({ className, ...props }: HTMLAttributes<HTMLDivElement>) => import("react").JSX.Element;
62
+ export declare const Sources: import("react").ForwardRefExoticComponent<DetailsHTMLAttributes<HTMLDetailsElement> & import("react").RefAttributes<HTMLDetailsElement>>;
63
+ export declare function SourcesTrigger({ count, children, className, ...props }: HTMLAttributes<HTMLElement> & {
64
+ count: number;
65
+ }): import("react").JSX.Element;
66
+ export declare const SourcesContent: ({ className, ...props }: HTMLAttributes<HTMLDivElement>) => import("react").JSX.Element;
67
+ export declare function Source({ title, children, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement> & {
68
+ title?: string;
69
+ }): import("react").JSX.Element;
70
+ export {};