@camstack/ui-library 1.2.245 → 1.2.246

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
@@ -1707,41 +1707,6 @@ var CookingPot = createLucideIcon("cooking-pot", [
1707
1707
  * This source code is licensed under the ISC license.
1708
1708
  * See the LICENSE file in the root directory of this source tree.
1709
1709
  */
1710
- var CopyPlus = createLucideIcon("copy-plus", [
1711
- ["line", {
1712
- x1: "15",
1713
- x2: "15",
1714
- y1: "12",
1715
- y2: "18",
1716
- key: "1p7wdc"
1717
- }],
1718
- ["line", {
1719
- x1: "12",
1720
- x2: "18",
1721
- y1: "15",
1722
- y2: "15",
1723
- key: "1nscbv"
1724
- }],
1725
- ["rect", {
1726
- width: "14",
1727
- height: "14",
1728
- x: "8",
1729
- y: "8",
1730
- rx: "2",
1731
- ry: "2",
1732
- key: "17jyea"
1733
- }],
1734
- ["path", {
1735
- d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",
1736
- key: "zix9uf"
1737
- }]
1738
- ]);
1739
- /**
1740
- * @license lucide-react v0.576.0 - ISC
1741
- *
1742
- * This source code is licensed under the ISC license.
1743
- * See the LICENSE file in the root directory of this source tree.
1744
- */
1745
1710
  var Copy = createLucideIcon("copy", [["rect", {
1746
1711
  width: "14",
1747
1712
  height: "14",
@@ -7914,14 +7879,6 @@ var usePipelineExecutorGetGlobalPipelineConfig = trpc.pipelineExecutor.getGlobal
7914
7879
  var usePipelineExecutorGetOrchestratorConfigSchema = trpc.pipelineExecutor.getOrchestratorConfigSchema.useQuery;
7915
7880
  /** Generated alias around `trpc.pipelineExecutor.validatePipeline.useQuery`. */
7916
7881
  var usePipelineExecutorValidatePipeline = trpc.pipelineExecutor.validatePipeline.useQuery;
7917
- /** Generated alias around `trpc.pipelineExecutor.listTemplates.useQuery`. */
7918
- var usePipelineExecutorListTemplates = trpc.pipelineExecutor.listTemplates.useQuery;
7919
- /** Generated alias around `trpc.pipelineExecutor.saveTemplate.useMutation`. */
7920
- var usePipelineExecutorSaveTemplate = trpc.pipelineExecutor.saveTemplate.useMutation;
7921
- /** Generated alias around `trpc.pipelineExecutor.updateTemplate.useMutation`. */
7922
- var usePipelineExecutorUpdateTemplate = trpc.pipelineExecutor.updateTemplate.useMutation;
7923
- /** Generated alias around `trpc.pipelineExecutor.deleteTemplate.useMutation`. */
7924
- var usePipelineExecutorDeleteTemplate = trpc.pipelineExecutor.deleteTemplate.useMutation;
7925
7882
  /** Generated alias around `trpc.pipelineExecutor.getCapabilities.useQuery`. */
7926
7883
  var usePipelineExecutorGetCapabilities = trpc.pipelineExecutor.getCapabilities.useQuery;
7927
7884
  /** Generated alias around `trpc.pipelineExecutor.getAddonModels.useQuery`. */
@@ -8068,14 +8025,6 @@ var usePipelineOrchestratorSetCameraSensors = trpc.pipelineOrchestrator.setCamer
8068
8025
  var usePipelineOrchestratorReconcileFromDisk = trpc.pipelineOrchestrator.reconcileFromDisk.useMutation;
8069
8026
  /** Generated alias around `trpc.pipelineOrchestrator.getReconcileFromDiskStatus.useQuery`. */
8070
8027
  var usePipelineOrchestratorGetReconcileFromDiskStatus = trpc.pipelineOrchestrator.getReconcileFromDiskStatus.useQuery;
8071
- /** Generated alias around `trpc.pipelineOrchestrator.listTemplates.useQuery`. */
8072
- var usePipelineOrchestratorListTemplates = trpc.pipelineOrchestrator.listTemplates.useQuery;
8073
- /** Generated alias around `trpc.pipelineOrchestrator.saveTemplate.useMutation`. */
8074
- var usePipelineOrchestratorSaveTemplate = trpc.pipelineOrchestrator.saveTemplate.useMutation;
8075
- /** Generated alias around `trpc.pipelineOrchestrator.updateTemplate.useMutation`. */
8076
- var usePipelineOrchestratorUpdateTemplate = trpc.pipelineOrchestrator.updateTemplate.useMutation;
8077
- /** Generated alias around `trpc.pipelineOrchestrator.deleteTemplate.useMutation`. */
8078
- var usePipelineOrchestratorDeleteTemplate = trpc.pipelineOrchestrator.deleteTemplate.useMutation;
8079
8028
  /** Generated alias around `trpc.pipelineOrchestrator.getDeviceSettingsContribution.useQuery`. */
8080
8029
  var usePipelineOrchestratorGetDeviceSettingsContribution = trpc.pipelineOrchestrator.getDeviceSettingsContribution.useQuery;
8081
8030
  /** Generated alias around `trpc.pipelineOrchestrator.getDeviceLiveContribution.useQuery`. */
@@ -28746,6 +28695,45 @@ function AddonGlobalSettingsForm({ trpc, addonId, nodeId, title, disabled, onAft
28746
28695
  });
28747
28696
  }
28748
28697
  //#endregion
28698
+ //#region src/composites/accelerator-label.ts
28699
+ function names(device) {
28700
+ const backend = device.backend.toLowerCase();
28701
+ const dev = device.device.toLowerCase();
28702
+ if (device.key === "cpu" || dev === "cpu" || backend === "cpu") return {
28703
+ label: "CPU",
28704
+ short: "CPU"
28705
+ };
28706
+ if (dev === "npu") return {
28707
+ label: "Intel NPU",
28708
+ short: "NPU"
28709
+ };
28710
+ if (dev === "gpu" || dev === "igpu") return {
28711
+ label: "Intel iGPU",
28712
+ short: "iGPU"
28713
+ };
28714
+ if (backend === "edgetpu") return {
28715
+ label: "Coral Edge TPU",
28716
+ short: "Coral"
28717
+ };
28718
+ if (backend === "coreml") return {
28719
+ label: "Apple Neural Engine",
28720
+ short: "ANE"
28721
+ };
28722
+ const pretty = `${device.backend} ${device.device}`.trim().replace(/\b\w/g, (c) => c.toUpperCase());
28723
+ return {
28724
+ label: pretty.length > 0 ? pretty : device.key,
28725
+ short: device.backend || device.key
28726
+ };
28727
+ }
28728
+ /** "Intel NPU", "Apple Neural Engine", "Coral Edge TPU", "CPU" — Auto tune's names too. */
28729
+ function acceleratorLabel(device) {
28730
+ return names(device).label;
28731
+ }
28732
+ /** "NPU", "ANE", "Coral", "CPU" — chips and tabs. */
28733
+ function acceleratorShortLabel(device) {
28734
+ return names(device).short;
28735
+ }
28736
+ //#endregion
28749
28737
  //#region src/composites/grouped-model-selector.tsx
28750
28738
  /**
28751
28739
  * GroupedModelSelector — the shared Family → Tier → Variant model picker.
@@ -29973,9 +29961,9 @@ function RootSuppliedModelNote({ rootModel, stepModelId }) {
29973
29961
  /**
29974
29962
  * AgentStepEditor — one step on ONE accelerator: its model and its settings,
29975
29963
  * as a single node-level layer. Model Studio's "settings for this installed
29976
- * model" dialog uses it. The pipeline pages use the step inspector, which lays
29977
- * the same parts out across accelerators and layers (D754/D756); the settings
29978
- * form here is that inspector's `LayeredSettingsForm` with one layer.
29964
+ * model" dialog uses it. The pipeline pages use the step matrix's panel, which lays
29965
+ * the same parts out across accelerators and layers (D754/D757); the settings
29966
+ * form here is that panel's `LayeredSettingsForm` with one layer.
29979
29967
  */
29980
29968
  function AgentStepEditor({ addon, agentDefault, onChangeAgentConfig, engineFormat, effectiveModelId, rootModel }) {
29981
29969
  const modelsForFormat = (0, react$1.useMemo)(() => stepModelOptions(addon.models, engineFormat), [addon.models, engineFormat]);
@@ -55405,25 +55393,46 @@ function ClassChips({ inputs, outputs }) {
55405
55393
  * the first column; each device's real format is shown per sub-column instead
55406
55394
  * ("Coral Edge TPU · tflite").
55407
55395
  */
55396
+ /** The per-accelerator lines of a column header: name, format, model, off. */
55397
+ function AgentColumnLines({ column }) {
55398
+ const name = column.deviceLabel ?? column.deviceKey;
55399
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
55400
+ (name !== void 0 || column.disabled === true) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
55401
+ className: "flex min-w-0 items-center gap-1",
55402
+ children: [name !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
55403
+ className: "truncate text-[10px] font-medium text-foreground/90",
55404
+ children: name
55405
+ }), column.disabled === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
55406
+ className: "shrink-0 rounded bg-muted px-1 text-[8px] font-semibold uppercase text-foreground-subtle",
55407
+ children: "off"
55408
+ })]
55409
+ }),
55410
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55411
+ className: "truncate text-[9px] text-foreground-subtle",
55412
+ children: column.engineLabel
55413
+ }),
55414
+ column.detail !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55415
+ className: "truncate font-mono text-[9px] text-foreground-subtle",
55416
+ title: column.detail,
55417
+ children: column.detail
55418
+ })
55419
+ ] });
55420
+ }
55408
55421
  function GroupedAgentHeader({ group }) {
55422
+ const nodeLabel = group.columns[0]?.nodeLabel ?? group.agentNodeId;
55409
55423
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
55424
+ role: "columnheader",
55410
55425
  className: "sticky top-0 z-10 min-w-0 border-b border-l border-border bg-muted/60",
55411
55426
  style: { gridColumn: `span ${group.columns.length}` },
55412
55427
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55413
55428
  className: "truncate px-3 pt-2 pb-1 text-xs font-semibold text-foreground",
55414
- children: group.agentNodeId
55429
+ children: nodeLabel
55415
55430
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55416
55431
  className: "mt-1 flex border-t border-border/50",
55417
- children: group.columns.map((c, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
55418
- className: cn("min-w-0 flex-1 px-3 py-1", i > 0 && "border-l border-border/50"),
55419
- title: `${c.deviceLabel ?? c.deviceKey} · ${c.engineLabel}`,
55420
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55421
- className: "truncate text-[10px] font-medium text-foreground/90",
55422
- children: c.deviceLabel ?? c.deviceKey
55423
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55424
- className: "truncate text-[9px] text-foreground-subtle",
55425
- children: c.engineLabel
55426
- })]
55432
+ children: group.columns.map((c, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55433
+ className: cn("min-w-0 flex-1 px-3 py-1", i > 0 && "border-l border-border/50", c.disabled === true && "opacity-70"),
55434
+ title: `${c.deviceLabel ?? c.deviceKey} · ${c.engineLabel}${c.detail ? ` · ${c.detail}` : ""}`,
55435
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AgentColumnLines, { column: c })
55427
55436
  }, agentColumnKey(c)))
