@tangle-network/sandbox-ui 0.41.0 → 0.43.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.
@@ -38,6 +38,9 @@ interface ReasoningEventData {
38
38
  interface ToolCallEventData {
39
39
  callId: string;
40
40
  name: string;
41
+ /** The parsed arguments the agent invoked the tool with. Lets a renderer show
42
+ * exactly what was called. Omitted by servers predating the field. */
43
+ args?: Record<string, unknown>;
41
44
  }
42
45
  interface ToolResultEventData {
43
46
  callId: string;
@@ -84,6 +87,9 @@ interface UsageEventData {
84
87
  completionTokens: number | null;
85
88
  costUsd: number | null;
86
89
  balanceUsd: number | null;
90
+ /** Wall-clock duration of the turn in milliseconds, when the server measures
91
+ * it. Drives the renderer's tokens/sec figure. Omitted by older servers. */
92
+ durationMs?: number | null;
87
93
  /** True when a completed turn was replayed from storage (no charge). */
88
94
  replayed?: boolean;
89
95
  }
@@ -151,11 +157,12 @@ interface ChatMessage {
151
157
  id: string;
152
158
  role: ChatRole;
153
159
  text: string;
154
- /** Present only on `tool` messages — the activity's tool name, state, and (once
155
- * finished) its outcome. */
160
+ /** Present only on `tool` messages — the activity's tool name, the arguments it
161
+ * was called with, its state, and (once finished) its outcome. */
156
162
  tool?: {
157
163
  name: string;
158
164
  status: ToolActivityStatus;
165
+ args?: Record<string, unknown>;
159
166
  outcome?: ToolOutcome;
160
167
  };
161
168
  }
@@ -176,6 +183,13 @@ interface PendingProposal {
176
183
  interface UsageInfo {
177
184
  costUsd: number | null;
178
185
  balanceUsd: number | null;
186
+ /** Token counts + wall-clock duration for the settled turn, when the server
187
+ * reports them — drive per-message tokens/sec + cost in a renderer. Optional
188
+ * so a consumer constructing this on the prior `{ costUsd, balanceUsd,
189
+ * replayed }` shape stays valid; a renderer treats a missing value as null. */
190
+ promptTokens?: number | null;
191
+ completionTokens?: number | null;
192
+ durationMs?: number | null;
179
193
  replayed: boolean;
180
194
  }
181
195
  /**
@@ -202,6 +216,10 @@ interface AssistantTranscriptView {
202
216
  * (drives a "thinking" affordance). */
203
217
  isThinking: boolean;
204
218
  pendingProposals: PendingProposal[];
219
+ /** Cost/tokens/duration for the most recently settled turn, or null before any
220
+ * turn settles. Optional so a host predating the field stays a valid consumer;
221
+ * a renderer treats a missing value the same as null. */
222
+ usage?: UsageInfo | null;
205
223
  /** The panel's bound proposal card for a pending proposal — render it where the
206
224
  * confirm/cancel UI should appear. */
207
225
  renderProposal: (proposal: PendingProposal) => ReactNode;
@@ -303,6 +321,13 @@ interface AssistantClient {
303
321
  fetchThreadHistory(threadId: string, signal?: AbortSignal): Promise<ThreadHistoryResult>;
304
322
  streamChat(req: ChatRequest, onEvent: (event: AssistantStreamEvent) => void, signal: AbortSignal): Promise<void>;
305
323
  confirmProposal(proposalId: string): Promise<ConfirmResult>;
324
+ /** Delete a thread and its server-side turns/proposals. Resolves `{ ok }`; a
325
+ * 404 (already gone) is treated as success so a double-delete is harmless.
326
+ * Optional so a host with no delete endpoint stays a valid client — the panel
327
+ * hides the delete affordance when it's absent (see `useAssistantThreads`). */
328
+ deleteThread?(threadId: string): Promise<{
329
+ ok: boolean;
330
+ }>;
306
331
  }
307
332
  /**
308
333
  * Build an assistant client bound to one host's transport. The returned methods
@@ -450,6 +475,15 @@ interface AssistantThreads {
450
475
  /** Load (or reload) the thread list. Must be called to populate `threads` —
451
476
  * the hook never fetches on mount (the panel calls this when history opens). */
452
477
  refresh: () => void;
478
+ /** Delete a thread. Optimistically drops it from the list (within the current
479
+ * owner scope); on failure the list is reloaded to restore the true state. A
480
+ * no-op resolving `{ ok: false }` when the client has no `deleteThread`. */
481
+ remove: (threadId: string) => Promise<{
482
+ ok: boolean;
483
+ }>;
484
+ /** Whether the configured client supports deletion — drives whether a host
485
+ * shows the delete affordance. */
486
+ canRemove: boolean;
453
487
  }
454
488
  declare function useAssistantThreads(userId: string | null): AssistantThreads;
455
489
 
package/dist/assistant.js CHANGED
@@ -443,6 +443,18 @@ function createAssistantClient(config) {
443
443
  ok: false,
444
444
  error: body?.error?.message ?? "The action could not be completed"
445
445
  };
446
+ },
447
+ async deleteThread(threadId) {
448
+ try {
449
+ const res = await fetch(url(`/threads/${encodeURIComponent(threadId)}`), {
450
+ method: "DELETE",
451
+ headers: authHeaders(),
452
+ credentials
453
+ });
454
+ return { ok: res.ok || res.status === 404 };
455
+ } catch {
456
+ return { ok: false };
457
+ }
446
458
  }
447
459
  };
