@camstack/ui-library 0.1.25 → 0.1.27

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.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -23,11 +33,17 @@ __export(src_exports, {
23
33
  AppShell: () => AppShell,
24
34
  Badge: () => Badge,
25
35
  Button: () => Button,
36
+ CLASS_COLORS: () => CLASS_COLORS,
26
37
  Card: () => Card,
27
38
  Checkbox: () => Checkbox,
28
39
  CodeBlock: () => CodeBlock,
29
40
  ConfirmDialog: () => ConfirmDialog,
41
+ DEFAULT_CLASS_COLORS: () => DEFAULT_CLASS_COLORS,
42
+ DEFAULT_COLOR: () => DEFAULT_COLOR,
30
43
  DataTable: () => DataTable,
44
+ DetectionCanvas: () => DetectionCanvas,
45
+ DetectionResultTree: () => DetectionResultTree,
46
+ DevShell: () => DevShell,
31
47
  DeviceCard: () => DeviceCard,
32
48
  DeviceGrid: () => DeviceGrid,
33
49
  Dialog: () => Dialog,
@@ -46,22 +62,30 @@ __export(src_exports, {
46
62
  FloatingPanel: () => FloatingPanel,
47
63
  FormField: () => FormField,
48
64
  IconButton: () => IconButton,
65
+ ImageSelector: () => ImageSelector,
66
+ InferenceConfigSelector: () => InferenceConfigSelector,
49
67
  Input: () => Input,
50
68
  KeyValueList: () => KeyValueList,
51
69
  Label: () => Label,
70
+ LoginForm: () => LoginForm,
52
71
  PageHeader: () => PageHeader,
72
+ PipelineBuilder: () => PipelineBuilder,
73
+ PipelineRuntimeSelector: () => PipelineRuntimeSelector,
74
+ PipelineStep: () => PipelineStep,
53
75
  Popover: () => Popover,
54
76
  PopoverContent: () => PopoverContent,
55
77
  PopoverTrigger: () => PopoverTrigger,
56
78
  ProviderBadge: () => ProviderBadge,
57
79
  ScrollArea: () => ScrollArea,
58
80
  Select: () => Select,
81
+ SemanticBadge: () => SemanticBadge,
59
82
  Separator: () => Separator,
60
83
  Sidebar: () => Sidebar,
61
84
  SidebarItem: () => SidebarItem,
62
85
  Skeleton: () => Skeleton,
63
86
  StatCard: () => StatCard,
64
87
  StatusBadge: () => StatusBadge,
88
+ StepTimings: () => StepTimings,
65
89
  Switch: () => Switch,
66
90
  Tabs: () => Tabs,
67
91
  TabsContent: () => TabsContent,
@@ -71,14 +95,18 @@ __export(src_exports, {
71
95
  Tooltip: () => Tooltip,
72
96
  TooltipContent: () => TooltipContent,
73
97
  TooltipTrigger: () => TooltipTrigger,
98
+ VersionBadge: () => VersionBadge,
74
99
  cn: () => cn,
75
100
  createTheme: () => createTheme,
76
101
  darkColors: () => darkColors,
77
102
  defaultTheme: () => defaultTheme,
103
+ getClassColor: () => getClassColor,
78
104
  lightColors: () => lightColors,
105
+ mountAddonPage: () => mountAddonPage,
79
106
  providerIcons: () => providerIcons,
80
107
  statusIcons: () => statusIcons,
81
108
  themeToCss: () => themeToCss,
109
+ useDevShell: () => useDevShell,
82
110
  useThemeMode: () => useThemeMode
83
111
  });
84
112
  module.exports = __toCommonJS(src_exports);
@@ -1322,8 +1350,30 @@ function ProviderBadge({
1322
1350
  ] });
1323
1351
  }
1324
1352
 
1325
- // src/composites/form-field.tsx
1353
+ // src/composites/version-badge.tsx
1326
1354
  var import_jsx_runtime22 = require("react/jsx-runtime");
1355
+ var VARIANT_STYLES = {
1356
+ success: "bg-emerald-400 text-emerald-950",
1357
+ warning: "bg-amber-400 text-amber-950",
1358
+ danger: "bg-red-400 text-red-950",
1359
+ info: "bg-blue-400 text-blue-950",
1360
+ neutral: "bg-foreground-subtle/20 text-foreground"
1361
+ };
1362
+ function SemanticBadge({ children, variant = "neutral", mono, className }) {
1363
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: cn(
1364
+ "inline-flex items-center rounded-md px-2 py-0.5 text-[11px] font-bold leading-tight",
1365
+ mono && "font-mono",
1366
+ VARIANT_STYLES[variant],
1367
+ className
1368
+ ), children });
1369
+ }
1370
+ function VersionBadge({ version, preRelease, className }) {
1371
+ const isPreRelease = preRelease ?? /-(alpha|beta|rc|dev|canary|next)/i.test(version);
1372
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(SemanticBadge, { variant: isPreRelease ? "warning" : "success", mono: true, className, children: version });
1373
+ }
1374
+
1375
+ // src/composites/form-field.tsx
1376
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1327
1377
  function FormField({
1328
1378
  label,
1329
1379
  description,
@@ -1334,7 +1384,7 @@ function FormField({
1334
1384
  className
1335
1385
  }) {
1336
1386
  const isHorizontal = orientation === "horizontal";
1337
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
1387
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
1338
1388
  "div",
1339
1389
  {
1340
1390
  className: cn(
@@ -1343,34 +1393,34 @@ function FormField({
1343
1393
  className
1344
1394
  ),
1345
1395
  children: [
1346
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: cn(isHorizontal ? "flex-1" : ""), children: [
1347
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Label, { children: [
1396
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: cn(isHorizontal ? "flex-1" : ""), children: [
1397
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(Label, { children: [
1348
1398
  label,
1349
- required && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-danger ml-0.5", children: "*" })
1399
+ required && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-danger ml-0.5", children: "*" })
1350
1400
  ] }),
1351
- description && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-foreground-subtle text-xs mt-0.5", children: description })
1401
+ description && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-foreground-subtle text-xs mt-0.5", children: description })
1352
1402
  ] }),
1353
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn(isHorizontal ? "shrink-0" : ""), children }),
1354
- error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-danger text-xs", children: error })
1403
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: cn(isHorizontal ? "shrink-0" : ""), children }),
1404
+ error && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-danger text-xs", children: error })
1355
1405
  ]
1356
1406
  }
1357
1407
  );
1358
1408
  }
1359
1409
 
1360
1410
  // src/composites/page-header.tsx
1361
- var import_jsx_runtime23 = require("react/jsx-runtime");
1411
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1362
1412
  function PageHeader({ title, subtitle, actions, className }) {
1363
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: cn("flex items-center justify-between mb-3", className), children: [
1364
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { children: [
1365
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h1", { className: "text-sm font-semibold text-foreground", children: title }),
1366
- subtitle && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-foreground-subtle text-xs", children: subtitle })
1413
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: cn("flex items-center justify-between mb-3", className), children: [
1414
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { children: [
1415
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h1", { className: "text-sm font-semibold text-foreground", children: title }),
1416
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-foreground-subtle text-xs", children: subtitle })
1367
1417
  ] }),
1368
- actions && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex items-center gap-2", children: actions })
1418
+ actions && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex items-center gap-2", children: actions })
1369
1419
  ] });
1370
1420
  }
1371
1421
 
1372
1422
  // src/composites/empty-state.tsx
1373
- var import_jsx_runtime24 = require("react/jsx-runtime");
1423
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1374
1424
  function EmptyState({
1375
1425
  icon: Icon,
1376
1426
  title,
@@ -1378,18 +1428,18 @@ function EmptyState({
1378
1428
  action,
1379
1429
  className
1380
1430
  }) {
1381
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: cn("flex flex-col items-center justify-center gap-3 py-12", className), children: [
1382
- Icon && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Icon, { className: "h-12 w-12 text-foreground-subtle", "aria-hidden": "true" }),
1383
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col items-center gap-1 text-center", children: [
1384
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-foreground-muted text-sm font-medium", children: title }),
1385
- description && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-foreground-subtle text-xs max-w-xs", children: description })
1431
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: cn("flex flex-col items-center justify-center gap-3 py-12", className), children: [
1432
+ Icon && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Icon, { className: "h-12 w-12 text-foreground-subtle", "aria-hidden": "true" }),
1433
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col items-center gap-1 text-center", children: [
1434
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "text-foreground-muted text-sm font-medium", children: title }),
1435
+ description && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "text-foreground-subtle text-xs max-w-xs", children: description })
1386
1436
  ] }),
1387
- action && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "mt-1", children: action })
1437
+ action && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "mt-1", children: action })
1388
1438
  ] });
1389
1439
  }
1390
1440
 
1391
1441
  // src/composites/confirm-dialog.tsx