55428
55437
  })]
55429
55438
  });
@@ -55527,25 +55536,30 @@ function cellButtonClass(state, isSelected, extra) {
55527
55536
  //#endregion
55528
55537
  //#region src/composites/device-step-matrix.tsx
55529
55538
  /**
55530
- * DeviceStepMatrix — the READ-ONLY "Compare" view of the step inspector
55531
- * (D756): one hierarchical steps × accelerators matrix showing what every
55532
- * accelerator runs for every step, side by side.
55533
- *
55534
- * Column 1 (STICKY): the indented step tree (slot + name + class-flow chips).
55535
- * Columns 2..N (one per accelerator): the model chip, "⚠ skip" when the step
55536
- * runs on the camera but the accelerator has no compatible model, or "—".
55537
- * A click selects that (step, accelerator) in the inspector — the matrix
55538
- * itself edits nothing (it used to host a slide-over editor that scrolled
55539
- * away with the page).
55540
- *
55541
- * Responsive, container-measured (never a global media query, so it also works
55542
- * embedded):
55543
- * A) wide — sticky step column + node columns in an `overflow-x:auto` box, so
55544
- * narrow viewports scroll the NODES sideways while col-1 stays pinned and
55545
- * the page body never scrolls horizontally.
55546
- * B) narrow — below the width needed for ≥2 node columns, collapse to a
55547
- * single-node view: a node selector picks ONE node and the matrix renders
55548
- * `step-tree × that-one-node`.
55539
+ * DeviceStepMatrix — the step × accelerator TABLE of a detection pipeline
55540
+ * (D757): the node's Pipeline tab and the camera's Detection pipeline → Steps
55541
+ * are this matrix, and a click on a cell (or a step's header) opens that step's
55542
+ * settings in a docked panel (`StepDetailDock`) that overlays the page instead
55543
+ * of reflowing the matrix.
55544
+ *
55545
+ * Column 1 (STICKY): the indented step tree — slot, name, class-flow chips,
55546
+ * and an optional badge (the camera's gate). The header is a button: it
55547
+ * opens the step on the selected accelerator.
55548
+ * Columns 2..N: one per accelerator, grouped under their node; the header
55549
+ * names the accelerator, its format, the model it elects, and "off" for a
55550
+ * switched-off one. A cell shows the model that runs there, or "⚠ skip",
55551
+ * "→ iGPU", "root", "cluster", "ovr", "—".
55552
+ *
55553
+ * Keyboard: ONE Tab stop (a roving tabindex over the step headers and cells);
55554
+ * arrows move, Home / End go to the row's ends, Enter or Space opens.
55555
+ *
55556
+ * `reserveRightPx`: the width a docked panel covers at the right of the
55557
+ * viewport. The matrix adds a trailing spacer that wide (so any column can
55558
+ * scroll clear of it) and scrolls the selected cell into the uncovered part —
55559
+ * it never shrinks, so nothing reflows when the panel opens.
55560
+ *
55561
+ * Responsive, container-measured: narrower than the step column plus two
55562
+ * columns, the matrix shows ONE node at a time with a node selector.
55549
55563
  */
55550
55564
  /** The sticky step column. */
55551
55565
  var STEP_COL_REM = 18;
@@ -55556,11 +55570,41 @@ var STEP_COL_PX = STEP_COL_REM * 16;
55556
55570
  * step column and lets the device columns scroll horizontally beside it.
55557
55571
  */
55558
55572
  var STEP_COL_SINGLE_REM = 11;
55559
- function StepCell({ node, depth }) {
55573
+ /** Where a key press moves the roving focus in a `rows × cols` grid. */
55574
+ function nextGridPosition(key, pos, rows, cols) {
55575
+ const clamp = (r, c) => ({
55576
+ r: Math.max(0, Math.min(rows - 1, r)),
55577
+ c: Math.max(0, Math.min(cols - 1, c))
55578
+ });
55579
+ switch (key) {
55580
+ case "ArrowRight": return clamp(pos.r, pos.c + 1);
55581
+ case "ArrowLeft": return clamp(pos.r, pos.c - 1);
55582
+ case "ArrowDown": return clamp(pos.r + 1, pos.c);
55583
+ case "ArrowUp": return clamp(pos.r - 1, pos.c);
55584
+ case "Home": return clamp(pos.r, 0);
55585
+ case "End": return clamp(pos.r, cols - 1);
55586
+ case "PageDown": return clamp(rows - 1, pos.c);
55587
+ case "PageUp": return clamp(0, pos.c);
55588
+ default: return null;
55589
+ }
55590
+ }
55591
+ function StepCell({ node, depth, r, tabbable, selected, badge, onClick, sticky }) {
55560
55592
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55561
- className: "sticky left-0 z-10 flex flex-col gap-2 border-b border-border bg-surface px-3 py-2 text-xs min-w-0",
55562
- style: { paddingLeft: `${12 + depth * 14}px` },
55563
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepLabel, { node })
55593
+ role: "rowheader",
55594
+ className: cn("min-w-0 border-b border-border bg-surface", sticky && "sticky left-0 z-10"),
55595
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
55596
+ type: "button",
55597
+ "data-r": r,
55598
+ "data-c": 0,
55599
+ "data-step": node.addonId,
55600
+ "data-col": "",
55601
+ tabIndex: tabbable ? 0 : -1,
55602
+ "aria-current": selected ? "true" : void 0,
55603
+ onClick: onClick ? (e) => onClick(e.detail === 0) : void 0,
55604
+ className: cn("flex h-full w-full min-w-0 items-start gap-2 px-3 py-2 text-left text-xs outline-none", "hover:bg-muted/40 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary", selected && "bg-primary/10 shadow-[inset_3px_0_0_var(--color-primary)]"),
55605
+ style: { paddingLeft: `${12 + depth * 14}px` },
55606
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepLabel, { node }), badge]
55607
+ })
55564
55608
  });
55565
55609
  }