448
460
  }
@@ -845,7 +857,11 @@ function applyStreamEvent(state, event) {
845
857
  id: chipId,
846
858
  role: "tool",
847
859
  text: "",
848
- tool: { name: event.data.name, status: "running" }
860
+ tool: {
861
+ name: event.data.name,
862
+ status: "running",
863
+ args: event.data.args
864
+ }
849
865
  };
850
866
  return {
851
867
  ...state,
@@ -865,7 +881,14 @@ function applyStreamEvent(state, event) {
865
881
  (m) => m.id === chipId ? {
866
882
  ...m,
867
883
  text: errText,
868
- tool: { name: event.data.name, status, outcome }
884
+ // Preserve the args the matching tool_call recorded — the
885
+ // result event doesn't carry them.
886
+ tool: {
887
+ name: event.data.name,
888
+ status,
889
+ args: m.tool?.args,
890
+ outcome
891
+ }
869
892
  } : m
870
893
  )
871
894
  };
@@ -893,6 +916,9 @@ function applyStreamEvent(state, event) {
893
916
  usage: {
894
917
  costUsd: event.data.costUsd,
895
918
  balanceUsd: event.data.balanceUsd,
919
+ promptTokens: event.data.promptTokens,
920
+ completionTokens: event.data.completionTokens,
921
+ durationMs: event.data.durationMs ?? null,
896
922
  replayed: event.data.replayed ?? false
897
923
  }
898
924
  };
