@tangle-network/sandbox-ui 0.137.2 → 0.138.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.
@@ -0,0 +1,397 @@
1
+ import { t as cn } from "./utils-BER_bdgE.js";
2
+ import { i as modelBrandFor, t as ModelBrandStack } from "./model-brand-bTcMjQtB.js";
3
+ import { t as ProviderIcon } from "./provider-logo-CPKRPHcS.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
6
+ import { X } from "lucide-react";
7
+ import { AgentTimeline } from "@tangle-network/ui/chat";
8
+ import { ApprovalCard, ApprovalDiffSummary, ExpandedToolDetail, InlineThinkingItem, InlineThinkingItem as InlineThinkingItem$1, InlineToolItem, LiveDuration, RunGroup, RunPhaseList, RunRowShell, RunRowStatusDot, TaskList, ToolCallFeed, parseToolEvent, showsTaskPriority, taskProgress, taskStatus } from "@tangle-network/ui/run";
9
+ import { FlowWaterfall } from "@tangle-network/charts/react";
10
+ // src/run/trace-waterfall.tsx
11
+ /** Agent and model spans show their lab/host mark; tool spans show their provider's mark. Unknown keeps the kind glyph. */
12
+ function traceSpanMark(span) {
13
+ if (span.kind === "pipeline" || span.kind === "model") {
14
+ const identity = span.detail ? modelBrandFor(span.detail) : null;
15
+ return identity ? /* @__PURE__ */ jsx(ModelBrandStack, {
16
+ identity,
17
+ size: "sm"
18
+ }) : void 0;
19
+ }
20
+ const provider = span.icon ?? (span.kind === "tool" ? span.detail : void 0);
21
+ return provider ? /* @__PURE__ */ jsx(ProviderIcon, {
22
+ id: provider,
23
+ displayName: provider,
24
+ size: 14,
25
+ className: "rounded-[3px]"
26
+ }) : void 0;
27
+ }
28
+ /**
29
+ * The run inspector: the agent timeline (`defaultView="agents"`) and the span waterfall over one clock, from
30
+ * `@tangle-network/charts`, with Sandbox UI's brand marks. Feed it `otlpFlowTrace(spans)` from the
31
+ * `traces convert` contract. Import `@tangle-network/charts/react/flow-waterfall.css` once.
32
+ */
33
+ function TraceWaterfall({ renderIcon = traceSpanMark, ...props }) {
34
+ return /* @__PURE__ */ jsx(FlowWaterfall, {
35
+ renderIcon,
36
+ ...props
37
+ });
38
+ }
39
+ // src/run/agent-stream.ts
40
+ const TOOL_TYPES = /* @__PURE__ */ new Set([
41
+ "bash",
42
+ "read",
43
+ "write",
44
+ "edit",
45
+ "glob",
46
+ "grep",
47
+ "list",
48
+ "download",
49
+ "inspect",
50
+ "audit"
51
+ ]);
52
+ const toolType = (name) => {
53
+ const key = (name ?? "").toLowerCase();
54
+ return TOOL_TYPES.has(key) ? key : "unknown";
55
+ };
56
+ const brief = (value, max = 240) => {
57
+ if (value === void 0 || value === null) return void 0;
58
+ const text = typeof value === "string" ? value : JSON.stringify(value);
59
+ return text.length > max ? `${text.slice(0, max - 1)}…` : text;
60
+ };
61
+ const failed = (result) => typeof result === "object" && result !== null && ("error" in result || result.is_error === true);
62
+ /** Fold stream rows into transcript entries: text of one turn merges, a tool call becomes a row its result completes. */
63
+ function foldAgentRows(entries, rows) {
64
+ const next = [...entries];
65
+ for (const row of rows) {
66
+ const at = Date.parse(row.at);
67
+ const { event } = row;
68
+ const last = next[next.length - 1];
69
+ if (event.kind === "text" && event.text) {
70
+ if (last?.kind === "message" && last.role === "assistant") next[next.length - 1] = {
71
+ ...last,
72
+ content: last.content + event.text
73
+ };
74
+ else next.push({
75
+ id: `m-${row.seq}`,
76
+ at,
77
+ kind: "message",
78
+ role: "assistant",
79
+ content: event.text
80
+ });
81
+ } else if (event.kind === "user" && event.text) next.push({
82
+ id: `u-${row.seq}`,
83
+ at,
84
+ kind: "message",
85
+ role: "user",
86
+ content: event.text
87
+ });
88
+ else if (event.kind === "reasoning" && event.text) {
89
+ if (last?.kind === "reasoning") next[next.length - 1] = {
90
+ ...last,
91
+ text: last.text + event.text
92
+ };
93
+ else next.push({
94
+ id: `r-${row.seq}`,
95
+ at,
96
+ kind: "reasoning",
97
+ text: event.text
98
+ });
99
+ } else if (event.kind === "tool-call") {
100
+ const id = event.toolCallId ?? `t-${row.seq}`;
101
+ next.push({
102
+ id,
103
+ at,
104
+ kind: "tool",
105
+ call: {
106
+ id,
107
+ type: toolType(event.toolName),
108
+ label: event.toolName ?? "tool",
109
+ status: "running",
110
+ detail: brief(event.args, 120)
111
+ }
112
+ });
113
+ } else if (event.kind === "tool-result") {
114
+ const index = next.findIndex((entry) => entry.kind === "tool" && entry.call.id === event.toolCallId);
115
+ const entry = next[index];
116
+ if (entry?.kind === "tool") next[index] = {
117
+ ...entry,
118
+ call: {
119
+ ...entry.call,
120
+ status: failed(event.result) ? "error" : "success",
121
+ output: brief(event.result),
122
+ duration: Math.max(0, at - entry.at)
123
+ }
124
+ };
125
+ }
126
+ }
127
+ return next;
128
+ }
129
+ function reduce(state, action) {
130
+ switch (action.type) {
131
+ case "reset": return {
132
+ generation: action.generation,
133
+ entries: [],
134
+ lastSeq: 0,
135
+ alive: true
136
+ };
137
+ case "rows": {
138
+ const base = action.generation !== void 0 && action.generation !== state.generation ? {
139
+ entries: [],
140
+ lastSeq: 0
141
+ } : state;
142
+ const fresh = action.rows.filter((row) => row.seq > base.lastSeq);
143
+ return {
144
+ generation: action.generation ?? state.generation,
145
+ entries: foldAgentRows(base.entries, fresh),
146
+ lastSeq: fresh.reduce((max, row) => Math.max(max, row.seq), base.lastSeq),
147
+ alive: action.alive ?? state.alive
148
+ };
149
+ }
150
+ case "end": return {
151
+ ...state,
152
+ alive: false
153
+ };
154
+ case "error": return {
155
+ ...state,
156
+ error: action.message
157
+ };
158
+ }
159
+ }
160
+ /**
161
+ * Follow one agent's message stream over Server-Sent Events: `rows`, `reset` and `end` events, reconnecting with
162
+ * `?after=<lastSeq>` when the server closes the connection, so a long turn streams without gaps or repeats.
163
+ */
164
+ function useAgentStream(url) {
165
+ const [state, dispatch] = useReducer(reduce, {
166
+ entries: [],
167
+ lastSeq: 0,
168
+ alive: false
169
+ });
170
+ useEffect(() => {
171
+ if (!url || typeof EventSource === "undefined") return;
172
+ dispatch({ type: "reset" });
173
+ let lastSeq = 0;
174
+ let closed = false;
175
+ let source;
176
+ let retry;
177
+ const open = () => {
178
+ const target = new URL(url, globalThis.location?.href);
179
+ if (lastSeq > 0) target.searchParams.set("after", String(lastSeq));
180
+ source = new EventSource(target.toString());
181
+ source.addEventListener("rows", (message) => {
182
+ const data = JSON.parse(message.data);
183
+ lastSeq = Math.max(lastSeq, data.lastSeq ?? 0, ...data.events.map((row) => row.seq));
184
+ dispatch({
185
+ type: "rows",
186
+ generation: data.generation,
187
+ rows: data.events,
188
+ alive: data.alive
189
+ });
190
+ });
191
+ source.addEventListener("reset", (message) => {
192
+ lastSeq = 0;
193
+ dispatch({
194
+ type: "reset",
195
+ generation: JSON.parse(message.data).generation
196
+ });
197
+ });
198
+ source.addEventListener("end", () => {
199
+ closed = true;
200
+ source?.close();
201
+ dispatch({ type: "end" });
202
+ });
203
+ source.onerror = () => {
204
+ source?.close();
205
+ if (!closed) retry = setTimeout(open, 1e3);
206
+ };
207
+ };
208
+ open();
209
+ return () => {
210
+ closed = true;
211
+ clearTimeout(retry);
212
+ source?.close();
213
+ };
214
+ }, [url]);
215
+ return state;
216
+ }
217
+ // src/run/trace-workspace.tsx
218
+ /** The agent that owns a span: the span itself when it is an agent, else its nearest agent ancestor. */
219
+ function agentOf(span, byId) {
220
+ for (let cursor = span; cursor; cursor = cursor.parentId ? byId.get(cursor.parentId) : void 0) if (cursor.kind === "pipeline") return cursor;
221
+ }
222
+ function timelineItems(entries, streaming) {
223
+ return entries.map((entry, index) => {
224
+ if (entry.kind === "message") return {
225
+ id: entry.id,
226
+ kind: "message",
227
+ role: entry.role,
228
+ content: entry.content,
229
+ isStreaming: streaming && index === entries.length - 1
230
+ };
231
+ if (entry.kind === "tool") return {
232
+ id: entry.id,
233
+ kind: "tool",
234
+ call: entry.call
235
+ };
236
+ return {
237
+ id: entry.id,
238
+ kind: "custom",
239
+ content: /* @__PURE__ */ jsx(InlineThinkingItem, {
240
+ part: {
241
+ type: "reasoning",
242
+ text: entry.text
243
+ },
244
+ defaultOpen: false,
245
+ openWhileActive: false
246
+ })
247
+ };
248
+ });
249
+ }
250
+ function NotTimeIndexed({ label }) {
251
+ return /* @__PURE__ */ jsxs("p", {
252
+ className: "p-4 text-sm text-muted-foreground",
253
+ children: [label, ": not time-indexed"]
254
+ });
255
+ }
256
+ /**
257
+ * The run workspace: the agent timeline and span waterfall over one clock, the selected agent's conversation
258
+ * streaming in a right-hand drawer, and the page's code workspace and scoreboard following the time cursor. Scrubbing is time travel:
259
+ * every panel shows what was true at the cursor, and the conversation shows what had been said by then.
260
+ */
261
+ function TraceWorkspace({ trace, streamUrl, events, onTimeChange, files, scoreboard, selectedAgentId, onSelectAgent, className, ...waterfall }) {
262
+ const byId = useMemo(() => new Map(trace.spans.flatMap((span) => span.id ? [[span.id, span]] : [])), [trace]);
263
+ const [ownSelection, setOwnSelection] = useState();
264
+ const selected = selectedAgentId ?? ownSelection;
265
+ const select = (agentId) => {
266
+ setOwnSelection(agentId);
267
+ onSelectAgent?.(agentId);
268
+ };
269
+ const agent = selected ? byId.get(selected) : void 0;
270
+ const [cursor, setCursor] = useState(null);
271
+ const stream = useAgentStream(selected && streamUrl ? streamUrl(selected) : void 0);
272
+ const origin = useMemo(() => {
273
+ if (waterfall.startedAt !== void 0) return waterfall.startedAt;
274
+ const first = stream.entries[0]?.at;
275
+ return agent && first !== void 0 ? first - agent.startMs : void 0;
276
+ }, [
277
+ waterfall.startedAt,
278
+ agent,
279
+ stream.entries
280
+ ]);
281
+ const shown = cursor === null || origin === void 0 ? stream.entries : stream.entries.filter((entry) => entry.at - origin <= cursor);
282
+ const items = useMemo(() => timelineItems(shown, cursor === null && stream.alive), [
283
+ shown,
284
+ cursor,
285
+ stream.alive
286
+ ]);
287
+ const scroller = useRef(null);
288
+ const pinned = useRef(true);
289
+ const [unseen, setUnseen] = useState(false);
290
+ useLayoutEffect(() => {
291
+ const element = scroller.current;
292
+ if (!element) return;
293
+ if (pinned.current) element.scrollTop = element.scrollHeight;
294
+ else setUnseen(true);
295
+ }, [items.length, items[items.length - 1]]);
296
+ useEffect(() => {
297
+ pinned.current = true;
298
+ setUnseen(false);
299
+ }, [selected]);
300
+ return /* @__PURE__ */ jsxs("div", {
301
+ className: cn("sandbox-trace-workspace flex min-h-0 flex-col gap-3", className),
302
+ children: [
303
+ /* @__PURE__ */ jsx(TraceWaterfall, {
304
+ ...waterfall,
305
+ trace,
306
+ markers: events,
307
+ selectedId: agent?.id,
308
+ onDeselect: () => select(void 0),
309
+ onSelect: (span) => {
310
+ const owner = agentOf(span, byId);
311
+ if (owner?.id) select(owner.id);
312
+ },
313
+ onTimeChange: (t) => {
314
+ setCursor(t);
315
+ onTimeChange?.(t);
316
+ }
317
+ }),
318
+ /* @__PURE__ */ jsx("section", {
319
+ "aria-label": "Code at the cursor",
320
+ className: "flex min-h-[70vh] flex-col overflow-hidden rounded-xl border border-border bg-card",
321
+ children: files ?? /* @__PURE__ */ jsx(NotTimeIndexed, { label: "Files" })
322
+ }),
323
+ scoreboard && /* @__PURE__ */ jsx("section", {
324
+ "aria-label": "Scoreboard at the cursor",
325
+ className: "overflow-hidden rounded-xl border border-border bg-card",
326
+ children: scoreboard
327
+ }),
328
+ agent && /* @__PURE__ */ jsxs("aside", {
329
+ "aria-label": `${agent.name} conversation`,
330
+ onKeyDown: (event) => {
331
+ if (event.key === "Escape") select(void 0);
332
+ },
333
+ className: "fixed inset-y-0 right-0 z-40 flex w-full max-w-[420px] flex-col border-l border-border bg-card shadow-2xl",
334
+ children: [
335
+ /* @__PURE__ */ jsxs("header", {
336
+ className: "flex shrink-0 items-center gap-2 border-b border-border px-3 py-2",
337
+ children: [
338
+ /* @__PURE__ */ jsx("span", {
339
+ className: cn("h-2 w-2 shrink-0 rounded-full", stream.alive && cursor === null ? "bg-[var(--status-running,#34c48c)]" : "bg-muted-foreground/50"),
340
+ "aria-hidden": true
341
+ }),
342
+ /* @__PURE__ */ jsx("h3", {
343
+ className: "min-w-0 flex-1 truncate text-sm font-semibold",
344
+ children: agent.name
345
+ }),
346
+ agent.detail && /* @__PURE__ */ jsx("span", {
347
+ className: "truncate text-xs text-muted-foreground",
348
+ children: agent.detail
349
+ }),
350
+ /* @__PURE__ */ jsx("button", {
351
+ type: "button",
352
+ "aria-label": "Close conversation",
353
+ title: "Close (Esc)",
354
+ onClick: () => select(void 0),
355
+ className: "shrink-0 rounded-md p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
356
+ children: /* @__PURE__ */ jsx(X, {
357
+ "aria-hidden": true,
358
+ className: "h-4 w-4"
359
+ })
360
+ })
361
+ ]
362
+ }),
363
+ /* @__PURE__ */ jsx("div", {
364
+ ref: scroller,
365
+ onScroll: (event) => {
366
+ const element = event.currentTarget;
367
+ pinned.current = element.scrollHeight - element.scrollTop - element.clientHeight < 48;
368
+ if (pinned.current) setUnseen(false);
369
+ },
370
+ "data-primary-content": "",
371
+ className: "min-h-0 flex-1 overflow-y-auto px-3 py-2",
372
+ children: streamUrl ? /* @__PURE__ */ jsx(AgentTimeline, {
373
+ items,
374
+ isThinking: stream.alive && cursor === null && items.length === 0,
375
+ emptyState: /* @__PURE__ */ jsx("p", {
376
+ className: "p-4 text-sm text-muted-foreground",
377
+ children: stream.error ?? "No messages yet."
378
+ })
379
+ }) : /* @__PURE__ */ jsx(NotTimeIndexed, { label: "Conversation" })
380
+ }),
381
+ unseen && /* @__PURE__ */ jsx("button", {
382
+ type: "button",
383
+ onClick: () => {
384
+ const element = scroller.current;
385
+ if (element) element.scrollTop = element.scrollHeight;
386
+ pinned.current = true;
387
+ setUnseen(false);
388
+ },
389
+ className: "absolute bottom-3 left-1/2 -translate-x-1/2 rounded-full border border-border bg-background px-3 py-1 text-xs shadow-sm",
390
+ children: "New output"
391
+ })
392
+ ]
393
+ })
394
+ ]
395
+ });
396
+ }
397
+ export { TraceWorkspace as _, InlineToolItem as a, TraceWaterfall as b, RunPhaseList as c, TaskList as d, ToolCallFeed as f, taskStatus as g, taskProgress as h, InlineThinkingItem$1 as i, RunRowShell as l, showsTaskPriority as m, ApprovalDiffSummary as n, LiveDuration as o, parseToolEvent as p, ExpandedToolDetail as r, RunGroup as s, ApprovalCard as t, RunRowStatusDot as u, foldAgentRows as v, traceSpanMark as x, useAgentStream as y };
package/dist/run.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { ApprovalCard, ApprovalCardProps, ApprovalDiffFile, ApprovalDiffSummary, ApprovalPlanStep, ExpandedToolDetail, ExpandedToolDetailProps, FeedSegment, InlineThinkingItem, InlineThinkingItemProps, InlineToolItem, InlineToolItemProps, LiveDuration, RunGroup, RunGroupProps, RunPhase, RunPhaseList, RunRowShell, RunRowShellProps, RunRowStatus, RunRowStatusDot, TaskList, TaskListItem, TaskListProps, TaskStatus, ToolCallData, ToolCallFeed, ToolCallFeedProps, ToolCallStatus, ToolCallType, parseToolEvent, showsTaskPriority, taskProgress, taskStatus } from "@tangle-network/ui/run";
2
- export { ApprovalCard, type ApprovalCardProps, type ApprovalDiffFile, ApprovalDiffSummary, type ApprovalPlanStep, ExpandedToolDetail, type ExpandedToolDetailProps, type FeedSegment, InlineThinkingItem, type InlineThinkingItemProps, InlineToolItem, type InlineToolItemProps, LiveDuration, RunGroup, type RunGroupProps, type RunPhase, RunPhaseList, RunRowShell, type RunRowShellProps, type RunRowStatus, RunRowStatusDot, TaskList, type TaskListItem, type TaskListProps, type TaskStatus, type ToolCallData, ToolCallFeed, type ToolCallFeedProps, type ToolCallStatus, type ToolCallType, parseToolEvent, showsTaskPriority, taskProgress, taskStatus };
1
+ import { A as ToolCallType, B as TraceEvent, C as TaskListItem, D as ToolCallFeed, E as ToolCallData, F as AgentStreamRow, G as traceSpanMark, H as TraceWorkspaceProps, I as AgentStreamState, L as AgentTranscriptEntry, M as showsTaskPriority, N as taskProgress, O as ToolCallFeedProps, P as taskStatus, R as foldAgentRows, S as TaskList, T as TaskStatus, U as TraceWaterfall, V as TraceWorkspace, W as TraceWaterfallProps, _ as RunPhaseList, a as ApprovalPlanStep, b as RunRowStatus, c as FeedSegment, d as InlineToolItem, f as InlineToolItemProps, g as RunPhase, h as RunGroupProps, i as ApprovalDiffSummary, j as parseToolEvent, k as ToolCallStatus, l as InlineThinkingItem, m as RunGroup, n as ApprovalCardProps, o as ExpandedToolDetail, p as LiveDuration, r as ApprovalDiffFile, s as ExpandedToolDetailProps, t as ApprovalCard, u as InlineThinkingItemProps, v as RunRowShell, w as TaskListProps, x as RunRowStatusDot, y as RunRowShellProps, z as useAgentStream } from "./index-C5h5T0nb.js";
2
+ export { type AgentStreamRow, type AgentStreamState, type AgentTranscriptEntry, ApprovalCard, type ApprovalCardProps, type ApprovalDiffFile, ApprovalDiffSummary, type ApprovalPlanStep, ExpandedToolDetail, type ExpandedToolDetailProps, type FeedSegment, InlineThinkingItem, type InlineThinkingItemProps, InlineToolItem, type InlineToolItemProps, LiveDuration, RunGroup, type RunGroupProps, type RunPhase, RunPhaseList, RunRowShell, type RunRowShellProps, type RunRowStatus, RunRowStatusDot, TaskList, type TaskListItem, type TaskListProps, type TaskStatus, type ToolCallData, ToolCallFeed, type ToolCallFeedProps, type ToolCallStatus, type ToolCallType, type TraceEvent, TraceWaterfall, type TraceWaterfallProps, TraceWorkspace, type TraceWorkspaceProps, foldAgentRows, parseToolEvent, showsTaskPriority, taskProgress, taskStatus, traceSpanMark, useAgentStream };
package/dist/run.js CHANGED
@@ -1 +1 @@
1
- export { ApprovalCard, ApprovalDiffSummary, ExpandedToolDetail, InlineThinkingItem, InlineToolItem, LiveDuration, RunGroup, RunPhaseList, RunRowShell, RunRowStatusDot, TaskList, ToolCallFeed, parseToolEvent, showsTaskPriority, taskProgress, taskStatus } from "@tangle-network/ui/run";
1
+ export { t as ApprovalCard, n as ApprovalDiffSummary, r as ExpandedToolDetail, i as InlineThinkingItem, a as InlineToolItem, o as LiveDuration, s as RunGroup, c as RunPhaseList, l as RunRowShell, u as RunRowStatusDot, d as TaskList, f as ToolCallFeed, b as TraceWaterfall, _ as TraceWorkspace, v as foldAgentRows, p as parseToolEvent, m as showsTaskPriority, h as taskProgress, g as taskStatus, x as traceSpanMark, y as useAgentStream } from "./run-CliTxkfw.js";
package/dist/styles.css CHANGED
@@ -397,6 +397,9 @@
397
397
  .bottom-2 {
398
398
  bottom: calc(var(--spacing) * 2);
399
399
  }