55566
55610
  /**
@@ -55590,68 +55634,179 @@ function NodeSelectorBar({ nodes, selected, onSelect }) {
55590
55634
  role: "tab",
55591
55635
  "aria-selected": isSelected,
55592
55636
  onClick: () => onSelect(a.agentNodeId),
55593
- title: `${a.agentNodeId} · ${a.engineLabel}`,
55637
+ title: `${a.columns[0]?.nodeLabel ?? a.agentNodeId} · ${a.engineLabel}`,
55594
55638
  className: cn("px-2.5 py-1 transition-colors", isSelected ? "bg-primary/15 text-primary" : "bg-transparent text-foreground-subtle hover:bg-surface-hover hover:text-foreground", idx > 0 && "border-l border-border"),
55595
- children: a.agentNodeId
55639
+ children: a.columns[0]?.nodeLabel ?? a.agentNodeId
55596
55640
  }, a.agentNodeId);
55597
55641
  })
55598
55642
  })
55599
55643
  ]
55600
55644
  });
55601
55645
  }
55602
- function MatrixGrid({ tree, agents, getCellState, onCellClick, selectedCell, single }) {
55646
+ var MatrixGrid = (0, react$1.forwardRef)(function MatrixGrid({ tree, agents, getCellState, onCellClick, onStepClick, stepBadge, selectedCell, single, reserveRightPx, label }, ref) {
55603
55647
  const rows = (0, react$1.useMemo)(() => flattenTree(tree), [tree]);
55604
55648
  const groups = (0, react$1.useMemo)(() => groupAgentColumns(agents), [agents]);
55649
+ const boxRef = (0, react$1.useRef)(null);
55650
+ const stepColRem = single ? STEP_COL_SINGLE_REM : STEP_COL_REM;
55651
+ const gridTemplate = `${stepColRem}rem repeat(${agents.length}, 13rem)`;
55652
+ const cols = agents.length + 1;
55653
+ const [stickySteps, setStickySteps] = (0, react$1.useState)(true);
55654
+ (0, react$1.useLayoutEffect)(() => {
55655
+ const box = boxRef.current;
55656
+ if (!box || reserveRightPx <= 0) {
55657
+ setStickySteps(true);
55658
+ return;
55659
+ }
55660
+ const measure = () => {
55661
+ const left = box.getBoundingClientRect().left;
55662
+ setStickySteps(window.innerWidth - reserveRightPx - left >= stepColRem * 16 + 208 + 24);
55663
+ };
55664
+ measure();
55665
+ window.addEventListener("resize", measure);
55666
+ return () => window.removeEventListener("resize", measure);
55667
+ }, [reserveRightPx, stepColRem]);
55668
+ const selectedPos = (0, react$1.useMemo)(() => {
55669
+ if (selectedCell === null) return null;
55670
+ const r = rows.findIndex((row) => row.node.addonId === selectedCell.addonId);
55671
+ if (r < 0) return null;
55672
+ const c = agents.findIndex((a) => agentColumnKey(a) === agentColumnKey(selectedCell));
55673
+ return {
55674
+ r,
55675
+ c: c < 0 ? 0 : c + 1
55676
+ };
55677
+ }, [
55678
+ selectedCell,
55679
+ rows,
55680
+ agents
55681
+ ]);
55682
+ const [focusPos, setFocusPos] = (0, react$1.useState)({
55683
+ r: 0,
55684
+ c: 0
55685
+ });
55686
+ (0, react$1.useEffect)(() => {
55687
+ if (selectedPos !== null) setFocusPos(selectedPos);
55688
+ }, [selectedPos]);
55689
+ const tab = focusPos.r < rows.length && focusPos.c < cols ? focusPos : {
55690
+ r: 0,
55691
+ c: 0
55692
+ };
55693
+ const focusAt = (r, c) => {
55694
+ boxRef.current?.querySelector(`[data-r="${r}"][data-c="${c}"]`)?.focus();
55695
+ };
55696
+ (0, react$1.useImperativeHandle)(ref, () => ({ focusCell: (addonId, columnKey) => {
55697
+ const r = rows.findIndex((row) => row.node.addonId === addonId);
55698
+ if (r < 0) return;
55699
+ const index = columnKey === null ? -1 : agents.findIndex((a) => agentColumnKey(a) === columnKey);
55700
+ const c = index < 0 ? 0 : index + 1;
55701
+ setFocusPos({
55702
+ r,
55703
+ c
55704
+ });
55705
+ focusAt(r, c);
55706
+ } }), [rows, agents]);
55707
+ (0, react$1.useLayoutEffect)(() => {
55708
+ if (selectedPos === null) return;
55709
+ const box = boxRef.current;
55710
+ const el = box?.querySelector(`[data-r="${selectedPos.r}"][data-c="${selectedPos.c}"]`);
55711
+ if (!box || !el) return;
55712
+ if (typeof el.scrollIntoView === "function") el.scrollIntoView({
55713
+ block: "nearest",
55714
+ inline: "nearest"
55715
+ });
55716
+ if (reserveRightPx <= 0) return;
55717
+ const limit = window.innerWidth - reserveRightPx - 12;
55718
+ const right = el.getBoundingClientRect().right;
55719
+ if (right > limit) box.scrollLeft += right - limit;
55720
+ }, [
55721
+ selectedPos,
55722
+ reserveRightPx,
55723
+ stickySteps
55724
+ ]);
55725
+ const onKeyDown = (e) => {
55726
+ if (!(e.target instanceof HTMLElement) || e.target.dataset.r === void 0) return;
55727
+ const pos = {
55728
+ r: Number(e.target.dataset.r),
55729
+ c: Number(e.target.dataset.c)
55730
+ };
55731
+ const next = nextGridPosition(e.key, pos, rows.length, cols);
55732
+ if (next === null) return;
55733
+ e.preventDefault();
55734
+ setFocusPos(next);
55735
+ focusAt(next.r, next.c);
55736
+ };
55605
55737
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55738
+ ref: boxRef,
55606
55739
  className: "overflow-auto rounded border border-border",
55607
55740
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
55608
- className: "grid w-max",
55609
- style: { gridTemplateColumns: `${single ? STEP_COL_SINGLE_REM : STEP_COL_REM}rem repeat(${agents.length}, 13rem)` },
55610
- children: [
55611
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55612
- className: "sticky left-0 top-0 z-20 border-b border-border bg-muted/60 px-3 py-2 text-[10px] uppercase tracking-widest text-foreground-subtle",
55613
- children: "Step"
55614
- }),
55615
- groups.map((group) => group.grouped ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupedAgentHeader, { group }, group.agentNodeId) : group.columns.map((col) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
55616
- className: "sticky top-0 z-10 min-w-0 border-b border-l border-border bg-muted/60 px-3 py-2",
55741
+ className: "flex w-max",
55742
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
55743
+ role: "grid",
55744
+ "aria-label": label,
55745
+ "aria-rowcount": rows.length + 1,
55746
+ className: "grid w-max",
55747
+ style: { gridTemplateColumns: gridTemplate },
55748
+ onKeyDown,
55749
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
55750
+ role: "row",
55751
+ className: "contents",
55617
55752
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55618
- className: "truncate text-xs font-semibold text-foreground",
55619
- children: col.agentNodeId
55620
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55621
- className: "truncate text-[10px] text-foreground-subtle",
55622
- children: col.engineLabel
55753
+ role: "columnheader",
55754
+ className: cn("top-0 z-20 border-b border-border bg-muted/60 px-3 py-2 text-[10px] uppercase tracking-widest text-foreground-subtle", stickySteps ? "sticky left-0" : "sticky"),
55755
+ children: "Step"
55756
+ }), groups.map((group) => group.grouped ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupedAgentHeader, { group }, group.agentNodeId) : group.columns.map((col) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
55757
+ role: "columnheader",
55758
+ className: cn("sticky top-0 z-10 min-w-0 border-b border-l border-border bg-muted/60 px-3 py-2", col.disabled === true && "opacity-70"),
55759
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55760
+ className: "truncate text-xs font-semibold text-foreground",
55761
+ children: col.nodeLabel ?? col.agentNodeId
55762
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AgentColumnLines, { column: col })]
55763
+ }, agentColumnKey(col))))]
55764
+ }), rows.map(({ node, depth }, r) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
55765
+ role: "row",
55766
+ className: "contents",
55767
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepCell, {
55768
+ node,
55769
+ depth,
55770
+ r,
55771
+ tabbable: tab.r === r && tab.c === 0,
55772
+ selected: selectedCell?.addonId === node.addonId,
55773
+ badge: stepBadge?.(node.addonId),
55774
+ onClick: onStepClick ? (kb) => onStepClick(node.addonId, kb) : void 0,
55775
+ sticky: stickySteps
55776
+ }), agents.map((a, i) => {
55777
+ const state = getCellState(node.addonId, a);
55778
+ const isSelected = selectedCell !== null && selectedCell.addonId === node.addonId && agentColumnKey(selectedCell) === agentColumnKey(a);
55779
+ const actionable = state.kind === "enabled" || state.kind === "skip";
55780
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55781
+ role: "gridcell",
55782
+ className: "min-w-0 border-b border-l border-border",
55783
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
55784
+ type: "button",
55785
+ "data-r": r,
55786
+ "data-c": i + 1,
55787
+ "data-step": node.addonId,
55788
+ "data-col": agentColumnKey(a),
55789
+ tabIndex: tab.r === r && tab.c === i + 1 ? 0 : -1,
55790
+ "aria-disabled": !actionable,
55791
+ "aria-selected": isSelected,
55792
+ onClick: (e) => {
55793
+ if (actionable) onCellClick(node.addonId, a, e.detail === 0);
55794
+ },
55795
+ className: cellButtonClass(state, isSelected, cn("h-full w-full min-w-0 overflow-hidden px-3 py-2 outline-none", "focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary", !actionable && "cursor-default hover:bg-transparent", a.disabled === true && "opacity-70")),
55796
+ children: renderCellContent(state)
55797
+ })
55798
+ }, agentColumnKey(a));
55623
55799
  })]
55624
- }, agentColumnKey(col)))),
55625
- rows.map(({ node, depth }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MatrixRow, {
55626
- node,
55627
- depth,
55628
- agents,
55629
- getCellState,
55630
- onCellClick,
55631
- selectedCell
55632
- }, node.addonId))
55633
- ]
55800
+ }, node.addonId))]
55801
+ }), reserveRightPx > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55802
+ "aria-hidden": true,
55803
+ style: { width: reserveRightPx },
55804
+ className: "shrink-0"
55805
+ })]
55634
55806
  })
55635
55807
  });
55636
- }
55637
- function MatrixRow({ node, depth, agents, getCellState, onCellClick, selectedCell }) {
55638
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepCell, {
55639
- node,
55640
- depth
55641
- }), agents.map((a) => {
55642
- const state = getCellState(node.addonId, a);
55643
- const isSelected = selectedCell !== null && selectedCell.addonId === node.addonId && agentColumnKey(selectedCell) === agentColumnKey(a);
55644
- const actionable = state.kind === "enabled" || state.kind === "skip";
55645
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
55646
- type: "button",
55647
- disabled: !actionable,
55648
- onClick: () => onCellClick(node.addonId, a),
55649
- className: cellButtonClass(state, isSelected, cn("min-w-0 overflow-hidden border-b border-l border-border px-3 py-2", !actionable && "cursor-default hover:bg-transparent")),
55650
- children: renderCellContent(state)
55651
- }, agentColumnKey(a));
55652
- })] });
55653
- }
55654
- function DeviceStepMatrix({ tree, agents, getCellState, onCellClick, selectedCell, fallbackNodeId, onFallbackNodeChange }) {
55808
+ });
55809
+ var DeviceStepMatrix = (0, react$1.forwardRef)(function DeviceStepMatrix({ tree, agents, getCellState, onCellClick, selectedCell, fallbackNodeId, onFallbackNodeChange, onStepClick, stepBadge, reserveRightPx = 0, label = "Detection steps" }, ref) {
55655
55810
  const containerRef = (0, react$1.useRef)(null);
55656
55811
  const [width, setWidth] = (0, react$1.useState)(0);
55657
55812
  (0, react$1.useLayoutEffect)(() => {
@@ -55669,6 +55824,15 @@ function DeviceStepMatrix({ tree, agents, getCellState, onCellClick, selectedCel
55669
55824
  if (nodes.length === 0) return null;
55670
55825
  return nodes.find((n) => n.agentNodeId === fallbackNodeId) ?? nodes[0] ?? null;
55671
55826
  }, [nodes, fallbackNodeId]);
55827
+ const selectedNodeId = selectedCell?.agentNodeId;
55828
+ (0, react$1.useEffect)(() => {
55829
+ if (single && selectedNodeId !== void 0 && selectedNodeId !== fallbackNodeId) onFallbackNodeChange(selectedNodeId);
55830
+ }, [
55831
+ single,
55832
+ selectedNodeId,
55833
+ fallbackNodeId,
55834
+ onFallbackNodeChange
55835
+ ]);
55672
55836
  const shownAgents = single && selectedNode ? selectedNode.columns : agents;
55673
55837
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
55674
55838
  ref: containerRef,
@@ -55678,15 +55842,20 @@ function DeviceStepMatrix({ tree, agents, getCellState, onCellClick, selectedCel
55678
55842
  selected: selectedNode.agentNodeId,
55679
55843
  onSelect: onFallbackNodeChange
55680
55844
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MatrixGrid, {
55845
+ ref,
55681
55846
  tree,
55682
55847
  agents: shownAgents,
55683
55848
  getCellState,
55684
55849
  onCellClick,
55850
+ onStepClick,
55851
+ stepBadge,
55685
55852
  selectedCell,
55686
- single: single && selectedNode !== null
55853
+ single: single && selectedNode !== null,
55854
+ reserveRightPx,
55855
+ label
55687
55856
  })]
55688
55857
  });
55689
- }
55858
+ });
55690
55859
  //#endregion
55691
55860
  //#region src/composites/discovery-panel.tsx
55692
55861
  function DiscoveryPanel({ deviceId, className = "", onOpenChild }) {
@@ -56625,43 +56794,13 @@ function PhaseIcon({ phase, ...props }) {
56625
56794
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { ...props });
56626
56795
  }
56627
56796
  //#endregion
56628
- //#region src/lib/validate-template.ts
56629
- function validateTemplate(steps, schema) {
56630
- const availableAddonIds = new Set(schema.slots.flatMap((s) => s.addons.map((a) => a.id)));
56631
- const warnings = [];
56632
- function validateStep(step) {
56633
- if (!availableAddonIds.has(step.addonId)) {
56634
- warnings.push(`Addon "${step.addonId}" is no longer available — step removed`);
56635
- return null;
56636
- }
56637
- const addon = schema.slots.flatMap((s) => s.addons).find((a) => a.id === step.addonId);
56638
- let modelId = step.modelId;
56639
- if (addon && !addon.models.some((m) => m.id === modelId)) {
56640
- const fallback = addon.defaultModelId;
56641
- warnings.push(`Model "${modelId}" not available for ${step.addonId} — using "${fallback}"`);
56642
- modelId = fallback;
56643
- }
56644
- const validChildren = step.children.map((c) => validateStep(c)).filter((c) => c !== null);
56645
- return {
56646
- ...step,
56647
- modelId,
56648
- children: validChildren
56649
- };
56650
- }
56651
- const validSteps = steps.map((s) => validateStep(s)).filter((s) => s !== null);
56652
- return {
56653
- valid: warnings.length === 0,
56654
- steps: validSteps,
56655
- warnings
56656
- };
56657
- }
56658
- //#endregion
56659
56797
  //#region src/composites/pipeline-builder.tsx
56660
56798
  /**
56661
- * PipelineBuilder — orchestrates the pipeline step tree with template management.
56799
+ * PipelineBuilder — orchestrates the pipeline step tree.
56662
56800
  *
56663
- * Renders: template bar (save/load/delete), step tree from schema with nested
56664
- * children, placeholder steps for disabled addons, all driven by API schema.
56801
+ * Renders the step tree from schema with nested children and placeholder steps
56802
+ * for disabled addons, all driven by API schema. There are no saved pipeline
56803
+ * templates (D759): the tree is the caller's state.
56665
56804
  */
