@camstack/ui-library 1.2.245 → 1.2.247

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",
@@ -6890,32 +6855,8 @@ var useDayNightGetOptions = trpc.dayNight.getOptions.useQuery;
6890
6855
  var useDayNightSetSettings = trpc.dayNight.setSettings.useMutation;
6891
6856
  /** Generated alias around `trpc.dayNight.getStatus.useQuery`. */
6892
6857
  var useDayNightGetStatus = trpc.dayNight.getStatus.useQuery;
6893
- /** Generated alias around `trpc.decoder.supportsCodec.useQuery`. */
6894
- var useDecoderSupportsCodec = trpc.decoder.supportsCodec.useQuery;
6895
6858
  /** Generated alias around `trpc.decoder.getInfo.useQuery`. */
6896
6859
  var useDecoderGetInfo = trpc.decoder.getInfo.useQuery;
6897
- /** Generated alias around `trpc.decoder.createSession.useQuery`. */
6898
- var useDecoderCreateSession = trpc.decoder.createSession.useQuery;
6899
- /** Generated alias around `trpc.decoder.destroySession.useQuery`. */
6900
- var useDecoderDestroySession = trpc.decoder.destroySession.useQuery;
6901
- /** Generated alias around `trpc.decoder.pushPacket.useQuery`. */
6902
- var useDecoderPushPacket = trpc.decoder.pushPacket.useQuery;
6903
- /** Generated alias around `trpc.decoder.openStream.useQuery`. */
6904
- var useDecoderOpenStream = trpc.decoder.openStream.useQuery;
6905
- /** Generated alias around `trpc.decoder.pullFrames.useQuery`. */
6906
- var useDecoderPullFrames = trpc.decoder.pullFrames.useQuery;
6907
- /** Generated alias around `trpc.decoder.pullHandles.useQuery`. */
6908
- var useDecoderPullHandles = trpc.decoder.pullHandles.useQuery;
6909
- /** Generated alias around `trpc.decoder.getFrame.useQuery`. */
6910
- var useDecoderGetFrame = trpc.decoder.getFrame.useQuery;
6911
- /** Generated alias around `trpc.decoder.getShmStats.useQuery`. */
6912
- var useDecoderGetShmStats = trpc.decoder.getShmStats.useQuery;
6913
- /** Generated alias around `trpc.decoder.updateConfig.useQuery`. */
6914
- var useDecoderUpdateConfig = trpc.decoder.updateConfig.useQuery;
6915
- /** Generated alias around `trpc.decoder.getStats.useQuery`. */
6916
- var useDecoderGetStats = trpc.decoder.getStats.useQuery;
6917
- /** Generated alias around `trpc.decoder.listActiveSessions.useQuery`. */
6918
- var useDecoderListActiveSessions = trpc.decoder.listActiveSessions.useQuery;
6919
6860
  /** Generated alias around `trpc.decoder.reprobeHwaccel.useMutation`. */
6920
6861
  var useDecoderReprobeHwaccel = trpc.decoder.reprobeHwaccel.useMutation;
6921
6862
  /** Generated alias around `trpc.detectionPipeline.getDeviceSettingsContribution.useQuery`. */
@@ -7914,14 +7855,6 @@ var usePipelineExecutorGetGlobalPipelineConfig = trpc.pipelineExecutor.getGlobal
7914
7855
  var usePipelineExecutorGetOrchestratorConfigSchema = trpc.pipelineExecutor.getOrchestratorConfigSchema.useQuery;
7915
7856
  /** Generated alias around `trpc.pipelineExecutor.validatePipeline.useQuery`. */
7916
7857
  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
7858
  /** Generated alias around `trpc.pipelineExecutor.getCapabilities.useQuery`. */
7926
7859
  var usePipelineExecutorGetCapabilities = trpc.pipelineExecutor.getCapabilities.useQuery;
7927
7860
  /** Generated alias around `trpc.pipelineExecutor.getAddonModels.useQuery`. */
@@ -8068,14 +8001,6 @@ var usePipelineOrchestratorSetCameraSensors = trpc.pipelineOrchestrator.setCamer
8068
8001
  var usePipelineOrchestratorReconcileFromDisk = trpc.pipelineOrchestrator.reconcileFromDisk.useMutation;
8069
8002
  /** Generated alias around `trpc.pipelineOrchestrator.getReconcileFromDiskStatus.useQuery`. */
8070
8003
  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
8004
  /** Generated alias around `trpc.pipelineOrchestrator.getDeviceSettingsContribution.useQuery`. */
8080
8005
  var usePipelineOrchestratorGetDeviceSettingsContribution = trpc.pipelineOrchestrator.getDeviceSettingsContribution.useQuery;
8081
8006
  /** Generated alias around `trpc.pipelineOrchestrator.getDeviceLiveContribution.useQuery`. */
@@ -8504,12 +8429,6 @@ var useStreamBrokerSubscribeAudioChunks = trpc.streamBroker.subscribeAudioChunks
8504
8429
  var useStreamBrokerPullAudioChunks = trpc.streamBroker.pullAudioChunks.useQuery;
8505
8430
  /** Generated alias around `trpc.streamBroker.unsubscribeAudioChunks.useMutation`. */
8506
8431
  var useStreamBrokerUnsubscribeAudioChunks = trpc.streamBroker.unsubscribeAudioChunks.useMutation;
8507
- /** Generated alias around `trpc.streamBroker.subscribeFrames.useMutation`. */
8508
- var useStreamBrokerSubscribeFrames = trpc.streamBroker.subscribeFrames.useMutation;
8509
- /** Generated alias around `trpc.streamBroker.pullFrameHandles.useQuery`. */
8510
- var useStreamBrokerPullFrameHandles = trpc.streamBroker.pullFrameHandles.useQuery;
8511
- /** Generated alias around `trpc.streamBroker.unsubscribeFrames.useMutation`. */
8512
- var useStreamBrokerUnsubscribeFrames = trpc.streamBroker.unsubscribeFrames.useMutation;
8513
8432
  /** Generated alias around `trpc.streamBroker.setPreBufferDuration.useMutation`. */
8514
8433
  var useStreamBrokerSetPreBufferDuration = trpc.streamBroker.setPreBufferDuration.useMutation;
8515
8434
  /** Generated alias around `trpc.streamBroker.getPreBufferInfo.useQuery`. */
@@ -28746,6 +28665,45 @@ function AddonGlobalSettingsForm({ trpc, addonId, nodeId, title, disabled, onAft
28746
28665
  });
28747
28666
  }
28748
28667
  //#endregion
28668
+ //#region src/composites/accelerator-label.ts
28669
+ function names(device) {
28670
+ const backend = device.backend.toLowerCase();
28671
+ const dev = device.device.toLowerCase();
28672
+ if (device.key === "cpu" || dev === "cpu" || backend === "cpu") return {
28673
+ label: "CPU",
28674
+ short: "CPU"
28675
+ };
28676
+ if (dev === "npu") return {
28677
+ label: "Intel NPU",
28678
+ short: "NPU"
28679
+ };
28680
+ if (dev === "gpu" || dev === "igpu") return {
28681
+ label: "Intel iGPU",
28682
+ short: "iGPU"
28683
+ };
28684
+ if (backend === "edgetpu") return {
28685
+ label: "Coral Edge TPU",
28686
+ short: "Coral"
28687
+ };
28688
+ if (backend === "coreml") return {
28689
+ label: "Apple Neural Engine",
28690
+ short: "ANE"
28691
+ };
28692
+ const pretty = `${device.backend} ${device.device}`.trim().replace(/\b\w/g, (c) => c.toUpperCase());
28693
+ return {
28694
+ label: pretty.length > 0 ? pretty : device.key,
28695
+ short: device.backend || device.key
28696
+ };
28697
+ }
28698
+ /** "Intel NPU", "Apple Neural Engine", "Coral Edge TPU", "CPU" — Auto tune's names too. */
28699
+ function acceleratorLabel(device) {
28700
+ return names(device).label;
28701
+ }
28702
+ /** "NPU", "ANE", "Coral", "CPU" — chips and tabs. */
28703
+ function acceleratorShortLabel(device) {
28704
+ return names(device).short;
28705
+ }
28706
+ //#endregion
28749
28707
  //#region src/composites/grouped-model-selector.tsx