400
+ .bottom-3 {
401
+ bottom: calc(var(--spacing) * 3);
402
+ }
400
403
  .bottom-4 {
401
404
  bottom: calc(var(--spacing) * 4);
402
405
  }
@@ -958,6 +961,9 @@
958
961
  .h-\[36rem\] {
959
962
  height: 36rem;
960
963
  }
964
+ .h-\[70vh\] {
965
+ height: 70vh;
966
+ }
961
967
  .h-\[72\%\] {
962
968
  height: 72%;
963
969
  }
@@ -1171,6 +1177,9 @@
1171
1177
  .min-h-\[66px\] {
1172
1178
  min-height: 66px;
1173
1179
  }
1180
+ .min-h-\[70vh\] {
1181
+ min-height: 70vh;
1182
+ }
1174
1183
  .min-h-\[72px\] {
1175
1184
  min-height: 72px;
1176
1185
  }
@@ -1465,6 +1474,9 @@
1465
1474
  .max-w-40 {
1466
1475
  max-width: calc(var(--spacing) * 40);
1467
1476
  }
1477
+ .max-w-48 {
1478
+ max-width: calc(var(--spacing) * 48);
1479
+ }
1468
1480
  .max-w-72 {
1469
1481
  max-width: calc(var(--spacing) * 72);
1470
1482
  }
