@tangle-network/ui 11.14.0 → 11.15.1
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/CHANGELOG.md +16 -0
- package/dist/chat/chat-container.js +31 -9
- package/dist/chat/synthetic-text.js +52 -0
- package/dist/chat/user-message.d.ts +2 -4
- package/dist/chat/user-message.js +34 -13
- package/dist/lib/control-presentation.js +11 -0
- package/dist/primitives/button.d.ts +2 -2
- package/dist/primitives/button.js +11 -9
- package/dist/primitives/input.d.ts +3 -1
- package/dist/primitives/input.js +7 -5
- package/dist/primitives/metric-strip.d.ts +6 -28
- package/dist/primitives/metric-strip.js +15 -18
- package/dist/primitives/select.d.ts +4 -1
- package/dist/primitives/select.js +7 -7
- package/dist/run/run-group.js +89 -62
- package/package.json +1 -1
- package/src/chat/chat-container.synthetic.stories.tsx +82 -0
- package/src/chat/chat-container.synthetic.test.tsx +171 -0
- package/src/chat/chat-container.tsx +33 -16
- package/src/chat/synthetic-text.test.tsx +80 -0
- package/src/chat/synthetic-text.tsx +51 -0
- package/src/chat/user-message.tsx +54 -36
- package/src/lib/control-presentation.ts +26 -0
- package/src/primitives/button.tsx +11 -9
- package/src/primitives/control-presentation.stories.tsx +79 -0
- package/src/primitives/control-presentation.test.tsx +67 -0
- package/src/primitives/input.tsx +12 -6
- package/src/primitives/metric-strip.stories.tsx +39 -0
- package/src/primitives/metric-strip.test.tsx +82 -144
- package/src/primitives/metric-strip.tsx +27 -61
- package/src/primitives/select.tsx +16 -9
- package/src/run/run-group.tsx +99 -64
package/src/run/run-group.tsx
CHANGED
|
@@ -25,6 +25,7 @@ import type { CustomToolRenderer } from "../types/tool-display";
|
|
|
25
25
|
import { InlineToolItem } from "./inline-tool-item";
|
|
26
26
|
import { InlineThinkingItem } from "./inline-thinking-item";
|
|
27
27
|
import { Markdown } from "../markdown/markdown";
|
|
28
|
+
import { SyntheticText } from "../chat/synthetic-text";
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* One row on the run's timeline spine: a connector line + accent dot in a
|
|
@@ -319,25 +320,23 @@ export const RunGroup = memo(
|
|
|
319
320
|
return true;
|
|
320
321
|
}
|
|
321
322
|
|
|
322
|
-
return part.type === "text" &&
|
|
323
|
+
return part.type === "text" && part.text.trim().length > 0;
|
|
323
324
|
});
|
|
324
325
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
326
|
+
const pendingResponse = (
|
|
327
|
+
<AssistantShell branding={branding} isStreaming={true}>
|
|
328
|
+
<div className="flex items-center gap-2 px-0.5 py-0.5 text-sm text-[var(--text-muted)]">
|
|
329
|
+
<span className="flex gap-[5px]">
|
|
330
|
+
<span className="h-2 w-2 animate-bounce rounded-full bg-[var(--brand-glow)]" style={{ animationDelay: "0ms" }} />
|
|
331
|
+
<span className="h-2 w-2 animate-bounce rounded-full bg-[var(--brand-glow)]" style={{ animationDelay: "150ms" }} />
|
|
332
|
+
<span className="h-2 w-2 animate-bounce rounded-full bg-[var(--brand-glow)]" style={{ animationDelay: "300ms" }} />
|
|
333
|
+
</span>
|
|
334
|
+
</div>
|
|
335
|
+
</AssistantShell>
|
|
336
|
+
);
|
|
329
337
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
<div className="flex items-center gap-2 px-0.5 py-0.5 text-sm text-[var(--text-muted)]">
|
|
333
|
-
<span className="flex gap-[5px]">
|
|
334
|
-
<span className="h-2 w-2 animate-bounce rounded-full bg-[var(--brand-glow)]" style={{ animationDelay: "0ms" }} />
|
|
335
|
-
<span className="h-2 w-2 animate-bounce rounded-full bg-[var(--brand-glow)]" style={{ animationDelay: "150ms" }} />
|
|
336
|
-
<span className="h-2 w-2 animate-bounce rounded-full bg-[var(--brand-glow)]" style={{ animationDelay: "300ms" }} />
|
|
337
|
-
</span>
|
|
338
|
-
</div>
|
|
339
|
-
</AssistantShell>
|
|
340
|
-
);
|
|
338
|
+
if (!hasRenderableParts) {
|
|
339
|
+
return isStreaming ? pendingResponse : null;
|
|
341
340
|
}
|
|
342
341
|
|
|
343
342
|
const showTraceChrome = allParts.some(({ part }) => {
|
|
@@ -353,72 +352,106 @@ export const RunGroup = memo(
|
|
|
353
352
|
});
|
|
354
353
|
|
|
355
354
|
if (!showTraceChrome) {
|
|
355
|
+
// Notes split authored blocks rather than living inside an Agent bubble.
|
|
356
|
+
// Original message/part indices remain available for keys and callbacks.
|
|
357
|
+
const segments: Array<
|
|
358
|
+
| { kind: "note"; key: string; text: string }
|
|
359
|
+
| { kind: "source"; key: string; entries: typeof allParts }
|
|
360
|
+
> = [];
|
|
361
|
+
let lastSourceSegment = -1;
|
|
362
|
+
for (const entry of allParts) {
|
|
363
|
+
const { part, msgId, index } = entry;
|
|
364
|
+
if (part.type === "text" && !part.text.trim()) continue;
|
|
365
|
+
const key = `${msgId}-${index}`;
|
|
366
|
+
if (part.type === "text" && part.synthetic) {
|
|
367
|
+
segments.push({ kind: "note", key, text: part.text });
|
|
368
|
+
} else {
|
|
369
|
+
const previous = segments[segments.length - 1];
|
|
370
|
+
if (previous?.kind === "source") {
|
|
371
|
+
previous.entries.push(entry);
|
|
372
|
+
} else {
|
|
373
|
+
segments.push({ kind: "source", key, entries: [entry] });
|
|
374
|
+
}
|
|
375
|
+
lastSourceSegment = segments.length - 1;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
356
378
|
return (
|
|
357
|
-
<
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
379
|
+
<div className="space-y-3">
|
|
380
|
+
{segments.map((segment, segmentIndex) => segment.kind === "note" ? (
|
|
381
|
+
<SyntheticText key={segment.key} text={segment.text} />
|
|
382
|
+
) : (
|
|
383
|
+
<AssistantShell
|
|
384
|
+
key={segment.key}
|
|
385
|
+
branding={branding}
|
|
386
|
+
isStreaming={isStreaming && segmentIndex === lastSourceSegment}
|
|
387
|
+
>
|
|
388
|
+
{segment.entries.map(({ part, msgId, index }) => {
|
|
389
|
+
const key = `${msgId}-${index}`;
|
|
365
390
|
|
|
366
|
-
if (
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
391
|
+
if (part.type === "tool" && isOpenUITool(part as ToolPart)) {
|
|
392
|
+
const toolPart = part as ToolPart;
|
|
393
|
+
const schema = extractOpenUISchema(toolPart.state.output);
|
|
394
|
+
const summary = getOpenUISummary(toolPart.state.output);
|
|
395
|
+
|
|
396
|
+
if (toolPart.state.status === "completed" && schema) {
|
|
397
|
+
return (
|
|
398
|
+
<div
|
|
399
|
+
key={key}
|
|
400
|
+
className="overflow-hidden rounded-[var(--radius-lg)] border border-[var(--border-subtle)] bg-[var(--md3-surface-container)]"
|
|
401
|
+
>
|
|
402
|
+
{summary ? (
|
|
403
|
+
<div className="border-b border-[var(--border-subtle)] px-4 py-3 text-sm leading-6 text-foreground">
|
|
404
|
+
{summary}
|
|
405
|
+
</div>
|
|
406
|
+
) : null}
|
|
407
|
+
<div className="p-4">
|
|
408
|
+
<OpenUIArtifactRenderer schema={schema} />
|
|
375
409
|
</div>
|
|
376
|
-
) : null}
|
|
377
|
-
<div className="p-4">
|
|
378
|
-
<OpenUIArtifactRenderer schema={schema} />
|
|
379
410
|
</div>
|
|
380
|
-
|
|
381
|
-
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (toolPart.state.status === "running") {
|
|
415
|
+
return (
|
|
416
|
+
<div
|
|
417
|
+
key={key}
|
|
418
|
+
className="flex items-center gap-2 rounded-[var(--radius-lg)] border border-[var(--border-subtle)] bg-[var(--md3-surface-container)] px-4 py-3 text-sm text-muted-foreground"
|
|
419
|
+
>
|
|
420
|
+
<Loader2 className="h-4 w-4 animate-spin text-primary" />
|
|
421
|
+
Building view…
|
|
422
|
+
</div>
|
|
423
|
+
);
|
|
424
|
+
}
|
|
382
425
|
}
|
|
383
426
|
|
|
384
|
-
if (
|
|
427
|
+
if (part.type === "text" && !part.synthetic && part.text.trim()) {
|
|
385
428
|
return (
|
|
386
|
-
<div
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
<Loader2 className="h-4 w-4 animate-spin text-primary" />
|
|
391
|
-
Building view…
|
|
429
|
+
<div key={key} className="px-0.5">
|
|
430
|
+
<Markdown className="tangle-prose text-[15px] leading-7 text-[var(--text-primary)]">
|
|
431
|
+
{part.text}
|
|
432
|
+
</Markdown>
|
|
392
433
|
</div>
|
|
393
434
|
);
|
|
394
435
|
}
|
|
395
|
-
}
|
|
396
436
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
</div>
|
|
404
|
-
);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
return null;
|
|
408
|
-
})}
|
|
409
|
-
</AssistantShell>
|
|
437
|
+
return null;
|
|
438
|
+
})}
|
|
439
|
+
</AssistantShell>
|
|
440
|
+
))}
|
|
441
|
+
{isStreaming && lastSourceSegment === -1 ? pendingResponse : null}
|
|
442
|
+
</div>
|
|
410
443
|
);
|
|
411
444
|
}
|
|
412
445
|
|
|
413
|
-
//
|
|
446
|
+
// Keep notes in source order; response statistics still count authored text only.
|
|
414
447
|
const rows = allParts.filter(({ part }) => {
|
|
415
448
|
if (part.type === "tool" || part.type === "reasoning") return true;
|
|
416
|
-
return part.type === "text" &&
|
|
449
|
+
return part.type === "text" && part.text.trim().length > 0;
|
|
417
450
|
});
|
|
418
451
|
|
|
419
452
|
const dotAccent = (part: SessionPart): string => {
|
|
420
453
|
if (part.type === "reasoning") return "bg-[var(--brand-glow)]";
|
|
421
|
-
if (part.type === "text") return "bg-primary";
|
|
454
|
+
if (part.type === "text" && !part.synthetic) return "bg-primary";
|
|
422
455
|
return "bg-[var(--border-hover)]";
|
|
423
456
|
};
|
|
424
457
|
|
|
@@ -532,8 +565,10 @@ export const RunGroup = memo(
|
|
|
532
565
|
node = (
|
|
533
566
|
<InlineThinkingItem part={part as ReasoningPart} defaultOpen={isStreaming} />
|
|
534
567
|
);
|
|
535
|
-
} else if (part.type === "text" &&
|
|
536
|
-
node = (
|
|
568
|
+
} else if (part.type === "text" && part.text.trim()) {
|
|
569
|
+
node = part.synthetic ? (
|
|
570
|
+
<SyntheticText text={part.text} />
|
|
571
|
+
) : (
|
|
537
572
|
<div className="px-1 py-0.5">
|
|
538
573
|
<Markdown className="tangle-prose text-[15px] leading-7">{part.text}</Markdown>
|
|
539
574
|
</div>
|