@tangle-network/ui 11.6.0 → 11.8.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/README.md +43 -0
- package/dist/chat.d.ts +18 -8
- package/dist/chat.js +9 -7
- package/dist/chunk-4CLN43XT.js +45 -0
- package/dist/{chunk-AZWDI2JG.js → chunk-565EATZZ.js} +1 -1
- package/dist/{chunk-XROQW5J7.js → chunk-BWOUSEHM.js} +1 -1
- package/dist/{chunk-J2G7KCYH.js → chunk-KNYSKNZX.js} +113 -69
- package/dist/chunk-L57WRIND.js +59 -0
- package/dist/{chunk-ULDNFLIM.js → chunk-ND3ZFXQK.js} +11 -58
- package/dist/{chunk-AAUNOHVL.js → chunk-NWECTSOH.js} +1 -1
- package/dist/{chunk-R5GVQDL3.js → chunk-OXZRCXJK.js} +9 -7
- package/dist/{chunk-XPYPZMYT.js → chunk-Q7ZR6LSD.js} +1 -1
- package/dist/{chunk-RKC3GXHE.js → chunk-X53RZSX6.js} +99 -104
- package/dist/{chunk-PLWRAS5Z.js → chunk-XGOWUDDW.js} +270 -106
- package/dist/{chunk-ZRVH3WCA.js → chunk-XU3HB5QL.js} +21 -1
- package/dist/chunk-YTO5V6QS.js +19 -0
- package/dist/files.d.ts +41 -12
- package/dist/files.js +13 -5
- package/dist/hooks.js +9 -7
- package/dist/index.d.ts +2 -2
- package/dist/index.js +37 -24
- package/dist/primitives.d.ts +15 -1
- package/dist/primitives.js +5 -1
- package/dist/run.d.ts +22 -17
- package/dist/run.js +11 -8
- package/dist/sdk-hooks.js +9 -7
- package/dist/tool-previews.js +2 -2
- package/dist/utils.d.ts +6 -0
- package/dist/utils.js +6 -4
- package/package.json +1 -1
- package/src/chat/agent-timeline.stories.tsx +163 -10
- package/src/chat/agent-timeline.test.tsx +114 -7
- package/src/chat/agent-timeline.tsx +120 -158
- package/src/chat/chat-container.test.tsx +59 -0
- package/src/chat/user-message.test.tsx +33 -0
- package/src/chat/user-message.tsx +13 -8
- package/src/files/file-artifact-pane.stories.tsx +149 -0
- package/src/files/file-artifact-pane.test.tsx +192 -0
- package/src/files/file-artifact-pane.tsx +6 -1
- package/src/files/file-format.ts +42 -4
- package/src/files/file-preview-kind.test.ts +109 -0
- package/src/files/file-preview.tsx +315 -132
- package/src/files/index.ts +8 -1
- package/src/primitives/index.ts +3 -0
- package/src/primitives/text-shimmer.test.tsx +34 -0
- package/src/primitives/text-shimmer.tsx +82 -0
- package/src/run/inline-thinking-item.stories.tsx +2 -2
- package/src/run/inline-thinking-item.tsx +2 -2
- package/src/run/inline-tool-item.test.tsx +76 -0
- package/src/run/inline-tool-item.tsx +9 -7
- package/src/run/run-group.tsx +4 -3
- package/src/run/run-row-shell.test.tsx +74 -4
- package/src/run/run-row-shell.tsx +81 -53
- package/src/run/tool-call-step.tsx +9 -7
- package/src/utils/tool-display.test.ts +53 -0
- package/src/utils/tool-display.ts +16 -15
- package/dist/chunk-LQS34IGP.js +0 -0
- /package/dist/{chunk-EJ5NBW7V.js → chunk-LDLOJKPC.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @tangle-network/ui
|
|
2
2
|
|
|
3
|
+
## 11.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3416c80: `FileArtifactPane` (and `FilePreview`, its body) now previews every file kind a sandbox produces, so a product no longer builds its own image, PDF, CSV, video, audio, or spreadsheet viewers. Pass `blobUrl` (an object URL or remote URL) for binary content and the pane renders an image fit to the pane on a checker ground (click for natural size), a PDF at full pane height, or a video/audio player; pass `content` for CSV and the pane parses quoted commas and line breaks (RFC 4180) into a sticky-header table capped at `CSV_PREVIEW_ROW_LIMIT` (500) rows with a "Showing N of M rows" line; an `.xlsx`/`.xls` file renders a download card with the filename, its size when the new optional `size` prop (bytes) is set, and a Download button wired to `onDownload`. A file with no `blobUrl` and no text content renders one sentence, "This file needs a download link to preview.", with the same Download button. Text, code, JSON, YAML, and markdown render as before. `@tangle-network/ui/files` also exports the pure `resolveFilePreviewKind(filename, mimeType)` helper (`FilePreviewKind`: MIME type wins over extension; anything unmatched is `binary`), `parseCsv`, and `CSV_PREVIEW_ROW_LIMIT`; `detectFileFormat` gains the `video` and `audio` formats and the `avif` image extension.
|
|
8
|
+
|
|
9
|
+
## 11.7.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 0982a63: Quiet transcript rows: the agent transcript (`AgentTimeline`, `ChatContainer`'s `timeline` presentation) now reads as one column of prose with tool calls as quiet one-liners between the paragraphs. `RunRowShell` (and so `InlineToolItem` / `InlineThinkingItem`) rests with no card chrome — transparent border and fill, a bare 14px glyph, title and description at `text-sm` in `text-muted-foreground` that brighten on hover — and takes the bordered `--md3-surface-container` card only while open; the expand chevron replaces the glyph on hover and open, the success dot is gone (success is silence), the running spinner and live-ticking duration are gone (a running row shimmers its title through the new `TextShimmer` primitive, exported from `/primitives`), and the completed duration shows on hover only. `RunRowStatusDot` still exists and now draws only the error dot; `RunRowShellProps.startTime` is accepted and ignored (deprecated). `getToolDisplayMetadata` titles are the bare verb (`Read`, `Edit`, `Write`, `Shell`, `Search`, `Find`, `Web search`, `Fetch`, `Task`) and the path, command, pattern or query lives in `description` only, so a row no longer prints its subject twice; `targetPath` / `commandSnippet` are unchanged. `AgentTimeline` drops the connector spine, the uppercase "Tool activity" header above grouped calls (the group keeps that name as its `aria-label`), and the per-block timestamp on assistant prose; prose renders at 15px/1.5 with a muted streaming caret, and vertical rhythm now follows the pair of neighbours (`mt-4` at a user turn, `mt-1` inside tool sequences and between prose and tools). `UserMessage` loses its border and shows its timestamp beside the bubble on hover. Consumers that composed `RunRowShell` with a shimmer title of their own get the intended result now that the shell draws no spinner beside it.
|
|
14
|
+
|
|
3
15
|
## 11.6.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -26,6 +26,49 @@ export function App() {
|
|
|
26
26
|
}
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
## Styling
|
|
30
|
+
|
|
31
|
+
This package ships no CSS. Its components are Tailwind v4 class names in source, and the build emits JavaScript only, so two pieces of consumer setup carry real weight.
|
|
32
|
+
|
|
33
|
+
### Scan this package
|
|
34
|
+
|
|
35
|
+
Your Tailwind or UnoCSS build has to read this package's files. Neither scans `node_modules` on its own, so a class that only this package names is never generated. Nothing fails loudly when one is missing: the element still renders, still occupies space and still takes clicks, it just looks inert. A `Switch` with no track colour in either state, a `Tabs` where the selected tab is indistinguishable from the rest, and a `Textarea` collapsed to one row all come out of this.
|
|
36
|
+
|
|
37
|
+
Tailwind v4, in the stylesheet that imports `tailwindcss`. `@source` paths resolve relative to that stylesheet, so adjust the prefix to wherever yours lives:
|
|
38
|
+
|
|
39
|
+
```css
|
|
40
|
+
/* from src/styles/app.css, with node_modules two levels up */
|
|
41
|
+
@source "../../node_modules/@tangle-network/ui/src/**/*.tsx";
|
|
42
|
+
@source "../../node_modules/@tangle-network/ui/src/**/*.ts";
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`sandbox-ui` runs exactly these two globs in `src/styles/globals.css`, alongside `@import "tailwindcss" source(none)` so the scan is the whole of what the bundle compiles from.
|
|
46
|
+
|
|
47
|
+
UnoCSS, in `uno.config.ts`:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
content: {
|
|
51
|
+
filesystem: ["node_modules/@tangle-network/ui/dist/**/*.js"],
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Under pnpm that plain glob can match zero files. `node_modules/@tangle-network/ui` is a symlink into `node_modules/.pnpm`, and UnoCSS's globber does not follow it, which leaves you exactly where you started with no error. Pre-expand the glob with something that does follow symlinks, resolve each hit through `realpath`, and hand UnoCSS concrete paths. `tangle-network/blueprint-agent`'s `apps/web/uno.config.ts` carries a worked version that walks the import graph of the two entries it uses rather than the whole `dist`.
|
|
56
|
+
|
|
57
|
+
Two things to check after wiring this up, because the failure is silent either way. Read the generated CSS for a class this package alone names, rather than eyeballing the app. And assert the scan resolved a non-zero number of files: a scan that silently matches nothing looks identical to no scan at all.
|
|
58
|
+
|
|
59
|
+
One consequence worth knowing. This is a scan, not a manifest, so a consumer emits only the classes the version it installed actually names. Upgrading this package can introduce a class your build has never generated, and it will go missing the same silent way.
|
|
60
|
+
|
|
61
|
+
### Import the brand stylesheet
|
|
62
|
+
|
|
63
|
+
`@tangle-network/brand` is a peer dependency. It holds the tokens every colour here resolves through, and a few real classes that some components reference by name instead of composing out of utilities. `Badge` with `dot` is the one to know about: it emits `status-dot status-dot-running` and its siblings, which are defined in `@tangle-network/brand/styles/globals.css` and nowhere else. Without that import the dots have no size and no colour.
|
|
64
|
+
|
|
65
|
+
```css
|
|
66
|
+
@import "@tangle-network/brand/styles";
|
|
67
|
+
@import "tailwindcss";
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
See [`@tangle-network/brand`'s README](../brand/README.md) for the token-level breakdown, the finer-grained imports, and the fonts, which brand deliberately does not bundle.
|
|
71
|
+
|
|
29
72
|
## Subpaths
|
|
30
73
|
|
|
31
74
|
Sixteen named exports: `primitives`, `chat`, `run`, `openui`, `files`, `editor`, `markdown`, `auth`, `hooks`, `sdk-hooks`, `stores`, `types`, `utils`, `tool-previews`, `nav`, `redaction`.
|
package/dist/chat.d.ts
CHANGED
|
@@ -87,9 +87,11 @@ interface UserMessageProps {
|
|
|
87
87
|
actions?: ReactNode;
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
90
|
-
* The single user message bubble — a quiet, right-aligned
|
|
91
|
-
*
|
|
92
|
-
*
|
|
90
|
+
* The single user message bubble — a quiet, right-aligned fill on the muted
|
|
91
|
+
* surface (no border, no loud fill, no uppercase label). The timestamp sits
|
|
92
|
+
* beside the bubble and appears on hover, so it costs no height. Used by both
|
|
93
|
+
* the run/message list (session-model `parts`) and AgentTimeline (direct
|
|
94
|
+
* `content`/`timestamp`).
|
|
93
95
|
*/
|
|
94
96
|
declare const UserMessage: React.MemoExoticComponent<({ message: _message, parts, content, timestamp, actions }: UserMessageProps) => react_jsx_runtime.JSX.Element | null>;
|
|
95
97
|
|
|
@@ -126,6 +128,8 @@ interface AgentTimelineMessageItem {
|
|
|
126
128
|
content: string;
|
|
127
129
|
toolCalls?: ReactNode;
|
|
128
130
|
isStreaming?: boolean;
|
|
131
|
+
/** Shown beside a user bubble on hover. Assistant prose carries no
|
|
132
|
+
* timestamp of its own — the turn's user message dates the exchange. */
|
|
129
133
|
timestamp?: Date;
|
|
130
134
|
after?: ReactNode;
|
|
131
135
|
}
|
|
@@ -140,6 +144,7 @@ interface AgentTimelineToolItem {
|
|
|
140
144
|
interface AgentTimelineToolGroupItem {
|
|
141
145
|
id: string;
|
|
142
146
|
kind: "tool_group";
|
|
147
|
+
/** Accessible name of the group. Not drawn — the rows are the label. */
|
|
143
148
|
title?: string;
|
|
144
149
|
calls: ToolCallData[];
|
|
145
150
|
/** Source tool parts, parallel to `calls`. */
|
|
@@ -166,6 +171,9 @@ interface AgentTimelineArtifactItem {
|
|
|
166
171
|
interface AgentTimelineCustomItem {
|
|
167
172
|
id: string;
|
|
168
173
|
kind: "custom";
|
|
174
|
+
/** Laid out as a run row: it takes the same horizontal bleed as tool rows,
|
|
175
|
+
* so a `RunRowShell`-based row (reasoning) aligns its glyph with the prose
|
|
176
|
+
* edge. */
|
|
169
177
|
content: ReactNode;
|
|
170
178
|
}
|
|
171
179
|
type AgentTimelineItem = AgentTimelineMessageItem | AgentTimelineToolItem | AgentTimelineToolGroupItem | AgentTimelineStatusItem | AgentTimelineArtifactItem | AgentTimelineCustomItem;
|
|
@@ -177,14 +185,16 @@ interface AgentTimelineProps {
|
|
|
177
185
|
/** Optional actions rendered beside each tool item (e.g. "open in artifacts").
|
|
178
186
|
* Receives the source tool part carried on the item. */
|
|
179
187
|
renderToolActions?: (part: ToolPart) => ReactNode;
|
|
180
|
-
/** When set, collapse the timeline to the first N
|
|
181
|
-
* "Show N more steps" toggle. Omit to always
|
|
188
|
+
/** When set, collapse the timeline to the first N step rows (every item
|
|
189
|
+
* except user messages) behind a "Show N more steps" toggle. Omit to always
|
|
190
|
+
* show every row. */
|
|
182
191
|
collapseAfter?: number;
|
|
183
192
|
}
|
|
184
193
|
/**
|
|
185
|
-
* AgentTimeline — unified mixed-content
|
|
186
|
-
* sessions. Renders messages, tool
|
|
187
|
-
*
|
|
194
|
+
* AgentTimeline — unified mixed-content transcript for agent-backed sandbox
|
|
195
|
+
* sessions. Renders messages, tool rows, status cards, and artifact handoffs
|
|
196
|
+
* as one plain column: prose at the reading size, tool calls as quiet
|
|
197
|
+
* one-liners between the paragraphs, no connector chrome.
|
|
188
198
|
*/
|
|
189
199
|
declare function AgentTimeline({ items, isThinking, emptyState, className, renderToolActions, collapseAfter, }: AgentTimelineProps): react_jsx_runtime.JSX.Element | null;
|
|
190
200
|
|
package/dist/chat.js
CHANGED
|
@@ -5,16 +5,18 @@ import {
|
|
|
5
5
|
MessageList,
|
|
6
6
|
ThinkingIndicator,
|
|
7
7
|
UserMessage
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-X53RZSX6.js";
|
|
9
|
+
import "./chunk-565EATZZ.js";
|
|
10
|
+
import "./chunk-OXZRCXJK.js";
|
|
11
|
+
import "./chunk-KNYSKNZX.js";
|
|
12
|
+
import "./chunk-ND3ZFXQK.js";
|
|
13
|
+
import "./chunk-NWECTSOH.js";
|
|
14
|
+
import "./chunk-L57WRIND.js";
|
|
14
15
|
import "./chunk-3M7BPSSW.js";
|
|
15
16
|
import "./chunk-GYPQXTJU.js";
|
|
17
|
+
import "./chunk-4CLN43XT.js";
|
|
18
|
+
import "./chunk-XU3HB5QL.js";
|
|
16
19
|
import "./chunk-MKTSMWVD.js";
|
|
17
|
-
import "./chunk-ZRVH3WCA.js";
|
|
18
20
|
import "./chunk-P5F43A65.js";
|
|
19
21
|
import "./chunk-WUQDUBJG.js";
|
|
20
22
|
import "./chunk-RQHJBTEU.js";
|
|
@@ -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
|
+
};
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
formatDuration,
|
|
3
2
|
getToolCategory,
|
|
4
3
|
getToolDisplayMetadata,
|
|
5
4
|
getToolErrorText
|
|
6
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ND3ZFXQK.js";
|
|
7
6
|
import {
|
|
8
7
|
CommandPreview,
|
|
9
8
|
DiffPreview,
|
|
@@ -12,7 +11,13 @@ import {
|
|
|
12
11
|
QuestionPreview,
|
|
13
12
|
WebSearchPreview,
|
|
14
13
|
WriteFilePreview
|
|
15
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-NWECTSOH.js";
|
|
15
|
+
import {
|
|
16
|
+
TextShimmer
|
|
17
|
+
} from "./chunk-L57WRIND.js";
|
|
18
|
+
import {
|
|
19
|
+
formatDuration
|
|
20
|
+
} from "./chunk-4CLN43XT.js";
|
|
16
21
|
import {
|
|
17
22
|
CodeBlock
|
|
18
23
|
} from "./chunk-WUQDUBJG.js";
|
|
@@ -204,34 +209,20 @@ var ExpandedToolDetail = memo(({ part }) => {
|
|
|
204
209
|
});
|
|
205
210
|
ExpandedToolDetail.displayName = "ExpandedToolDetail";
|
|
206
211
|
|
|
207
|
-
// src/run/run-item-primitives.tsx
|
|
208
|
-
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
209
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
210
|
-
function LiveDuration({ startTime }) {
|
|
211
|
-
const [elapsed, setElapsed] = useState2(Date.now() - startTime);
|
|
212
|
-
useEffect2(() => {
|
|
213
|
-
const id = setInterval(() => setElapsed(Date.now() - startTime), 100);
|
|
214
|
-
return () => clearInterval(id);
|
|
215
|
-
}, [startTime]);
|
|
216
|
-
return /* @__PURE__ */ jsx3("span", { className: "text-xs font-mono text-neutral-400 dark:text-neutral-500 tabular-nums", children: formatDuration(elapsed) });
|
|
217
|
-
}
|
|
218
|
-
|
|
219
212
|
// src/run/run-row-shell.tsx
|
|
220
|
-
import { useState as
|
|
213
|
+
import { useState as useState2 } from "react";
|
|
221
214
|
import * as Collapsible from "@radix-ui/react-collapsible";
|
|
222
|
-
import {
|
|
223
|
-
import { jsx as
|
|
215
|
+
import { ChevronRight } from "lucide-react";
|
|
216
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
224
217
|
function RunRowStatusDot({ status }) {
|
|
225
|
-
if (status
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
return null;
|
|
218
|
+
if (status !== "error") return null;
|
|
219
|
+
return /* @__PURE__ */ jsx3(
|
|
220
|
+
"span",
|
|
221
|
+
{
|
|
222
|
+
"data-run-row-status": "error",
|
|
223
|
+
className: "h-1.5 w-1.5 shrink-0 rounded-full bg-[var(--surface-danger-text)]"
|
|
224
|
+
}
|
|
225
|
+
);
|
|
235
226
|
}
|
|
236
227
|
var SHAPE_CLASS = {
|
|
237
228
|
single: "rounded-[var(--radius-lg)]",
|
|
@@ -239,13 +230,14 @@ var SHAPE_CLASS = {
|
|
|
239
230
|
middle: "rounded-[var(--radius-sm)]",
|
|
240
231
|
last: "rounded-t-[var(--radius-sm)] rounded-b-[var(--radius-lg)]"
|
|
241
232
|
};
|
|
233
|
+
var HEADER_INSET = "px-2 py-1";
|
|
234
|
+
var ERROR_INDENT = "pl-[2.375rem]";
|
|
242
235
|
function RunRowShell({
|
|
243
236
|
icon,
|
|
244
237
|
title,
|
|
245
238
|
description,
|
|
246
239
|
descriptionMono = false,
|
|
247
240
|
status = "idle",
|
|
248
|
-
startTime,
|
|
249
241
|
durationMs,
|
|
250
242
|
groupPosition = "single",
|
|
251
243
|
collapsedError,
|
|
@@ -257,7 +249,7 @@ function RunRowShell({
|
|
|
257
249
|
contentClassName,
|
|
258
250
|
children
|
|
259
251
|
}) {
|
|
260
|
-
const [openState, setOpenState] =
|
|
252
|
+
const [openState, setOpenState] = useState2(defaultOpen);
|
|
261
253
|
const isControlled = openProp !== void 0;
|
|
262
254
|
const open = isControlled ? openProp : openState;
|
|
263
255
|
const setOpen = (next) => {
|
|
@@ -266,62 +258,110 @@ function RunRowShell({
|
|
|
266
258
|
};
|
|
267
259
|
const isRunning = status === "running";
|
|
268
260
|
const expandable = children != null;
|
|
269
|
-
return /* @__PURE__ */
|
|
261
|
+
return /* @__PURE__ */ jsx3(Collapsible.Root, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs3("div", { className: "flex items-start gap-2", children: [
|
|
270
262
|
/* @__PURE__ */ jsxs3(
|
|
271
263
|
"div",
|
|
272
264
|
{
|
|
265
|
+
"data-run-row": "",
|
|
266
|
+
"data-state": open ? "open" : "closed",
|
|
273
267
|
className: cn(
|
|
274
|
-
"min-w-0 flex-1 overflow-hidden border transition-colors",
|
|
275
|
-
"border-
|
|
276
|
-
expandable && "hover:
|
|
277
|
-
open && "border-border",
|
|
268
|
+
"group min-w-0 flex-1 overflow-hidden border transition-colors",
|
|
269
|
+
open ? "border-border bg-[var(--md3-surface-container)]" : "border-transparent bg-transparent",
|
|
270
|
+
expandable && !open && "hover:bg-[var(--md3-surface-container)]",
|
|
278
271
|
SHAPE_CLASS[groupPosition],
|
|
279
272
|
className
|
|
280
273
|
),
|
|
281
274
|
children: [
|
|
282
|
-
/* @__PURE__ */
|
|
275
|
+
/* @__PURE__ */ jsx3(Collapsible.Trigger, { asChild: true, disabled: !expandable, children: /* @__PURE__ */ jsxs3(
|
|
283
276
|
"button",
|
|
284
277
|
{
|
|
285
278
|
type: "button",
|
|
286
279
|
className: cn(
|
|
287
|
-
"flex w-full items-center gap-2
|
|
280
|
+
"flex w-full items-center gap-2 text-left",
|
|
281
|
+
HEADER_INSET,
|
|
288
282
|
expandable ? "cursor-pointer" : "cursor-default"
|
|
289
283
|
),
|
|
290
284
|
children: [
|
|
291
|
-
/* @__PURE__ */
|
|
292
|
-
|
|
293
|
-
|
|
285
|
+
/* @__PURE__ */ jsxs3(
|
|
286
|
+
"span",
|
|
287
|
+
{
|
|
288
|
+
"aria-hidden": true,
|
|
289
|
+
className: "flex h-[1.375rem] w-[1.375rem] shrink-0 items-center justify-center text-muted-foreground",
|
|
290
|
+
children: [
|
|
291
|
+
/* @__PURE__ */ jsx3(
|
|
292
|
+
"span",
|
|
293
|
+
{
|
|
294
|
+
"data-run-row-glyph": "",
|
|
295
|
+
className: cn(
|
|
296
|
+
"flex items-center justify-center",
|
|
297
|
+
expandable && "group-hover:hidden",
|
|
298
|
+
open && "hidden"
|
|
299
|
+
),
|
|
300
|
+
children: icon
|
|
301
|
+
}
|
|
302
|
+
),
|
|
303
|
+
expandable ? /* @__PURE__ */ jsx3(
|
|
304
|
+
ChevronRight,
|
|
305
|
+
{
|
|
306
|
+
"data-run-row-chevron": "",
|
|
307
|
+
className: cn(
|
|
308
|
+
"hidden h-3.5 w-3.5 transition-transform group-hover:block",
|
|
309
|
+
open && "block rotate-90"
|
|
310
|
+
)
|
|
311
|
+
}
|
|
312
|
+
) : null
|
|
313
|
+
]
|
|
314
|
+
}
|
|
315
|
+
),
|
|
316
|
+
/* @__PURE__ */ jsx3(
|
|
294
317
|
"span",
|
|
295
318
|
{
|
|
296
319
|
className: cn(
|
|
297
|
-
"
|
|
320
|
+
"shrink-0 whitespace-nowrap text-sm font-normal text-muted-foreground transition-colors group-hover:text-foreground",
|
|
321
|
+
open && "text-foreground"
|
|
322
|
+
),
|
|
323
|
+
children: title
|
|
324
|
+
}
|
|
325
|
+
),
|
|
326
|
+
description ? /* @__PURE__ */ jsx3(
|
|
327
|
+
"span",
|
|
328
|
+
{
|
|
329
|
+
className: cn(
|
|
330
|
+
"hidden min-w-0 flex-1 truncate text-sm text-muted-foreground transition-colors group-hover:text-foreground sm:inline",
|
|
298
331
|
descriptionMono && "font-mono"
|
|
299
332
|
),
|
|
300
333
|
children: description
|
|
301
334
|
}
|
|
302
335
|
) : null,
|
|
303
|
-
/* @__PURE__ */ jsxs3("
|
|
304
|
-
isRunning &&
|
|
305
|
-
|
|
306
|
-
expandable ? /* @__PURE__ */ jsx4(
|
|
307
|
-
ChevronRight,
|
|
336
|
+
/* @__PURE__ */ jsxs3("span", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: [
|
|
337
|
+
!isRunning && durationMs != null ? /* @__PURE__ */ jsx3(
|
|
338
|
+
"span",
|
|
308
339
|
{
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
)
|
|
340
|
+
"data-run-row-duration": "",
|
|
341
|
+
className: "font-mono text-[10px] tabular-nums text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100",
|
|
342
|
+
children: formatDuration(durationMs)
|
|
313
343
|
}
|
|
314
|
-
) : null
|
|
344
|
+
) : null,
|
|
345
|
+
/* @__PURE__ */ jsx3(RunRowStatusDot, { status })
|
|
315
346
|
] })
|
|
316
347
|
]
|
|
317
348
|
}
|
|
318
349
|
) }),
|
|
319
|
-
collapsedError && !open ? /* @__PURE__ */
|
|
320
|
-
|
|
350
|
+
collapsedError && !open ? /* @__PURE__ */ jsx3(
|
|
351
|
+
"div",
|
|
352
|
+
{
|
|
353
|
+
className: cn(
|
|
354
|
+
"pb-1.5 pr-2 text-xs leading-snug text-[var(--surface-danger-text)]",
|
|
355
|
+
ERROR_INDENT
|
|
356
|
+
),
|
|
357
|
+
children: collapsedError
|
|
358
|
+
}
|
|
359
|
+
) : null,
|
|
360
|
+
expandable ? /* @__PURE__ */ jsx3(Collapsible.Content, { className: "overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp", children: /* @__PURE__ */ jsx3("div", { className: cn("border-t border-border", contentClassName), children }) }) : null
|
|
321
361
|
]
|
|
322
362
|
}
|
|
323
363
|
),
|
|
324
|
-
actions ? /* @__PURE__ */
|
|
364
|
+
actions ? /* @__PURE__ */ jsx3(
|
|
325
365
|
"div",
|
|
326
366
|
{
|
|
327
367
|
className: "flex shrink-0 flex-wrap items-center justify-end gap-1.5 pt-1",
|
|
@@ -345,7 +385,7 @@ import {
|
|
|
345
385
|
ClipboardList,
|
|
346
386
|
Settings
|
|
347
387
|
} from "lucide-react";
|
|
348
|
-
import { jsx as
|
|
388
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
349
389
|
var TOOL_CATEGORY_ICON_MAP = {
|
|
350
390
|
command: Terminal,
|
|
351
391
|
write: FileEdit,
|
|
@@ -382,22 +422,22 @@ var InlineToolItem = memo2(
|
|
|
382
422
|
const durationMs = startTime != null && endTime != null ? endTime - startTime : void 0;
|
|
383
423
|
const category = getToolCategory(part.tool);
|
|
384
424
|
const DefaultIcon = TOOL_CATEGORY_ICON_MAP[category] ?? Settings;
|
|
385
|
-
|
|
425
|
+
const status = toRowStatus(part.state.status);
|
|
426
|
+
return /* @__PURE__ */ jsx4(
|
|
386
427
|
RunRowShell,
|
|
387
428
|
{
|
|
388
|
-
icon: /* @__PURE__ */
|
|
389
|
-
title,
|
|
429
|
+
icon: /* @__PURE__ */ jsx4(DefaultIcon, { className: "h-3.5 w-3.5" }),
|
|
430
|
+
title: status === "running" ? /* @__PURE__ */ jsx4(TextShimmer, { children: title }) : title,
|
|
390
431
|
description,
|
|
391
432
|
descriptionMono: true,
|
|
392
|
-
status
|
|
393
|
-
startTime,
|
|
433
|
+
status,
|
|
394
434
|
durationMs,
|
|
395
435
|
groupPosition,
|
|
396
436
|
collapsedError: errorText ?? void 0,
|
|
397
437
|
actions,
|
|
398
438
|
className,
|
|
399
439
|
contentClassName: cn("p-2", contentClassName),
|
|
400
|
-
children: renderToolDetail?.(part) ?? /* @__PURE__ */
|
|
440
|
+
children: renderToolDetail?.(part) ?? /* @__PURE__ */ jsx4(ExpandedToolDetail, { part })
|
|
401
441
|
}
|
|
402
442
|
);
|
|
403
443
|
}
|
|
@@ -405,7 +445,7 @@ var InlineToolItem = memo2(
|
|
|
405
445
|
InlineToolItem.displayName = "InlineToolItem";
|
|
406
446
|
|
|
407
447
|
// src/run/tool-call-step.tsx
|
|
408
|
-
import { jsx as
|
|
448
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
409
449
|
var EXT_LANGUAGE = {
|
|
410
450
|
ts: "typescript",
|
|
411
451
|
tsx: "typescript",
|
|
@@ -468,7 +508,7 @@ function ToolCallStep({
|
|
|
468
508
|
}
|
|
469
509
|
};
|
|
470
510
|
const lang = inferLanguage(detail, language);
|
|
471
|
-
return /* @__PURE__ */
|
|
511
|
+
return /* @__PURE__ */ jsx5(
|
|
472
512
|
InlineToolItem,
|
|
473
513
|
{
|
|
474
514
|
part: resolvedPart,
|
|
@@ -476,7 +516,7 @@ function ToolCallStep({
|
|
|
476
516
|
description: detail,
|
|
477
517
|
className,
|
|
478
518
|
actions,
|
|
479
|
-
renderToolDetail: part ? void 0 : output ? () => /* @__PURE__ */
|
|
519
|
+
renderToolDetail: part ? void 0 : output ? () => /* @__PURE__ */ jsx5(
|
|
480
520
|
CodeBlock,
|
|
481
521
|
{
|
|
482
522
|
code: output,
|
|
@@ -488,16 +528,20 @@ function ToolCallStep({
|
|
|
488
528
|
);
|
|
489
529
|
}
|
|
490
530
|
function ToolCallGroup({ title, children, className }) {
|
|
491
|
-
return /* @__PURE__ */
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
531
|
+
return /* @__PURE__ */ jsx5(
|
|
532
|
+
"div",
|
|
533
|
+
{
|
|
534
|
+
role: "group",
|
|
535
|
+
"aria-label": title,
|
|
536
|
+
className: cn("flex flex-col gap-1", className),
|
|
537
|
+
children
|
|
538
|
+
}
|
|
539
|
+
);
|
|
495
540
|
}
|
|
496
541
|
|
|
497
542
|
export {
|
|
498
543
|
ExpandableContent,
|
|
499
544
|
ExpandedToolDetail,
|
|
500
|
-
LiveDuration,
|
|
501
545
|
RunRowStatusDot,
|
|
502
546
|
RunRowShell,
|
|
503
547
|
InlineToolItem,
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cn
|
|
3
|
+
} from "./chunk-RQHJBTEU.js";
|
|
4
|
+
|
|
5
|
+
// src/primitives/text-shimmer.tsx
|
|
6
|
+
import { useEffect, useLayoutEffect } from "react";
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
var CLASS_NAME = "tangle-text-shimmer";
|
|
9
|
+
var STYLE_ID = "tangle-text-shimmer-styles";
|
|
10
|
+
var SHIMMER_CSS = `
|
|
11
|
+
@keyframes ${CLASS_NAME} {
|
|
12
|
+
from { -webkit-mask-position: 150% center; mask-position: 150% center; }
|
|
13
|
+
to { -webkit-mask-position: -50% center; mask-position: -50% center; }
|
|
14
|
+
}
|
|
15
|
+
.${CLASS_NAME} {
|
|
16
|
+
-webkit-mask-image: linear-gradient(90deg, rgba(0,0,0,0.72) 0%, #000 45%, rgba(0,0,0,0.72) 60%);
|
|
17
|
+
mask-image: linear-gradient(90deg, rgba(0,0,0,0.72) 0%, #000 45%, rgba(0,0,0,0.72) 60%);
|
|
18
|
+
-webkit-mask-size: 250% 100%;
|
|
19
|
+
mask-size: 250% 100%;
|
|
20
|
+
animation: ${CLASS_NAME} 1.4s linear infinite;
|
|
21
|
+
}
|
|
22
|
+
@media (prefers-reduced-motion: reduce) {
|
|
23
|
+
.${CLASS_NAME} {
|
|
24
|
+
animation: none;
|
|
25
|
+
-webkit-mask-image: none;
|
|
26
|
+
mask-image: none;
|
|
27
|
+
text-decoration: underline dotted currentColor;
|
|
28
|
+
text-underline-offset: 3px;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
function ensureShimmerStyles() {
|
|
33
|
+
if (typeof document === "undefined") return;
|
|
34
|
+
if (document.getElementById(STYLE_ID)) return;
|
|
35
|
+
const style = document.createElement("style");
|
|
36
|
+
style.id = STYLE_ID;
|
|
37
|
+
style.textContent = SHIMMER_CSS;
|
|
38
|
+
document.head.appendChild(style);
|
|
39
|
+
}
|
|
40
|
+
var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
41
|
+
function TextShimmer({
|
|
42
|
+
children,
|
|
43
|
+
className,
|
|
44
|
+
durationMs = 1400
|
|
45
|
+
}) {
|
|
46
|
+
useIsomorphicLayoutEffect(ensureShimmerStyles, []);
|
|
47
|
+
return /* @__PURE__ */ jsx(
|
|
48
|
+
"span",
|
|
49
|
+
{
|
|
50
|
+
className: cn(CLASS_NAME, "text-foreground", className),
|
|
51
|
+
style: { animationDuration: `${durationMs}ms` },
|
|
52
|
+
children
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export {
|
|
58
|
+
TextShimmer
|
|
59
|
+
};
|