@@ -1406,6 +1432,7 @@ function useAssistantThreads(userId) {
1406
1432
  const clientRef = useRef2(client);
1407
1433
  clientRef.current = client;
1408
1434
  const abortRef = useRef2(null);
1435
+ const pendingDeletesRef = useRef2(/* @__PURE__ */ new Set());
1409
1436
  const [state, setState] = useState2(() => ({
1410
1437
  threads: [],
1411
1438
  loading: false,
@@ -1440,7 +1467,11 @@ function useAssistantThreads(userId) {
1440
1467
  if (!isCurrent()) return;
1441
1468
  setState((s) => ({
1442
1469
  // null = transient failure: keep the prior list, just drop the spinner.
1443
- threads: result ?? s.threads,
1470
+ // Drop any in-flight/finished deletions so a stale fetch can't resurrect
1471
+ // a row we already removed.
1472
+ threads: (result ?? s.threads).filter(
1473
+ (t) => !pendingDeletesRef.current.has(t.id)
1474
+ ),
1444
1475
  loading: false,
1445
1476
  loaded: true,
1446
1477
  ownerUserId: requestedUserId,
@@ -1452,13 +1483,45 @@ function useAssistantThreads(userId) {
1452
1483
  }
1453
1484
  });
1454
1485
  }, []);
1455
- useEffect3(() => () => abortRef.current?.abort(), [userId, client]);
1486
+ const remove = useCallback2(
1487
+ async (threadId) => {
1488
+ const requestedClient = clientRef.current;
1489
+ const requestedUserId = userRef.current;
1490
+ if (!requestedClient.deleteThread) return { ok: false };
1491
+ pendingDeletesRef.current.add(threadId);
1492
+ setState(
1493
+ (s) => s.ownerClient === requestedClient && s.ownerUserId === requestedUserId ? { ...s, threads: s.threads.filter((t) => t.id !== threadId) } : s
1494
+ );
1495
+ let res;
1496
+ try {
1497
+ res = await requestedClient.deleteThread(threadId);
1498
+ } catch {
1499
+ res = { ok: false };
1500
+ }
1501
+ if (!res.ok) {
1502
+ pendingDeletesRef.current.delete(threadId);
1503
+ if (userRef.current === requestedUserId && clientRef.current === requestedClient) {
1504
+ refresh();
1505
+ }
1506
+ }
1507
+ return res;
1508
+ },
1509
+ [refresh]
1510
+ );
1511
+ useEffect3(() => {
1512
+ return () => abortRef.current?.abort();
1513
+ }, [userId, client]);
1514
+ useEffect3(() => {
1515
+ return () => pendingDeletesRef.current.clear();
1516
+ }, []);
1456
1517
  const stale = state.ownerUserId !== userId || state.ownerClient !== client;
1457
1518
  return {
1458
1519
  threads: stale ? [] : state.threads,
1459
1520
  loading: stale ? false : state.loading,
1460
1521
  loaded: stale ? false : state.loaded,
1461
- refresh
1522
+ refresh,
1523
+ remove,
1524
+ canRemove: typeof client.deleteThread === "function"
1462
1525
  };
1463
1526
  }
1464
1527
 
@@ -1466,13 +1529,13 @@ function useAssistantThreads(userId) {
1466
1529
  import { MessageSquare } from "lucide-react";
1467
1530
  import {
1468
1531
  useEffect as useEffect5,
1469
- useRef as useRef5
1532
+ useRef as useRef6
1470
1533
  } from "react";
1471
1534
 
1472
1535
  // src/assistant/AssistantPanel.tsx
1473
1536
  import { AgentTimeline, ChatInput } from "@tangle-network/ui/chat";
1474
- import { History, Minus, Plus, RotateCcw, X } from "lucide-react";
1475
- import { useState as useState5 } from "react";
1537
+ import { History, Minus, Plus, RotateCcw, Trash2, X } from "lucide-react";
1538
+ import { useRef as useRef4, useState as useState5 } from "react";
1476
1539
 
1477
1540
  // src/assistant/build-timeline.tsx
1478
1541
  import { jsx as jsx2 } from "react/jsx-runtime";
@@ -1841,6 +1904,8 @@ function AssistantPanel({
1841
1904
  const font = useFontScale();
1842
1905
  const [historyOpen, setHistoryOpen] = useState5(false);
1843
1906
  const { state } = chat;
1907
+ const chatRef = useRef4(chat);
1908
+ chatRef.current = chat;
1844
1909
  const effectiveBalance = state.usage?.balanceUsd ?? balanceUsd;
1845
1910
  const errorView = state.error ? presentError(state.error.code, state.error.message) : null;
1846
1911
  const low = isLowBalance(effectiveBalance) && !errorView;
@@ -1864,6 +1929,18 @@ function AssistantPanel({
1864
1929
  return next;
1865
1930
  });
1866
1931
  };
1932
+ const deleteThread = async (threadId) => {
1933
+ const pre = chatRef.current.state;
1934
+ if (pre.threadId === threadId && pre.status !== "idle") return;
1935
+ if (!window.confirm("Delete this conversation? This can't be undone.")) {
1936
+ return;
1937
+ }
1938
+ const res = await threads.remove(threadId);
1939
+ const live = chatRef.current.state;
1940
+ if (res.ok && live.threadId === threadId && live.status === "idle") {
1941
+ chatRef.current.reset();
1942
+ }
1943
+ };
1867
1944
  return /* @__PURE__ */ jsxs2("div", { className: "flex h-full flex-col bg-background", children: [
1868
1945
  /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between border-border border-b px-4 py-3", children: [
1869
1946
  /* @__PURE__ */ jsxs2("div", { className: "flex items-baseline gap-2", children: [
@@ -1958,18 +2035,35 @@ function AssistantPanel({
1958
2035
  }
1959
2036
  )
1960
2037
  ] }),
1961
- historyOpen && /* @__PURE__ */ jsx4("div", { className: "max-h-48 overflow-y-auto border-border border-b", children: threads.threads.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "px-4 py-3 text-muted-foreground text-xs", children: threads.loaded ? "No past conversations." : "Loading\u2026" }) : /* @__PURE__ */ jsx4("ul", { children: threads.threads.map((t) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(
1962
- "button",
1963
- {
1964
- type: "button",
1965
- onClick: () => {
1966
- chat.switchThread(t.id);
1967
- setHistoryOpen(false);
1968
- },
1969
- className: "block w-full truncate px-4 py-2 text-left text-foreground text-xs hover:bg-muted/50",
1970
- children: t.title ?? "Untitled conversation"
1971
- }
1972
- ) }, t.id)) }) }),
2038
+ historyOpen && /* @__PURE__ */ jsx4("div", { className: "max-h-48 overflow-y-auto border-border border-b", children: threads.threads.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "px-4 py-3 text-muted-foreground text-xs", children: threads.loaded ? "No past conversations." : "Loading\u2026" }) : /* @__PURE__ */ jsx4("ul", { children: threads.threads.map((t) => {
2039
+ const busyActive = state.threadId === t.id && state.status !== "idle";
2040
+ return /* @__PURE__ */ jsxs2("li", { className: "group flex items-center hover:bg-muted/50", children: [
2041
+ /* @__PURE__ */ jsx4(
2042
+ "button",
2043
+ {
2044
+ type: "button",
2045
+ onClick: () => {
2046
+ chat.switchThread(t.id);
2047
+ setHistoryOpen(false);
2048
+ },
2049
+ className: "min-w-0 flex-1 truncate px-4 py-2 text-left text-foreground text-xs",
2050
+ children: t.title ?? "Untitled conversation"
2051
+ }
2052
+ ),
2053
+ threads.canRemove && /* @__PURE__ */ jsx4(
2054
+ "button",
2055
+ {
2056
+ type: "button",
2057
+ onClick: () => void deleteThread(t.id),
2058
+ disabled: busyActive,
2059
+ "aria-label": "Delete conversation",
2060
+ title: busyActive ? "Can't delete while this conversation is active" : "Delete conversation",
2061
+ className: "shrink-0 p-2 text-muted-foreground opacity-0 transition hover:text-destructive focus-visible:opacity-100 group-hover:opacity-100 disabled:cursor-not-allowed disabled:opacity-30",
2062
+ children: /* @__PURE__ */ jsx4(Trash2, { className: "h-3.5 w-3.5" })
2063
+ }
2064
+ )
2065
+ ] }, t.id);
2066
+ }) }) }),
1973
2067
  /* @__PURE__ */ jsx4(
1974
2068
  "div",
1975
2069
  {
@@ -1986,6 +2080,7 @@ function AssistantPanel({
1986
2080
  isStreaming: streaming,
1987
2081
  isThinking,
1988
2082
  pendingProposals: state.pendingProposals,
2083
+ usage: state.usage,
1989
2084
  renderProposal
1990
2085
  }) : /* @__PURE__ */ jsx4(
1991
2086
  AgentTimeline,
@@ -2092,7 +2187,7 @@ function useAssistantLauncher() {
2092
2187
  }
2093
2188
 
2094
2189
  // src/assistant/ResizeHandle.tsx
2095
- import { useRef as useRef4 } from "react";
2190
+ import { useRef as useRef5 } from "react";
2096
2191
  import { jsx as jsx6 } from "react/jsx-runtime";
2097
2192
  function ResizeHandle({
2098
2193
  width,
@@ -2101,7 +2196,7 @@ function ResizeHandle({
2101
2196
  onCommit,
2102
2197
  onNudge
2103
2198
  }) {
2104
- const dragRef = useRef4(null);
2199
+ const dragRef = useRef5(null);
2105
2200
  const onPointerDown = (e) => {
2106
2201
  if (e.button !== 0) return;
2107
2202
  e.preventDefault();
@@ -2190,10 +2285,10 @@ function AssistantDock({
2190
2285
  const chat = useAssistantChat(userId, { onWorkflowMutation });
2191
2286
  const isDesktop = useIsDesktop();
2192
2287
  const { width, maxWidth, setWidth, previewWidth, nudgeWidth } = usePanelWidth();
2193
- const launcherRef = useRef5(null);
2194
- const dialogRef = useRef5(null);
2195
- const returnFocusRef = useRef5(null);
2196
- const wasOpenRef = useRef5(false);
2288
+ const launcherRef = useRef6(null);
2289
+ const dialogRef = useRef6(null);
2290
+ const returnFocusRef = useRef6(null);
2291
+ const wasOpenRef = useRef6(false);
2197
2292
  useEffect5(() => {
2198
2293
  if (!open) return;
2199
2294
  const onKeyDown = (e) => {
@@ -1543,9 +1543,9 @@ function DashboardLayoutInner({
1543
1543
  );
1544
1544
  const activePanel = panels.find((p) => p.mode === mode);
1545
1545
  const buildSidebarContent = React6.useCallback(
1546
- (showLabels) => /* @__PURE__ */ jsxs13(Fragment8, { children: [
1546
+ (showLabels, allowCollapse) => /* @__PURE__ */ jsxs13(Fragment8, { children: [
1547
1547
  /* @__PURE__ */ jsxs13(SidebarRail, { wide: showLabels, children: [
1548
- /* @__PURE__ */ jsx14(SidebarRailHeader, { children: onLogoClick ? /* @__PURE__ */ jsx14(
1548
+ /* @__PURE__ */ jsx14(SidebarRailHeader, { className: cn(showLabels && "justify-start px-3"), children: onLogoClick ? /* @__PURE__ */ jsx14(
1549
1549
  "button",
1550
1550
  {
1551
1551
  type: "button",
@@ -1573,12 +1573,17 @@ function DashboardLayoutInner({
1573
1573
  children: /* @__PURE__ */ jsx14(Logo, { variant, size: "sm", iconOnly: true })
1574
1574
  }
1575
1575
  ) }),
1576
- /* @__PURE__ */ jsx14(SidebarRailNav, { children: navItems.map((item, i) => {
1576
+ /* @__PURE__ */ jsx14(SidebarRailNav, { className: showLabels ? "px-2" : void 0, children: navItems.map((item, i) => {
1577
1577
  const isMode = modeSet.has(item.id);
1578
1578
  const prevIsMode = i > 0 && modeSet.has(navItems[i - 1].id);
1579
1579
  const showSep = i > 0 && isMode && !prevIsMode;
1580
1580
  return /* @__PURE__ */ jsxs13(React6.Fragment, { children: [
1581
- showSep && /* @__PURE__ */ jsx14(RailSeparator, {}),
1581
+ showSep && /* @__PURE__ */ jsx14(
1582
+ RailSeparator,
1583
+ {
1584
+ className: showLabels ? "w-full" : void 0
1585
+ }
1586
+ ),
1582
1587
  isMode ? /* @__PURE__ */ jsx14(
1583
1588
  RailModeButton,
1584
1589
  {
@@ -1601,10 +1606,18 @@ function DashboardLayoutInner({
1601
1606
  )
1602
1607
  ] }, item.id);
1603
1608
  }) }),
1604
- /* @__PURE__ */ jsxs13(SidebarRailFooter, { children: [
1609
+ /* @__PURE__ */ jsxs13(SidebarRailFooter, { className: cn(showLabels && "items-stretch px-2"), children: [
1610
+ allowCollapse && /* @__PURE__ */ jsx14(
1611
+ RailCollapseToggle,
1612
+ {
1613
+ collapsed: railCollapsed,
1614
+ showLabel: showLabels,
1615
+ onToggle: toggleRail
1616
+ }
1617
+ ),
1605
1618
  onSettingsClick ? /* @__PURE__ */ jsx14(RailButton, { icon: SettingsIconSmall, label: "Settings", onClick: onSettingsClick, showLabel: showLabels }) : /* @__PURE__ */ jsx14(RailButton, { icon: SettingsIconSmall, label: "Settings", showLabel: showLabels, asChild: true, children: /* @__PURE__ */ jsx14(Link, { href: settingsHref, to: settingsHref }) }),
1606
1619
  railFooter,
1607
- /* @__PURE__ */ jsx14(RailSeparator, {}),
1620
+ /* @__PURE__ */ jsx14(RailSeparator, { className: showLabels ? "w-full" : void 0 }),
1608
1621
  /* @__PURE__ */ jsx14(
1609
1622
  ProfileAvatar,
1610
1623
  {
@@ -1613,6 +1626,7 @@ function DashboardLayoutInner({
1613
1626
  onLogout,
1614
1627
  onSettingsClick,
1615
1628
  settingsHref,
1629
+ showDetails: showLabels,
1616
1630
  LinkComponent,
1617
1631
  children: profileMenuItems
1618
1632
  }
@@ -1651,10 +1665,10 @@ function DashboardLayoutInner({
1651
1665
  ]
1652
1666
  );
1653
1667
  const sidebarContent = React6.useMemo(
1654
- () => buildSidebarContent(labeledRail && !railCollapsed),
1668
+ () => buildSidebarContent(labeledRail && !railCollapsed, labeledRail),
1655
1669
  [buildSidebarContent, labeledRail, railCollapsed]
1656
1670
  );
1657
- const mobileSidebarContent = React6.useMemo(() => buildSidebarContent(true), [buildSidebarContent]);
1671
+ const mobileSidebarContent = React6.useMemo(() => buildSidebarContent(true, false), [buildSidebarContent]);
1658
1672
  return /* @__PURE__ */ jsxs13("div", { className: cn("min-h-screen bg-background text-foreground", className), children: [
1659
1673
  /* @__PURE__ */ jsxs13(
1660
1674
  "nav",
@@ -1783,8 +1797,8 @@ function DashboardLayoutInner({
1783
1797
  footer
1784
1798
  ] });
1785
1799
  }
1786
- function DashboardLayout({ defaultPanelOpen, defaultMode, labeledRail, ...props }) {
1787
- return /* @__PURE__ */ jsx14(SidebarProvider, { defaultPanelOpen, defaultMode, labeledRail, hasPanels: (props.panels?.length ?? 0) > 0, children: /* @__PURE__ */ jsx14(DashboardLayoutInner, { defaultPanelOpen, defaultMode, labeledRail, ...props }) });
1800
+ function DashboardLayout({ defaultPanelOpen, defaultMode, labeledRail, defaultRailCollapsed, ...props }) {
1801
+ return /* @__PURE__ */ jsx14(SidebarProvider, { defaultPanelOpen, defaultMode, labeledRail, defaultRailCollapsed, hasPanels: (props.panels?.length ?? 0) > 0, children: /* @__PURE__ */ jsx14(DashboardLayoutInner, { defaultPanelOpen, defaultMode, labeledRail, ...props }) });
1788
1802
  }
1789
1803
 
1790
1804
  // src/dashboard/sidebar-layout.tsx
@@ -681,6 +681,12 @@ interface DashboardLayoutProps {
681
681
  * toggles between icon-only and labeled instead of navigating.
682
682
  */
683
683
  labeledRail?: boolean;
684
+ /**
685
+ * Initial rail-collapsed state when `labeledRail` is set and the rail is
686
+ * uncontrolled. Defaults to expanded (`false`). Pass `true` to start on the
687
+ * compact icon rail — the user's choice then persists to localStorage.
688
+ */
689
+ defaultRailCollapsed?: boolean;
684
690
  footer?: React.ReactNode;
685
691
  defaultPanelOpen?: boolean;
686
692
  defaultMode?: string;
@@ -702,7 +708,7 @@ interface DashboardLayoutProps {
702
708
  onMarkAllRead?: () => void;
703
709
  };
704
710
  }
705
- declare function DashboardLayout({ defaultPanelOpen, defaultMode, labeledRail, ...props }: DashboardLayoutProps): react_jsx_runtime.JSX.Element;
711
+ declare function DashboardLayout({ defaultPanelOpen, defaultMode, labeledRail, defaultRailCollapsed, ...props }: DashboardLayoutProps): react_jsx_runtime.JSX.Element;
706
712
 
707
713
  /** A sub-destination revealed inside a nav item's right-flyout. */
708
714
  interface SidebarLayoutFlyoutItem {
package/dist/dashboard.js CHANGED
@@ -52,7 +52,7 @@ import {
52
52
  canAdminSandbox,
53
53
  parseInsufficientBalance,
54
54
  useSidebar
55
- } from "./chunk-6UDHPI2R.js";
55
+ } from "./chunk-KCWIC3SJ.js";
56
56
  import {
57
57
  BillingDashboard,
58
58
  InfoPanel,
package/dist/index.js CHANGED
@@ -138,7 +138,7 @@ import {
138
138
  canAdminSandbox,
139
139
  parseInsufficientBalance,
140
140
  useSidebar
141
- } from "./chunk-6UDHPI2R.js";
141
+ } from "./chunk-KCWIC3SJ.js";
142
142
  import {
143
143
  usePtySession
144
144
  } from "./chunk-77WVVJA4.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-ui",
3
- "version": "0.41.0",
3
+ "version": "0.43.0",
4
4
  "description": "Unified UI component library for Tangle Sandbox — primitives, chat, dashboard, terminal, editor, and workspace components",
5
5
  "repository": {
6
6
  "type": "git",