28750
28708
  /**
28751
28709
  * GroupedModelSelector — the shared Family → Tier → Variant model picker.
@@ -29973,9 +29931,9 @@ function RootSuppliedModelNote({ rootModel, stepModelId }) {
29973
29931
  /**
29974
29932
  * AgentStepEditor — one step on ONE accelerator: its model and its settings,
29975
29933
  * 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.
29934
+ * model" dialog uses it. The pipeline pages use the step matrix's panel, which lays
29935
+ * the same parts out across accelerators and layers (D754/D757); the settings
29936
+ * form here is that panel's `LayeredSettingsForm` with one layer.
29979
29937
  */
29980
29938
  function AgentStepEditor({ addon, agentDefault, onChangeAgentConfig, engineFormat, effectiveModelId, rootModel }) {
29981
29939
  const modelsForFormat = (0, react$1.useMemo)(() => stepModelOptions(addon.models, engineFormat), [addon.models, engineFormat]);
@@ -35294,7 +35252,6 @@ function useDeviceWebrtc(trpc, deviceId, pollIntervalMs = 5e3) {
35294
35252
  target: r.target,
35295
35253
  status: r.status,
35296
35254
  inputFps: r.inputFps,
35297
- decodeFps: r.decodeFps,
35298
35255
  bitrateKbps: r.bitrateKbps
35299
35256
  })),
35300
35257
  hasWebrtc: true
@@ -55405,25 +55362,46 @@ function ClassChips({ inputs, outputs }) {
55405
55362
  * the first column; each device's real format is shown per sub-column instead
55406
55363
  * ("Coral Edge TPU · tflite").
55407
55364
  */
55365
+ /** The per-accelerator lines of a column header: name, format, model, off. */
55366
+ function AgentColumnLines({ column }) {
55367
+ const name = column.deviceLabel ?? column.deviceKey;
55368
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
55369
+ (name !== void 0 || column.disabled === true) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
55370
+ className: "flex min-w-0 items-center gap-1",
55371
+ children: [name !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
55372
+ className: "truncate text-[10px] font-medium text-foreground/90",
55373
+ children: name
55374
+ }), column.disabled === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
55375
+ className: "shrink-0 rounded bg-muted px-1 text-[8px] font-semibold uppercase text-foreground-subtle",
55376
+ children: "off"
55377
+ })]
55378
+ }),
55379
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55380
+ className: "truncate text-[9px] text-foreground-subtle",
55381
+ children: column.engineLabel
55382
+ }),
55383
+ column.detail !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55384
+ className: "truncate font-mono text-[9px] text-foreground-subtle",
55385
+ title: column.detail,
55386
+ children: column.detail
55387
+ })
55388
+ ] });
55389
+ }
55408
55390
  function GroupedAgentHeader({ group }) {
55391
+ const nodeLabel = group.columns[0]?.nodeLabel ?? group.agentNodeId;
55409
55392
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
55393
+ role: "columnheader",
55410
55394
  className: "sticky top-0 z-10 min-w-0 border-b border-l border-border bg-muted/60",
55411
55395
  style: { gridColumn: `span ${group.columns.length}` },
55412
55396
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55413
55397
  className: "truncate px-3 pt-2 pb-1 text-xs font-semibold text-foreground",
55414
- children: group.agentNodeId
55398
+ children: nodeLabel
55415
55399
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55416
55400
  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
- })]
55401
+ children: group.columns.map((c, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55402
+ className: cn("min-w-0 flex-1 px-3 py-1", i > 0 && "border-l border-border/50", c.disabled === true && "opacity-70"),
55403
+ title: `${c.deviceLabel ?? c.deviceKey} · ${c.engineLabel}${c.detail ? ` · ${c.detail}` : ""}`,
55404
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AgentColumnLines, { column: c })
55427
55405
  }, agentColumnKey(c)))
55428
55406
  })]
55429
55407
  });
@@ -55527,25 +55505,30 @@ function cellButtonClass(state, isSelected, extra) {
55527
55505
  //#endregion
55528
55506
  //#region src/composites/device-step-matrix.tsx
55529
55507
  /**
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`.
55508
+ * DeviceStepMatrix — the step × accelerator TABLE of a detection pipeline
55509
+ * (D757): the node's Pipeline tab and the camera's Detection pipeline → Steps
55510
+ * are this matrix, and a click on a cell (or a step's header) opens that step's
55511
+ * settings in a docked panel (`StepDetailDock`) that overlays the page instead
55512
+ * of reflowing the matrix.
55513
+ *
55514
+ * Column 1 (STICKY): the indented step tree — slot, name, class-flow chips,
55515
+ * and an optional badge (the camera's gate). The header is a button: it
55516
+ * opens the step on the selected accelerator.
55517
+ * Columns 2..N: one per accelerator, grouped under their node; the header
55518
+ * names the accelerator, its format, the model it elects, and "off" for a
55519
+ * switched-off one. A cell shows the model that runs there, or "⚠ skip",
55520
+ * "→ iGPU", "root", "cluster", "ovr", "—".
55521
+ *
55522
+ * Keyboard: ONE Tab stop (a roving tabindex over the step headers and cells);
55523
+ * arrows move, Home / End go to the row's ends, Enter or Space opens.
55524
+ *
55525
+ * `reserveRightPx`: the width a docked panel covers at the right of the
55526
+ * viewport. The matrix adds a trailing spacer that wide (so any column can
55527
+ * scroll clear of it) and scrolls the selected cell into the uncovered part —
55528
+ * it never shrinks, so nothing reflows when the panel opens.
55529
+ *
55530
+ * Responsive, container-measured: narrower than the step column plus two
55531
+ * columns, the matrix shows ONE node at a time with a node selector.
55549
55532
  */
55550
55533
  /** The sticky step column. */
55551
55534
  var STEP_COL_REM = 18;
@@ -55556,11 +55539,41 @@ var STEP_COL_PX = STEP_COL_REM * 16;
55556
55539
  * step column and lets the device columns scroll horizontally beside it.
55557
55540
  */
55558
55541
  var STEP_COL_SINGLE_REM = 11;
55559
- function StepCell({ node, depth }) {
55542
+ /** Where a key press moves the roving focus in a `rows × cols` grid. */
55543
+ function nextGridPosition(key, pos, rows, cols) {
55544
+ const clamp = (r, c) => ({
55545
+ r: Math.max(0, Math.min(rows - 1, r)),
55546
+ c: Math.max(0, Math.min(cols - 1, c))
55547
+ });
55548
+ switch (key) {
55549
+ case "ArrowRight": return clamp(pos.r, pos.c + 1);
55550
+ case "ArrowLeft": return clamp(pos.r, pos.c - 1);
55551
+ case "ArrowDown": return clamp(pos.r + 1, pos.c);
55552
+ case "ArrowUp": return clamp(pos.r - 1, pos.c);
55553
+ case "Home": return clamp(pos.r, 0);
55554
+ case "End": return clamp(pos.r, cols - 1);
55555
+ case "PageDown": return clamp(rows - 1, pos.c);
55556
+ case "PageUp": return clamp(0, pos.c);
55557
+ default: return null;
55558
+ }
55559
+ }
55560
+ function StepCell({ node, depth, r, tabbable, selected, badge, onClick, sticky }) {
55560
55561
  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 })
55562
+ role: "rowheader",
55563
+ className: cn("min-w-0 border-b border-border bg-surface", sticky && "sticky left-0 z-10"),
55564
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
55565
+ type: "button",
55566
+ "data-r": r,
55567
+ "data-c": 0,
55568
+ "data-step": node.addonId,
55569
+ "data-col": "",
55570
+ tabIndex: tabbable ? 0 : -1,
55571
+ "aria-current": selected ? "true" : void 0,
55572
+ onClick: onClick ? (e) => onClick(e.detail === 0) : void 0,
55573
+ 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)]"),
55574
+ style: { paddingLeft: `${12 + depth * 14}px` },
55575
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepLabel, { node }), badge]
55576
+ })
55564
55577
  });
55565
55578
  }
