@tangle-network/sandbox-ui 0.99.2 → 0.100.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,10 @@
1
+ // src/lib/motion.ts
2
+ var MOTION_CONTROL = "duration-[var(--duration-fast)] ease-[var(--ease-standard)]";
3
+ var MOTION_SURFACE = "duration-[var(--duration-base)] ease-[var(--ease-entrance)]";
4
+ var MOTION_TRAVEL = "duration-[var(--duration-slow)] ease-[var(--ease-standard)]";
5
+
6
+ export {
7
+ MOTION_CONTROL,
8
+ MOTION_SURFACE,
9
+ MOTION_TRAVEL
10
+ };
@@ -229,7 +229,7 @@ function imageExtension(file) {
229
229
  const fromName = /\.([a-z0-9]+)$/i.exec(file.name)?.[1];
230
230
  return fromName?.toLowerCase() ?? "png";
231
231
  }
232
- var MentionEditor = React.lazy(() => import("./mention-editor-3Q6MYOIU.js"));
232
+ var MentionEditor = React.lazy(() => import("./mention-editor-Q3TKSZDL.js"));
233
233
  function AgentComposer({
234
234
  value,
235
235
  onChange,
@@ -415,7 +415,7 @@ function AgentComposer({
415
415
  file.fileCount,
416
416
  ")"
417
417
  ] }),
418
- file.status === "uploading" && /* @__PURE__ */ jsx2(Loader2, { className: "size-3 animate-spin" }),
418
+ file.status === "uploading" && /* @__PURE__ */ jsx2(Loader2, { className: "size-3 animate-spin", "data-motion": "essential" }),
419
419
  isError && file.errorMessage && /* @__PURE__ */ jsx2("span", { className: "max-w-[150px] truncate text-[var(--status-error,#ff4d6d)]/80", children: file.errorMessage }),
420
420
  isError && onRetryFile && /* @__PURE__ */ jsx2(
421
421
  "button",
@@ -588,7 +588,7 @@ function AgentComposer({
588
588
  "flex size-9 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground",
589
589
  "transition-opacity hover:opacity-90 disabled:opacity-40"
590
590
  ),
591
- children: busy ? /* @__PURE__ */ jsx2(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx2(ArrowUp, { className: "size-4" })
591
+ children: busy ? /* @__PURE__ */ jsx2(Loader2, { className: "size-4 animate-spin", "data-motion": "essential" }) : /* @__PURE__ */ jsx2(ArrowUp, { className: "size-4" })
592
592
  }
593
593
  )
594
594
  ] })
@@ -1,3 +1,6 @@
1
+ import {
2
+ MOTION_CONTROL
3
+ } from "./chunk-BGVEHENC.js";
1
4
  import {
2
5
  cn
3
6
  } from "./chunk-EI44GEQ5.js";
@@ -639,7 +642,13 @@ function StatusBanner({ type, message, detail, onDismiss, className }) {
639
642
  className
640
643
  ),
