@tangle-network/ui 11.51.15 → 11.51.17
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 +12 -0
- package/dist/chat/thinking-indicator.js +9 -15
- package/dist/editor/tiptap-editor.js +4 -4
- package/dist/files/file-preview.js +1 -1
- package/dist/hooks/use-phone.d.ts +4 -0
- package/dist/hooks/use-phone.js +14 -0
- package/dist/hooks.d.ts +2 -1
- package/dist/hooks.js +3 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +5 -3
- package/dist/openui/openui-artifact-renderer.js +7 -7
- package/dist/presentation.d.ts +1 -1
- package/dist/presentation.js +1 -1
- package/dist/primitives/artifact-pane.js +1 -1
- package/dist/primitives/heading.js +1 -1
- package/dist/primitives/progress.js +1 -1
- package/dist/primitives/sidebar-drop-zone.js +1 -1
- package/dist/primitives/stat-card.js +1 -1
- package/dist/primitives/toast.js +1 -1
- package/dist/primitives/upload-progress.js +2 -2
- package/dist/primitives/wizard.js +2 -2
- package/dist/run/expandable-content.js +1 -1
- package/dist/run/expanded-tool-detail.js +3 -3
- package/dist/run/run-group.js +11 -14
- package/dist/run/run-item-primitives.d.ts +3 -0
- package/dist/run/run-item-primitives.js +12 -0
- package/dist/run/run-row-shell.d.ts +9 -0
- package/dist/run/run-row-shell.js +14 -1
- package/dist/run.d.ts +3 -2
- package/dist/run.js +3 -2
- package/dist/tool-previews/command-preview.js +3 -3
- package/dist/tool-previews/diff-preview.js +2 -2
- package/dist/tool-previews/question-preview.js +1 -1
- package/package.json +1 -1
- package/src/chat/thinking-indicator.tsx +2 -4
- package/src/editor/document-editor-pane.stories.tsx +1 -1
- package/src/editor/tiptap-editor.tsx +4 -4
- package/src/files/file-preview.tsx +1 -1
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-phone.ts +18 -0
- package/src/markdown/markdown.stories.tsx +1 -1
- package/src/openui/openui-artifact-renderer.tsx +7 -7
- package/src/presentation.ts +1 -1
- package/src/primitives/artifact-pane.tsx +1 -1
- package/src/primitives/avatar.stories.tsx +3 -3
- package/src/primitives/badge.stories.tsx +3 -3
- package/src/primitives/card.stories.tsx +1 -1
- package/src/primitives/compliance-badge.stories.tsx +3 -3
- package/src/primitives/dropdown-menu.stories.tsx +1 -1
- package/src/primitives/heading.tsx +1 -1
- package/src/primitives/input.stories.tsx +2 -2
- package/src/primitives/label.stories.tsx +3 -3
- package/src/primitives/progress.stories.tsx +5 -5
- package/src/primitives/progress.tsx +1 -1
- package/src/primitives/select.stories.tsx +3 -3
- package/src/primitives/sidebar-drop-zone.stories.tsx +1 -1
- package/src/primitives/sidebar-drop-zone.tsx +1 -1
- package/src/primitives/skeleton.stories.tsx +4 -4
- package/src/primitives/stat-card.stories.tsx +22 -33
- package/src/primitives/stat-card.tsx +1 -1
- package/src/primitives/status-pill.test.tsx +0 -52
- package/src/primitives/switch.stories.tsx +2 -2
- package/src/primitives/tabs.stories.tsx +3 -3
- package/src/primitives/toast.tsx +1 -1
- package/src/primitives/upload-progress.tsx +2 -2
- package/src/primitives/wizard.tsx +1 -1
- package/src/run/expandable-content.tsx +1 -1
- package/src/run/expanded-tool-detail.tsx +3 -3
- package/src/run/index.ts +2 -0
- package/src/run/inline-thinking-item.stories.tsx +3 -3
- package/src/run/inline-tool-item.stories.tsx +3 -3
- package/src/run/run-group.tsx +9 -9
- package/src/run/run-item-primitives.tsx +12 -0
- package/src/run/run-row-shell.tsx +16 -0
- package/src/styles/presentation.css +1 -1
- package/src/tool-previews/command-preview.tsx +3 -3
- package/src/tool-previews/diff-preview.tsx +2 -2
- package/src/tool-previews/question-preview.tsx +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
-
import {
|
|
2
|
+
import { Terminal } from 'lucide-react'
|
|
3
3
|
import { StatCard } from './stat-card'
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof StatCard> = {
|
|
@@ -13,7 +13,7 @@ type Story = StoryObj<typeof StatCard>
|
|
|
13
13
|
|
|
14
14
|
export const Default: Story = {
|
|
15
15
|
args: {
|
|
16
|
-
title: 'Active
|
|
16
|
+
title: 'Active sessions',
|
|
17
17
|
value: 24,
|
|
18
18
|
subtitle: 'across 3 regions',
|
|
19
19
|
icon: <Terminal className="h-5 w-5" />,
|
|
@@ -21,10 +21,10 @@ export const Default: Story = {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export const SandboxVariant: Story = {
|
|
24
|
-
name: 'Sandbox
|
|
24
|
+
name: 'Sandbox variant',
|
|
25
25
|
args: {
|
|
26
26
|
variant: 'sandbox',
|
|
27
|
-
title: 'Active
|
|
27
|
+
title: 'Active sessions',
|
|
28
28
|
value: 24,
|
|
29
29
|
subtitle: 'across 3 regions',
|
|
30
30
|
icon: <Terminal className="h-5 w-5" />,
|
|
@@ -33,103 +33,92 @@ export const SandboxVariant: Story = {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export const CpuUsage: Story = {
|
|
36
|
-
name: 'CPU
|
|
36
|
+
name: 'CPU usage',
|
|
37
37
|
args: {
|
|
38
|
-
title: 'Avg CPU
|
|
38
|
+
title: 'Avg CPU usage',
|
|
39
39
|
value: '68%',
|
|
40
40
|
subtitle: 'p95: 94%',
|
|
41
|
-
icon: <Cpu className="h-5 w-5" />,
|
|
42
41
|
trend: { value: -4, label: 'vs yesterday' },
|
|
43
42
|
},
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
export const MemoryUsage: Story = {
|
|
47
|
-
name: 'Memory
|
|
46
|
+
name: 'Memory usage',
|
|
48
47
|
args: {
|
|
49
|
-
title: 'Memory
|
|
48
|
+
title: 'Memory allocated',
|
|
50
49
|
value: '12.4 GB',
|
|
51
50
|
subtitle: 'of 32 GB pool',
|
|
52
|
-
icon: <HardDrive className="h-5 w-5" />,
|
|
53
51
|
trend: { value: 8, label: 'vs last hour' },
|
|
54
52
|
},
|
|
55
53
|
}
|
|
56
54
|
|
|
57
55
|
export const MonthlyCost: Story = {
|
|
58
|
-
name: 'Monthly
|
|
56
|
+
name: 'Monthly cost',
|
|
59
57
|
args: {
|
|
60
|
-
title: 'Estimated
|
|
58
|
+
title: 'Estimated cost',
|
|
61
59
|
value: '$142.80',
|
|
62
60
|
subtitle: 'current billing cycle',
|
|
63
|
-
icon: <DollarSign className="h-5 w-5" />,
|
|
64
61
|
trend: { value: 0, label: 'vs last month' },
|
|
65
62
|
},
|
|
66
63
|
}
|
|
67
64
|
|
|
68
65
|
export const TrendPositive: Story = {
|
|
69
|
-
name: 'Trend
|
|
66
|
+
name: 'Trend up',
|
|
70
67
|
args: {
|
|
71
|
-
title: 'Sessions
|
|
68
|
+
title: 'Sessions today',
|
|
72
69
|
value: 1284,
|
|
73
|
-
icon: <Activity className="h-5 w-5" />,
|
|
74
70
|
trend: { value: 23, label: 'vs yesterday' },
|
|
75
71
|
},
|
|
76
72
|
}
|
|
77
73
|
|
|
78
74
|
export const TrendNegative: Story = {
|
|
79
|
-
name: 'Trend
|
|
75
|
+
name: 'Trend down',
|
|
80
76
|
args: {
|
|
81
|
-
title: 'Error
|
|
77
|
+
title: 'Error rate',
|
|
82
78
|
value: '1.2%',
|
|
83
|
-
icon: <Zap className="h-5 w-5" />,
|
|
84
79
|
trend: { value: -18, label: 'vs last week' },
|
|
85
80
|
},
|
|
86
81
|
}
|
|
87
82
|
|
|
88
83
|
export const InfraGrid: Story = {
|
|
89
|
-
name: 'Infrastructure
|
|
84
|
+
name: 'Infrastructure grid',
|
|
90
85
|
render: () => (
|
|
91
86
|
<div className="grid grid-cols-2 gap-4 w-[640px]">
|
|
92
87
|
<StatCard
|
|
93
|
-
title="Active
|
|
88
|
+
title="Active sessions"
|
|
94
89
|
value={24}
|
|
95
90
|
subtitle="across 3 regions"
|
|
96
|
-
icon={<Terminal className="h-5 w-5" />}
|
|
97
91
|
trend={{ value: 12, label: 'vs last hour' }}
|
|
98
92
|
/>
|
|
99
93
|
<StatCard
|
|
100
|
-
title="CPU
|
|
94
|
+
title="CPU usage"
|
|
101
95
|
value="68%"
|
|
102
96
|
subtitle="p95: 94%"
|
|
103
|
-
icon={<Cpu className="h-5 w-5" />}
|
|
104
97
|
trend={{ value: -4, label: 'vs yesterday' }}
|
|
105
98
|
/>
|
|
106
99
|
<StatCard
|
|
107
|
-
title="Memory
|
|
100
|
+
title="Memory allocated"
|
|
108
101
|
value="12.4 GB"
|
|
109
102
|
subtitle="of 32 GB pool"
|
|
110
|
-
icon={<HardDrive className="h-5 w-5" />}
|
|
111
103
|
trend={{ value: 8, label: 'vs last hour' }}
|
|
112
104
|
/>
|
|
113
105
|
<StatCard
|
|
114
106
|
variant="sandbox"
|
|
115
|
-
title="Estimated
|
|
107
|
+
title="Estimated cost"
|
|
116
108
|
value="$142.80"
|
|
117
109
|
subtitle="current billing cycle"
|
|
118
|
-
icon={<DollarSign className="h-5 w-5" />}
|
|
119
110
|
trend={{ value: 3, label: 'vs last month' }}
|
|
120
111
|
/>
|
|
121
112
|
<StatCard
|
|
122
|
-
title="Unique
|
|
113
|
+
title="Unique users"
|
|
123
114
|
value={312}
|
|
124
115
|
subtitle="last 30 days"
|
|
125
|
-
|
|
126
|
-
trend={{ value: 19, label: 'MoM' }}
|
|
116
|
+
trend={{ value: 19, label: 'vs last month' }}
|
|
127
117
|
/>
|
|
128
118
|
<StatCard
|
|
129
|
-
title="Avg
|
|
119
|
+
title="Avg startup time"
|
|
130
120
|
value="840ms"
|
|
131
121
|
subtitle="p99: 1.4s"
|
|
132
|
-
icon={<Zap className="h-5 w-5" />}
|
|
133
122
|
trend={{ value: -11, label: 'vs last week' }}
|
|
134
123
|
/>
|
|
135
124
|
</div>
|
|
@@ -57,7 +57,7 @@ const StatCard = React.forwardRef<HTMLDivElement, StatCardProps>(
|
|
|
57
57
|
<div className="flex items-start justify-between">
|
|
58
58
|
<div className="space-y-1">
|
|
59
59
|
<p className="text-muted-foreground text-sm">{title}</p>
|
|
60
|
-
<p className="font-
|
|
60
|
+
<p className="font-medium text-3xl tabular-nums tracking-tight">{value}</p>
|
|
61
61
|
{/* A token, never `opacity-70` over the muted tone: a translucent
|
|
62
62
|
foreground renders as the token COMPOSITED over whatever plane sits
|
|
63
63
|
behind it, so the same class lands a different colour — and a
|
|
@@ -28,31 +28,6 @@ describe("StatusPill", () => {
|
|
|
28
28
|
expect(glyph).toHaveAttribute("aria-hidden", "true");
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
it.each(TONES)("brings %s's own paired background", (tone) => {
|
|
32
|
-
const { container } = render(<StatusPill tone={tone}>Status</StatusPill>);
|
|
33
|
-
const pill = container.firstElementChild as HTMLElement;
|
|
34
|
-
expect(pill.className).toContain(`bg-[var(--surface-`);
|
|
35
|
-
expect(pill.className).toContain("border");
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// A tone colour is solved against its own background. `bare` drops that
|
|
39
|
-
// background, so the tone may ride on the glyph only — lifting it onto the
|
|
40
|
-
// label would put it on whatever plane the caller happens to be on.
|
|
41
|
-
it.each(TONES)("keeps %s's tone off the label in bare mode", (tone) => {
|
|
42
|
-
const { container } = render(
|
|
43
|
-
<StatusPill bare tone={tone}>
|
|
44
|
-
Status
|
|
45
|
-
</StatusPill>,
|
|
46
|
-
);
|
|
47
|
-
const pill = container.firstElementChild as HTMLElement;
|
|
48
|
-
expect(pill.className).toContain("bg-transparent");
|
|
49
|
-
expect(pill.className).not.toContain("bg-[var(--surface-");
|
|
50
|
-
|
|
51
|
-
const glyphWrapper = container.querySelector("svg")
|
|
52
|
-
?.parentElement as HTMLElement;
|
|
53
|
-
expect(glyphWrapper.className).toContain("text-[var(--surface-");
|
|
54
|
-
});
|
|
55
|
-
|
|
56
31
|
// Guards the reason TONE_TEXT is declared rather than recovered by searching
|
|
57
32
|
// TONE_SURFACE for a `text-` class: a miss there yields undefined and removes
|
|
58
33
|
// the tone from the only channel bare mode has left.
|
|
@@ -68,22 +43,6 @@ describe("StatusPill", () => {
|
|
|
68
43
|
expect(glyphWrapper.className).toMatch(/text-\[var\(--surface-[a-z]+-text\)\]/);
|
|
69
44
|
});
|
|
70
45
|
|
|
71
|
-
// `px-2` from the size and `px-0` from bare are the same utility group, so
|
|
72
|
-
// which one lands is decided by the class merge rather than by reading order.
|
|
73
|
-
// Asserting the resolved class list keeps that a guarantee: a bare pill has to
|
|
74
|
-
// sit flush against the text beside it, and silently keeping the size padding
|
|
75
|
-
// would space it like a chip that lost only its background.
|
|
76
|
-
it.each(["sm", "md"] as const)("drops the %s size padding in bare mode", (size) => {
|
|
77
|
-
const { container } = render(
|
|
78
|
-
<StatusPill bare size={size} tone="info">
|
|
79
|
-
Queued
|
|
80
|
-
</StatusPill>,
|
|
81
|
-
);
|
|
82
|
-
const cls = (container.firstElementChild as HTMLElement).className;
|
|
83
|
-
expect(cls).toContain("px-0");
|
|
84
|
-
expect(cls).not.toMatch(/(^| )px-2(\.5)?( |$)/);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
46
|
it("gives no two tones the same silhouette", () => {
|
|
88
47
|
const shapes = TONES.map((tone) => {
|
|
89
48
|
const { container } = render(<StatusPill tone={tone}>S</StatusPill>);
|
|
@@ -91,15 +50,4 @@ describe("StatusPill", () => {
|
|
|
91
50
|
});
|
|
92
51
|
expect(new Set(shapes).size).toBe(shapes.length);
|
|
93
52
|
});
|
|
94
|
-
|
|
95
|
-
it("takes a larger size", () => {
|
|
96
|
-
const { container } = render(
|
|
97
|
-
<StatusPill size="md" tone="info">
|
|
98
|
-
Queued
|
|
99
|
-
</StatusPill>,
|
|
100
|
-
);
|
|
101
|
-
expect((container.firstElementChild as HTMLElement).className).toContain(
|
|
102
|
-
"text-sm",
|
|
103
|
-
);
|
|
104
|
-
});
|
|
105
53
|
});
|
|
@@ -41,7 +41,7 @@ export const Overview: Story = {
|
|
|
41
41
|
name: 'Overview',
|
|
42
42
|
render: () => (
|
|
43
43
|
<div className="flex flex-col gap-6 p-6 w-80">
|
|
44
|
-
<div className="text-muted-foreground text-xs font-mono
|
|
44
|
+
<div className="text-muted-foreground text-xs font-mono">States</div>
|
|
45
45
|
<div className="flex flex-col gap-4">
|
|
46
46
|
<div className="flex items-center justify-between">
|
|
47
47
|
<span className="text-sm text-muted-foreground">Off</span>
|
|
@@ -61,7 +61,7 @@ export const Overview: Story = {
|
|
|
61
61
|
</div>
|
|
62
62
|
</div>
|
|
63
63
|
|
|
64
|
-
<div className="text-muted-foreground text-xs font-mono
|
|
64
|
+
<div className="text-muted-foreground text-xs font-mono mt-2">Settings panel</div>
|
|
65
65
|
<div className="rounded-xl border border-border bg-card p-4 flex flex-col gap-4">
|
|
66
66
|
{[
|
|
67
67
|
{ id: 'auto-sleep', label: 'Auto-sleep after idle', checked: true },
|
|
@@ -104,7 +104,7 @@ export const Overview: Story = {
|
|
|
104
104
|
render: () => (
|
|
105
105
|
<div className="flex flex-col gap-10 p-6 w-[480px]">
|
|
106
106
|
<div>
|
|
107
|
-
<div className="text-muted-foreground text-xs font-mono
|
|
107
|
+
<div className="text-muted-foreground text-xs font-mono mb-4">Default (segmented)</div>
|
|
108
108
|
<Tabs defaultValue="overview">
|
|
109
109
|
<TabsList>
|
|
110
110
|
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
@@ -137,7 +137,7 @@ export const Overview: Story = {
|
|
|
137
137
|
</div>
|
|
138
138
|
|
|
139
139
|
<div>
|
|
140
|
-
<div className="text-muted-foreground text-xs font-mono
|
|
140
|
+
<div className="text-muted-foreground text-xs font-mono mb-4">Pills</div>
|
|
141
141
|
<Tabs defaultValue="all">
|
|
142
142
|
<TabsList variant="pills">
|
|
143
143
|
<TabsTrigger value="all" variant="pills">All</TabsTrigger>
|
|
@@ -157,7 +157,7 @@ export const Overview: Story = {
|
|
|
157
157
|
</div>
|
|
158
158
|
|
|
159
159
|
<div>
|
|
160
|
-
<div className="text-muted-foreground text-xs font-mono
|
|
160
|
+
<div className="text-muted-foreground text-xs font-mono mb-4">Underline</div>
|
|
161
161
|
<Tabs defaultValue="settings">
|
|
162
162
|
<TabsList variant="underline">
|
|
163
163
|
<TabsTrigger value="settings" variant="underline">Settings</TabsTrigger>
|
package/src/primitives/toast.tsx
CHANGED
|
@@ -13,7 +13,7 @@ import { focusRing } from "../lib/focus";
|
|
|
13
13
|
import { cn } from "../lib/utils";
|
|
14
14
|
|
|
15
15
|
const toastVariants = cva(
|
|
16
|
-
"pointer-events-auto relative flex w-full items-center justify-between gap-3 overflow-hidden rounded-lg border p-4 shadow-lg transition-
|
|
16
|
+
"pointer-events-auto relative flex w-full items-center justify-between gap-3 overflow-hidden rounded-lg border p-4 shadow-lg transition-[transform,opacity]",
|
|
17
17
|
{
|
|
18
18
|
variants: {
|
|
19
19
|
variant: {
|
|
@@ -67,8 +67,8 @@ export function UploadProgress({ files, onRemove, onRetry, className }: UploadPr
|
|
|
67
67
|
{file.status === "uploading" && file.progress !== undefined && (
|
|
68
68
|
<div className="mt-1 h-1 w-full overflow-hidden rounded-full bg-muted/50">
|
|
69
69
|
<div
|
|
70
|
-
className="h-full rounded-full bg-primary transition-
|
|
71
|
-
style={{
|
|
70
|
+
className="h-full w-full rounded-full bg-primary transition-transform duration-300 ease-out"
|
|
71
|
+
style={{ transform: `translateX(-${100 - file.progress}%)` }}
|
|
72
72
|
/>
|
|
73
73
|
</div>
|
|
74
74
|
)}
|
|
@@ -137,7 +137,7 @@ export function Wizard({ children, step, defaultStep = 0, onStepChange, onFinish
|
|
|
137
137
|
</p>
|
|
138
138
|
<div className="mt-2 h-1 overflow-hidden rounded-full bg-muted" role="progressbar"
|
|
139
139
|
aria-valuemin={1} aria-valuemax={total} aria-valuenow={current + 1} aria-label={`Step ${current + 1} of ${total}`}>
|
|
140
|
-
<div className="h-full rounded-full bg-primary transition-
|
|
140
|
+
<div className="h-full w-full rounded-full bg-primary transition-transform duration-300 ease-out" style={{ transform: `translateX(-${100 - ((current + 1) / total) * 100}%)` }} />
|
|
141
141
|
</div>
|
|
142
142
|
</div>
|
|
143
143
|
{steps.map((child, index) => index === current
|
|
@@ -68,7 +68,7 @@ export function ExpandableContent({
|
|
|
68
68
|
type="button"
|
|
69
69
|
onClick={() => setExpanded((value) => !value)}
|
|
70
70
|
className={cn(
|
|
71
|
-
"mt-1 text-
|
|
71
|
+
"mt-1 text-xs font-medium text-muted-foreground transition-colors hover:text-foreground",
|
|
72
72
|
toggleClassName,
|
|
73
73
|
)}
|
|
74
74
|
>
|
|
@@ -128,7 +128,7 @@ export const ExpandedToolDetail = memo(({ part }: ExpandedToolDetailProps) => {
|
|
|
128
128
|
<div className="overflow-hidden rounded-[var(--radius-lg)] border border-border bg-card">
|
|
129
129
|
<div className="flex items-center gap-2 border-b border-border bg-background px-3 py-2">
|
|
130
130
|
<ArrowRight className="h-3 w-3 text-primary" />
|
|
131
|
-
<span className="text-xs font-medium
|
|
131
|
+
<span className="text-xs font-medium text-muted-foreground">
|
|
132
132
|
Input
|
|
133
133
|
</span>
|
|
134
134
|
</div>
|
|
@@ -141,7 +141,7 @@ export const ExpandedToolDetail = memo(({ part }: ExpandedToolDetailProps) => {
|
|
|
141
141
|
<div className="overflow-hidden rounded-[var(--radius-lg)] border border-border bg-card">
|
|
142
142
|
<div className="flex items-center gap-2 border-b border-border bg-background px-3 py-2">
|
|
143
143
|
<ArrowLeft className="h-3 w-3 text-primary" />
|
|
144
|
-
<span className="text-xs font-medium
|
|
144
|
+
<span className="text-xs font-medium text-muted-foreground">
|
|
145
145
|
Output
|
|
146
146
|
</span>
|
|
147
147
|
</div>
|
|
@@ -164,7 +164,7 @@ export const ExpandedToolDetail = memo(({ part }: ExpandedToolDetailProps) => {
|
|
|
164
164
|
<div className="overflow-hidden rounded-[var(--radius-lg)] border border-[var(--surface-danger-border)]">
|
|
165
165
|
<div className="flex items-center gap-2 border-b border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] px-3 py-2">
|
|
166
166
|
<RunStatusIcon status="failed" aria-hidden className="size-3" />
|
|
167
|
-
<span className="text-xs font-medium
|
|
167
|
+
<span className="text-xs font-medium text-[var(--surface-danger-text)]">
|
|
168
168
|
Error
|
|
169
169
|
</span>
|
|
170
170
|
</div>
|
package/src/run/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { RunGroup, type RunGroupProps } from "./run-group";
|
|
2
2
|
export {
|
|
3
3
|
RunRowShell,
|
|
4
|
+
RunRowStatusDot,
|
|
4
5
|
type RunRowShellProps,
|
|
5
6
|
type RunRowStatus,
|
|
6
7
|
} from "./run-row-shell";
|
|
@@ -13,6 +14,7 @@ export {
|
|
|
13
14
|
ExpandedToolDetail,
|
|
14
15
|
type ExpandedToolDetailProps,
|
|
15
16
|
} from "./expanded-tool-detail";
|
|
17
|
+
export { LiveDuration } from "./run-item-primitives";
|
|
16
18
|
// ToolCallStep/ToolCallGroup are internal adapters over InlineToolItem (used by
|
|
17
19
|
// AgentTimeline + ToolCallFeed); only their status/type vocabulary is public
|
|
18
20
|
// because ToolCallData references it.
|
|
@@ -154,11 +154,11 @@ export const UnifiedWithToolRow: Story = {
|
|
|
154
154
|
export const AllStates: Story = {
|
|
155
155
|
render: () => (
|
|
156
156
|
<div className="p-6 max-w-2xl space-y-3">
|
|
157
|
-
<p className="text-xs font-mono
|
|
157
|
+
<p className="text-xs font-mono text-muted-foreground px-1">Active</p>
|
|
158
158
|
<InlineThinkingItem part={thinkingActive} defaultOpen={true} autoCollapse={false} />
|
|
159
|
-
<p className="text-xs font-mono
|
|
159
|
+
<p className="text-xs font-mono text-muted-foreground px-1 pt-2">Complete (collapsed)</p>
|
|
160
160
|
<InlineThinkingItem part={thinkingComplete} defaultOpen={false} autoCollapse={false} />
|
|
161
|
-
<p className="text-xs font-mono
|
|
161
|
+
<p className="text-xs font-mono text-muted-foreground px-1 pt-2">Complete (expanded)</p>
|
|
162
162
|
<InlineThinkingItem part={thinkingLong} defaultOpen={true} autoCollapse={false} />
|
|
163
163
|
</div>
|
|
164
164
|
),
|
|
@@ -211,11 +211,11 @@ export const GroupedSequence: Story = {
|
|
|
211
211
|
export const AllStates: Story = {
|
|
212
212
|
render: () => (
|
|
213
213
|
<div className="p-6 max-w-2xl space-y-3">
|
|
214
|
-
<p className="text-xs font-mono
|
|
214
|
+
<p className="text-xs font-mono text-muted-foreground px-1">Completed</p>
|
|
215
215
|
<InlineToolItem part={bashComplete} groupPosition="single" />
|
|
216
|
-
<p className="text-xs font-mono
|
|
216
|
+
<p className="text-xs font-mono text-muted-foreground px-1 pt-2">Running</p>
|
|
217
217
|
<InlineToolItem part={bashRunning} groupPosition="single" />
|
|
218
|
-
<p className="text-xs font-mono
|
|
218
|
+
<p className="text-xs font-mono text-muted-foreground px-1 pt-2">Error</p>
|
|
219
219
|
<InlineToolItem part={bashError} groupPosition="single" />
|
|
220
220
|
</div>
|
|
221
221
|
),
|
package/src/run/run-group.tsx
CHANGED
|
@@ -80,7 +80,7 @@ const DEFAULT_BRANDING: AgentBranding = {
|
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
const ASSISTANT_SHELL =
|
|
83
|
-
"min-w-0 flex-1 space-y-3 rounded-[
|
|
83
|
+
"min-w-0 flex-1 space-y-3 rounded-[var(--radius-panel)] border border-[var(--border-subtle)] bg-[color:color-mix(in_srgb,var(--bg-card)_94%,transparent)] px-5 py-4 shadow-[0_1px_2px_rgba(15,23,42,0.04)]";
|
|
84
84
|
|
|
85
85
|
function AssistantShell({
|
|
86
86
|
branding,
|
|
@@ -251,7 +251,7 @@ function CategoryBadges({ categories }: { categories: Set<ToolCategory> }) {
|
|
|
251
251
|
<span
|
|
252
252
|
key={cat}
|
|
253
253
|
title={cat}
|
|
254
|
-
className="flex h-5 w-5 items-center justify-center
|
|
254
|
+
className="flex h-5 w-5 items-center justify-center text-muted-foreground"
|
|
255
255
|
>
|
|
256
256
|
<Icon className="h-3 w-3" />
|
|
257
257
|
</span>
|
|
@@ -326,10 +326,10 @@ export const RunGroup = memo(
|
|
|
326
326
|
const pendingResponse = (
|
|
327
327
|
<AssistantShell branding={branding} isStreaming={true}>
|
|
328
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-
|
|
330
|
-
<span className="h-2 w-2 animate-
|
|
331
|
-
<span className="h-2 w-2 animate-
|
|
332
|
-
<span className="h-2 w-2 animate-
|
|
329
|
+
<span className="flex gap-1">
|
|
330
|
+
<span className="h-2 w-2 animate-[tangle-breathe_var(--motion-ambient)_infinite] motion-reduce:animate-none rounded-full bg-[var(--brand-glow)]" />
|
|
331
|
+
<span className="h-2 w-2 animate-[tangle-breathe_var(--motion-ambient)_infinite] motion-reduce:animate-none rounded-full bg-[var(--brand-glow)]" style={{ animationDelay: "200ms" }} />
|
|
332
|
+
<span className="h-2 w-2 animate-[tangle-breathe_var(--motion-ambient)_infinite] motion-reduce:animate-none rounded-full bg-[var(--brand-glow)]" style={{ animationDelay: "400ms" }} />
|
|
333
333
|
</span>
|
|
334
334
|
</div>
|
|
335
335
|
</AssistantShell>
|
|
@@ -474,16 +474,16 @@ export const RunGroup = memo(
|
|
|
474
474
|
{branding.label}
|
|
475
475
|
</span>
|
|
476
476
|
{renderSummary(run) ? (
|
|
477
|
-
<span className="text-
|
|
477
|
+
<span className="text-xs text-muted-foreground">{renderSummary(run)}</span>
|
|
478
478
|
) : null}
|
|
479
479
|
{collapsed && run.summaryText ? (
|
|
480
|
-
<span className="min-w-0 truncate text-
|
|
480
|
+
<span className="min-w-0 truncate text-xs text-foreground/70">
|
|
481
481
|
{run.summaryText}
|
|
482
482
|
</span>
|
|
483
483
|
) : null}
|
|
484
484
|
<span className="ml-auto flex shrink-0 items-center gap-1.5">
|
|
485
485
|
<CategoryBadges categories={stats.toolCategories} />
|
|
486
|
-
<span className="inline-flex items-center gap-1
|
|
486
|
+
<span className="inline-flex items-center gap-1 text-xs text-muted-foreground">
|
|
487
487
|
<RunStatusIcon status={isStreaming ? "running" : "done"} aria-hidden className="size-3" />
|
|
488
488
|
{statusMeta(isStreaming ? "running" : "done").label}
|
|
489
489
|
</span>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useLiveTime } from "../hooks/use-live-time";
|
|
2
|
+
import { formatDuration } from "../utils/format";
|
|
3
|
+
|
|
4
|
+
export function LiveDuration({ startTime }: { startTime: number }) {
|
|
5
|
+
const elapsed = useLiveTime(100) - startTime;
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<span className="text-xs font-mono text-muted-foreground tabular-nums">
|
|
9
|
+
{formatDuration(elapsed)}
|
|
10
|
+
</span>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
@@ -7,6 +7,22 @@ import { formatDuration } from "../utils/format";
|
|
|
7
7
|
|
|
8
8
|
export type RunRowStatus = "running" | "success" | "error" | "idle";
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* A red dot for a failed row, for rows composed outside the shell. The shell
|
|
12
|
+
* itself marks a failure on the row's own glyph, beside its name, never at the
|
|
13
|
+
* far edge. Success is silence, and a running row carries its state in the
|
|
14
|
+
* title — a `TextShimmer` sweep.
|
|
15
|
+
*/
|
|
16
|
+
export function RunRowStatusDot({ status }: { status: RunRowStatus }) {
|
|
17
|
+
if (status !== "error") return null;
|
|
18
|
+
return (
|
|
19
|
+
<span
|
|
20
|
+
data-run-row-status="error"
|
|
21
|
+
className="h-1.5 w-1.5 shrink-0 rounded-full bg-[var(--surface-danger-text)]"
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
10
26
|
export interface RunRowShellProps {
|
|
11
27
|
/** Semantic lead glyph (14px). At rest it is the row's only ornament; on
|
|
12
28
|
* hover and while open the expand chevron takes its slot. */
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
.tangle-heading[data-variant="page"] { font-size: var(--font-size-3xl); }
|
|
6
6
|
.tangle-heading[data-variant="section"] { font-size: var(--font-size-xl); }
|
|
7
7
|
.tangle-heading[data-variant="subsection"] { font-size: var(--font-size-base); }
|
|
8
|
-
.tangle-heading[data-variant="eyebrow"] { font-size: var(--font-size-xs);
|
|
8
|
+
.tangle-heading[data-variant="eyebrow"] { font-size: var(--font-size-xs); line-height: 1rem; font-weight: 500; letter-spacing: 0; color: hsl(var(--muted-foreground)); }
|
|
9
9
|
.tangle-page-header { min-width: 0; margin-bottom: 1.5rem; }
|
|
10
10
|
.tangle-page-header-main { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 1.5rem; }
|
|
11
11
|
.tangle-intro .tangle-page-header-main { display: block; }
|
|
@@ -139,7 +139,7 @@ function ExitBadge({
|
|
|
139
139
|
}) {
|
|
140
140
|
if (status === "running" || status === "pending") {
|
|
141
141
|
return (
|
|
142
|
-
<span className="inline-flex shrink-0 items-center gap-1 text-
|
|
142
|
+
<span className="inline-flex shrink-0 items-center gap-1 text-xs text-muted-foreground">
|
|
143
143
|
<Loader2 className="h-3 w-3 animate-spin" aria-hidden="true" />
|
|
144
144
|
running
|
|
145
145
|
</span>
|
|
@@ -171,7 +171,7 @@ function ExitBadge({
|
|
|
171
171
|
<span
|
|
172
172
|
className={cn(
|
|
173
173
|
// Every badge has a border, so mixed rows keep one height.
|
|
174
|
-
"inline-flex shrink-0 items-center rounded-full border px-1.5 py-px font-sans text-
|
|
174
|
+
"inline-flex shrink-0 items-center rounded-full border px-1.5 py-px font-sans text-xs font-medium tabular-nums",
|
|
175
175
|
tone === "danger" &&
|
|
176
176
|
"border-transparent bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]",
|
|
177
177
|
tone === "success" &&
|
|
@@ -253,7 +253,7 @@ function OutputRegion({
|
|
|
253
253
|
type="button"
|
|
254
254
|
onClick={() => setFull(true)}
|
|
255
255
|
className={cn(
|
|
256
|
-
"w-full border-t border-white/10 px-3 py-1.5 text-left font-sans text-
|
|
256
|
+
"w-full border-t border-white/10 px-3 py-1.5 text-left font-sans text-xs font-medium text-muted-foreground transition-colors hover:text-foreground",
|
|
257
257
|
focusRing,
|
|
258
258
|
)}
|
|
259
259
|
>
|
|
@@ -64,7 +64,7 @@ export const DiffPreview = memo(({ part }: DiffPreviewProps) => {
|
|
|
64
64
|
) : null}
|
|
65
65
|
{!payload.diff && payload.before ? (
|
|
66
66
|
<div className="space-y-2">
|
|
67
|
-
<div className="text-xs font-
|
|
67
|
+
<div className="text-xs font-medium text-muted-foreground">
|
|
68
68
|
Before
|
|
69
69
|
</div>
|
|
70
70
|
<CodeBlock code={payload.before} language="text" className="rounded-[var(--radius-md)]" />
|
|
@@ -72,7 +72,7 @@ export const DiffPreview = memo(({ part }: DiffPreviewProps) => {
|
|
|
72
72
|
) : null}
|
|
73
73
|
{!payload.diff && payload.after ? (
|
|
74
74
|
<div className="space-y-2">
|
|
75
|
-
<div className="text-xs font-
|
|
75
|
+
<div className="text-xs font-medium text-muted-foreground">
|
|
76
76
|
After
|
|
77
77
|
</div>
|
|
78
78
|
<CodeBlock code={payload.after} language="text" className="rounded-[var(--radius-md)]" />
|
|
@@ -83,7 +83,7 @@ export const QuestionPreview = memo(({ part }: QuestionPreviewProps) => {
|
|
|
83
83
|
key={`${question}-${index}`}
|
|
84
84
|
className="rounded-[var(--radius-md)] border border-border bg-muted px-3 py-3"
|
|
85
85
|
>
|
|
86
|
-
<div className="text-xs font-
|
|
86
|
+
<div className="text-xs font-medium text-muted-foreground">
|
|
87
87
|
Question {index + 1}
|
|
88
88
|
</div>
|
|
89
89
|
<div className="mt-2 text-sm leading-relaxed text-foreground">
|