@springbrand/message-panel 0.1.2 → 0.1.3-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/demo/camel-chat-showcase.tsx +1265 -0
  2. package/demo/fixtures.ts +292 -0
  3. package/demo/message-panel-chat-showcase.tsx +250 -0
  4. package/demo/message-panel-gallery.tsx +389 -0
  5. package/package.json +6 -16
  6. package/src/adapters/ai-sdk.ts +32 -0
  7. package/src/camel/camel-chat-messages.tsx +1423 -0
  8. package/src/camel/camel-model-select.tsx +88 -0
  9. package/src/camel/camel-plan-bar.tsx +115 -0
  10. package/src/camel/camel-prompt-input.tsx +547 -0
  11. package/src/camel/camel-tool-presentation.tsx +1270 -0
  12. package/src/camel/camel-turn.ts +316 -0
  13. package/src/camel/turn-summary-bar.tsx +81 -0
  14. package/src/chat-summary-panel.tsx +229 -0
  15. package/src/composer/chat-composer.tsx +216 -0
  16. package/src/composer/composer-attachments.tsx +138 -0
  17. package/src/composer/composer-trigger-popover.tsx +236 -0
  18. package/src/composer/composer.tsx +425 -0
  19. package/src/composer/drop-controller.ts +80 -0
  20. package/src/composer/types.ts +79 -0
  21. package/src/contracts.ts +138 -0
  22. package/src/conversation.tsx +76 -0
  23. package/src/internal/cn.ts +3 -0
  24. package/src/message-panel.tsx +368 -0
  25. package/src/message.tsx +254 -0
  26. package/src/parts/index.tsx +1149 -0
  27. package/src/parts/kind.ts +67 -0
  28. package/src/parts/plan.ts +63 -0
  29. package/src/parts/primitives.tsx +439 -0
  30. package/src/registry.ts +44 -0
  31. package/src/status.tsx +132 -0
  32. package/dist/demo/camel-chat-showcase.d.ts +0 -1
  33. package/dist/demo/camel-chat-showcase.js +0 -1007
  34. package/dist/demo/fixtures.d.ts +0 -32
  35. package/dist/demo/fixtures.js +0 -250
  36. package/dist/demo/index.js +0 -4
  37. package/dist/demo/message-panel-chat-showcase.d.ts +0 -1
  38. package/dist/demo/message-panel-chat-showcase.js +0 -114
  39. package/dist/demo/message-panel-gallery.d.ts +0 -1
  40. package/dist/demo/message-panel-gallery.js +0 -130
  41. package/dist/src/adapters/ai-sdk.d.ts +0 -4
  42. package/dist/src/adapters/ai-sdk.js +0 -25
  43. package/dist/src/camel/camel-chat-messages.d.ts +0 -44
  44. package/dist/src/camel/camel-chat-messages.js +0 -421
  45. package/dist/src/camel/camel-model-select.d.ts +0 -13
  46. package/dist/src/camel/camel-model-select.js +0 -7
  47. package/dist/src/camel/camel-plan-bar.d.ts +0 -5
  48. package/dist/src/camel/camel-plan-bar.js +0 -24
  49. package/dist/src/camel/camel-prompt-input.d.ts +0 -32
  50. package/dist/src/camel/camel-prompt-input.js +0 -182
  51. package/dist/src/camel/camel-tool-presentation.d.ts +0 -21
  52. package/dist/src/camel/camel-tool-presentation.js +0 -784
  53. package/dist/src/camel/camel-turn.d.ts +0 -57
  54. package/dist/src/camel/camel-turn.js +0 -203
  55. package/dist/src/camel/index.js +0 -7
  56. package/dist/src/camel/turn-summary-bar.d.ts +0 -8
  57. package/dist/src/camel/turn-summary-bar.js +0 -12
  58. package/dist/src/chat-summary-panel.d.ts +0 -22
  59. package/dist/src/chat-summary-panel.js +0 -26
  60. package/dist/src/composer/chat-composer.d.ts +0 -4
  61. package/dist/src/composer/chat-composer.js +0 -90
  62. package/dist/src/composer/composer-attachments.d.ts +0 -13
  63. package/dist/src/composer/composer-attachments.js +0 -47
  64. package/dist/src/composer/composer-trigger-popover.d.ts +0 -31
  65. package/dist/src/composer/composer-trigger-popover.js +0 -29
  66. package/dist/src/composer/composer.d.ts +0 -15
  67. package/dist/src/composer/composer.js +0 -180
  68. package/dist/src/composer/drop-controller.d.ts +0 -8
  69. package/dist/src/composer/drop-controller.js +0 -69
  70. package/dist/src/composer/index.js +0 -6
  71. package/dist/src/composer/types.d.ts +0 -62
  72. package/dist/src/composer/types.js +0 -1
  73. package/dist/src/contracts.d.ts +0 -94
  74. package/dist/src/contracts.js +0 -1
  75. package/dist/src/conversation.d.ts +0 -13
  76. package/dist/src/conversation.js +0 -18
  77. package/dist/src/index.js +0 -12
  78. package/dist/src/internal/cn.d.ts +0 -1
  79. package/dist/src/internal/cn.js +0 -3
  80. package/dist/src/message-panel.d.ts +0 -4
  81. package/dist/src/message-panel.js +0 -144
  82. package/dist/src/message.d.ts +0 -36
  83. package/dist/src/message.js +0 -83
  84. package/dist/src/parts/index.d.ts +0 -187
  85. package/dist/src/parts/index.js +0 -332
  86. package/dist/src/parts/kind.d.ts +0 -3
  87. package/dist/src/parts/kind.js +0 -69
  88. package/dist/src/parts/plan.d.ts +0 -10
  89. package/dist/src/parts/plan.js +0 -40
  90. package/dist/src/parts/primitives.d.ts +0 -70
  91. package/dist/src/parts/primitives.js +0 -153
  92. package/dist/src/registry.d.ts +0 -4
  93. package/dist/src/registry.js +0 -31
  94. package/dist/src/status.d.ts +0 -26
  95. package/dist/src/status.js +0 -74
  96. /package/{dist/demo/index.d.ts → demo/index.ts} +0 -0
  97. /package/{dist/src/camel/index.d.ts → src/camel/index.ts} +0 -0
  98. /package/{dist/src/composer/index.d.ts → src/composer/index.ts} +0 -0
  99. /package/{dist/src/index.d.ts → src/index.ts} +0 -0
