@springbrand/message-panel 0.1.3-alpha.8 → 0.2.0-alpha.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cloud-os/README.md +4 -3
- package/cloud-os/assets/followup-arrow.svg +3 -0
- package/cloud-os/assets/loading-corner.svg +3 -0
- package/cloud-os/assets/loading-mark.svg +16 -0
- package/cloud-os/assets/loading-spark.svg +3 -0
- package/cloud-os/assets/model-selected.svg +5 -0
- package/cloud-os/assets/thinking-star.svg +9 -0
- package/cloud-os/capability-chip.tsx +1 -1
- package/cloud-os/chat/cloud-os-chat-messages.tsx +441 -98
- package/cloud-os/chat/code-runner.tsx +106 -0
- package/cloud-os/chat/image-generation.tsx +76 -0
- package/cloud-os/chat/loading-state.tsx +25 -0
- package/cloud-os/chat/markdown-message.tsx +144 -41
- package/cloud-os/chat/range.ts +8 -0
- package/cloud-os/chat/rich-blocks.tsx +285 -227
- package/cloud-os/chat/surfaces.tsx +90 -0
- package/cloud-os/chat/tool-call.tsx +94 -0
- package/cloud-os/chat/tool-presentation.ts +31 -22
- package/cloud-os/chat/tool-rows.tsx +195 -143
- package/cloud-os/chat/tool-timeline.tsx +123 -0
- package/cloud-os/chat/transcript-model.ts +258 -20
- package/cloud-os/chat/web-search.tsx +135 -0
- package/cloud-os/composer/cloud-os-chat-input.tsx +38 -88
- package/cloud-os/composer/cloud-os-model-select.tsx +105 -0
- package/cloud-os/file-view.tsx +195 -12
- package/cloud-os/index.ts +24 -3
- package/cloud-os/layout/cloud-os-workspace-split.tsx +60 -3
- package/cloud-os/primitives/collapsible.tsx +21 -0
- package/cloud-os/primitives/workshop-controls.tsx +2 -2
- package/cloud-os/styles/cloud-os.css +132 -1
- package/demo/camel-chat-showcase.tsx +21 -13
- package/demo/chat-scenarios.ts +214 -40
- package/demo/cloud-os-chat-showcase.tsx +37 -14
- package/demo/fixtures.ts +4 -5
- package/demo/message-panel-gallery.tsx +16 -2
- package/package.json +8 -4
- package/springbrand-pet/LICENSE.bloub +21 -0
- package/springbrand-pet/bot/cycles.test.ts +221 -0
- package/springbrand-pet/bot/cycles.ts +225 -0
- package/springbrand-pet/bot/decor.ts +285 -0
- package/springbrand-pet/bot/engine.test.ts +543 -0
- package/springbrand-pet/bot/engine.ts +558 -0
- package/springbrand-pet/bot/expressions.test.ts +135 -0
- package/springbrand-pet/bot/expressions.ts +192 -0
- package/springbrand-pet/bot/eyefit.ts +455 -0
- package/springbrand-pet/bot/face.test.ts +101 -0
- package/springbrand-pet/bot/face.ts +179 -0
- package/springbrand-pet/bot/math.ts +42 -0
- package/springbrand-pet/bot/profiles.ts +22 -0
- package/springbrand-pet/bot/repere.ts +31 -0
- package/springbrand-pet/bot/shape.test.ts +97 -0
- package/springbrand-pet/bot/shape.ts +310 -0
- package/springbrand-pet/bot/skins.test.ts +325 -0
- package/springbrand-pet/bot/skins.ts +161 -0
- package/springbrand-pet/bot/states.ts +616 -0
- package/springbrand-pet/drag.test.ts +19 -0
- package/springbrand-pet/index.tsx +569 -0
- package/springbrand-pet/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/springbrand-pet/vitest.config.ts +9 -0
- package/src/camel/camel-chat-messages.tsx +78 -78
- package/src/camel/camel-tool-presentation.tsx +19 -15
- package/src/camel/camel-turn.ts +1 -17
- package/src/composer/composer-attachments.tsx +1 -1
- package/src/composer/composer.tsx +2 -2
- package/src/contracts.ts +0 -2
- package/src/message-panel.tsx +1 -24
- package/src/parts/index.tsx +103 -64
package/demo/chat-scenarios.ts
CHANGED
|
@@ -37,6 +37,9 @@ export type DemoScenario =
|
|
|
37
37
|
| "temporary_agent"
|
|
38
38
|
| "agent_tools"
|
|
39
39
|
| "background_agent"
|
|
40
|
+
| "action_loading"
|
|
41
|
+
| "action_files"
|
|
42
|
+
| "schedule_create"
|
|
40
43
|
| "schedule"
|
|
41
44
|
| "schedule_cancel"
|
|
42
45
|
| "approval"
|
|
@@ -118,9 +121,27 @@ export const scenarios: Record<DemoScenario, DemoScenarioMeta> = {
|
|
|
118
121
|
status: "ready",
|
|
119
122
|
flowStep: 3,
|
|
120
123
|
},
|
|
124
|
+
action_loading: {
|
|
125
|
+
label: "Action product loading",
|
|
126
|
+
description: "Preview loading UI for every Action product type.",
|
|
127
|
+
status: "streaming",
|
|
128
|
+
flowStep: 2,
|
|
129
|
+
},
|
|
130
|
+
action_files: {
|
|
131
|
+
label: "Action file results",
|
|
132
|
+
description: "Preview image, audio, video, document, spreadsheet, archive, and unknown files.",
|
|
133
|
+
status: "ready",
|
|
134
|
+
flowStep: 3,
|
|
135
|
+
},
|
|
136
|
+
schedule_create: {
|
|
137
|
+
label: "Create scheduled task",
|
|
138
|
+
description: "Review the task, cadence, and timezone before creating the schedule.",
|
|
139
|
+
status: "ready",
|
|
140
|
+
flowStep: 2,
|
|
141
|
+
},
|
|
121
142
|
schedule: {
|
|
122
|
-
label: "
|
|
123
|
-
description: "schedule
|
|
143
|
+
label: "Scheduled task created",
|
|
144
|
+
description: "A completed schedule shows its task, cadence, timezone, and final status.",
|
|
124
145
|
status: "ready",
|
|
125
146
|
flowStep: 3,
|
|
126
147
|
},
|
|
@@ -239,42 +260,84 @@ function userMessage(prompt: string): UIMessage {
|
|
|
239
260
|
} as UIMessage;
|
|
240
261
|
}
|
|
241
262
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
output: { asked: true },
|
|
256
|
-
},
|
|
263
|
+
function actionUserMessage(prompt: string): UIMessage {
|
|
264
|
+
const message = userMessage(prompt);
|
|
265
|
+
return { ...message, parts: message.parts.filter((part) => part.type === "text") };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export interface DemoAskUserResponse {
|
|
269
|
+
answers: Array<{ selections: string[]; text?: string }>;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export const demoAskUserResponse: DemoAskUserResponse = {
|
|
273
|
+
answers: [
|
|
274
|
+
{ selections: ["管理层"], text: "" },
|
|
275
|
+
{ selections: ["报告"], text: "" },
|
|
257
276
|
],
|
|
258
|
-
}
|
|
277
|
+
};
|
|
259
278
|
|
|
260
|
-
|
|
279
|
+
const askUserQuestions = [
|
|
280
|
+
{
|
|
281
|
+
question: "这份建议优先面向哪个读者?",
|
|
282
|
+
options: ["产品团队", "管理层", "设计团队"],
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
question: "需要哪些交付物?",
|
|
286
|
+
options: ["报告", "演示文稿", "执行清单"],
|
|
287
|
+
multiSelect: true,
|
|
288
|
+
allowCustom: true,
|
|
289
|
+
},
|
|
290
|
+
];
|
|
291
|
+
|
|
292
|
+
function askUserRequest(response?: DemoAskUserResponse): UIMessage {
|
|
261
293
|
return {
|
|
262
|
-
id: "camel-demo-
|
|
263
|
-
role: "
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
294
|
+
id: "camel-demo-ask-user",
|
|
295
|
+
role: "assistant",
|
|
296
|
+
parts: [
|
|
297
|
+
{
|
|
298
|
+
type: "tool-ask_user",
|
|
299
|
+
toolCallId: "camel-demo-ask-user-1",
|
|
300
|
+
state: response ? "output-available" : "input-available",
|
|
301
|
+
input: { questions: askUserQuestions },
|
|
302
|
+
...(response
|
|
303
|
+
? {
|
|
304
|
+
output: {
|
|
305
|
+
answers: response.answers.map((answer, index) => ({
|
|
306
|
+
question: askUserQuestions[index]!.question,
|
|
307
|
+
selections: answer.selections,
|
|
308
|
+
text: answer.text?.trim() ?? "",
|
|
309
|
+
})),
|
|
310
|
+
},
|
|
311
|
+
}
|
|
312
|
+
: {}),
|
|
313
|
+
},
|
|
314
|
+
],
|
|
315
|
+
} as unknown as UIMessage;
|
|
271
316
|
}
|
|
272
317
|
|
|
273
318
|
const planningWork = {
|
|
274
319
|
id: "camel-demo-planning-work",
|
|
275
320
|
role: "assistant",
|
|
276
|
-
metadata: {
|
|
321
|
+
metadata: {
|
|
322
|
+
turnStartedAt: demoTurnStartedAt + 3_000,
|
|
323
|
+
turnDurationMs: 18_000,
|
|
324
|
+
},
|
|
277
325
|
parts: [
|
|
326
|
+
{
|
|
327
|
+
type: "reasoning",
|
|
328
|
+
text: `先确认受众和交付范围,再验证产品案例。
|
|
329
|
+
|
|
330
|
+
用户选择的读者是管理层,交付物是报告。因此内容不能只罗列界面趋势,还需要把每项趋势连接到业务价值、落地成本和潜在风险,帮助管理层判断优先级。
|
|
331
|
+
|
|
332
|
+
接下来会分三步推进:
|
|
333
|
+
|
|
334
|
+
1. 检索 2025–2026 年主流 AI Agent 产品,优先采集官方发布说明、产品文档和可验证的真实界面。
|
|
335
|
+
2. 对比任务规划、工具调用、人工确认、后台执行和结果交付等关键交互,区分普遍模式与单个产品的特色设计。
|
|
336
|
+
3. 把证据整理成可执行建议,并为每条建议标注适用场景、实现复杂度和预期收益。
|
|
337
|
+
|
|
338
|
+
第一轮检索先覆盖通用 Agent、开发者 Agent 与企业自动化产品。如果不同来源对同一趋势的描述冲突,会保留分歧并继续寻找一手证据,而不是直接下结论。`,
|
|
339
|
+
state: "done",
|
|
340
|
+
},
|
|
278
341
|
{
|
|
279
342
|
type: "tool-update_plan",
|
|
280
343
|
state: "output-available",
|
|
@@ -459,6 +522,115 @@ const backgroundResult = {
|
|
|
459
522
|
],
|
|
460
523
|
} as unknown as UIMessage;
|
|
461
524
|
|
|
525
|
+
const actionProductHints = [
|
|
526
|
+
{ type: "image", mediaType: "image/png" },
|
|
527
|
+
{ type: "audio", mediaType: "audio/mpeg" },
|
|
528
|
+
{ type: "video", mediaType: "video/mp4" },
|
|
529
|
+
{ type: "document", mediaType: "application/pdf" },
|
|
530
|
+
{ type: "archive", mediaType: "application/zip" },
|
|
531
|
+
{ type: "data", mediaType: "application/json" },
|
|
532
|
+
{ type: "other" },
|
|
533
|
+
] as const;
|
|
534
|
+
|
|
535
|
+
function demoExecutionId(index: number) {
|
|
536
|
+
return `00000000-0000-4000-8000-${String(index + 1).padStart(12, "0")}`;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const actionProductLoading = {
|
|
540
|
+
id: "camel-demo-action-product-loading",
|
|
541
|
+
role: "assistant",
|
|
542
|
+
parts: actionProductHints.map((produces, index) => ({
|
|
543
|
+
type: "dynamic-tool",
|
|
544
|
+
toolCallId: `camel-demo-action-loading-${index}`,
|
|
545
|
+
toolName: `action_generate_${produces.type}`,
|
|
546
|
+
state: "output-available",
|
|
547
|
+
input: {},
|
|
548
|
+
output: {
|
|
549
|
+
status: "running",
|
|
550
|
+
executionId: demoExecutionId(index),
|
|
551
|
+
presentation: {
|
|
552
|
+
type: "action-execution",
|
|
553
|
+
executionId: demoExecutionId(index),
|
|
554
|
+
state: "running",
|
|
555
|
+
produces,
|
|
556
|
+
content: [],
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
})),
|
|
560
|
+
} as unknown as UIMessage;
|
|
561
|
+
|
|
562
|
+
const actionFileFixtures = [
|
|
563
|
+
{
|
|
564
|
+
name: "image",
|
|
565
|
+
mediaType: "image/png",
|
|
566
|
+
url: "https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=1024&q=80",
|
|
567
|
+
},
|
|
568
|
+
{ name: "audio", mediaType: "audio/mpeg", url: "https://files.example/action-demo/audio.mp3" },
|
|
569
|
+
{ name: "video", mediaType: "video/mp4", url: "https://files.example/action-demo/video.mp4" },
|
|
570
|
+
{ name: "pdf", mediaType: "application/pdf", url: "https://files.example/action-demo/report.pdf" },
|
|
571
|
+
{ name: "markdown", mediaType: "text/markdown", url: "https://files.example/action-demo/report.md" },
|
|
572
|
+
{
|
|
573
|
+
name: "spreadsheet",
|
|
574
|
+
mediaType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
575
|
+
url: "https://files.example/action-demo/data.xlsx",
|
|
576
|
+
},
|
|
577
|
+
{ name: "archive", mediaType: "application/zip", url: "https://files.example/action-demo/files.zip" },
|
|
578
|
+
{
|
|
579
|
+
name: "unknown",
|
|
580
|
+
mediaType: "application/octet-stream",
|
|
581
|
+
url: "https://files.example/action-demo/result.bin",
|
|
582
|
+
},
|
|
583
|
+
] as const;
|
|
584
|
+
|
|
585
|
+
const actionFileResults = {
|
|
586
|
+
id: "camel-demo-action-file-results",
|
|
587
|
+
role: "assistant",
|
|
588
|
+
parts: [
|
|
589
|
+
...actionFileFixtures.map((file, index) => ({
|
|
590
|
+
type: "dynamic-tool",
|
|
591
|
+
toolCallId: `camel-demo-action-file-${index}`,
|
|
592
|
+
toolName: `action_generate_${file.name}`,
|
|
593
|
+
state: "output-available",
|
|
594
|
+
input: {},
|
|
595
|
+
output: {
|
|
596
|
+
status: "succeeded",
|
|
597
|
+
executionId: demoExecutionId(index + actionProductHints.length),
|
|
598
|
+
result: { kind: "file", content_type: file.mediaType, url: file.url },
|
|
599
|
+
presentation: {
|
|
600
|
+
type: "action-execution",
|
|
601
|
+
executionId: demoExecutionId(index + actionProductHints.length),
|
|
602
|
+
state: "ready",
|
|
603
|
+
content: [{ type: "file", url: file.url, mediaType: file.mediaType }],
|
|
604
|
+
},
|
|
605
|
+
},
|
|
606
|
+
})),
|
|
607
|
+
{
|
|
608
|
+
type: "text",
|
|
609
|
+
text: "All Action file result types are rendered above from structured output.",
|
|
610
|
+
},
|
|
611
|
+
],
|
|
612
|
+
} as unknown as UIMessage;
|
|
613
|
+
|
|
614
|
+
export const demoScheduleInput = {
|
|
615
|
+
label: "Weekly competitor report",
|
|
616
|
+
prompt: "Generate and send this week's competitor report.",
|
|
617
|
+
trigger: { kind: "cron", cron: "0 9 * * 1" },
|
|
618
|
+
timezone: "Asia/Shanghai",
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
const scheduleRequest = {
|
|
622
|
+
id: "camel-demo-schedule-request",
|
|
623
|
+
role: "assistant",
|
|
624
|
+
parts: [
|
|
625
|
+
{
|
|
626
|
+
type: "tool-schedule",
|
|
627
|
+
toolCallId: "camel-demo-schedule-request-1",
|
|
628
|
+
state: "approval-requested",
|
|
629
|
+
input: demoScheduleInput,
|
|
630
|
+
},
|
|
631
|
+
],
|
|
632
|
+
} as unknown as UIMessage;
|
|
633
|
+
|
|
462
634
|
const scheduledTask = {
|
|
463
635
|
id: "camel-demo-schedule",
|
|
464
636
|
role: "assistant",
|
|
@@ -467,12 +639,7 @@ const scheduledTask = {
|
|
|
467
639
|
type: "tool-schedule",
|
|
468
640
|
toolCallId: "camel-demo-schedule-1",
|
|
469
641
|
state: "output-available",
|
|
470
|
-
input:
|
|
471
|
-
label: "每周发送竞品报告",
|
|
472
|
-
prompt: "生成并发送本周竞品报告。",
|
|
473
|
-
trigger: { kind: "cron", cron: "0 9 * * 1" },
|
|
474
|
-
timezone: "Asia/Shanghai",
|
|
475
|
-
},
|
|
642
|
+
input: demoScheduleInput,
|
|
476
643
|
output: { scheduled: true, id: "schedule-1" },
|
|
477
644
|
},
|
|
478
645
|
],
|
|
@@ -827,7 +994,7 @@ const failedWork = {
|
|
|
827
994
|
export function messagesForScenario(
|
|
828
995
|
scenario: DemoScenario,
|
|
829
996
|
prompt: string,
|
|
830
|
-
humanAnswer:
|
|
997
|
+
humanAnswer: DemoAskUserResponse,
|
|
831
998
|
approval: boolean | undefined,
|
|
832
999
|
): UIMessage[] {
|
|
833
1000
|
if (scenario === "empty") return [];
|
|
@@ -835,15 +1002,13 @@ export function messagesForScenario(
|
|
|
835
1002
|
return [noticeMessage(scenario), ...commandMessages];
|
|
836
1003
|
}
|
|
837
1004
|
const conversation = [noticeMessage(scenario), userMessage(prompt)];
|
|
838
|
-
const answer = humanAnswer || "管理层";
|
|
839
1005
|
const decidedConversation = [
|
|
840
1006
|
...conversation,
|
|
841
|
-
askUserRequest,
|
|
842
|
-
humanReplyMessage(answer),
|
|
1007
|
+
askUserRequest(humanAnswer),
|
|
843
1008
|
];
|
|
844
1009
|
if (scenario === "hydrating") return conversation;
|
|
845
1010
|
if (scenario === "submitted") return conversation;
|
|
846
|
-
if (scenario === "ask") return [...conversation, askUserRequest];
|
|
1011
|
+
if (scenario === "ask") return [...conversation, askUserRequest()];
|
|
847
1012
|
if (scenario === "planning") {
|
|
848
1013
|
return [...decidedConversation, planningWork];
|
|
849
1014
|
}
|
|
@@ -860,6 +1025,15 @@ export function messagesForScenario(
|
|
|
860
1025
|
if (scenario === "background_agent") {
|
|
861
1026
|
return [...conversation, backgroundDispatch, backgroundResult];
|
|
862
1027
|
}
|
|
1028
|
+
if (scenario === "action_loading") {
|
|
1029
|
+
return [noticeMessage(scenario), actionUserMessage(prompt), actionProductLoading];
|
|
1030
|
+
}
|
|
1031
|
+
if (scenario === "action_files") {
|
|
1032
|
+
return [noticeMessage(scenario), actionUserMessage(prompt), actionFileResults];
|
|
1033
|
+
}
|
|
1034
|
+
if (scenario === "schedule_create") {
|
|
1035
|
+
return [...conversation, scheduleRequest];
|
|
1036
|
+
}
|
|
863
1037
|
if (scenario === "schedule") {
|
|
864
1038
|
return [...conversation, scheduledTask];
|
|
865
1039
|
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
CloudOsRoot,
|
|
14
14
|
CloudOsWorkspacePanel,
|
|
15
15
|
CloudOsWorkspaceSplit,
|
|
16
|
+
ScheduleConfirmation,
|
|
16
17
|
WorkshopIconButton,
|
|
17
18
|
buildCloudOsEntries,
|
|
18
19
|
type CloudOsAttachmentView,
|
|
@@ -20,17 +21,19 @@ import {
|
|
|
20
21
|
} from "../cloud-os";
|
|
21
22
|
import {
|
|
22
23
|
defaultPrompt,
|
|
24
|
+
demoAskUserResponse,
|
|
23
25
|
demoModels,
|
|
24
26
|
flowSteps,
|
|
25
27
|
messagesForScenario,
|
|
26
28
|
scenarios,
|
|
27
29
|
type DemoScenario,
|
|
30
|
+
type DemoAskUserResponse,
|
|
28
31
|
type DemoScenarioMeta,
|
|
29
32
|
} from "./chat-scenarios";
|
|
30
33
|
|
|
31
34
|
/**
|
|
32
35
|
* cloud-os 版快速预览。场景表和 camel-chat-showcase 共用 `chat-scenarios.ts`,
|
|
33
|
-
* 所以两边的状态逐条对齐(同样
|
|
36
|
+
* 所以两边的状态逐条对齐(同样 23 个场景、同样的 7 步流程条、同样的初始 URL 参数)。
|
|
34
37
|
* 差别只在外壳:这里是 cloudflare-os 的左聊天 / 右工作区双栏。
|
|
35
38
|
*/
|
|
36
39
|
|
|
@@ -122,7 +125,7 @@ export function CloudOsChatShowcase() {
|
|
|
122
125
|
const [mode, setMode] = useState<CloudOsMode>("light");
|
|
123
126
|
const [scenario, setScenario] = useState<DemoScenario>(initialScenario);
|
|
124
127
|
const [prompt, setPrompt] = useState(defaultPrompt);
|
|
125
|
-
const [humanAnswer, setHumanAnswer] = useState(
|
|
128
|
+
const [humanAnswer, setHumanAnswer] = useState(demoAskUserResponse);
|
|
126
129
|
const [model, setModel] = useState<string | null>(cloudOsModels[0].id);
|
|
127
130
|
const [value, setValue] = useState("");
|
|
128
131
|
const [attachments, setAttachments] = useState<CloudOsAttachmentView[]>([]);
|
|
@@ -153,7 +156,8 @@ export function CloudOsChatShowcase() {
|
|
|
153
156
|
? (part as { type?: unknown; state?: unknown })
|
|
154
157
|
: {};
|
|
155
158
|
return (record.type === "tool-write_file" ||
|
|
156
|
-
record.type === "tool-cancel_schedule"
|
|
159
|
+
record.type === "tool-cancel_schedule" ||
|
|
160
|
+
record.type === "tool-schedule") &&
|
|
157
161
|
record.state === "approval-requested"
|
|
158
162
|
? "cloud-os-demo-approval"
|
|
159
163
|
: undefined;
|
|
@@ -283,6 +287,7 @@ export function CloudOsChatShowcase() {
|
|
|
283
287
|
|
|
284
288
|
<CloudOsWorkspaceSplit
|
|
285
289
|
workspaceOpen={workspaceOpen}
|
|
290
|
+
isLoading={scenario === "hydrating"}
|
|
286
291
|
chat={
|
|
287
292
|
<div className="flex h-full min-h-0 flex-col bg-kumo-base">
|
|
288
293
|
<aside
|
|
@@ -351,18 +356,36 @@ export function CloudOsChatShowcase() {
|
|
|
351
356
|
status === "submitted" ||
|
|
352
357
|
status === "streaming" ||
|
|
353
358
|
scenario === "ask" ||
|
|
359
|
+
scenario === "schedule_create" ||
|
|
354
360
|
((scenario === "approval" || scenario === "schedule_cancel") &&
|
|
355
361
|
approval === undefined)
|
|
356
362
|
}
|
|
357
|
-
isHydrating={scenario === "hydrating"}
|
|
358
363
|
isRecovering={scenario === "recovering"}
|
|
359
|
-
recoveryStatusLabel="
|
|
364
|
+
recoveryStatusLabel="Model response interrupted. Retrying…"
|
|
360
365
|
awaitingApproval={
|
|
361
|
-
(scenario === "approval" ||
|
|
366
|
+
(scenario === "approval" ||
|
|
367
|
+
scenario === "schedule_cancel" ||
|
|
368
|
+
scenario === "schedule_create") &&
|
|
362
369
|
approval === undefined
|
|
363
370
|
}
|
|
364
371
|
showThinkingTraces={showThinkingTraces}
|
|
365
372
|
approvalIdOf={approvalIdOf}
|
|
373
|
+
renderApproval={({ toolName, input }) =>
|
|
374
|
+
toolName === "schedule"
|
|
375
|
+
? (
|
|
376
|
+
<ScheduleConfirmation
|
|
377
|
+
input={input}
|
|
378
|
+
onCancel={() => {
|
|
379
|
+
selectScenario("submitted");
|
|
380
|
+
setLastEvent("Schedule creation cancelled");
|
|
381
|
+
}}
|
|
382
|
+
onConfirm={() => {
|
|
383
|
+
selectScenario("schedule");
|
|
384
|
+
setLastEvent("Scheduled task created");
|
|
385
|
+
}}
|
|
386
|
+
/>
|
|
387
|
+
)
|
|
388
|
+
: undefined}
|
|
366
389
|
error={statusError}
|
|
367
390
|
constrainWidth={!workspaceOpen}
|
|
368
391
|
onRetry={() => {
|
|
@@ -389,17 +412,17 @@ export function CloudOsChatShowcase() {
|
|
|
389
412
|
onRespond={async (_toolCallId, response) => {
|
|
390
413
|
const data =
|
|
391
414
|
response && typeof response === "object"
|
|
392
|
-
? (response as
|
|
393
|
-
: {};
|
|
394
|
-
const answer = [
|
|
395
|
-
...
|
|
396
|
-
|
|
397
|
-
]
|
|
415
|
+
? (response as DemoAskUserResponse)
|
|
416
|
+
: { answers: [] };
|
|
417
|
+
const answer = data.answers.flatMap((item) => [
|
|
418
|
+
...item.selections,
|
|
419
|
+
item.text?.trim() ?? "",
|
|
420
|
+
])
|
|
398
421
|
.filter(Boolean)
|
|
399
422
|
.join(" / ");
|
|
400
|
-
if (
|
|
423
|
+
if (data.answers.length > 0) setHumanAnswer(data);
|
|
401
424
|
selectScenario("planning");
|
|
402
|
-
setLastEvent(`已回答并继续:${answer
|
|
425
|
+
setLastEvent(`已回答并继续:${answer}`);
|
|
403
426
|
return true;
|
|
404
427
|
}}
|
|
405
428
|
/>
|
package/demo/fixtures.ts
CHANGED
|
@@ -166,17 +166,16 @@ export const allPartsMessage: DemoMessage = {
|
|
|
166
166
|
type: "ask_user",
|
|
167
167
|
questions: [
|
|
168
168
|
{
|
|
169
|
-
|
|
170
|
-
kind: "single",
|
|
169
|
+
question: "报告通过什么渠道发送?",
|
|
171
170
|
options: ["Email", "Slack", "Both"],
|
|
172
171
|
},
|
|
173
172
|
{
|
|
174
|
-
|
|
175
|
-
kind: "multi",
|
|
173
|
+
question: "需要包含哪些内容?",
|
|
176
174
|
options: ["定价", "功能", "动态"],
|
|
175
|
+
multiSelect: true,
|
|
176
|
+
allowCustom: true,
|
|
177
177
|
},
|
|
178
178
|
],
|
|
179
|
-
freeTextPlaceholder: "补充竞争对手名称…",
|
|
180
179
|
},
|
|
181
180
|
{
|
|
182
181
|
type: "suggestions",
|
|
@@ -220,6 +220,7 @@ function ComposerInteractionHarness() {
|
|
|
220
220
|
|
|
221
221
|
export function MessagePanelGallery() {
|
|
222
222
|
const [theme, setTheme] = useState<"light" | "dark">("dark");
|
|
223
|
+
const [askUserOutput, setAskUserOutput] = useState<unknown>();
|
|
223
224
|
const rootClass = theme === "dark" ? "dark" : "";
|
|
224
225
|
useEffect(() => {
|
|
225
226
|
document.documentElement.classList.toggle("dark", theme === "dark");
|
|
@@ -309,12 +310,25 @@ export function MessagePanelGallery() {
|
|
|
309
310
|
/>
|
|
310
311
|
<div style={{ height: 1100, overflow: "hidden", border: "1px solid var(--border)", borderRadius: 18 }}>
|
|
311
312
|
<MessagePanel
|
|
312
|
-
messages={[
|
|
313
|
+
messages={[
|
|
314
|
+
basicMessages[0],
|
|
315
|
+
{
|
|
316
|
+
...allPartsMessage,
|
|
317
|
+
parts: allPartsMessage.parts.map((part) =>
|
|
318
|
+
part.type === "ask_user" && askUserOutput
|
|
319
|
+
? { ...part, output: askUserOutput }
|
|
320
|
+
: part
|
|
321
|
+
),
|
|
322
|
+
},
|
|
323
|
+
]}
|
|
313
324
|
status="ready"
|
|
314
325
|
adapter={demoMessageAdapter}
|
|
315
326
|
actions={{
|
|
316
327
|
onSuggestion: noOp,
|
|
317
|
-
onAnswer:
|
|
328
|
+
onAnswer: async (output) => {
|
|
329
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
330
|
+
setAskUserOutput(output);
|
|
331
|
+
},
|
|
318
332
|
onApprove: noOp,
|
|
319
333
|
onPreview: noOp,
|
|
320
334
|
onDownload: noOp,
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@springbrand/message-panel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0-alpha.37",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"src",
|
|
7
7
|
"demo",
|
|
8
|
-
"cloud-os"
|
|
8
|
+
"cloud-os",
|
|
9
|
+
"springbrand-pet"
|
|
9
10
|
],
|
|
10
11
|
"publishConfig": {
|
|
11
12
|
"access": "public"
|
|
@@ -16,6 +17,7 @@
|
|
|
16
17
|
"./styles.css": "./src/styles/index.css",
|
|
17
18
|
"./cloud-os": "./cloud-os/index.ts",
|
|
18
19
|
"./cloud-os.css": "./cloud-os/styles/cloud-os.css",
|
|
20
|
+
"./springbrand-pet": "./springbrand-pet/index.tsx",
|
|
19
21
|
"./demo": "./demo/index.ts"
|
|
20
22
|
},
|
|
21
23
|
"peerDependencies": {
|
|
@@ -25,15 +27,15 @@
|
|
|
25
27
|
},
|
|
26
28
|
"dependencies": {
|
|
27
29
|
"@assistant-ui/react": "^0.15.1",
|
|
30
|
+
"@base-ui/react": "^1.7.0",
|
|
28
31
|
"@phosphor-icons/react": "^2.1.10",
|
|
29
32
|
"@streamdown/cjk": "^1.0.3",
|
|
30
33
|
"@streamdown/code": "^1.1.1",
|
|
31
34
|
"@streamdown/math": "^1.0.2",
|
|
32
35
|
"@streamdown/mermaid": "^1.0.2",
|
|
33
36
|
"lucide-react": "^1.24.0",
|
|
37
|
+
"motion": "^13.1.0",
|
|
34
38
|
"radix-ui": "^1.6.2",
|
|
35
|
-
"react-markdown": "^10.1.0",
|
|
36
|
-
"remark-gfm": "^4.0.1",
|
|
37
39
|
"streamdown": "^2.5.0",
|
|
38
40
|
"thinking-orbs": "0.2.0",
|
|
39
41
|
"use-stick-to-bottom": "^1.1.6"
|
|
@@ -44,11 +46,13 @@
|
|
|
44
46
|
"@types/react-dom": "^19.2.3",
|
|
45
47
|
"@vitejs/plugin-react": "^6.0.3",
|
|
46
48
|
"tailwindcss": "^4.3.2",
|
|
49
|
+
"tw-animate-css": "^1.4.0",
|
|
47
50
|
"typescript": "^7.0.2",
|
|
48
51
|
"vite": "^8.1.4"
|
|
49
52
|
},
|
|
50
53
|
"scripts": {
|
|
51
54
|
"typecheck": "tsc --noEmit",
|
|
55
|
+
"test:springbrand-pet": "vitest run --config springbrand-pet/vitest.config.ts",
|
|
52
56
|
"dev:cloud-os": "vite",
|
|
53
57
|
"build:cloud-os": "vite build"
|
|
54
58
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jérémy Perret
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|