56666
56805
  function buildSchemaMap(schema) {
56667
56806
  const map = /* @__PURE__ */ new Map();
@@ -56713,31 +56852,9 @@ function PlaceholderStep({ addon, onClick }) {
56713
56852
  })
56714
56853
  });
56715
56854
  }
56716
- function PipelineBuilder({ schema, steps, onChange, templates, selectedTemplateId, onSelectTemplate, onSaveTemplate, onUpdateTemplate, onDeleteTemplate, readOnly = false, excludeAddons = [], hideTemplates = false, showOnlyEnabled = false, showSlotLabels = false }) {
56855
+ function PipelineBuilder({ schema, steps, onChange, readOnly = false, excludeAddons = [], showOnlyEnabled = false, showSlotLabels = false }) {
56717
56856
  const excluded = (0, react$1.useMemo)(() => new Set(excludeAddons), [excludeAddons]);
56718
56857
  const schemaMap = (0, react$1.useMemo)(() => buildSchemaMap(schema), [schema]);
56719
- const [warnings, setWarnings] = (0, react$1.useState)([]);
56720
- const dirty = selectedTemplateId ? JSON.stringify(steps) !== JSON.stringify(templates.find((t) => t.id === selectedTemplateId)?.steps) : false;
56721
- function handleSelectTemplate(e) {
56722
- const id = e.target.value || null;
56723
- if (id) {
56724
- const tpl = templates.find((t) => t.id === id);
56725
- if (tpl) setWarnings([...validateTemplate(tpl.steps, schema).warnings]);
56726
- } else setWarnings([]);
56727
- onSelectTemplate(id);
56728
- }
56729
- function handleSave() {
56730
- if (selectedTemplateId) onUpdateTemplate(selectedTemplateId, steps);
56731
- }
56732
- function handleSaveAs() {
56733
- const name = window.prompt("Template name:");
56734
- if (name?.trim()) onSaveTemplate(name.trim(), steps);
56735
- }
56736
- function handleDelete() {
56737
- if (!selectedTemplateId) return;
56738
- const tpl = templates.find((t) => t.id === selectedTemplateId);
56739
- if (tpl && window.confirm(`Delete template "${tpl.name}"?`)) onDeleteTemplate(selectedTemplateId);
56740
- }
56741
56858
  /**
56742
56859
  * When a step is updated, auto-enable the entire ancestor chain if a
56743
56860
  * child/grandchild was just enabled. This ensures enabling face-recognition
@@ -56907,104 +57024,42 @@ function PipelineBuilder({ schema, steps, onChange, templates, selectedTemplateI
56907
57024
  })]
56908
57025
  }, step.addonId);
56909
57026
  }
56910
- const rootSlots = schema.slots.filter((s) => s.parentSlot === null).toSorted((a, b) => a.priority - b.priority);
56911
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
57027
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
56912
57028
  className: "space-y-4",
56913
- children: [
56914
- !hideTemplates && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
56915
- className: "rounded-lg border border-border bg-surface p-3",
56916
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
56917
- className: "flex items-center gap-2",
56918
- children: [
56919
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
56920
- className: "relative flex-1 min-w-0",
56921
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
56922
- value: selectedTemplateId ?? "",
56923
- onChange: handleSelectTemplate,
56924
- 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",
56925
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
56926
- value: "",
56927
- children: "No template"
56928
- }), templates.map((t) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
56929
- value: t.id,
56930
- children: t.name
56931
- }, t.id))]
56932
- })
56933
- }),
56934
- dirty && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "h-1.5 w-1.5 rounded-full bg-amber-500 shrink-0" }),
56935
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
56936
- onClick: handleSave,
56937
- disabled: !selectedTemplateId || readOnly,
56938
- title: "Save",
56939
- className: cn("p-2 rounded-lg border border-border transition-colors", selectedTemplateId && !readOnly ? "text-foreground-subtle hover:bg-surface-hover" : "text-foreground-subtle/30 cursor-not-allowed"),
56940
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Save, { className: "h-4 w-4" })
56941
- }),
56942
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
56943
- onClick: handleSaveAs,
56944
- disabled: readOnly,
56945
- title: "Save As",
56946
- className: cn("p-2 rounded-lg border border-border transition-colors", !readOnly ? "text-foreground-subtle hover:bg-surface-hover" : "text-foreground-subtle/30 cursor-not-allowed"),
56947
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopyPlus, { className: "h-4 w-4" })
56948
- }),
56949
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
56950
- onClick: handleDelete,
56951
- disabled: !selectedTemplateId || readOnly,
56952
- title: "Delete",
56953
- className: cn("p-2 rounded-lg border border-border transition-colors", selectedTemplateId && !readOnly ? "text-foreground-subtle hover:text-danger" : "text-foreground-subtle/30 cursor-not-allowed"),
56954
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Trash2, { className: "h-4 w-4" })
56955
- })
56956
- ]
56957
- })
56958
- }),
56959
- warnings.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
56960
- className: "rounded-lg border border-amber-500/30 bg-amber-500/5 p-3 text-xs text-amber-400 space-y-1",
56961
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
56962
- className: "flex items-center justify-between",
56963
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
56964
- className: "font-medium",
56965
- children: "Template loaded with warnings:"
56966
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
56967
- onClick: () => setWarnings([]),
56968
- className: "text-amber-400/60 hover:text-amber-400",
56969
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(X, { className: "h-3.5 w-3.5" })
56970
- })]
56971
- }), warnings.map((w, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: ["• ", w] }, i))]
56972
- }),
56973
- rootSlots.map((slot) => {
56974
- const allSlotSteps = steps.filter((s) => s.slot === slot.id && !excluded.has(s.addonId));
56975
- const slotSteps = showOnlyEnabled ? allSlotSteps.filter((s) => s.enabled) : allSlotSteps;
56976
- const missingRootAddons = showOnlyEnabled ? [] : slot.addons.filter((a) => !existingIds.has(a.id) && !excluded.has(a.id));
56977
- const groups = /* @__PURE__ */ new Map();
56978
- const ungrouped = [];
56979
- for (const step of slotSteps) if (step.group) {
56980
- if (!groups.has(step.group)) groups.set(step.group, []);
56981
- groups.get(step.group).push(step);
56982
- } else ungrouped.push(step);
56983
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
56984
- className: "space-y-2",
56985
- children: [
56986
- showSlotLabels && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
56987
- className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/50",
56988
- children: ["Slot: ", slot.label]
56989
- }),
56990
- ungrouped.map((step) => renderStep(step)),
56991
- [...groups.entries()].map(([groupName, groupSteps]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
56992
- className: "space-y-2 rounded-lg border border-border/40 p-2",
56993
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
56994
- className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/50 px-1",
56995
- children: groupName
56996
- }), groupSteps.map((step) => renderStep(step))]
56997
- }, groupName)),
56998
- !readOnly && missingRootAddons.map((addon) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlaceholderStep, {
56999
- addon,
57000
- onClick: () => {
57001
- onChange([...steps, createDefaultStep(addon)]);
57002
- }
57003
- }, addon.id))
57004
- ]
57005
- }, slot.id);
57006
- })
57007
- ]
57029
+ children: schema.slots.filter((s) => s.parentSlot === null).toSorted((a, b) => a.priority - b.priority).map((slot) => {
57030
+ const allSlotSteps = steps.filter((s) => s.slot === slot.id && !excluded.has(s.addonId));
57031
+ const slotSteps = showOnlyEnabled ? allSlotSteps.filter((s) => s.enabled) : allSlotSteps;
57032
+ const missingRootAddons = showOnlyEnabled ? [] : slot.addons.filter((a) => !existingIds.has(a.id) && !excluded.has(a.id));
57033
+ const groups = /* @__PURE__ */ new Map();
57034
+ const ungrouped = [];
57035
+ for (const step of slotSteps) if (step.group) {
57036
+ if (!groups.has(step.group)) groups.set(step.group, []);
57037
+ groups.get(step.group).push(step);
57038
+ } else ungrouped.push(step);
57039
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
57040
+ className: "space-y-2",
57041
+ children: [
57042
+ showSlotLabels && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
57043
+ className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/50",
57044
+ children: ["Slot: ", slot.label]
57045
+ }),
57046
+ ungrouped.map((step) => renderStep(step)),
57047
+ [...groups.entries()].map(([groupName, groupSteps]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
57048
+ className: "space-y-2 rounded-lg border border-border/40 p-2",
57049
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
57050
+ className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/50 px-1",
57051
+ children: groupName
57052
+ }), groupSteps.map((step) => renderStep(step))]
57053
+ }, groupName)),
57054
+ !readOnly && missingRootAddons.map((addon) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlaceholderStep, {
57055
+ addon,
57056
+ onClick: () => {
57057
+ onChange([...steps, createDefaultStep(addon)]);
57058
+ }
57059
+ }, addon.id))
57060
+ ]
57061
+ }, slot.id);
57062
+ })
57008
57063
  });