@@ -1,32 +0,0 @@
1
- import type { MessageAdapter, MessagePanelStatus } from "../src/contracts";
2
- export interface DemoMessage {
3
- id: string;
4
- role: "user" | "assistant";
5
- parts: Array<Record<string, unknown>>;
6
- metadata?: Record<string, unknown>;
7
- }
8
- export declare const demoMessageAdapter: MessageAdapter<DemoMessage>;
9
- export declare const allPartsMessage: DemoMessage;
10
- export declare const basicMessages: DemoMessage[];
11
- export declare const panelScenarios: Array<{
12
- id: string;
13
- title: string;
14
- messages: DemoMessage[];
15
- status: MessagePanelStatus;
16
- flags?: {
17
- isHydrating?: boolean;
18
- isRecovering?: boolean;
19
- isServerStreaming?: boolean;
20
- };
21
- error?: Error;
22
- }>;
23
- export interface DemoAttachment {
24
- id: string;
25
- filename: string;
26
- status: "uploading" | "ready" | "error";
27
- mediaType: string;
28
- size: number;
29
- }
30
- export declare const readyAttachment: DemoAttachment;
31
- export declare const uploadingAttachment: DemoAttachment;
32
- export declare const errorAttachment: DemoAttachment;
@@ -1,250 +0,0 @@
1
- export const demoMessageAdapter = {
2
- getId: (message) => message.id,
3
- getRole: (message) => message.role,
4
- getParts: (message) => message.parts,
5
- getMetadata: (message) => message.metadata,
6
- getText: (message) => message.parts
7
- .filter((part) => part.type === "text")
8
- .map((part) => String(part.text ?? ""))
9
- .join("\n"),
10
- isPartRenderable: () => true,
11
- };
12
- const demoImage = "data:image/svg+xml;charset=utf-8," +
13
- encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="900" height="520"><defs><linearGradient id="g" x1="0" x2="1" y1="0" y2="1"><stop stop-color="#39274f"/><stop offset=".55" stop-color="#e07042"/><stop offset="1" stop-color="#f6c274"/></linearGradient></defs><rect width="900" height="520" fill="url(#g)"/><circle cx="700" cy="125" r="68" fill="#ffe4ad"/><path d="M0 410 190 210 360 410 540 170 900 430V520H0Z" fill="#191e2c"/><path d="M270 435V320l80-58 82 58v115Z" fill="#69432e"/><rect x="324" y="348" width="48" height="52" fill="#ffd47d"/></svg>');
14
- export const allPartsMessage = {
15
- id: "assistant-all-parts",
16
- role: "assistant",
17
- parts: [
18
- {
19
- type: "text",
20
- text: "## Message Panel\n\n支持 **GFM Markdown**、真实流式状态与富组件叙事流。",
21
- },
22
- {
23
- type: "reasoning",
24
- label: "Thought for 3s",
25
- detail: "先理解目标,再安排步骤和并行任务。",
26
- },
27
- {
28
- type: "tool",
29
- icon: "web",
30
- label: "Searched the web",
31
- meta: "2 results",
32
- status: "output-available",
33
- results: [
34
- { title: "AI interface patterns", domain: "example.com" },
35
- { title: "Streaming UI guide", domain: "docs.example.com" },
36
- ],
37
- },
38
- {
39
- type: "error",
40
- title: "一项后台任务已中断",
41
- body: "连接恢复后可以安全重试,不会留下无限加载状态。",
42
- },
43
- {
44
- type: "image",
45
- src: demoImage,
46
- alt: "雪山日落与木屋的示意图",
47
- },
48
- {
49
- type: "chart",
50
- title: "Keyword opportunities",
51
- unit: "",
52
- expandable: true,
53
- bars: [
54
- { label: "Agent UI", value: 96 },
55
- { label: "RAG", value: 78 },
56
- { label: "Tools", value: 65 },
57
- { label: "Memory", value: 54 },
58
- ],
59
- },
60
- {
61
- type: "agent_card",
62
- title: "Your agent is ready",
63
- agentName: "Research Analyst",
64
- agentDesc: "持续调研市场并把结果汇总成结构化报告。",
65
- },
66
- {
67
- type: "plan",
68
- title: "任务计划",
69
- steps: [
70
- { text: "确认目标与边界", status: "done" },
71
- { text: "检索并验证资料", status: "in_progress" },
72
- { text: "交付报告", status: "pending" },
73
- ],
74
- },
75
- {
76
- type: "schedule",
77
- title: "每周发送竞品报告",
78
- cadence: "Every Monday at 9:00 AM",
79
- nextRun: "Mon, Aug 3 · Asia/Shanghai",
80
- },
81
- {
82
- type: "parallel",
83
- label: "Parallel research",
84
- tools: [
85
- { icon: "web", label: "市场调研", status: "done", meta: "8 sources" },
86
- { icon: "code", label: "数据分析", status: "running" },
87
- { icon: "file", label: "汇总报告", status: "done" },
88
- ],
89
- },
90
- {
91
- type: "subagents",
92
- agents: [
93
- {
94
- name: "Research Agent",
95
- tint: "#5577cc",
96
- summary: "完成 8 个来源的交叉验证。",
97
- status: "done",
98
- },
99
- {
100
- name: "Data Agent",
101
- tint: "#8f61c7",
102
- summary: "正在计算趋势指标。",
103
- status: "running",
104
- },
105
- ],
106
- },
107
- {
108
- type: "confirmation",
109
- id: "demo-confirmation",
110
- title: "创建每周竞品 Agent?",
111
- body: "将按上述计划在每周一运行。",
112
- confirmLabel: "创建 Agent",
113
- cancelLabel: "暂不",
114
- },
115
- {
116
- type: "file",
117
- name: "competitor-report.md",
118
- kind: "doc",
119
- note: "Markdown · 14 KB",
120
- },
121
- {
122
- type: "dashboard",
123
- title: "Agent 运行概览",
124
- tiles: [
125
- { label: "完成任务", value: "42" },
126
- { label: "成功率", value: "98%" },
127
- { label: "节省时间", value: "16h" },
128
- ],
129
- cards: [
130
- { icon: "🔎", title: "Research", body: "8 个来源完成验证" },
131
- { icon: "📊", title: "Analysis", body: "3 个趋势值得关注" },
132
- { icon: "📝", title: "Report", body: "报告已生成" },
133
- ],
134
- badges: ["实时流式", "可恢复", "工作区联动"],
135
- fileNote: {
136
- title: "文件已生成",
137
- body: "报告已保存到 Workspace。",
138
- },
139
- table: {
140
- columns: ["能力", "状态"],
141
- rows: [
142
- ["Message", "Ready"],
143
- ["Composer", "Ready"],
144
- ],
145
- },
146
- },
147
- {
148
- type: "ask_user",
149
- questions: [
150
- {
151
- prompt: "报告通过什么渠道发送?",
152
- kind: "single",
153
- options: ["Email", "Slack", "Both"],
154
- },
155
- {
156
- prompt: "需要包含哪些内容?",
157
- kind: "multi",
158
- options: ["定价", "功能", "动态"],
159
- },
160
- ],
161
- freeTextPlaceholder: "补充竞争对手名称…",
162
- },
163
- {
164
- type: "suggestions",
165
- items: ["立即运行一次", "加入 Slack 通知", "编辑跟踪名单"],
166
- },
167
- ],
168
- };
169
- export const basicMessages = [
170
- {
171
- id: "demo-user",
172
- role: "user",
173
- parts: [{ type: "text", text: "帮我制作一份竞品周报。" }],
174
- },
175
- {
176
- id: "demo-assistant",
177
- role: "assistant",
178
- parts: [{ type: "text", text: "好的,我会先做计划,再开始调研。" }],
179
- },
180
- ];
181
- export const panelScenarios = [
182
- { id: "empty", title: "Empty", messages: [], status: "ready" },
183
- {
184
- id: "hydrating",
185
- title: "Hydrating",
186
- messages: basicMessages,
187
- status: "ready",
188
- flags: { isHydrating: true },
189
- },
190
- {
191
- id: "thinking",
192
- title: "Thinking / Submitted",
193
- messages: basicMessages.slice(0, 1),
194
- status: "submitted",
195
- },
196
- {
197
- id: "streaming",
198
- title: "Streaming / Playing",
199
- messages: [
200
- basicMessages[0],
201
- {
202
- id: "streaming-assistant",
203
- role: "assistant",
204
- parts: [{ type: "text", text: "正在检索并交叉验证资料…" }],
205
- },
206
- ],
207
- status: "streaming",
208
- },
209
- {
210
- id: "completed",
211
- title: "Completed / Ready",
212
- messages: basicMessages,
213
- status: "ready",
214
- },
215
- {
216
- id: "error",
217
- title: "Error / Interrupted",
218
- messages: basicMessages,
219
- status: "error",
220
- error: new Error("网络连接已中断,可安全重试。"),
221
- },
222
- {
223
- id: "recovering",
224
- title: "Recovering / Background",
225
- messages: basicMessages,
226
- status: "ready",
227
- flags: { isRecovering: true, isServerStreaming: true },
228
- },
229
- ];
230
- export const readyAttachment = {
231
- id: "ready-file",
232
- filename: "brief.pdf",
233
- status: "ready",
234
- mediaType: "application/pdf",
235
- size: 21840,
236
- };
237
- export const uploadingAttachment = {
238
- ...readyAttachment,
239
- id: "uploading-file",
240
- filename: "research.csv",
241
- status: "uploading",
242
- mediaType: "text/csv",
243
- };
244
- export const errorAttachment = {
245
- ...readyAttachment,
246
- id: "error-file",
247
- filename: "broken.zip",
248
- status: "error",
249
- mediaType: "application/zip",
250
- };
@@ -1,4 +0,0 @@
1
- export * from "./camel-chat-showcase";
2
- export * from "./fixtures";
3
- export * from "./message-panel-chat-showcase";
4
- export * from "./message-panel-gallery";
@@ -1 +0,0 @@
1
- export declare function MessagePanelChatShowcase(): import("react").JSX.Element;
@@ -1,114 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useMemo, useState } from "react";
3
- import { MessagePanel } from "../src";
4
- import { Composer, DropOverlay } from "../src/composer";
5
- import { allPartsMessage, basicMessages, demoMessageAdapter, errorAttachment, panelScenarios, readyAttachment, uploadingAttachment, } from "./fixtures";
6
- const noOp = () => undefined;
7
- const composerScenarios = [
8
- { id: "empty", title: "Empty", status: "ready" },
9
- {
10
- id: "drafting",
11
- title: "Drafting",
12
- value: "为这份数据生成报告",
13
- status: "ready",
14
- },
15
- { id: "drag-active", title: "Drag active", status: "ready", overlay: true },
16
- {
17
- id: "uploading",
18
- title: "Uploading",
19
- attachments: [uploadingAttachment],
20
- status: "ready",
21
- },
22
- {
23
- id: "attachment-error",
24
- title: "Attachment error",
25
- value: "不要丢掉这份草稿",
26
- attachments: [errorAttachment],
27
- status: "ready",
28
- },
29
- {
30
- id: "ready-with-attachments",
31
- title: "Ready with attachments",
32
- attachments: [readyAttachment],
33
- status: "ready",
34
- },
35
- {
36
- id: "submitted-streaming",
37
- title: "Submitted / Streaming",
38
- value: "正在发送…",
39
- status: "streaming",
40
- },
41
- ];
42
- const controlStyle = {
43
- minHeight: 34,
44
- padding: "6px 10px",
45
- border: "1px solid var(--border)",
46
- borderRadius: 9,
47
- color: "var(--foreground)",
48
- background: "var(--card)",
49
- font: "inherit",
50
- fontSize: 12,
51
- };
52
- export function MessagePanelChatShowcase() {
53
- const [theme, setTheme] = useState("dark");
54
- const [panelScenarioId, setPanelScenarioId] = useState("completed");
55
- const [composerScenarioId, setComposerScenarioId] = useState("empty");
56
- const panelScenario = panelScenarios.find((scenario) => scenario.id === panelScenarioId) ??
57
- panelScenarios[4];
58
- const composerScenario = composerScenarios.find((scenario) => scenario.id === composerScenarioId) ?? composerScenarios[0];
59
- const messages = useMemo(() => panelScenario.id === "completed"
60
- ? [basicMessages[0], allPartsMessage]
61
- : panelScenario.messages, [panelScenario]);
62
- return (_jsxs("main", { className: theme === "dark" ? "dark" : "", "data-message-panel-chat-showcase": "", style: {
63
- display: "grid",
64
- height: "100dvh",
65
- minHeight: 0,
66
- gridTemplateRows: "auto minmax(0, 1fr) auto",
67
- overflow: "hidden",
68
- color: "var(--foreground)",
69
- background: "var(--background)",
70
- fontFamily: "var(--font-sans)",
71
- }, children: [_jsxs("header", { style: {
72
- zIndex: 30,
73
- display: "flex",
74
- minWidth: 0,
75
- flexWrap: "wrap",
76
- alignItems: "center",
77
- gap: 10,
78
- padding: "10px clamp(12px, 2vw, 24px)",
79
- borderBottom: "1px solid var(--border)",
80
- background: "var(--background)",
81
- }, children: [_jsx("a", { href: "/", style: {
82
- color: "var(--muted-foreground)",
83
- fontSize: 13,
84
- textDecoration: "none",
85
- }, children: "\u2190 \u8FD4\u56DE\u7CFB\u7EDF" }), _jsxs("div", { style: { minWidth: 190, marginRight: "auto" }, children: [_jsx("h1", { style: { margin: 0, fontSize: 14 }, children: "UI \u5168\u573A\u666F\u9A8C\u6536 Chat" }), _jsx("small", { style: { color: "var(--muted-foreground)" }, children: "16 \u7C7B\u7EC4\u4EF6 \u00B7 7 \u7C7B\u751F\u547D\u5468\u671F \u00B7 7 \u7C7B\u8F93\u5165\u72B6\u6001" })] }), _jsxs("label", { style: {
86
- display: "flex",
87
- alignItems: "center",
88
- gap: 7,
89
- color: "var(--muted-foreground)",
90
- fontSize: 12,
91
- }, children: ["\u6D88\u606F\u72B6\u6001", _jsx("select", { "aria-label": "\u6D88\u606F\u72B6\u6001", value: panelScenarioId, onChange: (event) => setPanelScenarioId(event.currentTarget.value), style: controlStyle, children: panelScenarios.map((scenario) => (_jsx("option", { value: scenario.id, children: scenario.title }, scenario.id))) })] }), _jsxs("label", { style: {
92
- display: "flex",
93
- alignItems: "center",
94
- gap: 7,
95
- color: "var(--muted-foreground)",
96
- fontSize: 12,
97
- }, children: ["\u8F93\u5165\u72B6\u6001", _jsx("select", { "aria-label": "\u8F93\u5165\u72B6\u6001", value: composerScenarioId, onChange: (event) => setComposerScenarioId(event.currentTarget.value), style: controlStyle, children: composerScenarios.map((scenario) => (_jsx("option", { value: scenario.id, children: scenario.title }, scenario.id))) })] }), _jsx("button", { type: "button", onClick: () => setTheme((current) => (current === "dark" ? "light" : "dark")), style: controlStyle, children: theme === "dark" ? "浅色" : "深色" })] }), _jsx(MessagePanel, { messages: messages, status: panelScenario.status, isHydrating: panelScenario.flags?.isHydrating, isRecovering: panelScenario.flags?.isRecovering, isServerStreaming: panelScenario.flags?.isServerStreaming, error: panelScenario.error, adapter: demoMessageAdapter, actions: {
98
- onSuggestion: noOp,
99
- onAnswer: noOp,
100
- onApprove: noOp,
101
- onPreview: noOp,
102
- onDownload: noOp,
103
- onRetry: noOp,
104
- } }), _jsx("footer", { style: {
105
- position: "relative",
106
- zIndex: 20,
107
- padding: "8px clamp(14px, 4vw, 32px) 18px",
108
- background: "linear-gradient(to top, var(--background) 76%, transparent)",
109
- }, children: _jsxs("div", { "data-composer-showcase": composerScenario.id, style: {
110
- position: "relative",
111
- width: "min(100%, 780px)",
112
- marginInline: "auto",
113
- }, children: [_jsx(Composer, { defaultValue: composerScenario.value, status: composerScenario.status, attachments: composerScenario.attachments, onSubmit: noOp, onStop: noOp, onFilesAdded: noOp, onAttachmentRemove: noOp, onAttachmentRetry: noOp, onAttachmentReorder: noOp, placeholder: "\u8FD9\u662F\u9A8C\u6536\u5939\u5177\uFF0C\u4E0D\u4F1A\u53D1\u9001\u6216\u4FDD\u5B58\u6570\u636E" }, composerScenario.id), composerScenario.overlay && _jsx(DropOverlay, {})] }) })] }));
114
- }
@@ -1 +0,0 @@
1
- export declare function MessagePanelGallery(): import("react").JSX.Element;
@@ -1,130 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useState } from "react";
3
- import { MessagePanel, UnknownPart, } from "../src";
4
- import { ChatComposer, Composer, DropOverlay, } from "../src/composer";
5
- import { allPartsMessage, basicMessages, demoMessageAdapter, errorAttachment, panelScenarios, readyAttachment, uploadingAttachment, } from "./fixtures";
6
- const noOp = () => undefined;
7
- function GalleryHeading({ eyebrow, title, description, }) {
8
- return (_jsxs("header", { style: { display: "grid", gap: 8 }, children: [_jsx("small", { style: { color: "var(--primary)", fontWeight: 700 }, children: eyebrow }), _jsx("h2", { style: { margin: 0, fontSize: 24 }, children: title }), _jsx("p", { style: { maxWidth: 720, margin: 0, color: "var(--muted-foreground)", lineHeight: 1.6 }, children: description })] }));
9
- }
10
- function ComposerScenario({ id, title, value, attachments = [], status = "ready", overlay = false, }) {
11
- return (_jsxs("article", { "data-composer-scenario": id, style: { position: "relative", display: "grid", gap: 8 }, children: [_jsx("strong", { style: { fontSize: 13 }, children: title }), _jsx(Composer, { value: value, status: status, attachments: attachments, onValueChange: noOp, onSubmit: noOp, onStop: noOp, onFilesAdded: noOp, onAttachmentRemove: noOp, onAttachmentRetry: noOp, onAttachmentReorder: noOp }), overlay && _jsx(DropOverlay, {})] }));
12
- }
13
- function ComposerInteractionHarness() {
14
- const [value, setValue] = useState("保留中的草稿");
15
- const [attachments, setAttachments] = useState([
16
- {
17
- id: "alpha",
18
- filename: "alpha.txt",
19
- status: "ready",
20
- mediaType: "text/plain",
21
- size: 12,
22
- },
23
- {
24
- id: "bravo",
25
- filename: "bravo.txt",
26
- status: "ready",
27
- mediaType: "text/plain",
28
- size: 12,
29
- },
30
- ]);
31
- const [events, setEvents] = useState([]);
32
- const record = (event) => setEvents((current) => [...current, event]);
33
- return (_jsxs("section", { "data-composer-interaction-harness": "", style: { display: "grid", gap: 16, marginTop: 48 }, children: [_jsx(GalleryHeading, { eyebrow: "INTERACTION CONTRACTS", title: "Composer \u4EA4\u4E92\u9A8C\u6536", description: "\u8FD9\u4E2A\u53EF\u64CD\u4F5C\u5939\u5177\u8986\u76D6\u53D7\u63A7\u4E0E\u975E\u53D7\u63A7\u8F93\u5165\u3001\u4E09\u79CD\u6587\u4EF6\u5165\u53E3\u3001\u5C40\u90E8\u62D6\u653E\u9694\u79BB\u3001\u6821\u9A8C\u3001\u63D0\u4EA4\u3001\u505C\u6B62\u3001\u91CD\u8BD5\u3001\u79FB\u9664\u548C\u9644\u4EF6\u91CD\u6392\u3002" }), _jsxs("div", { style: {
34
- display: "grid",
35
- gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
36
- gap: 16,
37
- }, children: [_jsx(Composer, { "data-harness-composer": "primary", value: value, onValueChange: setValue, status: "ready", attachments: attachments, accept: ".txt,image/png", maxFiles: 6, maxFileSize: 1024, onFilesAdded: (files) => {
38
- record(`add:${files.map((file) => file.name).join(",")}`);
39
- setAttachments((current) => [
40
- ...current,
41
- ...files.map((file, index) => ({
42
- id: `${file.name}-${current.length + index}`,
43
- filename: file.name,
44
- status: "ready",
45
- mediaType: file.type,
46
- size: file.size,
47
- })),
48
- ]);
49
- }, onAttachmentRemove: (attachment) => {
50
- record(`remove:${attachment.filename}`);
51
- setAttachments((current) => current.filter((item) => item.id !== attachment.id));
52
- }, onAttachmentRetry: (attachment) => record(`retry:${attachment.filename}`), onAttachmentReorder: (fromIndex, toIndex) => {
53
- record(`reorder:${fromIndex}:${toIndex}`);
54
- setAttachments((current) => {
55
- const next = [...current];
56
- const [moved] = next.splice(fromIndex, 1);
57
- next.splice(toIndex, 0, moved);
58
- return next;
59
- });
60
- }, onValidationError: (message) => record(`invalid:${message}`), onSubmit: ({ text }) => record(`submit:${text}`) }), _jsx(ChatComposer, { "data-harness-composer": "secondary", defaultValue: "", status: "ready", commands: [
61
- {
62
- name: "plan",
63
- description: "先制定计划",
64
- prompt: "请先制定计划:\n",
65
- },
66
- {
67
- name: "research",
68
- description: "深入调研",
69
- prompt: "请深入调研:\n",
70
- },
71
- ], onFilesAdded: (files) => record(`secondary:${files.map((file) => file.name).join(",")}`), onSubmit: noOp }), _jsx(Composer, { "data-harness-composer": "uncontrolled", defaultValue: "\u975E\u53D7\u63A7\u8349\u7A3F", status: "ready", onSubmit: ({ text }) => record(`uncontrolled:${text}`) }), _jsx(Composer, { "data-harness-composer": "streaming", value: "\u751F\u6210\u4E2D", status: "streaming", onValueChange: noOp, onSubmit: noOp, onStop: () => record("stop") }), _jsx("div", { "data-sb-message-panel": "", "data-harness-panel-drop": "", children: _jsx(Composer, { "data-harness-composer": "panel-scope", defaultValue: "", status: "ready", dropScope: "panel", onFilesAdded: (files) => record(`panel:${files.map((file) => file.name).join(",")}`), onSubmit: noOp }) })] }), _jsx("output", { "data-harness-order": "", children: attachments.map((attachment) => attachment.filename).join(",") }), _jsx("output", { "data-harness-events": "", children: events.join("|") })] }));
72
- }
73
- export function MessagePanelGallery() {
74
- const [theme, setTheme] = useState("dark");
75
- const rootClass = theme === "dark" ? "dark" : "";
76
- useEffect(() => {
77
- document.documentElement.classList.toggle("dark", theme === "dark");
78
- }, [theme]);
79
- const unknownContext = {
80
- messageId: "unknown",
81
- partIndex: 0,
82
- activityId: "unknown:0",
83
- isActive: false,
84
- isLatestOfKind: true,
85
- status: "ready",
86
- actions: {},
87
- };
88
- return (_jsxs("main", { className: rootClass, "data-message-panel-gallery": "", style: {
89
- minHeight: "100dvh",
90
- padding: "32px clamp(16px, 5vw, 72px) 80px",
91
- color: "var(--foreground)",
92
- background: "var(--background)",
93
- fontFamily: "var(--font-sans)",
94
- }, children: [_jsxs("header", { style: {
95
- display: "flex",
96
- alignItems: "flex-start",
97
- justifyContent: "space-between",
98
- gap: 24,
99
- marginBottom: 48,
100
- }, children: [_jsxs("div", { children: [_jsx("small", { style: { color: "var(--primary)", fontWeight: 700 }, children: "@springbrand/message-panel" }), _jsx("h1", { style: { margin: "8px 0", fontSize: "clamp(30px, 5vw, 52px)" }, children: "Message Panel Gallery" }), _jsx("p", { style: { margin: 0, color: "var(--muted-foreground)" }, children: "16 \u7C7B Part\u30017 \u7C7B Panel \u72B6\u6001\u548C 7 \u7C7B Composer \u72B6\u6001\u7684\u7A33\u5B9A\u9A8C\u6536\u5C55\u5385\u3002" })] }), _jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: 8 }, children: [_jsx("a", { href: "/__message-panel-chat", style: {
101
- padding: "8px 12px",
102
- border: "1px solid var(--border)",
103
- borderRadius: 10,
104
- color: "var(--foreground)",
105
- background: "var(--card)",
106
- fontSize: 13,
107
- textDecoration: "none",
108
- }, children: "\u6253\u5F00\u5355 Chat \u9A8C\u6536" }), _jsx("button", { type: "button", onClick: () => setTheme((current) => current === "dark" ? "light" : "dark"), style: {
109
- padding: "8px 12px",
110
- border: "1px solid var(--border)",
111
- borderRadius: 10,
112
- color: "var(--foreground)",
113
- background: "var(--card)",
114
- }, children: theme === "dark" ? "切换浅色" : "切换深色" })] })] }), _jsxs("section", { style: { display: "grid", gap: 20, marginBottom: 56 }, children: [_jsx(GalleryHeading, { eyebrow: "ROLE + PARTS", title: "\u5B8C\u6574\u53D9\u4E8B\u6D41", description: "\u7528\u6237\u4E0E Assistant \u4E24\u79CD\u89D2\u8272\u4FDD\u6301\u539F\u59CB Part \u987A\u5E8F\uFF1B\u4E0B\u65B9 Assistant \u6D88\u606F\u8986\u76D6\u9996\u7248\u5168\u90E8 16 \u79CD\u5C55\u793A\u5F62\u6001\u3002" }), _jsx("div", { style: { height: 1100, overflow: "hidden", border: "1px solid var(--border)", borderRadius: 18 }, children: _jsx(MessagePanel, { messages: [basicMessages[0], allPartsMessage], status: "ready", adapter: demoMessageAdapter, actions: {
115
- onSuggestion: noOp,
116
- onAnswer: noOp,
117
- onApprove: noOp,
118
- onPreview: noOp,
119
- onDownload: noOp,
120
- onRetry: noOp,
121
- } }) }), _jsxs("div", { "data-scenario": "unknown-fallback", children: [_jsx(UnknownPart, { part: { type: "data-future-component", data: { hello: "world" } } }), _jsx("span", { style: { display: "none" }, children: unknownContext.activityId })] })] }), _jsxs("section", { style: { display: "grid", gap: 20, marginBottom: 56 }, children: [_jsx(GalleryHeading, { eyebrow: "PANEL STATES", title: "7 \u7C7B\u6D88\u606F\u751F\u547D\u5468\u671F", description: "Hydrating\u3001Recovering \u4E0E Background \u662F\u6B63\u4EA4\u8986\u76D6\u6001\uFF1B\u9519\u8BEF\u548C\u4E2D\u65AD\u4E0D\u4F1A\u9057\u7559\u65E0\u9650 spinner\u3002" }), _jsx("div", { style: {
122
- display: "grid",
123
- gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
124
- gap: 16,
125
- }, children: panelScenarios.map((scenario) => (_jsxs("article", { "data-panel-scenario": scenario.id, style: { display: "grid", gap: 8 }, children: [_jsx("strong", { style: { fontSize: 13 }, children: scenario.title }), _jsx("div", { style: { height: 300, overflow: "hidden", border: "1px solid var(--border)", borderRadius: 16 }, children: _jsx(MessagePanel, { messages: scenario.messages, status: scenario.status, isHydrating: scenario.flags?.isHydrating, isRecovering: scenario.flags?.isRecovering, isServerStreaming: scenario.flags?.isServerStreaming, error: scenario.error, adapter: demoMessageAdapter, actions: { onRetry: noOp } }) })] }, scenario.id))) })] }), _jsxs("section", { style: { display: "grid", gap: 20 }, children: [_jsx(GalleryHeading, { eyebrow: "COMPOSER STATES", title: "7 \u7C7B\u8F93\u5165\u4E0E\u9644\u4EF6\u72B6\u6001", description: "\u9009\u62E9\u3001\u7C98\u8D34\u548C\u62D6\u5165\u6587\u4EF6\u5171\u4EAB\u4E00\u4E2A callback\uFF1B\u4E0A\u4F20\u4E2D\u963B\u6B62\u53D1\u9001\uFF0C\u5931\u8D25\u53EF\u91CD\u8BD5\uFF0C\u9644\u4EF6\u652F\u6301\u952E\u76D8\u7B49\u4EF7\u91CD\u6392\u3002" }), _jsxs("div", { style: {
126
- display: "grid",
127
- gridTemplateColumns: "repeat(auto-fit, minmax(320px, 1fr))",
128
- gap: 22,
129
- }, children: [_jsx(ComposerScenario, { id: "empty", title: "Empty" }), _jsx(ComposerScenario, { id: "drafting", title: "Drafting", value: "\u4E3A\u8FD9\u4EFD\u6570\u636E\u751F\u6210\u62A5\u544A" }), _jsx(ComposerScenario, { id: "drag-active", title: "Drag active", overlay: true }), _jsx(ComposerScenario, { id: "uploading", title: "Uploading", attachments: [uploadingAttachment] }), _jsx(ComposerScenario, { id: "attachment-error", title: "Attachment error", value: "\u4E0D\u8981\u4E22\u6389\u8FD9\u4EFD\u8349\u7A3F", attachments: [errorAttachment] }), _jsx(ComposerScenario, { id: "ready-with-attachments", title: "Ready with attachments", attachments: [readyAttachment] }), _jsx(ComposerScenario, { id: "submitted-streaming", title: "Submitted / Streaming", value: "\u6B63\u5728\u53D1\u9001\u2026", status: "streaming" })] })] }), _jsx(ComposerInteractionHarness, {})] }));
130
- }
@@ -1,4 +0,0 @@
1
- import type { UIMessage } from "ai";
2
- import type { MessageAdapter } from "../contracts";
3
- export declare function uiMessageText(message: UIMessage): string;
4
- export declare const aiSdkMessageAdapter: MessageAdapter<UIMessage>;
@@ -1,25 +0,0 @@
1
- import { resolvePartKind } from "../parts/kind";
2
- export function uiMessageText(message) {
3
- return message.parts
4
- .filter((part) => part.type === "text")
5
- .map((part) => part.text)
6
- .join("\n");
7
- }
8
- export const aiSdkMessageAdapter = {
9
- getId: (message, index) => message.id || `message-${index}`,
10
- getRole: (message) => message.role,
11
- getParts: (message) => message.parts,
12
- getMetadata: (message) => message.metadata,
13
- getText: uiMessageText,
14
- isPartRenderable: (part) => {
15
- const record = part != null && typeof part === "object"
16
- ? part
17
- : {};
18
- if (record.type === "step-start")
19
- return false;
20
- if (record.type === "text" || record.type === "reasoning") {
21
- return typeof record.text === "string" && record.text.trim().length > 0;
22
- }
23
- return resolvePartKind(part) !== null || typeof record.type === "string";
24
- },
25
- };
@@ -1,44 +0,0 @@
1
- import type { UIMessage } from "ai";
2
- import { type MessageUrlResolver } from "../message";
3
- import type { ToolApprovalDecision, ToolApprovalView } from "../contracts";
4
- type ChatStatus = "ready" | "submitted" | "streaming" | "error";
5
- export interface CamelPendingApproval {
6
- id: string;
7
- action: string;
8
- summary: string;
9
- executionLevelLabel: string;
10
- requiredExecutionLevelLabel: string;
11
- disabled?: boolean;
12
- }
13
- export interface CamelChatMessagesProps {
14
- messages: readonly UIMessage[];
15
- status: ChatStatus;
16
- /** Exact tool names promoted from the collapsible trace to final output. */
17
- finalToolNames?: readonly string[];
18
- isHydrating?: boolean;
19
- isRecovering?: boolean;
20
- isServerStreaming?: boolean;
21
- hasPendingSteer?: boolean;
22
- startedWithPending?: boolean;
23
- error?: unknown;
24
- onRetry?: () => void;
25
- onApprove: (approvalId: string, decision: ToolApprovalDecision) => void;
26
- onSuggestion: (text: string) => void;
27
- onAnswer: (payload: unknown) => void;
28
- onFork?: (messageId: string) => void | Promise<void>;
29
- getToolApproval?: (part: unknown) => ToolApprovalView | undefined;
30
- resolveUrl?: MessageUrlResolver;
31
- /** Set false when durable approval actions are rendered outside the trace. */
32
- inlineApprovalActions?: boolean;
33
- }
34
- export declare function CamelChatError({ error, onRetry, }: {
35
- error: unknown;
36
- onRetry?: () => void;
37
- }): import("react").JSX.Element;
38
- export declare function CamelApprovalPrompts({ approvals, error, onApprove, }: {
39
- approvals: readonly CamelPendingApproval[];
40
- error?: string | null;
41
- onApprove: (approvalId: string, decision: ToolApprovalDecision) => void;
42
- }): import("react").JSX.Element | null;
43
- export declare function CamelChatMessages({ messages, status, finalToolNames, isHydrating, isRecovering, isServerStreaming, hasPendingSteer, startedWithPending, error, onRetry, onApprove, onSuggestion, onAnswer, onFork, getToolApproval, resolveUrl, inlineApprovalActions, }: CamelChatMessagesProps): import("react").JSX.Element;
44
- export {};