1392
- var import_jsx_runtime25 = require("react/jsx-runtime");
1442
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1393
1443
  function ConfirmDialog({
1394
1444
  title,
1395
1445
  message,
@@ -1401,14 +1451,14 @@ function ConfirmDialog({
1401
1451
  open,
1402
1452
  onOpenChange
1403
1453
  }) {
1404
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Dialog, { open, onOpenChange, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DialogContent, { children: [
1405
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DialogHeader, { children: [
1406
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(DialogTitle, { children: title }),
1407
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(DialogDescription, { children: message })
1454
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Dialog, { open, onOpenChange, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DialogContent, { children: [
1455
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DialogHeader, { children: [
1456
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(DialogTitle, { children: title }),
1457
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(DialogDescription, { children: message })
1408
1458
  ] }),
1409
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DialogFooter, { children: [
1410
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Button, { variant: "ghost", onClick: onCancel, children: cancelLabel }),
1411
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1459
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DialogFooter, { children: [
1460
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Button, { variant: "ghost", onClick: onCancel, children: cancelLabel }),
1461
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1412
1462
  Button,
1413
1463
  {
1414
1464
  variant: variant === "danger" ? "danger" : "primary",
@@ -1422,12 +1472,12 @@ function ConfirmDialog({
1422
1472
 
1423
1473
  // src/composites/stat-card.tsx
1424
1474
  var import_lucide_react5 = require("lucide-react");
1425
- var import_jsx_runtime26 = require("react/jsx-runtime");
1475
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1426
1476
  function StatCard({ value, label, trend, className }) {
1427
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Card, { className: cn("flex flex-col gap-1", className), children: [
1428
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-baseline gap-2", children: [
1429
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-2xl font-semibold text-foreground", children: value }),
1430
- trend && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1477
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(Card, { className: cn("flex flex-col gap-1", className), children: [
1478
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-baseline gap-2", children: [
1479
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "text-2xl font-semibold text-foreground", children: value }),
1480
+ trend && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1431
1481
  "span",
1432
1482
  {
1433
1483
  className: cn(
@@ -1435,27 +1485,27 @@ function StatCard({ value, label, trend, className }) {
1435
1485
  trend.direction === "up" ? "text-success" : "text-danger"
1436
1486
  ),
1437
1487
  children: [
1438
- trend.direction === "up" ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react5.TrendingUp, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react5.TrendingDown, { className: "h-3 w-3" }),
1488
+ trend.direction === "up" ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react5.TrendingUp, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react5.TrendingDown, { className: "h-3 w-3" }),
1439
1489
  trend.value,
1440
1490
  "%"
1441
1491
  ]
1442
1492
  }
1443
1493
  )
1444
1494
  ] }),
1445
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-xs text-foreground-muted", children: label })
1495
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "text-xs text-foreground-muted", children: label })
1446
1496
  ] });
1447
1497
  }
1448
1498
 
1449
1499
  // src/composites/key-value-list.tsx
1450
- var import_jsx_runtime27 = require("react/jsx-runtime");
1500
+ var import_jsx_runtime28 = require("react/jsx-runtime");
1451
1501
  function KeyValueList({ items, className }) {
1452
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("dl", { className: cn("flex flex-col", className), children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1502
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("dl", { className: cn("flex flex-col", className), children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1453
1503
  "div",
1454
1504
  {
1455
1505
  className: "flex items-center h-7",
1456
1506
  children: [
1457
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("dt", { className: "text-foreground-subtle text-xs w-1/3 shrink-0", children: item.key }),
1458
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("dd", { className: "text-foreground text-xs", children: item.value })
1507
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("dt", { className: "text-foreground-subtle text-xs w-1/3 shrink-0", children: item.key }),
1508
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("dd", { className: "text-foreground text-xs", children: item.value })
1459
1509
  ]
1460
1510
  },
1461
1511
  item.key
@@ -1465,7 +1515,7 @@ function KeyValueList({ items, className }) {
1465
1515
  // src/composites/code-block.tsx
1466
1516
  var import_react21 = require("react");
1467
1517
  var import_lucide_react6 = require("lucide-react");
1468
- var import_jsx_runtime28 = require("react/jsx-runtime");
1518
+ var import_jsx_runtime29 = require("react/jsx-runtime");
1469
1519
  function CodeBlock({ children, maxHeight = 300, className }) {
1470
1520
  const [copied, setCopied] = (0, import_react21.useState)(false);
1471
1521
  const handleCopy = (0, import_react21.useCallback)(() => {
@@ -1474,9 +1524,9 @@ function CodeBlock({ children, maxHeight = 300, className }) {
1474
1524
  setTimeout(() => setCopied(false), 2e3);
1475
1525
  });
1476
1526
  }, [children]);
1477
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: cn("relative group", className), children: [
1478
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ScrollArea, { style: { maxHeight }, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("pre", { className: "font-mono text-xs bg-surface p-3 rounded-md border border-border-subtle", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("code", { children }) }) }),
1479
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1527
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: cn("relative group", className), children: [
1528
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ScrollArea, { style: { maxHeight }, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("pre", { className: "font-mono text-xs bg-surface p-3 rounded-md border border-border-subtle", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("code", { children }) }) }),
1529
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1480
1530
  IconButton,
1481
1531
  {
1482
1532
  icon: copied ? import_lucide_react6.Check : import_lucide_react6.Copy,
@@ -1491,27 +1541,27 @@ function CodeBlock({ children, maxHeight = 300, className }) {
1491
1541
 
1492
1542
  // src/composites/filter-bar.tsx
1493
1543
  var import_lucide_react7 = require("lucide-react");
1494
- var import_jsx_runtime29 = require("react/jsx-runtime");
1544
+ var import_jsx_runtime30 = require("react/jsx-runtime");
1495
1545
  function FilterBar({ filters, values, onChange, className }) {
1496
1546
  const handleChange = (key, value) => {
1497
1547
  onChange({ ...values, [key]: value });
1498
1548
  };
1499
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: cn("flex items-center gap-2 flex-wrap", className), children: filters.map((filter) => {
1549
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: cn("flex items-center gap-2 flex-wrap", className), children: filters.map((filter) => {
1500
1550
  switch (filter.type) {
1501
1551
  case "search":
1502
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1552
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1503
1553
  Input,
1504
1554
  {
1505
1555
  placeholder: filter.placeholder ?? "Search...",
1506
1556
  value: values[filter.key] ?? "",
1507
1557
  onChange: (e) => handleChange(filter.key, e.target.value),
1508
- leftSlot: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react7.Search, { className: "h-3 w-3 text-foreground-subtle" }),
1558
+ leftSlot: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react7.Search, { className: "h-3 w-3 text-foreground-subtle" }),
1509
1559
  className: "w-48"
1510
1560
  },
1511
1561
  filter.key
1512
1562
  );
1513
1563
  case "select":
1514
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1564
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1515
1565
  Select,
1516
1566
  {
1517
1567
  options: filter.options,
@@ -1522,10 +1572,10 @@ function FilterBar({ filters, values, onChange, className }) {
1522
1572
  filter.key
1523
1573
  );
1524
1574
  case "badge-toggle":
1525
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "flex items-center gap-1", children: filter.options.map((option) => {
1575
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "flex items-center gap-1", children: filter.options.map((option) => {
1526
1576
  const currentValue = values[filter.key];
1527
1577
  const isActive = currentValue === option.value;
1528
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1578
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1529
1579
  "button",
1530
1580
  {
1531
1581
  type: "button",
@@ -1533,7 +1583,7 @@ function FilterBar({ filters, values, onChange, className }) {
1533
1583
  filter.key,
1534
1584
  isActive ? void 0 : option.value
1535
1585
  ),
1536
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1586
+ children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1537
1587
  Badge,
1538
1588
  {
1539
1589
  variant: isActive ? "info" : "default",
@@ -1552,7 +1602,7 @@ function FilterBar({ filters, values, onChange, className }) {
1552
1602
  }
1553
1603
 
1554
1604
  // src/composites/app-shell/sidebar-item.tsx
1555
- var import_jsx_runtime30 = require("react/jsx-runtime");
1605
+ var import_jsx_runtime31 = require("react/jsx-runtime");
1556
1606
  function SidebarItem({
1557
1607
  label,
1558
1608
  icon: Icon,
@@ -1561,7 +1611,7 @@ function SidebarItem({
1561
1611
  active = false,
1562
1612
  className
1563
1613
  }) {
1564
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
1614
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
1565
1615
  "a",
1566
1616
  {
1567
1617
  href,
@@ -1571,18 +1621,18 @@ function SidebarItem({
1571
1621
  className
1572
1622
  ),
1573
1623
  children: [
1574
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0" }),
1575
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "truncate flex-1", children: label }),
1576
- badge !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Badge, { className: "ml-auto text-[10px] px-1.5 py-0", children: badge })
1624
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0" }),
1625
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "truncate flex-1", children: label }),
1626
+ badge !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Badge, { className: "ml-auto text-[10px] px-1.5 py-0", children: badge })
1577
1627
  ]
1578
1628
  }
1579
1629
  );
1580
1630
  }
1581
1631
 
1582
1632
  // src/composites/app-shell/sidebar.tsx
1583
- var import_jsx_runtime31 = require("react/jsx-runtime");
1633
+ var import_jsx_runtime32 = require("react/jsx-runtime");
1584
1634
  function Sidebar({ logo, sections, footer, className }) {
1585
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
1635
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
1586
1636
  "nav",
1587
1637
  {
1588
1638
  className: cn(
@@ -1590,14 +1640,14 @@ function Sidebar({ logo, sections, footer, className }) {
1590
1640
  className
1591
1641
  ),
1592
1642
  children: [
1593
- logo && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "px-3 py-2 shrink-0", children: logo }),
1594
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "flex-1 overflow-auto px-1 py-1", children: sections.map((section, sectionIndex) => /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: cn(sectionIndex > 0 ? "mt-3" : ""), children: [
1595
- section.label && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "text-[10px] text-foreground-disabled uppercase tracking-wider px-2 mb-1 block", children: section.label }),
1596
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "flex flex-col gap-0.5", children: section.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(SidebarItem, { ...item }, item.href)) })
1643
+ logo && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "px-3 py-2 shrink-0", children: logo }),
1644
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "flex-1 overflow-auto px-1 py-1", children: sections.map((section, sectionIndex) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: cn(sectionIndex > 0 ? "mt-3" : ""), children: [
1645
+ section.label && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-[10px] text-foreground-disabled uppercase tracking-wider px-2 mb-1 block", children: section.label }),
1646
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "flex flex-col gap-0.5", children: section.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(SidebarItem, { ...item }, item.href)) })
1597
1647
  ] }, sectionIndex)) }),
1598
- footer && footer.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "shrink-0 px-1 pb-1", children: [
1599
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Separator, { className: "mb-1" }),
1600
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "flex flex-col gap-0.5", children: footer.map((item) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(SidebarItem, { ...item }, item.href)) })
1648
+ footer && footer.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "shrink-0 px-1 pb-1", children: [
1649
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Separator, { className: "mb-1" }),
1650
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "flex flex-col gap-0.5", children: footer.map((item) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(SidebarItem, { ...item }, item.href)) })
1601
1651
  ] })
1602
1652
  ]
1603
1653
  }
@@ -1606,29 +1656,29 @@ function Sidebar({ logo, sections, footer, className }) {
1606
1656
 
1607
1657
  // src/composites/app-shell/app-shell.tsx
1608
1658
  var import_lucide_react8 = require("lucide-react");
1609
- var import_jsx_runtime32 = require("react/jsx-runtime");
1659
+ var import_jsx_runtime33 = require("react/jsx-runtime");
1610
1660
  function AppShell({ sidebar, header, children, className }) {
1611
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: cn("flex h-screen", className), children: [
1612
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Sidebar, { ...sidebar }),
1613
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-1 flex-col min-w-0", children: [
1614
- header && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("header", { className: "flex items-center h-10 border-b border-border px-4 shrink-0", children: [
1615
- header.breadcrumbs && header.breadcrumbs.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("nav", { className: "flex items-center gap-1 text-xs flex-1 min-w-0", children: header.breadcrumbs.map((crumb, index) => {
1661
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: cn("flex h-screen", className), children: [
1662
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Sidebar, { ...sidebar }),
1663
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-1 flex-col min-w-0", children: [
1664
+ header && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("header", { className: "flex items-center h-10 border-b border-border px-4 shrink-0", children: [
1665
+ header.breadcrumbs && header.breadcrumbs.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("nav", { className: "flex items-center gap-1 text-xs flex-1 min-w-0", children: header.breadcrumbs.map((crumb, index) => {
1616
1666
  const isLast = index === header.breadcrumbs.length - 1;
1617
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: "flex items-center gap-1", children: [
1618
- index > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react8.ChevronRight, { className: "h-3 w-3 text-foreground-subtle shrink-0" }),
1619
- crumb.href && !isLast ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1667
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: "flex items-center gap-1", children: [
1668
+ index > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react8.ChevronRight, { className: "h-3 w-3 text-foreground-subtle shrink-0" }),
1669
+ crumb.href && !isLast ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1620
1670
  "a",
1621
1671
  {
1622
1672
  href: crumb.href,
1623
1673
  className: "text-foreground-subtle hover:text-foreground transition-colors truncate",
1624
1674
  children: crumb.label
1625
1675
  }
1626
- ) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-foreground truncate", children: crumb.label })
1676
+ ) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-foreground truncate", children: crumb.label })
1627
1677
  ] }, index);
1628
1678
  }) }),
1629
- header.actions && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "flex items-center gap-2 ml-auto shrink-0", children: header.actions })
1679
+ header.actions && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex items-center gap-2 ml-auto shrink-0", children: header.actions })
1630
1680
  ] }),
1631
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("main", { className: "flex-1 overflow-auto p-4", children })
1681
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("main", { className: "flex-1 overflow-auto p-4", children })
1632
1682
  ] })
1633
1683
  ] });
1634
1684
  }
@@ -1639,20 +1689,20 @@ var import_react_table = require("@tanstack/react-table");
1639
1689
 
1640
1690
  // src/composites/data-table/data-table-header.tsx
1641
1691
  var import_lucide_react9 = require("lucide-react");