57009
57064
  }
57010
57065
  //#endregion
@@ -57793,166 +57848,104 @@ function SegmentedChoice({ label, options, value, onChange }) {
57793
57848
  });
57794
57849
  }
57795
57850
  //#endregion
57796
- //#region src/composites/step-inspector/step-chips.tsx
57797
- function chipText(state) {
57798
- switch (state.kind) {
57799
- case "enabled":
57800
- if (state.suppliedByRoot !== void 0) return "root";
57801
- return state.jumpTo !== void 0 ? `→ ${state.jumpTo}` : state.modelId;
57802
- case "skip": return state.jumpTo !== void 0 ? `→ ${state.jumpTo}` : "skip";
57803
- case "disabled": return "off";
57804
- case "na": return "—";
57805
- }
57806
- }
57807
- /** What a chip MEANS — the full model id, not its shortened text. */
57808
- function chipKey(state) {
57809
- return state.kind === "enabled" ? `${state.modelId}|${chipText(state)}` : chipText(state);
57810
- }
57811
- function chipTone(state, selected) {
57812
- if (selected) return "border-primary/60 text-primary";
57813
- if (state.kind === "skip" && state.jumpTo === void 0) return "border-amber-500/40 text-amber-500";
57814
- if (state.kind === "na" || state.kind === "disabled") return "border-border/60 text-foreground-subtle/70";
57815
- return "border-border text-foreground-subtle";
57816
- }
57817
- function StepChips({ chips, selectedId }) {
57818
- const first = chips[0];
57819
- if (first !== void 0 && chips.length > 1 && chips.every((c) => chipKey(c.state) === chipKey(first.state) && !(c.state.kind === "enabled" && c.state.overridden === true) && chipTone(c.state, c.id === selectedId) === chipTone(first.state, first.id === selectedId)) && first !== void 0) {
57820
- const text = chipText(first.state);
57821
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
57822
- className: "flex flex-wrap gap-1",
57823
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
57824
- title: `${text} on ${chips.map((c) => c.label).join(", ")}`,
57825
- className: cn("inline-flex max-w-[12rem] items-center gap-1 truncate rounded border px-1 font-mono text-[9px] leading-4", chipTone(first.state, first.id === selectedId)),
57826
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
57827
- className: "truncate",
57828
- children: text
57829
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
57830
- className: "shrink-0 font-sans text-foreground-subtle/80",
57831
- children: ["×", chips.length]
57832
- })]
57833
- })
57834
- });
57835
- }
57836
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
57837
- className: "flex flex-wrap gap-1",
57838
- children: chips.map((chip) => {
57839
- const overridden = chip.state.kind === "enabled" && chip.state.overridden === true;
57840
- const text = chipText(chip.state);
57841
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
57842
- title: `${chip.label}: ${chip.state.kind === "enabled" ? chip.state.modelId : text}${overridden ? " (camera override)" : ""}`,
57843
- className: cn("inline-flex max-w-[9rem] items-center gap-0.5 truncate rounded border px-1 font-mono text-[9px] leading-4", chipTone(chip.state, chip.id === selectedId)),
57844
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
57845
- className: "truncate",
57846
- children: text
57847
- }), overridden && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
57848
- className: "text-amber-500",
57849
- "aria-hidden": true,
57850
- children: "•"
57851
- })]
57852
- }, chip.id);
57853
- })
57854
- });
57855
- }
57856
- //#endregion
57857
- //#region src/composites/step-inspector/step-inspector-layout.tsx
57851
+ //#region src/composites/step-inspector/step-detail-dock.tsx
57858
57852
  /**
57859
- * StepInspectorLayout — the master–detail frame of the step inspector.
57853
+ * StepDetailDock — where a step's settings open from the step matrix
57854
+ * (D757): docked to the page's scroll container, never inside the scrolling
57855
+ * content.
57860
57856
  *
57861
- * Desktop: the step list and the inspector side by side, each scrolling on its
57862
- * own, in a block whose height FILLS the page's scroll container below it. So
57863
- * neither pane depends on the page scroll position: the inspector's header and
57864
- * save bar are always on screen, and the selected step stays visible in the
57865
- * list. (The slide-over this replaced was absolutely positioned inside the
57866
- * matrix block — its title and its Save button scrolled away with the page.)
57867
- * In a narrow container (the camera page beside its live view, a tablet) the
57868
- * two panes stack: the list on top, a third of the height.
57857
+ * Desktop: a side panel `position: fixed` over the RIGHT edge of the nearest
57858
+ * scroll container (its top, bottom and right are measured from that box), so
57859
+ * it stays where it is while the matrix scrolls under it, and its header and
57860
+ * save bar are always on screen. It overlays — it never reserves width, so the
57861
+ * matrix does not reflow (the slide-over before D756 reserved 360 px, the
57862
+ * matrix re-measured into a single-node layout, and the clicked cell slid
57863
+ * under the panel). The matrix uses {@link STEP_DOCK_WIDTH_PX} to scroll the
57864
+ * selected column clear of it.
57869
57865
  *
57870
- * Phone: the list flows with the page; the inspector opens as a full-screen
57871
- * sheet with "‹ Steps" to go back.
57866
+ * Phone: a full-screen `Dialog` sheet.
57872
57867
  */
