@springbrand/message-panel 0.3.0-alpha.6 → 0.3.0-alpha.9
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/chat/cloud-os-chat-messages.tsx +64 -6
- package/cloud-os/chat/tool-call.tsx +11 -0
- package/cloud-os/chat/tool-presentation.ts +38 -4
- package/cloud-os/chat/tool-rows.tsx +2 -0
- package/cloud-os/chat/tool-timeline.tsx +11 -0
- package/cloud-os/chat/transcript-model.ts +72 -5
- package/cloud-os/file-view.tsx +7 -0
- package/package.json +1 -1
- package/springbrand-pet/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
|
@@ -237,6 +237,7 @@ function UserBubble({
|
|
|
237
237
|
|
|
238
238
|
function ActionPresentationBlock({
|
|
239
239
|
presentation,
|
|
240
|
+
compact = false,
|
|
240
241
|
failureCount = 1,
|
|
241
242
|
partial = false,
|
|
242
243
|
renderFile,
|
|
@@ -245,6 +246,7 @@ function ActionPresentationBlock({
|
|
|
245
246
|
onOpenArtifact,
|
|
246
247
|
}: {
|
|
247
248
|
presentation: CloudOsActionPresentation;
|
|
249
|
+
compact?: boolean;
|
|
248
250
|
failureCount?: number;
|
|
249
251
|
partial?: boolean;
|
|
250
252
|
renderFile?: FileRenderer;
|
|
@@ -254,16 +256,41 @@ function ActionPresentationBlock({
|
|
|
254
256
|
}) {
|
|
255
257
|
const { state } = presentation;
|
|
256
258
|
if (state === "ready") {
|
|
259
|
+
const hasFileContent = presentation.content.length > 0;
|
|
260
|
+
const statusLabel = hasFileContent
|
|
261
|
+
? presentation.capabilityName || "Completed"
|
|
262
|
+
: presentation.toolBrief || "Completed";
|
|
263
|
+
const hasStatusMetadata = hasFileContent
|
|
264
|
+
? Boolean(presentation.capabilityName || presentation.iconUrl)
|
|
265
|
+
: Boolean(presentation.toolBrief || presentation.iconUrl);
|
|
266
|
+
const status = (
|
|
267
|
+
<span
|
|
268
|
+
className="flex shrink-0 items-center gap-2 text-[14px] leading-5 text-kumo-subtle"
|
|
269
|
+
data-action-status=""
|
|
270
|
+
>
|
|
271
|
+
{presentation.iconUrl && (
|
|
272
|
+
<img
|
|
273
|
+
src={presentation.iconUrl}
|
|
274
|
+
alt=""
|
|
275
|
+
aria-hidden="true"
|
|
276
|
+
className="size-4 rounded-sm object-contain"
|
|
277
|
+
data-action-icon=""
|
|
278
|
+
/>
|
|
279
|
+
)}
|
|
280
|
+
<span>{statusLabel}</span>
|
|
281
|
+
</span>
|
|
282
|
+
);
|
|
257
283
|
return (
|
|
258
284
|
<div
|
|
259
285
|
data-action-presentation={state}
|
|
260
286
|
role="status"
|
|
261
|
-
aria-label=
|
|
262
|
-
className=
|
|
287
|
+
aria-label={statusLabel}
|
|
288
|
+
className={compact
|
|
289
|
+
? "inline-flex items-center rounded-full border border-kumo-line bg-kumo-elevated px-3 py-1.5"
|
|
290
|
+
: "space-y-2"}
|
|
263
291
|
>
|
|
264
|
-
{presentation.content.length === 0
|
|
265
|
-
|
|
266
|
-
: presentation.content.map((file, index) => {
|
|
292
|
+
{presentation.content.length === 0 && status}
|
|
293
|
+
{presentation.content.map((file, index) => {
|
|
267
294
|
const url = resolveUrl?.(file.url) ?? file.url;
|
|
268
295
|
const artifactId = resolveArtifactId?.(url);
|
|
269
296
|
return (
|
|
@@ -276,6 +303,7 @@ function ActionPresentationBlock({
|
|
|
276
303
|
},
|
|
277
304
|
placement: "assistant-message",
|
|
278
305
|
variant: "product",
|
|
306
|
+
leadingMeta: hasStatusMetadata ? status : undefined,
|
|
279
307
|
onOpen: file.mediaType?.startsWith("image/") && artifactId && onOpenArtifact
|
|
280
308
|
? () => onOpenArtifact(artifactId)
|
|
281
309
|
: undefined,
|
|
@@ -897,10 +925,40 @@ export function CloudOsChatMessages({
|
|
|
897
925
|
|
|
898
926
|
{actionResultBlocks.length > 0 && (
|
|
899
927
|
<div data-action-results="" className="space-y-4">
|
|
928
|
+
{actionResultBlocks.filter((block) =>
|
|
929
|
+
block.presentation.state === "ready" &&
|
|
930
|
+
block.presentation.content.length === 0,
|
|
931
|
+
).length > 0 && (
|
|
932
|
+
<div data-action-empty-results="" className="flex flex-wrap gap-2">
|
|
933
|
+
{actionResultBlocks.map((block) => {
|
|
934
|
+
if (
|
|
935
|
+
block.presentation.state !== "ready" ||
|
|
936
|
+
block.presentation.content.length !== 0 ||
|
|
937
|
+
(block.presentation.failureCode === "insufficient_credits" &&
|
|
938
|
+
block !== firstInsufficientFailure)
|
|
939
|
+
) return null;
|
|
940
|
+
return (
|
|
941
|
+
<ActionPresentationBlock
|
|
942
|
+
key={block.key}
|
|
943
|
+
presentation={block.presentation}
|
|
944
|
+
compact
|
|
945
|
+
failureCount={insufficientFailures.length}
|
|
946
|
+
partial={hasCompletedAction}
|
|
947
|
+
renderFile={renderFile}
|
|
948
|
+
resolveUrl={resolveUrl}
|
|
949
|
+
resolveArtifactId={resolveArtifactId}
|
|
950
|
+
onOpenArtifact={onOpenArtifact}
|
|
951
|
+
/>
|
|
952
|
+
);
|
|
953
|
+
})}
|
|
954
|
+
</div>
|
|
955
|
+
)}
|
|
900
956
|
{actionResultBlocks.map((block) => {
|
|
901
957
|
if (
|
|
902
958
|
block.presentation.failureCode === "insufficient_credits" &&
|
|
903
|
-
block !== firstInsufficientFailure
|
|
959
|
+
block !== firstInsufficientFailure ||
|
|
960
|
+
(block.presentation.state === "ready" &&
|
|
961
|
+
block.presentation.content.length === 0)
|
|
904
962
|
) return null;
|
|
905
963
|
return (
|
|
906
964
|
<ActionPresentationBlock
|
|
@@ -21,6 +21,7 @@ export interface ToolCallProps {
|
|
|
21
21
|
query: string;
|
|
22
22
|
result: string;
|
|
23
23
|
running: boolean;
|
|
24
|
+
iconUrl?: string;
|
|
24
25
|
open: boolean;
|
|
25
26
|
onOpenChange: (open: boolean) => void;
|
|
26
27
|
className?: string;
|
|
@@ -33,6 +34,7 @@ export function ToolCall({
|
|
|
33
34
|
query,
|
|
34
35
|
result,
|
|
35
36
|
running,
|
|
37
|
+
iconUrl,
|
|
36
38
|
open,
|
|
37
39
|
onOpenChange,
|
|
38
40
|
className,
|
|
@@ -47,6 +49,15 @@ export function ToolCall({
|
|
|
47
49
|
>
|
|
48
50
|
<CollapsibleTrigger className="group/trigger text-foreground/55 hover:text-foreground/90 flex items-center gap-2 rounded-md py-1 text-[13.5px] transition-colors outline-none">
|
|
49
51
|
<ChevronRightIcon className="size-3.5 shrink-0 opacity-60 transition-transform duration-200 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-open/trigger:rotate-90 group-data-panel-open/trigger:rotate-90 motion-reduce:transition-none" />
|
|
52
|
+
{iconUrl && (
|
|
53
|
+
<img
|
|
54
|
+
src={iconUrl}
|
|
55
|
+
alt=""
|
|
56
|
+
aria-hidden="true"
|
|
57
|
+
className="size-4 shrink-0 rounded-sm object-contain"
|
|
58
|
+
data-tool-icon=""
|
|
59
|
+
/>
|
|
60
|
+
)}
|
|
50
61
|
<SwapLabel active={running ? 0 : 1} className="text-start">
|
|
51
62
|
<ShimmerLabel
|
|
52
63
|
active={running}
|
|
@@ -64,6 +64,9 @@ export interface CloudOsToolCall {
|
|
|
64
64
|
input: UnknownRecord;
|
|
65
65
|
output: unknown;
|
|
66
66
|
outputText: string;
|
|
67
|
+
capabilityName?: string;
|
|
68
|
+
toolBrief?: string;
|
|
69
|
+
iconUrl?: string;
|
|
67
70
|
errorText: string;
|
|
68
71
|
/** UIMessage 的 part.state 原值。 */
|
|
69
72
|
state: string;
|
|
@@ -77,6 +80,7 @@ export interface CloudOsToolCall {
|
|
|
77
80
|
export interface ToolCallGroup {
|
|
78
81
|
key: string;
|
|
79
82
|
Icon: PhosphorIcon;
|
|
83
|
+
iconUrl?: string;
|
|
80
84
|
label: string;
|
|
81
85
|
detailLines: string[];
|
|
82
86
|
calls: CloudOsToolCall[];
|
|
@@ -380,6 +384,12 @@ export function getToolCallSummary(call: CloudOsToolCall): {
|
|
|
380
384
|
break;
|
|
381
385
|
}
|
|
382
386
|
const displayName = humanizeToolName(toolName);
|
|
387
|
+
if (call.capabilityName) {
|
|
388
|
+
const capabilityLabel = [call.capabilityName, call.toolBrief]
|
|
389
|
+
.filter(Boolean)
|
|
390
|
+
.join(" · ");
|
|
391
|
+
return { verb: running ? `Using ${capabilityLabel}` : `Used ${capabilityLabel}` };
|
|
392
|
+
}
|
|
383
393
|
return { verb: running ? `Using ${displayName}` : `Used ${displayName}` };
|
|
384
394
|
}
|
|
385
395
|
|
|
@@ -448,10 +458,12 @@ export function toCloudOsToolCall(
|
|
|
448
458
|
part: unknown,
|
|
449
459
|
fallbackId: string,
|
|
450
460
|
isActive: boolean,
|
|
461
|
+
liveMetadata?: Record<string, unknown>,
|
|
451
462
|
): CloudOsToolCall {
|
|
452
463
|
const record = recordOf(part);
|
|
453
464
|
const toolName = toolNameOfPart(part) ?? stringField(record, "name") ?? "tool";
|
|
454
465
|
const data = recordOf(record.data);
|
|
466
|
+
const liveMeta = recordOf(liveMetadata);
|
|
455
467
|
const input =
|
|
456
468
|
record.input != null
|
|
457
469
|
? recordOf(record.input)
|
|
@@ -459,6 +471,18 @@ export function toCloudOsToolCall(
|
|
|
459
471
|
? recordOf(data.input)
|
|
460
472
|
: data;
|
|
461
473
|
const output = record.output ?? data.output ?? record.result;
|
|
474
|
+
const outputRecord = recordOf(output);
|
|
475
|
+
const presentation = recordOf(outputRecord.presentation);
|
|
476
|
+
const toolMeta = recordOf(outputRecord.toolMeta);
|
|
477
|
+
const capabilityName =
|
|
478
|
+
stringField(presentation, "capabilityName") || stringField(toolMeta, "capabilityName") ||
|
|
479
|
+
stringField(liveMeta, "capabilityName") || undefined;
|
|
480
|
+
const toolBrief =
|
|
481
|
+
stringField(presentation, "toolBrief") || stringField(toolMeta, "toolBrief") ||
|
|
482
|
+
stringField(liveMeta, "toolBrief") || undefined;
|
|
483
|
+
const iconUrl =
|
|
484
|
+
stringField(presentation, "iconUrl") || stringField(toolMeta, "iconUrl") ||
|
|
485
|
+
stringField(liveMeta, "iconUrl") || undefined;
|
|
462
486
|
const errorText = stringField(record, "errorText", "error");
|
|
463
487
|
const state = stringField(record, "state", "status");
|
|
464
488
|
const failed =
|
|
@@ -475,9 +499,12 @@ export function toCloudOsToolCall(
|
|
|
475
499
|
input,
|
|
476
500
|
output,
|
|
477
501
|
outputText: normalizeOutputText(output),
|
|
502
|
+
capabilityName,
|
|
503
|
+
toolBrief,
|
|
504
|
+
iconUrl,
|
|
478
505
|
errorText,
|
|
479
506
|
state,
|
|
480
|
-
running: isActive && !failed && RUNNING_STATES.has(state),
|
|
507
|
+
running: isActive && !failed && (RUNNING_STATES.has(state) || record.preliminary === true),
|
|
481
508
|
failed,
|
|
482
509
|
awaitingApproval,
|
|
483
510
|
raw: part,
|
|
@@ -608,10 +635,16 @@ export function buildToolCallGroups(calls: CloudOsToolCall[]): ToolCallGroup[] {
|
|
|
608
635
|
labelParts.push(`${summary.verb}${summary.target ? ` ${summary.target}` : ""}`);
|
|
609
636
|
} else if (distinctKinds.length === 1) {
|
|
610
637
|
const summary = getToolCallSummary(calls[0]);
|
|
638
|
+
const sameCapability = calls.every(
|
|
639
|
+
(call) => call.capabilityName === calls[0].capabilityName &&
|
|
640
|
+
call.toolBrief === calls[0].toolBrief,
|
|
641
|
+
);
|
|
611
642
|
labelParts.push(
|
|
612
|
-
|
|
613
|
-
? `${summary.verb} ${
|
|
614
|
-
:
|
|
643
|
+
sameCapability && calls[0].capabilityName
|
|
644
|
+
? `${summary.verb} ${formatTimes(calls.length)}`
|
|
645
|
+
: detailLines.length === 1 && summary.target
|
|
646
|
+
? `${summary.verb} ${summary.target}`
|
|
647
|
+
: describeToolCallCount(calls[0].kind, calls[0].toolName, calls.length),
|
|
615
648
|
);
|
|
616
649
|
} else if (distinctKinds.length <= 3) {
|
|
617
650
|
labelParts.push(
|
|
@@ -629,6 +662,7 @@ export function buildToolCallGroups(calls: CloudOsToolCall[]): ToolCallGroup[] {
|
|
|
629
662
|
// 用第一条 work item 的 id 作 key:展开态在「流式 → 已落库」之间要活下来。
|
|
630
663
|
key: `group-${calls[0].toolCallId}`,
|
|
631
664
|
Icon: getToolIcon(calls[0].kind),
|
|
665
|
+
iconUrl: calls.find((call) => call.iconUrl)?.iconUrl,
|
|
632
666
|
label: labelParts
|
|
633
667
|
.map((part, index) => (index === 0 ? part : lowerFirst(part)))
|
|
634
668
|
.join(", "),
|
|
@@ -54,6 +54,7 @@ function toolCallView(tc: CloudOsToolCall) {
|
|
|
54
54
|
return {
|
|
55
55
|
label: settled.verb,
|
|
56
56
|
activeLabel: active.verb,
|
|
57
|
+
iconUrl: tc.iconUrl,
|
|
57
58
|
query: settled.target ?? "",
|
|
58
59
|
result:
|
|
59
60
|
tc.errorText ||
|
|
@@ -246,6 +247,7 @@ export const ToolGroupRow = memo(function ToolGroupRow({
|
|
|
246
247
|
restingLabel={group.label}
|
|
247
248
|
activeLabel={`Working for ${elapsed}s`}
|
|
248
249
|
stats={[]}
|
|
250
|
+
iconUrl={group.iconUrl}
|
|
249
251
|
className="max-w-none"
|
|
250
252
|
>
|
|
251
253
|
{group.calls.map((toolCall) => {
|
|
@@ -32,6 +32,7 @@ export interface ToolTimelineProps {
|
|
|
32
32
|
restingLabel: string;
|
|
33
33
|
activeLabel: string;
|
|
34
34
|
stats: TimelineStat[];
|
|
35
|
+
iconUrl?: string;
|
|
35
36
|
className?: string;
|
|
36
37
|
children?: ReactNode;
|
|
37
38
|
}
|
|
@@ -45,6 +46,7 @@ export function ToolTimeline({
|
|
|
45
46
|
restingLabel,
|
|
46
47
|
activeLabel,
|
|
47
48
|
stats,
|
|
49
|
+
iconUrl,
|
|
48
50
|
className,
|
|
49
51
|
children,
|
|
50
52
|
}: ToolTimelineProps) {
|
|
@@ -57,6 +59,15 @@ export function ToolTimeline({
|
|
|
57
59
|
>
|
|
58
60
|
<CollapsibleTrigger className="group/trigger text-foreground/55 hover:text-foreground/90 flex items-center gap-1.5 rounded-md py-1 text-[13.5px] transition-colors outline-none">
|
|
59
61
|
<ChevronRightIcon className="size-3.5 shrink-0 opacity-60 transition-transform duration-200 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-open/trigger:rotate-90 group-data-panel-open/trigger:rotate-90 motion-reduce:transition-none" />
|
|
62
|
+
{iconUrl && (
|
|
63
|
+
<img
|
|
64
|
+
src={iconUrl}
|
|
65
|
+
alt=""
|
|
66
|
+
aria-hidden="true"
|
|
67
|
+
className="size-4 shrink-0 rounded-sm object-contain"
|
|
68
|
+
data-tool-icon=""
|
|
69
|
+
/>
|
|
70
|
+
)}
|
|
60
71
|
<SwapLabel
|
|
61
72
|
active={streaming ? 0 : 1}
|
|
62
73
|
className="text-start tabular-nums"
|
|
@@ -62,6 +62,9 @@ export interface CloudOsActionPresentation {
|
|
|
62
62
|
executionId: string;
|
|
63
63
|
state: "running" | "ready" | "failed" | "outcome_unknown";
|
|
64
64
|
failureCode?: "insufficient_credits";
|
|
65
|
+
capabilityName?: string;
|
|
66
|
+
toolBrief?: string;
|
|
67
|
+
iconUrl?: string;
|
|
65
68
|
produces?: CloudOsActionProduces;
|
|
66
69
|
content: CloudOsActionPresentationFile[];
|
|
67
70
|
}
|
|
@@ -338,6 +341,23 @@ function actionPresentationOf(value: unknown): CloudOsActionPresentation | null
|
|
|
338
341
|
}
|
|
339
342
|
if (state !== "ready" && content.length > 0) return null;
|
|
340
343
|
|
|
344
|
+
const toolBrief = typeof presentation.toolBrief === "string" && presentation.toolBrief.trim()
|
|
345
|
+
? presentation.toolBrief.trim()
|
|
346
|
+
: undefined;
|
|
347
|
+
const capabilityName = typeof presentation.capabilityName === "string" && presentation.capabilityName.trim()
|
|
348
|
+
? presentation.capabilityName.trim()
|
|
349
|
+
: undefined;
|
|
350
|
+
let iconUrl: string | undefined;
|
|
351
|
+
if (presentation.iconUrl !== undefined) {
|
|
352
|
+
if (typeof presentation.iconUrl !== "string" || !presentation.iconUrl.trim()) return null;
|
|
353
|
+
try {
|
|
354
|
+
if (new URL(presentation.iconUrl).protocol !== "https:") return null;
|
|
355
|
+
} catch {
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
iconUrl = presentation.iconUrl;
|
|
359
|
+
}
|
|
360
|
+
|
|
341
361
|
return {
|
|
342
362
|
type: "action-execution",
|
|
343
363
|
executionId: presentation.executionId,
|
|
@@ -345,6 +365,9 @@ function actionPresentationOf(value: unknown): CloudOsActionPresentation | null
|
|
|
345
365
|
...(state === "failed" && presentation.failureCode === "insufficient_credits"
|
|
346
366
|
? { failureCode: presentation.failureCode }
|
|
347
367
|
: {}),
|
|
368
|
+
...(capabilityName ? { capabilityName } : {}),
|
|
369
|
+
...(toolBrief ? { toolBrief } : {}),
|
|
370
|
+
...(iconUrl ? { iconUrl } : {}),
|
|
348
371
|
...(produces ? { produces } : {}),
|
|
349
372
|
content,
|
|
350
373
|
};
|
|
@@ -408,9 +431,26 @@ function assistantBlocks(
|
|
|
408
431
|
approvalIdOf: (part: unknown) => string | undefined,
|
|
409
432
|
): AssistantBlock[] {
|
|
410
433
|
const blocks: AssistantBlock[] = [];
|
|
434
|
+
const actionToolMetaByCallId = new Map<string, UnknownRecord>();
|
|
435
|
+
for (const part of message.parts) {
|
|
436
|
+
if (dataKindOf(part) !== "action_tool_meta") continue;
|
|
437
|
+
const record = recordOf(part);
|
|
438
|
+
const data = dataOf(part);
|
|
439
|
+
const toolCallId = String(data.toolCallId ?? record.id ?? "");
|
|
440
|
+
if (toolCallId) actionToolMetaByCallId.set(toolCallId, data);
|
|
441
|
+
}
|
|
411
442
|
let pendingCalls: CloudOsToolCall[] = [];
|
|
443
|
+
let pendingActionCalls: CloudOsToolCall[] = [];
|
|
412
444
|
|
|
413
|
-
const
|
|
445
|
+
const flushActionCalls = () => {
|
|
446
|
+
if (pendingActionCalls.length === 0) return;
|
|
447
|
+
for (const group of buildToolCallGroups(pendingActionCalls)) {
|
|
448
|
+
blocks.push({ kind: "toolGroup", key: group.key, group });
|
|
449
|
+
}
|
|
450
|
+
pendingActionCalls = [];
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
const flushRegularCalls = () => {
|
|
414
454
|
if (pendingCalls.length === 0) return;
|
|
415
455
|
for (const group of buildToolCallGroups(pendingCalls)) {
|
|
416
456
|
blocks.push({ kind: "toolGroup", key: group.key, group });
|
|
@@ -418,6 +458,11 @@ function assistantBlocks(
|
|
|
418
458
|
pendingCalls = [];
|
|
419
459
|
};
|
|
420
460
|
|
|
461
|
+
const flush = () => {
|
|
462
|
+
flushActionCalls();
|
|
463
|
+
flushRegularCalls();
|
|
464
|
+
};
|
|
465
|
+
|
|
421
466
|
message.parts.forEach((part, partIndex) => {
|
|
422
467
|
const key = `${message.id}:${partIndex}`;
|
|
423
468
|
const record = recordOf(part);
|
|
@@ -461,6 +506,7 @@ function assistantBlocks(
|
|
|
461
506
|
}
|
|
462
507
|
|
|
463
508
|
const dataKind = dataKindOf(part);
|
|
509
|
+
if (dataKind === "action_tool_meta") return;
|
|
464
510
|
if (dataKind === "error") {
|
|
465
511
|
const data = dataOf(part);
|
|
466
512
|
flush();
|
|
@@ -546,11 +592,23 @@ function assistantBlocks(
|
|
|
546
592
|
const toolName = toolNameOf(part);
|
|
547
593
|
if (toolName === null) return;
|
|
548
594
|
|
|
549
|
-
const
|
|
595
|
+
const toolCallId = String(record.toolCallId ?? record.id ?? "");
|
|
596
|
+
const call = toCloudOsToolCall(
|
|
597
|
+
part,
|
|
598
|
+
key,
|
|
599
|
+
isActive,
|
|
600
|
+
actionToolMetaByCallId.get(toolCallId),
|
|
601
|
+
);
|
|
550
602
|
const presentation = actionPresentationOf(call.output);
|
|
551
603
|
if (presentation) {
|
|
552
|
-
|
|
553
|
-
|
|
604
|
+
if (presentation.state === "running") {
|
|
605
|
+
flushActionCalls();
|
|
606
|
+
pendingCalls.push(call);
|
|
607
|
+
flush();
|
|
608
|
+
} else {
|
|
609
|
+
flushRegularCalls();
|
|
610
|
+
pendingActionCalls.push(call);
|
|
611
|
+
}
|
|
554
612
|
blocks.push({ kind: "actionPresentation", key, presentation });
|
|
555
613
|
return;
|
|
556
614
|
}
|
|
@@ -600,6 +658,7 @@ function assistantBlocks(
|
|
|
600
658
|
return;
|
|
601
659
|
}
|
|
602
660
|
|
|
661
|
+
flushActionCalls();
|
|
603
662
|
pendingCalls.push(call);
|
|
604
663
|
});
|
|
605
664
|
|
|
@@ -830,11 +889,19 @@ function dropSupersededActionPresentations(entries: CloudOsEntry[]): CloudOsEntr
|
|
|
830
889
|
return;
|
|
831
890
|
}
|
|
832
891
|
if (block.presentation.state !== "running") {
|
|
833
|
-
current.presentation =
|
|
892
|
+
current.presentation = {
|
|
893
|
+
...block.presentation,
|
|
894
|
+
capabilityName: block.presentation.capabilityName ?? current.presentation.capabilityName,
|
|
895
|
+
toolBrief: block.presentation.toolBrief ?? current.presentation.toolBrief,
|
|
896
|
+
iconUrl: block.presentation.iconUrl ?? current.presentation.iconUrl,
|
|
897
|
+
};
|
|
834
898
|
} else if (current.presentation.state === "running") {
|
|
835
899
|
current.presentation = {
|
|
836
900
|
...block.presentation,
|
|
837
901
|
produces: block.presentation.produces ?? current.presentation.produces,
|
|
902
|
+
capabilityName: block.presentation.capabilityName ?? current.presentation.capabilityName,
|
|
903
|
+
toolBrief: block.presentation.toolBrief ?? current.presentation.toolBrief,
|
|
904
|
+
iconUrl: block.presentation.iconUrl ?? current.presentation.iconUrl,
|
|
838
905
|
};
|
|
839
906
|
}
|
|
840
907
|
});
|
package/cloud-os/file-view.tsx
CHANGED
|
@@ -61,6 +61,7 @@ export interface FileViewProps {
|
|
|
61
61
|
file: FileViewFile;
|
|
62
62
|
placement: FileViewPlacement;
|
|
63
63
|
variant?: FileViewVariant;
|
|
64
|
+
leadingMeta?: ReactNode;
|
|
64
65
|
status?: FileViewStatus;
|
|
65
66
|
progress?: number;
|
|
66
67
|
onOpen?: () => void;
|
|
@@ -172,11 +173,13 @@ function ProductFileView({
|
|
|
172
173
|
file,
|
|
173
174
|
label,
|
|
174
175
|
placement,
|
|
176
|
+
leadingMeta,
|
|
175
177
|
onOpen,
|
|
176
178
|
}: {
|
|
177
179
|
file: FileViewFile;
|
|
178
180
|
label: string;
|
|
179
181
|
placement: Exclude<FileViewPlacement, "composer">;
|
|
182
|
+
leadingMeta?: ReactNode;
|
|
180
183
|
onOpen?: () => void;
|
|
181
184
|
}) {
|
|
182
185
|
const type = productTypeForMediaType(file.mediaType);
|
|
@@ -225,6 +228,8 @@ function ProductFileView({
|
|
|
225
228
|
<span className="flex w-fit max-w-full flex-col gap-2.5" data-product-file-view={type}>
|
|
226
229
|
{preview}
|
|
227
230
|
<span className="flex min-w-0 max-w-full items-center gap-1 text-xs text-kumo-inactive">
|
|
231
|
+
{leadingMeta}
|
|
232
|
+
{leadingMeta && <span aria-hidden="true">·</span>}
|
|
228
233
|
<span className="capitalize text-kumo-subtle">{type}</span>
|
|
229
234
|
<span aria-hidden="true">·</span>
|
|
230
235
|
<span className="min-w-0 truncate">{label}</span>
|
|
@@ -306,6 +311,7 @@ export function FileView({
|
|
|
306
311
|
file,
|
|
307
312
|
placement,
|
|
308
313
|
variant = "compact",
|
|
314
|
+
leadingMeta,
|
|
309
315
|
status = "ready",
|
|
310
316
|
progress = 0,
|
|
311
317
|
onOpen,
|
|
@@ -413,6 +419,7 @@ export function FileView({
|
|
|
413
419
|
file={file}
|
|
414
420
|
label={label}
|
|
415
421
|
placement={placement}
|
|
422
|
+
leadingMeta={leadingMeta}
|
|
416
423
|
onOpen={onOpen}
|
|
417
424
|
/>
|
|
418
425
|
);
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"4.1.10","results":[[":bot/cycles.test.ts",{"duration":
|
|
1
|
+
{"version":"4.1.10","results":[[":bot/cycles.test.ts",{"duration":54.568250000000006,"failed":false}],[":bot/skins.test.ts",{"duration":2830.836917,"failed":false}],[":bot/engine.test.ts",{"duration":80.72770900000003,"failed":false}],[":bot/shape.test.ts",{"duration":10.531834000000003,"failed":false}],[":bot/expressions.test.ts",{"duration":8.110375000000005,"failed":false}],[":bot/face.test.ts",{"duration":2.7700000000000102,"failed":false}],[":drag.test.ts",{"duration":1.9160829999999862,"failed":false}],[":animation.test.ts",{"duration":2.0596669999999904,"failed":false}]]}
|