@camstack/ui-library 0.1.32 → 0.1.35

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.
package/dist/index.js CHANGED
@@ -683,7 +683,7 @@ function DialogTrigger({ children, ...props }) {
683
683
  return /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => setOpen(true), ...props, children });
684
684
  }
685
685
  var contentVariants = cva7(
686
- "bg-background-elevated border border-border rounded-lg p-4 backdrop:bg-black/50 backdrop:backdrop-blur-sm",
686
+ "bg-background-elevated border border-border rounded-lg p-4 backdrop:bg-black/50 backdrop:backdrop-blur-sm max-w-[calc(100vw-2rem)] max-h-[calc(100dvh-2rem)] overflow-y-auto",
687
687
  {
688
688
  variants: {
689
689
  width: {
@@ -1100,6 +1100,26 @@ ScrollArea.displayName = "ScrollArea";
1100
1100
  // src/primitives/floating-panel.tsx
1101
1101
  import { useRef as useRef5, useState as useState7, useCallback as useCallback6, useEffect as useEffect5 } from "react";
1102
1102
  import { X, Minimize2, Maximize2, GripHorizontal } from "lucide-react";
1103
+
1104
+ // src/hooks/use-is-mobile.ts
1105
+ import { useSyncExternalStore } from "react";
1106
+ var MOBILE_QUERY = "(max-width: 767px)";
1107
+ function subscribe(callback) {
1108
+ const mql = window.matchMedia(MOBILE_QUERY);
1109
+ mql.addEventListener("change", callback);
1110
+ return () => mql.removeEventListener("change", callback);
1111
+ }
1112
+ function getSnapshot() {
1113
+ return window.matchMedia(MOBILE_QUERY).matches;
1114
+ }
1115
+ function getServerSnapshot() {
1116
+ return false;
1117
+ }
1118
+ function useIsMobile() {
1119
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
1120
+ }
1121
+
1122
+ // src/primitives/floating-panel.tsx
1103
1123
  import { jsx as jsx19, jsxs as jsxs5 } from "react/jsx-runtime";
1104
1124
  function FloatingPanel({
1105
1125
  title,
@@ -1118,6 +1138,7 @@ function FloatingPanel({
1118
1138
  const dragging = useRef5(false);
1119
1139
  const resizing = useRef5(false);
1120
1140
  const offset = useRef5({ x: 0, y: 0 });
1141
+ const isMobile = useIsMobile();
1121
1142
  const onDragStart = useCallback6((e) => {
1122
1143
  e.preventDefault();
1123
1144
  dragging.current = true;
@@ -1150,6 +1171,42 @@ function FloatingPanel({
1150
1171
  window.removeEventListener("mouseup", onMouseUp);
1151
1172
  };
1152
1173
  }, [minWidth, minHeight]);
1174
+ if (isMobile) {
1175
+ return /* @__PURE__ */ jsxs5(
1176
+ "div",
1177
+ {
1178
+ className: cn(
1179
+ "fixed inset-x-0 bottom-0 z-50 rounded-t-xl border-t border-border bg-background-elevated shadow-2xl flex flex-col overflow-hidden",
1180
+ className
1181
+ ),
1182
+ style: { maxHeight: "60dvh" },
1183
+ children: [
1184
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between gap-2 px-3 py-2 border-b border-border shrink-0 bg-surface", children: [
1185
+ /* @__PURE__ */ jsx19("span", { className: "text-[11px] font-medium truncate", children: title }),
1186
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1 shrink-0", children: [
1187
+ /* @__PURE__ */ jsx19(
1188
+ "button",
1189
+ {
1190
+ onClick: () => setMinimized(!minimized),
1191
+ className: "p-0.5 rounded hover:bg-surface-hover text-foreground-muted transition-colors",
1192
+ children: minimized ? /* @__PURE__ */ jsx19(Maximize2, { size: 12 }) : /* @__PURE__ */ jsx19(Minimize2, { size: 12 })
1193
+ }
1194
+ ),
1195
+ /* @__PURE__ */ jsx19(
1196
+ "button",
1197
+ {
1198
+ onClick: onClose,
1199
+ className: "p-0.5 rounded hover:bg-danger/20 text-foreground-muted hover:text-danger transition-colors",
1200
+ children: /* @__PURE__ */ jsx19(X, { size: 12 })
1201
+ }
1202
+ )
1203
+ ] })
1204
+ ] }),
1205
+ !minimized && /* @__PURE__ */ jsx19("div", { className: "flex-1 min-h-0 overflow-y-auto", children })
1206
+ ]
1207
+ }
1208
+ );
1209
+ }
1153
1210
  return /* @__PURE__ */ jsxs5(
1154
1211
  "div",
1155
1212
  {
@@ -1208,8 +1265,115 @@ function FloatingPanel({
1208
1265
  );
1209
1266
  }
1210
1267
 
1268
+ // src/primitives/mobile-drawer.tsx
1269
+ import { useEffect as useEffect6, useRef as useRef6 } from "react";
1270
+ import { Fragment, jsx as jsx20, jsxs as jsxs6 } from "react/jsx-runtime";
1271
+ function MobileDrawer({ open, onClose, children, className, width = "w-64" }) {
1272
+ const drawerRef = useRef6(null);
1273
+ useEffect6(() => {
1274
+ if (!open) return;
1275
+ const handleKeyDown = (e) => {
1276
+ if (e.key === "Escape") onClose();
1277
+ };
1278
+ document.addEventListener("keydown", handleKeyDown);
1279
+ document.body.style.overflow = "hidden";
1280
+ return () => {
1281
+ document.removeEventListener("keydown", handleKeyDown);
1282
+ document.body.style.overflow = "";
1283
+ };
1284
+ }, [open, onClose]);
1285
+ return /* @__PURE__ */ jsxs6(Fragment, { children: [
1286
+ /* @__PURE__ */ jsx20(
1287
+ "div",
1288
+ {
1289
+ className: cn(
1290
+ "fixed inset-0 z-40 bg-black/50 backdrop-blur-sm transition-opacity duration-200",
1291
+ open ? "opacity-100" : "pointer-events-none opacity-0"
1292
+ ),
1293
+ onClick: onClose,
1294
+ "aria-hidden": "true"
1295
+ }
1296
+ ),
1297
+ /* @__PURE__ */ jsx20(
1298
+ "div",
1299
+ {
1300
+ ref: drawerRef,
1301
+ role: "dialog",
1302
+ "aria-modal": "true",
1303
+ className: cn(
1304
+ "fixed inset-y-0 left-0 z-50 flex flex-col bg-surface border-r border-border shadow-2xl transition-transform duration-200 ease-out",
1305
+ width,
1306
+ open ? "translate-x-0" : "-translate-x-full",
1307
+ className
1308
+ ),
1309
+ children
1310
+ }
1311
+ )
1312
+ ] });
1313
+ }
1314
+
1315
+ // src/primitives/bottom-sheet.tsx
1316
+ import { useEffect as useEffect7 } from "react";
1317
+ import { X as X2 } from "lucide-react";
1318
+ import { Fragment as Fragment2, jsx as jsx21, jsxs as jsxs7 } from "react/jsx-runtime";
1319
+ function BottomSheet({ open, onClose, title, children, className }) {
1320
+ useEffect7(() => {
1321
+ if (!open) return;
1322
+ const handleKeyDown = (e) => {
1323
+ if (e.key === "Escape") onClose();
1324
+ };
1325
+ document.addEventListener("keydown", handleKeyDown);
1326
+ document.body.style.overflow = "hidden";
1327
+ return () => {
1328
+ document.removeEventListener("keydown", handleKeyDown);
1329
+ document.body.style.overflow = "";
1330
+ };
1331
+ }, [open, onClose]);
1332
+ return /* @__PURE__ */ jsxs7(Fragment2, { children: [
1333
+ /* @__PURE__ */ jsx21(
1334
+ "div",
1335
+ {
1336
+ className: cn(
1337
+ "fixed inset-0 z-40 bg-black/50 transition-opacity duration-200",
1338
+ open ? "opacity-100" : "pointer-events-none opacity-0"
1339
+ ),
1340
+ onClick: onClose,
1341
+ "aria-hidden": "true"
1342
+ }
1343
+ ),
1344
+ /* @__PURE__ */ jsxs7(
1345
+ "div",
1346
+ {
1347
+ role: "dialog",
1348
+ "aria-modal": "true",
1349
+ className: cn(
1350
+ "fixed inset-x-0 bottom-0 z-50 flex flex-col bg-background-elevated border-t border-border rounded-t-xl shadow-2xl transition-transform duration-200 ease-out",
1351
+ "max-h-[80dvh]",
1352
+ open ? "translate-y-0" : "translate-y-full",
1353
+ className
1354
+ ),
1355
+ children: [
1356
+ /* @__PURE__ */ jsx21("div", { className: "flex justify-center pt-2 pb-1", children: /* @__PURE__ */ jsx21("div", { className: "h-1 w-8 rounded-full bg-foreground-subtle/30" }) }),
1357
+ title && /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between px-4 pb-2", children: [
1358
+ /* @__PURE__ */ jsx21("span", { className: "text-sm font-medium text-foreground", children: title }),
1359
+ /* @__PURE__ */ jsx21(
1360
+ "button",
1361
+ {
1362
+ onClick: onClose,
1363
+ className: "p-1 rounded-md hover:bg-surface-hover text-foreground-muted transition-colors",
1364
+ children: /* @__PURE__ */ jsx21(X2, { className: "h-4 w-4" })
1365
+ }
1366
+ )
1367
+ ] }),
1368
+ /* @__PURE__ */ jsx21("div", { className: "flex-1 overflow-y-auto px-4 pb-4", children })
1369
+ ]
1370
+ }
1371
+ )
1372
+ ] });
1373
+ }
1374
+
1211
1375
  // src/composites/status-badge.tsx
1212
- import { jsx as jsx20, jsxs as jsxs6 } from "react/jsx-runtime";
1376
+ import { jsx as jsx22, jsxs as jsxs8 } from "react/jsx-runtime";
1213
1377
  var statusConfig = {
1214
1378
  online: { colorClass: "bg-success", label: "Online" },
1215
1379
  offline: { colorClass: "bg-danger", label: "Offline" },
@@ -1224,7 +1388,7 @@ function StatusBadge({
1224
1388
  className
1225
1389
  }) {
1226
1390
  const config = statusConfig[status];
1227
- return /* @__PURE__ */ jsxs6(
1391
+ return /* @__PURE__ */ jsxs8(
1228
1392
  "span",
1229
1393
  {
1230
1394
  className: cn(
@@ -1233,21 +1397,21 @@ function StatusBadge({
1233
1397
  className
1234
1398
  ),
1235
1399
  children: [
1236
- showDot && /* @__PURE__ */ jsx20(
1400
+ showDot && /* @__PURE__ */ jsx22(
1237
1401
  "span",
1238
1402
  {
1239
1403
  className: cn("h-1.5 w-1.5 shrink-0 rounded-full", config.colorClass),
1240
1404
  "aria-hidden": "true"
1241
1405
  }
1242
1406
  ),
1243
- showLabel && /* @__PURE__ */ jsx20("span", { className: "text-foreground", children: config.label })
1407
+ showLabel && /* @__PURE__ */ jsx22("span", { className: "text-foreground", children: config.label })
1244
1408
  ]
1245
1409
  }
1246
1410
  );
1247
1411
  }
1248
1412
 
1249
1413
  // src/composites/provider-badge.tsx
1250
- import { jsx as jsx21, jsxs as jsxs7 } from "react/jsx-runtime";
1414
+ import { jsx as jsx23, jsxs as jsxs9 } from "react/jsx-runtime";
1251
1415
  var providerConfig = {
1252
1416
  frigate: { colorClass: "bg-provider-frigate", label: "Frigate" },
1253
1417
  scrypted: { colorClass: "bg-provider-scrypted", label: "Scrypted" },
@@ -1261,20 +1425,20 @@ function ProviderBadge({
1261
1425
  className
1262
1426
  }) {
1263
1427
  const config = providerConfig[provider];
1264
- return /* @__PURE__ */ jsxs7("span", { className: cn("inline-flex items-center gap-1.5 text-xs", className), children: [
1265
- /* @__PURE__ */ jsx21(
1428
+ return /* @__PURE__ */ jsxs9("span", { className: cn("inline-flex items-center gap-1.5 text-xs", className), children: [
1429
+ /* @__PURE__ */ jsx23(
1266
1430
  "span",
1267
1431
  {
1268
1432
  className: cn("h-1.5 w-1.5 shrink-0 rounded-sm", config.colorClass),
1269
1433
  "aria-hidden": "true"
1270
1434
  }
1271
1435
  ),
1272
- showLabel && /* @__PURE__ */ jsx21("span", { className: "text-foreground", children: config.label })
1436
+ showLabel && /* @__PURE__ */ jsx23("span", { className: "text-foreground", children: config.label })
1273
1437
  ] });
1274
1438
  }
1275
1439
 
1276
1440
  // src/composites/version-badge.tsx
1277
- import { jsx as jsx22 } from "react/jsx-runtime";
1441
+ import { jsx as jsx24 } from "react/jsx-runtime";
1278
1442
  var VARIANT_STYLES = {
1279
1443
  success: "bg-emerald-400 text-emerald-950",
1280
1444
  warning: "bg-amber-400 text-amber-950",
@@ -1283,7 +1447,7 @@ var VARIANT_STYLES = {
1283
1447
  neutral: "bg-foreground-subtle/20 text-foreground"
1284
1448
  };
1285
1449
  function SemanticBadge({ children, variant = "neutral", mono, className }) {
1286
- return /* @__PURE__ */ jsx22("span", { className: cn(
1450
+ return /* @__PURE__ */ jsx24("span", { className: cn(
1287
1451
  "inline-flex items-center rounded-md px-2 py-0.5 text-[11px] font-bold leading-tight",
1288
1452
  mono && "font-mono",
1289
1453
  VARIANT_STYLES[variant],
@@ -1292,11 +1456,11 @@ function SemanticBadge({ children, variant = "neutral", mono, className }) {
1292
1456
  }
1293
1457
  function VersionBadge({ version, preRelease, className }) {
1294
1458
  const isPreRelease = preRelease ?? /-(alpha|beta|rc|dev|canary|next)/i.test(version);
1295
- return /* @__PURE__ */ jsx22(SemanticBadge, { variant: isPreRelease ? "warning" : "success", mono: true, className, children: version });
1459
+ return /* @__PURE__ */ jsx24(SemanticBadge, { variant: isPreRelease ? "warning" : "success", mono: true, className, children: version });
1296
1460
  }
1297
1461
 
1298
1462
  // src/composites/form-field.tsx
1299
- import { jsx as jsx23, jsxs as jsxs8 } from "react/jsx-runtime";
1463
+ import { jsx as jsx25, jsxs as jsxs10 } from "react/jsx-runtime";
1300
1464
  function FormField({
1301
1465
  label,
1302
1466
  description,
@@ -1307,7 +1471,7 @@ function FormField({
1307
1471
  className
1308
1472
  }) {
1309
1473
  const isHorizontal = orientation === "horizontal";
1310
- return /* @__PURE__ */ jsxs8(
1474
+ return /* @__PURE__ */ jsxs10(
1311
1475
  "div",
1312
1476
  {
1313
1477
  className: cn(
@@ -1316,34 +1480,34 @@ function FormField({
1316
1480
  className
1317
1481
  ),
1318
1482
  children: [
1319
- /* @__PURE__ */ jsxs8("div", { className: cn(isHorizontal ? "flex-1" : ""), children: [
1320
- /* @__PURE__ */ jsxs8(Label, { children: [
1483
+ /* @__PURE__ */ jsxs10("div", { className: cn(isHorizontal ? "flex-1" : ""), children: [
1484
+ /* @__PURE__ */ jsxs10(Label, { children: [
1321
1485
  label,
1322
- required && /* @__PURE__ */ jsx23("span", { className: "text-danger ml-0.5", children: "*" })
1486
+ required && /* @__PURE__ */ jsx25("span", { className: "text-danger ml-0.5", children: "*" })
1323
1487
  ] }),
1324
- description && /* @__PURE__ */ jsx23("p", { className: "text-foreground-subtle text-xs mt-0.5", children: description })
1488
+ description && /* @__PURE__ */ jsx25("p", { className: "text-foreground-subtle text-xs mt-0.5", children: description })
1325
1489
  ] }),
1326
- /* @__PURE__ */ jsx23("div", { className: cn(isHorizontal ? "shrink-0" : ""), children }),
1327
- error && /* @__PURE__ */ jsx23("p", { className: "text-danger text-xs", children: error })
1490
+ /* @__PURE__ */ jsx25("div", { className: cn(isHorizontal ? "shrink-0" : ""), children }),
1491
+ error && /* @__PURE__ */ jsx25("p", { className: "text-danger text-xs", children: error })
1328
1492
  ]
1329
1493
  }
1330
1494
  );
1331
1495
  }
1332
1496
 
1333
1497
  // src/composites/page-header.tsx
1334
- import { jsx as jsx24, jsxs as jsxs9 } from "react/jsx-runtime";
1498
+ import { jsx as jsx26, jsxs as jsxs11 } from "react/jsx-runtime";
1335
1499
  function PageHeader({ title, subtitle, actions, className }) {
1336
- return /* @__PURE__ */ jsxs9("div", { className: cn("flex items-center justify-between mb-3", className), children: [
1337
- /* @__PURE__ */ jsxs9("div", { children: [
1338
- /* @__PURE__ */ jsx24("h1", { className: "text-sm font-semibold text-foreground", children: title }),
1339
- subtitle && /* @__PURE__ */ jsx24("p", { className: "text-foreground-subtle text-xs", children: subtitle })
1500
+ return /* @__PURE__ */ jsxs11("div", { className: cn("flex flex-col gap-2 mb-3 sm:flex-row sm:items-center sm:justify-between", className), children: [
1501
+ /* @__PURE__ */ jsxs11("div", { children: [
1502
+ /* @__PURE__ */ jsx26("h1", { className: "text-sm font-semibold text-foreground", children: title }),
1503
+ subtitle && /* @__PURE__ */ jsx26("p", { className: "text-foreground-subtle text-xs", children: subtitle })
1340
1504
  ] }),
1341
- actions && /* @__PURE__ */ jsx24("div", { className: "flex items-center gap-2", children: actions })
1505
+ actions && /* @__PURE__ */ jsx26("div", { className: "flex items-center gap-2 flex-wrap", children: actions })
1342
1506
  ] });
1343
1507
  }
1344
1508
 
1345
1509
  // src/composites/empty-state.tsx
1346
- import { jsx as jsx25, jsxs as jsxs10 } from "react/jsx-runtime";
1510
+ import { jsx as jsx27, jsxs as jsxs12 } from "react/jsx-runtime";
1347
1511
  function EmptyState({
1348
1512
  icon: Icon,
1349
1513
  title,
@@ -1351,18 +1515,18 @@ function EmptyState({
1351
1515
  action,
1352
1516
  className
1353
1517
  }) {
1354
- return /* @__PURE__ */ jsxs10("div", { className: cn("flex flex-col items-center justify-center gap-3 py-12", className), children: [
1355
- Icon && /* @__PURE__ */ jsx25(Icon, { className: "h-12 w-12 text-foreground-subtle", "aria-hidden": "true" }),
1356
- /* @__PURE__ */ jsxs10("div", { className: "flex flex-col items-center gap-1 text-center", children: [
1357
- /* @__PURE__ */ jsx25("p", { className: "text-foreground-muted text-sm font-medium", children: title }),
1358
- description && /* @__PURE__ */ jsx25("p", { className: "text-foreground-subtle text-xs max-w-xs", children: description })
1518
+ return /* @__PURE__ */ jsxs12("div", { className: cn("flex flex-col items-center justify-center gap-3 py-12", className), children: [
1519
+ Icon && /* @__PURE__ */ jsx27(Icon, { className: "h-12 w-12 text-foreground-subtle", "aria-hidden": "true" }),
1520
+ /* @__PURE__ */ jsxs12("div", { className: "flex flex-col items-center gap-1 text-center", children: [
1521
+ /* @__PURE__ */ jsx27("p", { className: "text-foreground-muted text-sm font-medium", children: title }),
1522
+ description && /* @__PURE__ */ jsx27("p", { className: "text-foreground-subtle text-xs max-w-xs", children: description })
1359
1523
  ] }),
1360
- action && /* @__PURE__ */ jsx25("div", { className: "mt-1", children: action })
1524
+ action && /* @__PURE__ */ jsx27("div", { className: "mt-1", children: action })
1361
1525
  ] });
1362
1526
  }
1363
1527
 
1364
1528
  // src/composites/confirm-dialog.tsx
1365
- import { jsx as jsx26, jsxs as jsxs11 } from "react/jsx-runtime";
1529
+ import { jsx as jsx28, jsxs as jsxs13 } from "react/jsx-runtime";
1366
1530
  function ConfirmDialog({
1367
1531
  title,
1368
1532
  message,
@@ -1374,14 +1538,14 @@ function ConfirmDialog({
1374
1538
  open,
1375
1539
  onOpenChange
1376
1540
  }) {
1377
- return /* @__PURE__ */ jsx26(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs11(DialogContent, { children: [
1378
- /* @__PURE__ */ jsxs11(DialogHeader, { children: [
1379
- /* @__PURE__ */ jsx26(DialogTitle, { children: title }),
1380
- /* @__PURE__ */ jsx26(DialogDescription, { children: message })
1541
+ return /* @__PURE__ */ jsx28(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs13(DialogContent, { children: [
1542
+ /* @__PURE__ */ jsxs13(DialogHeader, { children: [
1543
+ /* @__PURE__ */ jsx28(DialogTitle, { children: title }),
1544
+ /* @__PURE__ */ jsx28(DialogDescription, { children: message })
1381
1545
  ] }),
1382
- /* @__PURE__ */ jsxs11(DialogFooter, { children: [
1383
- /* @__PURE__ */ jsx26(Button, { variant: "ghost", onClick: onCancel, children: cancelLabel }),
1384
- /* @__PURE__ */ jsx26(
1546
+ /* @__PURE__ */ jsxs13(DialogFooter, { children: [
1547
+ /* @__PURE__ */ jsx28(Button, { variant: "ghost", onClick: onCancel, children: cancelLabel }),
1548
+ /* @__PURE__ */ jsx28(
1385
1549
  Button,
1386
1550
  {
1387
1551
  variant: variant === "danger" ? "danger" : "primary",
@@ -1395,12 +1559,12 @@ function ConfirmDialog({
1395
1559
 
1396
1560
  // src/composites/stat-card.tsx
1397
1561
  import { TrendingUp, TrendingDown } from "lucide-react";
1398
- import { jsx as jsx27, jsxs as jsxs12 } from "react/jsx-runtime";
1562
+ import { jsx as jsx29, jsxs as jsxs14 } from "react/jsx-runtime";
1399
1563
  function StatCard({ value, label, trend, className }) {
1400
- return /* @__PURE__ */ jsxs12(Card, { className: cn("flex flex-col gap-1", className), children: [
1401
- /* @__PURE__ */ jsxs12("div", { className: "flex items-baseline gap-2", children: [
1402
- /* @__PURE__ */ jsx27("span", { className: "text-2xl font-semibold text-foreground", children: value }),
1403
- trend && /* @__PURE__ */ jsxs12(
1564
+ return /* @__PURE__ */ jsxs14(Card, { className: cn("flex flex-col gap-1", className), children: [
1565
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-baseline gap-2", children: [
1566
+ /* @__PURE__ */ jsx29("span", { className: "text-2xl font-semibold text-foreground", children: value }),
1567
+ trend && /* @__PURE__ */ jsxs14(
1404
1568
  "span",
1405
1569
  {
1406
1570
  className: cn(
@@ -1408,27 +1572,27 @@ function StatCard({ value, label, trend, className }) {
1408
1572
  trend.direction === "up" ? "text-success" : "text-danger"
1409
1573
  ),
1410
1574
  children: [
1411
- trend.direction === "up" ? /* @__PURE__ */ jsx27(TrendingUp, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx27(TrendingDown, { className: "h-3 w-3" }),
1575
+ trend.direction === "up" ? /* @__PURE__ */ jsx29(TrendingUp, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx29(TrendingDown, { className: "h-3 w-3" }),
1412
1576
  trend.value,
1413
1577
  "%"
1414
1578
  ]
1415
1579
  }
1416
1580
  )
1417
1581
  ] }),
1418
- /* @__PURE__ */ jsx27("span", { className: "text-xs text-foreground-muted", children: label })
1582
+ /* @__PURE__ */ jsx29("span", { className: "text-xs text-foreground-muted", children: label })
1419
1583
  ] });
1420
1584
  }
1421
1585
 
1422
1586
  // src/composites/key-value-list.tsx
1423
- import { jsx as jsx28, jsxs as jsxs13 } from "react/jsx-runtime";
1587
+ import { jsx as jsx30, jsxs as jsxs15 } from "react/jsx-runtime";
1424
1588
  function KeyValueList({ items, className }) {
1425
- return /* @__PURE__ */ jsx28("dl", { className: cn("flex flex-col", className), children: items.map((item) => /* @__PURE__ */ jsxs13(
1589
+ return /* @__PURE__ */ jsx30("dl", { className: cn("flex flex-col", className), children: items.map((item) => /* @__PURE__ */ jsxs15(
1426
1590
  "div",
1427
1591
  {
1428
1592
  className: "flex items-center h-7",
1429
1593
  children: [
1430
- /* @__PURE__ */ jsx28("dt", { className: "text-foreground-subtle text-xs w-1/3 shrink-0", children: item.key }),
1431
- /* @__PURE__ */ jsx28("dd", { className: "text-foreground text-xs", children: item.value })
1594
+ /* @__PURE__ */ jsx30("dt", { className: "text-foreground-subtle text-xs w-1/3 shrink-0", children: item.key }),
1595
+ /* @__PURE__ */ jsx30("dd", { className: "text-foreground text-xs", children: item.value })
1432
1596
  ]
1433
1597
  },
1434
1598
  item.key
@@ -1438,7 +1602,7 @@ function KeyValueList({ items, className }) {
1438
1602
  // src/composites/code-block.tsx
1439
1603
  import { useCallback as useCallback7, useState as useState8 } from "react";
1440
1604
  import { Copy, Check } from "lucide-react";
1441
- import { jsx as jsx29, jsxs as jsxs14 } from "react/jsx-runtime";
1605
+ import { jsx as jsx31, jsxs as jsxs16 } from "react/jsx-runtime";
1442
1606
  function CodeBlock({ children, maxHeight = 300, className }) {
1443
1607
  const [copied, setCopied] = useState8(false);
1444
1608
  const handleCopy = useCallback7(() => {
@@ -1447,9 +1611,9 @@ function CodeBlock({ children, maxHeight = 300, className }) {
1447
1611
  setTimeout(() => setCopied(false), 2e3);
1448
1612
  });
1449
1613
  }, [children]);
1450
- return /* @__PURE__ */ jsxs14("div", { className: cn("relative group", className), children: [
1451
- /* @__PURE__ */ jsx29(ScrollArea, { style: { maxHeight }, children: /* @__PURE__ */ jsx29("pre", { className: "font-mono text-xs bg-surface p-3 rounded-md border border-border-subtle", children: /* @__PURE__ */ jsx29("code", { children }) }) }),
1452
- /* @__PURE__ */ jsx29("div", { className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ jsx29(
1614
+ return /* @__PURE__ */ jsxs16("div", { className: cn("relative group", className), children: [
1615
+ /* @__PURE__ */ jsx31(ScrollArea, { style: { maxHeight }, children: /* @__PURE__ */ jsx31("pre", { className: "font-mono text-xs bg-surface p-3 rounded-md border border-border-subtle", children: /* @__PURE__ */ jsx31("code", { children }) }) }),
1616
+ /* @__PURE__ */ jsx31("div", { className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ jsx31(
1453
1617
  IconButton,
1454
1618
  {
1455
1619
  icon: copied ? Check : Copy,
@@ -1464,27 +1628,27 @@ function CodeBlock({ children, maxHeight = 300, className }) {
1464
1628
 
1465
1629
  // src/composites/filter-bar.tsx
1466
1630
  import { Search } from "lucide-react";
1467
- import { jsx as jsx30 } from "react/jsx-runtime";
1631
+ import { jsx as jsx32 } from "react/jsx-runtime";
1468
1632
  function FilterBar({ filters, values, onChange, className }) {
1469
1633
  const handleChange = (key, value) => {
1470
1634
  onChange({ ...values, [key]: value });
1471
1635
  };
1472
- return /* @__PURE__ */ jsx30("div", { className: cn("flex items-center gap-2 flex-wrap", className), children: filters.map((filter) => {
1636
+ return /* @__PURE__ */ jsx32("div", { className: cn("flex items-center gap-2 flex-wrap", className), children: filters.map((filter) => {
1473
1637
  switch (filter.type) {
1474
1638
  case "search":
1475
- return /* @__PURE__ */ jsx30(
1639
+ return /* @__PURE__ */ jsx32(
1476
1640
  Input,
1477
1641
  {
1478
1642
  placeholder: filter.placeholder ?? "Search...",
1479
1643
  value: values[filter.key] ?? "",
1480
1644
  onChange: (e) => handleChange(filter.key, e.target.value),
1481
- leftSlot: /* @__PURE__ */ jsx30(Search, { className: "h-3 w-3 text-foreground-subtle" }),
1645
+ leftSlot: /* @__PURE__ */ jsx32(Search, { className: "h-3 w-3 text-foreground-subtle" }),
1482
1646
  className: "w-48"
1483
1647
  },
1484
1648
  filter.key
1485
1649
  );
1486
1650
  case "select":
1487
- return /* @__PURE__ */ jsx30(
1651
+ return /* @__PURE__ */ jsx32(
1488
1652
  Select,
1489
1653
  {
1490
1654
  options: filter.options,
@@ -1495,10 +1659,10 @@ function FilterBar({ filters, values, onChange, className }) {
1495
1659
  filter.key
1496
1660
  );
1497
1661
  case "badge-toggle":
1498
- return /* @__PURE__ */ jsx30("div", { className: "flex items-center gap-1", children: filter.options.map((option) => {
1662
+ return /* @__PURE__ */ jsx32("div", { className: "flex items-center gap-1", children: filter.options.map((option) => {
1499
1663
  const currentValue = values[filter.key];
1500
1664
  const isActive = currentValue === option.value;
1501
- return /* @__PURE__ */ jsx30(
1665
+ return /* @__PURE__ */ jsx32(
1502
1666
  "button",
1503
1667
  {
1504
1668
  type: "button",
@@ -1506,7 +1670,7 @@ function FilterBar({ filters, values, onChange, className }) {
1506
1670
  filter.key,
1507
1671
  isActive ? void 0 : option.value
1508
1672
  ),
1509
- children: /* @__PURE__ */ jsx30(
1673
+ children: /* @__PURE__ */ jsx32(
1510
1674
  Badge,
1511
1675
  {
1512
1676
  variant: isActive ? "info" : "default",
@@ -1525,7 +1689,7 @@ function FilterBar({ filters, values, onChange, className }) {
1525
1689
  }
1526
1690
 
1527
1691
  // src/composites/app-shell/sidebar-item.tsx
1528
- import { jsx as jsx31, jsxs as jsxs15 } from "react/jsx-runtime";
1692
+ import { jsx as jsx33, jsxs as jsxs17 } from "react/jsx-runtime";
1529
1693
  function SidebarItem({
1530
1694
  label,
1531
1695
  icon: Icon,
@@ -1534,7 +1698,7 @@ function SidebarItem({
1534
1698
  active = false,
1535
1699
  className
1536
1700
  }) {
1537
- return /* @__PURE__ */ jsxs15(
1701
+ return /* @__PURE__ */ jsxs17(
1538
1702
  "a",
1539
1703
  {
1540
1704
  href,
@@ -1544,33 +1708,33 @@ function SidebarItem({
1544
1708
  className
1545
1709
  ),
1546
1710
  children: [
1547
- /* @__PURE__ */ jsx31(Icon, { className: "h-3.5 w-3.5 shrink-0" }),
1548
- /* @__PURE__ */ jsx31("span", { className: "truncate flex-1", children: label }),
1549
- badge !== void 0 && /* @__PURE__ */ jsx31(Badge, { className: "ml-auto text-[10px] px-1.5 py-0", children: badge })
1711
+ /* @__PURE__ */ jsx33(Icon, { className: "h-3.5 w-3.5 shrink-0" }),
1712
+ /* @__PURE__ */ jsx33("span", { className: "truncate flex-1", children: label }),
1713
+ badge !== void 0 && /* @__PURE__ */ jsx33(Badge, { className: "ml-auto text-[10px] px-1.5 py-0", children: badge })
1550
1714
  ]
1551
1715
  }
1552
1716
  );
1553
1717
  }
1554
1718
 
1555
1719
  // src/composites/app-shell/sidebar.tsx
1556
- import { jsx as jsx32, jsxs as jsxs16 } from "react/jsx-runtime";
1557
- function Sidebar({ logo, sections, footer, className }) {
1558
- return /* @__PURE__ */ jsxs16(
1720
+ import { jsx as jsx34, jsxs as jsxs18 } from "react/jsx-runtime";
1721
+ function Sidebar({ logo, sections, footer, onNavigate, className }) {
1722
+ return /* @__PURE__ */ jsxs18(
1559
1723
  "nav",
1560
1724
  {
1561
1725
  className: cn(
1562
- "w-44 bg-surface border-r border-border h-full flex flex-col",
1726
+ "bg-surface border-r border-border h-full flex flex-col",
1563
1727
  className
1564
1728
  ),
1565
1729
  children: [
1566
- logo && /* @__PURE__ */ jsx32("div", { className: "px-3 py-2 shrink-0", children: logo }),
1567
- /* @__PURE__ */ jsx32("div", { className: "flex-1 overflow-auto px-1 py-1", children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs16("div", { className: cn(sectionIndex > 0 ? "mt-3" : ""), children: [
1568
- section.label && /* @__PURE__ */ jsx32("span", { className: "text-[10px] text-foreground-disabled uppercase tracking-wider px-2 mb-1 block", children: section.label }),
1569
- /* @__PURE__ */ jsx32("div", { className: "flex flex-col gap-0.5", children: section.items.map((item) => /* @__PURE__ */ jsx32(SidebarItem, { ...item }, item.href)) })
1730
+ logo && /* @__PURE__ */ jsx34("div", { className: "px-3 py-2 shrink-0", children: logo }),
1731
+ /* @__PURE__ */ jsx34("div", { className: "flex-1 overflow-auto px-1 py-1", children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs18("div", { className: cn(sectionIndex > 0 ? "mt-3" : ""), children: [
1732
+ section.label && /* @__PURE__ */ jsx34("span", { className: "text-[10px] text-foreground-disabled uppercase tracking-wider px-2 mb-1 block", children: section.label }),
1733
+ /* @__PURE__ */ jsx34("div", { className: "flex flex-col gap-0.5", onClick: onNavigate, children: section.items.map((item) => /* @__PURE__ */ jsx34(SidebarItem, { ...item }, item.href)) })
1570
1734
  ] }, sectionIndex)) }),
1571
- footer && footer.length > 0 && /* @__PURE__ */ jsxs16("div", { className: "shrink-0 px-1 pb-1", children: [
1572
- /* @__PURE__ */ jsx32(Separator, { className: "mb-1" }),
1573
- /* @__PURE__ */ jsx32("div", { className: "flex flex-col gap-0.5", children: footer.map((item) => /* @__PURE__ */ jsx32(SidebarItem, { ...item }, item.href)) })
1735
+ footer && footer.length > 0 && /* @__PURE__ */ jsxs18("div", { className: "shrink-0 px-1 pb-1", children: [
1736
+ /* @__PURE__ */ jsx34(Separator, { className: "mb-1" }),
1737
+ /* @__PURE__ */ jsx34("div", { className: "flex flex-col gap-0.5", onClick: onNavigate, children: footer.map((item) => /* @__PURE__ */ jsx34(SidebarItem, { ...item }, item.href)) })
1574
1738
  ] })
1575
1739
  ]
1576
1740
  }
@@ -1578,30 +1742,54 @@ function Sidebar({ logo, sections, footer, className }) {
1578
1742
  }
1579
1743
 
1580
1744
  // src/composites/app-shell/app-shell.tsx
1581
- import { ChevronRight } from "lucide-react";
1582
- import { jsx as jsx33, jsxs as jsxs17 } from "react/jsx-runtime";
1583
- function AppShell({ sidebar, header, children, className }) {
1584
- return /* @__PURE__ */ jsxs17("div", { className: cn("flex h-screen", className), children: [
1585
- /* @__PURE__ */ jsx33(Sidebar, { ...sidebar }),
1586
- /* @__PURE__ */ jsxs17("div", { className: "flex flex-1 flex-col min-w-0", children: [
1587
- header && /* @__PURE__ */ jsxs17("header", { className: "flex items-center h-10 border-b border-border px-4 shrink-0", children: [
1588
- header.breadcrumbs && header.breadcrumbs.length > 0 && /* @__PURE__ */ jsx33("nav", { className: "flex items-center gap-1 text-xs flex-1 min-w-0", children: header.breadcrumbs.map((crumb, index) => {
1745
+ import { useState as useState9 } from "react";
1746
+ import { ChevronRight, Menu } from "lucide-react";
1747
+ import { jsx as jsx35, jsxs as jsxs19 } from "react/jsx-runtime";
1748
+ function AppShell({ sidebar, header, mobileLogo, mobileActions, children, className }) {
1749
+ const isMobile = useIsMobile();
1750
+ const [drawerOpen, setDrawerOpen] = useState9(false);
1751
+ return /* @__PURE__ */ jsxs19("div", { className: cn("flex h-screen", className), children: [
1752
+ !isMobile && /* @__PURE__ */ jsx35(Sidebar, { ...sidebar, className: cn("w-44", sidebar.className) }),
1753
+ isMobile && /* @__PURE__ */ jsx35(MobileDrawer, { open: drawerOpen, onClose: () => setDrawerOpen(false), width: "w-64", children: /* @__PURE__ */ jsx35(
1754
+ Sidebar,
1755
+ {
1756
+ ...sidebar,
1757
+ onNavigate: () => setDrawerOpen(false),
1758
+ className: "w-full border-r-0"
1759
+ }
1760
+ ) }),
1761
+ /* @__PURE__ */ jsxs19("div", { className: "flex flex-1 flex-col min-w-0", children: [
1762
+ isMobile && /* @__PURE__ */ jsxs19("header", { className: "flex items-center h-12 border-b border-border px-3 shrink-0 bg-surface/80 backdrop-blur-sm", children: [
1763
+ /* @__PURE__ */ jsx35(
1764
+ "button",
1765
+ {
1766
+ onClick: () => setDrawerOpen(true),
1767
+ className: "p-1.5 -ml-1.5 rounded-md hover:bg-surface-hover text-foreground-muted transition-colors",
1768
+ "aria-label": "Open menu",
1769
+ children: /* @__PURE__ */ jsx35(Menu, { className: "h-5 w-5" })
1770
+ }
1771
+ ),
1772
+ mobileLogo && /* @__PURE__ */ jsx35("div", { className: "flex-1 flex justify-center", children: mobileLogo }),
1773
+ mobileActions && /* @__PURE__ */ jsx35("div", { className: "shrink-0", children: mobileActions })
1774
+ ] }),
1775
+ !isMobile && header && /* @__PURE__ */ jsxs19("header", { className: "flex items-center h-10 border-b border-border px-4 shrink-0", children: [
1776
+ header.breadcrumbs && header.breadcrumbs.length > 0 && /* @__PURE__ */ jsx35("nav", { className: "flex items-center gap-1 text-xs flex-1 min-w-0", children: header.breadcrumbs.map((crumb, index) => {
1589
1777
  const isLast = index === header.breadcrumbs.length - 1;
1590
- return /* @__PURE__ */ jsxs17("span", { className: "flex items-center gap-1", children: [
1591
- index > 0 && /* @__PURE__ */ jsx33(ChevronRight, { className: "h-3 w-3 text-foreground-subtle shrink-0" }),
1592
- crumb.href && !isLast ? /* @__PURE__ */ jsx33(
1778
+ return /* @__PURE__ */ jsxs19("span", { className: "flex items-center gap-1", children: [
1779
+ index > 0 && /* @__PURE__ */ jsx35(ChevronRight, { className: "h-3 w-3 text-foreground-subtle shrink-0" }),
1780
+ crumb.href && !isLast ? /* @__PURE__ */ jsx35(
1593
1781
  "a",
1594
1782
  {
1595
1783
  href: crumb.href,
1596
1784
  className: "text-foreground-subtle hover:text-foreground transition-colors truncate",
1597
1785
  children: crumb.label
1598
1786
  }
1599
- ) : /* @__PURE__ */ jsx33("span", { className: "text-foreground truncate", children: crumb.label })
1787
+ ) : /* @__PURE__ */ jsx35("span", { className: "text-foreground truncate", children: crumb.label })
1600
1788
  ] }, index);
1601
1789
  }) }),
1602
- header.actions && /* @__PURE__ */ jsx33("div", { className: "flex items-center gap-2 ml-auto shrink-0", children: header.actions })
1790
+ header.actions && /* @__PURE__ */ jsx35("div", { className: "flex items-center gap-2 ml-auto shrink-0", children: header.actions })
1603
1791
  ] }),
1604
- /* @__PURE__ */ jsx33("main", { className: "flex-1 overflow-auto p-4", children })
1792
+ /* @__PURE__ */ jsx35("main", { className: "flex-1 overflow-auto p-4", children })
1605
1793
  ] })
1606
1794
  ] });
1607
1795
  }
@@ -1619,20 +1807,20 @@ import {
1619
1807
 
1620
1808
  // src/composites/data-table/data-table-header.tsx
1621
1809
  import { ArrowUpDown, ArrowUp, ArrowDown } from "lucide-react";
1622
- import { jsx as jsx34, jsxs as jsxs18 } from "react/jsx-runtime";
1810
+ import { jsx as jsx36, jsxs as jsxs20 } from "react/jsx-runtime";
1623
1811
  function DataTableHeader({
1624
1812
  headerGroups,
1625
1813
  onSortingChange,
1626
1814
  stickyHeader,
1627
1815
  flexRender: render
1628
1816
  }) {
1629
- return /* @__PURE__ */ jsx34(
1817
+ return /* @__PURE__ */ jsx36(
1630
1818
  "thead",
1631
1819
  {
1632
1820
  className: cn(
1633
1821
  stickyHeader && "sticky top-0 z-10 bg-background"
1634
1822
  ),
1635
- children: headerGroups.map((headerGroup) => /* @__PURE__ */ jsx34("tr", { className: "h-6", children: headerGroup.headers.map((header) => /* @__PURE__ */ jsx34(
1823
+ children: headerGroups.map((headerGroup) => /* @__PURE__ */ jsx36("tr", { className: "h-6", children: headerGroup.headers.map((header) => /* @__PURE__ */ jsx36(
1636
1824
  HeaderCell,
1637
1825
  {
1638
1826
  header,
@@ -1647,7 +1835,7 @@ function DataTableHeader({
1647
1835
  function HeaderCell({ header, sortable, flexRender: render }) {
1648
1836
  const sorted = header.column.getIsSorted();
1649
1837
  const SortIcon = sorted === "asc" ? ArrowUp : sorted === "desc" ? ArrowDown : ArrowUpDown;
1650
- return /* @__PURE__ */ jsx34(
1838
+ return /* @__PURE__ */ jsx36(
1651
1839
  "th",
1652
1840
  {
1653
1841
  className: cn(
@@ -1655,9 +1843,9 @@ function HeaderCell({ header, sortable, flexRender: render }) {
1655
1843
  sortable && "cursor-pointer select-none"
1656
1844
  ),
1657
1845
  onClick: sortable ? header.column.getToggleSortingHandler() : void 0,
1658
- children: /* @__PURE__ */ jsxs18("span", { className: "inline-flex items-center gap-1", children: [
1846
+ children: /* @__PURE__ */ jsxs20("span", { className: "inline-flex items-center gap-1", children: [
1659
1847
  header.isPlaceholder ? null : render(header.column.columnDef.header, header.getContext()),
1660
- sortable && /* @__PURE__ */ jsx34(SortIcon, { className: "h-3 w-3" })
1848
+ sortable && /* @__PURE__ */ jsx36(SortIcon, { className: "h-3 w-3" })
1661
1849
  ] })
1662
1850
  }
1663
1851
  );
@@ -1665,7 +1853,7 @@ function HeaderCell({ header, sortable, flexRender: render }) {
1665
1853
 
1666
1854
  // src/composites/data-table/data-table-row.tsx
1667
1855
  import { MoreHorizontal } from "lucide-react";
1668
- import { jsx as jsx35, jsxs as jsxs19 } from "react/jsx-runtime";
1856
+ import { jsx as jsx37, jsxs as jsxs21 } from "react/jsx-runtime";
1669
1857
  function DataTableRow({
1670
1858
  row,
1671
1859
  onRowClick,
@@ -1673,7 +1861,7 @@ function DataTableRow({
1673
1861
  flexRender: render
1674
1862
  }) {
1675
1863
  const actions = rowActions ? rowActions(row.original) : [];
1676
- return /* @__PURE__ */ jsxs19(
1864
+ return /* @__PURE__ */ jsxs21(
1677
1865
  "tr",
1678
1866
  {
1679
1867
  className: cn(
@@ -1683,17 +1871,17 @@ function DataTableRow({
1683
1871
  ),
1684
1872
  onClick: onRowClick ? () => onRowClick(row.original) : void 0,
1685
1873
  children: [
1686
- row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx35(DataTableCell, { cell, flexRender: render }, cell.id)),
1687
- actions.length > 0 && /* @__PURE__ */ jsx35("td", { className: "px-2 py-1.5 w-8", children: /* @__PURE__ */ jsxs19(Dropdown, { children: [
1688
- /* @__PURE__ */ jsx35(
1874
+ row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx37(DataTableCell, { cell, flexRender: render }, cell.id)),
1875
+ actions.length > 0 && /* @__PURE__ */ jsx37("td", { className: "px-2 py-1.5 w-8", children: /* @__PURE__ */ jsxs21(Dropdown, { children: [
1876
+ /* @__PURE__ */ jsx37(
1689
1877
  DropdownTrigger,
1690
1878
  {
1691
1879
  className: "p-0.5 rounded hover:bg-surface-hover",
1692
1880
  onClick: (e) => e.stopPropagation(),
1693
- children: /* @__PURE__ */ jsx35(MoreHorizontal, { className: "h-3.5 w-3.5 text-foreground-muted" })
1881
+ children: /* @__PURE__ */ jsx37(MoreHorizontal, { className: "h-3.5 w-3.5 text-foreground-muted" })
1694
1882
  }
1695
1883
  ),
1696
- /* @__PURE__ */ jsx35(DropdownContent, { className: "right-0 left-auto", children: actions.map((action) => /* @__PURE__ */ jsx35(
1884
+ /* @__PURE__ */ jsx37(DropdownContent, { className: "right-0 left-auto", children: actions.map((action) => /* @__PURE__ */ jsx37(
1697
1885
  DropdownItem,
1698
1886
  {
1699
1887
  icon: action.icon,
@@ -1712,12 +1900,12 @@ function DataTableRow({
1712
1900
  );
1713
1901
  }
1714
1902
  function DataTableCell({ cell, flexRender: render }) {
1715
- return /* @__PURE__ */ jsx35("td", { className: "px-2 py-1.5 text-xs text-foreground", children: render(cell.column.columnDef.cell, cell.getContext()) });
1903
+ return /* @__PURE__ */ jsx37("td", { className: "px-2 py-1.5 text-xs text-foreground", children: render(cell.column.columnDef.cell, cell.getContext()) });
1716
1904
  }
1717
1905
 
1718
1906
  // src/composites/data-table/data-table-pagination.tsx
1719
1907
  import { ChevronLeft, ChevronRight as ChevronRight2 } from "lucide-react";
1720
- import { jsx as jsx36, jsxs as jsxs20 } from "react/jsx-runtime";
1908
+ import { jsx as jsx38, jsxs as jsxs22 } from "react/jsx-runtime";
1721
1909
  var PAGE_SIZE_OPTIONS = [
1722
1910
  { value: "10", label: "10" },
1723
1911
  { value: "25", label: "25" },
@@ -1732,10 +1920,10 @@ function DataTablePagination({
1732
1920
  }) {
1733
1921
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
1734
1922
  const currentPage = page + 1;
1735
- return /* @__PURE__ */ jsxs20("div", { className: "flex items-center justify-between px-2 py-2 text-xs text-foreground-muted", children: [
1736
- /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2", children: [
1737
- /* @__PURE__ */ jsx36("span", { children: "Rows per page" }),
1738
- /* @__PURE__ */ jsx36("div", { className: "w-16", children: /* @__PURE__ */ jsx36(
1923
+ return /* @__PURE__ */ jsxs22("div", { className: "flex items-center justify-between px-2 py-2 text-xs text-foreground-muted", children: [
1924
+ /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-2", children: [
1925
+ /* @__PURE__ */ jsx38("span", { children: "Rows per page" }),
1926
+ /* @__PURE__ */ jsx38("div", { className: "w-16", children: /* @__PURE__ */ jsx38(
1739
1927
  Select,
1740
1928
  {
1741
1929
  options: PAGE_SIZE_OPTIONS,
@@ -1747,14 +1935,14 @@ function DataTablePagination({
1747
1935
  }
1748
1936
  ) })
1749
1937
  ] }),
1750
- /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2", children: [
1751
- /* @__PURE__ */ jsxs20("span", { children: [
1938
+ /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-2", children: [
1939
+ /* @__PURE__ */ jsxs22("span", { children: [
1752
1940
  "Page ",
1753
1941
  currentPage,
1754
1942
  " of ",
1755
1943
  totalPages
1756
1944
  ] }),
1757
- /* @__PURE__ */ jsx36(
1945
+ /* @__PURE__ */ jsx38(
1758
1946
  IconButton,
1759
1947
  {
1760
1948
  icon: ChevronLeft,
@@ -1765,7 +1953,7 @@ function DataTablePagination({
1765
1953
  onClick: () => onPaginationChange?.({ pageIndex: page - 1, pageSize })
1766
1954
  }
1767
1955
  ),
1768
- /* @__PURE__ */ jsx36(
1956
+ /* @__PURE__ */ jsx38(
1769
1957
  IconButton,
1770
1958
  {
1771
1959
  icon: ChevronRight2,
@@ -1781,7 +1969,7 @@ function DataTablePagination({
1781
1969
  }
1782
1970
 
1783
1971
  // src/composites/data-table/data-table.tsx
1784
- import { Fragment, jsx as jsx37, jsxs as jsxs21 } from "react/jsx-runtime";
1972
+ import { Fragment as Fragment3, jsx as jsx39, jsxs as jsxs23 } from "react/jsx-runtime";
1785
1973
  function DataTable({
1786
1974
  data,
1787
1975
  columns: userColumns,
@@ -1804,7 +1992,7 @@ function DataTable({
1804
1992
  if (!selectable) return userColumns;
1805
1993
  const selectColumn = {
1806
1994
  id: "__select",
1807
- header: ({ table: table2 }) => /* @__PURE__ */ jsx37(
1995
+ header: ({ table: table2 }) => /* @__PURE__ */ jsx39(
1808
1996
  Checkbox,
1809
1997
  {
1810
1998
  checked: table2.getIsAllPageRowsSelected(),
@@ -1812,7 +2000,7 @@ function DataTable({
1812
2000
  "aria-label": "Select all"
1813
2001
  }
1814
2002
  ),
1815
- cell: ({ row }) => /* @__PURE__ */ jsx37(
2003
+ cell: ({ row }) => /* @__PURE__ */ jsx39(
1816
2004
  Checkbox,
1817
2005
  {
1818
2006
  checked: row.getIsSelected(),
@@ -1850,9 +2038,9 @@ function DataTable({
1850
2038
  pageCount: pagination ? Math.ceil(pagination.total / pagination.pageSize) : void 0
1851
2039
  });
1852
2040
  const hasActions = !!rowActions;
1853
- return /* @__PURE__ */ jsxs21("div", { className: cn("overflow-auto", className), children: [
1854
- /* @__PURE__ */ jsxs21("table", { className: "w-full border-collapse", children: [
1855
- /* @__PURE__ */ jsx37(
2041
+ return /* @__PURE__ */ jsxs23("div", { className: cn("overflow-auto", className), children: [
2042
+ /* @__PURE__ */ jsxs23("table", { className: "w-full border-collapse", children: [
2043
+ /* @__PURE__ */ jsx39(
1856
2044
  DataTableHeader,
1857
2045
  {
1858
2046
  headerGroups: table.getHeaderGroups(),
@@ -1861,14 +2049,14 @@ function DataTable({
1861
2049
  flexRender
1862
2050
  }
1863
2051
  ),
1864
- /* @__PURE__ */ jsx37("tbody", { children: loading ? /* @__PURE__ */ jsx37(LoadingRows, { colSpan: columns.length + (hasActions ? 1 : 0), compact }) : table.getRowModel().rows.length === 0 ? /* @__PURE__ */ jsx37("tr", { children: /* @__PURE__ */ jsx37(
2052
+ /* @__PURE__ */ jsx39("tbody", { children: loading ? /* @__PURE__ */ jsx39(LoadingRows, { colSpan: columns.length + (hasActions ? 1 : 0), compact }) : table.getRowModel().rows.length === 0 ? /* @__PURE__ */ jsx39("tr", { children: /* @__PURE__ */ jsx39(
1865
2053
  "td",
1866
2054
  {
1867
2055
  colSpan: columns.length + (hasActions ? 1 : 0),
1868
2056
  className: "text-center py-8 text-xs text-foreground-muted",
1869
2057
  children: emptyState ?? "No data"
1870
2058
  }
1871
- ) }) : table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx37(
2059
+ ) }) : table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx39(
1872
2060
  DataTableRow,
1873
2061
  {
1874
2062
  row,
@@ -1879,7 +2067,7 @@ function DataTable({
1879
2067
  row.id
1880
2068
  )) })
1881
2069
  ] }),
1882
- pagination && /* @__PURE__ */ jsx37(
2070
+ pagination && /* @__PURE__ */ jsx39(
1883
2071
  DataTablePagination,
1884
2072
  {
1885
2073
  page: pagination.page,
@@ -1891,11 +2079,11 @@ function DataTable({
1891
2079
  ] });
1892
2080
  }
1893
2081
  function LoadingRows({ colSpan, compact }) {
1894
- return /* @__PURE__ */ jsx37(Fragment, { children: Array.from({ length: 5 }).map((_, rowIdx) => /* @__PURE__ */ jsx37("tr", { className: compact ? "h-7" : "h-9", children: Array.from({ length: colSpan }).map((_2, colIdx) => /* @__PURE__ */ jsx37("td", { className: "px-2 py-1.5", children: /* @__PURE__ */ jsx37(Skeleton, { className: "h-3 w-full" }) }, colIdx)) }, rowIdx)) });
2082
+ return /* @__PURE__ */ jsx39(Fragment3, { children: Array.from({ length: 5 }).map((_, rowIdx) => /* @__PURE__ */ jsx39("tr", { className: compact ? "h-7" : "h-9", children: Array.from({ length: colSpan }).map((_2, colIdx) => /* @__PURE__ */ jsx39("td", { className: "px-2 py-1.5", children: /* @__PURE__ */ jsx39(Skeleton, { className: "h-3 w-full" }) }, colIdx)) }, rowIdx)) });
1895
2083
  }
1896
2084
 
1897
2085
  // src/composites/device-card.tsx
1898
- import { jsx as jsx38, jsxs as jsxs22 } from "react/jsx-runtime";
2086
+ import { jsx as jsx40, jsxs as jsxs24 } from "react/jsx-runtime";
1899
2087
  var STATUS_COLORS = {
1900
2088
  online: "bg-success",
1901
2089
  offline: "bg-danger",
@@ -1914,7 +2102,7 @@ function DeviceCard({
1914
2102
  className
1915
2103
  }) {
1916
2104
  const isOffline = status === "offline";
1917
- return /* @__PURE__ */ jsxs22(
2105
+ return /* @__PURE__ */ jsxs24(
1918
2106
  "div",
1919
2107
  {
1920
2108
  onClick,
@@ -1926,18 +2114,18 @@ function DeviceCard({
1926
2114
  className
1927
2115
  ),
1928
2116
  children: [
1929
- /* @__PURE__ */ jsxs22("div", { className: "flex items-center justify-between mb-2", children: [
1930
- /* @__PURE__ */ jsx38("span", { className: "text-sm font-medium truncate", children: title }),
1931
- status && /* @__PURE__ */ jsx38("span", { className: cn("h-2 w-2 rounded-full shrink-0", STATUS_COLORS[status]) })
2117
+ /* @__PURE__ */ jsxs24("div", { className: "flex items-center justify-between mb-2", children: [
2118
+ /* @__PURE__ */ jsx40("span", { className: "text-sm font-medium truncate", children: title }),
2119
+ status && /* @__PURE__ */ jsx40("span", { className: cn("h-2 w-2 rounded-full shrink-0", STATUS_COLORS[status]) })
1932
2120
  ] }),
1933
- subtitle && /* @__PURE__ */ jsx38("div", { className: "text-[11px] text-foreground-muted", children: subtitle }),
1934
- badges && badges.length > 0 && /* @__PURE__ */ jsx38("div", { className: "flex flex-wrap gap-1 mt-2", children: badges.map((badge, i) => {
2121
+ subtitle && /* @__PURE__ */ jsx40("div", { className: "text-[11px] text-foreground-muted", children: subtitle }),
2122
+ badges && badges.length > 0 && /* @__PURE__ */ jsx40("div", { className: "flex flex-wrap gap-1 mt-2", children: badges.map((badge, i) => {
1935
2123
  const cls = cn(
1936
2124
  "rounded px-1.5 py-0.5 text-[10px] flex items-center gap-0.5",
1937
2125
  selected ? "bg-primary/20" : "bg-surface-hover",
1938
2126
  badge.onClick && "hover:opacity-80 transition-opacity cursor-pointer"
1939
2127
  );
1940
- return badge.onClick ? /* @__PURE__ */ jsxs22(
2128
+ return badge.onClick ? /* @__PURE__ */ jsxs24(
1941
2129
  "button",
1942
2130
  {
1943
2131
  onClick: (e) => {
@@ -1951,12 +2139,12 @@ function DeviceCard({
1951
2139
  ]
1952
2140
  },
1953
2141
  i
1954
- ) : /* @__PURE__ */ jsxs22("span", { className: cls, children: [
2142
+ ) : /* @__PURE__ */ jsxs24("span", { className: cls, children: [
1955
2143
  badge.icon,
1956
2144
  badge.label
1957
2145
  ] }, i);
1958
2146
  }) }),
1959
- !isOffline && actions && actions.length > 0 && /* @__PURE__ */ jsx38("div", { className: "flex items-center gap-0.5 mt-2 -mb-1", children: actions.map((action, i) => /* @__PURE__ */ jsx38(
2147
+ !isOffline && actions && actions.length > 0 && /* @__PURE__ */ jsx40("div", { className: "flex items-center gap-0.5 mt-2 -mb-1", children: actions.map((action, i) => /* @__PURE__ */ jsx40(
1960
2148
  "button",
1961
2149
  {
1962
2150
  onClick: (e) => {
@@ -1970,21 +2158,21 @@ function DeviceCard({
1970
2158
  },
1971
2159
  i
1972
2160
  )) }),
1973
- isOffline && offlineAction && /* @__PURE__ */ jsx38("div", { className: "mt-2", onClick: (e) => e.stopPropagation(), children: offlineAction })
2161
+ isOffline && offlineAction && /* @__PURE__ */ jsx40("div", { className: "mt-2", onClick: (e) => e.stopPropagation(), children: offlineAction })
1974
2162
  ]
1975
2163
  }
1976
2164
  );
1977
2165
  }
1978
2166
 
1979
2167
  // src/composites/device-grid.tsx
1980
- import { jsx as jsx39 } from "react/jsx-runtime";
2168
+ import { jsx as jsx41 } from "react/jsx-runtime";
1981
2169
  function DeviceGrid({
1982
2170
  children,
1983
2171
  minCardWidth = 220,
1984
2172
  gap = 3,
1985
2173
  className
1986
2174
  }) {
1987
- return /* @__PURE__ */ jsx39(
2175
+ return /* @__PURE__ */ jsx41(
1988
2176
  "div",
1989
2177
  {
1990
2178
  className: cn(
@@ -2002,9 +2190,9 @@ function DeviceGrid({
2002
2190
  }
2003
2191
 
2004
2192
  // src/composites/pipeline-step.tsx
2005
- import { useState as useState9 } from "react";
2193
+ import { useState as useState10 } from "react";
2006
2194
  import { ChevronRight as ChevronRight3, ChevronDown as ChevronDown2 } from "lucide-react";
2007
- import { jsx as jsx40, jsxs as jsxs23 } from "react/jsx-runtime";
2195
+ import { jsx as jsx42, jsxs as jsxs25 } from "react/jsx-runtime";
2008
2196
  var ADDON_COLORS = {
2009
2197
  "object-detection": "border-l-blue-500",
2010
2198
  "motion-detection": "border-l-amber-500",
@@ -2080,7 +2268,7 @@ function PipelineStep({
2080
2268
  onDelete,
2081
2269
  readOnly = false
2082
2270
  }) {
2083
- const [expanded, setExpanded] = useState9(false);
2271
+ const [expanded, setExpanded] = useState10(false);
2084
2272
  const color = borderColor(step.addonId, step.slot);
2085
2273
  const backends = backendsForRuntime(step.runtime, capabilities, schema);
2086
2274
  const rtOptions = runtimeOptions(capabilities);
@@ -2103,7 +2291,7 @@ function PipelineStep({
2103
2291
  if (e.target.closest(".step-config")) return;
2104
2292
  setExpanded((v) => !v);
2105
2293
  }
2106
- return /* @__PURE__ */ jsx40("div", { className: "space-y-2", children: /* @__PURE__ */ jsxs23(
2294
+ return /* @__PURE__ */ jsx42("div", { className: "space-y-2", children: /* @__PURE__ */ jsxs25(
2107
2295
  "div",
2108
2296
  {
2109
2297
  className: cn(
@@ -2112,18 +2300,18 @@ function PipelineStep({
2112
2300
  !step.enabled && "opacity-[0.45]"
2113
2301
  ),
2114
2302
  children: [
2115
- /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-2.5 px-3 py-2.5 cursor-pointer select-none", onClick: handleClick, children: [
2116
- /* @__PURE__ */ jsx40("span", { className: "text-foreground-subtle", children: expanded ? /* @__PURE__ */ jsx40(ChevronDown2, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx40(ChevronRight3, { className: "h-4 w-4" }) }),
2117
- /* @__PURE__ */ jsxs23("div", { className: "flex-1 min-w-0", children: [
2118
- /* @__PURE__ */ jsx40("span", { className: "text-[10px] uppercase tracking-wider font-medium text-foreground-subtle/60 block leading-none", children: step.slot }),
2119
- /* @__PURE__ */ jsx40("span", { className: "text-sm font-semibold text-foreground truncate block leading-tight", children: step.addonName }),
2120
- /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-1 mt-0.5 flex-wrap", children: [
2121
- step.inputClasses.map((c) => /* @__PURE__ */ jsx40("span", { className: "text-[9px] uppercase font-semibold tracking-wide px-1.5 py-0.5 rounded bg-blue-500/12 text-blue-400", children: c }, c)),
2122
- step.inputClasses.length > 0 && step.outputClasses.length > 0 && /* @__PURE__ */ jsx40("span", { className: "text-foreground-subtle/40 text-[10px]", children: "\u2192" }),
2123
- step.outputClasses.map((c) => /* @__PURE__ */ jsx40("span", { className: "text-[9px] uppercase font-semibold tracking-wide px-1.5 py-0.5 rounded bg-green-500/12 text-green-400", children: c }, c))
2303
+ /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2.5 px-3 py-2.5 cursor-pointer select-none", onClick: handleClick, children: [
2304
+ /* @__PURE__ */ jsx42("span", { className: "text-foreground-subtle", children: expanded ? /* @__PURE__ */ jsx42(ChevronDown2, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx42(ChevronRight3, { className: "h-4 w-4" }) }),
2305
+ /* @__PURE__ */ jsxs25("div", { className: "flex-1 min-w-0", children: [
2306
+ /* @__PURE__ */ jsx42("span", { className: "text-[10px] uppercase tracking-wider font-medium text-foreground-subtle/60 block leading-none", children: step.slot }),
2307
+ /* @__PURE__ */ jsx42("span", { className: "text-sm font-semibold text-foreground truncate block leading-tight", children: step.addonName }),
2308
+ /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-1 mt-0.5 flex-wrap", children: [
2309
+ step.inputClasses.map((c) => /* @__PURE__ */ jsx42("span", { className: "text-[9px] uppercase font-semibold tracking-wide px-1.5 py-0.5 rounded bg-blue-500/12 text-blue-400", children: c }, c)),
2310
+ step.inputClasses.length > 0 && step.outputClasses.length > 0 && /* @__PURE__ */ jsx42("span", { className: "text-foreground-subtle/40 text-[10px]", children: "\u2192" }),
2311
+ step.outputClasses.map((c) => /* @__PURE__ */ jsx42("span", { className: "text-[9px] uppercase font-semibold tracking-wide px-1.5 py-0.5 rounded bg-green-500/12 text-green-400", children: c }, c))
2124
2312
  ] })
2125
2313
  ] }),
2126
- /* @__PURE__ */ jsx40(
2314
+ /* @__PURE__ */ jsx42(
2127
2315
  "button",
2128
2316
  {
2129
2317
  onClick: (e) => {
@@ -2134,16 +2322,16 @@ function PipelineStep({
2134
2322
  "relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors",
2135
2323
  step.enabled ? "bg-success" : "bg-foreground-subtle/30"
2136
2324
  ),
2137
- children: /* @__PURE__ */ jsx40("span", { className: cn(
2325
+ children: /* @__PURE__ */ jsx42("span", { className: cn(
2138
2326
  "inline-block h-4 w-4 rounded-full bg-white shadow transition-transform",
2139
2327
  step.enabled ? "translate-x-6" : "translate-x-1"
2140
2328
  ) })
2141
2329
  }
2142
2330
  )
2143
2331
  ] }),
2144
- expanded && /* @__PURE__ */ jsxs23("div", { className: "step-config border-t border-border bg-background px-4 py-4 space-y-3", children: [
2145
- /* @__PURE__ */ jsxs23("div", { className: "grid grid-cols-2 gap-3", children: [
2146
- /* @__PURE__ */ jsx40(
2332
+ expanded && /* @__PURE__ */ jsxs25("div", { className: "step-config border-t border-border bg-background px-4 py-4 space-y-3", children: [
2333
+ /* @__PURE__ */ jsxs25("div", { className: "grid grid-cols-2 gap-3", children: [
2334
+ /* @__PURE__ */ jsx42(
2147
2335
  ConfigSelect,
2148
2336
  {
2149
2337
  label: "Agent",
@@ -2153,7 +2341,7 @@ function PipelineStep({
2153
2341
  onChange: (v) => onChange({ ...step, agentId: v })
2154
2342
  }
2155
2343
  ),
2156
- /* @__PURE__ */ jsx40(
2344
+ /* @__PURE__ */ jsx42(
2157
2345
  ConfigSelect,
2158
2346
  {
2159
2347
  label: "Runtime",
@@ -2163,7 +2351,7 @@ function PipelineStep({
2163
2351
  onChange: (v) => onChange({ ...step, runtime: v })
2164
2352
  }
2165
2353
  ),
2166
- /* @__PURE__ */ jsx40(
2354
+ /* @__PURE__ */ jsx42(
2167
2355
  ConfigSelect,
2168
2356
  {
2169
2357
  label: "Backend",
@@ -2173,7 +2361,7 @@ function PipelineStep({
2173
2361
  onChange: (v) => onChange({ ...step, backend: v })
2174
2362
  }
2175
2363
  ),
2176
- /* @__PURE__ */ jsx40(
2364
+ /* @__PURE__ */ jsx42(
2177
2365
  ConfigSelect,
2178
2366
  {
2179
2367
  label: "Model",
@@ -2184,15 +2372,15 @@ function PipelineStep({
2184
2372
  }
2185
2373
  )
2186
2374
  ] }),
2187
- /* @__PURE__ */ jsxs23("div", { children: [
2188
- /* @__PURE__ */ jsxs23("div", { className: "flex items-center justify-between mb-1", children: [
2189
- /* @__PURE__ */ jsx40("span", { className: "text-[10px] font-medium text-foreground-subtle uppercase tracking-wide", children: "Confidence" }),
2190
- /* @__PURE__ */ jsxs23("span", { className: "text-xs font-medium text-foreground tabular-nums", children: [
2375
+ /* @__PURE__ */ jsxs25("div", { children: [
2376
+ /* @__PURE__ */ jsxs25("div", { className: "flex items-center justify-between mb-1", children: [
2377
+ /* @__PURE__ */ jsx42("span", { className: "text-[10px] font-medium text-foreground-subtle uppercase tracking-wide", children: "Confidence" }),
2378
+ /* @__PURE__ */ jsxs25("span", { className: "text-xs font-medium text-foreground tabular-nums", children: [
2191
2379
  (step.confidence * 100).toFixed(0),
2192
2380
  "%"
2193
2381
  ] })
2194
2382
  ] }),
2195
- /* @__PURE__ */ jsx40(
2383
+ /* @__PURE__ */ jsx42(
2196
2384
  "input",
2197
2385
  {
2198
2386
  type: "range",
@@ -2212,9 +2400,9 @@ function PipelineStep({
2212
2400
  ) });
2213
2401
  }
2214
2402
  function ConfigSelect({ label, value, options, disabled, onChange }) {
2215
- return /* @__PURE__ */ jsxs23("div", { children: [
2216
- /* @__PURE__ */ jsx40("label", { className: "block text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-1.5", children: label }),
2217
- /* @__PURE__ */ jsxs23(
2403
+ return /* @__PURE__ */ jsxs25("div", { children: [
2404
+ /* @__PURE__ */ jsx42("label", { className: "block text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-1.5", children: label }),
2405
+ /* @__PURE__ */ jsxs25(
2218
2406
  "select",
2219
2407
  {
2220
2408
  value,
@@ -2222,8 +2410,8 @@ function ConfigSelect({ label, value, options, disabled, onChange }) {
2222
2410
  disabled,
2223
2411
  className: "w-full rounded-lg border border-border bg-surface px-3 py-2 text-xs text-foreground focus:outline-none focus:border-primary/50",
2224
2412
  children: [
2225
- options.length === 0 && /* @__PURE__ */ jsx40("option", { value, children: value || "default" }),
2226
- options.map((o) => /* @__PURE__ */ jsx40("option", { value: o.value, disabled: o.disabled, children: o.label }, o.value))
2413
+ options.length === 0 && /* @__PURE__ */ jsx42("option", { value, children: value || "default" }),
2414
+ options.map((o) => /* @__PURE__ */ jsx42("option", { value: o.value, disabled: o.disabled, children: o.label }, o.value))
2227
2415
  ]
2228
2416
  }
2229
2417
  )
@@ -2232,29 +2420,29 @@ function ConfigSelect({ label, value, options, disabled, onChange }) {
2232
2420
 
2233
2421
  // src/composites/pipeline-runtime-selector.tsx
2234
2422
  import { Cpu, Star } from "lucide-react";
2235
- import { jsx as jsx41, jsxs as jsxs24 } from "react/jsx-runtime";
2423
+ import { jsx as jsx43, jsxs as jsxs26 } from "react/jsx-runtime";
2236
2424
  function PipelineRuntimeSelector({ options, value, onChange }) {
2237
- return /* @__PURE__ */ jsx41("div", { className: "flex flex-wrap gap-2", children: options.map((opt) => {
2425
+ return /* @__PURE__ */ jsx43("div", { className: "flex flex-wrap gap-2", children: options.map((opt) => {
2238
2426
  const active = opt.id === value;
2239
- return /* @__PURE__ */ jsxs24(
2427
+ return /* @__PURE__ */ jsxs26(
2240
2428
  "button",
2241
2429
  {
2242
2430
  onClick: () => opt.available && onChange(opt.id),
2243
2431
  disabled: !opt.available,
2244
2432
  className: `flex items-center gap-2 rounded-lg border px-3 py-2 text-xs font-medium transition-all ${active ? "border-primary/40 bg-primary/10 text-primary" : opt.available ? "border-border bg-surface text-foreground-subtle hover:bg-surface-hover hover:text-foreground" : "border-border/40 bg-surface/40 text-foreground-subtle/40 cursor-not-allowed"}`,
2245
2433
  children: [
2246
- /* @__PURE__ */ jsx41(Cpu, { className: "h-3.5 w-3.5 shrink-0" }),
2434
+ /* @__PURE__ */ jsx43(Cpu, { className: "h-3.5 w-3.5 shrink-0" }),
2247
2435
  opt.label,
2248
- opt.isBest && /* @__PURE__ */ jsxs24("span", { className: "inline-flex items-center gap-0.5 rounded-full bg-amber-500/15 px-1.5 py-0.5 text-[10px] font-semibold text-amber-400", children: [
2249
- /* @__PURE__ */ jsx41(Star, { className: "h-2.5 w-2.5" }),
2436
+ opt.isBest && /* @__PURE__ */ jsxs26("span", { className: "inline-flex items-center gap-0.5 rounded-full bg-amber-500/15 px-1.5 py-0.5 text-[10px] font-semibold text-amber-400", children: [
2437
+ /* @__PURE__ */ jsx43(Star, { className: "h-2.5 w-2.5" }),
2250
2438
  "Best"
2251
2439
  ] }),
2252
- opt.platformScore != null && /* @__PURE__ */ jsxs24("span", { className: "text-[10px] text-foreground-subtle/60", children: [
2440
+ opt.platformScore != null && /* @__PURE__ */ jsxs26("span", { className: "text-[10px] text-foreground-subtle/60", children: [
2253
2441
  "(",
2254
2442
  opt.platformScore,
2255
2443
  ")"
2256
2444
  ] }),
2257
- /* @__PURE__ */ jsx41(
2445
+ /* @__PURE__ */ jsx43(
2258
2446
  "span",
2259
2447
  {
2260
2448
  className: `h-1.5 w-1.5 rounded-full ${opt.available ? "bg-success" : "bg-danger"}`
@@ -2268,8 +2456,8 @@ function PipelineRuntimeSelector({ options, value, onChange }) {
2268
2456
  }
2269
2457
 
2270
2458
  // src/composites/pipeline-builder.tsx
2271
- import { useMemo as useMemo3, useState as useState10 } from "react";
2272
- import { Save, CopyPlus, Trash2, PlusCircle, X as X2 } from "lucide-react";
2459
+ import { useMemo as useMemo3, useState as useState11 } from "react";
2460
+ import { Save, CopyPlus, Trash2, PlusCircle, X as X3 } from "lucide-react";
2273
2461
 
2274
2462
  // src/lib/validate-template.ts
2275
2463
  function validateTemplate(steps, schema) {
@@ -2301,7 +2489,7 @@ function validateTemplate(steps, schema) {
2301
2489
  }
2302
2490
 
2303
2491
  // src/composites/pipeline-builder.tsx
2304
- import { jsx as jsx42, jsxs as jsxs25 } from "react/jsx-runtime";
2492
+ import { jsx as jsx44, jsxs as jsxs27 } from "react/jsx-runtime";
2305
2493
  function buildSchemaMap(schema) {
2306
2494
  const map = /* @__PURE__ */ new Map();
2307
2495
  for (const slot of schema.slots) {
@@ -2330,20 +2518,20 @@ function createDefaultStep(addon, fallbackRuntime, fallbackBackend) {
2330
2518
  };
2331
2519
  }
2332
2520
  function PlaceholderStep({ addon, onClick }) {
2333
- return /* @__PURE__ */ jsx42(
2521
+ return /* @__PURE__ */ jsx44(
2334
2522
  "button",
2335
2523
  {
2336
2524
  type: "button",
2337
2525
  onClick,
2338
2526
  className: "w-full rounded-xl border-2 border-dashed border-border/60 px-4 py-3 text-left transition-all hover:border-primary/30 hover:bg-surface/60 group",
2339
- children: /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-3", children: [
2340
- /* @__PURE__ */ jsx42(PlusCircle, { className: "h-[18px] w-[18px] text-foreground-subtle/30 group-hover:text-primary/60 shrink-0" }),
2341
- /* @__PURE__ */ jsxs25("div", { className: "flex-1 min-w-0", children: [
2342
- /* @__PURE__ */ jsx42("span", { className: "text-[13px] font-medium text-foreground-subtle/50 group-hover:text-foreground-subtle block truncate", children: addon.name }),
2343
- /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-1 mt-0.5 flex-wrap", children: [
2344
- addon.inputClasses.map((c) => /* @__PURE__ */ jsx42("span", { className: "text-[9px] uppercase font-semibold tracking-wide px-1.5 py-0.5 rounded bg-blue-500/8 text-blue-400/50", children: c }, c)),
2345
- addon.inputClasses.length > 0 && addon.outputClasses.length > 0 && /* @__PURE__ */ jsx42("span", { className: "text-foreground-subtle/25 text-[10px]", children: "\u2192" }),
2346
- addon.outputClasses.map((c) => /* @__PURE__ */ jsx42("span", { className: "text-[9px] uppercase font-semibold tracking-wide px-1.5 py-0.5 rounded bg-green-500/8 text-green-400/50", children: c }, c))
2527
+ children: /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-3", children: [
2528
+ /* @__PURE__ */ jsx44(PlusCircle, { className: "h-[18px] w-[18px] text-foreground-subtle/30 group-hover:text-primary/60 shrink-0" }),
2529
+ /* @__PURE__ */ jsxs27("div", { className: "flex-1 min-w-0", children: [
2530
+ /* @__PURE__ */ jsx44("span", { className: "text-[13px] font-medium text-foreground-subtle/50 group-hover:text-foreground-subtle block truncate", children: addon.name }),
2531
+ /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-1 mt-0.5 flex-wrap", children: [
2532
+ addon.inputClasses.map((c) => /* @__PURE__ */ jsx44("span", { className: "text-[9px] uppercase font-semibold tracking-wide px-1.5 py-0.5 rounded bg-blue-500/8 text-blue-400/50", children: c }, c)),
2533
+ addon.inputClasses.length > 0 && addon.outputClasses.length > 0 && /* @__PURE__ */ jsx44("span", { className: "text-foreground-subtle/25 text-[10px]", children: "\u2192" }),
2534
+ addon.outputClasses.map((c) => /* @__PURE__ */ jsx44("span", { className: "text-[9px] uppercase font-semibold tracking-wide px-1.5 py-0.5 rounded bg-green-500/8 text-green-400/50", children: c }, c))
2347
2535
  ] })
2348
2536
  ] })
2349
2537
  ] })
@@ -2366,7 +2554,7 @@ function PipelineBuilder({
2366
2554
  }) {
2367
2555
  const excluded = useMemo3(() => new Set(excludeAddons), [excludeAddons]);
2368
2556
  const schemaMap = useMemo3(() => buildSchemaMap(schema), [schema]);
2369
- const [warnings, setWarnings] = useState10([]);
2557
+ const [warnings, setWarnings] = useState11([]);
2370
2558
  const bestPlatformScore = capabilities.platformScores?.find((s) => s.available);
2371
2559
  const hasPython = capabilities.runtimes.python.available && capabilities.runtimes.python.backends.some((b) => b.available);
2372
2560
  const defaultRuntime = bestPlatformScore?.runtime ?? (hasPython ? "python" : "node");
@@ -2455,8 +2643,8 @@ function PipelineBuilder({
2455
2643
  }
2456
2644
  function renderStep(step) {
2457
2645
  const childPlaceholders = getChildPlaceholders(step);
2458
- return /* @__PURE__ */ jsxs25("div", { className: "space-y-1.5", children: [
2459
- /* @__PURE__ */ jsx42(
2646
+ return /* @__PURE__ */ jsxs27("div", { className: "space-y-1.5", children: [
2647
+ /* @__PURE__ */ jsx44(
2460
2648
  PipelineStep,
2461
2649
  {
2462
2650
  step,
@@ -2468,12 +2656,12 @@ function PipelineBuilder({
2468
2656
  readOnly
2469
2657
  }
2470
2658
  ),
2471
- (step.children.length > 0 || childPlaceholders.length > 0) && /* @__PURE__ */ jsxs25("div", { className: "ml-6 pl-4 border-l-2 border-dashed border-border/40 space-y-1.5", children: [
2472
- /* @__PURE__ */ jsx42("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/40", children: "Slot: Cropper / Classifier" }),
2659
+ (step.children.length > 0 || childPlaceholders.length > 0) && /* @__PURE__ */ jsxs27("div", { className: "ml-6 pl-4 border-l-2 border-dashed border-border/40 space-y-1.5", children: [
2660
+ /* @__PURE__ */ jsx44("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/40", children: "Slot: Cropper / Classifier" }),
2473
2661
  step.children.map((child) => {
2474
2662
  const childChildPlaceholders = getChildPlaceholders(child);
2475
- return /* @__PURE__ */ jsxs25("div", { className: "space-y-1.5", children: [
2476
- /* @__PURE__ */ jsx42(
2663
+ return /* @__PURE__ */ jsxs27("div", { className: "space-y-1.5", children: [
2664
+ /* @__PURE__ */ jsx44(
2477
2665
  PipelineStep,
2478
2666
  {
2479
2667
  step: child,
@@ -2496,9 +2684,9 @@ function PipelineBuilder({
2496
2684
  readOnly
2497
2685
  }
2498
2686
  ),
2499
- (child.children.length > 0 || childChildPlaceholders.length > 0) && /* @__PURE__ */ jsxs25("div", { className: "ml-6 pl-4 border-l-2 border-dashed border-border/30 space-y-1.5", children: [
2500
- /* @__PURE__ */ jsx42("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/30", children: "Slot: Recognizer" }),
2501
- child.children.map((grandchild) => /* @__PURE__ */ jsx42(
2687
+ (child.children.length > 0 || childChildPlaceholders.length > 0) && /* @__PURE__ */ jsxs27("div", { className: "ml-6 pl-4 border-l-2 border-dashed border-border/30 space-y-1.5", children: [
2688
+ /* @__PURE__ */ jsx44("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/30", children: "Slot: Recognizer" }),
2689
+ child.children.map((grandchild) => /* @__PURE__ */ jsx44(
2502
2690
  PipelineStep,
2503
2691
  {
2504
2692
  step: grandchild,
@@ -2526,7 +2714,7 @@ function PipelineBuilder({
2526
2714
  },
2527
2715
  grandchild.addonId
2528
2716
  )),
2529
- !readOnly && childChildPlaceholders.map((addon) => /* @__PURE__ */ jsx42(
2717
+ !readOnly && childChildPlaceholders.map((addon) => /* @__PURE__ */ jsx44(
2530
2718
  PlaceholderStep,
2531
2719
  {
2532
2720
  addon,
@@ -2545,7 +2733,7 @@ function PipelineBuilder({
2545
2733
  ] })
2546
2734
  ] }, child.addonId);
2547
2735
  }),
2548
- !readOnly && childPlaceholders.map((addon) => /* @__PURE__ */ jsx42(
2736
+ !readOnly && childPlaceholders.map((addon) => /* @__PURE__ */ jsx44(
2549
2737
  PlaceholderStep,
2550
2738
  {
2551
2739
  addon,
@@ -2557,22 +2745,22 @@ function PipelineBuilder({
2557
2745
  ] }, step.addonId);
2558
2746
  }
2559
2747
  const rootSlots = schema.slots.filter((s) => s.parentSlot === null).sort((a, b) => a.priority - b.priority);
2560
- return /* @__PURE__ */ jsxs25("div", { className: "space-y-4", children: [
2561
- /* @__PURE__ */ jsx42("div", { className: "rounded-xl border border-border bg-surface p-3", children: /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2", children: [
2562
- /* @__PURE__ */ jsx42("div", { className: "relative flex-1 min-w-0", children: /* @__PURE__ */ jsxs25(
2748
+ return /* @__PURE__ */ jsxs27("div", { className: "space-y-4", children: [
2749
+ /* @__PURE__ */ jsx44("div", { className: "rounded-xl border border-border bg-surface p-3", children: /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-2", children: [
2750
+ /* @__PURE__ */ jsx44("div", { className: "relative flex-1 min-w-0", children: /* @__PURE__ */ jsxs27(
2563
2751
  "select",
2564
2752
  {
2565
2753
  value: selectedTemplateId ?? "",
2566
2754
  onChange: handleSelectTemplate,
2567
2755
  className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary/50",
2568
2756
  children: [
2569
- /* @__PURE__ */ jsx42("option", { value: "", children: "No template" }),
2570
- templates.map((t) => /* @__PURE__ */ jsx42("option", { value: t.id, children: t.name }, t.id))
2757
+ /* @__PURE__ */ jsx44("option", { value: "", children: "No template" }),
2758
+ templates.map((t) => /* @__PURE__ */ jsx44("option", { value: t.id, children: t.name }, t.id))
2571
2759
  ]
2572
2760
  }
2573
2761
  ) }),
2574
- dirty && /* @__PURE__ */ jsx42("span", { className: "h-1.5 w-1.5 rounded-full bg-amber-500 shrink-0" }),
2575
- /* @__PURE__ */ jsx42(
2762
+ dirty && /* @__PURE__ */ jsx44("span", { className: "h-1.5 w-1.5 rounded-full bg-amber-500 shrink-0" }),
2763
+ /* @__PURE__ */ jsx44(
2576
2764
  "button",
2577
2765
  {
2578
2766
  onClick: handleSave,
@@ -2582,10 +2770,10 @@ function PipelineBuilder({
2582
2770
  "p-2 rounded-lg border border-border transition-colors",
2583
2771
  selectedTemplateId && !readOnly ? "text-foreground-subtle hover:bg-surface-hover" : "text-foreground-subtle/30 cursor-not-allowed"
2584
2772
  ),
2585
- children: /* @__PURE__ */ jsx42(Save, { className: "h-4 w-4" })
2773
+ children: /* @__PURE__ */ jsx44(Save, { className: "h-4 w-4" })
2586
2774
  }
2587
2775
  ),
2588
- /* @__PURE__ */ jsx42(
2776
+ /* @__PURE__ */ jsx44(
2589
2777
  "button",
2590
2778
  {
2591
2779
  onClick: handleSaveAs,
@@ -2595,10 +2783,10 @@ function PipelineBuilder({
2595
2783
  "p-2 rounded-lg border border-border transition-colors",
2596
2784
  !readOnly ? "text-foreground-subtle hover:bg-surface-hover" : "text-foreground-subtle/30 cursor-not-allowed"
2597
2785
  ),
2598
- children: /* @__PURE__ */ jsx42(CopyPlus, { className: "h-4 w-4" })
2786
+ children: /* @__PURE__ */ jsx44(CopyPlus, { className: "h-4 w-4" })
2599
2787
  }
2600
2788
  ),
2601
- /* @__PURE__ */ jsx42(
2789
+ /* @__PURE__ */ jsx44(
2602
2790
  "button",
2603
2791
  {
2604
2792
  onClick: handleDelete,
@@ -2608,16 +2796,16 @@ function PipelineBuilder({
2608
2796
  "p-2 rounded-lg border border-border transition-colors",
2609
2797
  selectedTemplateId && !readOnly ? "text-foreground-subtle hover:text-danger" : "text-foreground-subtle/30 cursor-not-allowed"
2610
2798
  ),
2611
- children: /* @__PURE__ */ jsx42(Trash2, { className: "h-4 w-4" })
2799
+ children: /* @__PURE__ */ jsx44(Trash2, { className: "h-4 w-4" })
2612
2800
  }
2613
2801
  )
2614
2802
  ] }) }),
2615
- warnings.length > 0 && /* @__PURE__ */ jsxs25("div", { className: "rounded-lg border border-amber-500/30 bg-amber-500/5 p-3 text-xs text-amber-400 space-y-1", children: [
2616
- /* @__PURE__ */ jsxs25("div", { className: "flex items-center justify-between", children: [
2617
- /* @__PURE__ */ jsx42("span", { className: "font-medium", children: "Template loaded with warnings:" }),
2618
- /* @__PURE__ */ jsx42("button", { onClick: () => setWarnings([]), className: "text-amber-400/60 hover:text-amber-400", children: /* @__PURE__ */ jsx42(X2, { className: "h-3.5 w-3.5" }) })
2803
+ warnings.length > 0 && /* @__PURE__ */ jsxs27("div", { className: "rounded-lg border border-amber-500/30 bg-amber-500/5 p-3 text-xs text-amber-400 space-y-1", children: [
2804
+ /* @__PURE__ */ jsxs27("div", { className: "flex items-center justify-between", children: [
2805
+ /* @__PURE__ */ jsx44("span", { className: "font-medium", children: "Template loaded with warnings:" }),
2806
+ /* @__PURE__ */ jsx44("button", { onClick: () => setWarnings([]), className: "text-amber-400/60 hover:text-amber-400", children: /* @__PURE__ */ jsx44(X3, { className: "h-3.5 w-3.5" }) })
2619
2807
  ] }),
2620
- warnings.map((w, i) => /* @__PURE__ */ jsxs25("div", { children: [
2808
+ warnings.map((w, i) => /* @__PURE__ */ jsxs27("div", { children: [
2621
2809
  "\u2022 ",
2622
2810
  w
2623
2811
  ] }, i))
@@ -2625,13 +2813,13 @@ function PipelineBuilder({
2625
2813
  rootSlots.map((slot) => {
2626
2814
  const slotSteps = steps.filter((s) => s.slot === slot.id && !excluded.has(s.addonId));
2627
2815
  const missingRootAddons = slot.addons.filter((a) => !existingIds.has(a.id) && !excluded.has(a.id));
2628
- return /* @__PURE__ */ jsxs25("div", { className: "space-y-2", children: [
2629
- /* @__PURE__ */ jsxs25("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/50", children: [
2816
+ return /* @__PURE__ */ jsxs27("div", { className: "space-y-2", children: [
2817
+ /* @__PURE__ */ jsxs27("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/50", children: [
2630
2818
  "Slot: ",
2631
2819
  slot.label
2632
2820
  ] }),
2633
2821
  slotSteps.map((step) => renderStep(step)),
2634
- !readOnly && missingRootAddons.map((addon) => /* @__PURE__ */ jsx42(PlaceholderStep, { addon, onClick: () => {
2822
+ !readOnly && missingRootAddons.map((addon) => /* @__PURE__ */ jsx44(PlaceholderStep, { addon, onClick: () => {
2635
2823
  onChange([...steps, createDefaultStep(addon, defaultRuntime, defaultBackend)]);
2636
2824
  } }, addon.id))
2637
2825
  ] }, slot.id);
@@ -2711,8 +2899,8 @@ function getClassColor(className, customColors) {
2711
2899
  }
2712
2900
 
2713
2901
  // src/composites/detection-canvas.tsx
2714
- import { useRef as useRef6, useEffect as useEffect6 } from "react";
2715
- import { Fragment as Fragment2, jsx as jsx43, jsxs as jsxs26 } from "react/jsx-runtime";
2902
+ import { useRef as useRef7, useEffect as useEffect8 } from "react";
2903
+ import { Fragment as Fragment4, jsx as jsx45, jsxs as jsxs28 } from "react/jsx-runtime";
2716
2904
  var DEFAULT_CLASS_COLORS = CLASS_COLORS;
2717
2905
  function DetectionCanvas({
2718
2906
  src,
@@ -2732,7 +2920,7 @@ function DetectionCanvas({
2732
2920
  }
2733
2921
  const ratio = aspectRatio ?? (imageWidth && imageHeight ? `${imageWidth}/${imageHeight}` : "16/9");
2734
2922
  const filteredDetections = detections.filter((d) => d.confidence >= minConfidence);
2735
- return /* @__PURE__ */ jsx43(
2923
+ return /* @__PURE__ */ jsx45(
2736
2924
  "div",
2737
2925
  {
2738
2926
  className: cn(
@@ -2740,10 +2928,10 @@ function DetectionCanvas({
2740
2928
  className
2741
2929
  ),
2742
2930
  style: { aspectRatio: ratio },
2743
- children: src ? /* @__PURE__ */ jsxs26(Fragment2, { children: [
2744
- /* @__PURE__ */ jsx43("img", { src, className: "absolute inset-0 w-full h-full object-fill", alt: "" }),
2931
+ children: src ? /* @__PURE__ */ jsxs28(Fragment4, { children: [
2932
+ /* @__PURE__ */ jsx45("img", { src, className: "absolute inset-0 w-full h-full object-fill", alt: "" }),
2745
2933
  filteredDetections.map(
2746
- (d, i) => d.mask && d.maskWidth && d.maskHeight ? /* @__PURE__ */ jsx43(
2934
+ (d, i) => d.mask && d.maskWidth && d.maskHeight ? /* @__PURE__ */ jsx45(
2747
2935
  MaskOverlay,
2748
2936
  {
2749
2937
  mask: d.mask,
@@ -2757,7 +2945,7 @@ function DetectionCanvas({
2757
2945
  `mask-${i}`
2758
2946
  ) : null
2759
2947
  ),
2760
- filteredDetections.map((d, i) => /* @__PURE__ */ jsx43(
2948
+ filteredDetections.map((d, i) => /* @__PURE__ */ jsx45(
2761
2949
  BoundingBox,
2762
2950
  {
2763
2951
  detection: d,
@@ -2777,7 +2965,7 @@ function DetectionCanvas({
2777
2965
  const ph = py2 - py1;
2778
2966
  if (pw > 0 && ph > 0 && cw * ch / (pw * ph) > 0.8) return false;
2779
2967
  return true;
2780
- }).map((child, j) => /* @__PURE__ */ jsx43(
2968
+ }).map((child, j) => /* @__PURE__ */ jsx45(
2781
2969
  ChildBoundingBox,
2782
2970
  {
2783
2971
  child,
@@ -2790,7 +2978,7 @@ function DetectionCanvas({
2790
2978
  },
2791
2979
  `det-${i}`
2792
2980
  ))
2793
- ] }) : /* @__PURE__ */ jsx43("div", { className: "w-full h-full flex items-center justify-center text-foreground-subtle text-sm", children: placeholder ?? "No image loaded" })
2981
+ ] }) : /* @__PURE__ */ jsx45("div", { className: "w-full h-full flex items-center justify-center text-foreground-subtle text-sm", children: placeholder ?? "No image loaded" })
2794
2982
  }
2795
2983
  );
2796
2984
  }
@@ -2807,15 +2995,15 @@ function BoundingBox({
2807
2995
  const labelCount = 1 + (detection.labelsData?.length ?? 0);
2808
2996
  const labelHeightPx = labelCount * 16 + 4;
2809
2997
  const topPct = y1 / imageHeight * 100;
2810
- const containerRef = useRef6(null);
2998
+ const containerRef = useRef7(null);
2811
2999
  const showBelow = topPct < labelHeightPx / imageHeight * 100 * 1.5;
2812
- const labelsElement = /* @__PURE__ */ jsxs26(
3000
+ const labelsElement = /* @__PURE__ */ jsxs28(
2813
3001
  "div",
2814
3002
  {
2815
3003
  className: `absolute left-0 flex flex-col items-start gap-px ${showBelow ? "" : ""}`,
2816
3004
  style: showBelow ? { top: "100%", marginTop: "2px" } : { bottom: "100%", marginBottom: "2px" },
2817
3005
  children: [
2818
- /* @__PURE__ */ jsxs26(
3006
+ /* @__PURE__ */ jsxs28(
2819
3007
  "span",
2820
3008
  {
2821
3009
  className: "text-[10px] px-1 rounded-sm whitespace-nowrap text-white",
@@ -2826,7 +3014,7 @@ function BoundingBox({
2826
3014
  ]
2827
3015
  }
2828
3016
  ),
2829
- detection.labelsData?.map((l, k) => /* @__PURE__ */ jsxs26(
3017
+ detection.labelsData?.map((l, k) => /* @__PURE__ */ jsxs28(
2830
3018
  "span",
2831
3019
  {
2832
3020
  className: "text-[9px] font-semibold px-1 rounded-sm whitespace-nowrap text-white",
@@ -2843,7 +3031,7 @@ function BoundingBox({
2843
3031
  ]
2844
3032
  }
2845
3033
  );
2846
- return /* @__PURE__ */ jsxs26(
3034
+ return /* @__PURE__ */ jsxs28(
2847
3035
  "div",
2848
3036
  {
2849
3037
  ref: containerRef,
@@ -2873,8 +3061,8 @@ function MaskOverlay({
2873
3061
  imageHeight,
2874
3062
  color
2875
3063
  }) {
2876
- const canvasRef = useRef6(null);
2877
- useEffect6(() => {
3064
+ const canvasRef = useRef7(null);
3065
+ useEffect8(() => {
2878
3066
  const canvas = canvasRef.current;
2879
3067
  if (!canvas) return;
2880
3068
  const ctx = canvas.getContext("2d");
@@ -2902,7 +3090,7 @@ function MaskOverlay({
2902
3090
  ctx.putImageData(imageData, 0, 0);
2903
3091
  }, [mask, maskWidth, maskHeight, color]);
2904
3092
  const [x1, y1, x2, y2] = bbox;
2905
- return /* @__PURE__ */ jsx43(
3093
+ return /* @__PURE__ */ jsx45(
2906
3094
  "canvas",
2907
3095
  {
2908
3096
  ref: canvasRef,
@@ -2928,7 +3116,7 @@ function ChildBoundingBox({
2928
3116
  const pw = px2 - px1;
2929
3117
  const ph = py2 - py1;
2930
3118
  if (pw <= 0 || ph <= 0) return null;
2931
- return /* @__PURE__ */ jsx43(
3119
+ return /* @__PURE__ */ jsx45(
2932
3120
  "div",
2933
3121
  {
2934
3122
  className: "absolute rounded-sm",
@@ -2945,13 +3133,13 @@ function ChildBoundingBox({
2945
3133
  const labelCount = 1 + (child.labelsData?.length ?? 0);
2946
3134
  const relTop = (cy1 - py1) / ph * 100;
2947
3135
  const showBelow = relTop < labelCount * 6;
2948
- return /* @__PURE__ */ jsxs26(
3136
+ return /* @__PURE__ */ jsxs28(
2949
3137
  "div",
2950
3138
  {
2951
3139
  className: "absolute left-0 flex flex-col items-start gap-px",
2952
3140
  style: showBelow ? { top: "100%", marginTop: "1px" } : { bottom: "100%", marginBottom: "1px" },
2953
3141
  children: [
2954
- /* @__PURE__ */ jsxs26(
3142
+ /* @__PURE__ */ jsxs28(
2955
3143
  "span",
2956
3144
  {
2957
3145
  className: "text-[9px] px-0.5 rounded-sm whitespace-nowrap text-white",
@@ -2962,7 +3150,7 @@ function ChildBoundingBox({
2962
3150
  ]
2963
3151
  }
2964
3152
  ),
2965
- child.labelsData?.map((l, k) => /* @__PURE__ */ jsxs26(
3153
+ child.labelsData?.map((l, k) => /* @__PURE__ */ jsxs28(
2966
3154
  "span",
2967
3155
  {
2968
3156
  className: "text-[8px] font-semibold px-0.5 rounded-sm whitespace-nowrap text-white",
@@ -2985,7 +3173,7 @@ function ChildBoundingBox({
2985
3173
  }
2986
3174
 
2987
3175
  // src/composites/detection-result-tree.tsx
2988
- import { jsx as jsx44, jsxs as jsxs27 } from "react/jsx-runtime";
3176
+ import { jsx as jsx46, jsxs as jsxs29 } from "react/jsx-runtime";
2989
3177
  function DetectionResultTree({
2990
3178
  detections,
2991
3179
  classColors,
@@ -2995,15 +3183,15 @@ function DetectionResultTree({
2995
3183
  }) {
2996
3184
  const colors = classColors;
2997
3185
  if (detections.length === 0) {
2998
- return /* @__PURE__ */ jsx44("div", { className: "text-sm text-foreground-subtle italic text-center py-4", children: "No detections" });
3186
+ return /* @__PURE__ */ jsx46("div", { className: "text-sm text-foreground-subtle italic text-center py-4", children: "No detections" });
2999
3187
  }
3000
- return /* @__PURE__ */ jsxs27("div", { className, children: [
3001
- /* @__PURE__ */ jsxs27("div", { className: "text-xs font-medium text-foreground-subtle uppercase tracking-wide mb-2", children: [
3188
+ return /* @__PURE__ */ jsxs29("div", { className, children: [
3189
+ /* @__PURE__ */ jsxs29("div", { className: "text-xs font-medium text-foreground-subtle uppercase tracking-wide mb-2", children: [
3002
3190
  "Detections (",
3003
3191
  detections.length,
3004
3192
  ")"
3005
3193
  ] }),
3006
- /* @__PURE__ */ jsx44("div", { className: "space-y-2", children: detections.map((d, i) => /* @__PURE__ */ jsx44(
3194
+ /* @__PURE__ */ jsx46("div", { className: "space-y-2", children: detections.map((d, i) => /* @__PURE__ */ jsx46(
3007
3195
  DetectionNode,
3008
3196
  {
3009
3197
  detection: d,
@@ -3025,10 +3213,10 @@ function DetectionNode({
3025
3213
  }) {
3026
3214
  const color = getClassColor(detection.className, colors);
3027
3215
  const isVisible = !hiddenKeys?.has(path);
3028
- return /* @__PURE__ */ jsxs27("div", { className: `rounded-md border border-border bg-surface p-3 space-y-1 ${isVisible ? "" : "opacity-40"}`, children: [
3029
- /* @__PURE__ */ jsxs27("div", { className: "flex justify-between items-center", children: [
3030
- /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-2", children: [
3031
- onToggleVisibility && /* @__PURE__ */ jsx44(
3216
+ return /* @__PURE__ */ jsxs29("div", { className: `rounded-md border border-border bg-surface p-3 space-y-1 ${isVisible ? "" : "opacity-40"}`, children: [
3217
+ /* @__PURE__ */ jsxs29("div", { className: "flex justify-between items-center", children: [
3218
+ /* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-2", children: [
3219
+ onToggleVisibility && /* @__PURE__ */ jsx46(
3032
3220
  "input",
3033
3221
  {
3034
3222
  type: "checkbox",
@@ -3037,45 +3225,45 @@ function DetectionNode({
3037
3225
  className: "h-3.5 w-3.5 rounded border-border accent-primary cursor-pointer shrink-0"
3038
3226
  }
3039
3227
  ),
3040
- /* @__PURE__ */ jsx44(
3228
+ /* @__PURE__ */ jsx46(
3041
3229
  "span",
3042
3230
  {
3043
3231
  className: "h-2.5 w-2.5 rounded-full shrink-0",
3044
3232
  style: { backgroundColor: color }
3045
3233
  }
3046
3234
  ),
3047
- /* @__PURE__ */ jsx44("span", { className: "text-sm font-medium text-foreground", children: detection.className }),
3048
- detection.mask && detection.maskWidth && detection.maskHeight && /* @__PURE__ */ jsxs27("span", { className: "text-[9px] font-mono px-1 py-0.5 rounded bg-primary/10 text-primary", children: [
3235
+ /* @__PURE__ */ jsx46("span", { className: "text-sm font-medium text-foreground", children: detection.className }),
3236
+ detection.mask && detection.maskWidth && detection.maskHeight && /* @__PURE__ */ jsxs29("span", { className: "text-[9px] font-mono px-1 py-0.5 rounded bg-primary/10 text-primary", children: [
3049
3237
  "mask ",
3050
3238
  detection.maskWidth,
3051
3239
  "x",
3052
3240
  detection.maskHeight
3053
3241
  ] })
3054
3242
  ] }),
3055
- /* @__PURE__ */ jsx44(ConfidenceBadge, { confidence: detection.confidence })
3243
+ /* @__PURE__ */ jsx46(ConfidenceBadge, { confidence: detection.confidence })
3056
3244
  ] }),
3057
- /* @__PURE__ */ jsxs27("div", { className: "text-[10px] text-foreground-subtle font-mono", children: [
3245
+ /* @__PURE__ */ jsxs29("div", { className: "text-[10px] text-foreground-subtle font-mono", children: [
3058
3246
  "bbox: [",
3059
3247
  detection.bbox.map((v) => Math.round(v)).join(", "),
3060
3248
  "]"
3061
3249
  ] }),
3062
- detection.labelsData && detection.labelsData.length > 0 && /* @__PURE__ */ jsx44("div", { className: "flex flex-wrap gap-1 mt-1", children: detection.labelsData.map((l, k) => /* @__PURE__ */ jsxs27(
3250
+ detection.labelsData && detection.labelsData.length > 0 && /* @__PURE__ */ jsx46("div", { className: "flex flex-wrap gap-1 mt-1", children: detection.labelsData.map((l, k) => /* @__PURE__ */ jsxs29(
3063
3251
  "span",
3064
3252
  {
3065
3253
  className: "inline-flex items-center gap-1 text-[10px] font-medium px-1.5 py-0.5 rounded-full",
3066
3254
  style: { backgroundColor: getClassColor(l.addonId ?? l.label, colors) + "20", color: getClassColor(l.addonId ?? l.label, colors) },
3067
3255
  children: [
3068
3256
  l.label,
3069
- /* @__PURE__ */ jsxs27("span", { className: "opacity-60", children: [
3257
+ /* @__PURE__ */ jsxs29("span", { className: "opacity-60", children: [
3070
3258
  (l.score * 100).toFixed(0),
3071
3259
  "%"
3072
3260
  ] }),
3073
- l.addonId && /* @__PURE__ */ jsx44("span", { className: "opacity-40 text-[8px]", children: l.addonId })
3261
+ l.addonId && /* @__PURE__ */ jsx46("span", { className: "opacity-40 text-[8px]", children: l.addonId })
3074
3262
  ]
3075
3263
  },
3076
3264
  k
3077
3265
  )) }),
3078
- detection.children && detection.children.length > 0 && /* @__PURE__ */ jsx44(
3266
+ detection.children && detection.children.length > 0 && /* @__PURE__ */ jsx46(
3079
3267
  ChildrenTree,
3080
3268
  {
3081
3269
  children: detection.children,
@@ -3094,13 +3282,13 @@ function ChildrenTree({
3094
3282
  hiddenKeys,
3095
3283
  onToggleVisibility
3096
3284
  }) {
3097
- return /* @__PURE__ */ jsx44("div", { className: "ml-4 mt-1.5 space-y-1.5 border-l-2 border-border pl-3", children: children.map((child, j) => {
3285
+ return /* @__PURE__ */ jsx46("div", { className: "ml-4 mt-1.5 space-y-1.5 border-l-2 border-border pl-3", children: children.map((child, j) => {
3098
3286
  const childPath = `${parentPath}.${j}`;
3099
3287
  const childColor = getClassColor(child.className, colors);
3100
3288
  const isVisible = !hiddenKeys?.has(childPath);
3101
- return /* @__PURE__ */ jsxs27("div", { className: `text-xs space-y-0.5 ${isVisible ? "" : "opacity-40"}`, children: [
3102
- /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-1.5", children: [
3103
- onToggleVisibility && /* @__PURE__ */ jsx44(
3289
+ return /* @__PURE__ */ jsxs29("div", { className: `text-xs space-y-0.5 ${isVisible ? "" : "opacity-40"}`, children: [
3290
+ /* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-1.5", children: [
3291
+ onToggleVisibility && /* @__PURE__ */ jsx46(
3104
3292
  "input",
3105
3293
  {
3106
3294
  type: "checkbox",
@@ -3109,26 +3297,26 @@ function ChildrenTree({
3109
3297
  className: "h-3 w-3 rounded border-border accent-primary cursor-pointer shrink-0"
3110
3298
  }
3111
3299
  ),
3112
- /* @__PURE__ */ jsx44(
3300
+ /* @__PURE__ */ jsx46(
3113
3301
  "span",
3114
3302
  {
3115
3303
  className: "h-1.5 w-1.5 rounded-full shrink-0",
3116
3304
  style: { backgroundColor: childColor }
3117
3305
  }
3118
3306
  ),
3119
- /* @__PURE__ */ jsx44("span", { className: "font-medium", style: { color: childColor }, children: child.className }),
3120
- /* @__PURE__ */ jsxs27("span", { className: "text-foreground-subtle", children: [
3307
+ /* @__PURE__ */ jsx46("span", { className: "font-medium", style: { color: childColor }, children: child.className }),
3308
+ /* @__PURE__ */ jsxs29("span", { className: "text-foreground-subtle", children: [
3121
3309
  (child.confidence * 100).toFixed(0),
3122
3310
  "%"
3123
3311
  ] }),
3124
- child.mask && child.maskWidth && child.maskHeight && /* @__PURE__ */ jsxs27("span", { className: "text-[9px] font-mono px-1 py-0.5 rounded bg-primary/10 text-primary", children: [
3312
+ child.mask && child.maskWidth && child.maskHeight && /* @__PURE__ */ jsxs29("span", { className: "text-[9px] font-mono px-1 py-0.5 rounded bg-primary/10 text-primary", children: [
3125
3313
  "mask ",
3126
3314
  child.maskWidth,
3127
3315
  "x",
3128
3316
  child.maskHeight
3129
3317
  ] })
3130
3318
  ] }),
3131
- child.labelsData && child.labelsData.length > 0 && /* @__PURE__ */ jsx44("div", { className: "flex flex-wrap gap-1 ml-5 mt-0.5", children: child.labelsData.map((l, k) => /* @__PURE__ */ jsxs27(
3319
+ child.labelsData && child.labelsData.length > 0 && /* @__PURE__ */ jsx46("div", { className: "flex flex-wrap gap-1 ml-5 mt-0.5", children: child.labelsData.map((l, k) => /* @__PURE__ */ jsxs29(
3132
3320
  "span",
3133
3321
  {
3134
3322
  className: "inline-flex items-center gap-0.5 text-[9px] font-medium px-1 py-0.5 rounded-full",
@@ -3136,7 +3324,7 @@ function ChildrenTree({
3136
3324
  children: [
3137
3325
  l.label,
3138
3326
  " ",
3139
- /* @__PURE__ */ jsxs27("span", { className: "opacity-60", children: [
3327
+ /* @__PURE__ */ jsxs29("span", { className: "opacity-60", children: [
3140
3328
  (l.score * 100).toFixed(0),
3141
3329
  "%"
3142
3330
  ] })
@@ -3144,7 +3332,7 @@ function ChildrenTree({
3144
3332
  },
3145
3333
  k
3146
3334
  )) }),
3147
- child.children && child.children.length > 0 && /* @__PURE__ */ jsx44(
3335
+ child.children && child.children.length > 0 && /* @__PURE__ */ jsx46(
3148
3336
  ChildrenTree,
3149
3337
  {
3150
3338
  children: child.children,
@@ -3159,30 +3347,30 @@ function ChildrenTree({
3159
3347
  }
3160
3348
  function ConfidenceBadge({ confidence }) {
3161
3349
  const level = confidence >= 0.8 ? "bg-success/10 text-success" : confidence >= 0.5 ? "bg-warning/10 text-warning" : "bg-danger/10 text-danger";
3162
- return /* @__PURE__ */ jsxs27("span", { className: `text-xs font-medium px-2 py-0.5 rounded-full ${level}`, children: [
3350
+ return /* @__PURE__ */ jsxs29("span", { className: `text-xs font-medium px-2 py-0.5 rounded-full ${level}`, children: [
3163
3351
  (confidence * 100).toFixed(1),
3164
3352
  "%"
3165
3353
  ] });
3166
3354
  }
3167
3355
 
3168
3356
  // src/composites/step-timings.tsx
3169
- import { jsx as jsx45, jsxs as jsxs28 } from "react/jsx-runtime";
3357
+ import { jsx as jsx47, jsxs as jsxs30 } from "react/jsx-runtime";
3170
3358
  function StepTimings({ timings, totalMs, className }) {
3171
3359
  const entries = Object.entries(timings);
3172
3360
  if (entries.length === 0 && totalMs === void 0) return null;
3173
- return /* @__PURE__ */ jsxs28("div", { className: `rounded-lg border border-border bg-surface p-3 space-y-2 ${className ?? ""}`, children: [
3174
- /* @__PURE__ */ jsx45("div", { className: "text-xs font-medium text-foreground-subtle uppercase tracking-wide", children: "Timings" }),
3175
- /* @__PURE__ */ jsxs28("div", { className: "space-y-1 text-xs", children: [
3176
- entries.map(([step, ms]) => /* @__PURE__ */ jsxs28("div", { className: "flex justify-between", children: [
3177
- /* @__PURE__ */ jsx45("span", { className: "text-foreground-subtle", children: step }),
3178
- /* @__PURE__ */ jsxs28("span", { className: "font-mono text-foreground", children: [
3361
+ return /* @__PURE__ */ jsxs30("div", { className: `rounded-lg border border-border bg-surface p-3 space-y-2 ${className ?? ""}`, children: [
3362
+ /* @__PURE__ */ jsx47("div", { className: "text-xs font-medium text-foreground-subtle uppercase tracking-wide", children: "Timings" }),
3363
+ /* @__PURE__ */ jsxs30("div", { className: "space-y-1 text-xs", children: [
3364
+ entries.map(([step, ms]) => /* @__PURE__ */ jsxs30("div", { className: "flex justify-between", children: [
3365
+ /* @__PURE__ */ jsx47("span", { className: "text-foreground-subtle", children: step }),
3366
+ /* @__PURE__ */ jsxs30("span", { className: "font-mono text-foreground", children: [
3179
3367
  ms.toFixed(1),
3180
3368
  "ms"
3181
3369
  ] })
3182
3370
  ] }, step)),
3183
- totalMs !== void 0 && /* @__PURE__ */ jsxs28("div", { className: "flex justify-between pt-1 border-t border-border font-medium text-foreground", children: [
3184
- /* @__PURE__ */ jsx45("span", { children: "Total" }),
3185
- /* @__PURE__ */ jsxs28("span", { className: "font-mono", children: [
3371
+ totalMs !== void 0 && /* @__PURE__ */ jsxs30("div", { className: "flex justify-between pt-1 border-t border-border font-medium text-foreground", children: [
3372
+ /* @__PURE__ */ jsx47("span", { children: "Total" }),
3373
+ /* @__PURE__ */ jsxs30("span", { className: "font-mono", children: [
3186
3374
  totalMs.toFixed(1),
3187
3375
  "ms"
3188
3376
  ] })
@@ -3192,7 +3380,7 @@ function StepTimings({ timings, totalMs, className }) {
3192
3380
  }
3193
3381
 
3194
3382
  // src/composites/image-selector.tsx
3195
- import { jsx as jsx46, jsxs as jsxs29 } from "react/jsx-runtime";
3383
+ import { jsx as jsx48, jsxs as jsxs31 } from "react/jsx-runtime";
3196
3384
  function ImageSelector({
3197
3385
  images,
3198
3386
  selectedFilename,
@@ -3218,8 +3406,8 @@ function ImageSelector({
3218
3406
  };
3219
3407
  input.click();
3220
3408
  };
3221
- return /* @__PURE__ */ jsxs29("div", { className: `flex flex-wrap items-center gap-2 ${className ?? ""}`, children: [
3222
- images.map((img) => /* @__PURE__ */ jsx46(
3409
+ return /* @__PURE__ */ jsxs31("div", { className: `flex flex-wrap items-center gap-2 ${className ?? ""}`, children: [
3410
+ images.map((img) => /* @__PURE__ */ jsx48(
3223
3411
  "button",
3224
3412
  {
3225
3413
  onClick: () => onSelect(img.filename),
@@ -3228,7 +3416,7 @@ function ImageSelector({
3228
3416
  },
3229
3417
  img.filename
3230
3418
  )),
3231
- /* @__PURE__ */ jsx46(
3419
+ /* @__PURE__ */ jsx48(
3232
3420
  "button",
3233
3421
  {
3234
3422
  onClick: handleUploadClick,
@@ -3236,12 +3424,12 @@ function ImageSelector({
3236
3424
  children: "Upload..."
3237
3425
  }
3238
3426
  ),
3239
- uploadedName && /* @__PURE__ */ jsx46("span", { className: "text-xs text-foreground-subtle", children: uploadedName })
3427
+ uploadedName && /* @__PURE__ */ jsx48("span", { className: "text-xs text-foreground-subtle", children: uploadedName })
3240
3428
  ] });
3241
3429
  }
3242
3430
 
3243
3431
  // src/composites/inference-config-selector.tsx
3244
- import { jsx as jsx47, jsxs as jsxs30 } from "react/jsx-runtime";
3432
+ import { jsx as jsx49, jsxs as jsxs32 } from "react/jsx-runtime";
3245
3433
  var SELECT_CLASS = "w-full px-3 py-2 text-sm rounded-md border border-border bg-surface text-foreground focus:outline-none focus:ring-2 focus:ring-primary/50";
3246
3434
  function InferenceConfigSelector({
3247
3435
  runtime,
@@ -3261,16 +3449,16 @@ function InferenceConfigSelector({
3261
3449
  showAgent = false
3262
3450
  }) {
3263
3451
  const containerClass = layout === "grid" ? "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4" : layout === "horizontal" ? "flex flex-wrap items-end gap-4" : "space-y-3";
3264
- return /* @__PURE__ */ jsxs30("div", { className: `${containerClass} ${className ?? ""}`, children: [
3265
- showAgent && agents.length > 0 && /* @__PURE__ */ jsxs30("label", { className: "space-y-1", children: [
3266
- /* @__PURE__ */ jsx47("span", { className: "text-xs font-medium text-foreground-subtle", children: "Agent" }),
3267
- /* @__PURE__ */ jsx47(
3452
+ return /* @__PURE__ */ jsxs32("div", { className: `${containerClass} ${className ?? ""}`, children: [
3453
+ showAgent && agents.length > 0 && /* @__PURE__ */ jsxs32("label", { className: "space-y-1", children: [
3454
+ /* @__PURE__ */ jsx49("span", { className: "text-xs font-medium text-foreground-subtle", children: "Agent" }),
3455
+ /* @__PURE__ */ jsx49(
3268
3456
  "select",
3269
3457
  {
3270
3458
  value: agentId,
3271
3459
  onChange: (e) => onAgentChange?.(e.target.value),
3272
3460
  className: SELECT_CLASS,
3273
- children: agents.map((a) => /* @__PURE__ */ jsxs30("option", { value: a.id, children: [
3461
+ children: agents.map((a) => /* @__PURE__ */ jsxs32("option", { value: a.id, children: [
3274
3462
  a.name,
3275
3463
  " (",
3276
3464
  a.status,
@@ -3279,45 +3467,45 @@ function InferenceConfigSelector({
3279
3467
  }
3280
3468
  )
3281
3469
  ] }),
3282
- /* @__PURE__ */ jsxs30("label", { className: "space-y-1", children: [
3283
- /* @__PURE__ */ jsx47("span", { className: "text-xs font-medium text-foreground-subtle", children: "Runtime" }),
3284
- /* @__PURE__ */ jsx47(
3470
+ /* @__PURE__ */ jsxs32("label", { className: "space-y-1", children: [
3471
+ /* @__PURE__ */ jsx49("span", { className: "text-xs font-medium text-foreground-subtle", children: "Runtime" }),
3472
+ /* @__PURE__ */ jsx49(
3285
3473
  "select",
3286
3474
  {
3287
3475
  value: runtime,
3288
3476
  onChange: (e) => onRuntimeChange(e.target.value),
3289
3477
  className: SELECT_CLASS,
3290
- children: runtimes.map((r) => /* @__PURE__ */ jsxs30("option", { value: r.value, disabled: !r.available, children: [
3478
+ children: runtimes.map((r) => /* @__PURE__ */ jsxs32("option", { value: r.value, disabled: !r.available, children: [
3291
3479
  r.label,
3292
3480
  !r.available ? " (unavailable)" : ""
3293
3481
  ] }, r.value))
3294
3482
  }
3295
3483
  )
3296
3484
  ] }),
3297
- /* @__PURE__ */ jsxs30("label", { className: "space-y-1", children: [
3298
- /* @__PURE__ */ jsx47("span", { className: "text-xs font-medium text-foreground-subtle", children: "Backend" }),
3299
- /* @__PURE__ */ jsx47(
3485
+ /* @__PURE__ */ jsxs32("label", { className: "space-y-1", children: [
3486
+ /* @__PURE__ */ jsx49("span", { className: "text-xs font-medium text-foreground-subtle", children: "Backend" }),
3487
+ /* @__PURE__ */ jsx49(
3300
3488
  "select",
3301
3489
  {
3302
3490
  value: backend,
3303
3491
  onChange: (e) => onBackendChange(e.target.value),
3304
3492
  className: SELECT_CLASS,
3305
- children: backends.map((b) => /* @__PURE__ */ jsxs30("option", { value: b.id, disabled: !b.available, children: [
3493
+ children: backends.map((b) => /* @__PURE__ */ jsxs32("option", { value: b.id, disabled: !b.available, children: [
3306
3494
  b.label,
3307
3495
  !b.available ? " (unavailable)" : ""
3308
3496
  ] }, b.id))
3309
3497
  }
3310
3498
  )
3311
3499
  ] }),
3312
- /* @__PURE__ */ jsxs30("label", { className: "space-y-1", children: [
3313
- /* @__PURE__ */ jsx47("span", { className: "text-xs font-medium text-foreground-subtle", children: "Model" }),
3314
- /* @__PURE__ */ jsx47(
3500
+ /* @__PURE__ */ jsxs32("label", { className: "space-y-1", children: [
3501
+ /* @__PURE__ */ jsx49("span", { className: "text-xs font-medium text-foreground-subtle", children: "Model" }),
3502
+ /* @__PURE__ */ jsx49(
3315
3503
  "select",
3316
3504
  {
3317
3505
  value: modelId,
3318
3506
  onChange: (e) => onModelChange(e.target.value),
3319
3507
  className: SELECT_CLASS,
3320
- children: models.length === 0 ? /* @__PURE__ */ jsx47("option", { value: "", children: "No compatible models" }) : models.map((m) => /* @__PURE__ */ jsxs30("option", { value: m.id, children: [
3508
+ children: models.length === 0 ? /* @__PURE__ */ jsx49("option", { value: "", children: "No compatible models" }) : models.map((m) => /* @__PURE__ */ jsxs32("option", { value: m.id, children: [
3321
3509
  m.name,
3322
3510
  m.downloaded ? " \u2713" : ""
3323
3511
  ] }, m.id))
@@ -3332,15 +3520,15 @@ import { createElement } from "react";
3332
3520
  import { createRoot } from "react-dom/client";
3333
3521
 
3334
3522
  // src/composites/dev-shell.tsx
3335
- import { createContext as createContext7, useCallback as useCallback8, useContext as useContext7, useMemo as useMemo4, useState as useState12 } from "react";
3523
+ import { createContext as createContext7, useCallback as useCallback8, useContext as useContext7, useMemo as useMemo4, useState as useState13 } from "react";
3336
3524
  import { createTRPCClient, createWSClient, wsLink, httpLink, splitLink } from "@trpc/client";
3337
3525
  import superjson from "superjson";
3338
3526
 
3339
3527
  // src/composites/login-form.tsx
3340
- import { useState as useState11 } from "react";
3341
- import { jsx as jsx48, jsxs as jsxs31 } from "react/jsx-runtime";
3528
+ import { useState as useState12 } from "react";
3529
+ import { jsx as jsx50, jsxs as jsxs33 } from "react/jsx-runtime";
3342
3530
  function EyeIcon({ className }) {
3343
- return /* @__PURE__ */ jsxs31(
3531
+ return /* @__PURE__ */ jsxs33(
3344
3532
  "svg",
3345
3533
  {
3346
3534
  xmlns: "http://www.w3.org/2000/svg",
@@ -3352,14 +3540,14 @@ function EyeIcon({ className }) {
3352
3540
  strokeLinejoin: "round",
3353
3541
  className,
3354
3542
  children: [
3355
- /* @__PURE__ */ jsx48("path", { d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" }),
3356
- /* @__PURE__ */ jsx48("circle", { cx: "12", cy: "12", r: "3" })
3543
+ /* @__PURE__ */ jsx50("path", { d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" }),
3544
+ /* @__PURE__ */ jsx50("circle", { cx: "12", cy: "12", r: "3" })
3357
3545
  ]
3358
3546
  }
3359
3547
  );
3360
3548
  }
3361
3549
  function EyeOffIcon({ className }) {
3362
- return /* @__PURE__ */ jsxs31(
3550
+ return /* @__PURE__ */ jsxs33(
3363
3551
  "svg",
3364
3552
  {
3365
3553
  xmlns: "http://www.w3.org/2000/svg",
@@ -3371,16 +3559,16 @@ function EyeOffIcon({ className }) {
3371
3559
  strokeLinejoin: "round",
3372
3560
  className,
3373
3561
  children: [
3374
- /* @__PURE__ */ jsx48("path", { d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" }),
3375
- /* @__PURE__ */ jsx48("path", { d: "M14.084 14.158a3 3 0 0 1-4.242-4.242" }),
3376
- /* @__PURE__ */ jsx48("path", { d: "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" }),
3377
- /* @__PURE__ */ jsx48("path", { d: "m2 2 20 20" })
3562
+ /* @__PURE__ */ jsx50("path", { d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" }),
3563
+ /* @__PURE__ */ jsx50("path", { d: "M14.084 14.158a3 3 0 0 1-4.242-4.242" }),
3564
+ /* @__PURE__ */ jsx50("path", { d: "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" }),
3565
+ /* @__PURE__ */ jsx50("path", { d: "m2 2 20 20" })
3378
3566
  ]
3379
3567
  }
3380
3568
  );
3381
3569
  }
3382
3570
  function SpinnerIcon({ className }) {
3383
- return /* @__PURE__ */ jsx48(
3571
+ return /* @__PURE__ */ jsx50(
3384
3572
  "svg",
3385
3573
  {
3386
3574
  xmlns: "http://www.w3.org/2000/svg",
@@ -3391,7 +3579,7 @@ function SpinnerIcon({ className }) {
3391
3579
  strokeLinecap: "round",
3392
3580
  strokeLinejoin: "round",
3393
3581
  className,
3394
- children: /* @__PURE__ */ jsx48("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
3582
+ children: /* @__PURE__ */ jsx50("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
3395
3583
  }
3396
3584
  );
3397
3585
  }
@@ -3402,11 +3590,11 @@ function LoginForm({
3402
3590
  error: externalError,
3403
3591
  className
3404
3592
  }) {
3405
- const [username, setUsername] = useState11("");
3406
- const [password, setPassword] = useState11("");
3407
- const [showPassword, setShowPassword] = useState11(false);
3408
- const [submitting, setSubmitting] = useState11(false);
3409
- const [internalError, setInternalError] = useState11(null);
3593
+ const [username, setUsername] = useState12("");
3594
+ const [password, setPassword] = useState12("");
3595
+ const [showPassword, setShowPassword] = useState12(false);
3596
+ const [submitting, setSubmitting] = useState12(false);
3597
+ const [internalError, setInternalError] = useState12(null);
3410
3598
  const error = externalError ?? internalError;
3411
3599
  const handleSubmit = async (e) => {
3412
3600
  e.preventDefault();
@@ -3422,26 +3610,26 @@ function LoginForm({
3422
3610
  setSubmitting(false);
3423
3611
  }
3424
3612
  };
3425
- return /* @__PURE__ */ jsx48(
3613
+ return /* @__PURE__ */ jsx50(
3426
3614
  "div",
3427
3615
  {
3428
3616
  className: cn(
3429
3617
  "flex min-h-screen items-center justify-center bg-background p-4",
3430
3618
  className
3431
3619
  ),
3432
- children: /* @__PURE__ */ jsxs31("div", { className: "w-full max-w-sm", children: [
3433
- logoSrc && /* @__PURE__ */ jsx48("div", { className: "flex justify-center mb-8", children: /* @__PURE__ */ jsx48("img", { src: logoSrc, alt: "Logo", className: "h-12" }) }),
3434
- serverUrl && /* @__PURE__ */ jsx48("p", { className: "mb-4 text-center text-xs text-foreground-subtle truncate", children: serverUrl }),
3435
- /* @__PURE__ */ jsxs31(
3620
+ children: /* @__PURE__ */ jsxs33("div", { className: "w-full max-w-sm", children: [
3621
+ logoSrc && /* @__PURE__ */ jsx50("div", { className: "flex justify-center mb-8", children: /* @__PURE__ */ jsx50("img", { src: logoSrc, alt: "Logo", className: "h-12" }) }),
3622
+ serverUrl && /* @__PURE__ */ jsx50("p", { className: "mb-4 text-center text-xs text-foreground-subtle truncate", children: serverUrl }),
3623
+ /* @__PURE__ */ jsxs33(
3436
3624
  "form",
3437
3625
  {
3438
3626
  onSubmit: handleSubmit,
3439
3627
  className: "space-y-4 rounded-xl border border-border bg-surface p-6 shadow-xl shadow-black/10",
3440
3628
  children: [
3441
- error && /* @__PURE__ */ jsx48("div", { className: "rounded-md bg-danger/10 border border-danger/20 px-3 py-2 text-xs text-danger", children: error }),
3442
- /* @__PURE__ */ jsxs31("div", { className: "space-y-1.5", children: [
3443
- /* @__PURE__ */ jsx48("label", { className: "text-xs font-medium text-foreground-subtle", children: "Username" }),
3444
- /* @__PURE__ */ jsx48(
3629
+ error && /* @__PURE__ */ jsx50("div", { className: "rounded-md bg-danger/10 border border-danger/20 px-3 py-2 text-xs text-danger", children: error }),
3630
+ /* @__PURE__ */ jsxs33("div", { className: "space-y-1.5", children: [
3631
+ /* @__PURE__ */ jsx50("label", { className: "text-xs font-medium text-foreground-subtle", children: "Username" }),
3632
+ /* @__PURE__ */ jsx50(
3445
3633
  "input",
3446
3634
  {
3447
3635
  type: "text",
@@ -3453,10 +3641,10 @@ function LoginForm({
3453
3641
  }
3454
3642
  )
3455
3643
  ] }),
3456
- /* @__PURE__ */ jsxs31("div", { className: "space-y-1.5", children: [
3457
- /* @__PURE__ */ jsx48("label", { className: "text-xs font-medium text-foreground-subtle", children: "Password" }),
3458
- /* @__PURE__ */ jsxs31("div", { className: "relative", children: [
3459
- /* @__PURE__ */ jsx48(
3644
+ /* @__PURE__ */ jsxs33("div", { className: "space-y-1.5", children: [
3645
+ /* @__PURE__ */ jsx50("label", { className: "text-xs font-medium text-foreground-subtle", children: "Password" }),
3646
+ /* @__PURE__ */ jsxs33("div", { className: "relative", children: [
3647
+ /* @__PURE__ */ jsx50(
3460
3648
  "input",
3461
3649
  {
3462
3650
  type: showPassword ? "text" : "password",
@@ -3467,26 +3655,26 @@ function LoginForm({
3467
3655
  className: "w-full rounded-lg border border-border bg-background px-3 py-2.5 pr-10 text-sm text-foreground placeholder:text-foreground-subtle focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary"
3468
3656
  }
3469
3657
  ),
3470
- /* @__PURE__ */ jsx48(
3658
+ /* @__PURE__ */ jsx50(
3471
3659
  "button",
3472
3660
  {
3473
3661
  type: "button",
3474
3662
  onClick: () => setShowPassword((prev) => !prev),
3475
3663
  className: "absolute right-2.5 top-1/2 -translate-y-1/2 text-foreground-subtle hover:text-foreground",
3476
3664
  tabIndex: -1,
3477
- children: showPassword ? /* @__PURE__ */ jsx48(EyeOffIcon, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx48(EyeIcon, { className: "h-4 w-4" })
3665
+ children: showPassword ? /* @__PURE__ */ jsx50(EyeOffIcon, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx50(EyeIcon, { className: "h-4 w-4" })
3478
3666
  }
3479
3667
  )
3480
3668
  ] })
3481
3669
  ] }),
3482
- /* @__PURE__ */ jsxs31(
3670
+ /* @__PURE__ */ jsxs33(
3483
3671
  "button",
3484
3672
  {
3485
3673
  type: "submit",
3486
3674
  disabled: submitting,
3487
3675
  className: "w-full rounded-lg bg-primary px-4 py-2.5 text-sm font-semibold text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center justify-center gap-2",
3488
3676
  children: [
3489
- submitting && /* @__PURE__ */ jsx48(SpinnerIcon, { className: "h-4 w-4 animate-spin" }),
3677
+ submitting && /* @__PURE__ */ jsx50(SpinnerIcon, { className: "h-4 w-4 animate-spin" }),
3490
3678
  submitting ? "Logging in..." : "Log in"
3491
3679
  ]
3492
3680
  }
@@ -3500,7 +3688,7 @@ function LoginForm({
3500
3688
  }
3501
3689
 
3502
3690
  // src/composites/dev-shell.tsx
3503
- import { jsx as jsx49, jsxs as jsxs32 } from "react/jsx-runtime";
3691
+ import { jsx as jsx51, jsxs as jsxs34 } from "react/jsx-runtime";
3504
3692
  var STORAGE_KEY = "camstack_dev_token";
3505
3693
  var DevShellContext = createContext7(null);
3506
3694
  function useDevShell() {
@@ -3515,7 +3703,7 @@ function getStoredToken() {
3515
3703
  return localStorage.getItem(STORAGE_KEY);
3516
3704
  }
3517
3705
  function SunIcon({ className }) {
3518
- return /* @__PURE__ */ jsxs32(
3706
+ return /* @__PURE__ */ jsxs34(
3519
3707
  "svg",
3520
3708
  {
3521
3709
  xmlns: "http://www.w3.org/2000/svg",
@@ -3527,21 +3715,21 @@ function SunIcon({ className }) {
3527
3715
  strokeLinejoin: "round",
3528
3716
  className,
3529
3717
  children: [
3530
- /* @__PURE__ */ jsx49("circle", { cx: "12", cy: "12", r: "4" }),
3531
- /* @__PURE__ */ jsx49("path", { d: "M12 2v2" }),
3532
- /* @__PURE__ */ jsx49("path", { d: "M12 20v2" }),
3533
- /* @__PURE__ */ jsx49("path", { d: "m4.93 4.93 1.41 1.41" }),
3534
- /* @__PURE__ */ jsx49("path", { d: "m17.66 17.66 1.41 1.41" }),
3535
- /* @__PURE__ */ jsx49("path", { d: "M2 12h2" }),
3536
- /* @__PURE__ */ jsx49("path", { d: "M20 12h2" }),
3537
- /* @__PURE__ */ jsx49("path", { d: "m6.34 17.66-1.41 1.41" }),
3538
- /* @__PURE__ */ jsx49("path", { d: "m19.07 4.93-1.41 1.41" })
3718
+ /* @__PURE__ */ jsx51("circle", { cx: "12", cy: "12", r: "4" }),
3719
+ /* @__PURE__ */ jsx51("path", { d: "M12 2v2" }),
3720
+ /* @__PURE__ */ jsx51("path", { d: "M12 20v2" }),
3721
+ /* @__PURE__ */ jsx51("path", { d: "m4.93 4.93 1.41 1.41" }),
3722
+ /* @__PURE__ */ jsx51("path", { d: "m17.66 17.66 1.41 1.41" }),
3723
+ /* @__PURE__ */ jsx51("path", { d: "M2 12h2" }),
3724
+ /* @__PURE__ */ jsx51("path", { d: "M20 12h2" }),
3725
+ /* @__PURE__ */ jsx51("path", { d: "m6.34 17.66-1.41 1.41" }),
3726
+ /* @__PURE__ */ jsx51("path", { d: "m19.07 4.93-1.41 1.41" })
3539
3727
  ]
3540
3728
  }
3541
3729
  );
3542
3730
  }
3543
3731
  function MoonIcon({ className }) {
3544
- return /* @__PURE__ */ jsx49(
3732
+ return /* @__PURE__ */ jsx51(
3545
3733
  "svg",
3546
3734
  {
3547
3735
  xmlns: "http://www.w3.org/2000/svg",
@@ -3552,7 +3740,7 @@ function MoonIcon({ className }) {
3552
3740
  strokeLinecap: "round",
3553
3741
  strokeLinejoin: "round",
3554
3742
  className,
3555
- children: /* @__PURE__ */ jsx49("path", { d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" })
3743
+ children: /* @__PURE__ */ jsx51("path", { d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" })
3556
3744
  }
3557
3745
  );
3558
3746
  }
@@ -3591,15 +3779,15 @@ function DevShellInner({
3591
3779
  () => ({ trpc, token, logout: onLogout }),
3592
3780
  [trpc, token, onLogout]
3593
3781
  );
3594
- return /* @__PURE__ */ jsx49(DevShellContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs32("div", { className: "min-h-screen bg-background text-foreground", children: [
3595
- /* @__PURE__ */ jsxs32("div", { className: "flex items-center justify-between border-b border-border bg-surface px-4 py-2", children: [
3596
- /* @__PURE__ */ jsxs32("div", { className: "flex items-center gap-2", children: [
3597
- /* @__PURE__ */ jsx49("span", { className: "rounded bg-warning/20 px-2 py-0.5 text-xs font-bold text-warning", children: "DEV MODE" }),
3598
- title && /* @__PURE__ */ jsx49("span", { className: "text-sm font-medium text-foreground", children: title }),
3599
- /* @__PURE__ */ jsx49("span", { className: "text-xs text-foreground-subtle", children: serverUrl })
3782
+ return /* @__PURE__ */ jsx51(DevShellContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs34("div", { className: "min-h-screen bg-background text-foreground", children: [
3783
+ /* @__PURE__ */ jsxs34("div", { className: "flex items-center justify-between border-b border-border bg-surface px-4 py-2", children: [
3784
+ /* @__PURE__ */ jsxs34("div", { className: "flex items-center gap-2", children: [
3785
+ /* @__PURE__ */ jsx51("span", { className: "rounded bg-warning/20 px-2 py-0.5 text-xs font-bold text-warning", children: "DEV MODE" }),
3786
+ title && /* @__PURE__ */ jsx51("span", { className: "text-sm font-medium text-foreground", children: title }),
3787
+ /* @__PURE__ */ jsx51("span", { className: "text-xs text-foreground-subtle", children: serverUrl })
3600
3788
  ] }),
3601
- /* @__PURE__ */ jsxs32("div", { className: "flex items-center gap-2", children: [
3602
- /* @__PURE__ */ jsxs32(
3789
+ /* @__PURE__ */ jsxs34("div", { className: "flex items-center gap-2", children: [
3790
+ /* @__PURE__ */ jsxs34(
3603
3791
  "button",
3604
3792
  {
3605
3793
  type: "button",
@@ -3607,12 +3795,12 @@ function DevShellInner({
3607
3795
  className: "flex items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium text-foreground-subtle hover:text-foreground hover:bg-surface-hover transition-colors",
3608
3796
  title: `Theme: ${theme.mode}`,
3609
3797
  children: [
3610
- theme.resolvedMode === "dark" ? /* @__PURE__ */ jsx49(SunIcon, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx49(MoonIcon, { className: "h-3.5 w-3.5" }),
3798
+ theme.resolvedMode === "dark" ? /* @__PURE__ */ jsx51(SunIcon, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx51(MoonIcon, { className: "h-3.5 w-3.5" }),
3611
3799
  theme.mode === "dark" ? "Dark" : theme.mode === "light" ? "Light" : "System"
3612
3800
  ]
3613
3801
  }
3614
3802
  ),
3615
- /* @__PURE__ */ jsx49(
3803
+ /* @__PURE__ */ jsx51(
3616
3804
  "button",
3617
3805
  {
3618
3806
  type: "button",
@@ -3623,7 +3811,7 @@ function DevShellInner({
3623
3811
  )
3624
3812
  ] })
3625
3813
  ] }),
3626
- /* @__PURE__ */ jsx49("div", { className: "p-4", children: children({ trpc, theme }) })
3814
+ /* @__PURE__ */ jsx51("div", { className: "p-4", children: children({ trpc, theme }) })
3627
3815
  ] }) });
3628
3816
  }
3629
3817
  function DevShell({
@@ -3631,7 +3819,7 @@ function DevShell({
3631
3819
  serverUrl = "https://localhost:4443",
3632
3820
  title
3633
3821
  }) {
3634
- const [token, setToken] = useState12(getStoredToken);
3822
+ const [token, setToken] = useState13(getStoredToken);
3635
3823
  const handleLogin = useCallback8(
3636
3824
  async (username, password) => {
3637
3825
  const anonClient = createTRPCClient({
@@ -3654,9 +3842,9 @@ function DevShell({
3654
3842
  setToken(null);
3655
3843
  }, []);
3656
3844
  if (!token) {
3657
- return /* @__PURE__ */ jsx49(ThemeProvider, { children: /* @__PURE__ */ jsx49(LoginForm, { onLogin: handleLogin, serverUrl }) });
3845
+ return /* @__PURE__ */ jsx51(ThemeProvider, { children: /* @__PURE__ */ jsx51(LoginForm, { onLogin: handleLogin, serverUrl }) });
3658
3846
  }
3659
- return /* @__PURE__ */ jsx49(ThemeProvider, { children: /* @__PURE__ */ jsx49(
3847
+ return /* @__PURE__ */ jsx51(ThemeProvider, { children: /* @__PURE__ */ jsx51(
3660
3848
  DevShellInner,
3661
3849
  {
3662
3850
  serverUrl,
@@ -3697,6 +3885,7 @@ function mountAddonPage(PageComponent, options = {}) {
3697
3885
  export {
3698
3886
  AppShell,
3699
3887
  Badge,
3888
+ BottomSheet,
3700
3889
  Button,
3701
3890
  CLASS_COLORS,
3702
3891
  Card,
@@ -3733,6 +3922,7 @@ export {
3733
3922
  KeyValueList,
3734
3923
  Label,
3735
3924
  LoginForm,
3925
+ MobileDrawer,
3736
3926
  PageHeader,
3737
3927
  PipelineBuilder,
3738
3928
  PipelineRuntimeSelector,
@@ -3772,6 +3962,7 @@ export {
3772
3962
  statusIcons,
3773
3963
  themeToCss,
3774
3964
  useDevShell,
3965
+ useIsMobile,
3775
3966
  useThemeMode
3776
3967
  };
3777
3968
  //# sourceMappingURL=index.js.map