57873
- /** The tallest the inspector block can be without making its scroll container scroll. */
57874
- function fillHeightPx(input) {
57875
- const room = input.scrollerHeight - input.paddingBottom - input.offsetInScroller;
57876
- return Math.max(input.minPx, Math.floor(room));
57868
+ /** Width of the desktop dock — what the matrix keeps clear of it. */
57869
+ var STEP_DOCK_WIDTH_PX = 432;
57870
+ /** The dock's viewport box, from its scroll container's box. */
57871
+ function dockBox(input) {
57872
+ const right = Math.max(0, input.viewportWidth - input.scroller.right);
57873
+ const width = Math.min(input.widthPx, input.scroller.right);
57874
+ return {
57875
+ top: Math.max(0, input.scroller.top),
57876
+ height: Math.max(0, input.scroller.bottom - Math.max(0, input.scroller.top)),
57877
+ right,
57878
+ width
57879
+ };
57877
57880
  }
57878
57881
  function scrollParentOf(el) {
57879
- for (let p = el.parentElement; p !== null; p = p.parentElement) {
57882
+ for (let p = el?.parentElement ?? null; p !== null; p = p.parentElement) {
57880
57883
  const { overflowY } = getComputedStyle(p);
57881
57884
  if (overflowY === "auto" || overflowY === "scroll") return p;
57882
57885
  }
57883
57886
  return null;
57884
57887
  }
57885
- /** Height of the block so that it ends at the bottom of its scroll container. */
57886
- function useFillHeight(enabled, minPx) {
57887
- const ref = (0, react$1.useRef)(null);
57888
- const [height, setHeight] = (0, react$1.useState)(null);
57888
+ function useDockBox(anchor, enabled) {
57889
+ const [box, setBox] = (0, react$1.useState)(null);
57889
57890
  (0, react$1.useLayoutEffect)(() => {
57890
- const el = ref.current;
57891
- if (!enabled || el === null) return;
57892
- const scroller = scrollParentOf(el);
57891
+ if (!enabled) return;
57892
+ const scroller = scrollParentOf(anchor.current);
57893
57893
  const measure = () => {
57894
- const box = scroller ?? document.documentElement;
57895
- const style = getComputedStyle(box);
57896
- const top = scroller ? scroller.getBoundingClientRect().top : 0;
57897
- const scrollTop = scroller ? scroller.scrollTop : window.scrollY;
57898
- setHeight(fillHeightPx({
57899
- scrollerHeight: scroller ? scroller.clientHeight : window.innerHeight,
57900
- paddingBottom: Number.parseFloat(style.paddingBottom) || 0,
57901
- offsetInScroller: el.getBoundingClientRect().top - top + scrollTop,
57902
- minPx
57894
+ setBox(dockBox({
57895
+ scroller: scroller?.getBoundingClientRect() ?? {
57896
+ top: 0,
57897
+ bottom: window.innerHeight,
57898
+ right: window.innerWidth
57899
+ },
57900
+ viewportWidth: window.innerWidth,
57901
+ widthPx: 432
57903
57902
  }));
57904
57903
  };
57905
57904
  measure();
57906
57905
  const RO = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measure);
57907
57906
  if (scroller) RO?.observe(scroller);
57908
- if (el.parentElement) RO?.observe(el.parentElement);
57909
57907
  window.addEventListener("resize", measure);
57910
57908
  return () => {
57911
57909
  RO?.disconnect();
57912
57910
  window.removeEventListener("resize", measure);
57913
57911
  };
57914
- }, [enabled, minPx]);
57915
- return {
57916
- ref,
57917
- height
57918
- };
57912
+ }, [anchor, enabled]);
57913
+ return box;
57919
57914
  }
57920
- function StepInspectorLayout({ list, detail, mobileDetailOpen, onMobileClose, minHeightPx = 420, className }) {
57915
+ function StepDetailDock({ open, anchor, onClose, label, children }) {
57921
57916
  const isMobile = useIsMobile();
57922
- const { ref, height } = useFillHeight(!isMobile, minHeightPx);
57923
- if (isMobile) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
57924
- className: cn("space-y-3", className),
57925
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
57926
- className: SECTION_CARD,
57927
- children: list
57928
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dialog, {
57929
- open: mobileDetailOpen,
57930
- onOpenChange: (open) => !open && onMobileClose(),
57931
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogContent, {
57932
- placement: "sheet-on-mobile",
57933
- scroll: "body",
57934
- className: "h-[100dvh] max-h-none w-full max-w-full rounded-none border-0 p-0 pt-[env(safe-area-inset-top)] pb-[env(safe-area-inset-bottom)]",
57935
- children: mobileDetailOpen && detail
57936
- })
57937
- })]
57938
- });
57939
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
57940
- ref,
57941
- className: cn("@container/inspector", className),
57942
- style: height !== null ? { height } : void 0,
57943
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
57944
- className: cn("grid h-full min-h-0 gap-3", "grid-cols-1 grid-rows-[minmax(9rem,32%)_minmax(0,1fr)]", "@xl/inspector:grid-cols-[minmax(14rem,19rem)_minmax(0,1fr)] @xl/inspector:grid-rows-1"),
57945
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
57946
- className: cn(SECTION_CARD, "flex min-h-0 flex-col"),
57947
- children: list
57948
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
57949
- className: cn(SECTION_CARD, "flex min-h-0 flex-col"),
57950
- children: detail
57951
- })]
57917
+ const box = useDockBox(anchor, open && !isMobile);
57918
+ if (isMobile) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dialog, {
57919
+ open,
57920
+ onOpenChange: (next) => !next && onClose(),
57921
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogContent, {
57922
+ placement: "sheet-on-mobile",
57923
+ scroll: "body",
57924
+ "aria-label": label,
57925
+ className: "h-[100dvh] max-h-none w-full max-w-full rounded-none border-0 p-0 pt-[env(safe-area-inset-top)] pb-[env(safe-area-inset-bottom)]",
57926
+ children: open && children
57952
57927
  })
57953
57928
  });
57929
+ if (!open) return null;
57930
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("aside", {
57931
+ "aria-label": label,
57932
+ "data-step-dock": true,
57933
+ className: "fixed z-30 flex flex-col border-l border-border bg-surface shadow-2xl",
57934
+ style: box !== null ? {
57935
+ top: box.top,
57936
+ height: box.height,
57937
+ right: box.right,
57938
+ width: box.width
57939
+ } : {
57940
+ top: 0,
57941
+ bottom: 0,
57942
+ right: 0,
57943
+ width: 432
57944
+ },
57945
+ children
57946
+ });
57954
57947
  }
57955
- /** "‹ Steps" — the phone sheet's way back to the list. */
57948
+ /** "‹ Steps" — the phone sheet's way back to the matrix. */
57956
57949
  function BackToStepsButton({ onClick }) {
57957
57950
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
57958
57951
  type: "button",
@@ -58045,192 +58038,6 @@ var StepInspectorPanel = (0, react$1.forwardRef)(function StepInspectorPanel({ t
58045
58038
  });
58046
58039
  });
58047
58040
  //#endregion
