@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,186 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CodeBlock
|
|
3
|
+
} from "./chunk-66BNMOVT.js";
|
|
4
|
+
import {
|
|
5
|
+
cn
|
|
6
|
+
} from "./chunk-RQHJBTEU.js";
|
|
7
|
+
|
|
8
|
+
// src/run/tool-call-step.tsx
|
|
9
|
+
import { useState } from "react";
|
|
10
|
+
import {
|
|
11
|
+
Terminal,
|
|
12
|
+
FileText,
|
|
13
|
+
FileCode,
|
|
14
|
+
Search,
|
|
15
|
+
CheckCircle,
|
|
16
|
+
ChevronRight,
|
|
17
|
+
Loader2,
|
|
18
|
+
FolderOpen,
|
|
19
|
+
Download,
|
|
20
|
+
Pencil,
|
|
21
|
+
Eye
|
|
22
|
+
} from "lucide-react";
|
|
23
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
24
|
+
var EXT_LANGUAGE = {
|
|
25
|
+
ts: "typescript",
|
|
26
|
+
tsx: "typescript",
|
|
27
|
+
js: "javascript",
|
|
28
|
+
jsx: "javascript",
|
|
29
|
+
mjs: "javascript",
|
|
30
|
+
cjs: "javascript",
|
|
31
|
+
css: "css",
|
|
32
|
+
scss: "scss",
|
|
33
|
+
json: "json",
|
|
34
|
+
jsonc: "json",
|
|
35
|
+
md: "markdown",
|
|
36
|
+
mdx: "markdown",
|
|
37
|
+
py: "python",
|
|
38
|
+
sh: "bash",
|
|
39
|
+
bash: "bash",
|
|
40
|
+
zsh: "bash",
|
|
41
|
+
html: "html",
|
|
42
|
+
htm: "html",
|
|
43
|
+
yaml: "yaml",
|
|
44
|
+
yml: "yaml",
|
|
45
|
+
toml: "toml",
|
|
46
|
+
rs: "rust",
|
|
47
|
+
go: "go",
|
|
48
|
+
sql: "sql",
|
|
49
|
+
xml: "xml"
|
|
50
|
+
};
|
|
51
|
+
function inferLanguage(detail, language) {
|
|
52
|
+
if (language) return language;
|
|
53
|
+
if (!detail) return void 0;
|
|
54
|
+
const ext = detail.split(".").pop()?.toLowerCase();
|
|
55
|
+
return ext ? EXT_LANGUAGE[ext] : void 0;
|
|
56
|
+
}
|
|
57
|
+
function isFilePath(detail) {
|
|
58
|
+
return /[/\\]/.test(detail) || /\.\w{1,6}$/.test(detail);
|
|
59
|
+
}
|
|
60
|
+
function FilePathChip({ path }) {
|
|
61
|
+
const parts = path.replace(/\\/g, "/").split("/");
|
|
62
|
+
const filename = parts.pop() ?? path;
|
|
63
|
+
const dir = parts.length > 0 ? parts.join("/") + "/" : "";
|
|
64
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 rounded-[var(--radius-sm)] border border-border bg-background px-2.5 py-1.5 font-mono text-xs min-w-0", children: [
|
|
65
|
+
/* @__PURE__ */ jsx(FileCode, { className: "h-3.5 w-3.5 shrink-0 text-primary" }),
|
|
66
|
+
dir && /* @__PURE__ */ jsx("span", { className: "truncate text-muted-foreground", children: dir }),
|
|
67
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 font-semibold text-foreground", children: filename })
|
|
68
|
+
] });
|
|
69
|
+
}
|
|
70
|
+
var ICONS = {
|
|
71
|
+
bash: Terminal,
|
|
72
|
+
read: Eye,
|
|
73
|
+
write: FileText,
|
|
74
|
+
edit: Pencil,
|
|
75
|
+
glob: FolderOpen,
|
|
76
|
+
grep: Search,
|
|
77
|
+
list: FolderOpen,
|
|
78
|
+
download: Download,
|
|
79
|
+
inspect: Search,
|
|
80
|
+
audit: CheckCircle,
|
|
81
|
+
unknown: FileCode
|
|
82
|
+
};
|
|
83
|
+
var STATUS_COLORS = {
|
|
84
|
+
running: "text-primary",
|
|
85
|
+
success: "text-[var(--code-success)]",
|
|
86
|
+
error: "text-[var(--code-error)]"
|
|
87
|
+
};
|
|
88
|
+
function ToolCallStep({
|
|
89
|
+
type,
|
|
90
|
+
label,
|
|
91
|
+
status,
|
|
92
|
+
detail,
|
|
93
|
+
output,
|
|
94
|
+
language,
|
|
95
|
+
duration,
|
|
96
|
+
className
|
|
97
|
+
}) {
|
|
98
|
+
const [expanded, setExpanded] = useState(false);
|
|
99
|
+
const Icon = ICONS[type] || ICONS.unknown;
|
|
100
|
+
const hasExpandable = !!(detail || output);
|
|
101
|
+
const lang = inferLanguage(detail, language);
|
|
102
|
+
return /* @__PURE__ */ jsxs(
|
|
103
|
+
"div",
|
|
104
|
+
{
|
|
105
|
+
className: cn(
|
|
106
|
+
"group overflow-hidden rounded-[var(--radius-lg)] border bg-card transition-colors",
|
|
107
|
+
status === "running" && "border-border",
|
|
108
|
+
status === "success" && "border-border hover:border-primary/20",
|
|
109
|
+
status === "error" && "border-[var(--surface-danger-border)]",
|
|
110
|
+
className
|
|
111
|
+
),
|
|
112
|
+
children: [
|
|
113
|
+
/* @__PURE__ */ jsxs(
|
|
114
|
+
"button",
|
|
115
|
+
{
|
|
116
|
+
onClick: () => hasExpandable && setExpanded(!expanded),
|
|
117
|
+
disabled: !hasExpandable,
|
|
118
|
+
className: cn(
|
|
119
|
+
"flex w-full items-center gap-2.5 px-3 py-2 text-left text-sm",
|
|
120
|
+
hasExpandable && "cursor-pointer"
|
|
121
|
+
),
|
|
122
|
+
children: [
|
|
123
|
+
/* @__PURE__ */ jsx(
|
|
124
|
+
"div",
|
|
125
|
+
{
|
|
126
|
+
className: cn(
|
|
127
|
+
"flex h-6 w-6 shrink-0 items-center justify-center rounded-[var(--radius-sm)] border",
|
|
128
|
+
status === "running" && "border-[var(--border-accent)] bg-[var(--accent-surface-soft)] text-primary",
|
|
129
|
+
status === "success" && "border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]",
|
|
130
|
+
status === "error" && "border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]"
|
|
131
|
+
),
|
|
132
|
+
children: status === "running" ? /* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 animate-spin shrink-0" }) : /* @__PURE__ */ jsx(Icon, { className: cn("h-3 w-3 shrink-0", STATUS_COLORS[status]) })
|
|
133
|
+
}
|
|
134
|
+
),
|
|
135
|
+
/* @__PURE__ */ jsx("span", { className: "truncate flex-1 font-sans text-foreground", children: label }),
|
|
136
|
+
/* @__PURE__ */ jsx(
|
|
137
|
+
"span",
|
|
138
|
+
{
|
|
139
|
+
className: cn(
|
|
140
|
+
"rounded-full border px-2 py-0.5 text-[11px] font-semibold uppercase tracking-[0.06em]",
|
|
141
|
+
status === "running" && "border-border bg-[var(--accent-surface-soft)] text-primary",
|
|
142
|
+
status === "success" && "border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]",
|
|
143
|
+
status === "error" && "border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]"
|
|
144
|
+
),
|
|
145
|
+
children: status
|
|
146
|
+
}
|
|
147
|
+
),
|
|
148
|
+
duration !== void 0 && status !== "running" && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", children: duration < 1e3 ? `${duration}ms` : `${(duration / 1e3).toFixed(1)}s` }),
|
|
149
|
+
hasExpandable && /* @__PURE__ */ jsx(
|
|
150
|
+
ChevronRight,
|
|
151
|
+
{
|
|
152
|
+
className: cn(
|
|
153
|
+
"h-3 w-3 text-muted-foreground transition-transform shrink-0",
|
|
154
|
+
expanded && "rotate-90"
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
)
|
|
158
|
+
]
|
|
159
|
+
}
|
|
160
|
+
),
|
|
161
|
+
expanded && (detail || output) && /* @__PURE__ */ jsxs("div", { className: "space-y-2 border-t border-border bg-muted px-3 py-2.5", children: [
|
|
162
|
+
detail && (isFilePath(detail) ? /* @__PURE__ */ jsx(FilePathChip, { path: detail }) : /* @__PURE__ */ jsx("div", { className: "text-xs font-mono text-muted-foreground", children: detail })),
|
|
163
|
+
output && /* @__PURE__ */ jsx(
|
|
164
|
+
CodeBlock,
|
|
165
|
+
{
|
|
166
|
+
code: output,
|
|
167
|
+
language: lang,
|
|
168
|
+
className: "max-h-72 overflow-auto text-xs"
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
] })
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
function ToolCallGroup({ title, children, className }) {
|
|
177
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("my-2 space-y-2", className), children: [
|
|
178
|
+
title && /* @__PURE__ */ jsx("div", { className: "mb-1 px-1 text-xs font-medium uppercase tracking-wider text-muted-foreground", children: title }),
|
|
179
|
+
children
|
|
180
|
+
] });
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export {
|
|
184
|
+
ToolCallStep,
|
|
185
|
+
ToolCallGroup
|
|
186
|
+
};
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cn
|
|
3
|
+
} from "./chunk-RQHJBTEU.js";
|
|
4
|
+
|
|
5
|
+
// src/primitives/avatar.tsx
|
|
6
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
import { jsx } from "react/jsx-runtime";
|
|
9
|
+
var Avatar = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
10
|
+
AvatarPrimitive.Root,
|
|
11
|
+
{
|
|
12
|
+
ref,
|
|
13
|
+
className: cn(
|
|
14
|
+
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
|
15
|
+
className
|
|
16
|
+
),
|
|
17
|
+
...props
|
|
18
|
+
}
|
|
19
|
+
));
|
|
20
|
+
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
|
21
|
+
var AvatarImage = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
22
|
+
AvatarPrimitive.Image,
|
|
23
|
+
{
|
|
24
|
+
ref,
|
|
25
|
+
className: cn("aspect-square h-full w-full", className),
|
|
26
|
+
...props
|
|
27
|
+
}
|
|
28
|
+
));
|
|
29
|
+
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
|
30
|
+
var AvatarFallback = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
31
|
+
AvatarPrimitive.Fallback,
|
|
32
|
+
{
|
|
33
|
+
ref,
|
|
34
|
+
className: cn(
|
|
35
|
+
"flex h-full w-full items-center justify-center rounded-full bg-muted font-medium text-sm",
|
|
36
|
+
className
|
|
37
|
+
),
|
|
38
|
+
...props
|
|
39
|
+
}
|
|
40
|
+
));
|
|
41
|
+
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
|
42
|
+
|
|
43
|
+
// src/primitives/dropdown-menu.tsx
|
|
44
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
45
|
+
import { Check, ChevronRight, Circle } from "lucide-react";
|
|
46
|
+
import * as React2 from "react";
|
|
47
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
48
|
+
var DropdownMenu = DropdownMenuPrimitive.Root;
|
|
49
|
+
var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
50
|
+
var DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
51
|
+
var DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
52
|
+
var DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
53
|
+
var DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
54
|
+
var DropdownMenuSubTrigger = React2.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
55
|
+
DropdownMenuPrimitive.SubTrigger,
|
|
56
|
+
{
|
|
57
|
+
ref,
|
|
58
|
+
className: cn(
|
|
59
|
+
"flex cursor-default select-none items-center rounded-md px-2 py-1.5 text-sm outline-none",
|
|
60
|
+
"focus:bg-accent data-[state=open]:bg-muted/50",
|
|
61
|
+
inset && "pl-8",
|
|
62
|
+
className
|
|
63
|
+
),
|
|
64
|
+
...props,
|
|
65
|
+
children: [
|
|
66
|
+
children,
|
|
67
|
+
/* @__PURE__ */ jsx2(ChevronRight, { className: "ml-auto h-4 w-4" })
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
));
|
|
71
|
+
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
|
72
|
+
var DropdownMenuSubContent = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx2(
|
|
73
|
+
DropdownMenuPrimitive.SubContent,
|
|
74
|
+
{
|
|
75
|
+
ref,
|
|
76
|
+
className: cn(
|
|
77
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-xl border border-border bg-card p-1 text-foreground shadow-[var(--shadow-card)]",
|
|
78
|
+
"data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
79
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
80
|
+
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
81
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
|
82
|
+
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
83
|
+
className
|
|
84
|
+
),
|
|
85
|
+
...props
|
|
86
|
+
}
|
|
87
|
+
));
|
|
88
|
+
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
89
|
+
var DropdownMenuContent = React2.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx2(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx2(
|
|
90
|
+
DropdownMenuPrimitive.Content,
|
|
91
|
+
{
|
|
92
|
+
ref,
|
|
93
|
+
sideOffset,
|
|
94
|
+
className: cn(
|
|
95
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-xl border border-border bg-card p-1 text-foreground shadow-[var(--shadow-card)]",
|
|
96
|
+
"data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
97
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
98
|
+
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
99
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
|
100
|
+
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
101
|
+
className
|
|
102
|
+
),
|
|
103
|
+
...props
|
|
104
|
+
}
|
|
105
|
+
) }));
|
|
106
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
107
|
+
var DropdownMenuItem = React2.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx2(
|
|
108
|
+
DropdownMenuPrimitive.Item,
|
|
109
|
+
{
|
|
110
|
+
ref,
|
|
111
|
+
className: cn(
|
|
112
|
+
"relative flex cursor-pointer select-none items-center rounded-md px-2 py-1.5 text-sm outline-none transition-colors",
|
|
113
|
+
"focus:bg-muted/50 focus:text-foreground",
|
|
114
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
115
|
+
inset && "pl-8",
|
|
116
|
+
className
|
|
117
|
+
),
|
|
118
|
+
...props
|
|
119
|
+
}
|
|
120
|
+
));
|
|
121
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
122
|
+
var DropdownMenuCheckboxItem = React2.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
123
|
+
DropdownMenuPrimitive.CheckboxItem,
|
|
124
|
+
{
|
|
125
|
+
ref,
|
|
126
|
+
className: cn(
|
|
127
|
+
"relative flex cursor-pointer select-none items-center rounded-md py-1.5 pr-2 pl-8 text-sm outline-none transition-colors",
|
|
128
|
+
"focus:bg-muted/50 focus:text-foreground",
|
|
129
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
130
|
+
className
|
|
131
|
+
),
|
|
132
|
+
checked,
|
|
133
|
+
...props,
|
|
134
|
+
children: [
|
|
135
|
+
/* @__PURE__ */ jsx2("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx2(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx2(Check, { className: "h-4 w-4" }) }) }),
|
|
136
|
+
children
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
));
|
|
140
|
+
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
141
|
+
var DropdownMenuRadioItem = React2.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
142
|
+
DropdownMenuPrimitive.RadioItem,
|
|
143
|
+
{
|
|
144
|
+
ref,
|
|
145
|
+
className: cn(
|
|
146
|
+
"relative flex cursor-pointer select-none items-center rounded-md py-1.5 pr-2 pl-8 text-sm outline-none transition-colors",
|
|
147
|
+
"focus:bg-muted/50 focus:text-foreground",
|
|
148
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
149
|
+
className
|
|
150
|
+
),
|
|
151
|
+
...props,
|
|
152
|
+
children: [
|
|
153
|
+
/* @__PURE__ */ jsx2("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx2(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx2(Circle, { className: "h-2 w-2 fill-current" }) }) }),
|
|
154
|
+
children
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
));
|
|
158
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
159
|
+
var DropdownMenuLabel = React2.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx2(
|
|
160
|
+
DropdownMenuPrimitive.Label,
|
|
161
|
+
{
|
|
162
|
+
ref,
|
|
163
|
+
className: cn(
|
|
164
|
+
"px-2 py-1.5 font-semibold text-sm",
|
|
165
|
+
inset && "pl-8",
|
|
166
|
+
className
|
|
167
|
+
),
|
|
168
|
+
...props
|
|
169
|
+
}
|
|
170
|
+
));
|
|
171
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
172
|
+
var DropdownMenuSeparator = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx2(
|
|
173
|
+
DropdownMenuPrimitive.Separator,
|
|
174
|
+
{
|
|
175
|
+
ref,
|
|
176
|
+
className: cn("-mx-1 my-1 h-px bg-border", className),
|
|
177
|
+
...props
|
|
178
|
+
}
|
|
179
|
+
));
|
|
180
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
181
|
+
var DropdownMenuShortcut = ({
|
|
182
|
+
className,
|
|
183
|
+
...props
|
|
184
|
+
}) => {
|
|
185
|
+
return /* @__PURE__ */ jsx2(
|
|
186
|
+
"span",
|
|
187
|
+
{
|
|
188
|
+
className: cn("ml-auto text-xs tracking-widest opacity-60", className),
|
|
189
|
+
...props
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
};
|
|
193
|
+
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
|
194
|
+
|
|
195
|
+
export {
|
|
196
|
+
Avatar,
|
|
197
|
+
AvatarImage,
|
|
198
|
+
AvatarFallback,
|
|
199
|
+
DropdownMenu,
|
|
200
|
+
DropdownMenuTrigger,
|
|
201
|
+
DropdownMenuGroup,
|
|
202
|
+
DropdownMenuPortal,
|
|
203
|
+
DropdownMenuSub,
|
|
204
|
+
DropdownMenuRadioGroup,
|
|
205
|
+
DropdownMenuSubTrigger,
|
|
206
|
+
DropdownMenuSubContent,
|
|
207
|
+
DropdownMenuContent,
|
|
208
|
+
DropdownMenuItem,
|
|
209
|
+
DropdownMenuCheckboxItem,
|
|
210
|
+
DropdownMenuRadioItem,
|
|
211
|
+
DropdownMenuLabel,
|
|
212
|
+
DropdownMenuSeparator,
|
|
213
|
+
DropdownMenuShortcut
|
|
214
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// src/utils/format.ts
|
|
2
|
+
function formatDuration(ms) {
|
|
3
|
+
if (ms < 1e3) return "<1s";
|
|
4
|
+
const seconds = Math.floor(ms / 1e3);
|
|
5
|
+
if (seconds < 60) return `${seconds}s`;
|
|
6
|
+
const minutes = Math.floor(seconds / 60);
|
|
7
|
+
const remaining = seconds % 60;
|
|
8
|
+
return remaining > 0 ? `${minutes}m ${remaining}s` : `${minutes}m`;
|
|
9
|
+
}
|
|
10
|
+
function truncateText(text, max) {
|
|
11
|
+
const cleaned = text.replace(/\s+/g, " ").trim();
|
|
12
|
+
if (cleaned.length <= max) return cleaned;
|
|
13
|
+
return cleaned.slice(0, max).trim() + "...";
|
|
14
|
+
}
|
|
15
|
+
function formatUptime(ms) {
|
|
16
|
+
if (!Number.isFinite(ms) || ms < 0) return "\u2014";
|
|
17
|
+
const totalSeconds = Math.floor(ms / 1e3);
|
|
18
|
+
if (totalSeconds < 60) return `${totalSeconds}s`;
|
|
19
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
20
|
+
const seconds = totalSeconds % 60;
|
|
21
|
+
if (minutes < 60) return `${minutes}m ${seconds}s`;
|
|
22
|
+
const hours = Math.floor(minutes / 60);
|
|
23
|
+
const remMinutes = minutes % 60;
|
|
24
|
+
if (hours < 24) return `${hours}h ${remMinutes}m`;
|
|
25
|
+
const days = Math.floor(hours / 24);
|
|
26
|
+
const remHours = hours % 24;
|
|
27
|
+
return `${days}d ${remHours}h`;
|
|
28
|
+
}
|
|
29
|
+
function formatBytes(bytes) {
|
|
30
|
+
if (!Number.isFinite(bytes) || bytes < 0) return "\u2014";
|
|
31
|
+
if (bytes < 1024) return `${Math.round(bytes)} B`;
|
|
32
|
+
const kb = bytes / 1024;
|
|
33
|
+
if (kb < 1024) return `${kb < 10 ? kb.toFixed(1) : Math.round(kb)} KB`;
|
|
34
|
+
const mb = kb / 1024;
|
|
35
|
+
if (mb < 1024) return `${mb < 10 ? mb.toFixed(1) : Math.round(mb)} MB`;
|
|
36
|
+
const gb = mb / 1024;
|
|
37
|
+
return `${gb < 10 ? gb.toFixed(2) : gb.toFixed(1)} GB`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
formatDuration,
|
|
42
|
+
truncateText,
|
|
43
|
+
formatUptime,
|
|
44
|
+
formatBytes
|
|
45
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getToolCategory
|
|
3
|
+
} from "./chunk-BX6AQMUS.js";
|
|
4
|
+
|
|
5
|
+
// src/hooks/use-run-groups.ts
|
|
6
|
+
import { useMemo } from "react";
|
|
7
|
+
function computeRunStats(messages, partMap) {
|
|
8
|
+
const stats = {
|
|
9
|
+
toolCount: 0,
|
|
10
|
+
messageCount: messages.length,
|
|
11
|
+
thinkingDurationMs: 0,
|
|
12
|
+
textPartCount: 0,
|
|
13
|
+
toolCategories: /* @__PURE__ */ new Set()
|
|
14
|
+
};
|
|
15
|
+
for (const msg of messages) {
|
|
16
|
+
const parts = partMap[msg.id] ?? [];
|
|
17
|
+
for (const part of parts) {
|
|
18
|
+
switch (part.type) {
|
|
19
|
+
case "tool":
|
|
20
|
+
stats.toolCount++;
|
|
21
|
+
stats.toolCategories.add(getToolCategory(part.tool));
|
|
22
|
+
break;
|
|
23
|
+
case "text":
|
|
24
|
+
if (!part.synthetic) stats.textPartCount++;
|
|
25
|
+
break;
|
|
26
|
+
case "reasoning": {
|
|
27
|
+
const start = part.time?.start;
|
|
28
|
+
const end = part.time?.end;
|
|
29
|
+
if (start && end) stats.thinkingDurationMs += end - start;
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return stats;
|
|
36
|
+
}
|
|
37
|
+
function getLastTextContent(messages, partMap) {
|
|
38
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
39
|
+
const parts = partMap[messages[i].id] ?? [];
|
|
40
|
+
for (let j = parts.length - 1; j >= 0; j--) {
|
|
41
|
+
const part = parts[j];
|
|
42
|
+
if (part.type === "text" && !part.synthetic && part.text.trim()) {
|
|
43
|
+
return part.text.trim();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
function useRunGroups({
|
|
50
|
+
messages,
|
|
51
|
+
partMap,
|
|
52
|
+
isStreaming
|
|
53
|
+
}) {
|
|
54
|
+
return useMemo(() => {
|
|
55
|
+
const groups = [];
|
|
56
|
+
let currentRunMessages = [];
|
|
57
|
+
function flushRun(streaming) {
|
|
58
|
+
if (currentRunMessages.length === 0) return;
|
|
59
|
+
const msgs = [...currentRunMessages];
|
|
60
|
+
const stats = computeRunStats(msgs, partMap);
|
|
61
|
+
const summaryText = getLastTextContent(msgs, partMap);
|
|
62
|
+
const isComplete = !streaming;
|
|
63
|
+
groups.push({
|
|
64
|
+
type: "run",
|
|
65
|
+
run: {
|
|
66
|
+
id: msgs[0].id,
|
|
67
|
+
messages: msgs,
|
|
68
|
+
isComplete,
|
|
69
|
+
isStreaming: streaming,
|
|
70
|
+
stats,
|
|
71
|
+
summaryText,
|
|
72
|
+
finalTextPart: null
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
currentRunMessages = [];
|
|
76
|
+
}
|
|
77
|
+
for (let i = 0; i < messages.length; i++) {
|
|
78
|
+
const msg = messages[i];
|
|
79
|
+
if (msg.role === "user") {
|
|
80
|
+
flushRun(false);
|
|
81
|
+
groups.push({ type: "user", message: msg });
|
|
82
|
+
} else {
|
|
83
|
+
currentRunMessages.push(msg);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (currentRunMessages.length > 0) {
|
|
87
|
+
flushRun(isStreaming);
|
|
88
|
+
}
|
|
89
|
+
return groups;
|
|
90
|
+
}, [messages, partMap, isStreaming]);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/hooks/use-run-collapse-state.ts
|
|
94
|
+
import { useCallback, useState } from "react";
|
|
95
|
+
function useRunCollapseState(runs) {
|
|
96
|
+
const [collapsedMap, setCollapsedMap] = useState({});
|
|
97
|
+
const isCollapsed = useCallback(
|
|
98
|
+
(runId) => {
|
|
99
|
+
return collapsedMap[runId] ?? false;
|
|
100
|
+
},
|
|
101
|
+
[collapsedMap]
|
|
102
|
+
);
|
|
103
|
+
const toggleCollapse = useCallback((runId) => {
|
|
104
|
+
setCollapsedMap((prev) => ({ ...prev, [runId]: !prev[runId] }));
|
|
105
|
+
}, []);
|
|
106
|
+
return { isCollapsed, toggleCollapse };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/hooks/use-auto-scroll.ts
|
|
110
|
+
import {
|
|
111
|
+
useCallback as useCallback2,
|
|
112
|
+
useEffect,
|
|
113
|
+
useRef,
|
|
114
|
+
useState as useState2
|
|
115
|
+
} from "react";
|
|
116
|
+
var BOTTOM_THRESHOLD = 40;
|
|
117
|
+
function useAutoScroll(containerRef, deps = []) {
|
|
118
|
+
const [isAtBottom, setIsAtBottom] = useState2(true);
|
|
119
|
+
const userScrolledUp = useRef(false);
|
|
120
|
+
const checkBottom = useCallback2(() => {
|
|
121
|
+
const el = containerRef.current;
|
|
122
|
+
if (!el) return true;
|
|
123
|
+
return el.scrollHeight - el.scrollTop - el.clientHeight < BOTTOM_THRESHOLD;
|
|
124
|
+
}, [containerRef]);
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
const el = containerRef.current;
|
|
127
|
+
if (!el) return;
|
|
128
|
+
const onScroll = () => {
|
|
129
|
+
const atBottom = checkBottom();
|
|
130
|
+
setIsAtBottom(atBottom);
|
|
131
|
+
userScrolledUp.current = !atBottom;
|
|
132
|
+
};
|
|
133
|
+
el.addEventListener("scroll", onScroll, { passive: true });
|
|
134
|
+
return () => el.removeEventListener("scroll", onScroll);
|
|
135
|
+
}, [containerRef, checkBottom]);
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
if (userScrolledUp.current) return;
|
|
138
|
+
const el = containerRef.current;
|
|
139
|
+
if (!el) return;
|
|
140
|
+
el.scrollTop = el.scrollHeight;
|
|
141
|
+
}, deps);
|
|
142
|
+
const scrollToBottom = useCallback2(() => {
|
|
143
|
+
const el = containerRef.current;
|
|
144
|
+
if (!el) return;
|
|
145
|
+
userScrolledUp.current = false;
|
|
146
|
+
el.scrollTo({ top: el.scrollHeight, behavior: "smooth" });
|
|
147
|
+
setIsAtBottom(true);
|
|
148
|
+
}, [containerRef]);
|
|
149
|
+
return { isAtBottom, scrollToBottom };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export {
|
|
153
|
+
useRunGroups,
|
|
154
|
+
useRunCollapseState,
|
|
155
|
+
useAutoScroll
|
|
156
|
+
};
|
|
File without changes
|