@springbrand/message-panel 0.1.3-alpha.2 → 0.1.3-alpha.22
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 +6 -6
- 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/capability-chip.tsx +35 -0
- package/cloud-os/chat/activity-indicator.tsx +29 -0
- package/cloud-os/chat/cloud-os-chat-messages.tsx +130 -70
- package/cloud-os/chat/markdown-message.tsx +85 -40
- package/cloud-os/chat/rich-blocks.tsx +494 -190
- package/cloud-os/chat/tool-presentation.ts +67 -6
- package/cloud-os/chat/tool-rows.tsx +87 -43
- package/cloud-os/chat/transcript-model.ts +215 -23
- package/cloud-os/composer/cloud-os-chat-input.tsx +242 -127
- package/cloud-os/composer/cloud-os-model-select.tsx +105 -0
- package/cloud-os/file-view.tsx +266 -0
- package/cloud-os/index.ts +29 -2
- package/cloud-os/layout/cloud-os-workspace-split.tsx +107 -24
- package/cloud-os/primitives/workshop-controls.tsx +2 -2
- package/cloud-os/styles/cloud-os.css +93 -19
- package/demo/camel-chat-showcase.tsx +21 -13
- package/demo/chat-scenarios.ts +100 -40
- package/demo/cloud-os-chat-showcase.tsx +51 -17
- package/demo/fixtures.ts +4 -5
- package/demo/message-panel-gallery.tsx +16 -2
- package/package.json +3 -4
- package/src/camel/camel-chat-messages.tsx +82 -77
- package/src/camel/camel-prompt-input.tsx +2 -1
- package/src/camel/camel-tool-presentation.tsx +19 -15
- package/src/camel/camel-turn.ts +1 -17
- package/src/chat-summary-panel.tsx +1 -1
- package/src/composer/chat-composer.tsx +2 -1
- package/src/composer/composer-trigger-popover.tsx +1 -1
- package/src/composer/composer.tsx +2 -1
- package/src/composer/index.ts +1 -0
- package/src/composer/key-rules.ts +12 -0
- package/src/contracts.ts +0 -2
- package/src/message-panel.tsx +0 -23
- package/src/parts/index.tsx +102 -63
- package/src/styles/index.css +4 -1
|
@@ -341,6 +341,54 @@
|
|
|
341
341
|
display: none;
|
|
342
342
|
}
|
|
343
343
|
|
|
344
|
+
.cos-model-select {
|
|
345
|
+
--cos-model-select-bg: #fff;
|
|
346
|
+
--cos-model-select-border: rgb(0 0 0 / 7%);
|
|
347
|
+
--cos-model-select-text: #000;
|
|
348
|
+
--cos-model-select-muted: rgb(0 0 0 / 50%);
|
|
349
|
+
--cos-model-select-disabled: rgb(0 0 0 / 30%);
|
|
350
|
+
--cos-model-select-highlight: rgb(0 0 0 / 3%);
|
|
351
|
+
border-color: var(--cos-model-select-border);
|
|
352
|
+
background: var(--cos-model-select-bg);
|
|
353
|
+
box-shadow: 0 8px 14px rgb(0 0 0 / 6%);
|
|
354
|
+
color: var(--cos-model-select-text);
|
|
355
|
+
font-family: "Inter Variable", Inter, sans-serif;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.cos-model-select__item[data-highlighted] {
|
|
359
|
+
background: var(--cos-model-select-highlight);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.cos-model-select__item[data-disabled] {
|
|
363
|
+
cursor: not-allowed;
|
|
364
|
+
opacity: 1;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.cos-model-select__name {
|
|
368
|
+
color: var(--cos-model-select-text);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.cos-model-select__name[data-muted] {
|
|
372
|
+
color: var(--cos-model-select-muted);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.cos-model-select__plan {
|
|
376
|
+
color: var(--cos-model-select-muted);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.cos-model-select__plan[data-disabled] {
|
|
380
|
+
color: var(--cos-model-select-disabled);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
[data-cloud-os-portal][data-mode="dark"].cos-model-select {
|
|
384
|
+
--cos-model-select-bg: #343434;
|
|
385
|
+
--cos-model-select-border: rgb(255 255 255 / 10%);
|
|
386
|
+
--cos-model-select-text: #fff;
|
|
387
|
+
--cos-model-select-muted: rgb(255 255 255 / 50%);
|
|
388
|
+
--cos-model-select-disabled: rgb(255 255 255 / 30%);
|
|
389
|
+
--cos-model-select-highlight: rgb(255 255 255 / 7%);
|
|
390
|
+
}
|
|
391
|
+
|
|
344
392
|
.cos-chat-panel {
|
|
345
393
|
scrollbar-width: thin;
|
|
346
394
|
scrollbar-color: var(--color-kumo-line) transparent;
|
|
@@ -356,23 +404,6 @@
|
|
|
356
404
|
background: transparent;
|
|
357
405
|
}
|
|
358
406
|
|
|
359
|
-
/* ── 顶部推进条(isAgentActive)──────────────────────────────────────────── */
|
|
360
|
-
@keyframes cos-thinking {
|
|
361
|
-
0% {
|
|
362
|
-
left: -33%;
|
|
363
|
-
}
|
|
364
|
-
50% {
|
|
365
|
-
left: 100%;
|
|
366
|
-
}
|
|
367
|
-
100% {
|
|
368
|
-
left: -33%;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
.cos-progress-sweep {
|
|
373
|
-
animation: cos-thinking 1.5s ease-in-out infinite;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
407
|
/* ============================================================================
|
|
377
408
|
* 以下整段来自 ChatInterface.module.css —— CSS Module 类名改成 cos- 全局前缀
|
|
378
409
|
* ========================================================================== */
|
|
@@ -382,6 +413,9 @@
|
|
|
382
413
|
line-height: 1.6;
|
|
383
414
|
overflow-wrap: anywhere;
|
|
384
415
|
}
|
|
416
|
+
.cos-user-markdown :where(p, li) {
|
|
417
|
+
white-space: pre-wrap;
|
|
418
|
+
}
|
|
385
419
|
.cos-markdown p {
|
|
386
420
|
margin: 0.4em 0;
|
|
387
421
|
}
|
|
@@ -505,6 +539,20 @@
|
|
|
505
539
|
}
|
|
506
540
|
}
|
|
507
541
|
|
|
542
|
+
/* ── 活动指示:回合在跑但屏幕上没东西在动时 ──────────────────────────────── */
|
|
543
|
+
.cos-activity {
|
|
544
|
+
display: flex;
|
|
545
|
+
align-items: center;
|
|
546
|
+
gap: 8px;
|
|
547
|
+
min-height: 20px;
|
|
548
|
+
color: var(--text-color-kumo-subtle);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/* 球是固定 20px 的画布,不许被 flex 压扁。 */
|
|
552
|
+
.cos-activity canvas {
|
|
553
|
+
flex: none;
|
|
554
|
+
}
|
|
555
|
+
|
|
508
556
|
/* ── 流式占位:Thinking 扫光 ──────────────────────────────────────────────── */
|
|
509
557
|
.cos-thinking-shimmer {
|
|
510
558
|
color: var(--text-color-kumo-inactive);
|
|
@@ -536,8 +584,34 @@
|
|
|
536
584
|
background: none;
|
|
537
585
|
-webkit-text-fill-color: currentColor;
|
|
538
586
|
}
|
|
539
|
-
|
|
540
|
-
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/* ── 原位提问:逐题横向切换 ─────────────────────────────────────────────── */
|
|
590
|
+
.cos-ask-user-question[data-direction="forward"] {
|
|
591
|
+
animation: cos-ask-user-forward 180ms ease-out;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
.cos-ask-user-question[data-direction="back"] {
|
|
595
|
+
animation: cos-ask-user-back 180ms ease-out;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
@keyframes cos-ask-user-forward {
|
|
599
|
+
from {
|
|
600
|
+
opacity: 0;
|
|
601
|
+
transform: translateX(24px);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
@keyframes cos-ask-user-back {
|
|
606
|
+
from {
|
|
607
|
+
opacity: 0;
|
|
608
|
+
transform: translateX(-24px);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
@media (prefers-reduced-motion: reduce) {
|
|
613
|
+
.cos-ask-user-question {
|
|
614
|
+
animation: none !important;
|
|
541
615
|
}
|
|
542
616
|
}
|
|
543
617
|
|
|
@@ -15,11 +15,13 @@ import {
|
|
|
15
15
|
import {
|
|
16
16
|
commands as sharedCommands,
|
|
17
17
|
defaultPrompt,
|
|
18
|
+
demoAskUserResponse,
|
|
18
19
|
demoModels,
|
|
19
20
|
finalToolNames,
|
|
20
21
|
flowSteps,
|
|
21
22
|
messagesForScenario,
|
|
22
23
|
scenarios,
|
|
24
|
+
type DemoAskUserResponse,
|
|
23
25
|
type DemoScenario,
|
|
24
26
|
type DemoScenarioMeta,
|
|
25
27
|
} from "./chat-scenarios";
|
|
@@ -42,7 +44,7 @@ export function CamelChatShowcase() {
|
|
|
42
44
|
const [theme, setTheme] = useState<"light" | "dark">("dark");
|
|
43
45
|
const [scenario, setScenario] = useState<DemoScenario>(initialScenario);
|
|
44
46
|
const [prompt, setPrompt] = useState(defaultPrompt);
|
|
45
|
-
const [humanAnswer, setHumanAnswer] = useState(
|
|
47
|
+
const [humanAnswer, setHumanAnswer] = useState(demoAskUserResponse);
|
|
46
48
|
const [model, setModel] = useState<string>(demoModels[0].value);
|
|
47
49
|
const [value, setValue] = useState("");
|
|
48
50
|
const [attachments, setAttachments] =
|
|
@@ -70,7 +72,8 @@ export function CamelChatShowcase() {
|
|
|
70
72
|
: {};
|
|
71
73
|
return (
|
|
72
74
|
record.type === "tool-write_file" ||
|
|
73
|
-
record.type === "tool-cancel_schedule"
|
|
75
|
+
record.type === "tool-cancel_schedule" ||
|
|
76
|
+
record.type === "tool-schedule"
|
|
74
77
|
) &&
|
|
75
78
|
record.state === "approval-requested"
|
|
76
79
|
? { id: "camel-demo-approval", approved: approval }
|
|
@@ -254,6 +257,14 @@ export function CamelChatShowcase() {
|
|
|
254
257
|
}}
|
|
255
258
|
onApprove={(_approvalId, decision) => {
|
|
256
259
|
const approved = decision !== "deny";
|
|
260
|
+
if (scenario === "schedule_create") {
|
|
261
|
+
setApproval(approved);
|
|
262
|
+
selectScenario(approved ? "schedule" : "submitted");
|
|
263
|
+
setLastEvent(approved
|
|
264
|
+
? "Scheduled task created"
|
|
265
|
+
: "Schedule creation cancelled");
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
257
268
|
if (scenario === "schedule_cancel") {
|
|
258
269
|
setApproval(approved);
|
|
259
270
|
setLastEvent(
|
|
@@ -274,20 +285,17 @@ export function CamelChatShowcase() {
|
|
|
274
285
|
onAnswer={(payload) => {
|
|
275
286
|
const data =
|
|
276
287
|
payload && typeof payload === "object"
|
|
277
|
-
? (payload as
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
...(data.selections?.flat() ?? []),
|
|
284
|
-
data.text?.trim() ?? "",
|
|
285
|
-
]
|
|
288
|
+
? (payload as DemoAskUserResponse)
|
|
289
|
+
: { answers: [] };
|
|
290
|
+
const answer = data.answers.flatMap((item) => [
|
|
291
|
+
...item.selections,
|
|
292
|
+
item.text?.trim() ?? "",
|
|
293
|
+
])
|
|
286
294
|
.filter(Boolean)
|
|
287
295
|
.join("、");
|
|
288
|
-
if (
|
|
296
|
+
if (data.answers.length > 0) setHumanAnswer(data);
|
|
289
297
|
selectScenario("planning");
|
|
290
|
-
setLastEvent(`已回答并继续:${answer
|
|
298
|
+
setLastEvent(`已回答并继续:${answer}`);
|
|
291
299
|
}}
|
|
292
300
|
getToolApproval={approvalView}
|
|
293
301
|
/>
|
package/demo/chat-scenarios.ts
CHANGED
|
@@ -37,6 +37,7 @@ export type DemoScenario =
|
|
|
37
37
|
| "temporary_agent"
|
|
38
38
|
| "agent_tools"
|
|
39
39
|
| "background_agent"
|
|
40
|
+
| "schedule_create"
|
|
40
41
|
| "schedule"
|
|
41
42
|
| "schedule_cancel"
|
|
42
43
|
| "approval"
|
|
@@ -118,9 +119,15 @@ export const scenarios: Record<DemoScenario, DemoScenarioMeta> = {
|
|
|
118
119
|
status: "ready",
|
|
119
120
|
flowStep: 3,
|
|
120
121
|
},
|
|
122
|
+
schedule_create: {
|
|
123
|
+
label: "Create scheduled task",
|
|
124
|
+
description: "Review the task, cadence, and timezone before creating the schedule.",
|
|
125
|
+
status: "ready",
|
|
126
|
+
flowStep: 2,
|
|
127
|
+
},
|
|
121
128
|
schedule: {
|
|
122
|
-
label: "
|
|
123
|
-
description: "schedule
|
|
129
|
+
label: "Scheduled task created",
|
|
130
|
+
description: "A completed schedule shows its task, cadence, timezone, and final status.",
|
|
124
131
|
status: "ready",
|
|
125
132
|
flowStep: 3,
|
|
126
133
|
},
|
|
@@ -239,42 +246,79 @@ function userMessage(prompt: string): UIMessage {
|
|
|
239
246
|
} as UIMessage;
|
|
240
247
|
}
|
|
241
248
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
input: {
|
|
251
|
-
question: "这份建议优先面向哪个读者?",
|
|
252
|
-
options: ["产品团队", "管理层", "设计团队"],
|
|
253
|
-
multiSelect: false,
|
|
254
|
-
},
|
|
255
|
-
output: { asked: true },
|
|
256
|
-
},
|
|
249
|
+
export interface DemoAskUserResponse {
|
|
250
|
+
answers: Array<{ selections: string[]; text?: string }>;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export const demoAskUserResponse: DemoAskUserResponse = {
|
|
254
|
+
answers: [
|
|
255
|
+
{ selections: ["管理层"], text: "" },
|
|
256
|
+
{ selections: ["报告"], text: "" },
|
|
257
257
|
],
|
|
258
|
-
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const askUserQuestions = [
|
|
261
|
+
{
|
|
262
|
+
question: "这份建议优先面向哪个读者?",
|
|
263
|
+
options: ["产品团队", "管理层", "设计团队"],
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
question: "需要哪些交付物?",
|
|
267
|
+
options: ["报告", "演示文稿", "执行清单"],
|
|
268
|
+
multiSelect: true,
|
|
269
|
+
allowCustom: true,
|
|
270
|
+
},
|
|
271
|
+
];
|
|
259
272
|
|
|
260
|
-
function
|
|
273
|
+
function askUserRequest(response?: DemoAskUserResponse): UIMessage {
|
|
261
274
|
return {
|
|
262
|
-
id: "camel-demo-
|
|
263
|
-
role: "
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
275
|
+
id: "camel-demo-ask-user",
|
|
276
|
+
role: "assistant",
|
|
277
|
+
parts: [
|
|
278
|
+
{
|
|
279
|
+
type: "tool-ask_user",
|
|
280
|
+
toolCallId: "camel-demo-ask-user-1",
|
|
281
|
+
state: response ? "output-available" : "input-available",
|
|
282
|
+
input: { questions: askUserQuestions },
|
|
283
|
+
...(response
|
|
284
|
+
? {
|
|
285
|
+
output: {
|
|
286
|
+
answers: response.answers.map((answer, index) => ({
|
|
287
|
+
question: askUserQuestions[index]!.question,
|
|
288
|
+
selections: answer.selections,
|
|
289
|
+
text: answer.text?.trim() ?? "",
|
|
290
|
+
})),
|
|
291
|
+
},
|
|
292
|
+
}
|
|
293
|
+
: {}),
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
} as unknown as UIMessage;
|
|
271
297
|
}
|
|
272
298
|
|
|
273
299
|
const planningWork = {
|
|
274
300
|
id: "camel-demo-planning-work",
|
|
275
301
|
role: "assistant",
|
|
276
|
-
metadata: {
|
|
302
|
+
metadata: {
|
|
303
|
+
turnStartedAt: demoTurnStartedAt + 3_000,
|
|
304
|
+
turnDurationMs: 18_000,
|
|
305
|
+
},
|
|
277
306
|
parts: [
|
|
307
|
+
{
|
|
308
|
+
type: "reasoning",
|
|
309
|
+
text: `先确认受众和交付范围,再验证产品案例。
|
|
310
|
+
|
|
311
|
+
用户选择的读者是管理层,交付物是报告。因此内容不能只罗列界面趋势,还需要把每项趋势连接到业务价值、落地成本和潜在风险,帮助管理层判断优先级。
|
|
312
|
+
|
|
313
|
+
接下来会分三步推进:
|
|
314
|
+
|
|
315
|
+
1. 检索 2025–2026 年主流 AI Agent 产品,优先采集官方发布说明、产品文档和可验证的真实界面。
|
|
316
|
+
2. 对比任务规划、工具调用、人工确认、后台执行和结果交付等关键交互,区分普遍模式与单个产品的特色设计。
|
|
317
|
+
3. 把证据整理成可执行建议,并为每条建议标注适用场景、实现复杂度和预期收益。
|
|
318
|
+
|
|
319
|
+
第一轮检索先覆盖通用 Agent、开发者 Agent 与企业自动化产品。如果不同来源对同一趋势的描述冲突,会保留分歧并继续寻找一手证据,而不是直接下结论。`,
|
|
320
|
+
state: "done",
|
|
321
|
+
},
|
|
278
322
|
{
|
|
279
323
|
type: "tool-update_plan",
|
|
280
324
|
state: "output-available",
|
|
@@ -459,6 +503,26 @@ const backgroundResult = {
|
|
|
459
503
|
],
|
|
460
504
|
} as unknown as UIMessage;
|
|
461
505
|
|
|
506
|
+
export const demoScheduleInput = {
|
|
507
|
+
label: "Weekly competitor report",
|
|
508
|
+
prompt: "Generate and send this week's competitor report.",
|
|
509
|
+
trigger: { kind: "cron", cron: "0 9 * * 1" },
|
|
510
|
+
timezone: "Asia/Shanghai",
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
const scheduleRequest = {
|
|
514
|
+
id: "camel-demo-schedule-request",
|
|
515
|
+
role: "assistant",
|
|
516
|
+
parts: [
|
|
517
|
+
{
|
|
518
|
+
type: "tool-schedule",
|
|
519
|
+
toolCallId: "camel-demo-schedule-request-1",
|
|
520
|
+
state: "approval-requested",
|
|
521
|
+
input: demoScheduleInput,
|
|
522
|
+
},
|
|
523
|
+
],
|
|
524
|
+
} as unknown as UIMessage;
|
|
525
|
+
|
|
462
526
|
const scheduledTask = {
|
|
463
527
|
id: "camel-demo-schedule",
|
|
464
528
|
role: "assistant",
|
|
@@ -467,12 +531,7 @@ const scheduledTask = {
|
|
|
467
531
|
type: "tool-schedule",
|
|
468
532
|
toolCallId: "camel-demo-schedule-1",
|
|
469
533
|
state: "output-available",
|
|
470
|
-
input:
|
|
471
|
-
label: "每周发送竞品报告",
|
|
472
|
-
prompt: "生成并发送本周竞品报告。",
|
|
473
|
-
trigger: { kind: "cron", cron: "0 9 * * 1" },
|
|
474
|
-
timezone: "Asia/Shanghai",
|
|
475
|
-
},
|
|
534
|
+
input: demoScheduleInput,
|
|
476
535
|
output: { scheduled: true, id: "schedule-1" },
|
|
477
536
|
},
|
|
478
537
|
],
|
|
@@ -827,7 +886,7 @@ const failedWork = {
|
|
|
827
886
|
export function messagesForScenario(
|
|
828
887
|
scenario: DemoScenario,
|
|
829
888
|
prompt: string,
|
|
830
|
-
humanAnswer:
|
|
889
|
+
humanAnswer: DemoAskUserResponse,
|
|
831
890
|
approval: boolean | undefined,
|
|
832
891
|
): UIMessage[] {
|
|
833
892
|
if (scenario === "empty") return [];
|
|
@@ -835,15 +894,13 @@ export function messagesForScenario(
|
|
|
835
894
|
return [noticeMessage(scenario), ...commandMessages];
|
|
836
895
|
}
|
|
837
896
|
const conversation = [noticeMessage(scenario), userMessage(prompt)];
|
|
838
|
-
const answer = humanAnswer || "管理层";
|
|
839
897
|
const decidedConversation = [
|
|
840
898
|
...conversation,
|
|
841
|
-
askUserRequest,
|
|
842
|
-
humanReplyMessage(answer),
|
|
899
|
+
askUserRequest(humanAnswer),
|
|
843
900
|
];
|
|
844
901
|
if (scenario === "hydrating") return conversation;
|
|
845
902
|
if (scenario === "submitted") return conversation;
|
|
846
|
-
if (scenario === "ask") return [...conversation, askUserRequest];
|
|
903
|
+
if (scenario === "ask") return [...conversation, askUserRequest()];
|
|
847
904
|
if (scenario === "planning") {
|
|
848
905
|
return [...decidedConversation, planningWork];
|
|
849
906
|
}
|
|
@@ -860,6 +917,9 @@ export function messagesForScenario(
|
|
|
860
917
|
if (scenario === "background_agent") {
|
|
861
918
|
return [...conversation, backgroundDispatch, backgroundResult];
|
|
862
919
|
}
|
|
920
|
+
if (scenario === "schedule_create") {
|
|
921
|
+
return [...conversation, scheduleRequest];
|
|
922
|
+
}
|
|
863
923
|
if (scenario === "schedule") {
|
|
864
924
|
return [...conversation, scheduledTask];
|
|
865
925
|
}
|
|
@@ -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,7 +287,7 @@ export function CloudOsChatShowcase() {
|
|
|
283
287
|
|
|
284
288
|
<CloudOsWorkspaceSplit
|
|
285
289
|
workspaceOpen={workspaceOpen}
|
|
286
|
-
|
|
290
|
+
isLoading={scenario === "hydrating"}
|
|
287
291
|
chat={
|
|
288
292
|
<div className="flex h-full min-h-0 flex-col bg-kumo-base">
|
|
289
293
|
<aside
|
|
@@ -348,11 +352,40 @@ export function CloudOsChatShowcase() {
|
|
|
348
352
|
<CloudOsChatMessages
|
|
349
353
|
messages={messages}
|
|
350
354
|
status={status}
|
|
351
|
-
|
|
355
|
+
turnActive={
|
|
356
|
+
status === "submitted" ||
|
|
357
|
+
status === "streaming" ||
|
|
358
|
+
scenario === "ask" ||
|
|
359
|
+
scenario === "schedule_create" ||
|
|
360
|
+
((scenario === "approval" || scenario === "schedule_cancel") &&
|
|
361
|
+
approval === undefined)
|
|
362
|
+
}
|
|
352
363
|
isRecovering={scenario === "recovering"}
|
|
353
|
-
recoveryStatusLabel="
|
|
364
|
+
recoveryStatusLabel="Model response interrupted. Retrying…"
|
|
365
|
+
awaitingApproval={
|
|
366
|
+
(scenario === "approval" ||
|
|
367
|
+
scenario === "schedule_cancel" ||
|
|
368
|
+
scenario === "schedule_create") &&
|
|
369
|
+
approval === undefined
|
|
370
|
+
}
|
|
354
371
|
showThinkingTraces={showThinkingTraces}
|
|
355
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}
|
|
356
389
|
error={statusError}
|
|
357
390
|
constrainWidth={!workspaceOpen}
|
|
358
391
|
onRetry={() => {
|
|
@@ -376,20 +409,21 @@ export function CloudOsChatShowcase() {
|
|
|
376
409
|
setValue("");
|
|
377
410
|
setLastEvent(`已发送后续问题:${text}`);
|
|
378
411
|
}}
|
|
379
|
-
|
|
412
|
+
onRespond={async (_toolCallId, response) => {
|
|
380
413
|
const data =
|
|
381
|
-
|
|
382
|
-
? (
|
|
383
|
-
: {};
|
|
384
|
-
const answer = [
|
|
385
|
-
...
|
|
386
|
-
|
|
387
|
-
]
|
|
414
|
+
response && typeof response === "object"
|
|
415
|
+
? (response as DemoAskUserResponse)
|
|
416
|
+
: { answers: [] };
|
|
417
|
+
const answer = data.answers.flatMap((item) => [
|
|
418
|
+
...item.selections,
|
|
419
|
+
item.text?.trim() ?? "",
|
|
420
|
+
])
|
|
388
421
|
.filter(Boolean)
|
|
389
|
-
.join("
|
|
390
|
-
if (
|
|
422
|
+
.join(" / ");
|
|
423
|
+
if (data.answers.length > 0) setHumanAnswer(data);
|
|
391
424
|
selectScenario("planning");
|
|
392
|
-
setLastEvent(`已回答并继续:${answer
|
|
425
|
+
setLastEvent(`已回答并继续:${answer}`);
|
|
426
|
+
return true;
|
|
393
427
|
}}
|
|
394
428
|
/>
|
|
395
429
|
|
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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@springbrand/message-panel",
|
|
3
|
-
"version": "0.1.3-alpha.
|
|
3
|
+
"version": "0.1.3-alpha.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"src",
|
|
@@ -31,11 +31,10 @@
|
|
|
31
31
|
"@streamdown/math": "^1.0.2",
|
|
32
32
|
"@streamdown/mermaid": "^1.0.2",
|
|
33
33
|
"lucide-react": "^1.24.0",
|
|
34
|
+
"motion": "^13.1.0",
|
|
34
35
|
"radix-ui": "^1.6.2",
|
|
35
|
-
"react-markdown": "^10.1.0",
|
|
36
|
-
"remark-gfm": "^4.0.1",
|
|
37
36
|
"streamdown": "^2.5.0",
|
|
38
|
-
"thinking-orbs": "0.
|
|
37
|
+
"thinking-orbs": "0.2.0",
|
|
39
38
|
"use-stick-to-bottom": "^1.1.6"
|
|
40
39
|
},
|
|
41
40
|
"devDependencies": {
|