58048
- //#region src/composites/step-inspector/step-list.tsx
58049
- /**
58050
- * StepList — the master pane of the step inspector: every detection step of the
58051
- * tree, one compact row each, as ONE tab stop.
58052
- *
58053
- * Keyboard (a listbox with a roving tabindex): ↑/↓/Home/End move the selection,
58054
- * Enter or → hands focus to the inspector, `/` jumps to the search box. The
58055
- * search matches a step's name or any of its field labels, so a question about
58056
- * a knob ("face size", "person") finds the step that owns it; Enter in the box
58057
- * selects the first hit and says which field it matched.
58058
- */
58059
- function matches(text, tokens) {
58060
- const lower = text.toLowerCase();
58061
- return tokens.every((t) => lower.includes(t));
58062
- }
58063
- /** Rows matching `query` (all tokens in the name, or all in one field label). */
58064
- function searchSteps(rows, query) {
58065
- const tokens = query.trim().toLowerCase().split(/\s+/).filter(Boolean);
58066
- if (tokens.length === 0) return rows.map((row) => ({ row }));
58067
- const hits = [];
58068
- for (const row of rows) {
58069
- if (matches(row.name, tokens)) {
58070
- hits.push({ row });
58071
- continue;
58072
- }
58073
- const field = row.fieldLabels.find((l) => matches(l, tokens));
58074
- if (field !== void 0) hits.push({
58075
- row,
58076
- matchedField: field
58077
- });
58078
- }
58079
- return hits;
58080
- }
58081
- var StepList = (0, react$1.forwardRef)(function StepList({ rows, selectedId, onSelect, onActivate, onPick, label, className }, ref) {
58082
- const [query, setQuery] = (0, react$1.useState)("");
58083
- const listRef = (0, react$1.useRef)(null);
58084
- const searchRef = (0, react$1.useRef)(null);
58085
- const hits = (0, react$1.useMemo)(() => searchSteps(rows, query), [rows, query]);
58086
- const tabStopId = hits.some((h) => h.row.addonId === selectedId) ? selectedId : hits[0]?.row.addonId ?? null;
58087
- const pendingFocus = (0, react$1.useRef)(null);
58088
- const tryFocus = (addonId) => {
58089
- const el = listRef.current?.querySelector(`[data-step-id="${addonId}"]`);
58090
- if (!el) return false;
58091
- el.focus();
58092
- return true;
58093
- };
58094
- const focusRow = (addonId) => {
58095
- if (addonId === null) return;
58096
- pendingFocus.current = tryFocus(addonId) ? null : addonId;
58097
- };
58098
- (0, react$1.useLayoutEffect)(() => {
58099
- if (pendingFocus.current !== null && tryFocus(pendingFocus.current)) pendingFocus.current = null;
58100
- });
58101
- (0, react$1.useImperativeHandle)(ref, () => ({ focusSelected: () => focusRow(selectedId) }), [selectedId]);
58102
- const move = (index) => {
58103
- const target = hits[Math.max(0, Math.min(hits.length - 1, index))];
58104
- if (target === void 0) return;
58105
- onSelect(target.row.addonId, target.matchedField);
58106
- focusRow(target.row.addonId);
58107
- };
58108
- const onRowKeyDown = (e, index, hit) => {
58109
- switch (e.key) {
58110
- case "ArrowDown":
58111
- e.preventDefault();
58112
- move(index + 1);
58113
- return;
58114
- case "ArrowUp":
58115
- e.preventDefault();
58116
- move(index - 1);
58117
- return;
58118
- case "Home":
58119
- e.preventDefault();
58120
- move(0);
58121
- return;
58122
- case "End":
58123
- e.preventDefault();
58124
- move(hits.length - 1);
58125
- return;
58126
- case "Enter":
58127
- case "ArrowRight":
58128
- e.preventDefault();
58129
- onActivate(hit.row.addonId);
58130
- return;
58131
- case "/":
58132
- e.preventDefault();
58133
- searchRef.current?.focus();
58134
- return;
58135
- default:
58136
- }
58137
- };
58138
- const onSearchKeyDown = (e) => {
58139
- if (e.key === "Enter") {
58140
- e.preventDefault();
58141
- const first = hits[0];
58142
- if (first !== void 0) {
58143
- onSelect(first.row.addonId, first.matchedField);
58144
- onPick?.(first.row.addonId);
58145
- focusRow(first.row.addonId);
58146
- }
58147
- } else if (e.key === "Escape") {
58148
- e.preventDefault();
58149
- setQuery("");
58150
- focusRow(selectedId);
58151
- } else if (e.key === "ArrowDown") {
58152
- e.preventDefault();
58153
- focusRow(tabStopId);
58154
- }
58155
- };
58156
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
58157
- className: cn("flex min-h-0 flex-col", className),
58158
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
58159
- className: "relative shrink-0 border-b border-border p-1.5",
58160
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Search, {
58161
- className: "pointer-events-none absolute left-3.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-foreground-subtle",
58162
- "aria-hidden": true
58163
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
58164
- ref: searchRef,
58165
- type: "search",
58166
- value: query,
58167
- onChange: (e) => setQuery(e.target.value),
58168
- onKeyDown: onSearchKeyDown,
58169
- placeholder: "Find a step or setting /",
58170
- "aria-label": "Find a step or setting",
58171
- className: "h-9 w-full rounded-md border border-border bg-background pl-8 pr-2 text-xs text-foreground outline-none focus:border-primary focus:ring-1 focus:ring-primary/30 md:h-8"
58172
- })]
58173
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
58174
- ref: listRef,
58175
- role: "listbox",
58176
- "aria-label": label,
58177
- className: "min-h-0 flex-1 overflow-y-auto py-1",
58178
- children: [hits.map((hit, index) => {
58179
- const { row } = hit;
58180
- const selected = row.addonId === selectedId;
58181
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
58182
- role: "option",
58183
- "aria-selected": selected,
58184
- tabIndex: row.addonId === tabStopId ? 0 : -1,
58185
- "data-step-id": row.addonId,
58186
- onClick: () => {
58187
- onSelect(row.addonId, hit.matchedField);
58188
- onPick?.(row.addonId);
58189
- },
58190
- onKeyDown: (e) => onRowKeyDown(e, index, hit),
58191
- className: cn("cursor-pointer border-l-2 py-1 pl-2.5 pr-3 outline-none transition-colors", "focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary", selected ? "border-primary bg-primary/10" : "border-transparent hover:bg-muted/40"),
58192
- children: [
58193
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
58194
- className: "flex min-w-0 items-baseline gap-2",
58195
- children: [
58196
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
58197
- className: "w-[4.5rem] shrink-0 truncate",
58198
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SlotHeading, { slot: row.slot })
58199
- }),
58200
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
58201
- className: cn("min-w-0 flex-1 truncate text-xs font-medium", selected ? "text-foreground" : "text-foreground/90"),
58202
- style: { paddingLeft: `${row.depth * 12}px` },
58203
- children: [row.depth > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
58204
- className: "mr-1 text-foreground-subtle/60",
58205
- "aria-hidden": true,
58206
- children: "└"
58207
- }), row.name]
58208
- }),
58209
- row.status
58210
- ]
58211
- }),
58212
- hit.matchedField !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
58213
- className: "mt-0.5 truncate pl-[5rem] text-[10px] text-primary",
58214
- children: ["↳ ", hit.matchedField]
58215
- }),
58216
- row.detail !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
58217
- className: "mt-0.5 pl-[5rem]",
58218
- children: row.detail
58219
- })
58220
- ]
58221
- }, row.addonId);
58222
- }), hits.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
58223
- className: "px-3 py-4 text-center text-[11px] text-foreground-subtle",
58224
- children: [
58225
- "No step matches “",
58226
- query,
58227
- "”."
58228
- ]
58229
- })]
58230
- })]
58231
- });
58232
- });
58233
- //#endregion
58234
58041
  //#region src/composites/step-inspector/step-model-row.tsx
58235
58042
  var SOURCE_TONE = {
58236
58043
  camera: "border-primary/40 bg-primary/10 text-primary",
@@ -58386,6 +58193,30 @@ function StepModelList({ rows }) {
58386
58193
  });
58387
58194
  }
58388
58195
  //#endregion
58196
+ //#region src/composites/step-inspector/step-search.ts
58197
+ function matches(text, tokens) {
58198
+ const lower = text.toLowerCase();
58199
+ return tokens.every((t) => lower.includes(t));
58200
+ }
58201
+ /** Steps matching `query` (all tokens in the name, or all in one field label). Empty query = all. */
58202
+ function searchSteps(rows, query) {
58203
+ const tokens = query.trim().toLowerCase().split(/\s+/).filter(Boolean);
58204
+ if (tokens.length === 0) return rows.map((row) => ({ addonId: row.addonId }));
58205
+ const hits = [];
58206
+ for (const row of rows) {
58207
+ if (matches(row.name, tokens)) {
58208
+ hits.push({ addonId: row.addonId });
58209
+ continue;
58210
+ }
58211
+ const field = row.fieldLabels.find((l) => matches(l, tokens));
58212
+ if (field !== void 0) hits.push({
58213
+ addonId: row.addonId,
58214
+ matchedField: field
58215
+ });
58216
+ }
58217
+ return hits;
58218
+ }
58219
+ //#endregion
58389
58220
  //#region src/composites/step-tree.ts