55566
55579
  /**
@@ -55590,68 +55603,179 @@ function NodeSelectorBar({ nodes, selected, onSelect }) {
55590
55603
  role: "tab",
55591
55604
  "aria-selected": isSelected,
55592
55605
  onClick: () => onSelect(a.agentNodeId),
55593
- title: `${a.agentNodeId} · ${a.engineLabel}`,
55606
+ title: `${a.columns[0]?.nodeLabel ?? a.agentNodeId} · ${a.engineLabel}`,
55594
55607
  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
55608
+ children: a.columns[0]?.nodeLabel ?? a.agentNodeId
55596
55609
  }, a.agentNodeId);
55597
55610
  })
55598
55611
  })
55599
55612
  ]
55600
55613
  });
55601
55614
  }
55602
- function MatrixGrid({ tree, agents, getCellState, onCellClick, selectedCell, single }) {
55615
+ var MatrixGrid = (0, react$1.forwardRef)(function MatrixGrid({ tree, agents, getCellState, onCellClick, onStepClick, stepBadge, selectedCell, single, reserveRightPx, label }, ref) {
55603
55616
  const rows = (0, react$1.useMemo)(() => flattenTree(tree), [tree]);
55604
55617
  const groups = (0, react$1.useMemo)(() => groupAgentColumns(agents), [agents]);
55618
+ const boxRef = (0, react$1.useRef)(null);
55619
+ const stepColRem = single ? STEP_COL_SINGLE_REM : STEP_COL_REM;
55620
+ const gridTemplate = `${stepColRem}rem repeat(${agents.length}, 13rem)`;
55621
+ const cols = agents.length + 1;
55622
+ const [stickySteps, setStickySteps] = (0, react$1.useState)(true);
55623
+ (0, react$1.useLayoutEffect)(() => {
55624
+ const box = boxRef.current;
55625
+ if (!box || reserveRightPx <= 0) {
55626
+ setStickySteps(true);
55627
+ return;
55628
+ }
55629
+ const measure = () => {
55630
+ const left = box.getBoundingClientRect().left;
55631
+ setStickySteps(window.innerWidth - reserveRightPx - left >= stepColRem * 16 + 208 + 24);
55632
+ };
55633
+ measure();
55634
+ window.addEventListener("resize", measure);
55635
+ return () => window.removeEventListener("resize", measure);
55636
+ }, [reserveRightPx, stepColRem]);
55637
+ const selectedPos = (0, react$1.useMemo)(() => {
55638
+ if (selectedCell === null) return null;
55639
+ const r = rows.findIndex((row) => row.node.addonId === selectedCell.addonId);
55640
+ if (r < 0) return null;
55641
+ const c = agents.findIndex((a) => agentColumnKey(a) === agentColumnKey(selectedCell));
55642
+ return {
55643
+ r,
55644
+ c: c < 0 ? 0 : c + 1
55645
+ };
55646
+ }, [
55647
+ selectedCell,
55648
+ rows,
55649
+ agents
55650
+ ]);
55651
+ const [focusPos, setFocusPos] = (0, react$1.useState)({
55652
+ r: 0,
55653
+ c: 0
55654
+ });
55655
+ (0, react$1.useEffect)(() => {
55656
+ if (selectedPos !== null) setFocusPos(selectedPos);
55657
+ }, [selectedPos]);
55658
+ const tab = focusPos.r < rows.length && focusPos.c < cols ? focusPos : {
55659
+ r: 0,
55660
+ c: 0
55661
+ };
55662
+ const focusAt = (r, c) => {
55663
+ boxRef.current?.querySelector(`[data-r="${r}"][data-c="${c}"]`)?.focus();
55664
+ };
55665
+ (0, react$1.useImperativeHandle)(ref, () => ({ focusCell: (addonId, columnKey) => {
55666
+ const r = rows.findIndex((row) => row.node.addonId === addonId);
55667
+ if (r < 0) return;
55668
+ const index = columnKey === null ? -1 : agents.findIndex((a) => agentColumnKey(a) === columnKey);
55669
+ const c = index < 0 ? 0 : index + 1;
55670
+ setFocusPos({
55671
+ r,
55672
+ c
55673
+ });
55674
+ focusAt(r, c);
55675
+ } }), [rows, agents]);
55676
+ (0, react$1.useLayoutEffect)(() => {
55677
+ if (selectedPos === null) return;
55678
+ const box = boxRef.current;
55679
+ const el = box?.querySelector(`[data-r="${selectedPos.r}"][data-c="${selectedPos.c}"]`);
55680
+ if (!box || !el) return;
55681
+ if (typeof el.scrollIntoView === "function") el.scrollIntoView({
55682
+ block: "nearest",
55683
+ inline: "nearest"
55684
+ });
55685
+ if (reserveRightPx <= 0) return;
55686
+ const limit = window.innerWidth - reserveRightPx - 12;
55687
+ const right = el.getBoundingClientRect().right;
55688
+ if (right > limit) box.scrollLeft += right - limit;
55689
+ }, [
55690
+ selectedPos,
55691
+ reserveRightPx,
55692
+ stickySteps
55693
+ ]);
55694
+ const onKeyDown = (e) => {
55695
+ if (!(e.target instanceof HTMLElement) || e.target.dataset.r === void 0) return;
55696
+ const pos = {
55697
+ r: Number(e.target.dataset.r),
55698
+ c: Number(e.target.dataset.c)
55699
+ };
55700
+ const next = nextGridPosition(e.key, pos, rows.length, cols);
55701
+ if (next === null) return;
55702
+ e.preventDefault();
55703
+ setFocusPos(next);
55704
+ focusAt(next.r, next.c);
55705
+ };
55605
55706
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55707
+ ref: boxRef,
55606
55708
  className: "overflow-auto rounded border border-border",
55607
55709
  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",
55710
+ className: "flex w-max",
55711
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
55712
+ role: "grid",
55713
+ "aria-label": label,
55714
+ "aria-rowcount": rows.length + 1,
55715
+ className: "grid w-max",
55716
+ style: { gridTemplateColumns: gridTemplate },
55717
+ onKeyDown,
55718
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
55719
+ role: "row",
55720
+ className: "contents",
55617
55721
  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
55722
+ role: "columnheader",
55723
+ 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"),
55724
+ children: "Step"
55725
+ }), 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", {
55726
+ role: "columnheader",
55727
+ 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"),
55728
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55729
+ className: "truncate text-xs font-semibold text-foreground",
55730
+ children: col.nodeLabel ?? col.agentNodeId
55731
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AgentColumnLines, { column: col })]
55732
+ }, agentColumnKey(col))))]
55733
+ }), rows.map(({ node, depth }, r) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
55734
+ role: "row",
55735
+ className: "contents",
55736
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepCell, {
55737
+ node,
55738
+ depth,
55739
+ r,
55740
+ tabbable: tab.r === r && tab.c === 0,
55741
+ selected: selectedCell?.addonId === node.addonId,
55742
+ badge: stepBadge?.(node.addonId),
55743
+ onClick: onStepClick ? (kb) => onStepClick(node.addonId, kb) : void 0,
55744
+ sticky: stickySteps
55745
+ }), agents.map((a, i) => {
55746
+ const state = getCellState(node.addonId, a);
55747
+ const isSelected = selectedCell !== null && selectedCell.addonId === node.addonId && agentColumnKey(selectedCell) === agentColumnKey(a);
55748
+ const actionable = state.kind === "enabled" || state.kind === "skip";
55749
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55750
+ role: "gridcell",
55751
+ className: "min-w-0 border-b border-l border-border",
55752
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
55753
+ type: "button",
55754
+ "data-r": r,
55755
+ "data-c": i + 1,
55756
+ "data-step": node.addonId,
55757
+ "data-col": agentColumnKey(a),
55758
+ tabIndex: tab.r === r && tab.c === i + 1 ? 0 : -1,
55759
+ "aria-disabled": !actionable,
55760
+ "aria-selected": isSelected,
55761
+ onClick: (e) => {
55762
+ if (actionable) onCellClick(node.addonId, a, e.detail === 0);
55763
+ },
55764
+ 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")),
55765
+ children: renderCellContent(state)
55766
+ })
55767
+ }, agentColumnKey(a));
55623
55768
  })]
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
- ]
55769
+ }, node.addonId))]
55770
+ }), reserveRightPx > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55771
+ "aria-hidden": true,
55772
+ style: { width: reserveRightPx },
55773
+ className: "shrink-0"
55774
+ })]
55634
55775
  })
55635
55776
  });
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 }) {
55777
+ });
55778
+ var DeviceStepMatrix = (0, react$1.forwardRef)(function DeviceStepMatrix({ tree, agents, getCellState, onCellClick, selectedCell, fallbackNodeId, onFallbackNodeChange, onStepClick, stepBadge, reserveRightPx = 0, label = "Detection steps" }, ref) {
55655
55779
  const containerRef = (0, react$1.useRef)(null);
55656
55780
  const [width, setWidth] = (0, react$1.useState)(0);
55657
55781
  (0, react$1.useLayoutEffect)(() => {
@@ -55669,6 +55793,15 @@ function DeviceStepMatrix({ tree, agents, getCellState, onCellClick, selectedCel
55669
55793
  if (nodes.length === 0) return null;
55670
55794
  return nodes.find((n) => n.agentNodeId === fallbackNodeId) ?? nodes[0] ?? null;
55671
55795
  }, [nodes, fallbackNodeId]);
55796
+ const selectedNodeId = selectedCell?.agentNodeId;
55797
+ (0, react$1.useEffect)(() => {
55798
+ if (single && selectedNodeId !== void 0 && selectedNodeId !== fallbackNodeId) onFallbackNodeChange(selectedNodeId);
55799
+ }, [
55800
+ single,
55801
+ selectedNodeId,
55802
+ fallbackNodeId,
55803
+ onFallbackNodeChange
55804
+ ]);
55672
55805
  const shownAgents = single && selectedNode ? selectedNode.columns : agents;
55673
55806
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
55674
55807
  ref: containerRef,
@@ -55678,15 +55811,20 @@ function DeviceStepMatrix({ tree, agents, getCellState, onCellClick, selectedCel
55678
55811
  selected: selectedNode.agentNodeId,
55679
55812
  onSelect: onFallbackNodeChange
55680
55813
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MatrixGrid, {
55814
+ ref,
55681
55815
  tree,
55682
55816
  agents: shownAgents,
55683
55817
  getCellState,
55684
55818
  onCellClick,
55819
+ onStepClick,
55820
+ stepBadge,
55685
55821
  selectedCell,
55686
- single: single && selectedNode !== null
55822
+ single: single && selectedNode !== null,
55823
+ reserveRightPx,
55824
+ label
55687
55825
  })]
55688
55826
  });
55689
- }
55827
+ });
55690
55828
  //#endregion
55691
55829
  //#region src/composites/discovery-panel.tsx
55692
55830
  function DiscoveryPanel({ deviceId, className = "", onOpenChild }) {
@@ -56625,43 +56763,13 @@ function PhaseIcon({ phase, ...props }) {
56625
56763
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { ...props });
56626
56764
  }
56627
56765
  //#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
56766
  //#region src/composites/pipeline-builder.tsx
56660
56767
  /**
56661
- * PipelineBuilder — orchestrates the pipeline step tree with template management.
56768
+ * PipelineBuilder — orchestrates the pipeline step tree.
56662
56769
  *
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.
56770
+ * Renders the step tree from schema with nested children and placeholder steps
56771
+ * for disabled addons, all driven by API schema. There are no saved pipeline
56772
+ * templates (D759): the tree is the caller's state.
56665
56773
  */
