@tangle-network/ui 1.0.0
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/LICENSE +21 -0
- package/README.md +33 -0
- package/dist/active-sessions-store-CeOmXgv5.d.ts +85 -0
- package/dist/artifact-pane-DvJyPWV4.d.ts +24 -0
- package/dist/auth.d.ts +74 -0
- package/dist/auth.js +15 -0
- package/dist/button-CMQuQEW_.d.ts +17 -0
- package/dist/chat.d.ts +232 -0
- package/dist/chat.js +30 -0
- package/dist/chunk-2NFQRQOD.js +1009 -0
- package/dist/chunk-2VH6PUXD.js +186 -0
- package/dist/chunk-34A66VBG.js +214 -0
- package/dist/chunk-3OI2QKFD.js +0 -0
- package/dist/chunk-4CLN43XT.js +45 -0
- package/dist/chunk-54SQQMMM.js +156 -0
- package/dist/chunk-5Z5ZYMOJ.js +0 -0
- package/dist/chunk-66BNMOVT.js +167 -0
- package/dist/chunk-6BGQA4BQ.js +0 -0
- package/dist/chunk-7UO2ZMRQ.js +133 -0
- package/dist/chunk-BX6AQMUS.js +183 -0
- package/dist/chunk-CD53GZOM.js +59 -0
- package/dist/chunk-CSAIKY36.js +54 -0
- package/dist/chunk-EEE55AVS.js +1201 -0
- package/dist/chunk-GYPQXTJU.js +230 -0
- package/dist/chunk-HFL6R6IF.js +37 -0
- package/dist/chunk-HJKCSXCH.js +737 -0
- package/dist/chunk-LISXUB4D.js +1222 -0
- package/dist/chunk-LQS34IGP.js +0 -0
- package/dist/chunk-MKTSMWVD.js +109 -0
- package/dist/chunk-NKDZ7GZE.js +192 -0
- package/dist/chunk-OEX7NZE3.js +321 -0
- package/dist/chunk-Q56BYXQF.js +61 -0
- package/dist/chunk-Q7EIIWTC.js +0 -0
- package/dist/chunk-REJESC5U.js +117 -0
- package/dist/chunk-RQGKSCEZ.js +0 -0
- package/dist/chunk-RQHJBTEU.js +10 -0
- package/dist/chunk-TMFOPHHN.js +299 -0
- package/dist/chunk-XGKULLYE.js +40 -0
- package/dist/chunk-XIHMJ7ZQ.js +614 -0
- package/dist/chunk-YJ2G3XO5.js +1048 -0
- package/dist/chunk-YNN4O57I.js +754 -0
- package/dist/code-block-DjXf8eOG.d.ts +19 -0
- package/dist/document-editor-pane-A5LT5H4N.js +12 -0
- package/dist/document-editor-pane-DyDEX_Zm.d.ts +124 -0
- package/dist/editor.d.ts +120 -0
- package/dist/editor.js +34 -0
- package/dist/files.d.ts +175 -0
- package/dist/files.js +20 -0
- package/dist/hooks.d.ts +56 -0
- package/dist/hooks.js +41 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +446 -0
- package/dist/markdown.d.ts +15 -0
- package/dist/markdown.js +14 -0
- package/dist/message-BHWbxBtT.d.ts +15 -0
- package/dist/openui.d.ts +115 -0
- package/dist/openui.js +12 -0
- package/dist/parts-dj7AcUg0.d.ts +36 -0
- package/dist/primitives.d.ts +332 -0
- package/dist/primitives.js +191 -0
- package/dist/run-PfLmDAox.d.ts +41 -0
- package/dist/run.d.ts +69 -0
- package/dist/run.js +36 -0
- package/dist/sdk-hooks.d.ts +285 -0
- package/dist/sdk-hooks.js +31 -0
- package/dist/stores.d.ts +17 -0
- package/dist/stores.js +76 -0
- package/dist/tool-call-feed-Bs3MyQMT.d.ts +68 -0
- package/dist/tool-display-z4JcDmMQ.d.ts +32 -0
- package/dist/tool-previews.d.ts +48 -0
- package/dist/tool-previews.js +21 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +45 -0
- package/dist/utils.js +32 -0
- package/package.json +193 -0
- package/src/auth/auth.tsx +228 -0
- package/src/auth/index.ts +13 -0
- package/src/auth/login-layout.tsx +46 -0
- package/src/chat/agent-timeline.stories.tsx +429 -0
- package/src/chat/agent-timeline.tsx +360 -0
- package/src/chat/chat-container.tsx +486 -0
- package/src/chat/chat-input.stories.tsx +142 -0
- package/src/chat/chat-input.tsx +389 -0
- package/src/chat/chat-message.stories.tsx +237 -0
- package/src/chat/chat-message.tsx +129 -0
- package/src/chat/index.ts +18 -0
- package/src/chat/message-list.stories.tsx +336 -0
- package/src/chat/message-list.tsx +79 -0
- package/src/chat/thinking-indicator.stories.tsx +56 -0
- package/src/chat/thinking-indicator.tsx +30 -0
- package/src/chat/user-message.stories.tsx +92 -0
- package/src/chat/user-message.tsx +43 -0
- package/src/editor/document-editor-pane.tsx +351 -0
- package/src/editor/editor-provider.tsx +428 -0
- package/src/editor/editor-toolbar.tsx +130 -0
- package/src/editor/index.ts +31 -0
- package/src/editor/markdown-conversion.ts +21 -0
- package/src/editor/markdown-document-editor.tsx +137 -0
- package/src/editor/tiptap-editor.tsx +331 -0
- package/src/editor/use-editor.ts +221 -0
- package/src/files/file-artifact-pane.tsx +183 -0
- package/src/files/file-preview.tsx +342 -0
- package/src/files/file-tabs.tsx +71 -0
- package/src/files/file-tree.tsx +258 -0
- package/src/files/index.ts +17 -0
- package/src/files/rich-file-tree.stories.tsx +104 -0
- package/src/files/rich-file-tree.test.tsx +42 -0
- package/src/files/rich-file-tree.tsx +232 -0
- package/src/hooks/index.ts +10 -0
- package/src/hooks/use-auth.ts +153 -0
- package/src/hooks/use-auto-scroll.ts +59 -0
- package/src/hooks/use-dropdown-menu.ts +40 -0
- package/src/hooks/use-live-time.test.tsx +40 -0
- package/src/hooks/use-live-time.ts +27 -0
- package/src/hooks/use-realtime-session.ts +319 -0
- package/src/hooks/use-run-collapse-state.ts +25 -0
- package/src/hooks/use-run-groups.ts +111 -0
- package/src/hooks/use-sdk-session.ts +575 -0
- package/src/hooks/use-sse-stream.ts +475 -0
- package/src/hooks/use-tool-call-stream.ts +96 -0
- package/src/index.ts +14 -0
- package/src/lib/utils.ts +6 -0
- package/src/markdown/code-block.tsx +198 -0
- package/src/markdown/index.ts +2 -0
- package/src/markdown/markdown.stories.tsx +190 -0
- package/src/markdown/markdown.tsx +62 -0
- package/src/openui/index.ts +20 -0
- package/src/openui/openui-artifact-renderer.tsx +542 -0
- package/src/primitives/artifact-pane.tsx +91 -0
- package/src/primitives/avatar.stories.tsx +95 -0
- package/src/primitives/avatar.tsx +47 -0
- package/src/primitives/badge.stories.tsx +57 -0
- package/src/primitives/badge.tsx +97 -0
- package/src/primitives/button.stories.tsx +48 -0
- package/src/primitives/button.tsx +115 -0
- package/src/primitives/card.stories.tsx +53 -0
- package/src/primitives/card.tsx +98 -0
- package/src/primitives/code-block.stories.tsx +115 -0
- package/src/primitives/code-block.tsx +22 -0
- package/src/primitives/design-tokens.stories.tsx +162 -0
- package/src/primitives/dialog.stories.tsx +176 -0
- package/src/primitives/dialog.tsx +137 -0
- package/src/primitives/drop-zone.stories.tsx +123 -0
- package/src/primitives/drop-zone.tsx +131 -0
- package/src/primitives/dropdown-menu.stories.tsx +122 -0
- package/src/primitives/dropdown-menu.tsx +214 -0
- package/src/primitives/empty-state.stories.tsx +81 -0
- package/src/primitives/empty-state.tsx +40 -0
- package/src/primitives/index.ts +118 -0
- package/src/primitives/input.stories.tsx +113 -0
- package/src/primitives/input.tsx +136 -0
- package/src/primitives/label.stories.tsx +84 -0
- package/src/primitives/label.tsx +24 -0
- package/src/primitives/progress.stories.tsx +93 -0
- package/src/primitives/progress.tsx +50 -0
- package/src/primitives/segmented-control.test.tsx +328 -0
- package/src/primitives/segmented-control.tsx +154 -0
- package/src/primitives/select.stories.tsx +164 -0
- package/src/primitives/select.tsx +158 -0
- package/src/primitives/sidebar-drop-zone.stories.tsx +100 -0
- package/src/primitives/sidebar-drop-zone.tsx +149 -0
- package/src/primitives/skeleton.stories.tsx +79 -0
- package/src/primitives/skeleton.tsx +55 -0
- package/src/primitives/stat-card.stories.tsx +137 -0
- package/src/primitives/stat-card.tsx +97 -0
- package/src/primitives/switch.stories.tsx +85 -0
- package/src/primitives/switch.tsx +28 -0
- package/src/primitives/table.stories.tsx +170 -0
- package/src/primitives/table.tsx +116 -0
- package/src/primitives/tabs.stories.tsx +180 -0
- package/src/primitives/tabs.tsx +71 -0
- package/src/primitives/terminal-display.stories.tsx +191 -0
- package/src/primitives/terminal-display.tsx +189 -0
- package/src/primitives/theme-toggle.stories.tsx +32 -0
- package/src/primitives/theme-toggle.tsx +96 -0
- package/src/primitives/toast.stories.tsx +155 -0
- package/src/primitives/toast.tsx +190 -0
- package/src/primitives/upload-progress.stories.tsx +120 -0
- package/src/primitives/upload-progress.tsx +110 -0
- package/src/run/expanded-tool-detail.stories.tsx +182 -0
- package/src/run/expanded-tool-detail.tsx +186 -0
- package/src/run/index.ts +13 -0
- package/src/run/inline-thinking-item.stories.tsx +136 -0
- package/src/run/inline-thinking-item.tsx +120 -0
- package/src/run/inline-tool-item.stories.tsx +222 -0
- package/src/run/inline-tool-item.tsx +190 -0
- package/src/run/run-group.stories.tsx +322 -0
- package/src/run/run-group.tsx +569 -0
- package/src/run/run-item-primitives.tsx +17 -0
- package/src/run/tool-call-feed.stories.tsx +294 -0
- package/src/run/tool-call-feed.tsx +192 -0
- package/src/run/tool-call-step.stories.tsx +198 -0
- package/src/run/tool-call-step.tsx +240 -0
- package/src/sdk-hooks.ts +38 -0
- package/src/stores/active-sessions-store.ts +455 -0
- package/src/stores/chat-store.ts +43 -0
- package/src/stores/index.ts +2 -0
- package/src/tool-previews/command-preview.tsx +116 -0
- package/src/tool-previews/diff-preview.tsx +85 -0
- package/src/tool-previews/glob-results-preview.tsx +98 -0
- package/src/tool-previews/grep-results-preview.tsx +157 -0
- package/src/tool-previews/index.ts +22 -0
- package/src/tool-previews/preview-primitives.tsx +84 -0
- package/src/tool-previews/question-preview.tsx +101 -0
- package/src/tool-previews/web-search-preview.tsx +117 -0
- package/src/tool-previews/write-file-preview.tsx +80 -0
- package/src/types/branding.ts +11 -0
- package/src/types/index.ts +5 -0
- package/src/types/message.ts +13 -0
- package/src/types/parts.ts +51 -0
- package/src/types/run.ts +56 -0
- package/src/types/tool-display.ts +41 -0
- package/src/utils/copy-text.ts +30 -0
- package/src/utils/format.test.ts +43 -0
- package/src/utils/format.ts +56 -0
- package/src/utils/index.ts +10 -0
- package/src/utils/time-ago.ts +9 -0
- package/src/utils/tool-display.ts +238 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { type KeyboardEvent, type ReactNode } from "react";
|
|
2
|
+
import {
|
|
3
|
+
AlertTriangle,
|
|
4
|
+
CheckCircle2,
|
|
5
|
+
CircleDot,
|
|
6
|
+
FileText,
|
|
7
|
+
Info,
|
|
8
|
+
} from "lucide-react";
|
|
9
|
+
import { cn } from "../lib/utils";
|
|
10
|
+
import { type MessageRole } from "./chat-message";
|
|
11
|
+
import { Markdown } from "../markdown/markdown";
|
|
12
|
+
import { ThinkingIndicator } from "./thinking-indicator";
|
|
13
|
+
import { type ToolCallData } from "../run/tool-call-feed";
|
|
14
|
+
import { ToolCallGroup, ToolCallStep } from "../run/tool-call-step";
|
|
15
|
+
|
|
16
|
+
export type AgentTimelineTone = "default" | "info" | "success" | "warning" | "error";
|
|
17
|
+
|
|
18
|
+
export interface AgentTimelineMessageItem {
|
|
19
|
+
id: string;
|
|
20
|
+
kind: "message";
|
|
21
|
+
role: MessageRole;
|
|
22
|
+
content: string;
|
|
23
|
+
toolCalls?: ReactNode;
|
|
24
|
+
isStreaming?: boolean;
|
|
25
|
+
timestamp?: Date;
|
|
26
|
+
after?: ReactNode;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface AgentTimelineToolItem {
|
|
30
|
+
id: string;
|
|
31
|
+
kind: "tool";
|
|
32
|
+
call: ToolCallData;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface AgentTimelineToolGroupItem {
|
|
36
|
+
id: string;
|
|
37
|
+
kind: "tool_group";
|
|
38
|
+
title?: string;
|
|
39
|
+
calls: ToolCallData[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface AgentTimelineStatusItem {
|
|
43
|
+
id: string;
|
|
44
|
+
kind: "status";
|
|
45
|
+
label: string;
|
|
46
|
+
detail?: string;
|
|
47
|
+
tone?: AgentTimelineTone;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface AgentTimelineArtifactItem {
|
|
51
|
+
id: string;
|
|
52
|
+
kind: "artifact";
|
|
53
|
+
title: string;
|
|
54
|
+
description?: string;
|
|
55
|
+
meta?: ReactNode;
|
|
56
|
+
icon?: ReactNode;
|
|
57
|
+
tone?: AgentTimelineTone;
|
|
58
|
+
action?: ReactNode;
|
|
59
|
+
onClick?: () => void;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface AgentTimelineCustomItem {
|
|
63
|
+
id: string;
|
|
64
|
+
kind: "custom";
|
|
65
|
+
content: ReactNode;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type AgentTimelineItem =
|
|
69
|
+
| AgentTimelineMessageItem
|
|
70
|
+
| AgentTimelineToolItem
|
|
71
|
+
| AgentTimelineToolGroupItem
|
|
72
|
+
| AgentTimelineStatusItem
|
|
73
|
+
| AgentTimelineArtifactItem
|
|
74
|
+
| AgentTimelineCustomItem;
|
|
75
|
+
|
|
76
|
+
export interface AgentTimelineProps {
|
|
77
|
+
items: AgentTimelineItem[];
|
|
78
|
+
isThinking?: boolean;
|
|
79
|
+
emptyState?: ReactNode;
|
|
80
|
+
className?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const TONE_STYLES: Record<AgentTimelineTone, { dot: string; card: string; text: string; icon: typeof Info }> = {
|
|
84
|
+
default: {
|
|
85
|
+
dot: "bg-[var(--border-hover)]",
|
|
86
|
+
card: "border-border bg-card",
|
|
87
|
+
text: "text-foreground",
|
|
88
|
+
icon: CircleDot,
|
|
89
|
+
},
|
|
90
|
+
info: {
|
|
91
|
+
dot: "bg-[var(--surface-info-text)]",
|
|
92
|
+
card: "border-[var(--surface-info-border)] bg-[var(--surface-info-bg)]",
|
|
93
|
+
text: "text-[var(--surface-info-text)]",
|
|
94
|
+
icon: Info,
|
|
95
|
+
},
|
|
96
|
+
success: {
|
|
97
|
+
dot: "bg-[var(--surface-success-text)]",
|
|
98
|
+
card: "border-[var(--surface-success-border)] bg-[var(--surface-success-bg)]",
|
|
99
|
+
text: "text-[var(--surface-success-text)]",
|
|
100
|
+
icon: CheckCircle2,
|
|
101
|
+
},
|
|
102
|
+
warning: {
|
|
103
|
+
dot: "bg-[var(--surface-warning-text)]",
|
|
104
|
+
card: "border-[var(--surface-warning-border)] bg-[var(--surface-warning-bg)]",
|
|
105
|
+
text: "text-[var(--surface-warning-text)]",
|
|
106
|
+
icon: AlertTriangle,
|
|
107
|
+
},
|
|
108
|
+
error: {
|
|
109
|
+
dot: "bg-[var(--surface-danger-text)]",
|
|
110
|
+
card: "border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)]",
|
|
111
|
+
text: "text-[var(--surface-danger-text)]",
|
|
112
|
+
icon: AlertTriangle,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
function formatTime(date: Date): string {
|
|
117
|
+
return date.toLocaleTimeString(undefined, { hour: "numeric", minute: "2-digit" });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
interface AgentTimelineRowProps {
|
|
121
|
+
isLast: boolean;
|
|
122
|
+
accentClassName: string;
|
|
123
|
+
children: ReactNode;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function AgentTimelineRow({ isLast, accentClassName, children }: AgentTimelineRowProps) {
|
|
127
|
+
return (
|
|
128
|
+
<div className="grid grid-cols-[1.25rem_minmax(0,1fr)] gap-x-4">
|
|
129
|
+
<div className="relative flex justify-center">
|
|
130
|
+
{!isLast && (
|
|
131
|
+
<span className="absolute top-4 bottom-[-0.75rem] left-1/2 w-px -translate-x-1/2 bg-border" />
|
|
132
|
+
)}
|
|
133
|
+
<span className={cn("relative mt-2 h-[var(--timeline-dot-size)] w-[var(--timeline-dot-size)] rounded-full ring-4 ring-[var(--bg-root)]", accentClassName)} />
|
|
134
|
+
</div>
|
|
135
|
+
<div className="min-w-0 pb-3">{children}</div>
|
|
136
|
+
</div>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function UserMessage({ item }: { item: AgentTimelineMessageItem }) {
|
|
141
|
+
return (
|
|
142
|
+
<div className="mb-3 flex justify-end">
|
|
143
|
+
<div className="max-w-[72%]">
|
|
144
|
+
<div className="rounded-2xl border border-border bg-muted/50 px-4 py-3">
|
|
145
|
+
{item.timestamp && (
|
|
146
|
+
<div className="mb-1.5 text-right text-[var(--font-size-xs)] text-muted-foreground">
|
|
147
|
+
{formatTime(item.timestamp)}
|
|
148
|
+
</div>
|
|
149
|
+
)}
|
|
150
|
+
<div className="whitespace-pre-wrap text-[var(--font-size-base)] leading-[var(--line-height-base)] text-foreground">
|
|
151
|
+
{item.content}
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function AssistantMessage({ item }: { item: AgentTimelineMessageItem }) {
|
|
160
|
+
return (
|
|
161
|
+
<div className="-mt-0.5">
|
|
162
|
+
{item.timestamp && (
|
|
163
|
+
<div className="mb-2 text-[var(--font-size-xs)] text-muted-foreground">
|
|
164
|
+
{formatTime(item.timestamp)}
|
|
165
|
+
</div>
|
|
166
|
+
)}
|
|
167
|
+
{item.content && (
|
|
168
|
+
<Markdown className="tangle-prose text-[var(--font-size-base)] leading-[var(--line-height-base)]">{item.content}</Markdown>
|
|
169
|
+
)}
|
|
170
|
+
{item.isStreaming && (
|
|
171
|
+
<span className="ml-0.5 inline-block h-4 w-2 animate-pulse rounded-sm bg-primary align-text-bottom" />
|
|
172
|
+
)}
|
|
173
|
+
{item.toolCalls && <div className="mt-3">{item.toolCalls}</div>}
|
|
174
|
+
{item.after && (
|
|
175
|
+
<div className="mt-3 border-t border-border pt-3">
|
|
176
|
+
{item.after}
|
|
177
|
+
</div>
|
|
178
|
+
)}
|
|
179
|
+
</div>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function StatusCard({ item }: { item: AgentTimelineStatusItem }) {
|
|
184
|
+
const tone = TONE_STYLES[item.tone ?? "default"];
|
|
185
|
+
const Icon = tone.icon;
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<div className={cn("rounded-[var(--radius-lg)] border px-4 py-3", tone.card)}>
|
|
189
|
+
<div className="flex items-start gap-3">
|
|
190
|
+
<Icon className={cn("mt-0.5 h-4 w-4 shrink-0", tone.text)} />
|
|
191
|
+
<div className="min-w-0">
|
|
192
|
+
<div className={cn("text-sm font-medium", tone.text)}>{item.label}</div>
|
|
193
|
+
{item.detail && (
|
|
194
|
+
<div className="mt-0.5 text-sm text-muted-foreground">{item.detail}</div>
|
|
195
|
+
)}
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function ArtifactCard({ item }: { item: AgentTimelineArtifactItem }) {
|
|
203
|
+
const tone = TONE_STYLES[item.tone ?? "default"];
|
|
204
|
+
const content = (
|
|
205
|
+
<div className={cn("rounded-[var(--radius-lg)] border px-4 py-3", tone.card)}>
|
|
206
|
+
<div className="flex items-start gap-3">
|
|
207
|
+
<div className="mt-0.5 flex h-[var(--avatar-size)] w-[var(--avatar-size)] shrink-0 items-center justify-center rounded-[var(--radius-md)] bg-muted/50 text-foreground">
|
|
208
|
+
{item.icon ?? <FileText className="h-4 w-4" />}
|
|
209
|
+
</div>
|
|
210
|
+
<div className="min-w-0 flex-1">
|
|
211
|
+
<div className="text-sm font-medium text-foreground">{item.title}</div>
|
|
212
|
+
{item.description && (
|
|
213
|
+
<div className="mt-1 text-sm text-muted-foreground">{item.description}</div>
|
|
214
|
+
)}
|
|
215
|
+
{item.meta && (
|
|
216
|
+
<div className="mt-2 flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
|
|
217
|
+
{item.meta}
|
|
218
|
+
</div>
|
|
219
|
+
)}
|
|
220
|
+
</div>
|
|
221
|
+
{item.action && <div className="shrink-0">{item.action}</div>}
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
if (!item.onClick) return content;
|
|
227
|
+
|
|
228
|
+
return (
|
|
229
|
+
<div
|
|
230
|
+
role="button"
|
|
231
|
+
tabIndex={0}
|
|
232
|
+
onClick={item.onClick}
|
|
233
|
+
onKeyDown={(event: KeyboardEvent<HTMLDivElement>) => {
|
|
234
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
235
|
+
event.preventDefault();
|
|
236
|
+
item.onClick?.();
|
|
237
|
+
}
|
|
238
|
+
}}
|
|
239
|
+
className="block w-full text-left transition-transform hover:-translate-y-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60"
|
|
240
|
+
>
|
|
241
|
+
{content}
|
|
242
|
+
</div>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* AgentTimeline — unified mixed-content timeline for agent-backed sandbox
|
|
248
|
+
* sessions. Renders messages, tool steps, status cards, and artifact handoffs in
|
|
249
|
+
* a single execution narrative.
|
|
250
|
+
*/
|
|
251
|
+
export function AgentTimeline({
|
|
252
|
+
items,
|
|
253
|
+
isThinking,
|
|
254
|
+
emptyState,
|
|
255
|
+
className,
|
|
256
|
+
}: AgentTimelineProps) {
|
|
257
|
+
if (items.length === 0 && !isThinking) {
|
|
258
|
+
return emptyState ? (
|
|
259
|
+
<div className={cn("flex h-full items-center justify-center p-4", className)}>
|
|
260
|
+
{emptyState}
|
|
261
|
+
</div>
|
|
262
|
+
) : null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const renderedItems: AgentTimelineItem[] = isThinking
|
|
266
|
+
? [...items, { id: "__thinking__", kind: "custom", content: <ThinkingIndicator /> }]
|
|
267
|
+
: items;
|
|
268
|
+
|
|
269
|
+
// Determine which items participate in the timeline connector (non-user-message items)
|
|
270
|
+
// User messages are rendered outside the timeline grid
|
|
271
|
+
const timelineItems = renderedItems.filter((item) => !(item.kind === "message" && item.role === "user"));
|
|
272
|
+
|
|
273
|
+
return (
|
|
274
|
+
<div className={cn("mx-auto w-full max-w-5xl px-4 py-4", className)}>
|
|
275
|
+
{renderedItems.map((item, index) => {
|
|
276
|
+
// User messages: right-aligned bubble, no connector
|
|
277
|
+
if (item.kind === "message" && item.role === "user") {
|
|
278
|
+
return <UserMessage key={item.id} item={item} />;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const timelineIndex = timelineItems.indexOf(item);
|
|
282
|
+
const isLast = timelineIndex === timelineItems.length - 1;
|
|
283
|
+
|
|
284
|
+
if (item.kind === "message") {
|
|
285
|
+
return (
|
|
286
|
+
<AgentTimelineRow key={item.id} isLast={isLast} accentClassName="bg-[var(--brand-glow)]">
|
|
287
|
+
<AssistantMessage item={item} />
|
|
288
|
+
</AgentTimelineRow>
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (item.kind === "tool") {
|
|
293
|
+
return (
|
|
294
|
+
<AgentTimelineRow key={item.id} isLast={isLast} accentClassName="bg-[var(--border-hover)]">
|
|
295
|
+
<ToolCallStep
|
|
296
|
+
type={item.call.type}
|
|
297
|
+
label={item.call.label}
|
|
298
|
+
status={item.call.status}
|
|
299
|
+
detail={item.call.detail}
|
|
300
|
+
output={item.call.output}
|
|
301
|
+
duration={item.call.duration}
|
|
302
|
+
/>
|
|
303
|
+
</AgentTimelineRow>
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (item.kind === "tool_group") {
|
|
308
|
+
return (
|
|
309
|
+
<AgentTimelineRow key={item.id} isLast={isLast} accentClassName="bg-[var(--border-hover)]">
|
|
310
|
+
<ToolCallGroup title={item.title}>
|
|
311
|
+
{item.calls.map((call) => (
|
|
312
|
+
<ToolCallStep
|
|
313
|
+
key={call.id}
|
|
314
|
+
type={call.type}
|
|
315
|
+
label={call.label}
|
|
316
|
+
status={call.status}
|
|
317
|
+
detail={call.detail}
|
|
318
|
+
output={call.output}
|
|
319
|
+
duration={call.duration}
|
|
320
|
+
/>
|
|
321
|
+
))}
|
|
322
|
+
</ToolCallGroup>
|
|
323
|
+
</AgentTimelineRow>
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (item.kind === "status") {
|
|
328
|
+
return (
|
|
329
|
+
<AgentTimelineRow
|
|
330
|
+
key={item.id}
|
|
331
|
+
isLast={isLast}
|
|
332
|
+
accentClassName={TONE_STYLES[item.tone ?? "default"].dot}
|
|
333
|
+
>
|
|
334
|
+
<StatusCard item={item} />
|
|
335
|
+
</AgentTimelineRow>
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (item.kind === "artifact") {
|
|
340
|
+
return (
|
|
341
|
+
<AgentTimelineRow
|
|
342
|
+
key={item.id}
|
|
343
|
+
isLast={isLast}
|
|
344
|
+
accentClassName={TONE_STYLES[item.tone ?? "default"].dot}
|
|
345
|
+
>
|
|
346
|
+
<ArtifactCard item={item} />
|
|
347
|
+
</AgentTimelineRow>
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// custom
|
|
352
|
+
return (
|
|
353
|
+
<AgentTimelineRow key={item.id} isLast={isLast} accentClassName="bg-[var(--border-hover)]">
|
|
354
|
+
{(item as AgentTimelineCustomItem).content}
|
|
355
|
+
</AgentTimelineRow>
|
|
356
|
+
);
|
|
357
|
+
})}
|
|
358
|
+
</div>
|
|
359
|
+
);
|
|
360
|
+
}
|