@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
package/demo/fixtures.ts DELETED
@@ -1,292 +0,0 @@
1
- import type {
2
- MessageAdapter,
3
- MessagePanelStatus,
4
- } from "../src/contracts";
5
-
6
- export interface DemoMessage {
7
- id: string;
8
- role: "user" | "assistant";
9
- parts: Array<Record<string, unknown>>;
10
- metadata?: Record<string, unknown>;
11
- }
12
-
13
- export const demoMessageAdapter: MessageAdapter<DemoMessage> = {
14
- getId: (message) => message.id,
15
- getRole: (message) => message.role,
16
- getParts: (message) => message.parts,
17
- getMetadata: (message) => message.metadata,
18
- getText: (message) =>
19
- message.parts
20
- .filter((part) => part.type === "text")
21
- .map((part) => String(part.text ?? ""))
22
- .join("\n"),
23
- isPartRenderable: () => true,
24
- };
25
-
26
- const demoImage =
27
- "data:image/svg+xml;charset=utf-8," +
28
- encodeURIComponent(
29
- '<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>',
30
- );
31
-
32
- export const allPartsMessage: DemoMessage = {
33
- id: "assistant-all-parts",
34
- role: "assistant",
35
- parts: [
36
- {
37
- type: "text",
38
- text: "## Message Panel\n\n支持 **GFM Markdown**、真实流式状态与富组件叙事流。",
39
- },
40
- {
41
- type: "reasoning",
42
- label: "Thought for 3s",
43
- detail: "先理解目标,再安排步骤和并行任务。",
44
- },
45
- {
46
- type: "tool",
47
- icon: "web",
48
- label: "Searched the web",
49
- meta: "2 results",
50
- status: "output-available",
51
- results: [
52
- { title: "AI interface patterns", domain: "example.com" },
53
- { title: "Streaming UI guide", domain: "docs.example.com" },
54
- ],
55
- },
56
- {
57
- type: "error",
58
- title: "一项后台任务已中断",
59
- body: "连接恢复后可以安全重试,不会留下无限加载状态。",
60
- },
61
- {
62
- type: "image",
63
- src: demoImage,
64
- alt: "雪山日落与木屋的示意图",
65
- },
66
- {
67
- type: "chart",
68
- title: "Keyword opportunities",
69
- unit: "",
70
- expandable: true,
71
- bars: [
72
- { label: "Agent UI", value: 96 },
73
- { label: "RAG", value: 78 },
74
- { label: "Tools", value: 65 },
75
- { label: "Memory", value: 54 },
76
- ],
77
- },
78
- {
79
- type: "agent_card",
80
- title: "Your agent is ready",
81
- agentName: "Research Analyst",
82
- agentDesc: "持续调研市场并把结果汇总成结构化报告。",
83
- },
84
- {
85
- type: "plan",
86
- title: "任务计划",
87
- steps: [
88
- { text: "确认目标与边界", status: "done" },
89
- { text: "检索并验证资料", status: "in_progress" },
90
- { text: "交付报告", status: "pending" },
91
- ],
92
- },
93
- {
94
- type: "schedule",
95
- title: "每周发送竞品报告",
96
- cadence: "Every Monday at 9:00 AM",
97
- nextRun: "Mon, Aug 3 · Asia/Shanghai",
98
- },
99
- {
100
- type: "parallel",
101
- label: "Parallel research",
102
- tools: [
103
- { icon: "web", label: "市场调研", status: "done", meta: "8 sources" },
104
- { icon: "code", label: "数据分析", status: "running" },
105
- { icon: "file", label: "汇总报告", status: "done" },
106
- ],
107
- },
108
- {
109
- type: "subagents",
110
- agents: [
111
- {
112
- name: "Research Agent",
113
- tint: "#5577cc",
114
- summary: "完成 8 个来源的交叉验证。",
115
- status: "done",
116
- },
117
- {
118
- name: "Data Agent",
119
- tint: "#8f61c7",
120
- summary: "正在计算趋势指标。",
121
- status: "running",
122
- },
123
- ],
124
- },
125
- {
126
- type: "confirmation",
127
- id: "demo-confirmation",
128
- title: "创建每周竞品 Agent?",
129
- body: "将按上述计划在每周一运行。",
130
- confirmLabel: "创建 Agent",
131
- cancelLabel: "暂不",
132
- },
133
- {
134
- type: "file",
135
- name: "competitor-report.md",
136
- kind: "doc",
137
- note: "Markdown · 14 KB",
138
- },
139
- {
140
- type: "dashboard",
141
- title: "Agent 运行概览",
142
- tiles: [
143
- { label: "完成任务", value: "42" },
144
- { label: "成功率", value: "98%" },
145
- { label: "节省时间", value: "16h" },
146
- ],
147
- cards: [
148
- { icon: "🔎", title: "Research", body: "8 个来源完成验证" },
149
- { icon: "📊", title: "Analysis", body: "3 个趋势值得关注" },
150
- { icon: "📝", title: "Report", body: "报告已生成" },
151
- ],
152
- badges: ["实时流式", "可恢复", "工作区联动"],
153
- fileNote: {
154
- title: "文件已生成",
155
- body: "报告已保存到 Workspace。",
156
- },
157
- table: {
158
- columns: ["能力", "状态"],
159
- rows: [
160
- ["Message", "Ready"],
161
- ["Composer", "Ready"],
162
- ],
163
- },
164
- },
165
- {
166
- type: "ask_user",
167
- questions: [
168
- {
169
- prompt: "报告通过什么渠道发送?",
170
- kind: "single",
171
- options: ["Email", "Slack", "Both"],
172
- },
173
- {
174
- prompt: "需要包含哪些内容?",
175
- kind: "multi",
176
- options: ["定价", "功能", "动态"],
177
- },
178
- ],
179
- freeTextPlaceholder: "补充竞争对手名称…",
180
- },
181
- {
182
- type: "suggestions",
183
- items: ["立即运行一次", "加入 Slack 通知", "编辑跟踪名单"],
184
- },
185
- ],
186
- };
187
-
188
- export const basicMessages: DemoMessage[] = [
189
- {
190
- id: "demo-user",
191
- role: "user",
192
- parts: [{ type: "text", text: "帮我制作一份竞品周报。" }],
193
- },
194
- {
195
- id: "demo-assistant",
196
- role: "assistant",
197
- parts: [{ type: "text", text: "好的,我会先做计划,再开始调研。" }],
198
- },
199
- ];
200
-
201
- export const panelScenarios: Array<{
202
- id: string;
203
- title: string;
204
- messages: DemoMessage[];
205
- status: MessagePanelStatus;
206
- flags?: {
207
- isHydrating?: boolean;
208
- isRecovering?: boolean;
209
- isServerStreaming?: boolean;
210
- };
211
- error?: Error;
212
- }> = [
213
- { id: "empty", title: "Empty", messages: [], status: "ready" },
214
- {
215
- id: "hydrating",
216
- title: "Hydrating",
217
- messages: basicMessages,
218
- status: "ready",
219
- flags: { isHydrating: true },
220
- },
221
- {
222
- id: "thinking",
223
- title: "Thinking / Submitted",
224
- messages: basicMessages.slice(0, 1),
225
- status: "submitted",
226
- },
227
- {
228
- id: "streaming",
229
- title: "Streaming / Playing",
230
- messages: [
231
- basicMessages[0],
232
- {
233
- id: "streaming-assistant",
234
- role: "assistant",
235
- parts: [{ type: "text", text: "正在检索并交叉验证资料…" }],
236
- },
237
- ],
238
- status: "streaming",
239
- },
240
- {
241
- id: "completed",
242
- title: "Completed / Ready",
243
- messages: basicMessages,
244
- status: "ready",
245
- },
246
- {
247
- id: "error",
248
- title: "Error / Interrupted",
249
- messages: basicMessages,
250
- status: "error",
251
- error: new Error("网络连接已中断,可安全重试。"),
252
- },
253
- {
254
- id: "recovering",
255
- title: "Recovering / Background",
256
- messages: basicMessages,
257
- status: "ready",
258
- flags: { isRecovering: true, isServerStreaming: true },
259
- },
260
- ];
261
-
262
- export interface DemoAttachment {
263
- id: string;
264
- filename: string;
265
- status: "uploading" | "ready" | "error";
266
- mediaType: string;
267
- size: number;
268
- }
269
-
270
- export const readyAttachment: DemoAttachment = {
271
- id: "ready-file",
272
- filename: "brief.pdf",
273
- status: "ready",
274
- mediaType: "application/pdf",
275
- size: 21840,
276
- };
277
-
278
- export const uploadingAttachment: DemoAttachment = {
279
- ...readyAttachment,
280
- id: "uploading-file",
281
- filename: "research.csv",
282
- status: "uploading",
283
- mediaType: "text/csv",
284
- };
285
-
286
- export const errorAttachment: DemoAttachment = {
287
- ...readyAttachment,
288
- id: "error-file",
289
- filename: "broken.zip",
290
- status: "error",
291
- mediaType: "application/zip",
292
- };
@@ -1,250 +0,0 @@
1
- import { useMemo, useState } from "react";
2
- import { MessagePanel, type MessagePanelStatus } from "../src";
3
- import { Composer, DropOverlay } from "../src/composer";
4
- import {
5
- allPartsMessage,
6
- basicMessages,
7
- demoMessageAdapter,
8
- errorAttachment,
9
- panelScenarios,
10
- readyAttachment,
11
- uploadingAttachment,
12
- type DemoAttachment,
13
- } from "./fixtures";
14
-
15
- const noOp = () => undefined;
16
-
17
- interface ComposerScenario {
18
- id: string;
19
- title: string;
20
- value?: string;
21
- attachments?: DemoAttachment[];
22
- status: MessagePanelStatus;
23
- overlay?: boolean;
24
- }
25
-
26
- const composerScenarios: ComposerScenario[] = [
27
- { id: "empty", title: "Empty", status: "ready" },
28
- {
29
- id: "drafting",
30
- title: "Drafting",
31
- value: "为这份数据生成报告",
32
- status: "ready",
33
- },
34
- { id: "drag-active", title: "Drag active", status: "ready", overlay: true },
35
- {
36
- id: "uploading",
37
- title: "Uploading",
38
- attachments: [uploadingAttachment],
39
- status: "ready",
40
- },
41
- {
42
- id: "attachment-error",
43
- title: "Attachment error",
44
- value: "不要丢掉这份草稿",
45
- attachments: [errorAttachment],
46
- status: "ready",
47
- },
48
- {
49
- id: "ready-with-attachments",
50
- title: "Ready with attachments",
51
- attachments: [readyAttachment],
52
- status: "ready",
53
- },
54
- {
55
- id: "submitted-streaming",
56
- title: "Submitted / Streaming",
57
- value: "正在发送…",
58
- status: "streaming",
59
- },
60
- ];
61
-
62
- const controlStyle = {
63
- minHeight: 34,
64
- padding: "6px 10px",
65
- border: "1px solid var(--border)",
66
- borderRadius: 9,
67
- color: "var(--foreground)",
68
- background: "var(--card)",
69
- font: "inherit",
70
- fontSize: 12,
71
- } as const;
72
-
73
- export function MessagePanelChatShowcase() {
74
- const [theme, setTheme] = useState<"light" | "dark">("dark");
75
- const [panelScenarioId, setPanelScenarioId] = useState("completed");
76
- const [composerScenarioId, setComposerScenarioId] = useState("empty");
77
- const panelScenario =
78
- panelScenarios.find((scenario) => scenario.id === panelScenarioId) ??
79
- panelScenarios[4];
80
- const composerScenario =
81
- composerScenarios.find(
82
- (scenario) => scenario.id === composerScenarioId,
83
- ) ?? composerScenarios[0];
84
- const messages = useMemo(
85
- () =>
86
- panelScenario.id === "completed"
87
- ? [basicMessages[0], allPartsMessage]
88
- : panelScenario.messages,
89
- [panelScenario],
90
- );
91
-
92
- return (
93
- <main
94
- className={theme === "dark" ? "dark" : ""}
95
- data-message-panel-chat-showcase=""
96
- style={{
97
- display: "grid",
98
- height: "100dvh",
99
- minHeight: 0,
100
- gridTemplateRows: "auto minmax(0, 1fr) auto",
101
- overflow: "hidden",
102
- color: "var(--foreground)",
103
- background: "var(--background)",
104
- fontFamily: "var(--font-sans)",
105
- }}
106
- >
107
- <header
108
- style={{
109
- zIndex: 30,
110
- display: "flex",
111
- minWidth: 0,
112
- flexWrap: "wrap",
113
- alignItems: "center",
114
- gap: 10,
115
- padding: "10px clamp(12px, 2vw, 24px)",
116
- borderBottom: "1px solid var(--border)",
117
- background: "var(--background)",
118
- }}
119
- >
120
- <a
121
- href="/"
122
- style={{
123
- color: "var(--muted-foreground)",
124
- fontSize: 13,
125
- textDecoration: "none",
126
- }}
127
- >
128
- ← 返回系统
129
- </a>
130
- <div style={{ minWidth: 190, marginRight: "auto" }}>
131
- <h1 style={{ margin: 0, fontSize: 14 }}>
132
- UI 全场景验收 Chat
133
- </h1>
134
- <small style={{ color: "var(--muted-foreground)" }}>
135
- 16 类组件 · 7 类生命周期 · 7 类输入状态
136
- </small>
137
- </div>
138
- <label
139
- style={{
140
- display: "flex",
141
- alignItems: "center",
142
- gap: 7,
143
- color: "var(--muted-foreground)",
144
- fontSize: 12,
145
- }}
146
- >
147
- 消息状态
148
- <select
149
- aria-label="消息状态"
150
- value={panelScenarioId}
151
- onChange={(event) => setPanelScenarioId(event.currentTarget.value)}
152
- style={controlStyle}
153
- >
154
- {panelScenarios.map((scenario) => (
155
- <option key={scenario.id} value={scenario.id}>
156
- {scenario.title}
157
- </option>
158
- ))}
159
- </select>
160
- </label>
161
- <label
162
- style={{
163
- display: "flex",
164
- alignItems: "center",
165
- gap: 7,
166
- color: "var(--muted-foreground)",
167
- fontSize: 12,
168
- }}
169
- >
170
- 输入状态
171
- <select
172
- aria-label="输入状态"
173
- value={composerScenarioId}
174
- onChange={(event) =>
175
- setComposerScenarioId(event.currentTarget.value)
176
- }
177
- style={controlStyle}
178
- >
179
- {composerScenarios.map((scenario) => (
180
- <option key={scenario.id} value={scenario.id}>
181
- {scenario.title}
182
- </option>
183
- ))}
184
- </select>
185
- </label>
186
- <button
187
- type="button"
188
- onClick={() =>
189
- setTheme((current) => (current === "dark" ? "light" : "dark"))
190
- }
191
- style={controlStyle}
192
- >
193
- {theme === "dark" ? "浅色" : "深色"}
194
- </button>
195
- </header>
196
-
197
- <MessagePanel
198
- messages={messages}
199
- status={panelScenario.status}
200
- isHydrating={panelScenario.flags?.isHydrating}
201
- isRecovering={panelScenario.flags?.isRecovering}
202
- isServerStreaming={panelScenario.flags?.isServerStreaming}
203
- error={panelScenario.error}
204
- adapter={demoMessageAdapter}
205
- actions={{
206
- onSuggestion: noOp,
207
- onAnswer: noOp,
208
- onApprove: noOp,
209
- onPreview: noOp,
210
- onDownload: noOp,
211
- onRetry: noOp,
212
- }}
213
- />
214
-
215
- <footer
216
- style={{
217
- position: "relative",
218
- zIndex: 20,
219
- padding: "8px clamp(14px, 4vw, 32px) 18px",
220
- background:
221
- "linear-gradient(to top, var(--background) 76%, transparent)",
222
- }}
223
- >
224
- <div
225
- data-composer-showcase={composerScenario.id}
226
- style={{
227
- position: "relative",
228
- width: "min(100%, 780px)",
229
- marginInline: "auto",
230
- }}
231
- >
232
- <Composer
233
- key={composerScenario.id}
234
- defaultValue={composerScenario.value}
235
- status={composerScenario.status}
236
- attachments={composerScenario.attachments}
237
- onSubmit={noOp}
238
- onStop={noOp}
239
- onFilesAdded={noOp}
240
- onAttachmentRemove={noOp}
241
- onAttachmentRetry={noOp}
242
- onAttachmentReorder={noOp}
243
- placeholder="这是验收夹具,不会发送或保存数据"
244
- />
245
- {composerScenario.overlay && <DropOverlay />}
246
- </div>
247
- </footer>
248
- </main>
249
- );
250
- }