56666
56774
  function buildSchemaMap(schema) {
56667
56775
  const map = /* @__PURE__ */ new Map();
@@ -56713,31 +56821,9 @@ function PlaceholderStep({ addon, onClick }) {
56713
56821
  })
56714
56822
  });
56715
56823
  }
56716
- function PipelineBuilder({ schema, steps, onChange, templates, selectedTemplateId, onSelectTemplate, onSaveTemplate, onUpdateTemplate, onDeleteTemplate, readOnly = false, excludeAddons = [], hideTemplates = false, showOnlyEnabled = false, showSlotLabels = false }) {
56824
+ function PipelineBuilder({ schema, steps, onChange, readOnly = false, excludeAddons = [], showOnlyEnabled = false, showSlotLabels = false }) {
56717
56825
  const excluded = (0, react$1.useMemo)(() => new Set(excludeAddons), [excludeAddons]);
56718
56826
  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
56827
  /**
56742
56828
  * When a step is updated, auto-enable the entire ancestor chain if a
56743
56829
  * child/grandchild was just enabled. This ensures enabling face-recognition
@@ -56907,104 +56993,42 @@ function PipelineBuilder({ schema, steps, onChange, templates, selectedTemplateI
56907
56993
  })]
56908
56994
  }, step.addonId);
56909
56995
  }
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", {
56996
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
56912
56997
  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
- ]
56998
+ children: schema.slots.filter((s) => s.parentSlot === null).toSorted((a, b) => a.priority - b.priority).map((slot) => {
56999
+ const allSlotSteps = steps.filter((s) => s.slot === slot.id && !excluded.has(s.addonId));
57000
+ const slotSteps = showOnlyEnabled ? allSlotSteps.filter((s) => s.enabled) : allSlotSteps;
57001
+ const missingRootAddons = showOnlyEnabled ? [] : slot.addons.filter((a) => !existingIds.has(a.id) && !excluded.has(a.id));
57002
+ const groups = /* @__PURE__ */ new Map();
57003
+ const ungrouped = [];
57004
+ for (const step of slotSteps) if (step.group) {
57005
+ if (!groups.has(step.group)) groups.set(step.group, []);
57006
+ groups.get(step.group).push(step);
57007
+ } else ungrouped.push(step);
57008
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
57009
+ className: "space-y-2",
57010
+ children: [
57011
+ showSlotLabels && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
57012
+ className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/50",
57013
+ children: ["Slot: ", slot.label]
57014
+ }),
57015
+ ungrouped.map((step) => renderStep(step)),
57016
+ [...groups.entries()].map(([groupName, groupSteps]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
57017
+ className: "space-y-2 rounded-lg border border-border/40 p-2",
57018
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
57019
+ className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/50 px-1",
57020
+ children: groupName
57021
+ }), groupSteps.map((step) => renderStep(step))]
57022
+ }, groupName)),
57023
+ !readOnly && missingRootAddons.map((addon) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlaceholderStep, {
57024
+ addon,
57025
+ onClick: () => {
57026
+ onChange([...steps, createDefaultStep(addon)]);
57027
+ }
57028
+ }, addon.id))
57029
+ ]
57030
+ }, slot.id);
57031
+ })
57008
57032
  });
57009
57033
  }
57010
57034
  //#endregion
@@ -57668,39 +57692,6 @@ function StepTimings({ timings, totalMs, className }) {
57668
57692
  });
57669
57693
  }
57670
57694
  //#endregion
57671
- //#region src/composites/step-inspector/accelerator-tabs.tsx
57672
- function AcceleratorTabs({ items, selectedId, onSelect }) {
57673
- const onKeyDown = (e, index) => {
57674
- const delta = e.key === "ArrowRight" ? 1 : e.key === "ArrowLeft" ? -1 : 0;
57675
- if (delta === 0) return;
57676
- e.preventDefault();
57677
- const next = items[(index + delta + items.length) % items.length];
57678
- if (next !== void 0) onSelect(next.id);
57679
- };
57680
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
57681
- role: "tablist",
57682
- "aria-label": "Accelerator",
57683
- className: "-mx-1 flex min-w-0 flex-nowrap gap-1 overflow-x-auto px-1 pb-0.5",
57684
- children: items.map((item, index) => {
57685
- const selected = item.id === selectedId;
57686
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
57687
- type: "button",
57688
- role: "tab",
57689
- "aria-selected": selected,
57690
- "aria-label": item.marked ? `${item.label} (camera override)` : item.label,
57691
- tabIndex: selected ? 0 : -1,
57692
- onClick: () => onSelect(item.id),
57693
- onKeyDown: (e) => onKeyDown(e, index),
57694
- className: cn("inline-flex h-9 shrink-0 items-center gap-1 whitespace-nowrap rounded-md border px-2.5 text-[11px] font-medium transition-colors md:h-7", selected ? "border-primary/50 bg-primary/15 text-primary" : "border-border text-foreground-subtle hover:bg-surface-hover hover:text-foreground"),
57695
- children: [item.label, item.marked && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
57696
- className: "h-1.5 w-1.5 rounded-full bg-amber-500",
57697
- "aria-hidden": true
57698
- })]
57699
- }, item.id);
57700
- })
57701
- });
57702
- }
57703
- //#endregion
57704
57695
  //#region src/composites/step-inspector/gate-control.tsx