641
644
  children: [
642
- /* @__PURE__ */ jsx4(Icon, { className: cn("h-4 w-4 shrink-0", style.text, isAnimated && "animate-spin") }),
645
+ /* @__PURE__ */ jsx4(
646
+ Icon,
647
+ {
648
+ className: cn("h-4 w-4 shrink-0", style.text, isAnimated && "animate-spin"),
649
+ ...isAnimated ? { "data-motion": "essential" } : {}
650
+ }
651
+ ),
643
652
  /* @__PURE__ */ jsx4("span", { className: "font-medium text-foreground", children: message }),
644
653
  detail && /* @__PURE__ */ jsx4("span", { className: "font-mono text-xs text-muted-foreground tabular-nums", children: detail }),
645
654
  onDismiss && /* @__PURE__ */ jsx4(
@@ -1092,7 +1101,7 @@ function SessionSidebar({
1092
1101
  type: "button",
1093
1102
  onClick: onCreate,
1094
1103
  title: createLabel,
1095
- className: "flex h-7 w-7 items-center justify-center rounded-[var(--radius-sm)] border border-[var(--md3-outline-variant)] text-muted-foreground transition-colors hover:bg-[var(--accent-surface-soft)] hover:text-foreground",
1104
+ className: cn("flex h-7 w-7 items-center justify-center rounded-[var(--radius-sm)] border border-[var(--md3-outline-variant)] text-muted-foreground transition-colors hover:bg-[var(--accent-surface-soft)] hover:text-foreground", MOTION_CONTROL),
1096
1105
  children: /* @__PURE__ */ jsx8(Plus, { className: "h-3.5 w-3.5" })
1097
1106
  }
1098
1107
  )
@@ -1108,7 +1117,7 @@ function SessionSidebar({
1108
1117
  onChange: (event) => setQuery(event.target.value),
1109
1118
  placeholder: searchPlaceholder,
1110
1119
  "aria-label": searchPlaceholder,
1111
- className: "h-8 w-full rounded-[var(--radius-sm)] border border-[var(--md3-outline-variant)] bg-surface-container-lowest pl-7 pr-2 text-sm text-foreground placeholder:text-muted-foreground transition-colors focus-visible:border-[var(--border-accent)] focus-visible:bg-transparent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--border-accent)]"
1120
+ className: cn("h-8 w-full rounded-[var(--radius-sm)] border border-[var(--md3-outline-variant)] bg-surface-container-lowest pl-7 pr-2 text-sm text-foreground placeholder:text-muted-foreground transition-colors focus-visible:border-[var(--border-accent)] focus-visible:bg-transparent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--border-accent)]", MOTION_CONTROL)
1112
1121
  }
1113
1122
  )
1114
1123
  ] }),