@@ -2103,6 +2115,9 @@
2103
2115
  .self-start {
2104
2116
  align-self: flex-start;
2105
2117
  }
2118
+ .self-stretch {
2119
+ align-self: stretch;
2120
+ }
2106
2121
  .justify-self-start {
2107
2122
  justify-self: flex-start;
2108
2123
  }
@@ -2274,6 +2289,10 @@
2274
2289
  border-bottom-style: var(--tw-border-style);
2275
2290
  border-bottom-width: 1px;
2276
2291
  }
2292
+ .border-b-0 {
2293
+ border-bottom-style: var(--tw-border-style);
2294
+ border-bottom-width: 0px;
2295
+ }
2277
2296
  .border-b-2 {
2278
2297
  border-bottom-style: var(--tw-border-style);
2279
2298
  border-bottom-width: 2px;
@@ -2709,6 +2728,9 @@
2709
2728
  .bg-\[var\(--status-running\)\] {
2710
2729
  background-color: var(--status-running);
2711
2730
  }
2731
+ .bg-\[var\(--status-running\,\#34c48c\)\] {
2732
+ background-color: var(--status-running,#34c48c);
2733
+ }
2712
2734
  .bg-\[var\(--surface-danger-bg\)\] {
2713
2735
  background-color: var(--surface-danger-bg);
2714
2736
  }
@@ -2940,6 +2962,12 @@
2940
2962
  background-color: color-mix(in oklab, hsl(var(--muted-foreground)) 40%, transparent);
2941
2963
  }
2942
2964
  }
2965
+ .bg-muted-foreground\/50 {
2966
+ background-color: hsl(var(--muted-foreground));
2967
+ @supports (color: color-mix(in lab, red, red)) {
2968
+ background-color: color-mix(in oklab, hsl(var(--muted-foreground)) 50%, transparent);
2969
+ }
2970
+ }
2943
2971
  .bg-muted\/10 {
2944
2972
  background-color: hsl(var(--muted));
2945
2973
  @supports (color: color-mix(in lab, red, red)) {
@@ -4432,6 +4460,9 @@
4432
4460
  --tw-translate-x: calc(var(--spacing) * 0.5);
4433
4461
  translate: var(--tw-translate-x) var(--tw-translate-y);
4434
4462
  }
4463
+ .group-hover\:bg-primary:is(:where(.group):hover *) {
4464
+ background-color: hsl(var(--primary));
4465
+ }
4435
4466
  .group-hover\:bg-primary\/20:is(:where(.group):hover *) {
4436
4467
  background-color: hsl(var(--primary));
4437
4468
  }
@@ -4467,6 +4498,9 @@
4467
4498
  opacity: 100%;
4468
4499
  }
4469
4500
  }