57705
57696
  /**
57706
57697
  * GateControl — whether ONE camera runs a step: Auto (the catalog default) /
@@ -57793,166 +57784,104 @@ function SegmentedChoice({ label, options, value, onChange }) {
57793
57784
  });
57794
57785
  }
57795
57786
  //#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
57787
+ //#region src/composites/step-inspector/step-detail-dock.tsx
57858
57788
  /**
57859
- * StepInspectorLayout — the master–detail frame of the step inspector.
57789
+ * StepDetailDock — where a step's settings open from the step matrix
57790
+ * (D757): docked to the page's scroll container, never inside the scrolling
57791
+ * content.
57860
57792
  *
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.
57793
+ * Desktop: a side panel `position: fixed` over the RIGHT edge of the nearest
57794
+ * scroll container (its top, bottom and right are measured from that box), so
57795
+ * it stays where it is while the matrix scrolls under it, and its header and
57796
+ * save bar are always on screen. It overlays — it never reserves width, so the
57797
+ * matrix does not reflow (the slide-over before D756 reserved 360 px, the
57798
+ * matrix re-measured into a single-node layout, and the clicked cell slid
57799
+ * under the panel). The matrix uses {@link STEP_DOCK_WIDTH_PX} to scroll the
57800
+ * selected column clear of it.
57869
57801
  *
57870
- * Phone: the list flows with the page; the inspector opens as a full-screen
57871
- * sheet with "‹ Steps" to go back.
57802
+ * Phone: a full-screen `Dialog` sheet.
57872
57803
  */
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));
57804
+ /** Width of the desktop dock — what the matrix keeps clear of it. */
57805
+ var STEP_DOCK_WIDTH_PX = 432;
57806
+ /** The dock's viewport box, from its scroll container's box. */
57807
+ function dockBox(input) {
57808
+ const right = Math.max(0, input.viewportWidth - input.scroller.right);
57809
+ const width = Math.min(input.widthPx, input.scroller.right);
57810
+ return {
57811
+ top: Math.max(0, input.scroller.top),
57812
+ height: Math.max(0, input.scroller.bottom - Math.max(0, input.scroller.top)),
57813
+ right,
57814
+ width
57815
+ };
57877
57816
  }
57878
57817
  function scrollParentOf(el) {
57879
- for (let p = el.parentElement; p !== null; p = p.parentElement) {
57818
+ for (let p = el?.parentElement ?? null; p !== null; p = p.parentElement) {
57880
57819
  const { overflowY } = getComputedStyle(p);
57881
57820
  if (overflowY === "auto" || overflowY === "scroll") return p;
57882
57821
  }
57883
57822
  return null;
57884
57823
  }
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);
57824
+ function useDockBox(anchor, enabled) {
57825
+ const [box, setBox] = (0, react$1.useState)(null);
57889
57826
  (0, react$1.useLayoutEffect)(() => {
57890
- const el = ref.current;
57891
- if (!enabled || el === null) return;
57892
- const scroller = scrollParentOf(el);
57827
+ if (!enabled) return;
57828
+ const scroller = scrollParentOf(anchor.current);
57893
57829
  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
57830
+ setBox(dockBox({
57831
+ scroller: scroller?.getBoundingClientRect() ?? {
57832
+ top: 0,
57833
+ bottom: window.innerHeight,
57834
+ right: window.innerWidth
57835
+ },
57836
+ viewportWidth: window.innerWidth,
57837
+ widthPx: 432
57903
57838
  }));
57904
57839
  };
57905
57840
  measure();
57906
57841
  const RO = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measure);
57907
57842
  if (scroller) RO?.observe(scroller);
57908
- if (el.parentElement) RO?.observe(el.parentElement);
57909
57843
  window.addEventListener("resize", measure);
57910
57844
  return () => {
57911
57845
  RO?.disconnect();
57912
57846
  window.removeEventListener("resize", measure);
57913
57847
  };
57914
- }, [enabled, minPx]);
57915
- return {
57916
- ref,
57917
- height
57918
- };
57848
+ }, [anchor, enabled]);
57849
+ return box;
57919
57850
  }
57920
- function StepInspectorLayout({ list, detail, mobileDetailOpen, onMobileClose, minHeightPx = 420, className }) {
57851
+ function StepDetailDock({ open, anchor, onClose, label, children }) {
57921
57852
  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
- })]
57853
+ const box = useDockBox(anchor, open && !isMobile);
57854
+ if (isMobile) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dialog, {
57855
+ open,
57856
+ onOpenChange: (next) => !next && onClose(),
57857
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogContent, {
57858
+ placement: "sheet-on-mobile",
57859
+ scroll: "body",
57860
+ "aria-label": label,
57861
+ 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)]",
57862
+ children: open && children
57952
57863
  })
57953
57864
  });
57865
+ if (!open) return null;
57866
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("aside", {
57867
+ "aria-label": label,
57868
+ "data-step-dock": true,
57869
+ className: "fixed z-30 flex flex-col border-l border-border bg-surface shadow-2xl",
57870
+ style: box !== null ? {
57871
+ top: box.top,
57872
+ height: box.height,
57873
+ right: box.right,
57874
+ width: box.width
57875
+ } : {
57876
+ top: 0,
57877
+ bottom: 0,
57878
+ right: 0,
57879
+ width: 432
57880
+ },
57881
+ children
57882
+ });
57954
57883
  }
57955
- /** "‹ Steps" — the phone sheet's way back to the list. */
57884
+ /** "‹ Steps" — the phone sheet's way back to the matrix. */
57956
57885
  function BackToStepsButton({ onClick }) {
57957
57886
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
57958
57887
  type: "button",
@@ -57968,36 +57897,42 @@ function BackToStepsButton({ onClick }) {
57968
57897
  //#region src/composites/step-inspector/step-inspector-panel.tsx
57969
57898
  /**
57970
57899
  * StepInspectorPanel — the detail pane of the step inspector: a pinned header
57971
- * (the step, its gate, the accelerator tabs), ONE scrolling body, and a pinned
57972
- * save bar. Nothing in it depends on the page's scroll position — the slide-over
57900
+ * (the clicked step and accelerator, the gate), ONE scrolling body, and a
57901
+ * pinned save bar (with the "applies to" choice, when there is one). Nothing in it depends on the page's scroll position — the slide-over
57973
57902
  * it replaced was anchored to the matrix block and scrolled away with it.
57974
57903
  */
57975
- function SaveBar({ dirtyCount, saving, status, error, onSave, onDiscard }) {
57904
+ function SaveBar({ dirtyCount, saving, status, error, onSave, onDiscard, scope }) {
57976
57905
  const dirty = dirtyCount > 0;
57977
57906
  const message = saving ? "Saving…" : dirty ? `${dirtyCount} unsaved change${dirtyCount === 1 ? "" : "s"}` : status === "saved" ? "Saved" : status === "error" ? error ?? "Save failed" : "No unsaved changes";
57978
57907
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
57979
- className: "flex shrink-0 items-center gap-2 border-t border-border bg-surface px-4 py-2 pb-[max(0.5rem,env(safe-area-inset-bottom))] md:pb-2",
57980
- children: [
57981
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
57982
- role: "status",
57983
- className: cn("min-w-0 flex-1 truncate text-[11px]", status === "error" && !dirty ? "text-danger" : dirty ? "text-amber-500" : "text-foreground-subtle"),
57984
- children: message
57985
- }),
57986
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
57987
- variant: "outline",
57988
- size: "md",
57989
- disabled: !dirty || saving,
57990
- onClick: onDiscard,
57991
- children: "Discard"
57992
- }),
57993
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
57994
- variant: "primary",
57995
- size: "md",
57996
- disabled: !dirty || saving,
57997
- onClick: onSave,
57998
- children: "Save"
57999
- })
58000
- ]
57908
+ className: "shrink-0 space-y-2 border-t border-border bg-surface px-4 py-2 pb-[max(0.5rem,env(safe-area-inset-bottom))] md:pb-2",
57909
+ children: [scope !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
57910
+ "data-save-scope": true,
57911
+ children: scope
57912
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
57913
+ className: "flex items-center gap-2",
57914
+ children: [
57915
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
57916
+ role: "status",
57917
+ className: cn("min-w-0 flex-1 truncate text-[11px]", status === "error" && !dirty ? "text-danger" : dirty ? "text-amber-500" : "text-foreground-subtle"),
57918
+ children: message
57919
+ }),
57920
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
57921
+ variant: "outline",
57922
+ size: "md",
57923
+ disabled: !dirty || saving,
57924
+ onClick: onDiscard,
57925
+ children: "Discard"
57926
+ }),
57927
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
57928
+ variant: "primary",
57929
+ size: "md",
57930
+ disabled: !dirty || saving,
57931
+ onClick: onSave,
57932
+ children: "Save"
57933
+ })
57934
+ ]
57935
+ })]
58001
57936
  });