@@ -1121,6 +1130,7 @@ function SessionSidebar({
1121
1130
  onClick: () => setActiveFilterId(filter.id),
1122
1131
  className: cn(
1123
1132
  "inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs font-medium transition-colors",
1133
+ MOTION_CONTROL,
1124
1134
  isSelected ? "border-[var(--border-accent)] bg-[var(--accent-surface-soft)] text-primary" : "border-[var(--md3-outline-variant)] text-muted-foreground hover:text-foreground"
1125
1135
  ),
1126
1136
  children: [
@@ -1135,72 +1145,90 @@ function SessionSidebar({
1135
1145
  /* @__PURE__ */ jsx8("nav", { "aria-label": "Sessions", className: "flex-1 overflow-y-auto px-1.5 py-1.5", children: visibleItems.length === 0 ? /* @__PURE__ */ jsxs7("div", { className: "flex flex-col items-center justify-center gap-2 px-4 py-10 text-center text-sm text-muted-foreground", children: [
1136
1146
  /* @__PURE__ */ jsx8(MessageSquareText, { className: "h-5 w-5 opacity-50", "aria-hidden": "true" }),
1137
1147
  /* @__PURE__ */ jsx8("p", { children: query.trim() ? `No sessions match "${query.trim()}".` : emptyMessage })
1138
- ] }) : /* @__PURE__ */ jsx8("ul", { className: "space-y-px", children: visibleItems.map((item) => {
1148
+ ] }) : /* @__PURE__ */ jsx8("ul", { className: "space-y-px", children: visibleItems.map((item, index) => {
1139
1149
  const session = sessionsById.get(item.id) ?? null;
1140
1150
  const isActive = currentItemId === item.id;
1141
1151
  const status = session?.status ?? item.status;
1152
+ const isWorking = status === "running";
1142
1153
  const visibleBadges = [
1143
1154
  ...item.isPinned ? [{ id: `${item.id}-pinned`, label: "Pinned", tone: "neutral" }] : [],
1144
1155
  ...item.badges ?? []
1145
1156
  ];
1146
- return /* @__PURE__ */ jsx8("li", { className: "animate-row-in", children: /* @__PURE__ */ jsxs7(
1147
- "div",
1157
+ return /* @__PURE__ */ jsx8(
1158
+ "li",
1148
1159
  {
1149
- className: cn(
1150
- "group relative flex items-center gap-2 rounded-[var(--radius-sm)] px-2.5 py-1.5 transition-colors",
1151
- isActive ? "bg-accent font-medium text-foreground" : "text-muted-foreground hover:bg-accent/30"
1152
- ),
1153
- children: [
1154
- /* @__PURE__ */ jsxs7(
1155
- "button",
1156
- {
1157
- type: "button",
1158
- onClick: () => {
1159
- if (onSelectItem) {
1160
- onSelectItem(item);
1161
- return;
1160
+ className: "agent-arrive",
1161
+ style: { "--stagger-index": index },
1162
+ children: /* @__PURE__ */ jsxs7(
1163
+ "div",
1164
+ {
1165
+ className: cn(
1166
+ "group relative flex items-center gap-2 rounded-[var(--radius-sm)] px-2.5 py-1.5 transition-colors",
1167
+ MOTION_CONTROL,
1168
+ isActive ? "bg-accent font-medium text-foreground" : "text-muted-foreground hover:bg-accent/30"
1169
+ ),
1170
+ children: [
1171
+ /* @__PURE__ */ jsxs7(
1172
+ "button",
1173
+ {
1174
+ type: "button",
1175
+ onClick: () => {
1176
+ if (onSelectItem) {
1177
+ onSelectItem(item);
1178
+ return;
1179
+ }
1180
+ navigateToHref(item.href);
1181
+ },
1182
+ "aria-current": isActive ? "page" : void 0,
1183
+ className: "min-w-0 flex flex-1 items-center gap-2 text-left",
1184
+ children: [
1185
+ /* @__PURE__ */ jsx8("span", { className: cn("h-1.5 w-1.5 shrink-0 rounded-full", statusDot(status)) }),
1186
+ /* @__PURE__ */ jsxs7("div", { className: "min-w-0 flex-1", children: [
1187
+ /* @__PURE__ */ jsx8(
1188
+ "div",
1189
+ {
1190
+ className: cn(
1191
+ "truncate text-sm font-medium",
1192
+ isActive && "text-foreground",
1193
+ isWorking && "agent-shimmer"
1194
+ ),
1195
+ ...isWorking ? { "data-motion": "essential" } : {},
1196
+ children: item.title
1197
+ }
1198
+ ),
1199
+ item.subtitle && /* @__PURE__ */ jsx8("div", { className: "truncate text-xs leading-tight text-muted-foreground", children: item.subtitle })
1200
+ ] })
1201
+ ]
1162
1202
  }
1163
- navigateToHref(item.href);
1164
- },
1165
- "aria-current": isActive ? "page" : void 0,
1166
- className: "min-w-0 flex flex-1 items-center gap-2 text-left",
1167
- children: [
1168
- /* @__PURE__ */ jsx8("span", { className: cn("h-1.5 w-1.5 shrink-0 rounded-full", statusDot(status)) }),
1169
- /* @__PURE__ */ jsxs7("div", { className: "min-w-0 flex-1", children: [
1170
- /* @__PURE__ */ jsx8("div", { className: cn(
1171
- "truncate text-sm font-medium",
1172
- isActive && "text-foreground"
1173
- ), children: item.title }),
1174
- item.subtitle && /* @__PURE__ */ jsx8("div", { className: "truncate text-xs leading-tight text-muted-foreground", children: item.subtitle })
1175
- ] })
1176
- ]
1177
- }
1178
- ),
1179
- /* @__PURE__ */ jsxs7("div", { className: "flex shrink-0 items-center gap-1", children: [
1180
- visibleBadges.length > 0 && visibleBadges.slice(0, 1).map((badge) => /* @__PURE__ */ jsx8(
1181
- "span",
1182
- {
1183
- className: cn(
1184
- "rounded-full border px-1.5 py-px text-[8px] font-semibold uppercase",
1185
- badgeTone(badge.tone)
1186
- ),
1187
- children: badge.label
1188
- },
1189
- badge.id
1190
- )),
1191
- session?.isForeground && /* @__PURE__ */ jsx8("span", { className: "h-1.5 w-1.5 rounded-full bg-[var(--status-running)]", title: "Live" }),
1192
- renderItemActions ? /* @__PURE__ */ jsx8(
1193
- "div",
1194
- {
1195
- className: "opacity-0 transition-opacity group-hover:opacity-100",
1196
- onClick: (event) => event.stopPropagation(),
1197
- children: renderItemActions(item, { session, isActive })
1198
- }
1199
- ) : null
1200
- ] })
1201
- ]
1202
- }
1203
- ) }, item.id);
1203
+ ),
1204
+ /* @__PURE__ */ jsxs7("div", { className: "flex shrink-0 items-center gap-1", children: [
1205
+ visibleBadges.length > 0 && visibleBadges.slice(0, 1).map((badge) => /* @__PURE__ */ jsx8(
1206
+ "span",
1207
+ {
1208
+ className: cn(
1209
+ "rounded-full border px-1.5 py-px text-[8px] font-semibold uppercase",
1210
+ badgeTone(badge.tone)
1211
+ ),
1212
+ children: badge.label
1213
+ },
1214
+ badge.id
1215
+ )),
1216
+ session?.isForeground && /* @__PURE__ */ jsx8("span", { className: "h-1.5 w-1.5 rounded-full bg-[var(--status-running)]", title: "Live" }),
1217
+ renderItemActions ? /* @__PURE__ */ jsx8(
1218
+ "div",
1219
+ {
1220
+ className: cn("opacity-0 transition-opacity group-hover:opacity-100", MOTION_CONTROL),
1221
+ onClick: (event) => event.stopPropagation(),
1222
+ children: renderItemActions(item, { session, isActive })
1223
+ }
1224
+ ) : null
1225
+ ] })
1226
+ ]
1227
+ }
1228
+ )
1229
+ },
1230
+ item.id
1231
+ );
1204
1232
  }) }) }),
1205
1233
  activityMonitor && /* @__PURE__ */ jsx8("div", { className: "border-t border-[var(--md3-outline-variant)] px-2 py-1.5", children: activityMonitor }),
1206
1234
  links.length > 0 && /* @__PURE__ */ jsx8("nav", { "aria-label": "Workspace sections", className: "border-t border-[var(--md3-outline-variant)] px-1.5 py-1.5", children: /* @__PURE__ */ jsx8("div", { className: "space-y-px", children: links.map((link) => {
@@ -1216,7 +1244,7 @@ function SessionSidebar({
1216
1244
  }
1217
1245
  navigateToHref(link.href);
1218
1246
  },
1219
- className: "flex w-full items-center gap-2 rounded-[var(--radius-sm)] px-3 py-2 text-left text-sm text-muted-foreground transition-colors hover:bg-accent/30 hover:text-foreground",
1247
+ className: cn("flex w-full items-center gap-2 rounded-[var(--radius-sm)] px-3 py-2 text-left text-sm text-muted-foreground transition-colors hover:bg-accent/30 hover:text-foreground", MOTION_CONTROL),
1220
1248
  children: [
1221
1249
  /* @__PURE__ */ jsx8(Icon, { className: "h-4 w-4 shrink-0" }),
1222
1250
  /* @__PURE__ */ jsx8("span", { className: "truncate", children: link.label })
@@ -1228,7 +1256,7 @@ function SessionSidebar({
1228
1256
  resizable && /* @__PURE__ */ jsx8(
1229
1257
  "div",
1230
1258
  {
1231
- className: "absolute top-0 right-0 bottom-0 w-1 cursor-col-resize hover:bg-[var(--accent-surface-soft)] active:bg-[var(--border-accent)] transition-colors",
1259
+ className: cn("absolute top-0 right-0 bottom-0 w-1 cursor-col-resize hover:bg-[var(--accent-surface-soft)] active:bg-[var(--border-accent)] transition-colors", MOTION_CONTROL),
1232
1260
  onPointerDown: resize.onPointerDown,
1233
1261
  onPointerMove: resize.onPointerMove,
1234
1262
  onPointerUp: resize.onPointerUp,
@@ -1267,7 +1295,7 @@ function SessionStatusDot({ session }) {
1267
1295
  return /* @__PURE__ */ jsx9(AlertCircle2, { className: "h-3 w-3 text-[var(--surface-danger-text)]" });
1268
1296
  }
1269
1297
  if (session.status === "running") {
1270
- return /* @__PURE__ */ jsx9(LoaderCircle, { className: "h-3 w-3 animate-spin text-primary" });
1298
+ return /* @__PURE__ */ jsx9(LoaderCircle, { className: "h-3 w-3 animate-spin text-primary", "data-motion": "essential" });
1271
1299
  }
1272
1300
  if (session.status === "attention-needed") {
1273
1301
  return /* @__PURE__ */ jsx9(Activity, { className: "h-3 w-3 text-[var(--surface-warning-text)]" });
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-3J6FG3FJ.js";
4
4
  import {
5
5
  PortsList
6
- } from "./chunk-Y4OTBMZS.js";
6
+ } from "./chunk-3W343PY5.js";
7
7
  import {
8
8
  TerminalView
9
9
  } from "./chunk-WYMAC3T3.js";
@@ -594,7 +594,7 @@ function ConnectorCatalogList({
594
594
  {
595
595
  type: "button",
596
596
  onClick: () => onRequestIntegration(search.trim()),
597
- className: "text-primary text-sm transition-colors hover:underline",
597
+ className: "text-[var(--accent-text)] text-sm transition-colors hover:underline",
598
598
  children: "Request an integration"
599
599
  }
600
600
  )
@@ -147,8 +147,14 @@ interface RailButtonProps {
147
147
  * Defaults to `"normal"`.
148
148
  */
149
149
  variant?: "normal" | "primary";
150
+ /**
151
+ * Inline style for the rendered row. Layouts use it to set `--stagger-index`
152
+ * so a rail of nav items arrives as a sequence rather than a flash; the
153
+ * variable has to land on the animated element itself, which is this one.
154
+ */
155
+ style?: React.CSSProperties;
150
156
  }
151
- declare function RailButton({ icon: Icon, label, isActive, badge, onClick, className, showLabel, asChild, children, variant }: RailButtonProps): React.JSX.Element;
157
+ declare function RailButton({ icon: Icon, label, isActive, badge, onClick, className, showLabel, asChild, children, variant, style }: RailButtonProps): React.JSX.Element;
152
158
  interface RailFlyoutProps {
153
159
  icon: React.ComponentType<{
154
160
  className?: string;
@@ -163,6 +169,8 @@ interface RailFlyoutProps {
163
169
  /** Flyout body — the sub-surfaces revealed when the item is opened. */
164
170
  children: React.ReactNode;
165
171
  className?: string;
172
+ /** @see {@link RailButtonProps.style} — carries `--stagger-index` in a rail. */
173
+ style?: React.CSSProperties;
166
174
  }
167
175
  /**
168
176
  * A rail entry that reveals its sub-surfaces in a small panel anchored to the
@@ -171,7 +179,7 @@ interface RailFlyoutProps {
171
179
  * item inside it. Use for a single "door" (e.g. Studio) that fronts several
172
180
  * related destinations without permanently spending rail height on them.
173
181
  */
174
- declare function RailFlyout({ icon: Icon, label, isActive, showLabel, title, children, className, }: RailFlyoutProps): React.JSX.Element;
182
+ declare function RailFlyout({ icon: Icon, label, isActive, showLabel, title, children, className, style, }: RailFlyoutProps): React.JSX.Element;
175
183
  /** A single entry in a sub-item's hover actions menu (kebab). */
176
184
  interface RailSubItemAction {
177
185
  id: string;
@@ -229,6 +237,8 @@ interface RailExpandableProps {
229
237
  onNavigate?: () => void;
230
238
  LinkComponent?: React.ComponentType<any>;
231
239
  className?: string;
240
+ /** @see {@link RailButtonProps.style} — carries `--stagger-index` in a rail. */
241
+ style?: React.CSSProperties;
232
242
  }
233
243
  /**
234
244
  * A nav item that reveals sub-items. On the labeled rail it expands inline as an
@@ -238,7 +248,7 @@ interface RailExpandableProps {
238
248
  * Sub-items can be fixed (`subItems`) or lazy (`loadSubItems`, with a loading
239
249
  * skeleton). Used for e.g. a "History" entry fronting recent sessions.
240
250
  */
241
- declare function RailExpandable({ icon: Icon, label, href, isActive, showLabel, subItems, loadSubItems, activeSubIds, emptyLabel, defaultOpen, onNavigate, LinkComponent, className, }: RailExpandableProps): React.JSX.Element;
251
+ declare function RailExpandable({ icon: Icon, label, href, isActive, showLabel, subItems, loadSubItems, activeSubIds, emptyLabel, defaultOpen, onNavigate, LinkComponent, className, style, }: RailExpandableProps): React.JSX.Element;
242
252
  interface RailModeButtonProps {
243
253
  mode: string;
244
254
  icon: React.ComponentType<{
@@ -249,8 +259,10 @@ interface RailModeButtonProps {
249
259
  className?: string;
250
260
  /** Show label text next to icon (for mobile drawer) */
251
261
  showLabel?: boolean;
262
+ /** @see {@link RailButtonProps.style} — carries `--stagger-index` in a rail. */
263
+ style?: React.CSSProperties;
252
264
  }
253
- declare function RailModeButton({ mode, icon, label, badge, className, showLabel }: RailModeButtonProps): React.JSX.Element;
265
+ declare function RailModeButton({ mode, icon, label, badge, className, showLabel, style }: RailModeButtonProps): React.JSX.Element;
254
266
  interface SidebarPanelProps {
255
267
  children: React.ReactNode;
256
268
  className?: string;
@@ -310,6 +322,13 @@ interface RailTooltipProps {
310
322
  * short delay, and `pointer-events-none` so it never interferes with the
311
323
  * trigger. Hidden from the a11y tree (`aria-hidden`) since the trigger already
312
324
  * carries an accessible name.
325
+ *
326
+ * `disabled` suppresses the tooltip but NOT the wrapper. Returning a fragment
327
+ * instead changes the rendered element type, and React reconciles by type: the
328
+ * trigger inside would be torn down and rebuilt every time the rail toggles
329
+ * between icon-only and labeled. A rebuilt element replays its CSS entrance, so
330
+ * the whole nav would re-stagger on a collapse that moved nothing off screen.
331
+ * One element type, both states.
313
332
  */
314
333
  declare function RailTooltip({ label, children, disabled, className }: RailTooltipProps): React.JSX.Element;
315
334
 
package/dist/dashboard.js CHANGED
@@ -60,7 +60,7 @@ import {
60
60
  chatCapableHarnesses,
61
61
  parseInsufficientBalance,
62
62
  useSidebar
63
- } from "./chunk-Y4OTBMZS.js";
63
+ } from "./chunk-3W343PY5.js";
64
64
  import {
65
65
  BillingDashboard,
66
66
  InfoPanel,
@@ -75,6 +75,7 @@ import {
75
75
  canonicalModelId,
76
76
  modelBrandFor
77
77
  } from "./chunk-RQZRXKMR.js";
78
+ import "./chunk-BGVEHENC.js";
78
79
  import "./chunk-EI44GEQ5.js";
79
80
  import "./chunk-CZ7CSFO4.js";
80
81
  export {