1642
- var import_jsx_runtime33 = require("react/jsx-runtime");
1692
+ var import_jsx_runtime34 = require("react/jsx-runtime");
1643
1693
  function DataTableHeader({
1644
1694
  headerGroups,
1645
1695
  onSortingChange,
1646
1696
  stickyHeader,
1647
1697
  flexRender: render
1648
1698
  }) {
1649
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1699
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1650
1700
  "thead",
1651
1701
  {
1652
1702
  className: cn(
1653
1703
  stickyHeader && "sticky top-0 z-10 bg-background"
1654
1704
  ),
1655
- children: headerGroups.map((headerGroup) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("tr", { className: "h-6", children: headerGroup.headers.map((header) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1705
+ children: headerGroups.map((headerGroup) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("tr", { className: "h-6", children: headerGroup.headers.map((header) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1656
1706
  HeaderCell,
1657
1707
  {
1658
1708
  header,
@@ -1667,7 +1717,7 @@ function DataTableHeader({
1667
1717
  function HeaderCell({ header, sortable, flexRender: render }) {
1668
1718
  const sorted = header.column.getIsSorted();
1669
1719
  const SortIcon = sorted === "asc" ? import_lucide_react9.ArrowUp : sorted === "desc" ? import_lucide_react9.ArrowDown : import_lucide_react9.ArrowUpDown;
1670
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1720
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1671
1721
  "th",
1672
1722
  {
1673
1723
  className: cn(
@@ -1675,9 +1725,9 @@ function HeaderCell({ header, sortable, flexRender: render }) {
1675
1725
  sortable && "cursor-pointer select-none"
1676
1726
  ),
1677
1727
  onClick: sortable ? header.column.getToggleSortingHandler() : void 0,
1678
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
1728
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
1679
1729
  header.isPlaceholder ? null : render(header.column.columnDef.header, header.getContext()),
1680
- sortable && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(SortIcon, { className: "h-3 w-3" })
1730
+ sortable && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(SortIcon, { className: "h-3 w-3" })
1681
1731
  ] })
1682
1732
  }
1683
1733
  );
@@ -1685,7 +1735,7 @@ function HeaderCell({ header, sortable, flexRender: render }) {
1685
1735
 
1686
1736
  // src/composites/data-table/data-table-row.tsx
1687
1737
  var import_lucide_react10 = require("lucide-react");
1688
- var import_jsx_runtime34 = require("react/jsx-runtime");
1738
+ var import_jsx_runtime35 = require("react/jsx-runtime");
1689
1739
  function DataTableRow({
1690
1740
  row,
1691
1741
  onRowClick,
@@ -1693,7 +1743,7 @@ function DataTableRow({
1693
1743
  flexRender: render
1694
1744
  }) {
1695
1745
  const actions = rowActions ? rowActions(row.original) : [];
1696
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
1746
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
1697
1747
  "tr",
1698
1748
  {
1699
1749
  className: cn(
@@ -1703,17 +1753,17 @@ function DataTableRow({
1703
1753
  ),
1704
1754
  onClick: onRowClick ? () => onRowClick(row.original) : void 0,
1705
1755
  children: [
1706
- row.getVisibleCells().map((cell) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(DataTableCell, { cell, flexRender: render }, cell.id)),
1707
- actions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("td", { className: "px-2 py-1.5 w-8", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(Dropdown, { children: [
1708
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1756
+ row.getVisibleCells().map((cell) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(DataTableCell, { cell, flexRender: render }, cell.id)),
1757
+ actions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("td", { className: "px-2 py-1.5 w-8", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(Dropdown, { children: [
1758
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
1709
1759
  DropdownTrigger,
1710
1760
  {
1711
1761
  className: "p-0.5 rounded hover:bg-surface-hover",
1712
1762
  onClick: (e) => e.stopPropagation(),
1713
- children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react10.MoreHorizontal, { className: "h-3.5 w-3.5 text-foreground-muted" })
1763
+ children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react10.MoreHorizontal, { className: "h-3.5 w-3.5 text-foreground-muted" })
1714
1764
  }
1715
1765
  ),
1716
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(DropdownContent, { className: "right-0 left-auto", children: actions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1766
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(DropdownContent, { className: "right-0 left-auto", children: actions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
1717
1767
  DropdownItem,
1718
1768
  {
1719
1769
  icon: action.icon,
@@ -1732,12 +1782,12 @@ function DataTableRow({
1732
1782
  );
1733
1783
  }
1734
1784
  function DataTableCell({ cell, flexRender: render }) {
1735
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("td", { className: "px-2 py-1.5 text-xs text-foreground", children: render(cell.column.columnDef.cell, cell.getContext()) });
1785
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("td", { className: "px-2 py-1.5 text-xs text-foreground", children: render(cell.column.columnDef.cell, cell.getContext()) });
1736
1786
  }
1737
1787
 
1738
1788
  // src/composites/data-table/data-table-pagination.tsx
1739
1789
  var import_lucide_react11 = require("lucide-react");
1740
- var import_jsx_runtime35 = require("react/jsx-runtime");
1790
+ var import_jsx_runtime36 = require("react/jsx-runtime");
1741
1791
  var PAGE_SIZE_OPTIONS = [
1742
1792
  { value: "10", label: "10" },
1743
1793
  { value: "25", label: "25" },
@@ -1752,10 +1802,10 @@ function DataTablePagination({
1752
1802
  }) {
1753
1803
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
1754
1804
  const currentPage = page + 1;
1755
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex items-center justify-between px-2 py-2 text-xs text-foreground-muted", children: [
1756
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex items-center gap-2", children: [
1757
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { children: "Rows per page" }),
1758
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "w-16", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
1805
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex items-center justify-between px-2 py-2 text-xs text-foreground-muted", children: [
1806
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex items-center gap-2", children: [
1807
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { children: "Rows per page" }),
1808
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "w-16", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1759
1809
  Select,
1760
1810
  {
1761
1811
  options: PAGE_SIZE_OPTIONS,
@@ -1767,14 +1817,14 @@ function DataTablePagination({
1767
1817
  }
1768
1818
  ) })
1769
1819
  ] }),
1770
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex items-center gap-2", children: [
1771
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { children: [
1820
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex items-center gap-2", children: [
1821
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { children: [
1772
1822
  "Page ",
1773
1823
  currentPage,
1774
1824
  " of ",
1775
1825
  totalPages
1776
1826
  ] }),
1777
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
1827
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1778
1828
  IconButton,
1779
1829
  {
1780
1830
  icon: import_lucide_react11.ChevronLeft,
@@ -1785,7 +1835,7 @@ function DataTablePagination({
1785
1835
  onClick: () => onPaginationChange?.({ pageIndex: page - 1, pageSize })
1786
1836
  }
1787
1837
  ),
1788
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
1838
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1789
1839
  IconButton,
1790
1840
  {
1791
1841
  icon: import_lucide_react11.ChevronRight,
@@ -1801,7 +1851,7 @@ function DataTablePagination({
1801
1851
  }
1802
1852
 
1803
1853
  // src/composites/data-table/data-table.tsx
1804
- var import_jsx_runtime36 = require("react/jsx-runtime");
1854
+ var import_jsx_runtime37 = require("react/jsx-runtime");
1805
1855
  function DataTable({
1806
1856
  data,
1807
1857
  columns: userColumns,
@@ -1824,7 +1874,7 @@ function DataTable({
1824
1874
  if (!selectable) return userColumns;
1825
1875
  const selectColumn = {
1826
1876
  id: "__select",
1827
- header: ({ table: table2 }) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1877
+ header: ({ table: table2 }) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1828
1878
  Checkbox,
1829
1879
  {
1830
1880
  checked: table2.getIsAllPageRowsSelected(),
@@ -1832,7 +1882,7 @@ function DataTable({
1832
1882
  "aria-label": "Select all"
1833
1883
  }
1834
1884
  ),
1835
- cell: ({ row }) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1885
+ cell: ({ row }) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1836
1886
  Checkbox,
1837
1887
  {
1838
1888
  checked: row.getIsSelected(),
@@ -1870,9 +1920,9 @@ function DataTable({
1870
1920
  pageCount: pagination ? Math.ceil(pagination.total / pagination.pageSize) : void 0
1871
1921
  });
1872
1922
  const hasActions = !!rowActions;
1873
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: cn("overflow-auto", className), children: [
1874
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("table", { className: "w-full border-collapse", children: [
1875
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1923
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: cn("overflow-auto", className), children: [
1924
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("table", { className: "w-full border-collapse", children: [
1925
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1876
1926
  DataTableHeader,
1877
1927
  {
1878
1928
  headerGroups: table.getHeaderGroups(),
@@ -1881,14 +1931,14 @@ function DataTable({
1881
1931
  flexRender: import_react_table.flexRender
1882
1932
  }
1883
1933
  ),
1884
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("tbody", { children: loading ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(LoadingRows, { colSpan: columns.length + (hasActions ? 1 : 0), compact }) : table.getRowModel().rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1934
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("tbody", { children: loading ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LoadingRows, { colSpan: columns.length + (hasActions ? 1 : 0), compact }) : table.getRowModel().rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1885
1935
  "td",
1886
1936
  {
1887
1937
  colSpan: columns.length + (hasActions ? 1 : 0),
1888
1938
  className: "text-center py-8 text-xs text-foreground-muted",
1889
1939
  children: emptyState ?? "No data"
1890
1940
  }
1891
- ) }) : table.getRowModel().rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1941
+ ) }) : table.getRowModel().rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1892
1942
  DataTableRow,
1893
1943
  {
1894
1944
  row,
@@ -1899,7 +1949,7 @@ function DataTable({
1899
1949
  row.id
1900
1950
  )) })
1901
1951
  ] }),
1902
- pagination && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1952
+ pagination && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1903
1953
  DataTablePagination,
1904
1954
  {
1905
1955
  page: pagination.page,
@@ -1911,11 +1961,11 @@ function DataTable({
1911
1961
  ] });
1912
1962
  }
1913
1963
  function LoadingRows({ colSpan, compact }) {
1914
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_jsx_runtime36.Fragment, { children: Array.from({ length: 5 }).map((_, rowIdx) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("tr", { className: compact ? "h-7" : "h-9", children: Array.from({ length: colSpan }).map((_2, colIdx) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "px-2 py-1.5", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Skeleton, { className: "h-3 w-full" }) }, colIdx)) }, rowIdx)) });
1964
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_jsx_runtime37.Fragment, { children: Array.from({ length: 5 }).map((_, rowIdx) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("tr", { className: compact ? "h-7" : "h-9", children: Array.from({ length: colSpan }).map((_2, colIdx) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("td", { className: "px-2 py-1.5", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Skeleton, { className: "h-3 w-full" }) }, colIdx)) }, rowIdx)) });
1915
1965
  }
1916
1966
 
1917
1967
  // src/composites/device-card.tsx
1918
- var import_jsx_runtime37 = require("react/jsx-runtime");
1968
+ var import_jsx_runtime38 = require("react/jsx-runtime");
1919
1969
  var STATUS_COLORS = {
1920
1970
  online: "bg-success",
1921
1971
  offline: "bg-danger",
@@ -1934,7 +1984,7 @@ function DeviceCard({
1934
1984
  className
1935
1985
  }) {
1936
1986
  const isOffline = status === "offline";
1937
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
1987
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
1938
1988
  "div",
1939
1989
  {
1940
1990
  onClick,
@@ -1946,18 +1996,18 @@ function DeviceCard({
1946
1996
  className
1947
1997
  ),
1948
1998
  children: [
1949
- /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex items-center justify-between mb-2", children: [
1950
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "text-sm font-medium truncate", children: title }),
1951
- status && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: cn("h-2 w-2 rounded-full shrink-0", STATUS_COLORS[status]) })
1999
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex items-center justify-between mb-2", children: [
2000
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "text-sm font-medium truncate", children: title }),
2001
+ status && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: cn("h-2 w-2 rounded-full shrink-0", STATUS_COLORS[status]) })
1952
2002
  ] }),
1953
- subtitle && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "text-[11px] text-foreground-muted", children: subtitle }),
1954
- badges && badges.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "flex flex-wrap gap-1 mt-2", children: badges.map((badge, i) => {
2003
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "text-[11px] text-foreground-muted", children: subtitle }),
2004
+ badges && badges.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "flex flex-wrap gap-1 mt-2", children: badges.map((badge, i) => {
1955
2005
  const cls = cn(
1956
2006
  "rounded px-1.5 py-0.5 text-[10px] flex items-center gap-0.5",
1957
2007
  selected ? "bg-primary/20" : "bg-surface-hover",
1958
2008
  badge.onClick && "hover:opacity-80 transition-opacity cursor-pointer"
1959
2009
  );
1960
- return badge.onClick ? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
2010
+ return badge.onClick ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
1961
2011
  "button",
1962
2012
  {
1963
2013
  onClick: (e) => {
@@ -1971,12 +2021,12 @@ function DeviceCard({
1971
2021
  ]
1972
2022
  },
1973
2023
  i
1974
- ) : /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: cls, children: [
2024
+ ) : /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("span", { className: cls, children: [
1975
2025
  badge.icon,
1976
2026
  badge.label
1977
2027
  ] }, i);
1978
2028
  }) }),
1979
- !isOffline && actions && actions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "flex items-center gap-0.5 mt-2 -mb-1", children: actions.map((action, i) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2029
+ !isOffline && actions && actions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "flex items-center gap-0.5 mt-2 -mb-1", children: actions.map((action, i) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
1980
2030
  "button",
1981
2031
  {
1982
2032
  onClick: (e) => {
@@ -1990,21 +2040,21 @@ function DeviceCard({
1990
2040
  },
1991
2041
  i
1992
2042
  )) }),
1993
- isOffline && offlineAction && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "mt-2", onClick: (e) => e.stopPropagation(), children: offlineAction })
2043
+ isOffline && offlineAction && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "mt-2", onClick: (e) => e.stopPropagation(), children: offlineAction })
1994
2044
  ]
1995
2045
  }
1996
2046
  );
1997
2047
  }
1998
2048
 
1999
2049
  // src/composites/device-grid.tsx
2000
- var import_jsx_runtime38 = require("react/jsx-runtime");
2050
+ var import_jsx_runtime39 = require("react/jsx-runtime");
2001
2051
  function DeviceGrid({
2002
2052
  children,
2003
2053
  minCardWidth = 220,
2004
2054
  gap = 3,
2005
2055
  className
2006
2056
  }) {
2007
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2057
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2008
2058
  "div",
2009
2059
  {
2010
2060
  className: cn(
@@ -2020,16 +2070,1716 @@ function DeviceGrid({
2020
2070
  }
2021
2071
  );
2022
2072
  }
2073
+
2074
+ // src/composites/pipeline-step.tsx
2075
+ var import_react23 = require("react");
2076
+ var import_lucide_react12 = require("lucide-react");
2077
+ var import_jsx_runtime40 = require("react/jsx-runtime");
2078
+ var ADDON_COLORS = {
2079
+ "object-detection": "border-l-blue-500",
2080
+ "motion-detection": "border-l-amber-500",
2081
+ "face-detection": "border-l-purple-500",
2082
+ "face-recognition": "border-l-violet-500",
2083
+ "plate-detection": "border-l-pink-500",
2084
+ "plate-recognition": "border-l-rose-500",
2085
+ "animal-classifier": "border-l-lime-500",
2086
+ "bird-nabirds-classifier": "border-l-emerald-500",
2087
+ "bird-global-classifier": "border-l-teal-500",
2088
+ "audio-classification": "border-l-green-500",
2089
+ "segmentation-refiner": "border-l-cyan-500"
2090
+ };
2091
+ var SLOT_FALLBACK = {
2092
+ detector: "border-l-blue-500",
2093
+ cropper: "border-l-purple-500",
2094
+ classifier: "border-l-lime-500",
2095
+ refiner: "border-l-cyan-500"
2096
+ };
2097
+ function borderColor(addonId, slot) {
2098
+ return ADDON_COLORS[addonId] ?? SLOT_FALLBACK[slot] ?? "border-l-primary";
2099
+ }
2100
+ var BACKEND_FORMAT = {
2101
+ cpu: "onnx",
2102
+ coreml: "coreml",
2103
+ openvino: "openvino",
2104
+ cuda: "onnx",
2105
+ tensorrt: "onnx",
2106
+ "onnx-py": "onnx",
2107
+ pytorch: "pt"
2108
+ };
2109
+ function backendsForRuntime(runtime, caps, schema) {
2110
+ const allBackends = runtime === "node" ? caps.runtimes.node.backends : caps.runtimes.python.backends;
2111
+ if (!schema) return allBackends;
2112
+ const availableFormats = /* @__PURE__ */ new Set();
2113
+ for (const m of schema.models) {
2114
+ for (const fmt of Object.keys(m.formats)) {
2115
+ availableFormats.add(fmt);
2116
+ }
2117
+ }
2118
+ if (runtime === "node") availableFormats.add("onnx");
2119
+ return allBackends.map((b) => {
2120
+ const neededFormat = BACKEND_FORMAT[b.id] ?? "onnx";
2121
+ const hasFormat = availableFormats.has(neededFormat);
2122
+ return { ...b, available: b.available && hasFormat };
2123
+ });
2124
+ }
2125
+ function modelsForStep(schema, runtime, backend, caps) {
2126
+ if (!schema) return [];
2127
+ const fmt = runtime === "node" ? "onnx" : caps.runtimes.python.backends.find((b) => b.id === backend)?.modelFormat ?? "onnx";
2128
+ return schema.models.filter((m) => m.formats[fmt]).map((m) => ({
2129
+ id: m.id,
2130
+ name: m.name,
2131
+ downloaded: m.formats[fmt]?.downloaded ?? false
2132
+ }));
2133
+ }
2134
+ function runtimeOptions(caps) {
2135
+ const opts = [
2136
+ { id: "node", label: "Node.js (ONNX)", available: true }
2137
+ ];
2138
+ if (caps.runtimes.python.available && caps.runtimes.python.backends.some((b) => b.available)) {
2139
+ opts.unshift({ id: "python", label: "Python", available: true });
2140
+ }
2141
+ return opts;
2142
+ }
2143
+ function PipelineStep({
2144
+ step,
2145
+ schema,
2146
+ allSchemas,
2147
+ capabilities,
2148
+ depth = 0,
2149
+ onChange,
2150
+ onDelete,
2151
+ readOnly = false
2152
+ }) {
2153
+ const [expanded, setExpanded] = (0, import_react23.useState)(false);
2154
+ const color = borderColor(step.addonId, step.slot);
2155
+ const backends = backendsForRuntime(step.runtime, capabilities, schema);
2156
+ const rtOptions = runtimeOptions(capabilities);
2157
+ const currentBackendAvailable = backends.find((b) => b.id === step.backend)?.available ?? false;
2158
+ if (!currentBackendAvailable && !readOnly) {
2159
+ const firstAvailable = backends.find((b) => b.available);
2160
+ if (firstAvailable && firstAvailable.id !== step.backend) {
2161
+ queueMicrotask(() => onChange({ ...step, backend: firstAvailable.id }));
2162
+ }
2163
+ }
2164
+ const models = modelsForStep(schema, step.runtime, step.backend, capabilities);
2165
+ if (models.length > 0 && !models.some((m) => m.id === step.modelId) && !readOnly) {
2166
+ const defaultModel = schema?.defaultModelId ? models.find((m) => m.id === schema.defaultModelId) : null;
2167
+ const fallback = defaultModel ?? models[0];
2168
+ if (fallback && fallback.id !== step.modelId) {
2169
+ queueMicrotask(() => onChange({ ...step, modelId: fallback.id }));
2170
+ }
2171
+ }
2172
+ function handleClick(e) {
2173
+ if (e.target.closest(".step-config")) return;
2174
+ setExpanded((v) => !v);
2175
+ }
2176
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "space-y-2", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
2177
+ "div",
2178
+ {
2179
+ className: cn(
2180
+ "rounded-xl border border-border bg-surface overflow-hidden border-l-4 shadow-sm",
2181
+ color,
2182
+ !step.enabled && "opacity-[0.45]"
2183
+ ),
2184
+ children: [
2185
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex items-center gap-2.5 px-3 py-2.5 cursor-pointer select-none", onClick: handleClick, children: [
2186
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "text-foreground-subtle", children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react12.ChevronDown, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react12.ChevronRight, { className: "h-4 w-4" }) }),
2187
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex-1 min-w-0", children: [
2188
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "text-[10px] uppercase tracking-wider font-medium text-foreground-subtle/60 block leading-none", children: step.slot }),
2189
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "text-sm font-semibold text-foreground truncate block leading-tight", children: step.addonName }),
2190
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex items-center gap-1 mt-0.5 flex-wrap", children: [
2191
+ step.inputClasses.map((c) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("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)),
2192
+ step.inputClasses.length > 0 && step.outputClasses.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "text-foreground-subtle/40 text-[10px]", children: "\u2192" }),
2193
+ step.outputClasses.map((c) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("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))
2194
+ ] })
2195
+ ] }),
2196
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2197
+ "button",
2198
+ {
2199
+ onClick: (e) => {
2200
+ e.stopPropagation();
2201
+ onChange({ ...step, enabled: !step.enabled });
2202
+ },
2203
+ className: cn(
2204
+ "relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors",
2205
+ step.enabled ? "bg-success" : "bg-foreground-subtle/30"
2206
+ ),
2207
+ children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: cn(
2208
+ "inline-block h-4 w-4 rounded-full bg-white shadow transition-transform",
2209
+ step.enabled ? "translate-x-6" : "translate-x-1"
2210
+ ) })
2211
+ }
2212
+ )
2213
+ ] }),
2214
+ expanded && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "step-config border-t border-border bg-background px-4 py-4 space-y-3", children: [
2215
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "grid grid-cols-2 gap-3", children: [
2216
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2217
+ ConfigSelect,
2218
+ {
2219
+ label: "Agent",
2220
+ value: step.agentId,
2221
+ disabled: readOnly,
2222
+ options: [{ value: step.agentId || "hub", label: step.agentId || "Hub (local)" }],
2223
+ onChange: (v) => onChange({ ...step, agentId: v })
2224
+ }
2225
+ ),
2226
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2227
+ ConfigSelect,
2228
+ {
2229
+ label: "Runtime",
2230
+ value: step.runtime,
2231
+ disabled: readOnly,
2232
+ options: rtOptions.map((r) => ({ value: r.id, label: r.label, disabled: !r.available })),
2233
+ onChange: (v) => onChange({ ...step, runtime: v })
2234
+ }
2235
+ ),
2236
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2237
+ ConfigSelect,
2238
+ {
2239
+ label: "Backend",
2240
+ value: step.backend,
2241
+ disabled: readOnly,
2242
+ options: backends.map((b) => ({ value: b.id, label: b.label, disabled: !b.available })),
2243
+ onChange: (v) => onChange({ ...step, backend: v })
2244
+ }
2245
+ ),
2246
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2247
+ ConfigSelect,
2248
+ {
2249
+ label: "Model",
2250
+ value: step.modelId,
2251
+ disabled: readOnly,
2252
+ options: models.map((m) => ({ value: m.id, label: `${m.name}${m.downloaded ? " \u2713" : ""}` })),
2253
+ onChange: (v) => onChange({ ...step, modelId: v })
2254
+ }
2255
+ )
2256
+ ] }),
2257
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { children: [
2258
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex items-center justify-between mb-1", children: [
2259
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "text-[10px] font-medium text-foreground-subtle uppercase tracking-wide", children: "Confidence" }),
2260
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "text-xs font-medium text-foreground tabular-nums", children: [
2261
+ (step.confidence * 100).toFixed(0),
2262
+ "%"
2263
+ ] })
2264
+ ] }),
2265
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2266
+ "input",
2267
+ {
2268
+ type: "range",
2269
+ min: 0,
2270
+ max: 1,
2271
+ step: 0.01,
2272
+ value: step.confidence,
2273
+ disabled: readOnly,
2274
+ onChange: (e) => onChange({ ...step, confidence: Number(e.target.value) }),
2275
+ className: "w-full accent-primary h-1.5"
2276
+ }
2277
+ )
2278
+ ] })
2279
+ ] })
2280
+ ]
2281
+ }
2282
+ ) });
2283
+ }
2284
+ function ConfigSelect({ label, value, options, disabled, onChange }) {
2285
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { children: [
2286
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("label", { className: "block text-[10px] font-medium text-foreground-subtle uppercase tracking-wide mb-1.5", children: label }),
2287
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
2288
+ "select",
2289
+ {
2290
+ value,
2291
+ onChange: (e) => onChange(e.target.value),
2292
+ disabled,
2293
+ 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",
2294
+ children: [
2295
+ options.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("option", { value, children: value || "default" }),
2296
+ options.map((o) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("option", { value: o.value, disabled: o.disabled, children: o.label }, o.value))
2297
+ ]
2298
+ }
2299
+ )
2300
+ ] });
2301
+ }
2302
+
2303
+ // src/composites/pipeline-runtime-selector.tsx
2304
+ var import_lucide_react13 = require("lucide-react");
2305
+ var import_jsx_runtime41 = require("react/jsx-runtime");
2306
+ function PipelineRuntimeSelector({ options, value, onChange }) {
2307
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "flex flex-wrap gap-2", children: options.map((opt) => {
2308
+ const active = opt.id === value;
2309
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
2310
+ "button",
2311
+ {
2312
+ onClick: () => opt.available && onChange(opt.id),
2313
+ disabled: !opt.available,
2314
+ 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"}`,
2315
+ children: [
2316
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react13.Cpu, { className: "h-3.5 w-3.5 shrink-0" }),
2317
+ opt.label,
2318
+ opt.isBest && /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("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: [
2319
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react13.Star, { className: "h-2.5 w-2.5" }),
2320
+ "Best"
2321
+ ] }),
2322
+ opt.platformScore != null && /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "text-[10px] text-foreground-subtle/60", children: [
2323
+ "(",
2324
+ opt.platformScore,
2325
+ ")"
2326
+ ] }),
2327
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2328
+ "span",
2329
+ {
2330
+ className: `h-1.5 w-1.5 rounded-full ${opt.available ? "bg-success" : "bg-danger"}`
2331
+ }
2332
+ )
2333
+ ]
2334
+ },
2335
+ opt.id
2336
+ );
2337
+ }) });
2338
+ }
2339
+
2340
+ // src/composites/pipeline-builder.tsx
2341
+ var import_react24 = require("react");
2342
+ var import_lucide_react14 = require("lucide-react");
2343
+
2344
+ // src/lib/validate-template.ts
2345
+ function validateTemplate(steps, schema) {
2346
+ const availableAddonIds = new Set(
2347
+ schema.slots.flatMap((s) => s.addons.map((a) => a.id))
2348
+ );
2349
+ const warnings = [];
2350
+ function validateStep(step) {
2351
+ if (!availableAddonIds.has(step.addonId)) {
2352
+ warnings.push(`Addon "${step.addonId}" is no longer available \u2014 step removed`);
2353
+ return null;
2354
+ }
2355
+ const addon = schema.slots.flatMap((s) => s.addons).find((a) => a.id === step.addonId);
2356
+ let modelId = step.modelId;
2357
+ if (addon && !addon.models.some((m) => m.id === modelId)) {
2358
+ const fallback = addon.defaultModelId;
2359
+ warnings.push(`Model "${modelId}" not available for ${step.addonId} \u2014 using "${fallback}"`);
2360
+ modelId = fallback;
2361
+ }
2362
+ const validChildren = step.children.map((c) => validateStep(c)).filter((c) => c !== null);
2363
+ return { ...step, modelId, children: validChildren };
2364
+ }
2365
+ const validSteps = steps.map((s) => validateStep(s)).filter((s) => s !== null);
2366
+ return {
2367
+ valid: warnings.length === 0,
2368
+ steps: validSteps,
2369
+ warnings
2370
+ };
2371
+ }
2372
+
2373
+ // src/composites/pipeline-builder.tsx
2374
+ var import_jsx_runtime42 = require("react/jsx-runtime");
2375
+ function buildSchemaMap(schema) {
2376
+ const map = /* @__PURE__ */ new Map();
2377
+ for (const slot of schema.slots) {
2378
+ for (const addon of slot.addons) {
2379
+ map.set(addon.id, addon);
2380
+ }
2381
+ }
2382
+ return map;
2383
+ }
2384
+ function createDefaultStep(addon, fallbackRuntime, fallbackBackend) {
2385
+ return {
2386
+ addonId: addon.id,
2387
+ addonName: addon.name,
2388
+ slot: addon.slot,
2389
+ inputClasses: [...addon.inputClasses],
2390
+ outputClasses: [...addon.outputClasses],
2391
+ enabled: true,
2392
+ agentId: "hub",
2393
+ // Use per-addon defaults from backend PlatformScorer, fallback to provided
2394
+ runtime: addon.defaultRuntime ?? fallbackRuntime ?? "node",
2395
+ backend: addon.defaultBackend ?? fallbackBackend ?? "cpu",
2396
+ modelId: addon.defaultModelId,
2397
+ confidence: addon.defaultConfidence,
2398
+ classFilters: [...addon.inputClasses],
2399
+ children: []
2400
+ };
2401
+ }
2402
+ function PlaceholderStep({ addon, onClick }) {
2403
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2404
+ "button",
2405
+ {
2406
+ type: "button",
2407
+ onClick,
2408
+ 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",
2409
+ children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center gap-3", children: [
2410
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react14.PlusCircle, { className: "h-[18px] w-[18px] text-foreground-subtle/30 group-hover:text-primary/60 shrink-0" }),
2411
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex-1 min-w-0", children: [
2412
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "text-[13px] font-medium text-foreground-subtle/50 group-hover:text-foreground-subtle block truncate", children: addon.name }),
2413
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center gap-1 mt-0.5 flex-wrap", children: [
2414
+ addon.inputClasses.map((c) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("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)),
2415
+ addon.inputClasses.length > 0 && addon.outputClasses.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "text-foreground-subtle/25 text-[10px]", children: "\u2192" }),
2416
+ addon.outputClasses.map((c) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("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))
2417
+ ] })
2418
+ ] })
2419
+ ] })
2420
+ }
2421
+ );
2422
+ }
2423
+ function PipelineBuilder({
2424
+ schema,
2425
+ capabilities,
2426
+ steps,
2427
+ onChange,
2428
+ templates,
2429
+ selectedTemplateId,
2430
+ onSelectTemplate,
2431
+ onSaveTemplate,
2432
+ onUpdateTemplate,
2433
+ onDeleteTemplate,
2434
+ readOnly = false,
2435
+ excludeAddons = []
2436
+ }) {
2437
+ const excluded = (0, import_react24.useMemo)(() => new Set(excludeAddons), [excludeAddons]);
2438
+ const schemaMap = (0, import_react24.useMemo)(() => buildSchemaMap(schema), [schema]);
2439
+ const [warnings, setWarnings] = (0, import_react24.useState)([]);
2440
+ const bestPlatformScore = capabilities.platformScores?.find((s) => s.available);
2441
+ const hasPython = capabilities.runtimes.python.available && capabilities.runtimes.python.backends.some((b) => b.available);
2442
+ const defaultRuntime = bestPlatformScore?.runtime ?? (hasPython ? "python" : "node");
2443
+ const defaultBackend = bestPlatformScore?.backend ?? (hasPython ? capabilities.runtimes.python.backends.find((b) => b.available)?.id ?? "cpu" : capabilities.runtimes.node.backends.find((b) => b.available && b.id !== "cpu")?.id ?? "cpu");
2444
+ const dirty = selectedTemplateId ? JSON.stringify(steps) !== JSON.stringify(templates.find((t) => t.id === selectedTemplateId)?.steps) : false;
2445
+ function handleSelectTemplate(e) {
2446
+ const id = e.target.value || null;
2447
+ if (id) {
2448
+ const tpl = templates.find((t) => t.id === id);
2449
+ if (tpl) {
2450
+ const result = validateTemplate(tpl.steps, schema);
2451
+ setWarnings([...result.warnings]);
2452
+ }
2453
+ } else {
2454
+ setWarnings([]);
2455
+ }
2456
+ onSelectTemplate(id);
2457
+ }
2458
+ function handleSave() {
2459
+ if (selectedTemplateId) onUpdateTemplate(selectedTemplateId, steps);
2460
+ }
2461
+ function handleSaveAs() {
2462
+ const name = window.prompt("Template name:");
2463
+ if (name?.trim()) onSaveTemplate(name.trim(), steps);
2464
+ }
2465
+ function handleDelete() {
2466
+ if (!selectedTemplateId) return;
2467
+ const tpl = templates.find((t) => t.id === selectedTemplateId);
2468
+ if (tpl && window.confirm(`Delete template "${tpl.name}"?`)) {
2469
+ onDeleteTemplate(selectedTemplateId);
2470
+ }
2471
+ }
2472
+ function handleStepChange(updated) {
2473
+ onChange(steps.map((s) => {
2474
+ if (s.addonId !== updated.addonId) return s;
2475
+ return autoEnableAncestors(updated);
2476
+ }));
2477
+ }
2478
+ function autoEnableAncestors(step) {
2479
+ const hasEnabledChild = step.children.some((c) => c.enabled || c.children.some((gc) => gc.enabled));
2480
+ return {
2481
+ ...step,
2482
+ enabled: step.enabled || hasEnabledChild,
2483
+ children: step.children.map((c) => {
2484
+ const hasEnabledGrandchild = c.children.some((gc) => gc.enabled);
2485
+ return {
2486
+ ...c,
2487
+ enabled: c.enabled || hasEnabledGrandchild
2488
+ };
2489
+ })
2490
+ };
2491
+ }
2492
+ function handleAddChildToStep(parentAddonId, addon) {
2493
+ const child = createDefaultStep(addon, defaultRuntime, defaultBackend);
2494
+ onChange(steps.map((s) => {
2495
+ if (s.addonId !== parentAddonId) return s;
2496
+ return { ...s, children: [...s.children, child] };
2497
+ }));
2498
+ }
2499
+ function collectIds(list) {
2500
+ const ids = /* @__PURE__ */ new Set();
2501
+ for (const s of list) {
2502
+ ids.add(s.addonId);
2503
+ for (const c of s.children) ids.add(c.addonId);
2504
+ const childIds = collectIds(s.children);
2505
+ for (const id of childIds) ids.add(id);
2506
+ }
2507
+ return ids;
2508
+ }
2509
+ const existingIds = collectIds(steps);
2510
+ function getChildPlaceholders(step) {
2511
+ const stepSchema = schemaMap.get(step.addonId);
2512
+ if (!stepSchema) return [];
2513
+ const childSlotIds = stepSchema.childSlots;
2514
+ const childIds = new Set(step.children.map((c) => c.addonId));
2515
+ const placeholders = [];
2516
+ for (const slot of schema.slots) {
2517
+ if (!childSlotIds.includes(slot.id)) continue;
2518
+ for (const addon of slot.addons) {
2519
+ if (childIds.has(addon.id) || excluded.has(addon.id)) continue;
2520
+ const compatible = addon.inputClasses.some((ic) => step.outputClasses.includes(ic));
2521
+ if (compatible) placeholders.push(addon);
2522
+ }
2523
+ }
2524
+ return placeholders;
2525
+ }
2526
+ function renderStep(step) {
2527
+ const childPlaceholders = getChildPlaceholders(step);
2528
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "space-y-1.5", children: [
2529
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2530
+ PipelineStep,
2531
+ {
2532
+ step,
2533
+ schema: schemaMap.get(step.addonId) ?? null,
2534
+ allSchemas: schemaMap,
2535
+ capabilities,
2536
+ onChange: handleStepChange,
2537
+ onDelete: readOnly ? void 0 : (id) => onChange(steps.filter((s) => s.addonId !== id)),
2538
+ readOnly
2539
+ }
2540
+ ),
2541
+ (step.children.length > 0 || childPlaceholders.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "ml-6 pl-4 border-l-2 border-dashed border-border/40 space-y-1.5", children: [
2542
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/40", children: "Slot: Cropper / Classifier" }),
2543
+ step.children.map((child) => {
2544
+ const childChildPlaceholders = getChildPlaceholders(child);
2545
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "space-y-1.5", children: [
2546
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2547
+ PipelineStep,
2548
+ {
2549
+ step: child,
2550
+ schema: schemaMap.get(child.addonId) ?? null,
2551
+ allSchemas: schemaMap,
2552
+ capabilities,
2553
+ depth: 1,
2554
+ onChange: (updated) => {
2555
+ handleStepChange({
2556
+ ...step,
2557
+ children: step.children.map((c) => c.addonId === updated.addonId ? updated : c)
2558
+ });
2559
+ },
2560
+ onDelete: readOnly ? void 0 : (id) => {
2561
+ handleStepChange({
2562
+ ...step,
2563
+ children: step.children.filter((c) => c.addonId !== id)
2564
+ });
2565
+ },
2566
+ readOnly
2567
+ }
2568
+ ),
2569
+ (child.children.length > 0 || childChildPlaceholders.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "ml-6 pl-4 border-l-2 border-dashed border-border/30 space-y-1.5", children: [
2570
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/30", children: "Slot: Recognizer" }),
2571
+ child.children.map((grandchild) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2572
+ PipelineStep,
2573
+ {
2574
+ step: grandchild,
2575
+ schema: schemaMap.get(grandchild.addonId) ?? null,
2576
+ allSchemas: schemaMap,
2577
+ capabilities,
2578
+ depth: 2,
2579
+ onChange: (updatedGrandchild) => {
2580
+ handleStepChange({
2581
+ ...step,
2582
+ children: step.children.map(
2583
+ (c) => c.addonId === child.addonId ? { ...c, children: c.children.map((gc) => gc.addonId === updatedGrandchild.addonId ? updatedGrandchild : gc) } : c
2584
+ )
2585
+ });
2586
+ },
2587
+ onDelete: readOnly ? void 0 : (id) => {
2588
+ handleStepChange({
2589
+ ...step,
2590
+ children: step.children.map(
2591
+ (c) => c.addonId === child.addonId ? { ...c, children: c.children.filter((gc) => gc.addonId !== id) } : c
2592
+ )
2593
+ });
2594
+ },
2595
+ readOnly
2596
+ },
2597
+ grandchild.addonId
2598
+ )),
2599
+ !readOnly && childChildPlaceholders.map((addon) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2600
+ PlaceholderStep,
2601
+ {
2602
+ addon,
2603
+ onClick: () => {
2604
+ const newChild = createDefaultStep(addon, defaultRuntime, defaultBackend);
2605
+ handleStepChange({
2606
+ ...step,
2607
+ children: step.children.map(
2608
+ (c) => c.addonId === child.addonId ? { ...c, children: [...c.children, newChild] } : c
2609
+ )
2610
+ });
2611
+ }
2612
+ },
2613
+ addon.id
2614
+ ))
2615
+ ] })
2616
+ ] }, child.addonId);
2617
+ }),
2618
+ !readOnly && childPlaceholders.map((addon) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2619
+ PlaceholderStep,
2620
+ {
2621
+ addon,
2622
+ onClick: () => handleAddChildToStep(step.addonId, addon)
2623
+ },
2624
+ addon.id
2625
+ ))
2626
+ ] })
2627
+ ] }, step.addonId);
2628
+ }
2629
+ const rootSlots = schema.slots.filter((s) => s.parentSlot === null).sort((a, b) => a.priority - b.priority);
2630
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "space-y-4", children: [
2631
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "rounded-xl border border-border bg-surface p-3", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center gap-2", children: [
2632
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "relative flex-1 min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
2633
+ "select",
2634
+ {
2635
+ value: selectedTemplateId ?? "",
2636
+ onChange: handleSelectTemplate,
2637
+ 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",
2638
+ children: [
2639
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: "", children: "No template" }),
2640
+ templates.map((t) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("option", { value: t.id, children: t.name }, t.id))
2641
+ ]
2642
+ }
2643
+ ) }),
2644
+ dirty && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "h-1.5 w-1.5 rounded-full bg-amber-500 shrink-0" }),
2645
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2646
+ "button",
2647
+ {
2648
+ onClick: handleSave,
2649
+ disabled: !selectedTemplateId || readOnly,
2650
+ title: "Save",
2651
+ className: cn(
2652
+ "p-2 rounded-lg border border-border transition-colors",
2653
+ selectedTemplateId && !readOnly ? "text-foreground-subtle hover:bg-surface-hover" : "text-foreground-subtle/30 cursor-not-allowed"
2654
+ ),
2655
+ children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react14.Save, { className: "h-4 w-4" })
2656
+ }
2657
+ ),
2658
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2659
+ "button",
2660
+ {
2661
+ onClick: handleSaveAs,
2662
+ disabled: readOnly,
2663
+ title: "Save As",
2664
+ className: cn(
2665
+ "p-2 rounded-lg border border-border transition-colors",
2666
+ !readOnly ? "text-foreground-subtle hover:bg-surface-hover" : "text-foreground-subtle/30 cursor-not-allowed"
2667
+ ),
2668
+ children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react14.CopyPlus, { className: "h-4 w-4" })
2669
+ }
2670
+ ),
2671
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2672
+ "button",
2673
+ {
2674
+ onClick: handleDelete,
2675
+ disabled: !selectedTemplateId || readOnly,
2676
+ title: "Delete",
2677
+ className: cn(
2678
+ "p-2 rounded-lg border border-border transition-colors",
2679
+ selectedTemplateId && !readOnly ? "text-foreground-subtle hover:text-danger" : "text-foreground-subtle/30 cursor-not-allowed"
2680
+ ),
2681
+ children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react14.Trash2, { className: "h-4 w-4" })
2682
+ }
2683
+ )
2684
+ ] }) }),
2685
+ warnings.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "rounded-lg border border-amber-500/30 bg-amber-500/5 p-3 text-xs text-amber-400 space-y-1", children: [
2686
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center justify-between", children: [
2687
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "font-medium", children: "Template loaded with warnings:" }),
2688
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { onClick: () => setWarnings([]), className: "text-amber-400/60 hover:text-amber-400", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react14.X, { className: "h-3.5 w-3.5" }) })
2689
+ ] }),
2690
+ warnings.map((w, i) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { children: [
2691
+ "\u2022 ",
2692
+ w
2693
+ ] }, i))
2694
+ ] }),
2695
+ rootSlots.map((slot) => {
2696
+ const slotSteps = steps.filter((s) => s.slot === slot.id && !excluded.has(s.addonId));
2697
+ const missingRootAddons = slot.addons.filter((a) => !existingIds.has(a.id) && !excluded.has(a.id));
2698
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "space-y-2", children: [
2699
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/50", children: [
2700
+ "Slot: ",
2701
+ slot.label
2702
+ ] }),
2703
+ slotSteps.map((step) => renderStep(step)),
2704
+ !readOnly && missingRootAddons.map((addon) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(PlaceholderStep, { addon, onClick: () => {
2705
+ onChange([...steps, createDefaultStep(addon, defaultRuntime, defaultBackend)]);
2706
+ } }, addon.id))
2707
+ ] }, slot.id);
2708
+ })
2709
+ ] });
2710
+ }
2711
+
2712
+ // src/composites/detection-colors.ts
2713
+ var CLASS_COLORS = {
2714
+ // Primary detection classes
2715
+ person: "#3b82f6",
2716
+ // blue-500
2717
+ vehicle: "#f59e0b",
2718
+ // amber-500
2719
+ animal: "#22c55e",
2720
+ // green-500
2721
+ // Sub-detection classes
2722
+ face: "#a855f7",
2723
+ // purple-500
2724
+ plate: "#ec4899",
2725
+ // pink-500
2726
+ // Specific animal types
2727
+ bird: "#14b8a6",
2728
+ // teal-500
2729
+ dog: "#84cc16",
2730
+ // lime-500
2731
+ cat: "#f97316",
2732
+ // orange-500
2733
+ // Specific vehicle types
2734
+ car: "#f59e0b",
2735
+ // amber-500
2736
+ truck: "#d97706",
2737
+ // amber-600
2738
+ bus: "#b45309",
2739
+ // amber-700
2740
+ motorcycle: "#eab308",
2741
+ // yellow-500
2742
+ bicycle: "#ca8a04",
2743
+ // yellow-600
2744
+ // Other
2745
+ motion: "#facc15"
2746
+ // yellow-400
2747
+ };
2748
+ var FALLBACK_PALETTE = [
2749
+ "#ef4444",
2750
+ // red-500
2751
+ "#8b5cf6",
2752
+ // violet-500
2753
+ "#06b6d4",
2754
+ // cyan-500
2755
+ "#f97316",
2756
+ // orange-500
2757
+ "#10b981",
2758
+ // emerald-500
2759
+ "#6366f1",
2760
+ // indigo-500
2761
+ "#e11d48",
2762
+ // rose-600
2763
+ "#0891b2",
2764
+ // cyan-600
2765
+ "#7c3aed",
2766
+ // violet-600
2767
+ "#059669"
2768
+ // emerald-600
2769
+ ];
2770
+ var DEFAULT_COLOR = "#f59e42";
2771
+ function getClassColor(className, customColors) {
2772
+ if (customColors?.[className]) return customColors[className];
2773
+ if (customColors?.[className.toLowerCase()]) return customColors[className.toLowerCase()];
2774
+ if (CLASS_COLORS[className]) return CLASS_COLORS[className];
2775
+ if (CLASS_COLORS[className.toLowerCase()]) return CLASS_COLORS[className.toLowerCase()];
2776
+ let hash = 0;
2777
+ for (let i = 0; i < className.length; i++) {
2778
+ hash = hash * 31 + (className.codePointAt(i) ?? 0) >>> 0;
2779
+ }
2780
+ return FALLBACK_PALETTE[hash % FALLBACK_PALETTE.length] ?? DEFAULT_COLOR;
2781
+ }
2782
+
2783
+ // src/composites/detection-canvas.tsx
2784
+ var import_react25 = require("react");
2785
+ var import_jsx_runtime43 = require("react/jsx-runtime");
2786
+ var DEFAULT_CLASS_COLORS = CLASS_COLORS;
2787
+ function DetectionCanvas({
2788
+ src,
2789
+ imageWidth,
2790
+ imageHeight,
2791
+ detections = [],
2792
+ classColors,
2793
+ aspectRatio,
2794
+ className,
2795
+ placeholder,
2796
+ showConfidence = true,
2797
+ minConfidence = 0,
2798
+ borderWidth = 2
2799
+ }) {
2800
+ function getColor(className2) {
2801
+ return getClassColor(className2, classColors);
2802
+ }
2803
+ const ratio = aspectRatio ?? (imageWidth && imageHeight ? `${imageWidth}/${imageHeight}` : "16/9");
2804
+ const filteredDetections = detections.filter((d) => d.confidence >= minConfidence);
2805
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2806
+ "div",
2807
+ {
2808
+ className: cn(
2809
+ "rounded-lg border border-border bg-surface overflow-hidden relative",
2810
+ className
2811
+ ),
2812
+ style: { aspectRatio: ratio },
2813
+ children: src ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_jsx_runtime43.Fragment, { children: [
2814
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("img", { src, className: "absolute inset-0 w-full h-full object-fill", alt: "" }),
2815
+ filteredDetections.map(
2816
+ (d, i) => d.mask && d.maskWidth && d.maskHeight ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2817
+ MaskOverlay,
2818
+ {
2819
+ mask: d.mask,
2820
+ maskWidth: d.maskWidth,
2821
+ maskHeight: d.maskHeight,
2822
+ bbox: d.bbox,
2823
+ imageWidth,
2824
+ imageHeight,
2825
+ color: getColor(d.className)
2826
+ },
2827
+ `mask-${i}`
2828
+ ) : null
2829
+ ),
2830
+ filteredDetections.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2831
+ BoundingBox,
2832
+ {
2833
+ detection: d,
2834
+ imageWidth,
2835
+ imageHeight,
2836
+ color: getColor(d.className),
2837
+ showConfidence,
2838
+ borderWidth: d.mask ? 1 : borderWidth,
2839
+ children: d.children?.filter((c) => {
2840
+ if (c.confidence < minConfidence) return false;
2841
+ const [cx1, cy1, cx2, cy2] = c.bbox;
2842
+ const cw = cx2 - cx1;
2843
+ const ch = cy2 - cy1;
2844
+ if (cw <= 0 || ch <= 0) return false;
2845
+ const [px1, py1, px2, py2] = d.bbox;
2846
+ const pw = px2 - px1;
2847
+ const ph = py2 - py1;
2848
+ if (pw > 0 && ph > 0 && cw * ch / (pw * ph) > 0.8) return false;
2849
+ return true;
2850
+ }).map((child, j) => /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2851
+ ChildBoundingBox,
2852
+ {
2853
+ child,
2854
+ parentBbox: d.bbox,
2855
+ color: getColor(child.className),
2856
+ showConfidence
2857
+ },
2858
+ `child-${j}`
2859
+ ))
2860
+ },
2861
+ `det-${i}`
2862
+ ))
2863
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "w-full h-full flex items-center justify-center text-foreground-subtle text-sm", children: placeholder ?? "No image loaded" })
2864
+ }
2865
+ );
2866
+ }
2867
+ function BoundingBox({
2868
+ detection,
2869
+ imageWidth,
2870
+ imageHeight,
2871
+ color,
2872
+ showConfidence,
2873
+ borderWidth,
2874
+ children
2875
+ }) {
2876
+ const [x1, y1, x2, y2] = detection.bbox;
2877
+ const labelCount = 1 + (detection.labelsData?.length ?? 0);
2878
+ const labelHeightPx = labelCount * 16 + 4;
2879
+ const topPct = y1 / imageHeight * 100;
2880
+ const containerRef = (0, import_react25.useRef)(null);
2881
+ const showBelow = topPct < labelHeightPx / imageHeight * 100 * 1.5;
2882
+ const labelsElement = /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2883
+ "div",
2884
+ {
2885
+ className: `absolute left-0 flex flex-col items-start gap-px ${showBelow ? "" : ""}`,
2886
+ style: showBelow ? { top: "100%", marginTop: "2px" } : { bottom: "100%", marginBottom: "2px" },
2887
+ children: [
2888
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2889
+ "span",
2890
+ {
2891
+ className: "text-[10px] px-1 rounded-sm whitespace-nowrap text-white",
2892
+ style: { backgroundColor: color },
2893
+ children: [
2894
+ detection.className,
2895
+ showConfidence && ` ${(detection.confidence * 100).toFixed(0)}%`
2896
+ ]
2897
+ }
2898
+ ),
2899
+ detection.labelsData?.map((l, k) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2900
+ "span",
2901
+ {
2902
+ className: "text-[9px] font-semibold px-1 rounded-sm whitespace-nowrap text-white",
2903
+ style: { backgroundColor: getClassColor(l.addonId ?? l.label) },
2904
+ children: [
2905
+ l.label,
2906
+ " ",
2907
+ (l.score * 100).toFixed(0),
2908
+ "%"
2909
+ ]
2910
+ },
2911
+ k
2912
+ ))
2913
+ ]
2914
+ }
2915
+ );
2916
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2917
+ "div",
2918
+ {
2919
+ ref: containerRef,
2920
+ className: "absolute rounded-sm",
2921
+ style: {
2922
+ left: `${x1 / imageWidth * 100}%`,
2923
+ top: `${y1 / imageHeight * 100}%`,
2924
+ width: `${(x2 - x1) / imageWidth * 100}%`,
2925
+ height: `${(y2 - y1) / imageHeight * 100}%`,
2926
+ borderWidth: `${borderWidth}px`,
2927
+ borderStyle: "solid",
2928
+ borderColor: color
2929
+ },
2930
+ children: [
2931
+ labelsElement,
2932
+ children
2933
+ ]
2934
+ }
2935
+ );
2936
+ }
2937
+ function MaskOverlay({
2938
+ mask,
2939
+ maskWidth,
2940
+ maskHeight,
2941
+ bbox,
2942
+ imageWidth,
2943
+ imageHeight,
2944
+ color
2945
+ }) {
2946
+ const canvasRef = (0, import_react25.useRef)(null);
2947
+ (0, import_react25.useEffect)(() => {
2948
+ const canvas = canvasRef.current;
2949
+ if (!canvas) return;
2950
+ const ctx = canvas.getContext("2d");
2951
+ if (!ctx) return;
2952
+ const binary = atob(mask);
2953
+ const bytes = new Uint8Array(binary.length);
2954
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
2955
+ const r = parseInt(color.slice(1, 3), 16);
2956
+ const g = parseInt(color.slice(3, 5), 16);
2957
+ const b = parseInt(color.slice(5, 7), 16);
2958
+ canvas.width = maskWidth;
2959
+ canvas.height = maskHeight;
2960
+ const imageData = ctx.createImageData(maskWidth, maskHeight);
2961
+ const totalPixels = maskWidth * maskHeight;
2962
+ for (let i = 0; i < totalPixels; i++) {
2963
+ const val = i < bytes.length ? bytes[i] : 0;
2964
+ const px = i * 4;
2965
+ if (val > 0) {
2966
+ imageData.data[px] = r;
2967
+ imageData.data[px + 1] = g;
2968
+ imageData.data[px + 2] = b;
2969
+ imageData.data[px + 3] = 120;
2970
+ }
2971
+ }
2972
+ ctx.putImageData(imageData, 0, 0);
2973
+ }, [mask, maskWidth, maskHeight, color]);
2974
+ const [x1, y1, x2, y2] = bbox;
2975
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2976
+ "canvas",
2977
+ {
2978
+ ref: canvasRef,
2979
+ className: "absolute pointer-events-none",
2980
+ style: {
2981
+ left: `${x1 / imageWidth * 100}%`,
2982
+ top: `${y1 / imageHeight * 100}%`,
2983
+ width: `${(x2 - x1) / imageWidth * 100}%`,
2984
+ height: `${(y2 - y1) / imageHeight * 100}%`,
2985
+ imageRendering: "pixelated"
2986
+ }
2987
+ }
2988
+ );
2989
+ }
2990
+ function ChildBoundingBox({
2991
+ child,
2992
+ parentBbox,
2993
+ color,
2994
+ showConfidence
2995
+ }) {
2996
+ const [px1, py1, px2, py2] = parentBbox;
2997
+ const [cx1, cy1, cx2, cy2] = child.bbox;
2998
+ const pw = px2 - px1;
2999
+ const ph = py2 - py1;
3000
+ if (pw <= 0 || ph <= 0) return null;
3001
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
3002
+ "div",
3003
+ {
3004
+ className: "absolute rounded-sm",
3005
+ style: {
3006
+ left: `${Math.max(0, (cx1 - px1) / pw * 100)}%`,
3007
+ top: `${Math.max(0, (cy1 - py1) / ph * 100)}%`,
3008
+ width: `${Math.min(100, (cx2 - cx1) / pw * 100)}%`,
3009
+ height: `${Math.min(100, (cy2 - cy1) / ph * 100)}%`,
3010
+ borderWidth: "1px",
3011
+ borderStyle: "solid",
3012
+ borderColor: color
3013
+ },
3014
+ children: (() => {
3015
+ const labelCount = 1 + (child.labelsData?.length ?? 0);
3016
+ const relTop = (cy1 - py1) / ph * 100;
3017
+ const showBelow = relTop < labelCount * 6;
3018
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
3019
+ "div",
3020
+ {
3021
+ className: "absolute left-0 flex flex-col items-start gap-px",
3022
+ style: showBelow ? { top: "100%", marginTop: "1px" } : { bottom: "100%", marginBottom: "1px" },
3023
+ children: [
3024
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
3025
+ "span",
3026
+ {
3027
+ className: "text-[9px] px-0.5 rounded-sm whitespace-nowrap text-white",
3028
+ style: { backgroundColor: color },
3029
+ children: [
3030
+ child.className,
3031
+ showConfidence && ` ${(child.confidence * 100).toFixed(0)}%`
3032
+ ]
3033
+ }
3034
+ ),
3035
+ child.labelsData?.map((l, k) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
3036
+ "span",
3037
+ {
3038
+ className: "text-[8px] font-semibold px-0.5 rounded-sm whitespace-nowrap text-white",
3039
+ style: { backgroundColor: getClassColor(l.addonId ?? l.label) },
3040
+ children: [
3041
+ l.label,
3042
+ " ",
3043
+ (l.score * 100).toFixed(0),
3044
+ "%"
3045
+ ]
3046
+ },
3047
+ k
3048
+ ))
3049
+ ]
3050
+ }
3051
+ );
3052
+ })()
3053
+ }
3054
+ );
3055
+ }
3056
+
3057
+ // src/composites/detection-result-tree.tsx
3058
+ var import_jsx_runtime44 = require("react/jsx-runtime");
3059
+ function DetectionResultTree({
3060
+ detections,
3061
+ classColors,
3062
+ className,
3063
+ hiddenKeys,
3064
+ onToggleVisibility
3065
+ }) {
3066
+ const colors = classColors;
3067
+ if (detections.length === 0) {
3068
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "text-sm text-foreground-subtle italic text-center py-4", children: "No detections" });
3069
+ }
3070
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className, children: [
3071
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "text-xs font-medium text-foreground-subtle uppercase tracking-wide mb-2", children: [
3072
+ "Detections (",
3073
+ detections.length,
3074
+ ")"
3075
+ ] }),
3076
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "space-y-2", children: detections.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3077
+ DetectionNode,
3078
+ {
3079
+ detection: d,
3080
+ path: String(i),
3081
+ colors,
3082
+ hiddenKeys,
3083
+ onToggleVisibility
3084
+ },
3085
+ i
3086
+ )) })
3087
+ ] });
3088
+ }
3089
+ function DetectionNode({
3090
+ detection,
3091
+ path,
3092
+ colors,
3093
+ hiddenKeys,
3094
+ onToggleVisibility
3095
+ }) {
3096
+ const color = getClassColor(detection.className, colors);
3097
+ const isVisible = !hiddenKeys?.has(path);
3098
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: `rounded-md border border-border bg-surface p-3 space-y-1 ${isVisible ? "" : "opacity-40"}`, children: [
3099
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex justify-between items-center", children: [
3100
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-2", children: [
3101
+ onToggleVisibility && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3102
+ "input",
3103
+ {
3104
+ type: "checkbox",
3105
+ checked: isVisible,
3106
+ onChange: () => onToggleVisibility(path, !isVisible),
3107
+ className: "h-3.5 w-3.5 rounded border-border accent-primary cursor-pointer shrink-0"
3108
+ }
3109
+ ),
3110
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3111
+ "span",
3112
+ {
3113
+ className: "h-2.5 w-2.5 rounded-full shrink-0",
3114
+ style: { backgroundColor: color }
3115
+ }
3116
+ ),
3117
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "text-sm font-medium text-foreground", children: detection.className }),
3118
+ detection.mask && detection.maskWidth && detection.maskHeight && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "text-[9px] font-mono px-1 py-0.5 rounded bg-primary/10 text-primary", children: [
3119
+ "mask ",
3120
+ detection.maskWidth,
3121
+ "x",
3122
+ detection.maskHeight
3123
+ ] })
3124
+ ] }),
3125
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ConfidenceBadge, { confidence: detection.confidence })
3126
+ ] }),
3127
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "text-[10px] text-foreground-subtle font-mono", children: [
3128
+ "bbox: [",
3129
+ detection.bbox.map((v) => Math.round(v)).join(", "),
3130
+ "]"
3131
+ ] }),
3132
+ detection.labelsData && detection.labelsData.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "flex flex-wrap gap-1 mt-1", children: detection.labelsData.map((l, k) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
3133
+ "span",
3134
+ {
3135
+ className: "inline-flex items-center gap-1 text-[10px] font-medium px-1.5 py-0.5 rounded-full",
3136
+ style: { backgroundColor: getClassColor(l.addonId ?? l.label, colors) + "20", color: getClassColor(l.addonId ?? l.label, colors) },
3137
+ children: [
3138
+ l.label,
3139
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "opacity-60", children: [
3140
+ (l.score * 100).toFixed(0),
3141
+ "%"
3142
+ ] }),
3143
+ l.addonId && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "opacity-40 text-[8px]", children: l.addonId })
3144
+ ]
3145
+ },
3146
+ k
3147
+ )) }),
3148
+ detection.children && detection.children.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3149
+ ChildrenTree,
3150
+ {
3151
+ children: detection.children,
3152
+ parentPath: path,
3153
+ colors,
3154
+ hiddenKeys,
3155
+ onToggleVisibility
3156
+ }
3157
+ )
3158
+ ] });
3159
+ }
3160
+ function ChildrenTree({
3161
+ children,
3162
+ parentPath,
3163
+ colors,
3164
+ hiddenKeys,
3165
+ onToggleVisibility
3166
+ }) {
3167
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "ml-4 mt-1.5 space-y-1.5 border-l-2 border-border pl-3", children: children.map((child, j) => {
3168
+ const childPath = `${parentPath}.${j}`;
3169
+ const childColor = getClassColor(child.className, colors);
3170
+ const isVisible = !hiddenKeys?.has(childPath);
3171
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: `text-xs space-y-0.5 ${isVisible ? "" : "opacity-40"}`, children: [
3172
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-1.5", children: [
3173
+ onToggleVisibility && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3174
+ "input",
3175
+ {
3176
+ type: "checkbox",
3177
+ checked: isVisible,
3178
+ onChange: () => onToggleVisibility(childPath, !isVisible),
3179
+ className: "h-3 w-3 rounded border-border accent-primary cursor-pointer shrink-0"
3180
+ }
3181
+ ),
3182
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3183
+ "span",
3184
+ {
3185
+ className: "h-1.5 w-1.5 rounded-full shrink-0",
3186
+ style: { backgroundColor: childColor }
3187
+ }
3188
+ ),
3189
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "font-medium", style: { color: childColor }, children: child.className }),
3190
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "text-foreground-subtle", children: [
3191
+ (child.confidence * 100).toFixed(0),
3192
+ "%"
3193
+ ] }),
3194
+ child.mask && child.maskWidth && child.maskHeight && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "text-[9px] font-mono px-1 py-0.5 rounded bg-primary/10 text-primary", children: [
3195
+ "mask ",
3196
+ child.maskWidth,
3197
+ "x",
3198
+ child.maskHeight
3199
+ ] })
3200
+ ] }),
3201
+ child.labelsData && child.labelsData.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "flex flex-wrap gap-1 ml-5 mt-0.5", children: child.labelsData.map((l, k) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
3202
+ "span",
3203
+ {
3204
+ className: "inline-flex items-center gap-0.5 text-[9px] font-medium px-1 py-0.5 rounded-full",
3205
+ style: { backgroundColor: getClassColor(l.addonId ?? l.label, colors) + "20", color: getClassColor(l.addonId ?? l.label, colors) },
3206
+ children: [
3207
+ l.label,
3208
+ " ",
3209
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "opacity-60", children: [
3210
+ (l.score * 100).toFixed(0),
3211
+ "%"
3212
+ ] })
3213
+ ]
3214
+ },
3215
+ k
3216
+ )) }),
3217
+ child.children && child.children.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3218
+ ChildrenTree,
3219
+ {
3220
+ children: child.children,
3221
+ parentPath: childPath,
3222
+ colors,
3223
+ hiddenKeys,
3224
+ onToggleVisibility
3225
+ }
3226
+ )
3227
+ ] }, j);
3228
+ }) });
3229
+ }
3230
+ function ConfidenceBadge({ confidence }) {
3231
+ const level = confidence >= 0.8 ? "bg-success/10 text-success" : confidence >= 0.5 ? "bg-warning/10 text-warning" : "bg-danger/10 text-danger";
3232
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: `text-xs font-medium px-2 py-0.5 rounded-full ${level}`, children: [
3233
+ (confidence * 100).toFixed(1),
3234
+ "%"
3235
+ ] });
3236
+ }
3237
+
3238
+ // src/composites/step-timings.tsx
3239
+ var import_jsx_runtime45 = require("react/jsx-runtime");
3240
+ function StepTimings({ timings, totalMs, className }) {
3241
+ const entries = Object.entries(timings);
3242
+ if (entries.length === 0 && totalMs === void 0) return null;
3243
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: `rounded-lg border border-border bg-surface p-3 space-y-2 ${className ?? ""}`, children: [
3244
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "text-xs font-medium text-foreground-subtle uppercase tracking-wide", children: "Timings" }),
3245
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "space-y-1 text-xs", children: [
3246
+ entries.map(([step, ms]) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex justify-between", children: [
3247
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "text-foreground-subtle", children: step }),
3248
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "font-mono text-foreground", children: [
3249
+ ms.toFixed(1),
3250
+ "ms"
3251
+ ] })
3252
+ ] }, step)),
3253
+ totalMs !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex justify-between pt-1 border-t border-border font-medium text-foreground", children: [
3254
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { children: "Total" }),
3255
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "font-mono", children: [
3256
+ totalMs.toFixed(1),
3257
+ "ms"
3258
+ ] })
3259
+ ] })
3260
+ ] })
3261
+ ] });
3262
+ }
3263
+
3264
+ // src/composites/image-selector.tsx
3265
+ var import_jsx_runtime46 = require("react/jsx-runtime");
3266
+ function ImageSelector({
3267
+ images,
3268
+ selectedFilename,
3269
+ uploadedName,
3270
+ onSelect,
3271
+ onUpload,
3272
+ className
3273
+ }) {
3274
+ const handleUploadClick = () => {
3275
+ const input = document.createElement("input");
3276
+ input.type = "file";
3277
+ input.accept = "image/*";
3278
+ input.onchange = (ev) => {
3279
+ const file = ev.target.files?.[0];
3280
+ if (!file) return;
3281
+ const reader = new FileReader();
3282
+ reader.onload = (e) => {
3283
+ const dataUrl = e.target?.result;
3284
+ const b64 = dataUrl.split(",")[1];
3285
+ if (b64) onUpload(b64, file.name, dataUrl);
3286
+ };
3287
+ reader.readAsDataURL(file);
3288
+ };
3289
+ input.click();
3290
+ };
3291
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: `flex flex-wrap items-center gap-2 ${className ?? ""}`, children: [
3292
+ images.map((img) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3293
+ "button",
3294
+ {
3295
+ onClick: () => onSelect(img.filename),
3296
+ className: `px-3 py-1.5 text-xs rounded-md border transition-colors ${selectedFilename === img.filename ? "bg-primary text-primary-foreground border-primary" : "bg-surface border-border text-foreground hover:border-primary/50"}`,
3297
+ children: img.id
3298
+ },
3299
+ img.filename
3300
+ )),
3301
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3302
+ "button",
3303
+ {
3304
+ onClick: handleUploadClick,
3305
+ className: "px-3 py-1.5 text-xs rounded-md border border-border bg-surface text-foreground hover:bg-surface-hover transition-colors",
3306
+ children: "Upload..."
3307
+ }
3308
+ ),
3309
+ uploadedName && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "text-xs text-foreground-subtle", children: uploadedName })
3310
+ ] });
3311
+ }
3312
+
3313
+ // src/composites/inference-config-selector.tsx
3314
+ var import_jsx_runtime47 = require("react/jsx-runtime");
3315
+ 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";
3316
+ function InferenceConfigSelector({
3317
+ runtime,
3318
+ backend,
3319
+ modelId,
3320
+ agentId = "hub",
3321
+ runtimes,
3322
+ backends,
3323
+ models,
3324
+ agents = [],
3325
+ onRuntimeChange,
3326
+ onBackendChange,
3327
+ onModelChange,
3328
+ onAgentChange,
3329
+ layout = "grid",
3330
+ className,
3331
+ showAgent = false
3332
+ }) {
3333
+ 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";
3334
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: `${containerClass} ${className ?? ""}`, children: [
3335
+ showAgent && agents.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("label", { className: "space-y-1", children: [
3336
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "text-xs font-medium text-foreground-subtle", children: "Agent" }),
3337
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3338
+ "select",
3339
+ {
3340
+ value: agentId,
3341
+ onChange: (e) => onAgentChange?.(e.target.value),
3342
+ className: SELECT_CLASS,
3343
+ children: agents.map((a) => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("option", { value: a.id, children: [
3344
+ a.name,
3345
+ " (",
3346
+ a.status,
3347
+ ")"
3348
+ ] }, a.id))
3349
+ }
3350
+ )
3351
+ ] }),
3352
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("label", { className: "space-y-1", children: [
3353
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "text-xs font-medium text-foreground-subtle", children: "Runtime" }),
3354
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3355
+ "select",
3356
+ {
3357
+ value: runtime,
3358
+ onChange: (e) => onRuntimeChange(e.target.value),
3359
+ className: SELECT_CLASS,
3360
+ children: runtimes.map((r) => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("option", { value: r.value, disabled: !r.available, children: [
3361
+ r.label,
3362
+ !r.available ? " (unavailable)" : ""
3363
+ ] }, r.value))
3364
+ }
3365
+ )
3366
+ ] }),
3367
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("label", { className: "space-y-1", children: [
3368
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "text-xs font-medium text-foreground-subtle", children: "Backend" }),
3369
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3370
+ "select",
3371
+ {
3372
+ value: backend,
3373
+ onChange: (e) => onBackendChange(e.target.value),
3374
+ className: SELECT_CLASS,
3375
+ children: backends.map((b) => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("option", { value: b.id, disabled: !b.available, children: [
3376
+ b.label,
3377
+ !b.available ? " (unavailable)" : ""
3378
+ ] }, b.id))
3379
+ }
3380
+ )
3381
+ ] }),
3382
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("label", { className: "space-y-1", children: [
3383
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "text-xs font-medium text-foreground-subtle", children: "Model" }),
3384
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3385
+ "select",
3386
+ {
3387
+ value: modelId,
3388
+ onChange: (e) => onModelChange(e.target.value),
3389
+ className: SELECT_CLASS,
3390
+ children: models.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("option", { value: "", children: "No compatible models" }) : models.map((m) => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("option", { value: m.id, children: [
3391
+ m.name,
3392
+ m.downloaded ? " \u2713" : ""
3393
+ ] }, m.id))
3394
+ }
3395
+ )
3396
+ ] })
3397
+ ] });
3398
+ }
3399
+
3400
+ // src/composites/mount-addon-page.tsx
3401
+ var import_react28 = require("react");
3402
+ var import_client2 = require("react-dom/client");
3403
+
3404
+ // src/composites/dev-shell.tsx
3405
+ var import_react27 = require("react");
3406
+ var import_client = require("@trpc/client");
3407
+ var import_superjson = __toESM(require("superjson"), 1);
3408
+
3409
+ // src/composites/login-form.tsx
3410
+ var import_react26 = require("react");
3411
+ var import_jsx_runtime48 = require("react/jsx-runtime");
3412
+ function EyeIcon({ className }) {
3413
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
3414
+ "svg",
3415
+ {
3416
+ xmlns: "http://www.w3.org/2000/svg",
3417
+ viewBox: "0 0 24 24",
3418
+ fill: "none",
3419
+ stroke: "currentColor",
3420
+ strokeWidth: "2",
3421
+ strokeLinecap: "round",
3422
+ strokeLinejoin: "round",
3423
+ className,
3424
+ children: [
3425
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("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" }),
3426
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("circle", { cx: "12", cy: "12", r: "3" })
3427
+ ]
3428
+ }
3429
+ );
3430
+ }
3431
+ function EyeOffIcon({ className }) {
3432
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
3433
+ "svg",
3434
+ {
3435
+ xmlns: "http://www.w3.org/2000/svg",
3436
+ viewBox: "0 0 24 24",
3437
+ fill: "none",
3438
+ stroke: "currentColor",
3439
+ strokeWidth: "2",
3440
+ strokeLinecap: "round",
3441
+ strokeLinejoin: "round",
3442
+ className,
3443
+ children: [
3444
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("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" }),
3445
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("path", { d: "M14.084 14.158a3 3 0 0 1-4.242-4.242" }),
3446
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("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" }),
3447
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("path", { d: "m2 2 20 20" })
3448
+ ]
3449
+ }
3450
+ );
3451
+ }
3452
+ function SpinnerIcon({ className }) {
3453
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3454
+ "svg",
3455
+ {
3456
+ xmlns: "http://www.w3.org/2000/svg",
3457
+ viewBox: "0 0 24 24",
3458
+ fill: "none",
3459
+ stroke: "currentColor",
3460
+ strokeWidth: "2",
3461
+ strokeLinecap: "round",
3462
+ strokeLinejoin: "round",
3463
+ className,
3464
+ children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
3465
+ }
3466
+ );
3467
+ }
3468
+ function LoginForm({
3469
+ onLogin,
3470
+ serverUrl,
3471
+ logoSrc,
3472
+ error: externalError,
3473
+ className
3474
+ }) {
3475
+ const [username, setUsername] = (0, import_react26.useState)("");
3476
+ const [password, setPassword] = (0, import_react26.useState)("");
3477
+ const [showPassword, setShowPassword] = (0, import_react26.useState)(false);
3478
+ const [submitting, setSubmitting] = (0, import_react26.useState)(false);
3479
+ const [internalError, setInternalError] = (0, import_react26.useState)(null);
3480
+ const error = externalError ?? internalError;
3481
+ const handleSubmit = async (e) => {
3482
+ e.preventDefault();
3483
+ if (submitting) return;
3484
+ setInternalError(null);
3485
+ setSubmitting(true);
3486
+ try {
3487
+ await onLogin(username, password);
3488
+ } catch (err) {
3489
+ const message = err instanceof Error ? err.message : "Login failed. Please try again.";
3490
+ setInternalError(message);
3491
+ } finally {
3492
+ setSubmitting(false);
3493
+ }
3494
+ };
3495
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3496
+ "div",
3497
+ {
3498
+ className: cn(
3499
+ "flex min-h-screen items-center justify-center bg-background p-4",
3500
+ className
3501
+ ),
3502
+ children: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "w-full max-w-sm", children: [
3503
+ logoSrc && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "flex justify-center mb-8", children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("img", { src: logoSrc, alt: "Logo", className: "h-12" }) }),
3504
+ serverUrl && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "mb-4 text-center text-xs text-foreground-subtle truncate", children: serverUrl }),
3505
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
3506
+ "form",
3507
+ {
3508
+ onSubmit: handleSubmit,
3509
+ className: "space-y-4 rounded-xl border border-border bg-surface p-6 shadow-xl shadow-black/10",
3510
+ children: [
3511
+ error && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "rounded-md bg-danger/10 border border-danger/20 px-3 py-2 text-xs text-danger", children: error }),
3512
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "space-y-1.5", children: [
3513
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("label", { className: "text-xs font-medium text-foreground-subtle", children: "Username" }),
3514
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3515
+ "input",
3516
+ {
3517
+ type: "text",
3518
+ value: username,
3519
+ onChange: (e) => setUsername(e.target.value),
3520
+ autoComplete: "username",
3521
+ required: true,
3522
+ className: "w-full rounded-lg border border-border bg-background px-3 py-2.5 text-sm text-foreground placeholder:text-foreground-subtle focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary"
3523
+ }
3524
+ )
3525
+ ] }),
3526
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "space-y-1.5", children: [
3527
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("label", { className: "text-xs font-medium text-foreground-subtle", children: "Password" }),
3528
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "relative", children: [
3529
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3530
+ "input",
3531
+ {
3532
+ type: showPassword ? "text" : "password",
3533
+ value: password,
3534
+ onChange: (e) => setPassword(e.target.value),
3535
+ autoComplete: "current-password",
3536
+ required: true,
3537
+ 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"
3538
+ }
3539
+ ),
3540
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3541
+ "button",
3542
+ {
3543
+ type: "button",
3544
+ onClick: () => setShowPassword((prev) => !prev),
3545
+ className: "absolute right-2.5 top-1/2 -translate-y-1/2 text-foreground-subtle hover:text-foreground",
3546
+ tabIndex: -1,
3547
+ children: showPassword ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(EyeOffIcon, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(EyeIcon, { className: "h-4 w-4" })
3548
+ }
3549
+ )
3550
+ ] })
3551
+ ] }),
3552
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
3553
+ "button",
3554
+ {
3555
+ type: "submit",
3556
+ disabled: submitting,
3557
+ 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",
3558
+ children: [
3559
+ submitting && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(SpinnerIcon, { className: "h-4 w-4 animate-spin" }),
3560
+ submitting ? "Logging in..." : "Log in"
3561
+ ]
3562
+ }
3563
+ )
3564
+ ]
3565
+ }
3566
+ )
3567
+ ] })
3568
+ }
3569
+ );
3570
+ }
3571
+
3572
+ // src/composites/dev-shell.tsx
3573
+ var import_jsx_runtime49 = require("react/jsx-runtime");
3574
+ var STORAGE_KEY = "camstack_dev_token";
3575
+ var DevShellContext = (0, import_react27.createContext)(null);
3576
+ function useDevShell() {
3577
+ const ctx = (0, import_react27.useContext)(DevShellContext);
3578
+ if (!ctx) {
3579
+ throw new Error("useDevShell must be used within a DevShell");
3580
+ }
3581
+ return ctx;
3582
+ }
3583
+ function getStoredToken() {
3584
+ if (typeof window === "undefined") return null;
3585
+ return localStorage.getItem(STORAGE_KEY);
3586
+ }
3587
+ function SunIcon({ className }) {
3588
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
3589
+ "svg",
3590
+ {
3591
+ xmlns: "http://www.w3.org/2000/svg",
3592
+ viewBox: "0 0 24 24",
3593
+ fill: "none",
3594
+ stroke: "currentColor",
3595
+ strokeWidth: "2",
3596
+ strokeLinecap: "round",
3597
+ strokeLinejoin: "round",
3598
+ className,
3599
+ children: [
3600
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("circle", { cx: "12", cy: "12", r: "4" }),
3601
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("path", { d: "M12 2v2" }),
3602
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("path", { d: "M12 20v2" }),
3603
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("path", { d: "m4.93 4.93 1.41 1.41" }),
3604
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("path", { d: "m17.66 17.66 1.41 1.41" }),
3605
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("path", { d: "M2 12h2" }),
3606
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("path", { d: "M20 12h2" }),
3607
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("path", { d: "m6.34 17.66-1.41 1.41" }),
3608
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("path", { d: "m19.07 4.93-1.41 1.41" })
3609
+ ]
3610
+ }
3611
+ );
3612
+ }
3613
+ function MoonIcon({ className }) {
3614
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3615
+ "svg",
3616
+ {
3617
+ xmlns: "http://www.w3.org/2000/svg",
3618
+ viewBox: "0 0 24 24",
3619
+ fill: "none",
3620
+ stroke: "currentColor",
3621
+ strokeWidth: "2",
3622
+ strokeLinecap: "round",
3623
+ strokeLinejoin: "round",
3624
+ className,
3625
+ children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("path", { d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" })
3626
+ }
3627
+ );
3628
+ }
3629
+ function DevShellInner({
3630
+ children,
3631
+ serverUrl,
3632
+ title,
3633
+ token,
3634
+ onLogout
3635
+ }) {
3636
+ const theme = useThemeMode();
3637
+ const trpc = (0, import_react27.useMemo)(
3638
+ () => {
3639
+ const wsUrl = serverUrl.replace(/^http/, "ws") + "/trpc";
3640
+ const wsClient = (0, import_client.createWSClient)({
3641
+ url: wsUrl,
3642
+ connectionParams: () => ({ token })
3643
+ });
3644
+ return (0, import_client.createTRPCClient)({
3645
+ links: [
3646
+ (0, import_client.splitLink)({
3647
+ condition: (op) => op.type === "subscription",
3648
+ true: (0, import_client.wsLink)({ client: wsClient, transformer: import_superjson.default }),
3649
+ false: (0, import_client.httpLink)({
3650
+ url: `${serverUrl}/trpc`,
3651
+ transformer: import_superjson.default,
3652
+ headers: () => ({ authorization: `Bearer ${token}` })
3653
+ })
3654
+ })
3655
+ ]
3656
+ });
3657
+ },
3658
+ [serverUrl, token]
3659
+ );
3660
+ const contextValue = (0, import_react27.useMemo)(
3661
+ () => ({ trpc, token, logout: onLogout }),
3662
+ [trpc, token, onLogout]
3663
+ );
3664
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(DevShellContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "min-h-screen bg-background text-foreground", children: [
3665
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex items-center justify-between border-b border-border bg-surface px-4 py-2", children: [
3666
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex items-center gap-2", children: [
3667
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "rounded bg-warning/20 px-2 py-0.5 text-xs font-bold text-warning", children: "DEV MODE" }),
3668
+ title && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "text-sm font-medium text-foreground", children: title }),
3669
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "text-xs text-foreground-subtle", children: serverUrl })
3670
+ ] }),
3671
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex items-center gap-2", children: [
3672
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
3673
+ "button",
3674
+ {
3675
+ type: "button",
3676
+ onClick: theme.toggleMode,
3677
+ 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",
3678
+ title: `Theme: ${theme.mode}`,
3679
+ children: [
3680
+ theme.resolvedMode === "dark" ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(SunIcon, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(MoonIcon, { className: "h-3.5 w-3.5" }),
3681
+ theme.mode === "dark" ? "Dark" : theme.mode === "light" ? "Light" : "System"
3682
+ ]
3683
+ }
3684
+ ),
3685
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3686
+ "button",
3687
+ {
3688
+ type: "button",
3689
+ onClick: onLogout,
3690
+ className: "rounded-md px-2.5 py-1 text-xs font-medium text-danger hover:bg-danger/10 transition-colors",
3691
+ children: "Logout"
3692
+ }
3693
+ )
3694
+ ] })
3695
+ ] }),
3696
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "p-4", children: children({ trpc, theme }) })
3697
+ ] }) });
3698
+ }
3699
+ function DevShell({
3700
+ children,
3701
+ serverUrl = "https://localhost:4443",
3702
+ title
3703
+ }) {
3704
+ const [token, setToken] = (0, import_react27.useState)(getStoredToken);
3705
+ const handleLogin = (0, import_react27.useCallback)(
3706
+ async (username, password) => {
3707
+ const anonClient = (0, import_client.createTRPCClient)({
3708
+ links: [
3709
+ (0, import_client.httpLink)({
3710
+ url: `${serverUrl}/trpc`,
3711
+ transformer: import_superjson.default
3712
+ })
3713
+ ]
3714
+ });
3715
+ const res = await anonClient.auth.login.mutate({ username, password });
3716
+ if (!res?.token) throw new Error("No token returned");
3717
+ localStorage.setItem(STORAGE_KEY, res.token);
3718
+ setToken(res.token);
3719
+ },
3720
+ [serverUrl]
3721
+ );
3722
+ const handleLogout = (0, import_react27.useCallback)(() => {
3723
+ localStorage.removeItem(STORAGE_KEY);
3724
+ setToken(null);
3725
+ }, []);
3726
+ if (!token) {
3727
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ThemeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(LoginForm, { onLogin: handleLogin, serverUrl }) });
3728
+ }
3729
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ThemeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3730
+ DevShellInner,
3731
+ {
3732
+ serverUrl,
3733
+ title,
3734
+ token,
3735
+ onLogout: handleLogout,
3736
+ children
3737
+ }
3738
+ ) });
3739
+ }
3740
+
3741
+ // src/composites/mount-addon-page.tsx
3742
+ function mountAddonPage(PageComponent, options = {}) {
3743
+ const {
3744
+ serverUrl = "https://localhost:4443",
3745
+ title,
3746
+ rootId = "root"
3747
+ } = options;
3748
+ const root = document.getElementById(rootId);
3749
+ if (!root) {
3750
+ console.error(`[mountAddonPage] Element #${rootId} not found`);
3751
+ return;
3752
+ }
3753
+ (0, import_client2.createRoot)(root).render(
3754
+ (0, import_react28.createElement)(DevShell, {
3755
+ serverUrl,
3756
+ title,
3757
+ children: ({ trpc, theme }) => (0, import_react28.createElement)(PageComponent, {
3758
+ trpc,
3759
+ theme: { isDark: theme.resolvedMode === "dark" },
3760
+ navigate: (path) => {
3761
+ console.log("[dev] navigate:", path);
3762
+ }
3763
+ })
3764
+ })
3765
+ );
3766
+ }
2023
3767
  // Annotate the CommonJS export names for ESM import in node:
2024
3768
  0 && (module.exports = {
2025
3769
  AppShell,
2026
3770
  Badge,
2027
3771
  Button,
3772
+ CLASS_COLORS,
2028
3773
  Card,
2029
3774
  Checkbox,
2030
3775
  CodeBlock,
2031
3776
  ConfirmDialog,
3777
+ DEFAULT_CLASS_COLORS,
3778
+ DEFAULT_COLOR,
2032
3779
  DataTable,
3780
+ DetectionCanvas,
3781
+ DetectionResultTree,
3782
+ DevShell,
2033
3783
  DeviceCard,
2034
3784
  DeviceGrid,
2035
3785
  Dialog,
@@ -2048,22 +3798,30 @@ function DeviceGrid({
2048
3798
  FloatingPanel,
2049
3799
  FormField,
2050
3800
  IconButton,
3801
+ ImageSelector,
3802
+ InferenceConfigSelector,
2051
3803
  Input,
2052
3804
  KeyValueList,
2053
3805
  Label,
3806
+ LoginForm,
2054
3807
  PageHeader,
3808
+ PipelineBuilder,
3809
+ PipelineRuntimeSelector,
3810
+ PipelineStep,
2055
3811
  Popover,
2056
3812
  PopoverContent,
2057
3813
  PopoverTrigger,
2058
3814
  ProviderBadge,
2059
3815
  ScrollArea,
2060
3816
  Select,
3817
+ SemanticBadge,
2061
3818
  Separator,
2062
3819
  Sidebar,
2063
3820
  SidebarItem,
2064
3821
  Skeleton,
2065
3822
  StatCard,
2066
3823
  StatusBadge,
3824
+ StepTimings,
2067
3825
  Switch,
2068
3826
  Tabs,
2069
3827
  TabsContent,
@@ -2073,14 +3831,18 @@ function DeviceGrid({
2073
3831
  Tooltip,
2074
3832
  TooltipContent,
2075
3833
  TooltipTrigger,
3834
+ VersionBadge,
2076
3835
  cn,
2077
3836
  createTheme,
2078
3837
  darkColors,
2079
3838
  defaultTheme,
3839
+ getClassColor,
2080
3840
  lightColors,
3841
+ mountAddonPage,
2081
3842
  providerIcons,
2082
3843
  statusIcons,
2083
3844
  themeToCss,
3845
+ useDevShell,
2084
3846
  useThemeMode
2085
3847
  });
2086
3848
  //# sourceMappingURL=index.cjs.map