58390
58221
  /**
58391
58222
  * Helper: build a StepTreeNode[] from a `PipelineSchema` by chaining on
@@ -61589,6 +61420,7 @@ exports.SPLIT_PANEL_OUTER = SPLIT_PANEL_OUTER;
61589
61420
  exports.SPLIT_PANEL_SIDE = SPLIT_PANEL_SIDE;
61590
61421
  exports.STACK_GAP = STACK_GAP;
61591
61422
  exports.STATE_COLOR = STATE_COLOR;
61423
+ exports.STEP_DOCK_WIDTH_PX = STEP_DOCK_WIDTH_PX;
61592
61424
  exports.SceneMonitorEditor = SceneMonitorEditor;
61593
61425
  exports.ScopePicker = ScopePicker;
61594
61426
  exports.ScriptHeroCard = ScriptHeroCard;
@@ -61614,10 +61446,8 @@ exports.Square = Square;
61614
61446
  exports.StatCard = StatCard;
61615
61447
  exports.StateValuesStream = StateValuesStream;
61616
61448
  exports.StatusBadge = StatusBadge;
61617
- exports.StepChips = StepChips;
61618
- exports.StepInspectorLayout = StepInspectorLayout;
61449
+ exports.StepDetailDock = StepDetailDock;
61619
61450
  exports.StepInspectorPanel = StepInspectorPanel;
61620
- exports.StepList = StepList;
61621
61451
  exports.StepModelList = StepModelList;
61622
61452
  exports.StepModelRow = StepModelRow;
61623
61453
  exports.StepTimings = StepTimings;
@@ -61667,6 +61497,8 @@ exports.WidgetPanel = WidgetPanel;
61667
61497
  exports.WidgetRegistryProvider = WidgetRegistryProvider;
61668
61498
  exports.WidgetSlot = WidgetSlot;
61669
61499
  exports.ZoneEditingProvider = ZoneEditingProvider;
61500
+ exports.acceleratorLabel = acceleratorLabel;
61501
+ exports.acceleratorShortLabel = acceleratorShortLabel;
61670
61502
  exports.agentColumnKey = agentColumnKey;
61671
61503
  exports.allDeviceTypeFilterOptions = allDeviceTypeFilterOptions;
61672
61504
  exports.applyFleetQueryDefaults = applyFleetQueryDefaults;
@@ -61714,12 +61546,12 @@ exports.deviceTypeLabel = deviceTypeLabel;
61714
61546
  exports.deviceTypeMeta = deviceTypeMeta;
61715
61547
  exports.deviceTypeMetaOf = deviceTypeMetaOf;
61716
61548
  exports.devicesToOptions = devicesToOptions;
61549
+ exports.dockBox = dockBox;
61717
61550
  exports.draftFromQuery = draftFromQuery;
61718
61551
  exports.emptyDevicesWidens = emptyDevicesWidens;
61719
61552
  exports.emptySessionRun = emptySessionRun;
61720
61553
  exports.ensureMfHostInit = ensureMfHostInit;
61721
61554
  exports.eventKindLabel = eventKindLabel;
61722
- exports.fillHeightPx = fillHeightPx;
61723
61555
  exports.filterDataTableRows = filterDataTableRows;
61724
61556
  exports.filterDeviceOptions = filterDeviceOptions;
61725
61557
  exports.filterOptionSelectOptions = filterOptions;
@@ -61767,6 +61599,7 @@ exports.mirror = mirror;
61767
61599
  exports.modelRowsAreFoldable = modelRowsAreFoldable;
61768
61600
  exports.mountAddonPage = mountAddonPage;
61769
61601
  exports.mountPath = mountPath;
61602
+ exports.nextGridPosition = nextGridPosition;
61770
61603
  exports.nextReconnectAction = nextReconnectAction;
61771
61604
  exports.nextSort = nextSort;
61772
61605
  exports.nextSortDirection = nextSortDirection;
@@ -62586,7 +62419,6 @@ exports.usePipelineExecutorCacheFrameInPool = usePipelineExecutorCacheFrameInPoo
62586
62419
  exports.usePipelineExecutorClearDeviceOverrides = usePipelineExecutorClearDeviceOverrides;
62587
62420
  exports.usePipelineExecutorClearModelBoundDeviceOverrides = usePipelineExecutorClearModelBoundDeviceOverrides;
62588
62421
  exports.usePipelineExecutorDeleteModel = usePipelineExecutorDeleteModel;
62589
- exports.usePipelineExecutorDeleteTemplate = usePipelineExecutorDeleteTemplate;
62590
62422
  exports.usePipelineExecutorDownloadModel = usePipelineExecutorDownloadModel;
62591
62423
  exports.usePipelineExecutorGetAddonModels = usePipelineExecutorGetAddonModels;
62592
62424
  exports.usePipelineExecutorGetAudioCapabilities = usePipelineExecutorGetAudioCapabilities;
@@ -62610,22 +62442,18 @@ exports.usePipelineExecutorInferCached = usePipelineExecutorInferCached;
62610
62442
  exports.usePipelineExecutorKillEngine = usePipelineExecutorKillEngine;
62611
62443
  exports.usePipelineExecutorListLoadedEngines = usePipelineExecutorListLoadedEngines;
62612
62444
  exports.usePipelineExecutorListReferenceImages = usePipelineExecutorListReferenceImages;
62613
- exports.usePipelineExecutorListTemplates = usePipelineExecutorListTemplates;
62614
62445
  exports.usePipelineExecutorRearmInferenceDevice = usePipelineExecutorRearmInferenceDevice;
62615
62446
  exports.usePipelineExecutorRunAudioTest = usePipelineExecutorRunAudioTest;
62616
62447
  exports.usePipelineExecutorRunPipeline = usePipelineExecutorRunPipeline;
62617
62448
  exports.usePipelineExecutorRunPipelineBatch = usePipelineExecutorRunPipelineBatch;
62618
- exports.usePipelineExecutorSaveTemplate = usePipelineExecutorSaveTemplate;
62619
62449
  exports.usePipelineExecutorSetVideoPipelineSteps = usePipelineExecutorSetVideoPipelineSteps;
62620
62450
  exports.usePipelineExecutorSpinEngine = usePipelineExecutorSpinEngine;
62621
62451
  exports.usePipelineExecutorUncacheFrame = usePipelineExecutorUncacheFrame;
62622
- exports.usePipelineExecutorUpdateTemplate = usePipelineExecutorUpdateTemplate;
62623
62452
  exports.usePipelineExecutorValidatePipeline = usePipelineExecutorValidatePipeline;
62624
62453
  exports.usePipelineOrchestratorApplyAutoTune = usePipelineOrchestratorApplyAutoTune;
62625
62454
  exports.usePipelineOrchestratorApplyDeviceSettingsPatch = usePipelineOrchestratorApplyDeviceSettingsPatch;
62626
62455
  exports.usePipelineOrchestratorAssignAudio = usePipelineOrchestratorAssignAudio;
62627
62456
  exports.usePipelineOrchestratorAssignPipeline = usePipelineOrchestratorAssignPipeline;
62628
- exports.usePipelineOrchestratorDeleteTemplate = usePipelineOrchestratorDeleteTemplate;
62629
62457
  exports.usePipelineOrchestratorGetAgentLoad = usePipelineOrchestratorGetAgentLoad;
62630
62458
  exports.usePipelineOrchestratorGetAgentSettings = usePipelineOrchestratorGetAgentSettings;
62631
62459
  exports.usePipelineOrchestratorGetAudioAssignment = usePipelineOrchestratorGetAudioAssignment;
@@ -62651,7 +62479,6 @@ exports.usePipelineOrchestratorGetPipelineDevicePin = usePipelineOrchestratorGet
62651
62479
  exports.usePipelineOrchestratorGetPlacementStats = usePipelineOrchestratorGetPlacementStats;
62652
62480
  exports.usePipelineOrchestratorGetReconcileFromDiskStatus = usePipelineOrchestratorGetReconcileFromDiskStatus;
62653
62481
  exports.usePipelineOrchestratorListAgentSettings = usePipelineOrchestratorListAgentSettings;
62654
- exports.usePipelineOrchestratorListTemplates = usePipelineOrchestratorListTemplates;
62655
62482
  exports.usePipelineOrchestratorPauseForStorageMigration = usePipelineOrchestratorPauseForStorageMigration;
62656
62483
  exports.usePipelineOrchestratorPlanAutoTune = usePipelineOrchestratorPlanAutoTune;
62657
62484
  exports.usePipelineOrchestratorRebalance = usePipelineOrchestratorRebalance;
@@ -62661,7 +62488,6 @@ exports.usePipelineOrchestratorRequestOccupancyLook = usePipelineOrchestratorReq
62661
62488
  exports.usePipelineOrchestratorResetNodePipelineDefaults = usePipelineOrchestratorResetNodePipelineDefaults;
62662
62489
  exports.usePipelineOrchestratorResolvePipeline = usePipelineOrchestratorResolvePipeline;
62663
62490
  exports.usePipelineOrchestratorResumeForStorageMigration = usePipelineOrchestratorResumeForStorageMigration;
62664
- exports.usePipelineOrchestratorSaveTemplate = usePipelineOrchestratorSaveTemplate;
62665
62491
  exports.usePipelineOrchestratorSetAgentCapabilities = usePipelineOrchestratorSetAgentCapabilities;
62666
62492
  exports.usePipelineOrchestratorSetAgentDecodeLimits = usePipelineOrchestratorSetAgentDecodeLimits;
62667
62493
  exports.usePipelineOrchestratorSetAgentInferenceDevices = usePipelineOrchestratorSetAgentInferenceDevices;
@@ -62680,7 +62506,6 @@ exports.usePipelineOrchestratorSetShareSource = usePipelineOrchestratorSetShareS
62680
62506
  exports.usePipelineOrchestratorSuggestShares = usePipelineOrchestratorSuggestShares;
62681
62507
  exports.usePipelineOrchestratorUnassignAudio = usePipelineOrchestratorUnassignAudio;
62682
62508
  exports.usePipelineOrchestratorUnassignPipeline = usePipelineOrchestratorUnassignPipeline;
62683
- exports.usePipelineOrchestratorUpdateTemplate = usePipelineOrchestratorUpdateTemplate;
62684
62509
  exports.usePipelineRunnerAttachCamera = usePipelineRunnerAttachCamera;
62685
62510
  exports.usePipelineRunnerDetachCamera = usePipelineRunnerDetachCamera;
62686
62511
  exports.usePipelineRunnerGetAllCameraMetrics = usePipelineRunnerGetAllCameraMetrics;