@tangle-network/sandbox-ui 0.40.0 → 0.42.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.
@@ -1,5 +1,5 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
1
  import { ReactNode } from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
 
4
4
  /**
5
5
  * Wire + UI types for the in-app assistant panel. The wire shapes mirror the
@@ -7,6 +7,7 @@ import { ReactNode } from 'react';
7
7
  * `routes/assistant.ts`). Drift here only mis-renders the client; the server
8
8
  * is the source of truth for what it accepts and emits.
9
9
  */
10
+
10
11
  /** Request body for `POST /api/v1/assistant/chat`. */
11
12
  interface ChatRequest {
12
13
  message: string;
@@ -132,14 +133,30 @@ type AssistantStreamEvent = {
132
133
  type ChatRole = "user" | "assistant" | "status" | "tool";
133
134
  /** Live status of a tool-activity chip. */
134
135
  type ToolActivityStatus = "running" | "ok" | "failed";
136
+ /** The outcome of a finished read-only tool, retained on the chip so a renderer
137
+ * can show the result body (not just the name + status). Mirrors the
138
+ * `tool_result` event: a success carries the tool's `result`; a failure carries
139
+ * the error. Absent while the tool is still running. */
140
+ type ToolOutcome = {
141
+ ok: true;
142
+ result?: unknown;
143
+ } | {
144
+ ok: false;
145
+ error?: {
146
+ code: string;
147
+ message: string;
148
+ };
149
+ };
135
150
  interface ChatMessage {
136
151
  id: string;
137
152
  role: ChatRole;
138
153
  text: string;
139
- /** Present only on `tool` messages — the activity's tool name and state. */
154
+ /** Present only on `tool` messages — the activity's tool name, state, and (once
155
+ * finished) its outcome. */
140
156
  tool?: {
141
157
  name: string;
142
158
  status: ToolActivityStatus;
159
+ outcome?: ToolOutcome;
143
160
  };
144
161
  }
145
162
  /** A mutating action the assistant proposed, awaiting the user's confirmation. */
@@ -161,6 +178,34 @@ interface UsageInfo {
161
178
  balanceUsd: number | null;
162
179
  replayed: boolean;
163
180
  }
181
+ /**
182
+ * The transcript slice handed to a host-supplied `renderTranscript` (see
183
+ * {@link AssistantPanelProps}). It lets a host swap ONLY the conversation
184
+ * rendering — to use its own message renderer — while the panel keeps owning the
185
+ * dock chrome, composer, model picker, history, transport, and proposal
186
+ * orchestration. The bound `renderProposal` returns the panel's own proposal
187
+ * card so the host can place it (e.g. inline after the proposing turn) without
188
+ * re-implementing the confirm/cancel flow.
189
+ */
190
+ interface AssistantTranscriptView {
191
+ messages: ChatMessage[];
192
+ /** The current turn's reasoning/thinking text, if any (streamed before the
193
+ * answer for reasoning models). */
194
+ reasoning: string | null;
195
+ /** Id of the assistant message currently accumulating deltas, if any. */
196
+ streamingId: string | null;
197
+ /** Model slug the current/most-recent turn ran against, or null. */
198
+ model: string | null;
199
+ /** True while a turn is streaming. */
200
+ isStreaming: boolean;
201
+ /** True while the agent is working but has produced no visible output yet
202
+ * (drives a "thinking" affordance). */
203
+ isThinking: boolean;
204
+ pendingProposals: PendingProposal[];
205
+ /** The panel's bound proposal card for a pending proposal — render it where the
206
+ * confirm/cancel UI should appear. */
207
+ renderProposal: (proposal: PendingProposal) => ReactNode;
208
+ }
164
209
 
165
210
  /**
166
211
  * Configurable network client for the assistant panel: the chat SSE stream, the
@@ -419,8 +464,12 @@ interface AssistantDockProps {
419
464
  renderGraph?: (yaml: string) => ReactNode;
420
465
  /** Called after a workflow-mutating tool is confirmed (host re-fetches its list). */
421
466
  onWorkflowMutation?: () => void;
467
+ /** Swap the conversation rendering for a host-supplied renderer (see
468
+ * {@link AssistantPanelProps.renderTranscript}); the dock chrome, composer,
469
+ * transport, and proposal flow stay owned by the panel. */
470
+ renderTranscript?: (view: AssistantTranscriptView) => ReactNode;
422
471
  }
423
- declare function AssistantDock({ userId, navigate, balanceUsd, formatMoney, renderGraph, onWorkflowMutation, }: AssistantDockProps): react_jsx_runtime.JSX.Element;
472
+ declare function AssistantDock({ userId, navigate, balanceUsd, formatMoney, renderGraph, onWorkflowMutation, renderTranscript, }: AssistantDockProps): react_jsx_runtime.JSX.Element;
424
473
 
425
474
  interface AssistantPanelProps {
426
475
  chat: AssistantChat;
@@ -435,8 +484,14 @@ interface AssistantPanelProps {
435
484
  /** Render workflow YAML as a node graph in a proposal card (the `./workflows`
436
485
  * WorkflowGraph). When absent, proposals show YAML as text. */
437
486
  renderGraph?: (yaml: string) => ReactNode;
487
+ /** Swap ONLY the conversation rendering for a host-supplied renderer (e.g. a
488
+ * different chat-message component), while the panel keeps owning the header,
489
+ * composer, model picker, history, transport, and proposal orchestration.
490
+ * Receives the transcript slice plus a bound proposal card to place. When
491
+ * absent, the built-in `AgentTimeline` renders the conversation. */
492
+ renderTranscript?: (view: AssistantTranscriptView) => ReactNode;
438
493
  }
439
- declare function AssistantPanel({ chat, userId, onClose, navigate, balanceUsd, formatMoney, renderGraph, }: AssistantPanelProps): react_jsx_runtime.JSX.Element;
494
+ declare function AssistantPanel({ chat, userId, onClose, navigate, balanceUsd, formatMoney, renderGraph, renderTranscript, }: AssistantPanelProps): react_jsx_runtime.JSX.Element;
440
495
 
441
496
  interface ProposalCardProps {
442
497
  proposal: PendingProposal;
@@ -468,4 +523,4 @@ declare function AssistantLauncherProvider({ children, }: {
468
523
  }): react_jsx_runtime.JSX.Element;
469
524
  declare function useAssistantLauncher(): AssistantLauncher;
470
525
 
471
- export { type AssistantChat, type AssistantClient, type AssistantClientConfig, AssistantClientProvider, AssistantDock, type AssistantDockProps, type AssistantLauncher, AssistantLauncherProvider, type AssistantModelOption, type AssistantModels, type AssistantModelsResult, AssistantPanel, type AssistantPanelProps, type AssistantStreamEvent, type AssistantThreadSummary, type AssistantThreads, type ChatMessage, type ChatRequest, type ChatRole, type ConfirmResult, type ConnectionRequirement, type ConnectionRequirementKind, type DeltaEventData, type DoneEventData, type ErrorEventData, type PendingProposal, ProposalCard, type ProposalCardProps, type ReasoningEventData, type ThreadEventData, type ThreadHistoryResult, type ToolActivityStatus, type ToolCallEventData, type ToolProposalEventData, type ToolResultEventData, type UsageEventData, type UsageInfo, type UseAssistantChatOptions, createAssistantClient, useAssistantChat, useAssistantClient, useAssistantLauncher, useAssistantModels, useAssistantThreads };
526
+ export { type AssistantChat, type AssistantClient, type AssistantClientConfig, AssistantClientProvider, AssistantDock, type AssistantDockProps, type AssistantLauncher, AssistantLauncherProvider, type AssistantModelOption, type AssistantModels, type AssistantModelsResult, AssistantPanel, type AssistantPanelProps, type AssistantStreamEvent, type AssistantThreadSummary, type AssistantThreads, type AssistantTranscriptView, type ChatMessage, type ChatRequest, type ChatRole, type ConfirmResult, type ConnectionRequirement, type ConnectionRequirementKind, type DeltaEventData, type DoneEventData, type ErrorEventData, type PendingProposal, ProposalCard, type ProposalCardProps, type ReasoningEventData, type ThreadEventData, type ThreadHistoryResult, type ToolActivityStatus, type ToolCallEventData, type ToolOutcome, type ToolProposalEventData, type ToolResultEventData, type UsageEventData, type UsageInfo, type UseAssistantChatOptions, createAssistantClient, useAssistantChat, useAssistantClient, useAssistantLauncher, useAssistantModels, useAssistantThreads };
package/dist/assistant.js CHANGED
@@ -857,11 +857,16 @@ function applyStreamEvent(state, event) {
857
857
  const chipId = `tool-${event.data.callId}`;
858
858
  const status = event.data.ok ? "ok" : "failed";
859
859
  const errText = event.data.ok ? "" : event.data.error?.message ?? "unknown error";
860
+ const outcome = event.data.ok ? { ok: true, result: event.data.output } : { ok: false, error: event.data.error };
860
861
  if (state.messages.some((m) => m.id === chipId)) {
861
862
  return {
862
863
  ...state,
863
864
  messages: state.messages.map(
864
- (m) => m.id === chipId ? { ...m, text: errText, tool: { name: event.data.name, status } } : m
865
+ (m) => m.id === chipId ? {
866
+ ...m,
867
+ text: errText,
868
+ tool: { name: event.data.name, status, outcome }
869
+ } : m
865
870
  )
866
871
  };
867
872
  }
@@ -869,7 +874,7 @@ function applyStreamEvent(state, event) {
869
874
  id: chipId,
870
875
  role: "tool",
871
876
  text: errText,
872
- tool: { name: event.data.name, status }
877
+ tool: { name: event.data.name, status, outcome }
873
878
  };
874
879
  return { ...state, messages: capMessages([...state.messages, chip]) };
875
880
  }
@@ -1828,7 +1833,8 @@ function AssistantPanel({
1828
1833
  navigate,
1829
1834
  balanceUsd = null,
1830
1835
  formatMoney = defaultFormatMoney,
1831
- renderGraph
1836
+ renderGraph,
1837
+ renderTranscript
1832
1838
  }) {
1833
1839
  const models = useAssistantModels();
1834
1840
  const threads = useAssistantThreads(userId);
@@ -1850,7 +1856,7 @@ function AssistantPanel({
1850
1856
  renderGraph
1851
1857
  }
1852
1858
  );
1853
- const items = buildAssistantTimeline(state, renderProposal);
1859
+ const isThinking = assistantIsThinking(state);
1854
1860
  const openHistory = () => {
1855
1861
  setHistoryOpen((v) => {
1856
1862
  const next = !v;
@@ -1972,11 +1978,20 @@ function AssistantPanel({
1972
1978
  "aria-live": "polite",
1973
1979
  className: "min-h-0 flex-1 overflow-y-auto px-2 py-3",
1974
1980
  style: { fontSize: `${font.scale}rem` },
1975
- children: /* @__PURE__ */ jsx4(
1981
+ children: renderTranscript ? renderTranscript({
1982
+ messages: state.messages,
1983
+ reasoning: state.reasoning,
1984
+ streamingId: state.streamingId,
1985
+ model: state.model,
1986
+ isStreaming: streaming,
1987
+ isThinking,
1988
+ pendingProposals: state.pendingProposals,
1989
+ renderProposal
1990
+ }) : /* @__PURE__ */ jsx4(
1976
1991
  AgentTimeline,
1977
1992
  {
1978
- items,
1979
- isThinking: assistantIsThinking(state),
1993
+ items: buildAssistantTimeline(state, renderProposal),
1994
+ isThinking,
1980
1995
  emptyState: /* @__PURE__ */ jsx4("p", { className: "px-4 py-8 text-center text-muted-foreground text-sm", children: EMPTY_STATE })
1981
1996
  }
1982
1997
  )
@@ -2168,7 +2183,8 @@ function AssistantDock({
2168
2183
  balanceUsd = null,
2169
2184
  formatMoney,
2170
2185
  renderGraph,
2171
- onWorkflowMutation
2186
+ onWorkflowMutation,
2187
+ renderTranscript
2172
2188
  }) {
2173
2189
  const { open, openAssistant, closeAssistant } = useAssistantLauncher();
2174
2190
  const chat = useAssistantChat(userId, { onWorkflowMutation });
@@ -2272,7 +2288,8 @@ function AssistantDock({
2272
2288
  navigate,
2273
2289
  balanceUsd,
2274
2290
  formatMoney,
2275
- renderGraph
2291
+ renderGraph,
2292
+ renderTranscript
2276
2293
  },
2277
2294
  userId ?? "anon"
2278
2295
  ),
@@ -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";
@@ -10,7 +10,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
10
10
  interface IntegrationConnection {
11
11
  id: string;
12
12
  providerId: string;
13
- connectorId: string;
13
+ connectorId?: string;
14
14
  status: "connected" | "pending" | "revoked" | "expired" | (string & {});
15
15
  grantedScopes?: string[];
16
16
  account?: {
@@ -215,7 +215,7 @@ function buildConnectionIndex(connections) {
215
215
  const index = /* @__PURE__ */ new Map();
216
216
  for (const conn of connections) {
217
217
  if (conn.status === "revoked") continue;
218
- index.set(`${conn.providerId}:${conn.connectorId}`, conn);
218
+ index.set(`${conn.providerId}:${conn.connectorId ?? conn.providerId}`, conn);
219
219
  }
220
220
  return index;
221
221
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-ui",
3
- "version": "0.40.0",
3
+ "version": "0.42.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",