@tangle-network/ui 7.0.0 → 8.1.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/dist/chat.d.ts +17 -72
- package/dist/chat.js +2 -4
- package/dist/{chunk-QIRVZMQY.js → chunk-C3BIVG72.js} +143 -107
- package/dist/{chunk-5CS3I7Y3.js → chunk-QUAU6ZNC.js} +111 -395
- package/dist/hooks.d.ts +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +5 -9
- package/dist/run.d.ts +30 -2
- package/dist/run.js +4 -6
- package/dist/sdk-hooks.d.ts +1 -1
- package/dist/{tool-call-feed-Bs3MyQMT.d.ts → tool-call-feed-D9iofJgW.d.ts} +1 -23
- package/package.json +2 -2
- package/src/chat/agent-timeline.tsx +139 -45
- package/src/chat/chat-container.tsx +6 -48
- package/src/chat/chat-message.tsx +0 -4
- package/src/chat/index.ts +0 -1
- package/src/markdown/markdown.stories.tsx +1 -1
- package/src/run/assistant-run-shell.tsx +115 -0
- package/src/run/index.ts +5 -1
- package/src/run/run-group.tsx +12 -76
- package/src/run/tool-call-step.tsx +5 -7
- package/src/chat/chat-input.stories.tsx +0 -142
- package/src/chat/chat-input.tsx +0 -389
package/dist/hooks.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ export { B as Button, a as ButtonProps, b as buttonVariants } from './button-CMQ
|
|
|
2
2
|
export { Avatar, AvatarFallback, AvatarImage, Badge, BadgeProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, EmptyStateProps, InlineCode, InlineCodeProps, Input, InputProps, Label, Progress, SegmentedControl, SegmentedControlOption, SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SidebarDropZone, SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, StatCard, StatCardProps, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, Textarea, TextareaProps, ThemeToggle, Toast, ToastContainer, ToastProvider, UploadFile, UploadProgress, UploadProgressProps, badgeVariants, useTheme, useToast } from './primitives.js';
|
|
3
3
|
export { Logo, LogoProps, TangleKnot } from '@tangle-network/brand';
|
|
4
4
|
export { A as ArtifactPane, a as ArtifactPaneProps } from './artifact-pane-DvJyPWV4.js';
|
|
5
|
-
export { AgentTimeline, AgentTimelineArtifactItem, AgentTimelineCustomItem, AgentTimelineItem, AgentTimelineMessageItem, AgentTimelineProps, AgentTimelineStatusItem, AgentTimelineTone, AgentTimelineToolGroupItem, AgentTimelineToolItem, ChatContainer, ChatContainerProps,
|
|
6
|
-
export { ExpandedToolDetail, ExpandedToolDetailProps, InlineThinkingItem, InlineThinkingItemProps, InlineToolItem, InlineToolItemProps, LiveDuration, RunGroup, RunGroupProps } from './run.js';
|
|
7
|
-
export { F as FeedSegment, T as ToolCallData, a as ToolCallFeed, b as ToolCallFeedProps, c as
|
|
5
|
+
export { AgentTimeline, AgentTimelineArtifactItem, AgentTimelineCustomItem, AgentTimelineItem, AgentTimelineMessageItem, AgentTimelineProps, AgentTimelineStatusItem, AgentTimelineTone, AgentTimelineToolGroupItem, AgentTimelineToolItem, ChatContainer, ChatContainerProps, ChatMessage, ChatMessageProps, MessageList, MessageListProps, MessageRole, ThinkingIndicator, ThinkingIndicatorProps, UserMessage, UserMessageProps } from './chat.js';
|
|
6
|
+
export { AssistantRunShell, AssistantRunShellProps, ExpandedToolDetail, ExpandedToolDetailProps, InlineThinkingItem, InlineThinkingItemProps, InlineToolItem, InlineToolItemProps, LiveDuration, RunGroup, RunGroupProps } from './run.js';
|
|
7
|
+
export { F as FeedSegment, T as ToolCallData, a as ToolCallFeed, b as ToolCallFeedProps, c as ToolCallStatus, d as ToolCallType, p as parseToolEvent } from './tool-call-feed-D9iofJgW.js';
|
|
8
8
|
export { OpenUIAction, OpenUIActionsNode, OpenUIArtifactRenderer, OpenUIArtifactRendererProps, OpenUIBadgeNode, OpenUICardNode, OpenUICodeNode, OpenUIComponentNode, OpenUIGridNode, OpenUIHeadingNode, OpenUIKeyValueNode, OpenUIMarkdownNode, OpenUIPrimitive, OpenUISeparatorNode, OpenUIStackNode, OpenUIStatNode, OpenUITableNode, OpenUITextNode } from './openui.js';
|
|
9
9
|
export { FileArtifactPane, FileArtifactPaneProps, FileFormat, FileNode, FilePreview, FilePreviewProps, FileTabData, FileTabs, FileTabsProps, FileTree, FileTreeProps, FileTreeVisibilityOptions, RichFileTree, RichFileTreeGitEntry, RichFileTreeGitStatus, RichFileTreeProps, RichFileTreeThemeVars, detectFileFormat, fileExtension, filterFileTree, getCodeLanguage, getFormatLabel, getSyntaxLanguage } from './files.js';
|
|
10
10
|
export { Markdown, MarkdownProps } from './markdown.js';
|
package/dist/index.js
CHANGED
|
@@ -133,12 +133,11 @@ import {
|
|
|
133
133
|
import {
|
|
134
134
|
AgentTimeline,
|
|
135
135
|
ChatContainer,
|
|
136
|
-
ChatInput,
|
|
137
136
|
ChatMessage,
|
|
138
137
|
MessageList,
|
|
139
138
|
ThinkingIndicator,
|
|
140
139
|
UserMessage
|
|
141
|
-
} from "./chunk-
|
|
140
|
+
} from "./chunk-QUAU6ZNC.js";
|
|
142
141
|
import {
|
|
143
142
|
useAutoScroll,
|
|
144
143
|
useRunCollapseState,
|
|
@@ -150,15 +149,14 @@ import {
|
|
|
150
149
|
parseToolEvent
|
|
151
150
|
} from "./chunk-IWQZXL6A.js";
|
|
152
151
|
import {
|
|
152
|
+
AssistantRunShell,
|
|
153
153
|
InlineThinkingItem,
|
|
154
154
|
RunGroup
|
|
155
|
-
} from "./chunk-
|
|
155
|
+
} from "./chunk-C3BIVG72.js";
|
|
156
156
|
import {
|
|
157
157
|
ExpandedToolDetail,
|
|
158
158
|
InlineToolItem,
|
|
159
|
-
LiveDuration
|
|
160
|
-
ToolCallGroup,
|
|
161
|
-
ToolCallStep
|
|
159
|
+
LiveDuration
|
|
162
160
|
} from "./chunk-RKQDBRTC.js";
|
|
163
161
|
import {
|
|
164
162
|
TOOL_CATEGORY_ICONS,
|
|
@@ -341,6 +339,7 @@ function RedactedDocument({
|
|
|
341
339
|
export {
|
|
342
340
|
AgentTimeline,
|
|
343
341
|
ArtifactPane,
|
|
342
|
+
AssistantRunShell,
|
|
344
343
|
AuthHeader,
|
|
345
344
|
Avatar,
|
|
346
345
|
AvatarFallback,
|
|
@@ -354,7 +353,6 @@ export {
|
|
|
354
353
|
CardHeader,
|
|
355
354
|
CardTitle,
|
|
356
355
|
ChatContainer,
|
|
357
|
-
ChatInput,
|
|
358
356
|
ChatMessage,
|
|
359
357
|
CodeBlock,
|
|
360
358
|
CommandPreview,
|
|
@@ -453,8 +451,6 @@ export {
|
|
|
453
451
|
ToastContainer,
|
|
454
452
|
ToastProvider,
|
|
455
453
|
ToolCallFeed,
|
|
456
|
-
ToolCallGroup,
|
|
457
|
-
ToolCallStep,
|
|
458
454
|
UploadProgress,
|
|
459
455
|
UserMenu,
|
|
460
456
|
UserMessage,
|
package/dist/run.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { R as Run } from './run-PfLmDAox.js';
|
|
|
5
5
|
import { S as SessionPart, a as ToolPart, R as ReasoningPart } from './parts-dj7AcUg0.js';
|
|
6
6
|
import { AgentBranding } from './types.js';
|
|
7
7
|
import { C as CustomToolRenderer } from './tool-display-z4JcDmMQ.js';
|
|
8
|
-
export { F as FeedSegment, T as ToolCallData, a as ToolCallFeed, b as ToolCallFeedProps, c as
|
|
8
|
+
export { F as FeedSegment, T as ToolCallData, a as ToolCallFeed, b as ToolCallFeedProps, c as ToolCallStatus, d as ToolCallType, p as parseToolEvent } from './tool-call-feed-D9iofJgW.js';
|
|
9
9
|
import './message-BHWbxBtT.js';
|
|
10
10
|
|
|
11
11
|
interface RunGroupProps {
|
|
@@ -28,6 +28,34 @@ interface RunGroupProps {
|
|
|
28
28
|
*/
|
|
29
29
|
declare const RunGroup: React.MemoExoticComponent<({ run, partMap, collapsed, onToggle, branding, renderToolDetail, headerActions, renderToolActions, }: RunGroupProps) => react_jsx_runtime.JSX.Element | null>;
|
|
30
30
|
|
|
31
|
+
interface AssistantRunShellProps {
|
|
32
|
+
/** Header label, e.g. the agent name or "Tools". */
|
|
33
|
+
label: string;
|
|
34
|
+
/** Terse stat line beside the label, e.g. "3 tools, 2s thinking". */
|
|
35
|
+
summary?: string;
|
|
36
|
+
/** One-line preview shown next to the label AND below the header when collapsed. */
|
|
37
|
+
collapsedPreview?: string;
|
|
38
|
+
/** Small trailing glyphs before the status pill (e.g. category badges). */
|
|
39
|
+
badges?: ReactNode;
|
|
40
|
+
/** Drives the status pill and header spinner. */
|
|
41
|
+
isStreaming?: boolean;
|
|
42
|
+
collapsed: boolean;
|
|
43
|
+
onToggle: () => void;
|
|
44
|
+
/** Actions rendered outside the collapse trigger, right of the header. */
|
|
45
|
+
headerActions?: ReactNode;
|
|
46
|
+
children: ReactNode;
|
|
47
|
+
className?: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The collapsible "assistant run" container shared by `RunGroup` (session-model
|
|
51
|
+
* driven) and `AgentTimeline` (declarative item list). Owns the header
|
|
52
|
+
* (label · summary · badges · status pill · chevron), the collapsed preview, and
|
|
53
|
+
* the Radix collapse — so both transcripts fold agent activity the same way and
|
|
54
|
+
* there is one implementation of a run, not two. It renders only chrome; callers
|
|
55
|
+
* pass the run body (tool rows, reasoning, text) as `children`.
|
|
56
|
+
*/
|
|
57
|
+
declare function AssistantRunShell({ label, summary, collapsedPreview, badges, isStreaming, collapsed, onToggle, headerActions, children, className, }: AssistantRunShellProps): react_jsx_runtime.JSX.Element;
|
|
58
|
+
|
|
31
59
|
interface InlineToolItemProps {
|
|
32
60
|
part: ToolPart;
|
|
33
61
|
renderToolDetail?: CustomToolRenderer;
|
|
@@ -70,4 +98,4 @@ declare function LiveDuration({ startTime }: {
|
|
|
70
98
|
startTime: number;
|
|
71
99
|
}): react_jsx_runtime.JSX.Element;
|
|
72
100
|
|
|
73
|
-
export { ExpandedToolDetail, type ExpandedToolDetailProps, InlineThinkingItem, type InlineThinkingItemProps, InlineToolItem, type InlineToolItemProps, LiveDuration, RunGroup, type RunGroupProps };
|
|
101
|
+
export { AssistantRunShell, type AssistantRunShellProps, ExpandedToolDetail, type ExpandedToolDetailProps, InlineThinkingItem, type InlineThinkingItemProps, InlineToolItem, type InlineToolItemProps, LiveDuration, RunGroup, type RunGroupProps };
|
package/dist/run.js
CHANGED
|
@@ -4,15 +4,14 @@ import {
|
|
|
4
4
|
parseToolEvent
|
|
5
5
|
} from "./chunk-IWQZXL6A.js";
|
|
6
6
|
import {
|
|
7
|
+
AssistantRunShell,
|
|
7
8
|
InlineThinkingItem,
|
|
8
9
|
RunGroup
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-C3BIVG72.js";
|
|
10
11
|
import {
|
|
11
12
|
ExpandedToolDetail,
|
|
12
13
|
InlineToolItem,
|
|
13
|
-
LiveDuration
|
|
14
|
-
ToolCallGroup,
|
|
15
|
-
ToolCallStep
|
|
14
|
+
LiveDuration
|
|
16
15
|
} from "./chunk-RKQDBRTC.js";
|
|
17
16
|
import "./chunk-ULDNFLIM.js";
|
|
18
17
|
import "./chunk-AAUNOHVL.js";
|
|
@@ -24,13 +23,12 @@ import "./chunk-FJBTCTZM.js";
|
|
|
24
23
|
import "./chunk-WUQDUBJG.js";
|
|
25
24
|
import "./chunk-RQHJBTEU.js";
|
|
26
25
|
export {
|
|
26
|
+
AssistantRunShell,
|
|
27
27
|
ExpandedToolDetail,
|
|
28
28
|
InlineThinkingItem,
|
|
29
29
|
InlineToolItem,
|
|
30
30
|
LiveDuration,
|
|
31
31
|
RunGroup,
|
|
32
32
|
ToolCallFeed,
|
|
33
|
-
ToolCallGroup,
|
|
34
|
-
ToolCallStep,
|
|
35
33
|
parseToolEvent
|
|
36
34
|
};
|
package/dist/sdk-hooks.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { R as Run, G as GroupedMessage } from './run-PfLmDAox.js';
|
|
|
4
4
|
import { S as SessionMessage } from './message-BHWbxBtT.js';
|
|
5
5
|
import { S as SessionPart } from './parts-dj7AcUg0.js';
|
|
6
6
|
import { R as RegisterActiveSessionOptions, g as ActiveSessionTransportMode, c as ActiveSessionConnectionState } from './active-sessions-store-CeOmXgv5.js';
|
|
7
|
-
import { F as FeedSegment } from './tool-call-feed-
|
|
7
|
+
import { F as FeedSegment } from './tool-call-feed-D9iofJgW.js';
|
|
8
8
|
import 'nanostores';
|
|
9
9
|
import 'react/jsx-runtime';
|
|
10
10
|
|
|
@@ -1,29 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
3
2
|
|
|
4
3
|
type ToolCallType = "bash" | "read" | "write" | "edit" | "glob" | "grep" | "list" | "download" | "inspect" | "audit" | "unknown";
|
|
5
4
|
type ToolCallStatus = "running" | "success" | "error";
|
|
6
|
-
interface ToolCallStepProps {
|
|
7
|
-
type: ToolCallType;
|
|
8
|
-
label: string;
|
|
9
|
-
status: ToolCallStatus;
|
|
10
|
-
detail?: string;
|
|
11
|
-
output?: string;
|
|
12
|
-
/** Override syntax highlighting language; inferred from detail path if omitted */
|
|
13
|
-
language?: string;
|
|
14
|
-
duration?: number;
|
|
15
|
-
className?: string;
|
|
16
|
-
}
|
|
17
|
-
declare function ToolCallStep({ type, label, status, detail, output, language, duration, className, }: ToolCallStepProps): react_jsx_runtime.JSX.Element;
|
|
18
|
-
/**
|
|
19
|
-
* ToolCallGroup — groups multiple tool calls under a heading.
|
|
20
|
-
*/
|
|
21
|
-
interface ToolCallGroupProps {
|
|
22
|
-
title?: string;
|
|
23
|
-
children: ReactNode;
|
|
24
|
-
className?: string;
|
|
25
|
-
}
|
|
26
|
-
declare function ToolCallGroup({ title, children, className }: ToolCallGroupProps): react_jsx_runtime.JSX.Element;
|
|
27
5
|
|
|
28
6
|
interface ToolCallData {
|
|
29
7
|
id: string;
|
|
@@ -65,4 +43,4 @@ declare function parseToolEvent(event: {
|
|
|
65
43
|
data: Record<string, unknown>;
|
|
66
44
|
}): ToolCallData | null;
|
|
67
45
|
|
|
68
|
-
export { type FeedSegment as F, type ToolCallData as T, ToolCallFeed as a, type ToolCallFeedProps as b,
|
|
46
|
+
export { type FeedSegment as F, type ToolCallData as T, ToolCallFeed as a, type ToolCallFeedProps as b, type ToolCallStatus as c, type ToolCallType as d, parseToolEvent as p };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.1.0",
|
|
4
4
|
"description": "Generic React UI components for Tangle products — primitives, chat, run, files, editor, markdown.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"react": "^18 || ^19",
|
|
133
133
|
"react-dom": "^18 || ^19",
|
|
134
134
|
"react-router": "^7",
|
|
135
|
-
"@tangle-network/brand": "^0.8.
|
|
135
|
+
"@tangle-network/brand": "^0.8.1"
|
|
136
136
|
},
|
|
137
137
|
"peerDependenciesMeta": {
|
|
138
138
|
"@nanostores/react": {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type KeyboardEvent, type ReactNode } from "react";
|
|
1
|
+
import { type KeyboardEvent, type ReactNode, useState } from "react";
|
|
2
2
|
import {
|
|
3
3
|
AlertTriangle,
|
|
4
4
|
CheckCircle2,
|
|
@@ -12,6 +12,7 @@ import { Markdown } from "../markdown/markdown";
|
|
|
12
12
|
import { ThinkingIndicator } from "./thinking-indicator";
|
|
13
13
|
import { type ToolCallData } from "../run/tool-call-feed";
|
|
14
14
|
import { ToolCallGroup, ToolCallStep } from "../run/tool-call-step";
|
|
15
|
+
import { AssistantRunShell } from "../run/assistant-run-shell";
|
|
15
16
|
|
|
16
17
|
export type AgentTimelineTone = "default" | "info" | "success" | "warning" | "error";
|
|
17
18
|
|
|
@@ -78,6 +79,71 @@ export interface AgentTimelineProps {
|
|
|
78
79
|
isThinking?: boolean;
|
|
79
80
|
emptyState?: ReactNode;
|
|
80
81
|
className?: string;
|
|
82
|
+
/**
|
|
83
|
+
* Fold consecutive tool / tool-group items into one collapsible run shell
|
|
84
|
+
* (the same `AssistantRunShell` `RunGroup` uses), so a burst of tool activity
|
|
85
|
+
* reads as a single toggleable step instead of a long ladder of rows.
|
|
86
|
+
* Default true; pass false for the flat one-row-per-tool timeline.
|
|
87
|
+
*/
|
|
88
|
+
collapsibleToolRuns?: boolean;
|
|
89
|
+
/** Start collapsed tool runs open (true) or collapsed (false). Default open. */
|
|
90
|
+
defaultToolRunsOpen?: boolean;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** A run of consecutive tool / tool-group items folded into one shell. */
|
|
94
|
+
interface ToolRunGroup {
|
|
95
|
+
id: string;
|
|
96
|
+
kind: "tool_run";
|
|
97
|
+
items: (AgentTimelineToolItem | AgentTimelineToolGroupItem)[];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
type TimelineNode = AgentTimelineItem | ToolRunGroup;
|
|
101
|
+
|
|
102
|
+
function foldToolRuns(items: AgentTimelineItem[]): TimelineNode[] {
|
|
103
|
+
const nodes: TimelineNode[] = [];
|
|
104
|
+
let run: (AgentTimelineToolItem | AgentTimelineToolGroupItem)[] = [];
|
|
105
|
+
|
|
106
|
+
const flush = () => {
|
|
107
|
+
if (run.length === 0) return;
|
|
108
|
+
// A single tool stays a plain row; two or more fold into a collapsible run.
|
|
109
|
+
if (run.length === 1) {
|
|
110
|
+
nodes.push(run[0]);
|
|
111
|
+
} else {
|
|
112
|
+
nodes.push({ id: `tool-run-${run[0].id}`, kind: "tool_run", items: run });
|
|
113
|
+
}
|
|
114
|
+
run = [];
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
for (const item of items) {
|
|
118
|
+
if (item.kind === "tool" || item.kind === "tool_group") {
|
|
119
|
+
run.push(item);
|
|
120
|
+
} else {
|
|
121
|
+
flush();
|
|
122
|
+
nodes.push(item);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
flush();
|
|
126
|
+
return nodes;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function countTools(group: ToolRunGroup): number {
|
|
130
|
+
return group.items.reduce(
|
|
131
|
+
(n, item) => n + (item.kind === "tool_group" ? item.calls.length : 1),
|
|
132
|
+
0,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function ToolCallRow({ call }: { call: ToolCallData }) {
|
|
137
|
+
return (
|
|
138
|
+
<ToolCallStep
|
|
139
|
+
type={call.type}
|
|
140
|
+
label={call.label}
|
|
141
|
+
status={call.status}
|
|
142
|
+
detail={call.detail}
|
|
143
|
+
output={call.output}
|
|
144
|
+
duration={call.duration}
|
|
145
|
+
/>
|
|
146
|
+
);
|
|
81
147
|
}
|
|
82
148
|
|
|
83
149
|
const TONE_STYLES: Record<AgentTimelineTone, { dot: string; card: string; text: string; icon: typeof Info }> = {
|
|
@@ -253,7 +319,17 @@ export function AgentTimeline({
|
|
|
253
319
|
isThinking,
|
|
254
320
|
emptyState,
|
|
255
321
|
className,
|
|
322
|
+
collapsibleToolRuns = true,
|
|
323
|
+
defaultToolRunsOpen = true,
|
|
256
324
|
}: AgentTimelineProps) {
|
|
325
|
+
// Collapse state for folded tool runs, keyed by run id. Absent → default.
|
|
326
|
+
const [collapsedRuns, setCollapsedRuns] = useState<Record<string, boolean>>({});
|
|
327
|
+
const toggleRun = (id: string) =>
|
|
328
|
+
setCollapsedRuns((prev) => ({
|
|
329
|
+
...prev,
|
|
330
|
+
[id]: prev[id] === undefined ? defaultToolRunsOpen : !prev[id],
|
|
331
|
+
}));
|
|
332
|
+
|
|
257
333
|
if (items.length === 0 && !isThinking) {
|
|
258
334
|
return emptyState ? (
|
|
259
335
|
<div className={cn("flex h-full items-center justify-center p-4", className)}>
|
|
@@ -266,92 +342,110 @@ export function AgentTimeline({
|
|
|
266
342
|
? [...items, { id: "__thinking__", kind: "custom", content: <ThinkingIndicator /> }]
|
|
267
343
|
: items;
|
|
268
344
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
345
|
+
const nodes: TimelineNode[] = collapsibleToolRuns
|
|
346
|
+
? foldToolRuns(renderedItems)
|
|
347
|
+
: renderedItems;
|
|
348
|
+
|
|
349
|
+
// Non-user rows participate in the connector spine.
|
|
350
|
+
const timelineNodes = nodes.filter(
|
|
351
|
+
(node) => !(node.kind === "message" && node.role === "user"),
|
|
352
|
+
);
|
|
272
353
|
|
|
273
354
|
return (
|
|
274
355
|
<div className={cn("mx-auto w-full max-w-5xl px-4 py-4", className)}>
|
|
275
|
-
{
|
|
356
|
+
{nodes.map((node) => {
|
|
276
357
|
// User messages: right-aligned bubble, no connector
|
|
277
|
-
if (
|
|
278
|
-
return <UserMessage key={
|
|
358
|
+
if (node.kind === "message" && node.role === "user") {
|
|
359
|
+
return <UserMessage key={node.id} item={node} />;
|
|
279
360
|
}
|
|
280
361
|
|
|
281
|
-
const
|
|
282
|
-
|
|
362
|
+
const isLast = timelineNodes.indexOf(node) === timelineNodes.length - 1;
|
|
363
|
+
|
|
364
|
+
if (node.kind === "tool_run") {
|
|
365
|
+
const collapsed = collapsedRuns[node.id] ?? !defaultToolRunsOpen;
|
|
366
|
+
const total = countTools(node);
|
|
367
|
+
return (
|
|
368
|
+
<AgentTimelineRow key={node.id} isLast={isLast} accentClassName="bg-[var(--border-hover)]">
|
|
369
|
+
<AssistantRunShell
|
|
370
|
+
label="Tools"
|
|
371
|
+
summary={`${total} ${total === 1 ? "tool" : "tools"}`}
|
|
372
|
+
collapsed={collapsed}
|
|
373
|
+
onToggle={() => toggleRun(node.id)}
|
|
374
|
+
>
|
|
375
|
+
<div className="space-y-px">
|
|
376
|
+
{node.items.map((item) =>
|
|
377
|
+
item.kind === "tool_group" ? (
|
|
378
|
+
<ToolCallGroup key={item.id} title={item.title}>
|
|
379
|
+
{item.calls.map((call) => (
|
|
380
|
+
<ToolCallRow key={call.id} call={call} />
|
|
381
|
+
))}
|
|
382
|
+
</ToolCallGroup>
|
|
383
|
+
) : (
|
|
384
|
+
<ToolCallRow key={item.id} call={item.call} />
|
|
385
|
+
),
|
|
386
|
+
)}
|
|
387
|
+
</div>
|
|
388
|
+
</AssistantRunShell>
|
|
389
|
+
</AgentTimelineRow>
|
|
390
|
+
);
|
|
391
|
+
}
|
|
283
392
|
|
|
284
|
-
if (
|
|
393
|
+
if (node.kind === "message") {
|
|
285
394
|
return (
|
|
286
|
-
<AgentTimelineRow key={
|
|
287
|
-
<AssistantMessage item={
|
|
395
|
+
<AgentTimelineRow key={node.id} isLast={isLast} accentClassName="bg-[var(--brand-glow)]">
|
|
396
|
+
<AssistantMessage item={node} />
|
|
288
397
|
</AgentTimelineRow>
|
|
289
398
|
);
|
|
290
399
|
}
|
|
291
400
|
|
|
292
|
-
if (
|
|
401
|
+
if (node.kind === "tool") {
|
|
293
402
|
return (
|
|
294
|
-
<AgentTimelineRow key={
|
|
295
|
-
<
|
|
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
|
-
/>
|
|
403
|
+
<AgentTimelineRow key={node.id} isLast={isLast} accentClassName="bg-[var(--border-hover)]">
|
|
404
|
+
<ToolCallRow call={node.call} />
|
|
303
405
|
</AgentTimelineRow>
|
|
304
406
|
);
|
|
305
407
|
}
|
|
306
408
|
|
|
307
|
-
if (
|
|
409
|
+
if (node.kind === "tool_group") {
|
|
308
410
|
return (
|
|
309
|
-
<AgentTimelineRow key={
|
|
310
|
-
<ToolCallGroup title={
|
|
311
|
-
{
|
|
312
|
-
<
|
|
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
|
-
/>
|
|
411
|
+
<AgentTimelineRow key={node.id} isLast={isLast} accentClassName="bg-[var(--border-hover)]">
|
|
412
|
+
<ToolCallGroup title={node.title}>
|
|
413
|
+
{node.calls.map((call) => (
|
|
414
|
+
<ToolCallRow key={call.id} call={call} />
|
|
321
415
|
))}
|
|
322
416
|
</ToolCallGroup>
|
|
323
417
|
</AgentTimelineRow>
|
|
324
418
|
);
|
|
325
419
|
}
|
|
326
420
|
|
|
327
|
-
if (
|
|
421
|
+
if (node.kind === "status") {
|
|
328
422
|
return (
|
|
329
423
|
<AgentTimelineRow
|
|
330
|
-
key={
|
|
424
|
+
key={node.id}
|
|
331
425
|
isLast={isLast}
|
|
332
|
-
accentClassName={TONE_STYLES[
|
|
426
|
+
accentClassName={TONE_STYLES[node.tone ?? "default"].dot}
|
|
333
427
|
>
|
|
334
|
-
<StatusCard item={
|
|
428
|
+
<StatusCard item={node} />
|
|
335
429
|
</AgentTimelineRow>
|
|
336
430
|
);
|
|
337
431
|
}
|
|
338
432
|
|
|
339
|
-
if (
|
|
433
|
+
if (node.kind === "artifact") {
|
|
340
434
|
return (
|
|
341
435
|
<AgentTimelineRow
|
|
342
|
-
key={
|
|
436
|
+
key={node.id}
|
|
343
437
|
isLast={isLast}
|
|
344
|
-
accentClassName={TONE_STYLES[
|
|
438
|
+
accentClassName={TONE_STYLES[node.tone ?? "default"].dot}
|
|
345
439
|
>
|
|
346
|
-
<ArtifactCard item={
|
|
440
|
+
<ArtifactCard item={node} />
|
|
347
441
|
</AgentTimelineRow>
|
|
348
442
|
);
|
|
349
443
|
}
|
|
350
444
|
|
|
351
445
|
// custom
|
|
352
446
|
return (
|
|
353
|
-
<AgentTimelineRow key={
|
|
354
|
-
{(
|
|
447
|
+
<AgentTimelineRow key={node.id} isLast={isLast} accentClassName="bg-[var(--border-hover)]">
|
|
448
|
+
{(node as AgentTimelineCustomItem).content}
|
|
355
449
|
</AgentTimelineRow>
|
|
356
450
|
);
|
|
357
451
|
})}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
memo,
|
|
3
3
|
type ReactNode,
|
|
4
|
-
useCallback,
|
|
5
4
|
useMemo,
|
|
6
5
|
useRef,
|
|
7
6
|
} from "react";
|
|
@@ -20,7 +19,6 @@ import {
|
|
|
20
19
|
AgentTimeline,
|
|
21
20
|
type AgentTimelineItem,
|
|
22
21
|
} from "./agent-timeline";
|
|
23
|
-
import { ChatInput, type PendingFile } from "./chat-input";
|
|
24
22
|
import { InlineThinkingItem } from "../run/inline-thinking-item";
|
|
25
23
|
import { getToolDisplayMetadata } from "../utils/tool-display";
|
|
26
24
|
import {
|
|
@@ -29,27 +27,21 @@ import {
|
|
|
29
27
|
type OpenUIComponentNode,
|
|
30
28
|
} from "../openui/openui-artifact-renderer";
|
|
31
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Transcript-only container: message list + auto-scroll. Composers are
|
|
32
|
+
* composed BELOW this by the app (the canonical one is `AgentComposer` in
|
|
33
|
+
* `@tangle-network/sandbox-ui`) — this component never renders an input.
|
|
34
|
+
*/
|
|
32
35
|
export interface ChatContainerProps {
|
|
33
36
|
messages: SessionMessage[];
|
|
34
37
|
partMap: Record<string, SessionPart[]>;
|
|
35
38
|
isStreaming: boolean;
|
|
36
|
-
onSend?: (text: string) => void;
|
|
37
|
-
onCancel?: () => void;
|
|
38
39
|
branding?: AgentBranding;
|
|
39
|
-
placeholder?: string;
|
|
40
40
|
className?: string;
|
|
41
|
-
/** Hide the input area (useful for read-only views). */
|
|
42
|
-
hideInput?: boolean;
|
|
43
41
|
/** Custom renderer for tool details. Return ReactNode to override, null to use default. */
|
|
44
42
|
renderToolDetail?: CustomToolRenderer;
|
|
45
43
|
/** Presentation mode for the session view. */
|
|
46
44
|
presentation?: "runs" | "timeline";
|
|
47
|
-
modelLabel?: string;
|
|
48
|
-
onModelClick?: () => void;
|
|
49
|
-
pendingFiles?: PendingFile[];
|
|
50
|
-
onRemoveFile?: (id: string) => void;
|
|
51
|
-
onAttach?: (files: FileList) => void;
|
|
52
|
-
disabled?: boolean;
|
|
53
45
|
/** Callback when an OpenUI action button is pressed within inline OpenUI blocks. */
|
|
54
46
|
onOpenUIAction?: (action: OpenUIAction) => void;
|
|
55
47
|
/** Enable rendering OpenUI schemas inline in the chat timeline. Defaults to true. */
|
|
@@ -355,7 +347,7 @@ function buildTimelineItems(
|
|
|
355
347
|
}
|
|
356
348
|
|
|
357
349
|
/**
|
|
358
|
-
*
|
|
350
|
+
* Chat transcript container: message list + auto-scroll.
|
|
359
351
|
* Orchestrates useRunGroups, useRunCollapseState, and useAutoScroll.
|
|
360
352
|
*/
|
|
361
353
|
export const ChatContainer = memo(
|
|
@@ -363,20 +355,10 @@ export const ChatContainer = memo(
|
|
|
363
355
|
messages,
|
|
364
356
|
partMap,
|
|
365
357
|
isStreaming,
|
|
366
|
-
onSend,
|
|
367
|
-
onCancel,
|
|
368
358
|
branding,
|
|
369
|
-
placeholder = "Type a message...",
|
|
370
359
|
className,
|
|
371
|
-
hideInput = false,
|
|
372
360
|
renderToolDetail,
|
|
373
361
|
presentation = "runs",
|
|
374
|
-
modelLabel,
|
|
375
|
-
onModelClick,
|
|
376
|
-
pendingFiles,
|
|
377
|
-
onRemoveFile,
|
|
378
|
-
onAttach,
|
|
379
|
-
disabled = false,
|
|
380
362
|
onOpenUIAction,
|
|
381
363
|
enableOpenUI = true,
|
|
382
364
|
renderRunActions,
|
|
@@ -404,13 +386,6 @@ export const ChatContainer = memo(
|
|
|
404
386
|
[messages, partMap, isStreaming, onOpenUIAction, enableOpenUI],
|
|
405
387
|
);
|
|
406
388
|
|
|
407
|
-
const handleSend = useCallback(
|
|
408
|
-
(text: string) => {
|
|
409
|
-
onSend?.(text);
|
|
410
|
-
},
|
|
411
|
-
[onSend],
|
|
412
|
-
);
|
|
413
|
-
|
|
414
389
|
return (
|
|
415
390
|
<div className={cn("flex h-full flex-col", className)}>
|
|
416
391
|
{/* Message area */}
|
|
@@ -462,23 +437,6 @@ export const ChatContainer = memo(
|
|
|
462
437
|
</button>
|
|
463
438
|
</div>
|
|
464
439
|
)}
|
|
465
|
-
|
|
466
|
-
{/* Input area */}
|
|
467
|
-
{!hideInput && onSend && (
|
|
468
|
-
<ChatInput
|
|
469
|
-
onSend={handleSend}
|
|
470
|
-
onCancel={onCancel}
|
|
471
|
-
isStreaming={isStreaming}
|
|
472
|
-
placeholder={placeholder}
|
|
473
|
-
modelLabel={modelLabel}
|
|
474
|
-
onModelClick={onModelClick}
|
|
475
|
-
pendingFiles={pendingFiles}
|
|
476
|
-
onRemoveFile={onRemoveFile}
|
|
477
|
-
onAttach={onAttach}
|
|
478
|
-
disabled={disabled}
|
|
479
|
-
className="shrink-0 border-t border-border bg-background"
|
|
480
|
-
/>
|
|
481
|
-
)}
|
|
482
440
|
</div>
|
|
483
441
|
);
|
|
484
442
|
},
|
|
@@ -27,10 +27,6 @@ export interface ChatMessageProps {
|
|
|
27
27
|
assistantLabel?: string;
|
|
28
28
|
/** Hide the role label row entirely */
|
|
29
29
|
hideRoleLabel?: boolean;
|
|
30
|
-
/** @deprecated Avatars were removed from the bubble design; this prop is ignored. */
|
|
31
|
-
hideAvatar?: boolean;
|
|
32
|
-
/** @deprecated Avatars were removed from the bubble design; this prop is ignored. */
|
|
33
|
-
avatar?: ReactNode;
|
|
34
30
|
}
|
|
35
31
|
|
|
36
32
|
export function ChatMessage({
|
package/src/chat/index.ts
CHANGED
|
@@ -2,7 +2,6 @@ export { ChatContainer, type ChatContainerProps } from "./chat-container";
|
|
|
2
2
|
export { MessageList, type MessageListProps } from "./message-list";
|
|
3
3
|
export { UserMessage, type UserMessageProps } from "./user-message";
|
|
4
4
|
export { ChatMessage, type ChatMessageProps, type MessageRole } from "./chat-message";
|
|
5
|
-
export { ChatInput, type ChatInputProps, type PendingFile } from "./chat-input";
|
|
6
5
|
export { ThinkingIndicator, type ThinkingIndicatorProps } from "./thinking-indicator";
|
|
7
6
|
export {
|
|
8
7
|
AgentTimeline,
|
|
@@ -39,7 +39,7 @@ pnpm add @tangle/sandbox-ui
|
|
|
39
39
|
Import the components you need:
|
|
40
40
|
|
|
41
41
|
\`\`\`tsx
|
|
42
|
-
import {
|
|
42
|
+
import { AgentComposer, DropZone, UploadProgress } from '@tangle-network/sandbox-ui'
|
|
43
43
|
\`\`\`
|
|
44
44
|
|
|
45
45
|
> **Note:** Components assume a Tailwind CSS v4 setup with the design tokens
|