58002
57937
  }
58003
57938
  var StepInspectorPanel = (0, react$1.forwardRef)(function StepInspectorPanel({ title, subtitle, headerActions, headerExtra, leading, saveBar, onEscape, children, className }, headingRef) {
@@ -58024,7 +57959,8 @@ var StepInspectorPanel = (0, react$1.forwardRef)(function StepInspectorPanel({ t
58024
57959
  id: headingId,
58025
57960
  ref: headingRef,
58026
57961
  tabIndex: -1,
58027
- className: "truncate text-sm font-semibold text-foreground outline-none focus-visible:ring-2 focus-visible:ring-primary rounded",
57962
+ title,
57963
+ className: "line-clamp-2 break-words text-sm font-semibold leading-snug text-foreground outline-none focus-visible:ring-2 focus-visible:ring-primary rounded",
58028
57964
  children: title
58029
57965
  }), subtitle !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
58030
57966
  className: "mt-0.5 text-[11px] text-foreground-subtle",
@@ -58045,192 +57981,6 @@ var StepInspectorPanel = (0, react$1.forwardRef)(function StepInspectorPanel({ t
58045
57981
  });
58046
57982
  });
58047
57983
  //#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
57984
  //#region src/composites/step-inspector/step-model-row.tsx
58235
57985
  var SOURCE_TONE = {
58236
57986
  camera: "border-primary/40 bg-primary/10 text-primary",
@@ -58309,81 +58059,28 @@ function StepModelRow({ label, format, modelId, source, sourceLabel, models, def
58309
58059
  });
58310
58060
  }
58311
58061
  //#endregion
58312
- //#region src/composites/step-inspector/step-model-list.tsx
58313
- /**
58314
- * StepModelList — the model section of the step inspector.
58315
- *
58316
- * While nothing is pinned, edited or worth a note, the accelerators are
58317
- * summarised by model — one line per distinct model, naming the accelerators
58318
- * that run it ("yolov9m-320-int8 · hub iGPU, hub NPU, little-unraid iGPU") —
58319
- * with "Per accelerator" to open one `StepModelRow` each. Four near-identical
58320
- * rows pushed the settings, the thing most edits are about, below the fold.
58321
- * A pin, a pending change or a note (no build here, root-supplied) shows every
58322
- * row, so nothing that differs from the default is ever folded away.
58323
- */
58324
- /** True when the rows can be summarised: nothing pinned here, edited, or noted. */
58325
- function modelRowsAreFoldable(rows) {
58326
- return rows.length >= 2 && rows.every((r) => r.note === void 0 && r.onReset === void 0 && r.changed !== true && !r.expanded);
58062
+ //#region src/composites/step-inspector/step-search.ts
58063
+ function matches(text, tokens) {
58064
+ const lower = text.toLowerCase();
58065
+ return tokens.every((t) => lower.includes(t));
58327
58066
  }
58328
- function groupByModel(rows) {
58329
- const groups = [];
58067
+ /** Steps matching `query` (all tokens in the name, or all in one field label). Empty query = all. */
58068
+ function searchSteps(rows, query) {
58069
+ const tokens = query.trim().toLowerCase().split(/\s+/).filter(Boolean);
58070
+ if (tokens.length === 0) return rows.map((row) => ({ addonId: row.addonId }));
58071
+ const hits = [];
58330
58072
  for (const row of rows) {
58331
- const modelId = row.modelId ?? "—";
58332
- const index = groups.findIndex((g) => g.modelId === modelId && g.sourceLabel === row.sourceLabel);
58333
- const existing = groups[index];
58334
- if (existing !== void 0) groups[index] = {
58335
- ...existing,
58336
- labels: [...existing.labels, row.label]
58337
- };
58338
- else groups.push({
58339
- modelId,
58340
- labels: [row.label],
58341
- sourceLabel: row.sourceLabel
58342
- });
58343
- }
58344
- return groups;
58345
- }
58346
- function StepModelList({ rows }) {
58347
- const [open, setOpen] = (0, react$1.useState)(false);
58348
- if (!open && modelRowsAreFoldable(rows)) {
58349
- const groups = groupByModel(rows);
58350
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
58351
- className: cn("rounded-md border border-border", LIST_ROW),
58352
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
58353
- className: "space-y-1",
58354
- children: groups.map((g) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
58355
- className: "flex flex-wrap items-baseline gap-x-2 text-xs",
58356
- children: [
58357
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
58358
- className: "font-mono text-foreground",
58359
- children: g.modelId
58360
- }),
58361
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
58362
- className: "min-w-0 flex-1 text-[11px] text-foreground-subtle",
58363
- children: g.labels.length === rows.length ? `all ${rows.length} accelerators` : g.labels.join(", ")
58364
- }),
58365
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
58366
- className: "rounded border border-border px-1.5 py-px text-[10px] font-medium text-foreground-subtle",
58367
- children: g.sourceLabel
58368
- })
58369
- ]
58370
- }, `${g.modelId}|${g.sourceLabel}`))
58371
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
58372
- className: "mt-1.5 flex justify-end",
58373
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
58374
- type: "button",
58375
- className: cn(BTN_COMPACT, "min-h-9 md:min-h-0"),
58376
- "aria-expanded": false,
58377
- onClick: () => setOpen(true),
58378
- children: "Per accelerator"
58379
- })
58380
- })]
58073
+ if (matches(row.name, tokens)) {
58074
+ hits.push({ addonId: row.addonId });
58075
+ continue;
58076
+ }
58077
+ const field = row.fieldLabels.find((l) => matches(l, tokens));
58078
+ if (field !== void 0) hits.push({
58079
+ addonId: row.addonId,
58080
+ matchedField: field
58381
58081
  });
58382
58082
  }
58383
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
58384
- className: "space-y-2",
58385
- children: rows.map(({ id, ...row }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepModelRow, { ...row }, id))
58386
- });
58083
+ return hits;
58387
58084
  }
58388
58085
  //#endregion
58389
58086
  //#region src/composites/step-tree.ts
@@ -59016,29 +58713,19 @@ function StreamPanel({ serverUrl, createSession, sendAnswer, handleOffer, getIce
59016
58713
  children: [
59017
58714
  showStreamStats && streamStats && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
59018
58715
  className: "px-3 py-1.5 text-[10px] text-foreground-subtle border-b border-border-subtle",
59019
- children: [
59020
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
59021
- className: "flex justify-between gap-3",
59022
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Input" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
59023
- className: "tabular-nums text-foreground",
59024
- children: [streamStats.inputFps.toFixed(1), " fps"]
59025
- })]
59026
- }),
59027
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
59028
- className: "flex justify-between gap-3",
59029
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Decode" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
59030
- className: "tabular-nums text-foreground",
59031
- children: [streamStats.decodeFps.toFixed(1), " fps"]
59032
- })]
59033
- }),
59034
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
59035
- className: "flex justify-between gap-3",
59036
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Uptime" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
59037
- className: "tabular-nums text-foreground",
59038
- children: fmtUptime(streamStats.uptimeMs)
59039
- })]
59040
- })
59041
- ]
58716
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
58717
+ className: "flex justify-between gap-3",
58718
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Input" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
58719
+ className: "tabular-nums text-foreground",
58720
+ children: [streamStats.inputFps.toFixed(1), " fps"]
58721
+ })]
58722
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
58723
+ className: "flex justify-between gap-3",
58724
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Uptime" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
58725
+ className: "tabular-nums text-foreground",
58726
+ children: fmtUptime(streamStats.uptimeMs)
58727
+ })]
58728
+ })]
59042
58729
  }),
