@tangle-network/ui 6.0.0 → 8.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 +20 -0
- package/README.md +2 -0
- package/dist/chat.d.ts +8 -72
- package/dist/chat.js +3 -5
- package/dist/{chunk-PN3S2MTV.js → chunk-DLSGUNRD.js} +1 -1
- package/dist/{chunk-O6NUUCT2.js → chunk-IWQZXL6A.js} +1 -1
- package/dist/{chunk-LASW7CYH.js → chunk-QIRVZMQY.js} +11 -23
- package/dist/{chunk-EOGJX2TU.js → chunk-RKQDBRTC.js} +3 -3
- package/dist/{chunk-SJ6IL4HI.js → chunk-UOLL2YHG.js} +27 -373
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7 -13
- package/dist/run.d.ts +1 -1
- package/dist/run.js +5 -9
- package/dist/sdk-hooks.d.ts +1 -1
- package/dist/sdk-hooks.js +3 -3
- package/dist/{tool-call-feed-Bs3MyQMT.d.ts → tool-call-feed-D9iofJgW.d.ts} +1 -23
- package/package.json +2 -2
- package/src/chat/chat-container.tsx +6 -48
- package/src/chat/chat-message.stories.tsx +28 -15
- package/src/chat/chat-message.tsx +13 -40
- package/src/chat/index.ts +0 -1
- package/src/markdown/markdown.stories.tsx +1 -1
- package/src/run/index.ts +4 -1
- package/src/run/inline-tool-item.tsx +5 -5
- package/src/run/run-group.tsx +11 -25
- package/src/run/tool-call-step.tsx +5 -7
- package/src/stories/theme-showcase.stories.tsx +117 -0
- package/src/chat/chat-input.stories.tsx +0 -142
- package/src/chat/chat-input.tsx +0 -389
- package/src/run/tool-call-feed.stories.tsx +0 -294
- package/src/run/tool-call-step.stories.tsx +0 -198
- /package/dist/{chunk-LQS34IGP.js → chunk-47XH56SV.js} +0 -0
package/dist/run.js
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
import "./chunk-
|
|
1
|
+
import "./chunk-47XH56SV.js";
|
|
2
2
|
import {
|
|
3
3
|
ToolCallFeed,
|
|
4
4
|
parseToolEvent
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-IWQZXL6A.js";
|
|
6
6
|
import {
|
|
7
7
|
InlineThinkingItem,
|
|
8
8
|
RunGroup
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-QIRVZMQY.js";
|
|
10
10
|
import {
|
|
11
11
|
ExpandedToolDetail,
|
|
12
12
|
InlineToolItem,
|
|
13
|
-
LiveDuration
|
|
14
|
-
|
|
15
|
-
ToolCallStep
|
|
16
|
-
} from "./chunk-EOGJX2TU.js";
|
|
13
|
+
LiveDuration
|
|
14
|
+
} from "./chunk-RKQDBRTC.js";
|
|
17
15
|
import "./chunk-ULDNFLIM.js";
|
|
18
16
|
import "./chunk-AAUNOHVL.js";
|
|
19
17
|
import "./chunk-52Y3FMFI.js";
|
|
@@ -30,7 +28,5 @@ export {
|
|
|
30
28
|
LiveDuration,
|
|
31
29
|
RunGroup,
|
|
32
30
|
ToolCallFeed,
|
|
33
|
-
ToolCallGroup,
|
|
34
|
-
ToolCallStep,
|
|
35
31
|
parseToolEvent
|
|
36
32
|
};
|
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
|
|
package/dist/sdk-hooks.js
CHANGED
|
@@ -5,15 +5,15 @@ import {
|
|
|
5
5
|
useSSEStream,
|
|
6
6
|
useSdkSession,
|
|
7
7
|
useToolCallStream
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-DLSGUNRD.js";
|
|
9
9
|
import "./chunk-OEX7NZE3.js";
|
|
10
10
|
import {
|
|
11
11
|
useAutoScroll,
|
|
12
12
|
useRunCollapseState,
|
|
13
13
|
useRunGroups
|
|
14
14
|
} from "./chunk-AZWDI2JG.js";
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-IWQZXL6A.js";
|
|
16
|
+
import "./chunk-RKQDBRTC.js";
|
|
17
17
|
import "./chunk-ULDNFLIM.js";
|
|
18
18
|
import "./chunk-AAUNOHVL.js";
|
|
19
19
|
import "./chunk-ZRVH3WCA.js";
|
|
@@ -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.0.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.
|
|
135
|
+
"@tangle-network/brand": "^0.8.0"
|
|
136
136
|
},
|
|
137
137
|
"peerDependenciesMeta": {
|
|
138
138
|
"@nanostores/react": {
|
|
@@ -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
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
2
|
import { ChatMessage } from './chat-message'
|
|
3
|
-
import {
|
|
3
|
+
import { InlineToolItem } from '../run/inline-tool-item'
|
|
4
|
+
import type { ToolPart } from '../types/parts'
|
|
4
5
|
|
|
5
6
|
const meta: Meta<typeof ChatMessage> = {
|
|
6
7
|
title: 'Chat/ChatMessage',
|
|
@@ -26,6 +27,30 @@ type Story = StoryObj<typeof ChatMessage>
|
|
|
26
27
|
const ts = (offsetMinutes = 0) =>
|
|
27
28
|
new Date(Date.now() - offsetMinutes * 60 * 1000)
|
|
28
29
|
|
|
30
|
+
const readToolPart: ToolPart = {
|
|
31
|
+
type: 'tool',
|
|
32
|
+
id: 'chat-read',
|
|
33
|
+
tool: 'read',
|
|
34
|
+
state: {
|
|
35
|
+
status: 'completed',
|
|
36
|
+
input: { file_path: 'src/hooks/useFetchData.ts' },
|
|
37
|
+
output: `export function useFetchData<T>(url: string) {\n const [data, setData] = useState<T | null>(null)\n const [loading, setLoading] = useState(true)\n // ...`,
|
|
38
|
+
time: { start: Date.now() - 1200, end: Date.now() - 1152 },
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const searchToolPart: ToolPart = {
|
|
43
|
+
type: 'tool',
|
|
44
|
+
id: 'chat-search',
|
|
45
|
+
tool: 'grep',
|
|
46
|
+
state: {
|
|
47
|
+
status: 'completed',
|
|
48
|
+
input: { pattern: 'cache', path: 'src/hooks/useFetchData.ts' },
|
|
49
|
+
output: 'No cache layer found. Data fetched on every mount.',
|
|
50
|
+
time: { start: Date.now() - 900, end: Date.now() - 888 },
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
|
|
29
54
|
export const UserMessage: Story = {
|
|
30
55
|
args: {
|
|
31
56
|
role: 'user',
|
|
@@ -117,20 +142,8 @@ export const AssistantWithToolCalls: Story = {
|
|
|
117
142
|
content: 'Let me read the current implementation first.',
|
|
118
143
|
toolCalls: (
|
|
119
144
|
<div className="mt-3 space-y-2">
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
label="Read src/hooks/useFetchData.ts"
|
|
123
|
-
status="success"
|
|
124
|
-
output={`export function useFetchData<T>(url: string) {\n const [data, setData] = useState<T | null>(null)\n const [loading, setLoading] = useState(true)\n // ...`}
|
|
125
|
-
duration={48}
|
|
126
|
-
/>
|
|
127
|
-
<ToolCallStep
|
|
128
|
-
type="grep"
|
|
129
|
-
label="Search for cache references"
|
|
130
|
-
status="success"
|
|
131
|
-
output="No cache layer found. Data fetched on every mount."
|
|
132
|
-
duration={12}
|
|
133
|
-
/>
|
|
145
|
+
<InlineToolItem part={readToolPart} groupPosition="first" />
|
|
146
|
+
<InlineToolItem part={searchToolPart} groupPosition="last" />
|
|
134
147
|
</div>
|
|
135
148
|
),
|
|
136
149
|
timestamp: ts(1),
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { type ReactNode } from "react";
|
|
9
|
-
import { User, Bot } from "lucide-react";
|
|
10
9
|
import { cn } from "../lib/utils";
|
|
11
10
|
import { Markdown } from "../markdown/markdown";
|
|
12
11
|
|
|
@@ -28,10 +27,6 @@ export interface ChatMessageProps {
|
|
|
28
27
|
assistantLabel?: string;
|
|
29
28
|
/** Hide the role label row entirely */
|
|
30
29
|
hideRoleLabel?: boolean;
|
|
31
|
-
/** Hide the avatar icon */
|
|
32
|
-
hideAvatar?: boolean;
|
|
33
|
-
/** Custom avatar element (replaces default User/Bot icon) */
|
|
34
|
-
avatar?: ReactNode;
|
|
35
30
|
}
|
|
36
31
|
|
|
37
32
|
export function ChatMessage({
|
|
@@ -44,36 +39,28 @@ export function ChatMessage({
|
|
|
44
39
|
userLabel = "You",
|
|
45
40
|
assistantLabel = "Agent",
|
|
46
41
|
hideRoleLabel,
|
|
47
|
-
hideAvatar,
|
|
48
|
-
avatar,
|
|
49
42
|
}: ChatMessageProps) {
|
|
50
43
|
const isUser = role === "user";
|
|
51
44
|
|
|
52
45
|
return (
|
|
53
46
|
<div
|
|
54
47
|
className={cn(
|
|
55
|
-
"flex gap-
|
|
56
|
-
isUser ? "
|
|
48
|
+
"flex flex-col gap-1",
|
|
49
|
+
isUser ? "items-end" : "items-start",
|
|
57
50
|
className,
|
|
58
51
|
)}
|
|
59
52
|
>
|
|
60
|
-
{
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
className=
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
: "border-border bg-muted text-[var(--brand-cool)]",
|
|
72
|
-
)}
|
|
73
|
-
>
|
|
74
|
-
{isUser ? <User className="h-3.5 w-3.5" /> : <Bot className="h-3.5 w-3.5" />}
|
|
75
|
-
</div>
|
|
76
|
-
)
|
|
53
|
+
{!hideRoleLabel && (
|
|
54
|
+
<div className={cn("flex items-center gap-2 px-1", isUser && "flex-row-reverse")}>
|
|
55
|
+
<span className="font-medium text-foreground text-xs">
|
|
56
|
+
{isUser ? userLabel : assistantLabel}
|
|
57
|
+
</span>
|
|
58
|
+
{timestamp && (
|
|
59
|
+
<span className="text-muted-foreground text-xs">
|
|
60
|
+
{formatTime(timestamp)}
|
|
61
|
+
</span>
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
77
64
|
)}
|
|
78
65
|
|
|
79
66
|
{/* Bubble */}
|
|
@@ -86,20 +73,6 @@ export function ChatMessage({
|
|
|
86
73
|
: "border-border bg-card",
|
|
87
74
|
)}
|
|
88
75
|
>
|
|
89
|
-
{/* Role label + timestamp */}
|
|
90
|
-
{!hideRoleLabel && (
|
|
91
|
-
<div className={cn("flex items-center gap-2", isUser && "flex-row-reverse")}>
|
|
92
|
-
<span className="text-[var(--font-size-xs)] font-[var(--chat-label-weight,600)] uppercase tracking-[var(--chat-label-tracking,0.14em)] text-foreground">
|
|
93
|
-
{isUser ? userLabel : assistantLabel}
|
|
94
|
-
</span>
|
|
95
|
-
{timestamp && (
|
|
96
|
-
<span className="text-[var(--font-size-xs)] text-muted-foreground">
|
|
97
|
-
{formatTime(timestamp)}
|
|
98
|
-
</span>
|
|
99
|
-
)}
|
|
100
|
-
</div>
|
|
101
|
-
)}
|
|
102
|
-
|
|
103
76
|
{/* Message body */}
|
|
104
77
|
{isUser ? (
|
|
105
78
|
<div className="whitespace-pre-wrap text-[var(--font-size-base)] leading-[var(--line-height-base)] text-foreground">
|
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
|
package/src/run/index.ts
CHANGED
|
@@ -9,5 +9,8 @@ export {
|
|
|
9
9
|
type ExpandedToolDetailProps,
|
|
10
10
|
} from "./expanded-tool-detail";
|
|
11
11
|
export { LiveDuration } from "./run-item-primitives";
|
|
12
|
-
|
|
12
|
+
// ToolCallStep/ToolCallGroup are internal adapters over InlineToolItem (used by
|
|
13
|
+
// AgentTimeline + ToolCallFeed); only their status/type vocabulary is public
|
|
14
|
+
// because ToolCallData references it.
|
|
15
|
+
export { type ToolCallType, type ToolCallStatus } from "./tool-call-step";
|
|
13
16
|
export { ToolCallFeed, parseToolEvent, type ToolCallFeedProps, type ToolCallData, type FeedSegment } from "./tool-call-feed";
|
|
@@ -115,7 +115,7 @@ export const InlineToolItem = memo(
|
|
|
115
115
|
className,
|
|
116
116
|
)}
|
|
117
117
|
>
|
|
118
|
-
<div className="flex items-center gap-2 px-
|
|
118
|
+
<div className="flex items-center gap-2.5 px-3 py-2.5">
|
|
119
119
|
<div className={cn(
|
|
120
120
|
"shrink-0",
|
|
121
121
|
isRunning && "text-primary",
|
|
@@ -151,13 +151,13 @@ export const InlineToolItem = memo(
|
|
|
151
151
|
</span>
|
|
152
152
|
) : null}
|
|
153
153
|
{isError ? (
|
|
154
|
-
<span className="
|
|
155
|
-
|
|
154
|
+
<span className="text-[10px] font-medium text-[var(--surface-danger-text)]">
|
|
155
|
+
failed
|
|
156
156
|
</span>
|
|
157
157
|
) : null}
|
|
158
158
|
{isRunning ? (
|
|
159
|
-
<span className="
|
|
160
|
-
|
|
159
|
+
<span className="text-[10px] font-medium text-primary">
|
|
160
|
+
running
|
|
161
161
|
</span>
|
|
162
162
|
) : null}
|
|
163
163
|
{open ? (
|
package/src/run/run-group.tsx
CHANGED
|
@@ -58,23 +58,17 @@ function AssistantShell({
|
|
|
58
58
|
children: ReactNode;
|
|
59
59
|
}) {
|
|
60
60
|
return (
|
|
61
|
-
<div className="flex gap-
|
|
62
|
-
<div className="
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<span className="inline-flex items-center gap-1.5 text-[var(--text-muted)]">
|
|
71
|
-
<Loader2 className="h-3 w-3 animate-spin" />
|
|
72
|
-
Thinking
|
|
73
|
-
</span>
|
|
74
|
-
) : null}
|
|
75
|
-
</div>
|
|
76
|
-
{children}
|
|
61
|
+
<div className="flex flex-col gap-1">
|
|
62
|
+
<div className="flex items-center gap-2 px-1 font-medium text-muted-foreground text-xs">
|
|
63
|
+
<span>{branding.label}</span>
|
|
64
|
+
{isStreaming ? (
|
|
65
|
+
<span className="inline-flex items-center gap-1.5">
|
|
66
|
+
<Loader2 className="h-3 w-3 animate-spin" />
|
|
67
|
+
Thinking
|
|
68
|
+
</span>
|
|
69
|
+
) : null}
|
|
77
70
|
</div>
|
|
71
|
+
<div className={ASSISTANT_SHELL}>{children}</div>
|
|
78
72
|
</div>
|
|
79
73
|
);
|
|
80
74
|
}
|
|
@@ -411,15 +405,7 @@ export const RunGroup = memo(
|
|
|
411
405
|
)}
|
|
412
406
|
>
|
|
413
407
|
<div className="flex items-center gap-2">
|
|
414
|
-
<
|
|
415
|
-
className={cn(
|
|
416
|
-
"flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-[var(--brand-primary)] text-white",
|
|
417
|
-
)}
|
|
418
|
-
>
|
|
419
|
-
<Bot className="h-3.5 w-3.5" />
|
|
420
|
-
</div>
|
|
421
|
-
|
|
422
|
-
<span className={cn("text-sm font-semibold", branding.textClass)}>
|
|
408
|
+
<span className={cn("font-semibold text-sm", branding.textClass)}>
|
|
423
409
|
{branding.label}
|
|
424
410
|
</span>
|
|
425
411
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ToolCallStep —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `ToolCallFeed`) and the run group share ONE row implementation and one look.
|
|
8
|
-
* The bespoke row markup is gone; only the prop adapter remains.
|
|
2
|
+
* ToolCallStep — internal adapter over the canonical `InlineToolItem` row.
|
|
3
|
+
* Maps flat `ToolCallData`-style props (label / status / detail / output /
|
|
4
|
+
* duration) onto a `ToolPart` so `AgentTimeline` and `ToolCallFeed` share the
|
|
5
|
+
* one row implementation. Not exported publicly; only the `ToolCallType` /
|
|
6
|
+
* `ToolCallStatus` vocabulary is (via `ToolCallData`).
|
|
9
7
|
*/
|
|
10
8
|
|
|
11
9
|
import { type ReactNode } from "react";
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { InlineToolItem } from "../run/inline-tool-item";
|
|
3
|
+
import type { ToolPart } from "../types/parts";
|
|
4
|
+
|
|
5
|
+
const meta: Meta = {
|
|
6
|
+
title: "Foundations/Theme Showcase",
|
|
7
|
+
parameters: { layout: "fullscreen" },
|
|
8
|
+
};
|
|
9
|
+
export default meta;
|
|
10
|
+
type Story = StoryObj;
|
|
11
|
+
|
|
12
|
+
const THEMES: { name: string; theme?: string }[] = [
|
|
13
|
+
{ name: "Tangle (neutral)", theme: undefined }, // default :root dark
|
|
14
|
+
{ name: "Tangle · light", theme: "tangle-light" },
|
|
15
|
+
{ name: "Aubergine (bazaar)", theme: "aubergine" },
|
|
16
|
+
{ name: "Aubergine · light", theme: "aubergine-light" },
|
|
17
|
+
{ name: "Experimental green", theme: "arena" },
|
|
18
|
+
{ name: "Experimental green · light", theme: "arena-light" },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const NOW = Date.now();
|
|
22
|
+
|
|
23
|
+
const themeToolParts: ToolPart[] = [
|
|
24
|
+
{
|
|
25
|
+
type: "tool",
|
|
26
|
+
id: "theme-read",
|
|
27
|
+
tool: "read",
|
|
28
|
+
state: {
|
|
29
|
+
status: "completed",
|
|
30
|
+
input: { file_path: "src/batch-writer.ts" },
|
|
31
|
+
time: { start: NOW - 3100, end: NOW - 2500 },
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
type: "tool",
|
|
36
|
+
id: "theme-search",
|
|
37
|
+
tool: "grep",
|
|
38
|
+
state: {
|
|
39
|
+
status: "completed",
|
|
40
|
+
input: { pattern: "await sleep\\(" },
|
|
41
|
+
time: { start: NOW - 2200, end: NOW - 1800 },
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
type: "tool",
|
|
46
|
+
id: "theme-build",
|
|
47
|
+
tool: "bash",
|
|
48
|
+
state: {
|
|
49
|
+
status: "error",
|
|
50
|
+
input: { command: "pnpm test" },
|
|
51
|
+
error: "1 failing test",
|
|
52
|
+
time: { start: NOW - 1600, end: NOW - 400 },
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
function Cell({ name, theme }: { name: string; theme?: string }) {
|
|
58
|
+
return (
|
|
59
|
+
<div
|
|
60
|
+
data-theme={theme}
|
|
61
|
+
className="flex flex-col gap-3 rounded-2xl border border-border bg-background p-5"
|
|
62
|
+
>
|
|
63
|
+
<div className="flex items-center justify-between">
|
|
64
|
+
<span className="font-semibold text-foreground text-sm">{name}</span>
|
|
65
|
+
<span className="font-medium text-muted-foreground text-xs">
|
|
66
|
+
{theme?.includes("light") ? "Light" : "Dark"}
|
|
67
|
+
</span>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div className="space-y-1.5 rounded-xl border border-[var(--border-subtle)] bg-surface-container-high p-3">
|
|
71
|
+
<div className="flex items-center gap-2">
|
|
72
|
+
<span className="size-2 rounded-full bg-primary" />
|
|
73
|
+
<span className="font-semibold text-foreground text-xs">Agent</span>
|
|
74
|
+
<span className="text-[11px] text-muted-foreground">4 tools · 3s</span>
|
|
75
|
+
</div>
|
|
76
|
+
{themeToolParts.map((part, index) => (
|
|
77
|
+
<InlineToolItem
|
|
78
|
+
key={part.id}
|
|
79
|
+
part={part}
|
|
80
|
+
groupPosition={index === 0 ? "first" : index === themeToolParts.length - 1 ? "last" : "middle"}
|
|
81
|
+
/>
|
|
82
|
+
))}
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<p className="text-muted-foreground text-xs leading-relaxed">
|
|
86
|
+
Body copy on the canvas. Surfaces separate by fill; the border is a quiet edge.
|
|
87
|
+
</p>
|
|
88
|
+
|
|
89
|
+
<div className="flex items-center gap-2">
|
|
90
|
+
<button
|
|
91
|
+
type="button"
|
|
92
|
+
className="rounded-lg bg-primary px-3 py-1.5 font-medium text-primary-foreground text-xs"
|
|
93
|
+
>
|
|
94
|
+
Primary
|
|
95
|
+
</button>
|
|
96
|
+
<button
|
|
97
|
+
type="button"
|
|
98
|
+
className="rounded-lg border border-border bg-muted px-3 py-1.5 font-medium text-foreground text-xs"
|
|
99
|
+
>
|
|
100
|
+
Secondary
|
|
101
|
+
</button>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const AllThemes: Story = {
|
|
108
|
+
render: () => (
|
|
109
|
+
<div className="min-h-screen bg-[#0a0a0c] p-6">
|
|
110
|
+
<div className="grid grid-cols-1 gap-5 md:grid-cols-2">
|
|
111
|
+
{THEMES.map((t) => (
|
|
112
|
+
<Cell key={t.name} name={t.name} theme={t.theme} />
|
|
113
|
+
))}
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
),
|
|
117
|
+
};
|