@tangle-network/sandbox-ui 0.29.0 → 0.31.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.
@@ -23,12 +23,13 @@ import { Skeleton, useTheme } from "@tangle-network/ui/primitives";
23
23
  import * as React from "react";
24
24
  import { jsx } from "react/jsx-runtime";
25
25
  var PANEL_OPEN_KEY = "sandbox-sidebar-panel-open";
26
+ var RAIL_COLLAPSED_KEY = "sandbox-sidebar-rail-collapsed";
26
27
  var SIDEBAR_MODE_KEY = "sandbox-sidebar-mode";
27
- var SIDEBAR_RAIL_WIDTH = 64;
28
- var SIDEBAR_PANEL_WIDTH = 260;
28
+ var SIDEBAR_RAIL_WIDTH = 56;
29
+ var SIDEBAR_PANEL_WIDTH = 268;
29
30
  var SIDEBAR_TOTAL_WIDTH = SIDEBAR_RAIL_WIDTH + SIDEBAR_PANEL_WIDTH;
30
31
  var SIDEBAR_MOBILE_WIDTH = 256;
31
- var SIDEBAR_RAIL_LABELED_WIDTH = 224;
32
+ var SIDEBAR_RAIL_LABELED_WIDTH = 248;
32
33
  var SidebarContext = React.createContext(null);