59043
58730
  !showStreamStats && choiceStats && choiceStats.inputFps > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
59044
58731
  className: "px-3 py-1.5 text-[10px] text-foreground-subtle border-b border-border-subtle",
@@ -61331,7 +61018,6 @@ var SceneMonitorEditor = (0, react$1.lazy)(() => Promise.resolve().then(() => re
61331
61018
  /** Lazy-wrapped `PrivacyMaskSettings` — code-split off the main bundle. */
61332
61019
  var PrivacyMaskSettings = (0, react$1.lazy)(() => Promise.resolve().then(() => require("./PrivacyMaskSettings-BqjXsXZR.cjs")).then((m) => ({ default: m.PrivacyMaskSettings })));
61333
61020
  //#endregion
61334
- exports.AcceleratorTabs = AcceleratorTabs;
61335
61021
  exports.AddonGlobalSettingsForm = AddonGlobalSettingsForm;
61336
61022
  exports.AdminLinkQuickAction = AdminLinkQuickAction;
61337
61023
  exports.AgentStepEditor = AgentStepEditor;
@@ -61589,6 +61275,7 @@ exports.SPLIT_PANEL_OUTER = SPLIT_PANEL_OUTER;
61589
61275
  exports.SPLIT_PANEL_SIDE = SPLIT_PANEL_SIDE;
61590
61276
  exports.STACK_GAP = STACK_GAP;
61591
61277
  exports.STATE_COLOR = STATE_COLOR;
61278
+ exports.STEP_DOCK_WIDTH_PX = STEP_DOCK_WIDTH_PX;
61592
61279
  exports.SceneMonitorEditor = SceneMonitorEditor;
61593
61280
  exports.ScopePicker = ScopePicker;
61594
61281
  exports.ScriptHeroCard = ScriptHeroCard;
@@ -61614,11 +61301,8 @@ exports.Square = Square;
61614
61301
  exports.StatCard = StatCard;
61615
61302
  exports.StateValuesStream = StateValuesStream;
61616
61303
  exports.StatusBadge = StatusBadge;
61617
- exports.StepChips = StepChips;
61618
- exports.StepInspectorLayout = StepInspectorLayout;
61304
+ exports.StepDetailDock = StepDetailDock;
61619
61305
  exports.StepInspectorPanel = StepInspectorPanel;
61620
- exports.StepList = StepList;
61621
- exports.StepModelList = StepModelList;
61622
61306
  exports.StepModelRow = StepModelRow;
61623
61307
  exports.StepTimings = StepTimings;
61624
61308
  exports.Stepper = Stepper;
@@ -61667,6 +61351,8 @@ exports.WidgetPanel = WidgetPanel;
61667
61351
  exports.WidgetRegistryProvider = WidgetRegistryProvider;
61668
61352
  exports.WidgetSlot = WidgetSlot;
61669
61353
  exports.ZoneEditingProvider = ZoneEditingProvider;
61354
+ exports.acceleratorLabel = acceleratorLabel;
61355
+ exports.acceleratorShortLabel = acceleratorShortLabel;
61670
61356
  exports.agentColumnKey = agentColumnKey;
61671
61357
  exports.allDeviceTypeFilterOptions = allDeviceTypeFilterOptions;
61672
61358
  exports.applyFleetQueryDefaults = applyFleetQueryDefaults;
@@ -61714,12 +61400,12 @@ exports.deviceTypeLabel = deviceTypeLabel;
61714
61400
  exports.deviceTypeMeta = deviceTypeMeta;
61715
61401
  exports.deviceTypeMetaOf = deviceTypeMetaOf;
61716
61402
  exports.devicesToOptions = devicesToOptions;
61403
+ exports.dockBox = dockBox;
61717
61404
  exports.draftFromQuery = draftFromQuery;
61718
61405
  exports.emptyDevicesWidens = emptyDevicesWidens;
61719
61406
  exports.emptySessionRun = emptySessionRun;
61720
61407
  exports.ensureMfHostInit = ensureMfHostInit;
61721
61408
  exports.eventKindLabel = eventKindLabel;
61722
- exports.fillHeightPx = fillHeightPx;
61723
61409
  exports.filterDataTableRows = filterDataTableRows;
61724
61410
  exports.filterDeviceOptions = filterDeviceOptions;
61725
61411
  exports.filterOptionSelectOptions = filterOptions;
@@ -61764,9 +61450,9 @@ exports.makeScrubBridge = makeScrubBridge;
61764
61450
  exports.metadataEntries = metadataEntries;
61765
61451
  exports.metadataString = metadataString;
61766
61452
  exports.mirror = mirror;
61767
- exports.modelRowsAreFoldable = modelRowsAreFoldable;
61768
61453
  exports.mountAddonPage = mountAddonPage;
61769
61454
  exports.mountPath = mountPath;
61455
+ exports.nextGridPosition = nextGridPosition;
61770
61456
  exports.nextReconnectAction = nextReconnectAction;
61771
61457
  exports.nextSort = nextSort;
61772
61458
  exports.nextSortDirection = nextSortDirection;
@@ -62049,20 +61735,8 @@ exports.useDayNightGetOptions = useDayNightGetOptions;
62049
61735
  exports.useDayNightGetStatus = useDayNightGetStatus;
62050
61736
  exports.useDayNightSetSettings = useDayNightSetSettings;
62051
61737
  exports.useDebouncedString = useDebouncedString;
62052
- exports.useDecoderCreateSession = useDecoderCreateSession;
62053
- exports.useDecoderDestroySession = useDecoderDestroySession;
62054
- exports.useDecoderGetFrame = useDecoderGetFrame;
62055
61738
  exports.useDecoderGetInfo = useDecoderGetInfo;
62056
- exports.useDecoderGetShmStats = useDecoderGetShmStats;
62057
- exports.useDecoderGetStats = useDecoderGetStats;
62058
- exports.useDecoderListActiveSessions = useDecoderListActiveSessions;
62059
- exports.useDecoderOpenStream = useDecoderOpenStream;
62060
- exports.useDecoderPullFrames = useDecoderPullFrames;
62061
- exports.useDecoderPullHandles = useDecoderPullHandles;
62062
- exports.useDecoderPushPacket = useDecoderPushPacket;
62063
61739
  exports.useDecoderReprobeHwaccel = useDecoderReprobeHwaccel;
62064
- exports.useDecoderSupportsCodec = useDecoderSupportsCodec;
62065
- exports.useDecoderUpdateConfig = useDecoderUpdateConfig;
62066
61740
  exports.useDetectionPipelineApplyDeviceSettingsPatch = useDetectionPipelineApplyDeviceSettingsPatch;
62067
61741
  exports.useDetectionPipelineGetDeviceLiveContribution = useDetectionPipelineGetDeviceLiveContribution;
62068
61742
  exports.useDetectionPipelineGetDeviceSettingsContribution = useDetectionPipelineGetDeviceSettingsContribution;
@@ -62586,7 +62260,6 @@ exports.usePipelineExecutorCacheFrameInPool = usePipelineExecutorCacheFrameInPoo
62586
62260
  exports.usePipelineExecutorClearDeviceOverrides = usePipelineExecutorClearDeviceOverrides;
62587
62261
  exports.usePipelineExecutorClearModelBoundDeviceOverrides = usePipelineExecutorClearModelBoundDeviceOverrides;
62588
62262
  exports.usePipelineExecutorDeleteModel = usePipelineExecutorDeleteModel;
62589
- exports.usePipelineExecutorDeleteTemplate = usePipelineExecutorDeleteTemplate;
62590
62263
  exports.usePipelineExecutorDownloadModel = usePipelineExecutorDownloadModel;
62591
62264
  exports.usePipelineExecutorGetAddonModels = usePipelineExecutorGetAddonModels;
62592
62265
  exports.usePipelineExecutorGetAudioCapabilities = usePipelineExecutorGetAudioCapabilities;
@@ -62610,22 +62283,18 @@ exports.usePipelineExecutorInferCached = usePipelineExecutorInferCached;
62610
62283
  exports.usePipelineExecutorKillEngine = usePipelineExecutorKillEngine;
62611
62284
  exports.usePipelineExecutorListLoadedEngines = usePipelineExecutorListLoadedEngines;
62612
62285
  exports.usePipelineExecutorListReferenceImages = usePipelineExecutorListReferenceImages;
62613
- exports.usePipelineExecutorListTemplates = usePipelineExecutorListTemplates;
62614
62286
  exports.usePipelineExecutorRearmInferenceDevice = usePipelineExecutorRearmInferenceDevice;
62615
62287
  exports.usePipelineExecutorRunAudioTest = usePipelineExecutorRunAudioTest;
62616
62288
  exports.usePipelineExecutorRunPipeline = usePipelineExecutorRunPipeline;
62617
62289
  exports.usePipelineExecutorRunPipelineBatch = usePipelineExecutorRunPipelineBatch;
62618
- exports.usePipelineExecutorSaveTemplate = usePipelineExecutorSaveTemplate;
62619
62290
  exports.usePipelineExecutorSetVideoPipelineSteps = usePipelineExecutorSetVideoPipelineSteps;
62620
62291
  exports.usePipelineExecutorSpinEngine = usePipelineExecutorSpinEngine;
62621
62292
  exports.usePipelineExecutorUncacheFrame = usePipelineExecutorUncacheFrame;
62622
- exports.usePipelineExecutorUpdateTemplate = usePipelineExecutorUpdateTemplate;
62623
62293
  exports.usePipelineExecutorValidatePipeline = usePipelineExecutorValidatePipeline;
62624
62294
  exports.usePipelineOrchestratorApplyAutoTune = usePipelineOrchestratorApplyAutoTune;
62625
62295
  exports.usePipelineOrchestratorApplyDeviceSettingsPatch = usePipelineOrchestratorApplyDeviceSettingsPatch;
62626
62296
  exports.usePipelineOrchestratorAssignAudio = usePipelineOrchestratorAssignAudio;
62627
62297
  exports.usePipelineOrchestratorAssignPipeline = usePipelineOrchestratorAssignPipeline;
62628
- exports.usePipelineOrchestratorDeleteTemplate = usePipelineOrchestratorDeleteTemplate;
62629
62298
  exports.usePipelineOrchestratorGetAgentLoad = usePipelineOrchestratorGetAgentLoad;
62630
62299
  exports.usePipelineOrchestratorGetAgentSettings = usePipelineOrchestratorGetAgentSettings;
62631
62300
  exports.usePipelineOrchestratorGetAudioAssignment = usePipelineOrchestratorGetAudioAssignment;
@@ -62651,7 +62320,6 @@ exports.usePipelineOrchestratorGetPipelineDevicePin = usePipelineOrchestratorGet
62651
62320
  exports.usePipelineOrchestratorGetPlacementStats = usePipelineOrchestratorGetPlacementStats;
62652
62321
  exports.usePipelineOrchestratorGetReconcileFromDiskStatus = usePipelineOrchestratorGetReconcileFromDiskStatus;
62653
62322
  exports.usePipelineOrchestratorListAgentSettings = usePipelineOrchestratorListAgentSettings;
62654
- exports.usePipelineOrchestratorListTemplates = usePipelineOrchestratorListTemplates;
62655
62323
  exports.usePipelineOrchestratorPauseForStorageMigration = usePipelineOrchestratorPauseForStorageMigration;
62656
62324
  exports.usePipelineOrchestratorPlanAutoTune = usePipelineOrchestratorPlanAutoTune;
62657
62325
  exports.usePipelineOrchestratorRebalance = usePipelineOrchestratorRebalance;
@@ -62661,7 +62329,6 @@ exports.usePipelineOrchestratorRequestOccupancyLook = usePipelineOrchestratorReq
62661
62329
  exports.usePipelineOrchestratorResetNodePipelineDefaults = usePipelineOrchestratorResetNodePipelineDefaults;
62662
62330
  exports.usePipelineOrchestratorResolvePipeline = usePipelineOrchestratorResolvePipeline;
62663
62331
  exports.usePipelineOrchestratorResumeForStorageMigration = usePipelineOrchestratorResumeForStorageMigration;
62664
- exports.usePipelineOrchestratorSaveTemplate = usePipelineOrchestratorSaveTemplate;
62665
62332
  exports.usePipelineOrchestratorSetAgentCapabilities = usePipelineOrchestratorSetAgentCapabilities;
62666
62333
  exports.usePipelineOrchestratorSetAgentDecodeLimits = usePipelineOrchestratorSetAgentDecodeLimits;
62667
62334
  exports.usePipelineOrchestratorSetAgentInferenceDevices = usePipelineOrchestratorSetAgentInferenceDevices;
@@ -62680,7 +62347,6 @@ exports.usePipelineOrchestratorSetShareSource = usePipelineOrchestratorSetShareS
62680
62347
  exports.usePipelineOrchestratorSuggestShares = usePipelineOrchestratorSuggestShares;
62681
62348
  exports.usePipelineOrchestratorUnassignAudio = usePipelineOrchestratorUnassignAudio;
62682
62349
  exports.usePipelineOrchestratorUnassignPipeline = usePipelineOrchestratorUnassignPipeline;
62683
- exports.usePipelineOrchestratorUpdateTemplate = usePipelineOrchestratorUpdateTemplate;
62684
62350
  exports.usePipelineRunnerAttachCamera = usePipelineRunnerAttachCamera;
62685
62351
  exports.usePipelineRunnerDetachCamera = usePipelineRunnerDetachCamera;
62686
62352
  exports.usePipelineRunnerGetAllCameraMetrics = usePipelineRunnerGetAllCameraMetrics;
@@ -62903,7 +62569,6 @@ exports.useStreamBrokerProbeStream = useStreamBrokerProbeStream;
62903
62569
  exports.useStreamBrokerProduceEventMedia = useStreamBrokerProduceEventMedia;
62904
62570
  exports.useStreamBrokerPublishCameraStream = useStreamBrokerPublishCameraStream;
62905
62571
  exports.useStreamBrokerPullAudioChunks = useStreamBrokerPullAudioChunks;
62906
- exports.useStreamBrokerPullFrameHandles = useStreamBrokerPullFrameHandles;
62907
62572
  exports.useStreamBrokerRegenerateRtspToken = useStreamBrokerRegenerateRtspToken;
62908
62573
  exports.useStreamBrokerReleaseEgressTranscode = useStreamBrokerReleaseEgressTranscode;
62909
62574
  exports.useStreamBrokerReleaseStreamWithCodec = useStreamBrokerReleaseStreamWithCodec;
@@ -62915,10 +62580,8 @@ exports.useStreamBrokerSetDeviceAudioMute = useStreamBrokerSetDeviceAudioMute;
62915
62580
  exports.useStreamBrokerSetPreBufferDuration = useStreamBrokerSetPreBufferDuration;
62916
62581
  exports.useStreamBrokerSetRtspEnabled = useStreamBrokerSetRtspEnabled;
62917
62582
  exports.useStreamBrokerSubscribeAudioChunks = useStreamBrokerSubscribeAudioChunks;
62918
- exports.useStreamBrokerSubscribeFrames = useStreamBrokerSubscribeFrames;
62919
62583
  exports.useStreamBrokerUnassignProfile = useStreamBrokerUnassignProfile;
62920
62584
  exports.useStreamBrokerUnsubscribeAudioChunks = useStreamBrokerUnsubscribeAudioChunks;
62921
- exports.useStreamBrokerUnsubscribeFrames = useStreamBrokerUnsubscribeFrames;
62922
62585
  exports.useStreamCatalogGetCatalog = useStreamCatalogGetCatalog;
62923
62586
  exports.useStreamParamsGetConfigSchema = useStreamParamsGetConfigSchema;
62924
62587
  exports.useStreamParamsGetOptions = useStreamParamsGetOptions;