@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,542 @@
|
|
|
1
|
+
import { Fragment, type ReactNode } from "react";
|
|
2
|
+
import { Minus } from "lucide-react";
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
import { Badge } from "../primitives/badge";
|
|
5
|
+
import { Button } from "../primitives/button";
|
|
6
|
+
import {
|
|
7
|
+
Card,
|
|
8
|
+
CardContent,
|
|
9
|
+
CardDescription,
|
|
10
|
+
CardHeader,
|
|
11
|
+
CardTitle,
|
|
12
|
+
} from "../primitives/card";
|
|
13
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../primitives/table";
|
|
14
|
+
import { CodeBlock } from "../primitives/code-block";
|
|
15
|
+
import { Markdown } from "../markdown/markdown";
|
|
16
|
+
|
|
17
|
+
export type OpenUIPrimitive = string | number | boolean | null | undefined;
|
|
18
|
+
|
|
19
|
+
export interface OpenUIAction {
|
|
20
|
+
id: string;
|
|
21
|
+
label: string;
|
|
22
|
+
tone?: "default" | "secondary" | "outline" | "ghost" | "destructive";
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
onPress?: () => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface OpenUIBaseNode {
|
|
28
|
+
id?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface OpenUIHeadingNode extends OpenUIBaseNode {
|
|
32
|
+
type: "heading";
|
|
33
|
+
text: string;
|
|
34
|
+
level?: 1 | 2 | 3 | 4;
|
|
35
|
+
kicker?: string;
|
|
36
|
+
meta?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface OpenUITextNode extends OpenUIBaseNode {
|
|
40
|
+
type: "text";
|
|
41
|
+
text: string;
|
|
42
|
+
tone?: "default" | "muted" | "success" | "warning" | "error";
|
|
43
|
+
mono?: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface OpenUIBadgeNode extends OpenUIBaseNode {
|
|
47
|
+
type: "badge";
|
|
48
|
+
label: string;
|
|
49
|
+
tone?: "default" | "secondary" | "success" | "warning" | "error" | "info" | "sandbox";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface OpenUIStatNode extends OpenUIBaseNode {
|
|
53
|
+
type: "stat";
|
|
54
|
+
label: string;
|
|
55
|
+
value: string;
|
|
56
|
+
change?: string;
|
|
57
|
+
tone?: "default" | "success" | "warning" | "error" | "info";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface OpenUIKeyValueNode extends OpenUIBaseNode {
|
|
61
|
+
type: "key_value";
|
|
62
|
+
items: Array<{
|
|
63
|
+
id?: string;
|
|
64
|
+
label: string;
|
|
65
|
+
value: ReactNode | OpenUIPrimitive;
|
|
66
|
+
tone?: "default" | "muted";
|
|
67
|
+
}>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface OpenUICodeNode extends OpenUIBaseNode {
|
|
71
|
+
type: "code";
|
|
72
|
+
code: string;
|
|
73
|
+
language?: string;
|
|
74
|
+
title?: string;
|
|
75
|
+
showLineNumbers?: boolean;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface OpenUIMarkdownNode extends OpenUIBaseNode {
|
|
79
|
+
type: "markdown";
|
|
80
|
+
content: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface OpenUITableNode extends OpenUIBaseNode {
|
|
84
|
+
type: "table";
|
|
85
|
+
columns: Array<{
|
|
86
|
+
key: string;
|
|
87
|
+
header: string;
|
|
88
|
+
align?: "left" | "right";
|
|
89
|
+
}>;
|
|
90
|
+
rows: Array<Record<string, ReactNode | OpenUIPrimitive>>;
|
|
91
|
+
caption?: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface OpenUIActionsNode extends OpenUIBaseNode {
|
|
95
|
+
type: "actions";
|
|
96
|
+
actions: OpenUIAction[];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface OpenUISeparatorNode extends OpenUIBaseNode {
|
|
100
|
+
type: "separator";
|
|
101
|
+
label?: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface OpenUIStackNode extends OpenUIBaseNode {
|
|
105
|
+
type: "stack";
|
|
106
|
+
direction?: "row" | "column";
|
|
107
|
+
gap?: "sm" | "md" | "lg";
|
|
108
|
+
align?: "start" | "center" | "end" | "stretch";
|
|
109
|
+
wrap?: boolean;
|
|
110
|
+
children: OpenUIComponentNode[];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface OpenUIGridNode extends OpenUIBaseNode {
|
|
114
|
+
type: "grid";
|
|
115
|
+
columns?: 1 | 2 | 3 | 4;
|
|
116
|
+
gap?: "sm" | "md" | "lg";
|
|
117
|
+
children: OpenUIComponentNode[];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface OpenUICardNode extends OpenUIBaseNode {
|
|
121
|
+
type: "card";
|
|
122
|
+
title?: string;
|
|
123
|
+
description?: string;
|
|
124
|
+
eyebrow?: string;
|
|
125
|
+
badge?: OpenUIBadgeNode;
|
|
126
|
+
actions?: OpenUIAction[];
|
|
127
|
+
children?: OpenUIComponentNode[];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export type OpenUIComponentNode =
|
|
131
|
+
| OpenUIActionsNode
|
|
132
|
+
| OpenUIBadgeNode
|
|
133
|
+
| OpenUICardNode
|
|
134
|
+
| OpenUICodeNode
|
|
135
|
+
| OpenUIGridNode
|
|
136
|
+
| OpenUIHeadingNode
|
|
137
|
+
| OpenUIKeyValueNode
|
|
138
|
+
| OpenUIMarkdownNode
|
|
139
|
+
| OpenUISeparatorNode
|
|
140
|
+
| OpenUIStackNode
|
|
141
|
+
| OpenUIStatNode
|
|
142
|
+
| OpenUITableNode
|
|
143
|
+
| OpenUITextNode;
|
|
144
|
+
|
|
145
|
+
export interface OpenUIArtifactRendererProps {
|
|
146
|
+
schema: OpenUIComponentNode | OpenUIComponentNode[];
|
|
147
|
+
onAction?: (action: OpenUIAction) => void;
|
|
148
|
+
className?: string;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const NODE_TYPES = new Set([
|
|
152
|
+
"heading",
|
|
153
|
+
"text",
|
|
154
|
+
"badge",
|
|
155
|
+
"stat",
|
|
156
|
+
"key_value",
|
|
157
|
+
"code",
|
|
158
|
+
"markdown",
|
|
159
|
+
"table",
|
|
160
|
+
"actions",
|
|
161
|
+
"separator",
|
|
162
|
+
"stack",
|
|
163
|
+
"grid",
|
|
164
|
+
"card",
|
|
165
|
+
]);
|
|
166
|
+
|
|
167
|
+
const GAP_STYLES = {
|
|
168
|
+
sm: "gap-2",
|
|
169
|
+
md: "gap-4",
|
|
170
|
+
lg: "gap-6",
|
|
171
|
+
} as const;
|
|
172
|
+
|
|
173
|
+
const GRID_STYLES = {
|
|
174
|
+
1: "grid-cols-1",
|
|
175
|
+
2: "grid-cols-1 md:grid-cols-2",
|
|
176
|
+
3: "grid-cols-1 md:grid-cols-3",
|
|
177
|
+
4: "grid-cols-1 md:grid-cols-2 xl:grid-cols-4",
|
|
178
|
+
} as const;
|
|
179
|
+
|
|
180
|
+
const ALIGN_STYLES = {
|
|
181
|
+
start: "items-start",
|
|
182
|
+
center: "items-center",
|
|
183
|
+
end: "items-end",
|
|
184
|
+
stretch: "items-stretch",
|
|
185
|
+
} as const;
|
|
186
|
+
|
|
187
|
+
function formatValue(value: ReactNode | OpenUIPrimitive) {
|
|
188
|
+
if (value === null || value === undefined) {
|
|
189
|
+
return <span className="text-muted-foreground">—</span>;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (typeof value === "boolean") {
|
|
193
|
+
return value ? "Yes" : "No";
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return value;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function asArray<T>(value: unknown): T[] {
|
|
200
|
+
return Array.isArray(value) ? (value as T[]) : [];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function isOpenUIComponentNode(value: unknown): value is OpenUIComponentNode {
|
|
204
|
+
return (
|
|
205
|
+
typeof value === "object" &&
|
|
206
|
+
value !== null &&
|
|
207
|
+
"type" in value &&
|
|
208
|
+
typeof (value as { type?: unknown }).type === "string" &&
|
|
209
|
+
NODE_TYPES.has((value as { type: string }).type)
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function renderActions(actions: OpenUIAction[], onAction?: (action: OpenUIAction) => void) {
|
|
214
|
+
if (actions.length === 0) return null;
|
|
215
|
+
|
|
216
|
+
return (
|
|
217
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
218
|
+
{actions.map((action) => (
|
|
219
|
+
<Button
|
|
220
|
+
key={action.id}
|
|
221
|
+
type="button"
|
|
222
|
+
size="sm"
|
|
223
|
+
variant={action.tone ?? "outline"}
|
|
224
|
+
disabled={action.disabled}
|
|
225
|
+
onClick={() => {
|
|
226
|
+
action.onPress?.();
|
|
227
|
+
onAction?.(action);
|
|
228
|
+
}}
|
|
229
|
+
>
|
|
230
|
+
{action.label}
|
|
231
|
+
</Button>
|
|
232
|
+
))}
|
|
233
|
+
</div>
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function renderNode(node: OpenUIComponentNode, onAction?: (action: OpenUIAction) => void): ReactNode {
|
|
238
|
+
switch (node.type) {
|
|
239
|
+
case "heading": {
|
|
240
|
+
const level = node.level ?? 2;
|
|
241
|
+
const HeadingTag = `h${level}` as const;
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
<div className="space-y-1">
|
|
245
|
+
{node.kicker && (
|
|
246
|
+
<div className="text-[10px] font-semibold uppercase tracking-[0.16em] text-muted-foreground">
|
|
247
|
+
{node.kicker}
|
|
248
|
+
</div>
|
|
249
|
+
)}
|
|
250
|
+
<HeadingTag
|
|
251
|
+
className={cn(
|
|
252
|
+
"font-semibold tracking-tight text-foreground",
|
|
253
|
+
level === 1 && "text-2xl",
|
|
254
|
+
level === 2 && "text-xl",
|
|
255
|
+
level === 3 && "text-lg",
|
|
256
|
+
level === 4 && "text-base",
|
|
257
|
+
)}
|
|
258
|
+
>
|
|
259
|
+
{node.text}
|
|
260
|
+
</HeadingTag>
|
|
261
|
+
{node.meta && <p className="text-sm text-muted-foreground">{node.meta}</p>}
|
|
262
|
+
</div>
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
case "text":
|
|
267
|
+
return (
|
|
268
|
+
<p
|
|
269
|
+
className={cn(
|
|
270
|
+
"text-sm leading-6 text-foreground",
|
|
271
|
+
node.tone === "muted" && "text-muted-foreground",
|
|
272
|
+
node.tone === "success" && "text-[var(--surface-success-text)]",
|
|
273
|
+
node.tone === "warning" && "text-[var(--surface-warning-text)]",
|
|
274
|
+
node.tone === "error" && "text-[var(--surface-danger-text)]",
|
|
275
|
+
node.mono && "font-mono text-[13px]",
|
|
276
|
+
)}
|
|
277
|
+
>
|
|
278
|
+
{node.text}
|
|
279
|
+
</p>
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
case "badge":
|
|
283
|
+
return <Badge variant={node.tone ?? "outline"}>{node.label}</Badge>;
|
|
284
|
+
|
|
285
|
+
case "stat":
|
|
286
|
+
return (
|
|
287
|
+
<Card variant="glass" className="border-border shadow-[var(--shadow-card)]">
|
|
288
|
+
<CardContent className="space-y-2 p-4">
|
|
289
|
+
<div className="text-xs uppercase tracking-[0.12em] text-muted-foreground">
|
|
290
|
+
{node.label}
|
|
291
|
+
</div>
|
|
292
|
+
<div className="text-2xl font-semibold tracking-tight text-foreground">
|
|
293
|
+
{node.value}
|
|
294
|
+
</div>
|
|
295
|
+
{node.change && (
|
|
296
|
+
<div
|
|
297
|
+
className={cn(
|
|
298
|
+
"text-xs",
|
|
299
|
+
node.tone === "success" && "text-[var(--surface-success-text)]",
|
|
300
|
+
node.tone === "warning" && "text-[var(--surface-warning-text)]",
|
|
301
|
+
node.tone === "error" && "text-[var(--surface-danger-text)]",
|
|
302
|
+
node.tone === "info" && "text-[var(--surface-info-text)]",
|
|
303
|
+
!node.tone || node.tone === "default" ? "text-muted-foreground" : undefined,
|
|
304
|
+
)}
|
|
305
|
+
>
|
|
306
|
+
{node.change}
|
|
307
|
+
</div>
|
|
308
|
+
)}
|
|
309
|
+
</CardContent>
|
|
310
|
+
</Card>
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
case "key_value":
|
|
314
|
+
if (asArray(node.items).length === 0) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
return (
|
|
318
|
+
<dl className="grid gap-3 sm:grid-cols-2">
|
|
319
|
+
{asArray<OpenUIKeyValueNode["items"][number]>(node.items).map((item, index) => (
|
|
320
|
+
<div
|
|
321
|
+
key={item.id ?? `${item.label}-${index}`}
|
|
322
|
+
className="rounded-[var(--radius-lg)] border border-border bg-card px-4 py-3"
|
|
323
|
+
>
|
|
324
|
+
<dt className="text-[11px] uppercase tracking-[0.12em] text-muted-foreground">
|
|
325
|
+
{item.label}
|
|
326
|
+
</dt>
|
|
327
|
+
<dd
|
|
328
|
+
className={cn(
|
|
329
|
+
"mt-1 text-sm font-medium text-foreground",
|
|
330
|
+
item.tone === "muted" && "text-foreground",
|
|
331
|
+
)}
|
|
332
|
+
>
|
|
333
|
+
{formatValue(item.value)}
|
|
334
|
+
</dd>
|
|
335
|
+
</div>
|
|
336
|
+
))}
|
|
337
|
+
</dl>
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
case "code":
|
|
341
|
+
return (
|
|
342
|
+
<div className="space-y-2">
|
|
343
|
+
{node.title && (
|
|
344
|
+
<div className="text-xs uppercase tracking-[0.12em] text-muted-foreground">
|
|
345
|
+
{node.title}
|
|
346
|
+
</div>
|
|
347
|
+
)}
|
|
348
|
+
<CodeBlock
|
|
349
|
+
code={node.code}
|
|
350
|
+
language={node.language}
|
|
351
|
+
showLineNumbers={node.showLineNumbers}
|
|
352
|
+
className="border-border bg-background"
|
|
353
|
+
/>
|
|
354
|
+
</div>
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
case "markdown":
|
|
358
|
+
return (
|
|
359
|
+
<div className="rounded-[var(--radius-lg)] border border-border bg-card p-5">
|
|
360
|
+
<Markdown className="prose-sm max-w-none">{node.content}</Markdown>
|
|
361
|
+
</div>
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
case "table":
|
|
365
|
+
if (asArray(node.columns).length === 0) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
return (
|
|
369
|
+
<div className="overflow-hidden rounded-[var(--radius-lg)] border border-border bg-card">
|
|
370
|
+
<Table>
|
|
371
|
+
<TableHeader>
|
|
372
|
+
<TableRow className="border-border">
|
|
373
|
+
{asArray<OpenUITableNode["columns"][number]>(node.columns).map((column) => (
|
|
374
|
+
<TableHead
|
|
375
|
+
key={column.key}
|
|
376
|
+
className={cn(
|
|
377
|
+
"h-10 text-[11px] uppercase tracking-[0.1em] text-muted-foreground",
|
|
378
|
+
column.align === "right" && "text-right",
|
|
379
|
+
)}
|
|
380
|
+
>
|
|
381
|
+
{column.header}
|
|
382
|
+
</TableHead>
|
|
383
|
+
))}
|
|
384
|
+
</TableRow>
|
|
385
|
+
</TableHeader>
|
|
386
|
+
<TableBody>
|
|
387
|
+
{asArray<OpenUITableNode["rows"][number]>(node.rows).map((row, rowIndex) => (
|
|
388
|
+
<TableRow key={rowIndex} className="border-border">
|
|
389
|
+
{asArray<OpenUITableNode["columns"][number]>(node.columns).map((column) => (
|
|
390
|
+
<TableCell
|
|
391
|
+
key={column.key}
|
|
392
|
+
className={cn(
|
|
393
|
+
"py-3 text-sm text-foreground",
|
|
394
|
+
column.align === "right" && "text-right tabular-nums",
|
|
395
|
+
)}
|
|
396
|
+
>
|
|
397
|
+
{formatValue(row[column.key])}
|
|
398
|
+
</TableCell>
|
|
399
|
+
))}
|
|
400
|
+
</TableRow>
|
|
401
|
+
))}
|
|
402
|
+
</TableBody>
|
|
403
|
+
</Table>
|
|
404
|
+
{node.caption && (
|
|
405
|
+
<div className="border-t border-border px-4 py-2 text-xs text-muted-foreground">
|
|
406
|
+
{node.caption}
|
|
407
|
+
</div>
|
|
408
|
+
)}
|
|
409
|
+
</div>
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
case "actions":
|
|
413
|
+
return renderActions(asArray<OpenUIAction>(node.actions), onAction);
|
|
414
|
+
|
|
415
|
+
case "separator":
|
|
416
|
+
return (
|
|
417
|
+
<div className="flex items-center gap-3">
|
|
418
|
+
<div className="h-px flex-1 bg-border" />
|
|
419
|
+
{node.label && (
|
|
420
|
+
<span className="text-[10px] uppercase tracking-[0.14em] text-muted-foreground">
|
|
421
|
+
{node.label}
|
|
422
|
+
</span>
|
|
423
|
+
)}
|
|
424
|
+
<div className="h-px flex-1 bg-border" />
|
|
425
|
+
</div>
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
case "stack":
|
|
429
|
+
if (asArray(node.children).length === 0) {
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
return (
|
|
433
|
+
<div
|
|
434
|
+
className={cn(
|
|
435
|
+
"flex",
|
|
436
|
+
node.direction === "row" ? "flex-row" : "flex-col",
|
|
437
|
+
GAP_STYLES[node.gap ?? "md"],
|
|
438
|
+
ALIGN_STYLES[node.align ?? "stretch"],
|
|
439
|
+
node.wrap && "flex-wrap",
|
|
440
|
+
)}
|
|
441
|
+
>
|
|
442
|
+
{asArray<OpenUIComponentNode>(node.children).map((child, index) => (
|
|
443
|
+
<Fragment key={child.id ?? `${child.type}-${index}`}>
|
|
444
|
+
{renderNode(child, onAction)}
|
|
445
|
+
</Fragment>
|
|
446
|
+
))}
|
|
447
|
+
</div>
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
case "grid":
|
|
451
|
+
if (asArray(node.children).length === 0) {
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
return (
|
|
455
|
+
<div className={cn("grid", GRID_STYLES[node.columns ?? 2], GAP_STYLES[node.gap ?? "md"])}>
|
|
456
|
+
{asArray<OpenUIComponentNode>(node.children).map((child, index) => (
|
|
457
|
+
<Fragment key={child.id ?? `${child.type}-${index}`}>
|
|
458
|
+
{renderNode(child, onAction)}
|
|
459
|
+
</Fragment>
|
|
460
|
+
))}
|
|
461
|
+
</div>
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
case "card":
|
|
465
|
+
return (
|
|
466
|
+
<Card variant="glass" className="border-border shadow-[var(--shadow-card)]">
|
|
467
|
+
{(node.eyebrow || node.title || node.description || node.badge || asArray(node.actions).length > 0) && (
|
|
468
|
+
<CardHeader className="gap-2 p-4 pb-0">
|
|
469
|
+
<div className="flex items-start justify-between gap-3">
|
|
470
|
+
<div className="min-w-0 flex-1 space-y-1">
|
|
471
|
+
{node.eyebrow && (
|
|
472
|
+
<div className="text-[10px] font-semibold uppercase tracking-[0.16em] text-muted-foreground">
|
|
473
|
+
{node.eyebrow}
|
|
474
|
+
</div>
|
|
475
|
+
)}
|
|
476
|
+
{node.title && <CardTitle className="text-base text-foreground">{node.title}</CardTitle>}
|
|
477
|
+
{node.description && (
|
|
478
|
+
<CardDescription className="text-muted-foreground">
|
|
479
|
+
{node.description}
|
|
480
|
+
</CardDescription>
|
|
481
|
+
)}
|
|
482
|
+
</div>
|
|
483
|
+
{node.badge && <Badge variant={node.badge.tone ?? "outline"}>{node.badge.label}</Badge>}
|
|
484
|
+
</div>
|
|
485
|
+
{asArray(node.actions).length > 0 && renderActions(asArray<OpenUIAction>(node.actions), onAction)}
|
|
486
|
+
</CardHeader>
|
|
487
|
+
)}
|
|
488
|
+
{asArray(node.children).length > 0 && (
|
|
489
|
+
<CardContent className="space-y-4 p-4">
|
|
490
|
+
{asArray<OpenUIComponentNode>(node.children).map((child, index) => (
|
|
491
|
+
<Fragment key={child.id ?? `${child.type}-${index}`}>
|
|
492
|
+
{renderNode(child, onAction)}
|
|
493
|
+
</Fragment>
|
|
494
|
+
))}
|
|
495
|
+
</CardContent>
|
|
496
|
+
)}
|
|
497
|
+
</Card>
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* OpenUIArtifactRenderer — contained renderer for OpenUI-like structured
|
|
504
|
+
* artifact payloads using sandbox-ui primitives and theme tokens. This is an
|
|
505
|
+
* extension surface for generated inspectors/results, not a replacement for
|
|
506
|
+
* the sandbox shell.
|
|
507
|
+
*/
|
|
508
|
+
export function OpenUIArtifactRenderer({
|
|
509
|
+
schema,
|
|
510
|
+
onAction,
|
|
511
|
+
className,
|
|
512
|
+
}: OpenUIArtifactRendererProps) {
|
|
513
|
+
const nodes = (Array.isArray(schema) ? schema : [schema]).filter(isOpenUIComponentNode);
|
|
514
|
+
|
|
515
|
+
if (nodes.length === 0) {
|
|
516
|
+
return (
|
|
517
|
+
<div
|
|
518
|
+
className={cn(
|
|
519
|
+
"flex min-h-[6rem] items-center justify-center rounded-[var(--radius-xl)] border border-dashed border-border bg-card p-5 text-center",
|
|
520
|
+
className,
|
|
521
|
+
)}
|
|
522
|
+
>
|
|
523
|
+
<div className="space-y-2">
|
|
524
|
+
<div className="mx-auto flex h-10 w-10 items-center justify-center rounded-full bg-muted/50 text-muted-foreground">
|
|
525
|
+
<Minus className="h-4 w-4" />
|
|
526
|
+
</div>
|
|
527
|
+
<div className="text-sm font-medium text-foreground">No view was generated.</div>
|
|
528
|
+
</div>
|
|
529
|
+
</div>
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return (
|
|
534
|
+
<div className={cn("space-y-4", className)}>
|
|
535
|
+
{nodes.map((node, index) => (
|
|
536
|
+
<Fragment key={node.id ?? `${node.type}-${index}`}>
|
|
537
|
+
{renderNode(node, onAction)}
|
|
538
|
+
</Fragment>
|
|
539
|
+
))}
|
|
540
|
+
</div>
|
|
541
|
+
);
|
|
542
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
|
|
4
|
+
export interface ArtifactPaneProps {
|
|
5
|
+
eyebrow?: ReactNode;
|
|
6
|
+
title: ReactNode;
|
|
7
|
+
subtitle?: ReactNode;
|
|
8
|
+
meta?: ReactNode;
|
|
9
|
+
headerActions?: ReactNode;
|
|
10
|
+
tabs?: ReactNode;
|
|
11
|
+
toolbar?: ReactNode;
|
|
12
|
+
footer?: ReactNode;
|
|
13
|
+
emptyState?: ReactNode;
|
|
14
|
+
children?: ReactNode;
|
|
15
|
+
className?: string;
|
|
16
|
+
contentClassName?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* ArtifactPane — shared frame for files, previews, documents, inspectors, and
|
|
21
|
+
* other artifact-like surfaces inside sandbox applications.
|
|
22
|
+
*/
|
|
23
|
+
export function ArtifactPane({
|
|
24
|
+
eyebrow,
|
|
25
|
+
title,
|
|
26
|
+
subtitle,
|
|
27
|
+
meta,
|
|
28
|
+
headerActions,
|
|
29
|
+
tabs,
|
|
30
|
+
toolbar,
|
|
31
|
+
footer,
|
|
32
|
+
emptyState,
|
|
33
|
+
children,
|
|
34
|
+
className,
|
|
35
|
+
contentClassName,
|
|
36
|
+
}: ArtifactPaneProps) {
|
|
37
|
+
const hasContent = children !== undefined && children !== null;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<section
|
|
41
|
+
className={cn(
|
|
42
|
+
"flex h-full min-h-0 flex-col overflow-hidden bg-background text-foreground",
|
|
43
|
+
className,
|
|
44
|
+
)}
|
|
45
|
+
>
|
|
46
|
+
<header className="border-b border-border bg-muted/10">
|
|
47
|
+
<div className="flex items-start justify-between gap-3 px-3 py-2">
|
|
48
|
+
<div className="min-w-0 flex-1">
|
|
49
|
+
{eyebrow && (
|
|
50
|
+
<div className="mb-1 inline-flex max-w-full items-center px-1 py-0.5 text-[10px] font-bold uppercase tracking-wider text-muted-foreground">
|
|
51
|
+
{eyebrow}
|
|
52
|
+
</div>
|
|
53
|
+
)}
|
|
54
|
+
<div className="min-w-0 text-[13px] font-medium text-foreground">
|
|
55
|
+
{title}
|
|
56
|
+
</div>
|
|
57
|
+
{(subtitle || meta) && (
|
|
58
|
+
<div className="mt-1 flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-xs leading-relaxed text-muted-foreground">
|
|
59
|
+
{subtitle && <span className="truncate">{subtitle}</span>}
|
|
60
|
+
{meta && <span className="flex items-center gap-2">{meta}</span>}
|
|
61
|
+
</div>
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
64
|
+
{headerActions && (
|
|
65
|
+
<div className="flex shrink-0 items-center gap-1.5">{headerActions}</div>
|
|
66
|
+
)}
|
|
67
|
+
</div>
|
|
68
|
+
{tabs}
|
|
69
|
+
{toolbar && (
|
|
70
|
+
<div className="border-t border-border px-3 py-2">
|
|
71
|
+
{toolbar}
|
|
72
|
+
</div>
|
|
73
|
+
)}
|
|
74
|
+
</header>
|
|
75
|
+
|
|
76
|
+
<div className={cn("min-h-0 flex-1 overflow-auto", contentClassName)}>
|
|
77
|
+
{hasContent ? (
|
|
78
|
+
children
|
|
79
|
+
) : emptyState ? (
|
|
80
|
+
<div className="flex h-full items-center justify-center p-6">{emptyState}</div>
|
|
81
|
+
) : null}
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
{footer && (
|
|
85
|
+
<footer className="shrink-0 border-t border-border bg-card px-3 py-2">
|
|
86
|
+
{footer}
|
|
87
|
+
</footer>
|
|
88
|
+
)}
|
|
89
|
+
</section>
|
|
90
|
+
);
|
|
91
|
+
}
|