4501
+ .group-focus-visible\:bg-primary:is(:where(.group):focus-visible *) {
4502
+ background-color: hsl(var(--primary));
4503
+ }
4470
4504
  .group-focus-visible\:bg-primary\/30:is(:where(.group):focus-visible *) {
4471
4505
  background-color: hsl(var(--primary));
4472
4506
  @supports (color: color-mix(in lab, red, red)) {
@@ -4600,6 +4634,9 @@
4600
4634
  border-color: color-mix(in oklab, hsl(var(--accent)) 50%, transparent);
4601
4635
  }
4602
4636
  }
4637
+ .hover\:border-border:hover {
4638
+ border-color: hsl(var(--border));
4639
+ }
4603
4640
  .hover\:border-destructive\/30:hover {
4604
4641
  border-color: hsl(var(--destructive));
4605
4642
  }
@@ -5111,6 +5148,9 @@
5111
5148
  .focus-visible\:text-foreground:focus-visible {
5112
5149
  color: hsl(var(--foreground));
5113
5150
  }
5151
+ .focus-visible\:underline:focus-visible {
5152
+ text-decoration-line: underline;
5153
+ }
5114
5154
  .focus-visible\:opacity-100:focus-visible {
5115
5155
  opacity: 100%;
5116
5156
  }
@@ -6149,6 +6189,9 @@
6149
6189
  .\[\&\:not\(\:first-child\)\]\:pt-3:not(:first-child) {
6150
6190
  padding-top: calc(var(--spacing) * 3);
6151
6191
  }
6192
+ .\[\&\>\*\]\:min-h-full > * {
6193
+ min-height: 100%;
6194
+ }
6152
6195
  .\[\&\>\*\]\:max-w-full > * {
6153
6196
  max-width: 100%;
6154
6197
  }
@@ -0,0 +1,2 @@
1
+ import { n as DiffViewProps, t as DiffView } from "../diff-view-DDHWtR6X.js";
2
+ export { DiffView, type DiffViewProps };
@@ -0,0 +1 @@
1
+ export { t as DiffView } from "../diff-view-A0O0Z_SR.js";