33
34
  function readStorage(key, fallback) {
34
35
  if (typeof window === "undefined") return fallback;
@@ -49,9 +50,12 @@ function SidebarProvider({
49
50
  defaultPanelOpen = true,
50
51
  defaultMode = "projects",
51
52
  hasPanels = true,
52
- railWidth = SIDEBAR_RAIL_WIDTH,
53
+ labeledRail = false,
53
54
  panelOpen: controlledPanelOpen,
54
55
  onPanelOpenChange,
56
+ railCollapsed: controlledRailCollapsed,
57
+ onRailCollapsedChange,
58
+ defaultRailCollapsed = false,
55
59
  children
56
60
  }) {
57
61
  const isControlled = controlledPanelOpen !== void 0;
@@ -59,6 +63,11 @@ function SidebarProvider({
59
63
  () => isControlled ? controlledPanelOpen : readStorage(PANEL_OPEN_KEY, String(defaultPanelOpen)) === "true"
60
64
  );
61
65
  const panelOpen = isControlled ? controlledPanelOpen : uncontrolledPanelOpen;
66
+ const isRailControlled = controlledRailCollapsed !== void 0;
67
+ const [uncontrolledRailCollapsed, setUncontrolledRailCollapsed] = React.useState(
68
+ () => isRailControlled ? controlledRailCollapsed : readStorage(RAIL_COLLAPSED_KEY, String(defaultRailCollapsed)) === "true"
69
+ );
70
+ const railCollapsed = isRailControlled ? controlledRailCollapsed : uncontrolledRailCollapsed;
62
71
  const [mode, setModeState] = React.useState(
63
72
  () => isControlled ? defaultMode : readStorage(SIDEBAR_MODE_KEY, defaultMode)
64
73
  );
@@ -73,6 +82,16 @@ function SidebarProvider({
73
82
  const togglePanel = React.useCallback(() => {
74
83
  setPanelOpen(!panelOpen);
75
84
  }, [setPanelOpen, panelOpen]);
85
+ const setRailCollapsed = React.useCallback((collapsed) => {
86
+ if (!isRailControlled) {
87
+ setUncontrolledRailCollapsed(collapsed);
88
+ writeStorage(RAIL_COLLAPSED_KEY, String(collapsed));
89
+ }
90
+ onRailCollapsedChange?.(collapsed);
91
+ }, [isRailControlled, onRailCollapsedChange]);
92
+ const toggleRail = React.useCallback(() => {
93
+ setRailCollapsed(!railCollapsed);
94
+ }, [setRailCollapsed, railCollapsed]);
76
95
  const setMode = React.useCallback((m) => {
77
96
  setModeState(m);
78
97
  if (!isControlled) writeStorage(SIDEBAR_MODE_KEY, m);
@@ -86,6 +105,7 @@ function SidebarProvider({
86
105
  if (!isControlled) writeStorage(SIDEBAR_MODE_KEY, m);
87
106
  setPanelOpen(true);
88
107
  }, [mode, panelOpen, isControlled, setPanelOpen]);
108
+ const railWidth = labeledRail && !railCollapsed ? SIDEBAR_RAIL_LABELED_WIDTH : SIDEBAR_RAIL_WIDTH;
89
109
  const contentMargin = hidden ? 0 : panelOpen && hasPanels ? railWidth + SIDEBAR_PANEL_WIDTH : railWidth;
90
110
  const value = React.useMemo(
91
111
  () => ({
@@ -99,9 +119,12 @@ function SidebarProvider({
99
119
  setHidden,
100
120
  contentMargin,
101
121
  hasPanels,
122
+ railCollapsed,
123
+ setRailCollapsed,
124
+ toggleRail,
102
125
  railWidth
103
126
  }),
104
- [panelOpen, setPanelOpen, togglePanel, mode, setMode, switchMode, hidden, setHidden, contentMargin, hasPanels, railWidth]
127
+ [panelOpen, setPanelOpen, togglePanel, mode, setMode, switchMode, hidden, setHidden, contentMargin, hasPanels, railCollapsed, setRailCollapsed, toggleRail, railWidth]
105
128
  );
106
129
  return /* @__PURE__ */ jsx(SidebarContext.Provider, { value, children });
107
130
  }
@@ -168,8 +191,8 @@ function RailThemeToggle({ className }) {
168
191
  "aria-label": "Toggle theme",
169
192
  title: isDark ? "Switch to light mode" : "Switch to dark mode",
170
193
  className: cn(
171
- "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg text-muted-foreground transition-colors",
172
- "hover:bg-[var(--accent-surface-soft)] hover:text-[var(--accent-text)]",
194
+ "flex h-9 w-9 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors",
195
+ "hover:bg-[var(--accent-surface-soft)] hover:text-foreground",
173
196
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary",
174
197
  className
175
198
  ),
@@ -177,6 +200,43 @@ function RailThemeToggle({ className }) {
177
200
  }
178
201
  );
179
202
  }
203
+ function ChevronsLeftIcon({ className }) {
204
+ return /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
205
+ /* @__PURE__ */ jsx2("title", { children: "Collapse sidebar" }),
206
+ /* @__PURE__ */ jsx2("path", { d: "m11 17-5-5 5-5M18 17l-5-5 5-5" })
207
+ ] });
208
+ }
209
+ function ChevronsRightIcon({ className }) {
210
+ return /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
211
+ /* @__PURE__ */ jsx2("title", { children: "Expand sidebar" }),
212
+ /* @__PURE__ */ jsx2("path", { d: "m6 17 5-5-5-5M13 17l5-5-5-5" })
213
+ ] });
214
+ }
215
+ function RailCollapseToggle({ collapsed, showLabel, onToggle, className }) {
216
+ const label = collapsed ? "Expand sidebar" : "Collapse sidebar";
217
+ const Icon = collapsed ? ChevronsRightIcon : ChevronsLeftIcon;
218
+ return /* @__PURE__ */ jsxs(
219
+ "button",
220
+ {
221
+ type: "button",
222
+ onClick: onToggle,
223
+ "aria-label": label,
224
+ "aria-pressed": collapsed,
225
+ title: label,
226
+ className: cn(
227
+ "group flex shrink-0 items-center rounded-md text-muted-foreground transition-colors",
228
+ showLabel ? "h-9 w-full justify-start gap-2.5 px-2.5" : "h-9 w-9 justify-center",
229
+ "hover:bg-[var(--accent-surface-soft)] hover:text-foreground",
230
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary",
231
+ className
232
+ ),
233
+ children: [
234
+ /* @__PURE__ */ jsx2(Icon, { className: "h-[17px] w-[17px] shrink-0" }),
235
+ showLabel && /* @__PURE__ */ jsx2("span", { className: "text-[13.5px] font-medium", children: label })
236
+ ]
237
+ }
238
+ );
239
+ }
180
240
  function Sidebar({ children, className, style }) {
181
241
  const { panelOpen, hidden, hasPanels, railWidth } = useSidebar();
182
242
  return /* @__PURE__ */ jsx2(
@@ -222,9 +282,9 @@ function RailSeparator({ className }) {
222
282
  }
223
283
  function RailButton({ icon: Icon, label, isActive, badge, onClick, className, showLabel, asChild, children }) {
224
284
  const classes = cn(
225
- "group relative flex shrink-0 items-center justify-center rounded-xl transition-all duration-200",
226
- showLabel ? "w-full justify-start px-3 h-11 gap-3" : "w-11 h-11 justify-center",
227
- "hover:bg-[var(--accent-surface-soft)] hover:text-[var(--accent-text)]",
285
+ "group relative flex shrink-0 items-center justify-center rounded-md transition-all duration-200",
286
+ showLabel ? "w-full justify-start px-2.5 h-9 gap-2.5" : "w-9 h-9 justify-center",
287
+ "hover:bg-[var(--accent-surface-soft)] hover:text-foreground",
228
288
  "active:scale-95",
229
289
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary",
230
290
  isActive && "bg-[var(--accent-surface-strong)] text-[var(--accent-text)]",
@@ -232,8 +292,8 @@ function RailButton({ icon: Icon, label, isActive, badge, onClick, className, sh
232
292
  className
233
293
  );
234
294
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [
235
- /* @__PURE__ */ jsx2(Icon, { className: "h-5 w-5 shrink-0" }),
236
- showLabel && /* @__PURE__ */ jsx2("span", { className: "text-sm font-medium", children: label }),
295
+ /* @__PURE__ */ jsx2(Icon, { className: "h-[17px] w-[17px] shrink-0" }),
296
+ showLabel && /* @__PURE__ */ jsx2("span", { className: "text-[13.5px] font-medium", children: label }),
237
297
  badge !== void 0 && badge > 0 && /* @__PURE__ */ jsx2("span", { className: "absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-primary text-[9px] font-bold text-[var(--md3-on-primary)] px-1 shadow-sm", children: badge > 99 ? "99+" : badge })
238
298
  ] });
239
299
  if (asChild && React2.isValidElement(children)) {
@@ -273,9 +333,9 @@ function RailFlyout({
273
333
  };
274
334
  }, [open]);
275
335
  const triggerClasses = cn(
276
- "group relative flex shrink-0 items-center rounded-xl transition-all duration-200",
277
- showLabel ? "w-full justify-start px-3 h-11 gap-3" : "w-11 h-11 justify-center",
278
- "hover:bg-[var(--accent-surface-soft)] hover:text-[var(--accent-text)]",
336
+ "group relative flex shrink-0 items-center rounded-md transition-all duration-200",
337
+ showLabel ? "w-full justify-start px-2.5 h-9 gap-2.5" : "w-9 h-9 justify-center",
338
+ "hover:bg-[var(--accent-surface-soft)] hover:text-foreground",
279
339
  "active:scale-95 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary",
280
340
  isActive || open ? "bg-[var(--accent-surface-strong)] text-[var(--accent-text)]" : "text-muted-foreground",
281
341
  className
@@ -317,7 +377,7 @@ function RailFlyout({
317
377
  if (el.closest("a,button")) setOpen(false);
318
378
  },
319
379
  className: cn(
320
- "absolute top-0 z-50 min-w-[12rem] rounded-xl border border-border bg-card p-1.5 shadow-lg",
380
+ "absolute top-0 z-50 min-w-[12rem] rounded-md border border-border bg-popover p-1.5 shadow-dropdown",
321
381
  showLabel ? "left-full ml-2" : "left-full ml-2"
322
382
  ),
323
383
  children: [
@@ -356,10 +416,10 @@ function SidebarPanel({ children, className }) {
356
416
  {
357
417
  className: cn(
358
418
  "transition-[opacity] duration-150 h-full overflow-hidden border-l border-border bg-card",
359
- panelOpen ? "w-[260px] opacity-100" : "w-0 opacity-0 pointer-events-none",
419
+ panelOpen ? "w-[268px] opacity-100" : "w-0 opacity-0 pointer-events-none",
360
420
  className
361
421
  ),
362
- children: /* @__PURE__ */ jsx2("div", { className: "flex flex-col h-full w-[260px]", children })
422
+ children: /* @__PURE__ */ jsx2("div", { className: "flex flex-col h-full w-[268px]", children })
363
423
  }
364
424
  );
365
425
  }
@@ -1691,13 +1751,14 @@ function SidebarLayoutInner({
1691
1751
  contentClassName
1692
1752
  }) {
1693
1753
  const Link = LinkComponent ?? DefaultLink3;
1694
- const { panelOpen, togglePanel, setPanelOpen } = useSidebar();
1754
+ const { panelOpen, togglePanel, setPanelOpen, railCollapsed, toggleRail } = useSidebar();
1695
1755
  const handleNavClick = closePanelOnNavigate && panelOpen ? () => setPanelOpen(false) : void 0;
1696
1756
  const hasProfile = user !== void 0 || onLogout !== void 0 || onSettingsClick !== void 0;
1757
+ const showLabels = railLabels && !railCollapsed;
1697
1758
  return /* @__PURE__ */ jsxs13("div", { className: cn("min-h-screen bg-background text-foreground", className), children: [
1698
1759
  /* @__PURE__ */ jsx14("div", { className: cn(hideBelow && HIDE_BELOW_CLASS[hideBelow]), children: /* @__PURE__ */ jsxs13(Sidebar, { className: sidebarClassName, children: [
1699
1760
  /* @__PURE__ */ jsxs13(SidebarRail, { children: [
1700
- (railHeaderContent !== void 0 || logo !== void 0) && /* @__PURE__ */ jsx14(SidebarRailHeader, { className: cn(railLabels && (railHeaderContent !== void 0 ? "px-2" : "justify-start px-4")), children: railHeaderContent !== void 0 ? railHeaderContent : /* @__PURE__ */ jsx14(
1761
+ (railHeaderContent !== void 0 || logo !== void 0) && /* @__PURE__ */ jsx14(SidebarRailHeader, { className: cn(showLabels && (railHeaderContent !== void 0 ? "px-2" : "justify-start px-4")), children: railHeaderContent !== void 0 ? railHeaderContent : /* @__PURE__ */ jsx14(
1701
1762
  Link,
1702
1763
  {
1703
1764
  href: logoHref,
@@ -1706,7 +1767,7 @@ function SidebarLayoutInner({
1706
1767
  children: logo
1707
1768
  }
1708
1769
  ) }),
1709
- /* @__PURE__ */ jsx14(SidebarRailNav, { className: railLabels ? "px-2" : void 0, children: navItems.map((item) => {
1770
+ /* @__PURE__ */ jsx14(SidebarRailNav, { className: showLabels ? "px-2" : void 0, children: navItems.map((item) => {
1710
1771
  if (item.flyoutItems && item.flyoutItems.length > 0) {
1711
1772
  const activeSet = new Set(item.flyoutActiveIds ?? []);
1712
1773
  return /* @__PURE__ */ jsx14(
@@ -1715,7 +1776,7 @@ function SidebarLayoutInner({
1715
1776
  icon: item.icon,
1716
1777
  label: item.label,
1717
1778
  title: item.label,
1718
- showLabel: railLabels,
1779
+ showLabel: showLabels,
1719
1780
  isActive: activeId === item.id || item.flyoutItems.some((f) => activeSet.has(f.id)),
1720
1781
  children: item.flyoutItems.map((f) => {
1721
1782
  const FIcon = f.icon;
@@ -1751,10 +1812,10 @@ function SidebarLayoutInner({
1751
1812
  badge: item.badge,
1752
1813
  isActive: panelOpen,
1753
1814
  onClick: togglePanel,
1754
- showLabel: railLabels
1815
+ showLabel: showLabels
1755
1816
  },
1756
1817
  item.id
1757
- ) : /* @__PURE__ */ jsx14(RailButton, { icon: item.icon, label: item.label, badge: item.badge, isActive: activeId === item.id, showLabel: railLabels, asChild: true, children: /* @__PURE__ */ jsx14(
1818
+ ) : /* @__PURE__ */ jsx14(RailButton, { icon: item.icon, label: item.label, badge: item.badge, isActive: activeId === item.id, showLabel: showLabels, asChild: true, children: /* @__PURE__ */ jsx14(
1758
1819
  Link,
1759
1820
  {
1760
1821
  href: item.href,
@@ -1764,7 +1825,8 @@ function SidebarLayoutInner({
1764
1825
  }
1765
1826
  ) }, item.id);
1766
1827
  }) }),
1767
- (railFooter !== void 0 || hasProfile) && /* @__PURE__ */ jsxs13(SidebarRailFooter, { className: cn("border-t border-border pt-2", railLabels && "items-stretch px-2"), children: [
1828
+ (railLabels || railFooter !== void 0 || hasProfile) && /* @__PURE__ */ jsxs13(SidebarRailFooter, { className: cn("border-t border-border pt-2", showLabels && "items-stretch px-2"), children: [
1829
+ railLabels && /* @__PURE__ */ jsx14(RailCollapseToggle, { collapsed: railCollapsed, showLabel: showLabels, onToggle: toggleRail }),
1768
1830
  railFooter,
1769
1831
  hasProfile && (() => {
1770
1832
  const profile = /* @__PURE__ */ jsx14(
@@ -1775,13 +1837,13 @@ function SidebarLayoutInner({
1775
1837
  onLogout,
1776
1838
  onSettingsClick,
1777
1839
  settingsHref,
1778
- showDetails: railLabels,
1840
+ showDetails: showLabels,
1779
1841
  LinkComponent: Link,
1780
1842
  children: profileMenuItems
1781
1843
  }
1782
1844
  );
1783
1845
  if (!showThemeToggle) return profile;
1784
- return railLabels ? /* @__PURE__ */ jsxs13("div", { className: "flex w-full items-center gap-1", children: [
1846
+ return showLabels ? /* @__PURE__ */ jsxs13("div", { className: "flex w-full items-center gap-1", children: [
1785
1847
  /* @__PURE__ */ jsx14("div", { className: "min-w-0 flex-1", children: profile }),
1786
1848
  /* @__PURE__ */ jsx14(RailThemeToggle, {})
1787
1849
  ] }) : /* @__PURE__ */ jsxs13("div", { className: "flex flex-col items-center gap-1", children: [
@@ -1800,6 +1862,9 @@ function SidebarLayout({
1800
1862
  panelOpen,
1801
1863
  onPanelOpenChange,
1802
1864
  defaultPanelOpen,
1865
+ railCollapsed,
1866
+ onRailCollapsedChange,
1867
+ defaultRailCollapsed,
1803
1868
  ...props
1804
1869
  }) {
1805
1870
  return /* @__PURE__ */ jsx14(
@@ -1808,8 +1873,11 @@ function SidebarLayout({
1808
1873
  panelOpen,
1809
1874
  onPanelOpenChange,
1810
1875
  defaultPanelOpen,
1876
+ railCollapsed,
1877
+ onRailCollapsedChange,
1878
+ defaultRailCollapsed,
1811
1879
  hasPanels: props.panel != null,
1812
- railWidth: props.railLabels ? SIDEBAR_RAIL_LABELED_WIDTH : void 0,
1880
+ labeledRail: props.railLabels ?? false,
1813
1881
  children: /* @__PURE__ */ jsx14(SidebarLayoutInner, { ...props })
1814
1882
  }
1815
1883
  );
@@ -3223,8 +3291,11 @@ export {
3223
3291
  SIDEBAR_PANEL_WIDTH,
3224
3292
  SIDEBAR_TOTAL_WIDTH,
3225
3293
  SIDEBAR_MOBILE_WIDTH,
3294
+ SIDEBAR_RAIL_LABELED_WIDTH,
3226
3295
  SidebarProvider,
3227
3296
  useSidebar,
3297
+ RailThemeToggle,
3298
+ RailCollapseToggle,
3228
3299
  Sidebar,
3229
3300
  SidebarRail,
3230
3301
  SidebarRailHeader,
@@ -11,6 +11,18 @@ interface SidebarUser {
11
11
  tier?: string;
12
12
  avatarUrl?: string;
13
13
  }
14
+ declare function RailThemeToggle({ className }: {
15
+ className?: string;
16
+ }): react_jsx_runtime.JSX.Element;
17
+ interface RailCollapseToggleProps {
18
+ /** Whether the rail is currently collapsed (icon-only). */
19
+ collapsed: boolean;
20
+ /** Render with a label beside the chevron (expanded/labeled rail) vs. icon-only. */
21
+ showLabel?: boolean;
22
+ onToggle: () => void;
23
+ className?: string;
24
+ }
25
+ declare function RailCollapseToggle({ collapsed, showLabel, onToggle, className }: RailCollapseToggleProps): react_jsx_runtime.JSX.Element;
14
26
  interface SidebarProps {
15
27
  children: React.ReactNode;
16
28
  className?: string;
@@ -140,9 +152,9 @@ interface ProfileAvatarProps {
140
152
  }
141
153
  declare function ProfileAvatar({ user, isLoading, onLogout, onSettingsClick, settingsHref, children, className, showDetails, LinkComponent, }: ProfileAvatarProps): react_jsx_runtime.JSX.Element;
142
154
 
143
- /** Width constants (px) matching blueprint-agent layout */
144
- declare const SIDEBAR_RAIL_WIDTH = 64;
145
- declare const SIDEBAR_PANEL_WIDTH = 260;
155
+ /** Width constants (px) — Tangle Quiet density (ChatGPT-minimal) */
156
+ declare const SIDEBAR_RAIL_WIDTH = 56;
157
+ declare const SIDEBAR_PANEL_WIDTH = 268;
146
158
  declare const SIDEBAR_TOTAL_WIDTH: number;
147
159
  /**
148
160
  * Width of the mobile drawer when the rail is rendered with labels
@@ -150,6 +162,12 @@ declare const SIDEBAR_TOTAL_WIDTH: number;
150
162
  * wide enough for icons; labels need a wider container to avoid truncation.
151
163
  */
152
164
  declare const SIDEBAR_MOBILE_WIDTH = 256;
165
+ /**
166
+ * Rail width when labels are shown beside icons on desktop. The default rail is
167
+ * icon-only at {@link SIDEBAR_RAIL_WIDTH}; a labeled rail needs room for the
168
+ * longest nav label plus its badge.
169
+ */
170
+ declare const SIDEBAR_RAIL_LABELED_WIDTH = 248;
153
171
  interface SidebarContextValue {
154
172
  /** Whether the content panel beside the rail is open */
155
173
  panelOpen: boolean;
@@ -167,15 +185,34 @@ interface SidebarContextValue {
167
185
  contentMargin: number;
168
186
  /** Whether there are panels at all */
169
187
  hasPanels: boolean;
170
- /** Rail width in px — 64 for the icon-only rail, wider when labels show */
188
+ /**
189
+ * Whether the labeled nav rail is collapsed to the icon-only width. Independent
190
+ * of {@link panelOpen} (the slide-out content panel) — two separate collapses.
191
+ * Only meaningful when the rail is label-capable; an icon-only rail stays
192
+ * icon-only regardless.
193
+ */
194
+ railCollapsed: boolean;
195
+ setRailCollapsed: (collapsed: boolean) => void;
196
+ toggleRail: () => void;
197
+ /**
198
+ * Rail width in px — derived from {@link railCollapsed}: the icon-only width
199
+ * when collapsed (or when the rail is not label-capable), the labeled width
200
+ * when expanded.
201
+ */
171
202
  railWidth: number;
172
203
  }
173
204
  interface SidebarProviderProps {
174
205
  defaultPanelOpen?: boolean;
175
206
  defaultMode?: string;
176
207
  hasPanels?: boolean;
177
- /** Rail width in px. Defaults to the 64px icon-only rail. */
178
- railWidth?: number;
208
+ /**
209
+ * Whether the rail can show labels beside icons. When `true`, the expanded
210
+ * rail renders at {@link SIDEBAR_RAIL_LABELED_WIDTH} and the user can collapse
211
+ * it to the icon-only {@link SIDEBAR_RAIL_WIDTH} via {@link toggleRail}. When
212
+ * `false` (default) the rail is permanently icon-only and `railCollapsed` has
213
+ * no width effect.
214
+ */
215
+ labeledRail?: boolean;
179
216
  /**
180
217
  * Controlled panel-open state. When provided, the provider treats panel
181
218
  * visibility as controlled: it never reads or writes localStorage and
@@ -186,9 +223,19 @@ interface SidebarProviderProps {
186
223
  */
187
224
  panelOpen?: boolean;
188
225
  onPanelOpenChange?: (open: boolean) => void;
226
+ /**
227
+ * Controlled rail-collapsed state. Parallels {@link panelOpen}: when provided
228
+ * the provider never touches localStorage for the rail and reports changes
229
+ * through {@link onRailCollapsedChange}. Seed it from a cookie in SSR apps so
230
+ * the rail width survives reload without a hydration mismatch.
231
+ */
232
+ railCollapsed?: boolean;
233
+ onRailCollapsedChange?: (collapsed: boolean) => void;
234
+ /** Initial rail-collapsed state for the uncontrolled path. Default: expanded. */
235
+ defaultRailCollapsed?: boolean;
189
236
  children: React.ReactNode;
190
237
  }
191
- declare function SidebarProvider({ defaultPanelOpen, defaultMode, hasPanels, railWidth, panelOpen: controlledPanelOpen, onPanelOpenChange, children, }: SidebarProviderProps): react_jsx_runtime.JSX.Element;
238
+ declare function SidebarProvider({ defaultPanelOpen, defaultMode, hasPanels, labeledRail, panelOpen: controlledPanelOpen, onPanelOpenChange, railCollapsed: controlledRailCollapsed, onRailCollapsedChange, defaultRailCollapsed, children, }: SidebarProviderProps): react_jsx_runtime.JSX.Element;
192
239
  declare function useSidebar(): SidebarContextValue;
193
240
 
194
241
  interface CreditBalanceProps {
@@ -633,8 +680,24 @@ interface SidebarLayoutProps {
633
680
  defaultPanelOpen?: boolean;
634
681
  /** Hide the whole sidebar below this breakpoint (content spans full width). */
635
682
  hideBelow?: "md" | "lg";
636
- /** Show text labels beside the rail icons on a wider rail (vs. icon-only with hover tooltips). */
683
+ /**
684
+ * Make the rail label-capable: it renders labels beside the icons on a wider
685
+ * rail and gains a collapse/expand control that toggles between the labeled
686
+ * width and the icon-only rail (icons + hover tooltips). When omitted the rail
687
+ * is permanently icon-only with no collapse control.
688
+ */
637
689
  railLabels?: boolean;
690
+ /**
691
+ * Controlled rail-collapsed state. Provide it (seeded from a server-readable
692
+ * source such as a cookie) in SSR apps so the collapsed/expanded rail survives
693
+ * reload without a hydration mismatch. Omit to let the rail persist to
694
+ * localStorage. Independent of `panelOpen` — the nav rail and the slide-out
695
+ * panel collapse separately. Only meaningful with `railLabels`.
696
+ */
697
+ railCollapsed?: boolean;
698
+ onRailCollapsedChange?: (collapsed: boolean) => void;
699
+ /** Initial rail-collapsed state when uncontrolled (default: expanded). */
700
+ defaultRailCollapsed?: boolean;
638
701
  /**
639
702
  * Close the panel when a link nav item is clicked. Useful when the panel is
640
703
  * contextual to one section (e.g. a chat thread list) rather than a global
@@ -653,7 +716,7 @@ interface SidebarLayoutProps {
653
716
  * hide all live here. Pair `panelOpen`/`onPanelOpenChange` with a cookie for
654
717
  * SSR-safe persistence.
655
718
  */
656
- declare function SidebarLayout({ panelOpen, onPanelOpenChange, defaultPanelOpen, ...props }: SidebarLayoutProps): react_jsx_runtime.JSX.Element;
719
+ declare function SidebarLayout({ panelOpen, onPanelOpenChange, defaultPanelOpen, railCollapsed, onRailCollapsedChange, defaultRailCollapsed, ...props }: SidebarLayoutProps): react_jsx_runtime.JSX.Element;
657
720
 
658
721
  interface Profile {
659
722
  id: string;
@@ -898,4 +961,4 @@ interface InfoPanelProps {
898
961
  }
899
962
  declare function InfoPanel({ label, title, description, className }: InfoPanelProps): react_jsx_runtime.JSX.Element;
900
963
 
901
- export { ActivityFeed, type ActivityFeedProps, type ActivityItem, BackendConfig, type BackendConfigProps, type BackendStatusData, ClusterStatusBar, type ClusterStatusBarProps, type ClusterStatusItem, CreditBalance, type CreditBalanceProps, DashboardLayout, type DashboardLayoutProps, type Profile as DashboardProfile, type SnapshotInfo as DashboardSnapshotInfo, type DashboardUser, type ExposedPort, type GitCommitData, GitPanel, type GitPanelProps, type GitStatusData, HARNESS_BRAND, type HarnessBrand, HarnessLogo, type HarnessLogoProps, HarnessType, INSUFFICIENT_BALANCE_CODE, InfoPanel, type InfoPanelProps, type InsufficientBalance, type Invoice, InvoiceTable, type InvoiceTableProps, type McpServer, MetricAreaChart, type MetricAreaChartProps, type MetricChartPoint, type MetricChartTone, type NavItem, NetworkConfig, type NetworkConfigData, type NetworkConfigProps, NewSandboxCard, type NewSandboxCardProps, OutOfCreditsModal, type OutOfCreditsModalProps, type PlanCardData, PlanCards, type PlanCardsProps, type PlanFeature, PortsList, type PortsListProps, type ProcessInfo, ProcessList, type ProcessListProps, type ProductVariant, ProfileAvatar, type ProfileAvatarProps, ProfileComparison, type ProfileComparisonProps, ProfileSelector, type ProfileSelectorProps, PromoBanner, type PromoBannerProps, RailButton, type RailButtonProps, RailFlyout, type RailFlyoutProps, RailModeButton, type RailModeButtonProps, RailSeparator, type RailSeparatorProps, ResourceMeter, type ResourceMeterProps, ResourceSnapshot, type ResourceSnapshotItem, type ResourceSnapshotProps, SIDEBAR_MOBILE_WIDTH, SIDEBAR_PANEL_WIDTH, SIDEBAR_RAIL_WIDTH, SIDEBAR_TOTAL_WIDTH, SandboxCard, type SandboxCardData, type SandboxCardProps, type SandboxStatus, SandboxTable, type SandboxTableProps, Sidebar, SidebarContent, type SidebarContentProps, SidebarLayout, type SidebarLayoutFlyoutItem, type SidebarLayoutNavItem, type SidebarLayoutProps, SidebarPanel, SidebarPanelContent, type SidebarPanelContentProps, SidebarPanelHeader, type SidebarPanelHeaderProps, type SidebarPanelProps, type SidebarProps, SidebarProvider, type SidebarProviderProps, SidebarRail, SidebarRailFooter, type SidebarRailFooterProps, SidebarRailHeader, type SidebarRailHeaderProps, SidebarRailNav, type SidebarRailNavProps, type SidebarRailProps, type SidebarUser, SnapshotList, type SnapshotListProps, SystemLogsViewer, type SystemLogsViewerProps, type TeamRole, UsageSummary, type UsageSummaryData, type UsageSummaryProps, type Variant, VariantList, type VariantListProps, type VariantOutcome, type VariantStatus, canAdminSandbox, parseInsufficientBalance, useSidebar };
964
+ export { ActivityFeed, type ActivityFeedProps, type ActivityItem, BackendConfig, type BackendConfigProps, type BackendStatusData, ClusterStatusBar, type ClusterStatusBarProps, type ClusterStatusItem, CreditBalance, type CreditBalanceProps, DashboardLayout, type DashboardLayoutProps, type Profile as DashboardProfile, type SnapshotInfo as DashboardSnapshotInfo, type DashboardUser, type ExposedPort, type GitCommitData, GitPanel, type GitPanelProps, type GitStatusData, HARNESS_BRAND, type HarnessBrand, HarnessLogo, type HarnessLogoProps, HarnessType, INSUFFICIENT_BALANCE_CODE, InfoPanel, type InfoPanelProps, type InsufficientBalance, type Invoice, InvoiceTable, type InvoiceTableProps, type McpServer, MetricAreaChart, type MetricAreaChartProps, type MetricChartPoint, type MetricChartTone, type NavItem, NetworkConfig, type NetworkConfigData, type NetworkConfigProps, NewSandboxCard, type NewSandboxCardProps, OutOfCreditsModal, type OutOfCreditsModalProps, type PlanCardData, PlanCards, type PlanCardsProps, type PlanFeature, PortsList, type PortsListProps, type ProcessInfo, ProcessList, type ProcessListProps, type ProductVariant, ProfileAvatar, type ProfileAvatarProps, ProfileComparison, type ProfileComparisonProps, ProfileSelector, type ProfileSelectorProps, PromoBanner, type PromoBannerProps, RailButton, type RailButtonProps, RailCollapseToggle, type RailCollapseToggleProps, RailFlyout, type RailFlyoutProps, RailModeButton, type RailModeButtonProps, RailSeparator, type RailSeparatorProps, RailThemeToggle, ResourceMeter, type ResourceMeterProps, ResourceSnapshot, type ResourceSnapshotItem, type ResourceSnapshotProps, SIDEBAR_MOBILE_WIDTH, SIDEBAR_PANEL_WIDTH, SIDEBAR_RAIL_LABELED_WIDTH, SIDEBAR_RAIL_WIDTH, SIDEBAR_TOTAL_WIDTH, SandboxCard, type SandboxCardData, type SandboxCardProps, type SandboxStatus, SandboxTable, type SandboxTableProps, Sidebar, SidebarContent, type SidebarContentProps, SidebarLayout, type SidebarLayoutFlyoutItem, type SidebarLayoutNavItem, type SidebarLayoutProps, SidebarPanel, SidebarPanelContent, type SidebarPanelContentProps, SidebarPanelHeader, type SidebarPanelHeaderProps, type SidebarPanelProps, type SidebarProps, SidebarProvider, type SidebarProviderProps, SidebarRail, SidebarRailFooter, type SidebarRailFooterProps, SidebarRailHeader, type SidebarRailHeaderProps, SidebarRailNav, type SidebarRailNavProps, type SidebarRailProps, type SidebarUser, SnapshotList, type SnapshotListProps, SystemLogsViewer, type SystemLogsViewerProps, type TeamRole, UsageSummary, type UsageSummaryData, type UsageSummaryProps, type Variant, VariantList, type VariantListProps, type VariantOutcome, type VariantStatus, canAdminSandbox, parseInsufficientBalance, useSidebar };
package/dist/dashboard.js CHANGED
@@ -19,13 +19,16 @@ import {
19
19
  ProfileSelector,
20
20
  PromoBanner,
21
21
  RailButton,
22
+ RailCollapseToggle,
22
23
  RailFlyout,
23
24
  RailModeButton,
24
25
  RailSeparator,
26
+ RailThemeToggle,
25
27
  ResourceMeter,
26
28
  ResourceSnapshot,
27
29
  SIDEBAR_MOBILE_WIDTH,
28
30
  SIDEBAR_PANEL_WIDTH,
31
+ SIDEBAR_RAIL_LABELED_WIDTH,
29
32
  SIDEBAR_RAIL_WIDTH,
30
33
  SIDEBAR_TOTAL_WIDTH,
31
34
  SandboxCard,
@@ -48,7 +51,7 @@ import {
48
51
  canAdminSandbox,
49
52
  parseInsufficientBalance,
50
53
  useSidebar
51
- } from "./chunk-F2M5MN7K.js";
54
+ } from "./chunk-SXAMR3TV.js";
52
55
  import {
53
56
  BillingDashboard,
54
57
  InfoPanel,
@@ -107,13 +110,16 @@ export {
107
110
  ProfileSelector,
108
111
  PromoBanner,
109
112
  RailButton,
113
+ RailCollapseToggle,
110
114
  RailFlyout,
111
115
  RailModeButton,
112
116
  RailSeparator,
117
+ RailThemeToggle,
113
118
  ResourceMeter,
114
119
  ResourceSnapshot,
115
120
  SIDEBAR_MOBILE_WIDTH,
116
121
  SIDEBAR_PANEL_WIDTH,
122
+ SIDEBAR_RAIL_LABELED_WIDTH,
117
123
  SIDEBAR_RAIL_WIDTH,
118
124
  SIDEBAR_TOTAL_WIDTH,
119
125
  SandboxCard,