@camstack/ui-library 1.2.244 → 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/composites/accelerator-label.d.ts +17 -0
- package/dist/composites/agent-step-editor.d.ts +41 -16
- package/dist/composites/device-step-matrix.d.ts +26 -26
- package/dist/composites/index.d.ts +10 -12
- package/dist/composites/model-bound-slider.d.ts +8 -1
- package/dist/composites/pipeline-builder.d.ts +2 -37
- package/dist/composites/pipeline-matrix-shared.d.ts +11 -1
- package/dist/composites/pipeline-step.d.ts +4 -44
- package/dist/composites/step-inspector/accelerator-tabs.d.ts +12 -0
- package/dist/composites/step-inspector/gate-control.d.ts +20 -0
- package/dist/composites/step-inspector/index.d.ts +10 -0
- package/dist/composites/step-inspector/layered-settings-form.d.ts +38 -0
- package/dist/composites/step-inspector/segmented-choice.d.ts +11 -0
- package/dist/composites/step-inspector/setting-values.d.ts +19 -0
- package/dist/composites/step-inspector/step-detail-dock.d.ts +33 -0
- package/dist/composites/step-inspector/step-inspector-panel.d.ts +26 -0
- package/dist/composites/step-inspector/step-model-list.d.ts +9 -0
- package/dist/composites/step-inspector/step-model-row.d.ts +27 -0
- package/dist/composites/step-inspector/step-search.d.ts +18 -0
- package/dist/composites/{step-tree-master.d.ts → step-tree.d.ts} +0 -12
- package/dist/generated/system-hooks.d.ts +0 -16
- package/dist/index.cjs +1431 -1286
- package/dist/index.js +1407 -1278
- package/package.json +1 -1
- package/dist/composites/pipeline-tree-matrix.d.ts +0 -18
- package/dist/composites/slide-over-panel.d.ts +0 -35
- package/dist/lib/validate-template.d.ts +0 -2
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,30 +28695,43 @@ function AddonGlobalSettingsForm({ trpc, addonId, nodeId, title, disabled, onAft
|
|
|
28746
28695
|
});
|
|
28747
28696
|
}
|
|
28748
28697
|
//#endregion
|
|
28749
|
-
//#region src/
|
|
28750
|
-
function
|
|
28751
|
-
|
|
28752
|
-
|
|
28753
|
-
|
|
28754
|
-
|
|
28755
|
-
|
|
28756
|
-
|
|
28757
|
-
|
|
28758
|
-
|
|
28759
|
-
|
|
28760
|
-
|
|
28761
|
-
|
|
28762
|
-
|
|
28763
|
-
|
|
28764
|
-
|
|
28765
|
-
|
|
28766
|
-
|
|
28767
|
-
|
|
28768
|
-
|
|
28769
|
-
|
|
28770
|
-
|
|
28771
|
-
|
|
28772
|
-
}
|
|
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;
|
|
28773
28735
|
}
|
|
28774
28736
|
//#endregion
|
|
28775
28737
|
//#region src/composites/grouped-model-selector.tsx
|
|
@@ -29052,6 +29014,257 @@ function ModelPicker({ models, value, onChange, disabled, defaultModelId }) {
|
|
|
29052
29014
|
});
|
|
29053
29015
|
}
|
|
29054
29016
|
//#endregion
|
|
29017
|
+
//#region src/composites/resolve-step-default-model.ts
|
|
29018
|
+
function resolveStepDefaultModel(catalog, format) {
|
|
29019
|
+
const hasFormatBuild = (modelId) => catalog.models.find((m) => m.id === modelId)?.formats[format] !== void 0;
|
|
29020
|
+
const declared = catalog.defaultModelIdByFormat?.[format];
|
|
29021
|
+
if (declared !== void 0 && hasFormatBuild(declared)) return declared;
|
|
29022
|
+
if (hasFormatBuild(catalog.defaultModelId)) return catalog.defaultModelId;
|
|
29023
|
+
const available = catalog.models.filter((m) => m.formats[format] !== void 0 && m.legacy !== true);
|
|
29024
|
+
if (available.length === 0) return catalog.defaultModelId;
|
|
29025
|
+
return [...available].toSorted((a, b) => (a.formats[format]?.sizeMB ?? Infinity) - (b.formats[format]?.sizeMB ?? Infinity))[0].id;
|
|
29026
|
+
}
|
|
29027
|
+
/**
|
|
29028
|
+
* The model a matrix CELL shows as this step's effective default on `device`.
|
|
29029
|
+
*
|
|
29030
|
+
* The device's ELECTED model (`device.defaultModelId`, the per-device
|
|
29031
|
+
* object-detection election) applies ONLY when this step's catalog actually
|
|
29032
|
+
* contains it — `slot === 'detector'` alone is too wide, because
|
|
29033
|
+
* package-detection shares the detector slot with the root step. Bit live
|
|
29034
|
+
* 2026-08-08: rfdetr-package became the package step's default and every
|
|
29035
|
+
* matrix cell still showed the device's yolo as the "selected" model, because
|
|
29036
|
+
* the election was applied slot-wide. Membership is the principled gate: an
|
|
29037
|
+
* elected model this step cannot run is never displayed for it.
|
|
29038
|
+
*/
|
|
29039
|
+
function resolveEffectiveDefaultModel(catalog, device) {
|
|
29040
|
+
const format = device?.format;
|
|
29041
|
+
if (format === void 0) return void 0;
|
|
29042
|
+
const elected = device?.defaultModelId;
|
|
29043
|
+
return catalog.slot === "detector" && elected !== void 0 && catalog.models.some((m) => m.id === elected) ? elected : resolveStepDefaultModel(catalog, format);
|
|
29044
|
+
}
|
|
29045
|
+
/**
|
|
29046
|
+
* The models a step may run on an engine `format` — the ONE derivation of the
|
|
29047
|
+
* option list. An unknown format yields NOTHING rather than everything: a
|
|
29048
|
+
* picker that offered a model with no build for the device would write a pin
|
|
29049
|
+
* the runner cannot honour.
|
|
29050
|
+
*/
|
|
29051
|
+
function stepModelOptions(models, format) {
|
|
29052
|
+
if (format === void 0) return [];
|
|
29053
|
+
return models.filter((m) => m.formats[format] !== void 0);
|
|
29054
|
+
}
|
|
29055
|
+
/** Can this step run on `format` at all? (`false` ⇒ the caller renders "⚠ skip"/"—".) */
|
|
29056
|
+
function stepHasModelForFormat(models, format) {
|
|
29057
|
+
return stepModelOptions(models, format).length > 0;
|
|
29058
|
+
}
|
|
29059
|
+
/**
|
|
29060
|
+
* The model a step ACTUALLY runs for a (device, camera) — the whole effective
|
|
29061
|
+
* chain in one place: per-camera override → per-(node,device) base → the
|
|
29062
|
+
* resolved default (`resolveEffectiveDefaultModel`).
|
|
29063
|
+
*
|
|
29064
|
+
* Every surface calls this: the matrix cell, and the drawer's picker seed. They
|
|
29065
|
+
* are the same number, and the only way to keep them the same number is to have
|
|
29066
|
+
* one function produce it. A surface that owns fewer layers omits them (the node
|
|
29067
|
+
* matrix passes no `override`) — it does NOT get its own resolver.
|
|
29068
|
+
*/
|
|
29069
|
+
function resolveEffectiveStepModel(input) {
|
|
29070
|
+
return input.override ?? input.base ?? resolveEffectiveDefaultModel(input.catalog, input.device);
|
|
29071
|
+
}
|
|
29072
|
+
//#endregion
|
|
29073
|
+
//#region src/composites/root-model-source.tsx
|
|
29074
|
+
/**
|
|
29075
|
+
* "Use the root model" — the step editor's control for a step the camera's
|
|
29076
|
+
* ROOT detection model can supply (D730).
|
|
29077
|
+
*
|
|
29078
|
+
* The flag (`useRootModel`, in the step's own settings) is an operator INTENT
|
|
29079
|
+
* kept beside the step's model: checking it never changes the model, and a
|
|
29080
|
+
* root that cannot supply the step leaves the step running its own model while
|
|
29081
|
+
* the flag waits, inactive, for a root that can. Whether it is in force is
|
|
29082
|
+
* `resolveRootSupply` — the same function the runner decides with.
|
|
29083
|
+
*/
|
|
29084
|
+
/** The editor's view of the flag for one step, or `null` for a step no root can supply. */
|
|
29085
|
+
function rootSourceState(stepId, settings, rootModel) {
|
|
29086
|
+
if (!(0, _camstack_types.isRootSuppliableStepId)(stepId)) return null;
|
|
29087
|
+
return (0, _camstack_types.resolveRootSupply)(stepId, settings, rootModel?.supplies);
|
|
29088
|
+
}
|
|
29089
|
+
/** The settings bag with the flag set — every other key untouched. */
|
|
29090
|
+
function withUseRootModel(settings, value) {
|
|
29091
|
+
return {
|
|
29092
|
+
...settings,
|
|
29093
|
+
[_camstack_types.USE_ROOT_MODEL_SETTING_KEY]: value
|
|
29094
|
+
};
|
|
29095
|
+
}
|
|
29096
|
+
/** What the operator must know about a supply, beyond "it is on". */
|
|
29097
|
+
function supplyNotes(supply) {
|
|
29098
|
+
if (supply.kind === "label") {
|
|
29099
|
+
const notes = supply.uncovered.length > 0 ? [`Not produced by this model: ${supply.uncovered.join(", ")}.`] : [];
|
|
29100
|
+
if (supply.stepId === "package-detection") return [...notes, "The root model's own package boxes become the package result at this step's threshold; this step's zone-crop view is not used, so small parcels the full-frame model misses stay missed."];
|
|
29101
|
+
return [...notes, "This step's class list decides which of the root's labels become the fine class; a switched-off class stays at its macro."];
|
|
29102
|
+
}
|
|
29103
|
+
if (supply.kind === "mask") return ["Masks come from the root model: each detection carries the root's own instance mask, so this step's model does not run. A root mask follows the model's mask grid (a quarter of its input), coarser than this step's own per-crop mask."];
|
|
29104
|
+
if (supply.stepId === "face-detection") return ["Face boxes come from the root model. Each is re-found by this step's face detector in a square around it before recognition, so only a face it finds is recognised; a box with no face in it is dropped."];
|
|
29105
|
+
return ["Plate boxes come from the root model and the plate reader reads them. The crop margin around a root plate box has not been measured for this model yet."];
|
|
29106
|
+
}
|
|
29107
|
+
/** The reason an intended supply is not in force, in the operator's words. */
|
|
29108
|
+
function inactiveText(state, rootModel) {
|
|
29109
|
+
switch (state.inactiveReason) {
|
|
29110
|
+
case "root-input-too-small": return `the current root model (${rootModel.name}) sees the frame too small to find faces reliably (it needs an input of 640 px or more).`;
|
|
29111
|
+
case "root-does-not-declare":
|
|
29112
|
+
case void 0: return `the current root model (${rootModel.name}) cannot supply this.`;
|
|
29113
|
+
}
|
|
29114
|
+
}
|
|
29115
|
+
function RootModelSourceControl({ stepId, settings, rootModel, disabled, onChange }) {
|
|
29116
|
+
if (rootModel === void 0) return null;
|
|
29117
|
+
const state = rootSourceState(stepId, settings, rootModel);
|
|
29118
|
+
if (state === null) return null;
|
|
29119
|
+
const inactiveNote = state.inactive ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29120
|
+
className: "rounded bg-muted/40 px-3 py-2 text-[11px] text-foreground-subtle",
|
|
29121
|
+
"data-testid": "root-model-inactive",
|
|
29122
|
+
children: [
|
|
29123
|
+
"Use the root model: inactive — ",
|
|
29124
|
+
inactiveText(state, rootModel),
|
|
29125
|
+
" The step runs its own model."
|
|
29126
|
+
]
|
|
29127
|
+
}) : null;
|
|
29128
|
+
if (state.supply === void 0) return inactiveNote;
|
|
29129
|
+
const checked = state.intent;
|
|
29130
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29131
|
+
className: "space-y-1",
|
|
29132
|
+
"data-testid": "root-model-source",
|
|
29133
|
+
children: [
|
|
29134
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
29135
|
+
className: "flex items-center gap-2 text-xs text-foreground",
|
|
29136
|
+
children: [
|
|
29137
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
29138
|
+
type: "checkbox",
|
|
29139
|
+
checked,
|
|
29140
|
+
disabled,
|
|
29141
|
+
onChange: (e) => onChange(e.target.checked)
|
|
29142
|
+
}),
|
|
29143
|
+
"Use the root model (",
|
|
29144
|
+
rootModel.name,
|
|
29145
|
+
")"
|
|
29146
|
+
]
|
|
29147
|
+
}),
|
|
29148
|
+
inactiveNote,
|
|
29149
|
+
state.effective && supplyNotes(state.supply).map((note) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
29150
|
+
className: "text-[10px] leading-snug text-foreground-subtle",
|
|
29151
|
+
children: note
|
|
29152
|
+
}, note))
|
|
29153
|
+
]
|
|
29154
|
+
});
|
|
29155
|
+
}
|
|
29156
|
+
//#endregion
|
|
29157
|
+
//#region src/lib/responsive.ts
|
|
29158
|
+
/**
|
|
29159
|
+
* Responsive design tokens — three-tier system.
|
|
29160
|
+
*
|
|
29161
|
+
* The admin-ui targets three width tiers; every component should pick
|
|
29162
|
+
* the right token from this file instead of hand-coding breakpoints.
|
|
29163
|
+
* Adopting the tokens uniformly is what makes the app feel consistent
|
|
29164
|
+
* across operator devices (mobile / tablet / desktop) and what unlocks
|
|
29165
|
+
* targeted tuning (e.g. shrink every label by 1px in one place).
|
|
29166
|
+
*
|
|
29167
|
+
* Tier definitions (Tailwind breakpoints):
|
|
29168
|
+
*
|
|
29169
|
+
* • **L1 — Compact** (`< 640px`, default = mobile portrait)
|
|
29170
|
+
* Single column, tight padding, smallest text, icon-only buttons
|
|
29171
|
+
* where labels can be inferred. The operator can still see every
|
|
29172
|
+
* stat / action — no horizontal scroll, no clipped text. The user
|
|
29173
|
+
* directive: "molta informazione in poco spazio quando la width è
|
|
29174
|
+
* bassa." [a lot of information in little space when the width is low.]
|
|
29175
|
+
*
|
|
29176
|
+
* • **L2 — Standard** (`sm` — `640–1023px`, tablet + small laptop)
|
|
29177
|
+
* 2-column grids, side panels become visible, button labels return.
|
|
29178
|
+
* Targets ~768px–1024px viewports — the most common operator sizes.
|
|
29179
|
+
*
|
|
29180
|
+
* • **L3 — Spacious** (`lg+` — `≥ 1024px`, desktop)
|
|
29181
|
+
* 4-column grids, generous padding, full-width header text,
|
|
29182
|
+
* side-by-side composite layouts. The operator has elbow room.
|
|
29183
|
+
*
|
|
29184
|
+
* Conventions:
|
|
29185
|
+
* - **Always start from L1** — the bare class is always the compact
|
|
29186
|
+
* variant, then `sm:` adds room at 640px, `lg:` at 1024px. No
|
|
29187
|
+
* desktop-first breakpoints; no max-width queries.
|
|
29188
|
+
* - **Token strings are static** — kept as `as const` so callers can
|
|
29189
|
+
* compose them via `cn()` / template literals without running a
|
|
29190
|
+
* class generator at runtime.
|
|
29191
|
+
* - **Pair the token with a comment** — the comment in the file
|
|
29192
|
+
* names every breakpoint it crosses, so a reader can predict the
|
|
29193
|
+
* layout without going to Tailwind docs.
|
|
29194
|
+
*/
|
|
29195
|
+
/** Container padding for a card-style section (header + body). */
|
|
29196
|
+
var SECTION_CARD = "rounded-lg border border-border bg-surface overflow-hidden";
|
|
29197
|
+
/** Header strip inside a card (between border-b lines). */
|
|
29198
|
+
var SECTION_HEADER = "border-b border-border px-3 sm:px-4 py-1.5 sm:py-2";
|
|
29199
|
+
/** Body padding for a card-style section. */
|
|
29200
|
+
var SECTION_BODY = "p-2 sm:p-3 lg:p-4";
|
|
29201
|
+
/** Gap between cards in a vertical stack. */
|
|
29202
|
+
var STACK_GAP = "space-y-3 sm:space-y-4";
|
|
29203
|
+
/** Gap between cards in a grid (4 cells / row at lg). */
|
|
29204
|
+
var GRID_GAP = "gap-2 sm:gap-3";
|
|
29205
|
+
/** Row padding inside a divided list (e.g. InfoRow). */
|
|
29206
|
+
var LIST_ROW = "px-3 sm:px-4 py-1.5 sm:py-2";
|
|
29207
|
+
/** 1-up at L1, 2-up at L2, 4-up at L3 — quick-stat row. */
|
|
29208
|
+
var GRID_QUICK_STATS = "grid grid-cols-2 sm:grid-cols-2 lg:grid-cols-4";
|
|
29209
|
+
/** 1-up at L1, 2-up at L2+ — paired panels (motion + detection rules). */
|
|
29210
|
+
var GRID_PAIRED = "grid grid-cols-1 lg:grid-cols-2";
|
|
29211
|
+
/** Side-by-side layout that stacks below md (canvas + side panel). */
|
|
29212
|
+
var SPLIT_PANEL_OUTER = "flex flex-col-reverse md:flex-row";
|
|
29213
|
+
/** Sidebar narrow lane in a SPLIT_PANEL_OUTER layout. */
|
|
29214
|
+
var SPLIT_PANEL_SIDE = "w-full md:w-44 lg:w-52 md:flex-shrink-0 border-b md:border-b-0 md:border-l border-border";
|
|
29215
|
+
/**
|
|
29216
|
+
* The tier boundary at which a settings label/value row stops stacking.
|
|
29217
|
+
*
|
|
29218
|
+
* Below it (L1 — compact) the row is ONE column: label on its own line, value
|
|
29219
|
+
* on the next at full width. Above it the row is label-left / value-right as
|
|
29220
|
+
* before. The two-column form needs `SETTING_ROW_LABEL`'s lane plus a readable
|
|
29221
|
+
* value beside it; a phone has neither, and the failure mode is not "cramped"
|
|
29222
|
+
* but "the value is the part that gets cut" — the one thing the operator
|
|
29223
|
+
* opened the page to read.
|
|
29224
|
+
*/
|
|
29225
|
+
var SETTING_ROW_STACK_BREAKPOINT = "sm";
|
|
29226
|
+
/** Outer row: single column at L1, label/value columns from `sm` up. */
|
|
29227
|
+
var SETTING_ROW = "flex min-w-0 flex-col gap-0.5 py-1.5 sm:flex-row sm:items-center";
|
|
29228
|
+
/** Label lane. Full width (and free to wrap) at L1; a fixed lane from `sm` up. */
|
|
29229
|
+
var SETTING_ROW_LABEL = "min-w-0 text-[11px] leading-tight text-foreground-subtle sm:w-32 sm:shrink-0 sm:pr-2";
|
|
29230
|
+
/**
|
|
29231
|
+
* Value lane — the value text plus its trailing affordances (copy / reveal).
|
|
29232
|
+
* `w-full` at L1 so the value owns the whole row once the label is above it;
|
|
29233
|
+
* `sm:flex-1` makes it share the row again from `sm` up.
|
|
29234
|
+
*/
|
|
29235
|
+
var SETTING_ROW_VALUE = "flex w-full min-w-0 items-center gap-1 sm:flex-1";
|
|
29236
|
+
/**
|
|
29237
|
+
* The value text itself. `break-all` lets an opaque token (a key, a URL) wrap
|
|
29238
|
+
* at L1 instead of being cut mid-word; `sm:truncate` restores the single-line
|
|
29239
|
+
* desktop form, where the row is wide enough for truncation to be a choice
|
|
29240
|
+
* rather than data loss.
|
|
29241
|
+
*/
|
|
29242
|
+
var SETTING_ROW_VALUE_TEXT = "min-w-0 break-all text-xs text-foreground sm:truncate";
|
|
29243
|
+
/** Section header label (uppercase tracking-wider). */
|
|
29244
|
+
var TEXT_SECTION_LABEL = "text-[10px] sm:text-[11px] font-semibold text-foreground uppercase tracking-wider";
|
|
29245
|
+
/** Field label inside a row. */
|
|
29246
|
+
var TEXT_FIELD_LABEL = "text-[11px] sm:text-xs text-foreground";
|
|
29247
|
+
/** Sub-label / hint text. */
|
|
29248
|
+
var TEXT_HINT = "text-[10px] text-foreground-subtle";
|
|
29249
|
+
/** Primary value (a metric, badge, …). */
|
|
29250
|
+
var TEXT_VALUE = "text-[11px] sm:text-xs font-medium text-foreground";
|
|
29251
|
+
/** Big metric value (StatCard). */
|
|
29252
|
+
var TEXT_METRIC = "text-base sm:text-lg font-semibold leading-tight text-foreground";
|
|
29253
|
+
/** Default compact button. */
|
|
29254
|
+
var BTN_COMPACT = "inline-flex items-center justify-center gap-1 rounded border border-border bg-surface px-1.5 py-0.5 sm:px-2 sm:py-1 text-[10px] sm:text-[11px] font-medium text-foreground hover:bg-surface-hover transition-colors disabled:opacity-40";
|
|
29255
|
+
/** Primary compact button. */
|
|
29256
|
+
var BTN_COMPACT_PRIMARY = "inline-flex items-center justify-center gap-1 rounded border border-primary/40 bg-primary/15 px-1.5 py-0.5 sm:px-2 sm:py-1 text-[10px] sm:text-[11px] font-medium text-primary hover:bg-primary/25 transition-colors disabled:opacity-40";
|
|
29257
|
+
/** Danger compact button. */
|
|
29258
|
+
var BTN_COMPACT_DANGER = "inline-flex items-center justify-center gap-1 rounded border border-danger/40 bg-danger/10 px-1.5 py-0.5 sm:px-2 sm:py-1 text-[10px] sm:text-[11px] font-medium text-danger hover:bg-danger/20 transition-colors disabled:opacity-40";
|
|
29259
|
+
/** Warning compact button (e.g. cancel-drawing). */
|
|
29260
|
+
var BTN_COMPACT_WARNING = "inline-flex items-center justify-center gap-1 rounded border border-warning/40 bg-warning/10 px-1.5 py-0.5 sm:px-2 sm:py-1 text-[10px] sm:text-[11px] font-medium text-warning hover:bg-warning/20 transition-colors disabled:opacity-40";
|
|
29261
|
+
/** Compact text/select/number input. */
|
|
29262
|
+
var INPUT_COMPACT = "w-full rounded border border-border bg-background px-1.5 py-0.5 sm:px-2 sm:py-1 text-[11px] sm:text-xs text-foreground focus:border-primary focus:ring-1 focus:ring-primary/30 outline-none";
|
|
29263
|
+
/** Chip-style toggle (zone / class selectors in ZoneRulesEditor). */
|
|
29264
|
+
var CHIP_BASE = "inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-[10px] font-medium transition-colors";
|
|
29265
|
+
var CHIP_ACTIVE = "border-primary/50 bg-primary/15 text-primary";
|
|
29266
|
+
var CHIP_INACTIVE = "border-border bg-surface text-foreground-subtle hover:bg-surface-hover";
|
|
29267
|
+
//#endregion
|
|
29055
29268
|
//#region src/composites/model-bound-slider.tsx
|
|
29056
29269
|
var SOURCE_LABEL = {
|
|
29057
29270
|
override: "Override for this model",
|
|
@@ -29061,7 +29274,7 @@ var SOURCE_LABEL = {
|
|
|
29061
29274
|
function formatNumber(value, scale) {
|
|
29062
29275
|
return (scale !== void 0 ? value / scale : value).toFixed(2).replace(/\.?0+$/, "");
|
|
29063
29276
|
}
|
|
29064
|
-
function ModelBoundSlider({ field, modelId, settings, disabled, onChangeSettings }) {
|
|
29277
|
+
function ModelBoundSlider({ field, modelId, settings, disabled, onChangeSettings, hideSourceRow = false }) {
|
|
29065
29278
|
const modelDefault = field.modelApplicability?.defaultByModel?.[modelId];
|
|
29066
29279
|
const state = (0, _camstack_types.resolveModelBoundSetting)(settings, field.key, modelId.length > 0 ? modelId : void 0, modelDefault, field.default);
|
|
29067
29280
|
const fallback = typeof field.default === "number" ? field.default : field.min ?? 0;
|
|
@@ -29101,7 +29314,7 @@ function ModelBoundSlider({ field, modelId, settings, disabled, onChangeSettings
|
|
|
29101
29314
|
onChange: (e) => handleChange(Number(e.target.value)),
|
|
29102
29315
|
className: "w-full accent-primary h-1.5"
|
|
29103
29316
|
}),
|
|
29104
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29317
|
+
!hideSourceRow && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29105
29318
|
className: "mt-1 flex items-center justify-between gap-2 text-[10px]",
|
|
29106
29319
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29107
29320
|
"data-testid": "model-bound-source",
|
|
@@ -29152,6 +29365,32 @@ function ResetModelDefaultsButton({ fields, modelId, settings, disabled, onReset
|
|
|
29152
29365
|
});
|
|
29153
29366
|
}
|
|
29154
29367
|
//#endregion
|
|
29368
|
+
//#region src/primitives/collapsible-card.tsx
|
|
29369
|
+
function CollapsibleCard({ expanded, onExpandedChange, header, children, dimmed = false, hideChevron = false, className }) {
|
|
29370
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29371
|
+
className: cn("rounded-lg border border-border bg-surface overflow-hidden", dimmed && "opacity-[0.45]", className),
|
|
29372
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29373
|
+
role: "button",
|
|
29374
|
+
tabIndex: 0,
|
|
29375
|
+
className: "flex items-center gap-2.5 px-3 py-2.5 cursor-pointer select-none",
|
|
29376
|
+
onClick: () => onExpandedChange(!expanded),
|
|
29377
|
+
onKeyDown: (e) => {
|
|
29378
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
29379
|
+
e.preventDefault();
|
|
29380
|
+
onExpandedChange(!expanded);
|
|
29381
|
+
}
|
|
29382
|
+
},
|
|
29383
|
+
children: [!hideChevron && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29384
|
+
className: "text-foreground-subtle shrink-0",
|
|
29385
|
+
children: expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronDown, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronRight, { className: "h-4 w-4" })
|
|
29386
|
+
}), header]
|
|
29387
|
+
}), expanded && children && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
29388
|
+
className: "border-t border-border px-3 py-3",
|
|
29389
|
+
children
|
|
29390
|
+
})]
|
|
29391
|
+
});
|
|
29392
|
+
}
|
|
29393
|
+
//#endregion
|
|
29155
29394
|
//#region src/composites/pipeline-step.tsx
|
|
29156
29395
|
/**
|
|
29157
29396
|
* PipelineStep — single step card in the pipeline builder.
|
|
@@ -29163,124 +29402,77 @@ function ResetModelDefaultsButton({ fields, modelId, settings, disabled, onReset
|
|
|
29163
29402
|
function modelsForStep(schema) {
|
|
29164
29403
|
return schema?.models ?? [];
|
|
29165
29404
|
}
|
|
29166
|
-
function PipelineStep({ step, schema, allSchemas: _allSchemas, depth: _depth = 0, onChange, onDelete: _onDelete, readOnly = false
|
|
29405
|
+
function PipelineStep({ step, schema, allSchemas: _allSchemas, depth: _depth = 0, onChange, onDelete: _onDelete, readOnly = false }) {
|
|
29167
29406
|
const [expanded, setExpanded] = (0, react$1.useState)(false);
|
|
29168
29407
|
const models = modelsForStep(schema);
|
|
29169
|
-
|
|
29170
|
-
if (models.length > 0 && !isAutoSentinel && !models.some((m) => m.id === step.modelId) && !readOnly) {
|
|
29408
|
+
if (models.length > 0 && !models.some((m) => m.id === step.modelId) && !readOnly) {
|
|
29171
29409
|
const fallback = (schema?.defaultModelId ? models.find((m) => m.id === schema.defaultModelId) : null) ?? models[0];
|
|
29172
29410
|
if (fallback && fallback.id !== step.modelId) queueMicrotask(() => onChange({
|
|
29173
29411
|
...step,
|
|
29174
29412
|
modelId: fallback.id
|
|
29175
29413
|
}));
|
|
29176
29414
|
}
|
|
29177
|
-
const header = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
29178
|
-
|
|
29179
|
-
|
|
29180
|
-
|
|
29181
|
-
|
|
29182
|
-
|
|
29183
|
-
|
|
29184
|
-
|
|
29185
|
-
|
|
29186
|
-
|
|
29187
|
-
|
|
29188
|
-
|
|
29189
|
-
|
|
29190
|
-
|
|
29191
|
-
|
|
29192
|
-
|
|
29193
|
-
|
|
29194
|
-
|
|
29195
|
-
|
|
29196
|
-
|
|
29197
|
-
|
|
29198
|
-
|
|
29199
|
-
|
|
29200
|
-
|
|
29201
|
-
|
|
29202
|
-
|
|
29203
|
-
|
|
29204
|
-
|
|
29205
|
-
|
|
29206
|
-
|
|
29207
|
-
|
|
29208
|
-
|
|
29209
|
-
|
|
29210
|
-
|
|
29211
|
-
|
|
29212
|
-
|
|
29213
|
-
|
|
29214
|
-
|
|
29215
|
-
|
|
29216
|
-
|
|
29217
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: cn("inline-block h-4 w-4 rounded-full bg-white shadow transition-transform", step.enabled ? "translate-x-6" : "translate-x-1") })
|
|
29218
|
-
}),
|
|
29219
|
-
!readOnly && toggleMode === "threeState" && onOverrideChange && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29220
|
-
className: "flex items-center gap-0.5 rounded-md border border-border p-0.5",
|
|
29221
|
-
onClick: (e) => e.stopPropagation(),
|
|
29222
|
-
children: [
|
|
29223
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThreeStateButton, {
|
|
29224
|
-
label: "Inherit",
|
|
29225
|
-
active: overrideState === null,
|
|
29226
|
-
variant: "inherit",
|
|
29227
|
-
subtext: inheritedEnabled === void 0 ? void 0 : inheritedEnabled ? "on" : "off",
|
|
29228
|
-
onClick: () => onOverrideChange(null)
|
|
29229
|
-
}),
|
|
29230
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThreeStateButton, {
|
|
29231
|
-
label: "On",
|
|
29232
|
-
active: overrideState === true,
|
|
29233
|
-
variant: "on",
|
|
29234
|
-
onClick: () => onOverrideChange(true)
|
|
29235
|
-
}),
|
|
29236
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThreeStateButton, {
|
|
29237
|
-
label: "Off",
|
|
29238
|
-
active: overrideState === false,
|
|
29239
|
-
variant: "off",
|
|
29240
|
-
onClick: () => onOverrideChange(false)
|
|
29241
|
-
})
|
|
29242
|
-
]
|
|
29243
|
-
})
|
|
29244
|
-
] });
|
|
29415
|
+
const header = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29416
|
+
className: "flex-1 min-w-0",
|
|
29417
|
+
children: [
|
|
29418
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29419
|
+
className: "text-[10px] uppercase tracking-wider font-medium text-foreground-subtle/60 block leading-none",
|
|
29420
|
+
children: step.slot
|
|
29421
|
+
}),
|
|
29422
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29423
|
+
className: "text-sm font-semibold text-foreground truncate block leading-tight",
|
|
29424
|
+
children: step.addonName
|
|
29425
|
+
}),
|
|
29426
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29427
|
+
className: "flex items-center gap-1 mt-0.5 flex-wrap",
|
|
29428
|
+
children: [
|
|
29429
|
+
step.inputClasses.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29430
|
+
className: "text-[9px] uppercase font-semibold tracking-wide px-1.5 py-0.5 rounded bg-blue-500/12 text-blue-400",
|
|
29431
|
+
children: c
|
|
29432
|
+
}, c)),
|
|
29433
|
+
step.inputClasses.length > 0 && step.outputClasses.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29434
|
+
className: "text-foreground-subtle/40 text-[10px]",
|
|
29435
|
+
children: "→"
|
|
29436
|
+
}),
|
|
29437
|
+
step.outputClasses.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29438
|
+
className: "text-[9px] uppercase font-semibold tracking-wide px-1.5 py-0.5 rounded bg-green-500/12 text-green-400",
|
|
29439
|
+
children: c
|
|
29440
|
+
}, c))
|
|
29441
|
+
]
|
|
29442
|
+
})
|
|
29443
|
+
]
|
|
29444
|
+
}), !readOnly && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
29445
|
+
onClick: (e) => {
|
|
29446
|
+
e.stopPropagation();
|
|
29447
|
+
onChange({
|
|
29448
|
+
...step,
|
|
29449
|
+
enabled: !step.enabled
|
|
29450
|
+
});
|
|
29451
|
+
},
|
|
29452
|
+
className: cn("relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors", step.enabled ? "bg-success" : "bg-foreground-subtle/30"),
|
|
29453
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: cn("inline-block h-4 w-4 rounded-full bg-white shadow transition-transform", step.enabled ? "translate-x-6" : "translate-x-1") })
|
|
29454
|
+
})] });
|
|
29245
29455
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CollapsibleCard, {
|
|
29246
29456
|
expanded,
|
|
29247
29457
|
onExpandedChange: setExpanded,
|
|
29248
29458
|
dimmed: !step.enabled,
|
|
29249
29459
|
header,
|
|
29250
|
-
children:
|
|
29251
|
-
className: "text-[11px] text-foreground-subtle",
|
|
29252
|
-
children: "Model and detection settings are managed per agent — open the Pipeline page for this camera's agent to edit them."
|
|
29253
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29460
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29254
29461
|
className: "space-y-3",
|
|
29255
29462
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29256
29463
|
className: "space-y-2",
|
|
29257
|
-
children: [
|
|
29258
|
-
|
|
29259
|
-
|
|
29260
|
-
|
|
29261
|
-
|
|
29262
|
-
|
|
29263
|
-
|
|
29264
|
-
|
|
29265
|
-
|
|
29266
|
-
|
|
29267
|
-
disabled: readOnly,
|
|
29268
|
-
onChange: (e) => onChange({
|
|
29269
|
-
...step,
|
|
29270
|
-
modelId: e.target.checked ? "" : schema?.defaultModelId ?? models[0]?.id ?? ""
|
|
29271
|
-
})
|
|
29272
|
-
}), "Auto (node default)"]
|
|
29273
|
-
}),
|
|
29274
|
-
!isAutoSentinel && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelPicker, {
|
|
29275
|
-
models,
|
|
29276
|
-
value: step.modelId,
|
|
29277
|
-
disabled: readOnly,
|
|
29278
|
-
onChange: (id) => onChange({
|
|
29279
|
-
...step,
|
|
29280
|
-
modelId: id
|
|
29281
|
-
})
|
|
29464
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
29465
|
+
className: "block text-[10px] font-medium text-foreground-subtle uppercase tracking-wide",
|
|
29466
|
+
children: "Model"
|
|
29467
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelPicker, {
|
|
29468
|
+
models,
|
|
29469
|
+
value: step.modelId,
|
|
29470
|
+
disabled: readOnly,
|
|
29471
|
+
onChange: (id) => onChange({
|
|
29472
|
+
...step,
|
|
29473
|
+
modelId: id
|
|
29282
29474
|
})
|
|
29283
|
-
]
|
|
29475
|
+
})]
|
|
29284
29476
|
}), schema?.configSchema?.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigSchemaField, {
|
|
29285
29477
|
field,
|
|
29286
29478
|
allFields: schema.configSchema ?? [],
|
|
@@ -29347,7 +29539,7 @@ function resolveSettingValue(key, step, allFields) {
|
|
|
29347
29539
|
const sibling = allFields.find((f) => "key" in f && f.key === key);
|
|
29348
29540
|
return sibling && "default" in sibling ? sibling.default : void 0;
|
|
29349
29541
|
}
|
|
29350
|
-
function ConfigSchemaField({ field, allFields, step, disabled, onChange }) {
|
|
29542
|
+
function ConfigSchemaField({ field, allFields, step, disabled, onChange, hideModelBoundSourceRow }) {
|
|
29351
29543
|
const applicability = "modelApplicability" in field ? field.modelApplicability : void 0;
|
|
29352
29544
|
const inapplicableReason = applicability?.inapplicable?.[step.modelId];
|
|
29353
29545
|
const control = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigSchemaFieldControl, {
|
|
@@ -29356,7 +29548,8 @@ function ConfigSchemaField({ field, allFields, step, disabled, onChange }) {
|
|
|
29356
29548
|
step,
|
|
29357
29549
|
disabled: disabled || inapplicableReason !== void 0,
|
|
29358
29550
|
modelScopedOptions: applicability?.optionsByModel?.[step.modelId],
|
|
29359
|
-
onChange
|
|
29551
|
+
onChange,
|
|
29552
|
+
hideModelBoundSourceRow: hideModelBoundSourceRow === true
|
|
29360
29553
|
});
|
|
29361
29554
|
if (inapplicableReason === void 0) return control;
|
|
29362
29555
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -29367,7 +29560,7 @@ function ConfigSchemaField({ field, allFields, step, disabled, onChange }) {
|
|
|
29367
29560
|
})]
|
|
29368
29561
|
});
|
|
29369
29562
|
}
|
|
29370
|
-
function ConfigSchemaFieldControl({ field, allFields, step, disabled, modelScopedOptions, onChange }) {
|
|
29563
|
+
function ConfigSchemaFieldControl({ field, allFields, step, disabled, modelScopedOptions, onChange, hideModelBoundSourceRow }) {
|
|
29371
29564
|
const showWhen = field.type !== "separator" && field.type !== "info" ? field.showWhen : void 0;
|
|
29372
29565
|
if (showWhen) {
|
|
29373
29566
|
const { field: condField, equals, notEquals, in: inList, notIn } = showWhen;
|
|
@@ -29417,7 +29610,8 @@ function ConfigSchemaFieldControl({ field, allFields, step, disabled, modelScope
|
|
|
29417
29610
|
onChangeSettings: (settings) => onChange({
|
|
29418
29611
|
...step,
|
|
29419
29612
|
settings
|
|
29420
|
-
})
|
|
29613
|
+
}),
|
|
29614
|
+
hideSourceRow: hideModelBoundSourceRow
|
|
29421
29615
|
});
|
|
29422
29616
|
if (field.type === "slider" || field.type === "number") {
|
|
29423
29617
|
const raw = step.settings?.[field.key];
|
|
@@ -29503,204 +29697,247 @@ function ConfigSchemaFieldControl({ field, allFields, step, disabled, modelScope
|
|
|
29503
29697
|
});
|
|
29504
29698
|
return null;
|
|
29505
29699
|
}
|
|
29506
|
-
function ThreeStateButton({ label, active, variant, subtext, onClick }) {
|
|
29507
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
29508
|
-
type: "button",
|
|
29509
|
-
onClick,
|
|
29510
|
-
className: cn("px-2 py-1 text-[10px] font-medium rounded transition-colors", active ? variant === "on" ? "bg-green-500/20 text-green-400" : variant === "off" ? "bg-red-500/20 text-red-400" : "bg-primary/20 text-primary" : "text-foreground-subtle hover:bg-surface-hover"),
|
|
29511
|
-
children: [label, subtext && active && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
29512
|
-
className: "ml-1 opacity-60",
|
|
29513
|
-
children: [
|
|
29514
|
-
"(",
|
|
29515
|
-
subtext,
|
|
29516
|
-
")"
|
|
29517
|
-
]
|
|
29518
|
-
})]
|
|
29519
|
-
});
|
|
29520
|
-
}
|
|
29521
29700
|
//#endregion
|
|
29522
|
-
//#region src/composites/
|
|
29523
|
-
function
|
|
29524
|
-
|
|
29525
|
-
const declared = catalog.defaultModelIdByFormat?.[format];
|
|
29526
|
-
if (declared !== void 0 && hasFormatBuild(declared)) return declared;
|
|
29527
|
-
if (hasFormatBuild(catalog.defaultModelId)) return catalog.defaultModelId;
|
|
29528
|
-
const available = catalog.models.filter((m) => m.formats[format] !== void 0 && m.legacy !== true);
|
|
29529
|
-
if (available.length === 0) return catalog.defaultModelId;
|
|
29530
|
-
return [...available].toSorted((a, b) => (a.formats[format]?.sizeMB ?? Infinity) - (b.formats[format]?.sizeMB ?? Infinity))[0].id;
|
|
29701
|
+
//#region src/composites/step-inspector/setting-values.ts
|
|
29702
|
+
function isValueField(field) {
|
|
29703
|
+
return field.type !== "separator" && field.type !== "info" && field.type !== "group";
|
|
29531
29704
|
}
|
|
29532
|
-
/**
|
|
29533
|
-
|
|
29534
|
-
|
|
29535
|
-
* The device's ELECTED model (`device.defaultModelId`, the per-device
|
|
29536
|
-
* object-detection election) applies ONLY when this step's catalog actually
|
|
29537
|
-
* contains it — `slot === 'detector'` alone is too wide, because
|
|
29538
|
-
* package-detection shares the detector slot with the root step. Bit live
|
|
29539
|
-
* 2026-08-08: rfdetr-package became the package step's default and every
|
|
29540
|
-
* matrix cell still showed the device's yolo as the "selected" model, because
|
|
29541
|
-
* the election was applied slot-wide. Membership is the principled gate: an
|
|
29542
|
-
* elected model this step cannot run is never displayed for it.
|
|
29543
|
-
*/
|
|
29544
|
-
function resolveEffectiveDefaultModel(catalog, device) {
|
|
29545
|
-
const format = device?.format;
|
|
29546
|
-
if (format === void 0) return void 0;
|
|
29547
|
-
const elected = device?.defaultModelId;
|
|
29548
|
-
return catalog.slot === "detector" && elected !== void 0 && catalog.models.some((m) => m.id === elected) ? elected : resolveStepDefaultModel(catalog, format);
|
|
29705
|
+
/** True for a D738 field whose value is stored with the model it was made for. */
|
|
29706
|
+
function bindsToModel(field) {
|
|
29707
|
+
return (field.type === "slider" || field.type === "number") && field.bindsToModel === true;
|
|
29549
29708
|
}
|
|
29550
29709
|
/**
|
|
29551
|
-
*
|
|
29552
|
-
*
|
|
29553
|
-
*
|
|
29554
|
-
* the runner cannot honour.
|
|
29710
|
+
* What runs when no layer stores the field: the running model's default for a
|
|
29711
|
+
* model-bound field that the model declares one for (D738), else the field's
|
|
29712
|
+
* own default. `fromModel` says which.
|
|
29555
29713
|
*/
|
|
29556
|
-
function
|
|
29557
|
-
|
|
29558
|
-
|
|
29714
|
+
function defaultOfField(field, modelId) {
|
|
29715
|
+
const byModel = "modelApplicability" in field ? field.modelApplicability?.defaultByModel?.[modelId] : void 0;
|
|
29716
|
+
if (byModel !== void 0) return {
|
|
29717
|
+
value: byModel,
|
|
29718
|
+
fromModel: true
|
|
29719
|
+
};
|
|
29720
|
+
return {
|
|
29721
|
+
value: "default" in field ? field.default : void 0,
|
|
29722
|
+
fromModel: false
|
|
29723
|
+
};
|
|
29559
29724
|
}
|
|
29560
|
-
|
|
29561
|
-
|
|
29562
|
-
return stepModelOptions(models, format).length > 0;
|
|
29725
|
+
function trimNumber(n) {
|
|
29726
|
+
return n.toFixed(2).replace(/\.?0+$/, "");
|
|
29563
29727
|
}
|
|
29564
|
-
/**
|
|
29565
|
-
|
|
29566
|
-
|
|
29567
|
-
|
|
29568
|
-
|
|
29569
|
-
|
|
29570
|
-
|
|
29571
|
-
|
|
29572
|
-
|
|
29573
|
-
|
|
29574
|
-
|
|
29575
|
-
|
|
29728
|
+
/** One stored value as the operator reads it (`0.65`, `on`, `Person, Vehicle`, `—`). */
|
|
29729
|
+
function formatSettingValue(field, value) {
|
|
29730
|
+
if (value === void 0 || value === null) return "—";
|
|
29731
|
+
if (typeof value === "boolean") return value ? "on" : "off";
|
|
29732
|
+
if (typeof value === "number") {
|
|
29733
|
+
const scale = field.type === "slider" ? field.displayScale : void 0;
|
|
29734
|
+
const unit = "unit" in field && typeof field.unit === "string" ? ` ${field.unit}` : "";
|
|
29735
|
+
return `${trimNumber(scale !== void 0 ? value / scale : value)}${unit}`;
|
|
29736
|
+
}
|
|
29737
|
+
const options = "options" in field && Array.isArray(field.options) ? field.options : [];
|
|
29738
|
+
const labelOf = (v) => {
|
|
29739
|
+
const hit = options.find((o) => o.value === v);
|
|
29740
|
+
return hit ? hit.label : String(v);
|
|
29741
|
+
};
|
|
29742
|
+
if (Array.isArray(value)) return value.length === 0 ? "none" : value.map(labelOf).join(", ");
|
|
29743
|
+
if (typeof value === "string") return labelOf(value);
|
|
29744
|
+
return JSON.stringify(value);
|
|
29576
29745
|
}
|
|
29577
29746
|
//#endregion
|
|
29578
|
-
//#region src/composites/
|
|
29579
|
-
|
|
29580
|
-
|
|
29581
|
-
|
|
29582
|
-
|
|
29583
|
-
|
|
29584
|
-
|
|
29585
|
-
|
|
29586
|
-
|
|
29587
|
-
|
|
29588
|
-
*/
|
|
29589
|
-
/** The editor's view of the flag for one step, or `null` for a step no root can supply. */
|
|
29590
|
-
function rootSourceState(stepId, settings, rootModel) {
|
|
29591
|
-
if (!(0, _camstack_types.isRootSuppliableStepId)(stepId)) return null;
|
|
29592
|
-
return (0, _camstack_types.resolveRootSupply)(stepId, settings, rootModel?.supplies);
|
|
29747
|
+
//#region src/composites/step-inspector/layered-settings-form.tsx
|
|
29748
|
+
var SOURCE_TONE$1 = {
|
|
29749
|
+
accelerator: "border-amber-500/40 bg-amber-500/10 text-amber-500",
|
|
29750
|
+
camera: "border-primary/40 bg-primary/10 text-primary",
|
|
29751
|
+
node: "border-border bg-muted/40 text-foreground",
|
|
29752
|
+
default: "border-border/60 text-foreground-subtle"
|
|
29753
|
+
};
|
|
29754
|
+
/** DOM id of a field's box — the target of a search hit. */
|
|
29755
|
+
function stepFieldDomId(key) {
|
|
29756
|
+
return `step-field-${key}`;
|
|
29593
29757
|
}
|
|
29594
|
-
|
|
29595
|
-
function withUseRootModel(settings, value) {
|
|
29758
|
+
function displayStep(addon, modelId, settings) {
|
|
29596
29759
|
return {
|
|
29597
|
-
|
|
29598
|
-
|
|
29760
|
+
addonId: addon.id,
|
|
29761
|
+
addonName: addon.name,
|
|
29762
|
+
slot: addon.slot,
|
|
29763
|
+
inputClasses: [...addon.inputClasses],
|
|
29764
|
+
outputClasses: [...addon.outputClasses],
|
|
29765
|
+
enabled: true,
|
|
29766
|
+
modelId,
|
|
29767
|
+
children: [],
|
|
29768
|
+
settings: { ...settings }
|
|
29599
29769
|
};
|
|
29600
29770
|
}
|
|
29601
|
-
|
|
29602
|
-
|
|
29603
|
-
|
|
29604
|
-
|
|
29605
|
-
|
|
29606
|
-
|
|
29607
|
-
|
|
29608
|
-
|
|
29609
|
-
|
|
29610
|
-
|
|
29611
|
-
|
|
29612
|
-
|
|
29613
|
-
|
|
29614
|
-
|
|
29615
|
-
|
|
29616
|
-
|
|
29617
|
-
|
|
29618
|
-
|
|
29619
|
-
}
|
|
29620
|
-
|
|
29621
|
-
|
|
29622
|
-
const
|
|
29623
|
-
|
|
29624
|
-
const
|
|
29625
|
-
|
|
29626
|
-
|
|
29771
|
+
var LAYER_IDS = [
|
|
29772
|
+
"accelerator",
|
|
29773
|
+
"camera",
|
|
29774
|
+
"node"
|
|
29775
|
+
];
|
|
29776
|
+
/** "Camera · node 0.65 · model default 0.78 · [Reset to node]" under one field. */
|
|
29777
|
+
function SettingProvenance({ field, layers, target, labels, boundKeys, modelId, pinned, disabled, onReset, action }) {
|
|
29778
|
+
const effective = (0, _camstack_types.effectiveStepSettings)(layers);
|
|
29779
|
+
const fallback = defaultOfField(field, modelId);
|
|
29780
|
+
const defaultLabel = fallback.fromModel ? "model default" : "default";
|
|
29781
|
+
const stored = (0, _camstack_types.stepSettingSource)(layers, field.key, boundKeys);
|
|
29782
|
+
const bound = bindsToModel(field);
|
|
29783
|
+
const state = bound ? (0, _camstack_types.resolveModelBoundSetting)(effective, field.key, modelId, fallback.value, fallback.value) : null;
|
|
29784
|
+
const source = state === null || state.source === "override" ? stored : "default";
|
|
29785
|
+
const sourceText = source === "default" ? defaultLabel : labels[source] ?? source;
|
|
29786
|
+
const levels = LAYER_IDS.filter((id) => id !== source && labels[id] !== void 0).filter((id) => layers[id]?.[field.key] !== void 0).map((id) => {
|
|
29787
|
+
const binding = bound ? layers[id]?.[(0, _camstack_types.modelBindingKey)(field.key)] : void 0;
|
|
29788
|
+
const forOther = typeof binding === "string" && binding !== modelId ? ` (for ${binding})` : "";
|
|
29789
|
+
return `${labels[id]} ${formatSettingValue(field, layers[id]?.[field.key])}${forOther}`;
|
|
29790
|
+
});
|
|
29791
|
+
const withDefault = source === "default" ? levels : [...levels, `${defaultLabel} ${formatSettingValue(field, fallback.value)}`];
|
|
29792
|
+
const targetStores = (0, _camstack_types.stepSettingSource)({ [target]: layers[target] }, field.key, boundKeys) !== "default";
|
|
29793
|
+
const below = LAYER_IDS.slice(LAYER_IDS.indexOf(target) + 1).find((id) => labels[id] !== void 0 && layers[id]?.[field.key] !== void 0);
|
|
29794
|
+
const resetTo = below !== void 0 ? labels[below] ?? below : defaultLabel;
|
|
29795
|
+
const belowSource = stored === "default" ? "default" : (0, _camstack_types.stepSettingSource)({
|
|
29796
|
+
...layers,
|
|
29797
|
+
[stored]: void 0
|
|
29798
|
+
}, field.key, boundKeys);
|
|
29799
|
+
const pinnedAt = belowSource === "default" ? defaultLabel : labels[belowSource] ?? belowSource;
|
|
29800
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29801
|
+
className: "mt-1 flex flex-wrap items-center gap-x-2 gap-y-1 text-[10px]",
|
|
29627
29802
|
children: [
|
|
29628
|
-
|
|
29629
|
-
|
|
29630
|
-
|
|
29803
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29804
|
+
"data-testid": "setting-source",
|
|
29805
|
+
className: cn("rounded border px-1.5 py-px font-medium", SOURCE_TONE$1[source]),
|
|
29806
|
+
children: sourceText
|
|
29807
|
+
}),
|
|
29808
|
+
withDefault.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29809
|
+
"data-testid": "setting-levels",
|
|
29810
|
+
className: "text-foreground-subtle",
|
|
29811
|
+
children: withDefault.join(" · ")
|
|
29812
|
+
}),
|
|
29813
|
+
pinned && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
29814
|
+
className: "rounded border border-amber-500/40 px-1.5 py-px text-amber-500",
|
|
29815
|
+
title: "Stored by an older editor as a copy of the value below. It changes nothing today, but it stops later changes below from reaching this camera.",
|
|
29816
|
+
children: [
|
|
29817
|
+
"pinned at the ",
|
|
29818
|
+
pinnedAt.toLowerCase(),
|
|
29819
|
+
" value"
|
|
29820
|
+
]
|
|
29821
|
+
}),
|
|
29822
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
29823
|
+
className: "ml-auto flex items-center gap-2",
|
|
29824
|
+
children: [action, targetStores && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
29825
|
+
type: "button",
|
|
29826
|
+
disabled,
|
|
29827
|
+
onClick: onReset,
|
|
29828
|
+
className: "rounded px-1 text-foreground-subtle hover:bg-muted hover:text-foreground disabled:opacity-50",
|
|
29829
|
+
children: ["Reset to ", resetTo.toLowerCase()]
|
|
29830
|
+
})]
|
|
29831
|
+
})
|
|
29631
29832
|
]
|
|
29632
|
-
})
|
|
29633
|
-
|
|
29634
|
-
|
|
29833
|
+
});
|
|
29834
|
+
}
|
|
29835
|
+
function LayeredSettingsForm({ addon, fields, layers, target, labels, modelId, onChangeLayer, rootModel, pinnedKeys = [], fieldActions, headerActions, highlightKey = null, disabled = false }) {
|
|
29836
|
+
const boundKeys = (0, _camstack_types.collectModelBoundKeys)(fields);
|
|
29837
|
+
const effective = (0, _camstack_types.effectiveStepSettings)(layers);
|
|
29838
|
+
const targetLayer = layers[target];
|
|
29839
|
+
const step = displayStep(addon, modelId, effective);
|
|
29840
|
+
const rootState = rootSourceState(addon.id, effective, rootModel);
|
|
29635
29841
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29636
|
-
className: "space-y-
|
|
29637
|
-
"data-testid": "root-model-source",
|
|
29842
|
+
className: "space-y-2 text-xs",
|
|
29638
29843
|
children: [
|
|
29639
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
29640
|
-
className: "flex items-center gap-2
|
|
29844
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29845
|
+
className: "flex flex-wrap items-center gap-2",
|
|
29641
29846
|
children: [
|
|
29642
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
29643
|
-
|
|
29644
|
-
|
|
29645
|
-
disabled,
|
|
29646
|
-
onChange: (e) => onChange(e.target.checked)
|
|
29847
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
29848
|
+
className: TEXT_SECTION_LABEL,
|
|
29849
|
+
children: "Settings"
|
|
29647
29850
|
}),
|
|
29648
|
-
|
|
29649
|
-
|
|
29650
|
-
|
|
29851
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
29852
|
+
className: TEXT_HINT,
|
|
29853
|
+
children: ["editing: ", labels[target] ?? target]
|
|
29854
|
+
}),
|
|
29855
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
29856
|
+
className: "ml-auto flex flex-wrap items-center gap-3",
|
|
29857
|
+
children: [headerActions, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResetModelDefaultsButton, {
|
|
29858
|
+
fields,
|
|
29859
|
+
modelId,
|
|
29860
|
+
settings: targetLayer,
|
|
29861
|
+
disabled,
|
|
29862
|
+
onReset: (next) => onChangeLayer(next)
|
|
29863
|
+
})]
|
|
29864
|
+
})
|
|
29651
29865
|
]
|
|
29652
29866
|
}),
|
|
29653
|
-
|
|
29654
|
-
|
|
29655
|
-
|
|
29656
|
-
|
|
29657
|
-
|
|
29867
|
+
rootState !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RootModelSourceControl, {
|
|
29868
|
+
stepId: addon.id,
|
|
29869
|
+
settings: effective,
|
|
29870
|
+
rootModel,
|
|
29871
|
+
disabled,
|
|
29872
|
+
onChange: (v) => onChangeLayer((0, _camstack_types.withFieldFrom)(targetLayer, _camstack_types.USE_ROOT_MODEL_SETTING_KEY, withUseRootModel(effective, v), []))
|
|
29873
|
+
}),
|
|
29874
|
+
fields.map((field) => {
|
|
29875
|
+
if (!isValueField(field)) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigSchemaField, {
|
|
29876
|
+
field,
|
|
29877
|
+
allFields: fields,
|
|
29878
|
+
step,
|
|
29879
|
+
disabled,
|
|
29880
|
+
onChange: () => {}
|
|
29881
|
+
}, field.key);
|
|
29882
|
+
const highlighted = highlightKey === field.key;
|
|
29883
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29884
|
+
id: stepFieldDomId(field.key),
|
|
29885
|
+
"data-field-key": field.key,
|
|
29886
|
+
className: cn("rounded-md border border-transparent px-2 py-1 -mx-2", highlighted && "border-primary/50 bg-primary/5"),
|
|
29887
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigSchemaField, {
|
|
29888
|
+
field,
|
|
29889
|
+
allFields: fields,
|
|
29890
|
+
step,
|
|
29891
|
+
disabled,
|
|
29892
|
+
hideModelBoundSourceRow: true,
|
|
29893
|
+
onChange: (next) => onChangeLayer((0, _camstack_types.withFieldFrom)(targetLayer, field.key, next.settings ?? {}, boundKeys))
|
|
29894
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingProvenance, {
|
|
29895
|
+
field,
|
|
29896
|
+
layers,
|
|
29897
|
+
target,
|
|
29898
|
+
labels,
|
|
29899
|
+
boundKeys,
|
|
29900
|
+
modelId,
|
|
29901
|
+
pinned: pinnedKeys.includes(field.key),
|
|
29902
|
+
disabled,
|
|
29903
|
+
onReset: () => onChangeLayer((0, _camstack_types.withoutField)(targetLayer, field.key, boundKeys)),
|
|
29904
|
+
action: fieldActions?.(field.key, (0, _camstack_types.stepSettingSource)(layers, field.key, boundKeys))
|
|
29905
|
+
})]
|
|
29906
|
+
}, field.key);
|
|
29907
|
+
})
|
|
29658
29908
|
]
|
|
29659
29909
|
});
|
|
29660
29910
|
}
|
|
29661
29911
|
//#endregion
|
|
29662
29912
|
//#region src/composites/agent-step-editor.tsx
|
|
29663
29913
|
/**
|
|
29664
|
-
*
|
|
29665
|
-
*
|
|
29666
|
-
*
|
|
29914
|
+
* A step's node-local fields: the catalog schema minus the knobs the CLUSTER
|
|
29915
|
+
* owns (`clusterStepSettingFieldsFor`). Those still exist on the schema so the
|
|
29916
|
+
* runner can hydrate `child.settings`, but writing them per node or per camera
|
|
29667
29917
|
* would be a second store the overlay silently ignores.
|
|
29668
29918
|
*/
|
|
29669
|
-
function
|
|
29919
|
+
function stepLocalFields(addon) {
|
|
29670
29920
|
const clusterKeys = new Set((0, _camstack_types.clusterStepSettingFieldsFor)(addon.id).map((field) => field.key));
|
|
29671
29921
|
return (addon.configSchema ?? []).filter((field) => !clusterKeys.has(field.key));
|
|
29672
29922
|
}
|
|
29673
|
-
function ClusterSettingsHint({ addonId }) {
|
|
29674
|
-
if ((0, _camstack_types.clusterStepSettingFieldsFor)(addonId).length === 0) return null;
|
|
29675
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
29676
|
-
className: "text-[10px] leading-snug text-foreground-subtle",
|
|
29677
|
-
children: "Admission knobs for this step live in Pipeline → Cluster → Settings → Enrichment models. They apply to every camera — a per-device copy would fill the shared index from two policies."
|
|
29678
|
-
});
|
|
29679
|
-
}
|
|
29680
29923
|
/**
|
|
29681
|
-
* The model
|
|
29924
|
+
* The model a step's settings are read and written for — what a model-bound
|
|
29682
29925
|
* field (D738) binds to. While the ROOT supplies the step's fine label (D741),
|
|
29683
29926
|
* that is the root model: the step's threshold then gates the root's label, on
|
|
29684
29927
|
* the root's score scale. Otherwise the step's own model: its pin, else the
|
|
29685
29928
|
* model the row really runs (`effectiveModelId` — the device's hardware-aware
|
|
29686
29929
|
* default when unpinned), else the step's flat catalog default.
|
|
29687
29930
|
*/
|
|
29688
|
-
function settingsModelIdFor(addon,
|
|
29689
|
-
if (rootModel !== void 0 && rootSourceState(addon.id,
|
|
29690
|
-
return
|
|
29931
|
+
function settingsModelIdFor(addon, modelId, settings, rootModel) {
|
|
29932
|
+
if (rootModel !== void 0 && rootSourceState(addon.id, settings, rootModel)?.effective) return rootModel.modelId;
|
|
29933
|
+
return modelId ?? addon.defaultModelId;
|
|
29691
29934
|
}
|
|
29692
|
-
function
|
|
29693
|
-
return
|
|
29694
|
-
|
|
29695
|
-
|
|
29696
|
-
|
|
29697
|
-
|
|
29698
|
-
outputClasses: [...addon.outputClasses],
|
|
29699
|
-
enabled: cfg.enabled,
|
|
29700
|
-
modelId,
|
|
29701
|
-
children: [],
|
|
29702
|
-
settings: { ...cfg.settings }
|
|
29703
|
-
};
|
|
29935
|
+
function ClusterSettingsHint({ addonId }) {
|
|
29936
|
+
if ((0, _camstack_types.clusterStepSettingFieldsFor)(addonId).length === 0) return null;
|
|
29937
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
29938
|
+
className: "text-[10px] leading-snug text-foreground-subtle",
|
|
29939
|
+
children: "Admission knobs for this step live in Pipeline → Cluster → Settings → Enrichment models. They apply to every camera — a per-device copy would fill the shared index from two policies."
|
|
29940
|
+
});
|
|
29704
29941
|
}
|
|
29705
29942
|
/**
|
|
29706
29943
|
* Shown in place of the model picker while the root model supplies the step:
|
|
@@ -29721,233 +29958,57 @@ function RootSuppliedModelNote({ rootModel, stepModelId }) {
|
|
|
29721
29958
|
]
|
|
29722
29959
|
});
|
|
29723
29960
|
}
|
|
29724
|
-
|
|
29725
|
-
|
|
29726
|
-
|
|
29727
|
-
|
|
29728
|
-
|
|
29729
|
-
|
|
29730
|
-
|
|
29731
|
-
}
|
|
29732
|
-
|
|
29961
|
+
/**
|
|
29962
|
+
* AgentStepEditor — one step on ONE accelerator: its model and its settings,
|
|
29963
|
+
* as a single node-level layer. Model Studio's "settings for this installed
|
|
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.
|
|
29967
|
+
*/
|
|
29968
|
+
function AgentStepEditor({ addon, agentDefault, onChangeAgentConfig, engineFormat, effectiveModelId, rootModel }) {
|
|
29969
|
+
const modelsForFormat = (0, react$1.useMemo)(() => stepModelOptions(addon.models, engineFormat), [addon.models, engineFormat]);
|
|
29733
29970
|
const update = (patch) => {
|
|
29734
29971
|
onChangeAgentConfig?.({
|
|
29735
29972
|
...agentDefault,
|
|
29736
29973
|
...patch
|
|
29737
29974
|
});
|
|
29738
29975
|
};
|
|
29739
|
-
const
|
|
29976
|
+
const fields = stepLocalFields(addon);
|
|
29740
29977
|
const formatDefault = addon.defaultModelIdByFormat?.[engineFormat] ?? addon.defaultModelId;
|
|
29741
29978
|
const rootSupplied = rootSourceState(addon.id, agentDefault.settings, rootModel)?.effective === true;
|
|
29979
|
+
const runningModel = agentDefault.modelId ?? effectiveModelId;
|
|
29742
29980
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29743
29981
|
className: "space-y-4 text-xs",
|
|
29744
29982
|
children: [
|
|
29745
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RootModelSourceControl, {
|
|
29746
|
-
stepId: addon.id,
|
|
29747
|
-
settings: agentDefault.settings,
|
|
29748
|
-
rootModel,
|
|
29749
|
-
onChange: (v) => update({ settings: withUseRootModel(agentDefault.settings, v) })
|
|
29750
|
-
}),
|
|
29751
29983
|
rootSupplied && rootModel !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RootSuppliedModelNote, {
|
|
29752
29984
|
rootModel,
|
|
29753
|
-
stepModelId:
|
|
29985
|
+
stepModelId: runningModel
|
|
29754
29986
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
29755
29987
|
className: "text-[10px] uppercase tracking-widest text-foreground-subtle mb-1",
|
|
29756
29988
|
children: "Model"
|
|
29757
29989
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelPicker, {
|
|
29758
29990
|
models: modelsForFormat,
|
|
29759
|
-
value:
|
|
29991
|
+
value: runningModel ?? "",
|
|
29760
29992
|
defaultModelId: formatDefault,
|
|
29761
29993
|
onChange: (modelId) => update({ modelId })
|
|
29762
29994
|
})] }),
|
|
29763
|
-
|
|
29764
|
-
className: "pt-2 border-t border-border/50
|
|
29765
|
-
children:
|
|
29766
|
-
|
|
29767
|
-
|
|
29768
|
-
|
|
29769
|
-
|
|
29770
|
-
|
|
29771
|
-
|
|
29772
|
-
|
|
29773
|
-
|
|
29774
|
-
|
|
29775
|
-
})]
|
|
29776
|
-
}), localSchema.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigSchemaField, {
|
|
29777
|
-
field,
|
|
29778
|
-
allFields: localSchema,
|
|
29779
|
-
step: buildDisplayStep(addon, agentDefault, settingsModelIdFor(addon, agentDefault, effectiveModelId, rootModel)),
|
|
29780
|
-
disabled: false,
|
|
29781
|
-
onChange: (next) => update({ settings: { ...next.settings } })
|
|
29782
|
-
}, field.key))]
|
|
29995
|
+
(fields.length > 0 || rootSourceState(addon.id, agentDefault.settings, rootModel)) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
29996
|
+
className: "pt-2 border-t border-border/50",
|
|
29997
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LayeredSettingsForm, {
|
|
29998
|
+
addon,
|
|
29999
|
+
fields,
|
|
30000
|
+
layers: { node: agentDefault.settings },
|
|
30001
|
+
target: "node",
|
|
30002
|
+
labels: { node: "This node" },
|
|
30003
|
+
modelId: settingsModelIdFor(addon, runningModel, agentDefault.settings, rootModel),
|
|
30004
|
+
...rootModel !== void 0 ? { rootModel } : {},
|
|
30005
|
+
onChangeLayer: (settings) => update({ settings })
|
|
30006
|
+
})
|
|
29783
30007
|
}),
|
|
29784
30008
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClusterSettingsHint, { addonId: addon.id })
|
|
29785
30009
|
]
|
|
29786
30010
|
});
|
|
29787
30011
|
}
|
|
29788
|
-
function DeviceModeEditor({ addon, agentDefault, agentNodeId, currentPatch, modelsForFormat, engineFormat, effectiveModelId, rootModel, onChangePatch }) {
|
|
29789
|
-
const effective = computeEffective("device", agentDefault, currentPatch);
|
|
29790
|
-
const inheritedModelId = agentDefault.modelId ?? effectiveModelId;
|
|
29791
|
-
const formatDefault = addon.defaultModelIdByFormat?.[engineFormat] ?? addon.defaultModelId;
|
|
29792
|
-
const commitPatch = (next) => {
|
|
29793
|
-
const hasAnyField = next.modelId !== void 0 || next.settings !== void 0;
|
|
29794
|
-
onChangePatch?.(hasAnyField ? next : null);
|
|
29795
|
-
};
|
|
29796
|
-
const setModel = (modelId) => {
|
|
29797
|
-
const { modelId: _prev, ...rest } = currentPatch ?? {};
|
|
29798
|
-
commitPatch(modelId === void 0 ? rest : {
|
|
29799
|
-
...rest,
|
|
29800
|
-
modelId
|
|
29801
|
-
});
|
|
29802
|
-
};
|
|
29803
|
-
const setSettings = (settings) => {
|
|
29804
|
-
const { settings: _prev, ...rest } = currentPatch ?? {};
|
|
29805
|
-
commitPatch(settings === void 0 ? rest : {
|
|
29806
|
-
...rest,
|
|
29807
|
-
settings
|
|
29808
|
-
});
|
|
29809
|
-
};
|
|
29810
|
-
const modelOverridden = currentPatch?.modelId !== void 0;
|
|
29811
|
-
const localSchema = nodeLocalConfigSchema(addon);
|
|
29812
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29813
|
-
className: "space-y-4 text-xs",
|
|
29814
|
-
children: [
|
|
29815
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29816
|
-
className: "rounded bg-muted/40 px-3 py-2 text-[11px] text-foreground-subtle",
|
|
29817
|
-
children: [
|
|
29818
|
-
"Provisioning for ",
|
|
29819
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29820
|
-
className: "font-semibold text-foreground",
|
|
29821
|
-
children: agentNodeId
|
|
29822
|
-
}),
|
|
29823
|
-
" — model + settings only. Whether this camera runs the step is set by its per-camera gate above."
|
|
29824
|
-
]
|
|
29825
|
-
}),
|
|
29826
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RootModelSourceControl, {
|
|
29827
|
-
stepId: addon.id,
|
|
29828
|
-
settings: effective.settings,
|
|
29829
|
-
rootModel,
|
|
29830
|
-
onChange: (v) => setSettings(withUseRootModel(effective.settings, v))
|
|
29831
|
-
}),
|
|
29832
|
-
rootSourceState(addon.id, effective.settings, rootModel)?.effective === true && rootModel !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RootSuppliedModelNote, {
|
|
29833
|
-
rootModel,
|
|
29834
|
-
stepModelId: effective.modelId ?? inheritedModelId
|
|
29835
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
29836
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29837
|
-
className: "flex items-center justify-between mb-1",
|
|
29838
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
29839
|
-
className: "text-[10px] uppercase tracking-widest text-foreground-subtle",
|
|
29840
|
-
children: "Model"
|
|
29841
|
-
}), modelOverridden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
29842
|
-
type: "button",
|
|
29843
|
-
className: "text-[10px] text-foreground-subtle hover:text-foreground",
|
|
29844
|
-
onClick: () => setModel(void 0),
|
|
29845
|
-
children: "Clear"
|
|
29846
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
29847
|
-
type: "button",
|
|
29848
|
-
className: "text-[10px] text-primary hover:underline",
|
|
29849
|
-
onClick: () => setModel(inheritedModelId),
|
|
29850
|
-
children: "Override"
|
|
29851
|
-
})]
|
|
29852
|
-
}),
|
|
29853
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelPicker, {
|
|
29854
|
-
models: modelsForFormat,
|
|
29855
|
-
disabled: !modelOverridden,
|
|
29856
|
-
value: (modelOverridden ? currentPatch?.modelId ?? inheritedModelId : inheritedModelId) ?? "",
|
|
29857
|
-
defaultModelId: formatDefault,
|
|
29858
|
-
onChange: (modelId) => setModel(modelId)
|
|
29859
|
-
}),
|
|
29860
|
-
!modelOverridden && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29861
|
-
className: "text-[10px] text-foreground-subtle mt-1",
|
|
29862
|
-
children: ["inherits from ", agentNodeId]
|
|
29863
|
-
})
|
|
29864
|
-
] }),
|
|
29865
|
-
localSchema.length > 0 && (() => {
|
|
29866
|
-
const settingsOverridden = currentPatch?.settings !== void 0;
|
|
29867
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29868
|
-
className: "pt-2 border-t border-border/50 space-y-3",
|
|
29869
|
-
children: [
|
|
29870
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29871
|
-
className: "flex items-center justify-between",
|
|
29872
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
29873
|
-
className: "text-[10px] uppercase tracking-widest text-foreground-subtle",
|
|
29874
|
-
children: "Settings"
|
|
29875
|
-
}), settingsOverridden ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29876
|
-
className: "flex items-center gap-3",
|
|
29877
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResetModelDefaultsButton, {
|
|
29878
|
-
fields: localSchema,
|
|
29879
|
-
modelId: settingsModelIdFor(addon, effective, inheritedModelId, rootModel),
|
|
29880
|
-
settings: currentPatch?.settings,
|
|
29881
|
-
onReset: (settings) => setSettings(settings)
|
|
29882
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
29883
|
-
type: "button",
|
|
29884
|
-
className: "text-[10px] text-foreground-subtle hover:text-foreground",
|
|
29885
|
-
onClick: () => setSettings(void 0),
|
|
29886
|
-
children: "Clear"
|
|
29887
|
-
})]
|
|
29888
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
29889
|
-
type: "button",
|
|
29890
|
-
className: "text-[10px] text-primary hover:underline",
|
|
29891
|
-
onClick: () => setSettings({ ...agentDefault.settings }),
|
|
29892
|
-
children: "Override"
|
|
29893
|
-
})]
|
|
29894
|
-
}),
|
|
29895
|
-
localSchema.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigSchemaField, {
|
|
29896
|
-
field,
|
|
29897
|
-
allFields: localSchema,
|
|
29898
|
-
step: buildDisplayStep(addon, effective, settingsModelIdFor(addon, effective, inheritedModelId, rootModel)),
|
|
29899
|
-
disabled: !settingsOverridden,
|
|
29900
|
-
onChange: (next) => setSettings({ ...next.settings })
|
|
29901
|
-
}, field.key)),
|
|
29902
|
-
!settingsOverridden && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29903
|
-
className: "text-[10px] text-foreground-subtle",
|
|
29904
|
-
children: ["inherits from ", agentNodeId]
|
|
29905
|
-
})
|
|
29906
|
-
]
|
|
29907
|
-
});
|
|
29908
|
-
})(),
|
|
29909
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClusterSettingsHint, { addonId: addon.id }),
|
|
29910
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29911
|
-
className: "rounded bg-muted/40 px-3 py-2 text-[11px] text-foreground-subtle",
|
|
29912
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
29913
|
-
className: "font-semibold text-foreground mb-0.5",
|
|
29914
|
-
children: "Effective provisioning preview"
|
|
29915
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29916
|
-
className: "font-mono truncate",
|
|
29917
|
-
children: [
|
|
29918
|
-
addon.id,
|
|
29919
|
-
" · model: ",
|
|
29920
|
-
effective.modelId ?? inheritedModelId ?? "Auto"
|
|
29921
|
-
]
|
|
29922
|
-
})]
|
|
29923
|
-
})
|
|
29924
|
-
]
|
|
29925
|
-
});
|
|
29926
|
-
}
|
|
29927
|
-
function AgentStepEditor(props) {
|
|
29928
|
-
const { mode, addon, agentDefault, agentNodeId, currentPatch, onChangeAgentConfig, onChangePatch, engineFormat, effectiveModelId, rootModel } = props;
|
|
29929
|
-
const modelsForFormat = (0, react$1.useMemo)(() => stepModelOptions(addon.models, engineFormat), [addon.models, engineFormat]);
|
|
29930
|
-
if (mode === "agent") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AgentModeEditor, {
|
|
29931
|
-
addon,
|
|
29932
|
-
agentDefault,
|
|
29933
|
-
modelsForFormat,
|
|
29934
|
-
engineFormat,
|
|
29935
|
-
effectiveModelId,
|
|
29936
|
-
...rootModel !== void 0 ? { rootModel } : {},
|
|
29937
|
-
onChangeAgentConfig
|
|
29938
|
-
});
|
|
29939
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeviceModeEditor, {
|
|
29940
|
-
addon,
|
|
29941
|
-
agentDefault,
|
|
29942
|
-
agentNodeId,
|
|
29943
|
-
currentPatch,
|
|
29944
|
-
modelsForFormat,
|
|
29945
|
-
engineFormat,
|
|
29946
|
-
effectiveModelId,
|
|
29947
|
-
...rootModel !== void 0 ? { rootModel } : {},
|
|
29948
|
-
onChangePatch
|
|
29949
|
-
});
|
|
29950
|
-
}
|
|
29951
30012
|
//#endregion
|
|
29952
30013
|
//#region src/primitives/icon-button.tsx
|
|
29953
30014
|
var iconButtonVariants = cva("inline-flex items-center justify-center rounded-md font-medium transition-colors cursor-pointer disabled:opacity-50 disabled:pointer-events-none", {
|
|
@@ -33862,117 +33923,6 @@ async function waitForIceGathering(pc) {
|
|
|
33862
33923
|
});
|
|
33863
33924
|
}
|
|
33864
33925
|
//#endregion
|
|
33865
|
-
//#region src/lib/responsive.ts
|
|
33866
|
-
/**
|
|
33867
|
-
* Responsive design tokens — three-tier system.
|
|
33868
|
-
*
|
|
33869
|
-
* The admin-ui targets three width tiers; every component should pick
|
|
33870
|
-
* the right token from this file instead of hand-coding breakpoints.
|
|
33871
|
-
* Adopting the tokens uniformly is what makes the app feel consistent
|
|
33872
|
-
* across operator devices (mobile / tablet / desktop) and what unlocks
|
|
33873
|
-
* targeted tuning (e.g. shrink every label by 1px in one place).
|
|
33874
|
-
*
|
|
33875
|
-
* Tier definitions (Tailwind breakpoints):
|
|
33876
|
-
*
|
|
33877
|
-
* • **L1 — Compact** (`< 640px`, default = mobile portrait)
|
|
33878
|
-
* Single column, tight padding, smallest text, icon-only buttons
|
|
33879
|
-
* where labels can be inferred. The operator can still see every
|
|
33880
|
-
* stat / action — no horizontal scroll, no clipped text. The user
|
|
33881
|
-
* directive: "molta informazione in poco spazio quando la width è
|
|
33882
|
-
* bassa." [a lot of information in little space when the width is low.]
|
|
33883
|
-
*
|
|
33884
|
-
* • **L2 — Standard** (`sm` — `640–1023px`, tablet + small laptop)
|
|
33885
|
-
* 2-column grids, side panels become visible, button labels return.
|
|
33886
|
-
* Targets ~768px–1024px viewports — the most common operator sizes.
|
|
33887
|
-
*
|
|
33888
|
-
* • **L3 — Spacious** (`lg+` — `≥ 1024px`, desktop)
|
|
33889
|
-
* 4-column grids, generous padding, full-width header text,
|
|
33890
|
-
* side-by-side composite layouts. The operator has elbow room.
|
|
33891
|
-
*
|
|
33892
|
-
* Conventions:
|
|
33893
|
-
* - **Always start from L1** — the bare class is always the compact
|
|
33894
|
-
* variant, then `sm:` adds room at 640px, `lg:` at 1024px. No
|
|
33895
|
-
* desktop-first breakpoints; no max-width queries.
|
|
33896
|
-
* - **Token strings are static** — kept as `as const` so callers can
|
|
33897
|
-
* compose them via `cn()` / template literals without running a
|
|
33898
|
-
* class generator at runtime.
|
|
33899
|
-
* - **Pair the token with a comment** — the comment in the file
|
|
33900
|
-
* names every breakpoint it crosses, so a reader can predict the
|
|
33901
|
-
* layout without going to Tailwind docs.
|
|
33902
|
-
*/
|
|
33903
|
-
/** Container padding for a card-style section (header + body). */
|
|
33904
|
-
var SECTION_CARD = "rounded-lg border border-border bg-surface overflow-hidden";
|
|
33905
|
-
/** Header strip inside a card (between border-b lines). */
|
|
33906
|
-
var SECTION_HEADER = "border-b border-border px-3 sm:px-4 py-1.5 sm:py-2";
|
|
33907
|
-
/** Body padding for a card-style section. */
|
|
33908
|
-
var SECTION_BODY = "p-2 sm:p-3 lg:p-4";
|
|
33909
|
-
/** Gap between cards in a vertical stack. */
|
|
33910
|
-
var STACK_GAP = "space-y-3 sm:space-y-4";
|
|
33911
|
-
/** Gap between cards in a grid (4 cells / row at lg). */
|
|
33912
|
-
var GRID_GAP = "gap-2 sm:gap-3";
|
|
33913
|
-
/** Row padding inside a divided list (e.g. InfoRow). */
|
|
33914
|
-
var LIST_ROW = "px-3 sm:px-4 py-1.5 sm:py-2";
|
|
33915
|
-
/** 1-up at L1, 2-up at L2, 4-up at L3 — quick-stat row. */
|
|
33916
|
-
var GRID_QUICK_STATS = "grid grid-cols-2 sm:grid-cols-2 lg:grid-cols-4";
|
|
33917
|
-
/** 1-up at L1, 2-up at L2+ — paired panels (motion + detection rules). */
|
|
33918
|
-
var GRID_PAIRED = "grid grid-cols-1 lg:grid-cols-2";
|
|
33919
|
-
/** Side-by-side layout that stacks below md (canvas + side panel). */
|
|
33920
|
-
var SPLIT_PANEL_OUTER = "flex flex-col-reverse md:flex-row";
|
|
33921
|
-
/** Sidebar narrow lane in a SPLIT_PANEL_OUTER layout. */
|
|
33922
|
-
var SPLIT_PANEL_SIDE = "w-full md:w-44 lg:w-52 md:flex-shrink-0 border-b md:border-b-0 md:border-l border-border";
|
|
33923
|
-
/**
|
|
33924
|
-
* The tier boundary at which a settings label/value row stops stacking.
|
|
33925
|
-
*
|
|
33926
|
-
* Below it (L1 — compact) the row is ONE column: label on its own line, value
|
|
33927
|
-
* on the next at full width. Above it the row is label-left / value-right as
|
|
33928
|
-
* before. The two-column form needs `SETTING_ROW_LABEL`'s lane plus a readable
|
|
33929
|
-
* value beside it; a phone has neither, and the failure mode is not "cramped"
|
|
33930
|
-
* but "the value is the part that gets cut" — the one thing the operator
|
|
33931
|
-
* opened the page to read.
|
|
33932
|
-
*/
|
|
33933
|
-
var SETTING_ROW_STACK_BREAKPOINT = "sm";
|
|
33934
|
-
/** Outer row: single column at L1, label/value columns from `sm` up. */
|
|
33935
|
-
var SETTING_ROW = "flex min-w-0 flex-col gap-0.5 py-1.5 sm:flex-row sm:items-center";
|
|
33936
|
-
/** Label lane. Full width (and free to wrap) at L1; a fixed lane from `sm` up. */
|
|
33937
|
-
var SETTING_ROW_LABEL = "min-w-0 text-[11px] leading-tight text-foreground-subtle sm:w-32 sm:shrink-0 sm:pr-2";
|
|
33938
|
-
/**
|
|
33939
|
-
* Value lane — the value text plus its trailing affordances (copy / reveal).
|
|
33940
|
-
* `w-full` at L1 so the value owns the whole row once the label is above it;
|
|
33941
|
-
* `sm:flex-1` makes it share the row again from `sm` up.
|
|
33942
|
-
*/
|
|
33943
|
-
var SETTING_ROW_VALUE = "flex w-full min-w-0 items-center gap-1 sm:flex-1";
|
|
33944
|
-
/**
|
|
33945
|
-
* The value text itself. `break-all` lets an opaque token (a key, a URL) wrap
|
|
33946
|
-
* at L1 instead of being cut mid-word; `sm:truncate` restores the single-line
|
|
33947
|
-
* desktop form, where the row is wide enough for truncation to be a choice
|
|
33948
|
-
* rather than data loss.
|
|
33949
|
-
*/
|
|
33950
|
-
var SETTING_ROW_VALUE_TEXT = "min-w-0 break-all text-xs text-foreground sm:truncate";
|
|
33951
|
-
/** Section header label (uppercase tracking-wider). */
|
|
33952
|
-
var TEXT_SECTION_LABEL = "text-[10px] sm:text-[11px] font-semibold text-foreground uppercase tracking-wider";
|
|
33953
|
-
/** Field label inside a row. */
|
|
33954
|
-
var TEXT_FIELD_LABEL = "text-[11px] sm:text-xs text-foreground";
|
|
33955
|
-
/** Sub-label / hint text. */
|
|
33956
|
-
var TEXT_HINT = "text-[10px] text-foreground-subtle";
|
|
33957
|
-
/** Primary value (a metric, badge, …). */
|
|
33958
|
-
var TEXT_VALUE = "text-[11px] sm:text-xs font-medium text-foreground";
|
|
33959
|
-
/** Big metric value (StatCard). */
|
|
33960
|
-
var TEXT_METRIC = "text-base sm:text-lg font-semibold leading-tight text-foreground";
|
|
33961
|
-
/** Default compact button. */
|
|
33962
|
-
var BTN_COMPACT = "inline-flex items-center justify-center gap-1 rounded border border-border bg-surface px-1.5 py-0.5 sm:px-2 sm:py-1 text-[10px] sm:text-[11px] font-medium text-foreground hover:bg-surface-hover transition-colors disabled:opacity-40";
|
|
33963
|
-
/** Primary compact button. */
|
|
33964
|
-
var BTN_COMPACT_PRIMARY = "inline-flex items-center justify-center gap-1 rounded border border-primary/40 bg-primary/15 px-1.5 py-0.5 sm:px-2 sm:py-1 text-[10px] sm:text-[11px] font-medium text-primary hover:bg-primary/25 transition-colors disabled:opacity-40";
|
|
33965
|
-
/** Danger compact button. */
|
|
33966
|
-
var BTN_COMPACT_DANGER = "inline-flex items-center justify-center gap-1 rounded border border-danger/40 bg-danger/10 px-1.5 py-0.5 sm:px-2 sm:py-1 text-[10px] sm:text-[11px] font-medium text-danger hover:bg-danger/20 transition-colors disabled:opacity-40";
|
|
33967
|
-
/** Warning compact button (e.g. cancel-drawing). */
|
|
33968
|
-
var BTN_COMPACT_WARNING = "inline-flex items-center justify-center gap-1 rounded border border-warning/40 bg-warning/10 px-1.5 py-0.5 sm:px-2 sm:py-1 text-[10px] sm:text-[11px] font-medium text-warning hover:bg-warning/20 transition-colors disabled:opacity-40";
|
|
33969
|
-
/** Compact text/select/number input. */
|
|
33970
|
-
var INPUT_COMPACT = "w-full rounded border border-border bg-background px-1.5 py-0.5 sm:px-2 sm:py-1 text-[11px] sm:text-xs text-foreground focus:border-primary focus:ring-1 focus:ring-primary/30 outline-none";
|
|
33971
|
-
/** Chip-style toggle (zone / class selectors in ZoneRulesEditor). */
|
|
33972
|
-
var CHIP_BASE = "inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-[10px] font-medium transition-colors";
|
|
33973
|
-
var CHIP_ACTIVE = "border-primary/50 bg-primary/15 text-primary";
|
|
33974
|
-
var CHIP_INACTIVE = "border-border bg-surface text-foreground-subtle hover:bg-surface-hover";
|
|
33975
|
-
//#endregion
|
|
33976
33926
|
//#region src/composites/data-table-layout.ts
|
|
33977
33927
|
/**
|
|
33978
33928
|
* At this many columns and above, `auto` switches a narrow viewport to cards.
|
|
@@ -55443,25 +55393,46 @@ function ClassChips({ inputs, outputs }) {
|
|
|
55443
55393
|
* the first column; each device's real format is shown per sub-column instead
|
|
55444
55394
|
* ("Coral Edge TPU · tflite").
|
|
55445
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
|
+
}
|
|
55446
55421
|
function GroupedAgentHeader({ group }) {
|
|
55422
|
+
const nodeLabel = group.columns[0]?.nodeLabel ?? group.agentNodeId;
|
|
55447
55423
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
55424
|
+
role: "columnheader",
|
|
55448
55425
|
className: "sticky top-0 z-10 min-w-0 border-b border-l border-border bg-muted/60",
|
|
55449
55426
|
style: { gridColumn: `span ${group.columns.length}` },
|
|
55450
55427
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
55451
55428
|
className: "truncate px-3 pt-2 pb-1 text-xs font-semibold text-foreground",
|
|
55452
|
-
children:
|
|
55429
|
+
children: nodeLabel
|
|
55453
55430
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
55454
55431
|
className: "mt-1 flex border-t border-border/50",
|
|
55455
|
-
children: group.columns.map((c, i) => /* @__PURE__ */ (0, react_jsx_runtime.
|
|
55456
|
-
className: cn("min-w-0 flex-1 px-3 py-1", i > 0 && "border-l border-border/50"),
|
|
55457
|
-
title: `${c.deviceLabel ?? c.deviceKey} · ${c.engineLabel}`,
|
|
55458
|
-
children:
|
|
55459
|
-
className: "truncate text-[10px] font-medium text-foreground/90",
|
|
55460
|
-
children: c.deviceLabel ?? c.deviceKey
|
|
55461
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
55462
|
-
className: "truncate text-[9px] text-foreground-subtle",
|
|
55463
|
-
children: c.engineLabel
|
|
55464
|
-
})]
|
|
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 })
|
|
55465
55436
|
}, agentColumnKey(c)))
|
|
55466
55437
|
})]
|
|
55467
55438
|
});
|
|
@@ -55565,97 +55536,75 @@ function cellButtonClass(state, isSelected, extra) {
|
|
|
55565
55536
|
//#endregion
|
|
55566
55537
|
//#region src/composites/device-step-matrix.tsx
|
|
55567
55538
|
/**
|
|
55568
|
-
* DeviceStepMatrix — the
|
|
55569
|
-
*
|
|
55570
|
-
*
|
|
55571
|
-
*
|
|
55572
|
-
*
|
|
55573
|
-
*
|
|
55574
|
-
*
|
|
55575
|
-
*
|
|
55576
|
-
* the
|
|
55577
|
-
*
|
|
55578
|
-
*
|
|
55579
|
-
*
|
|
55580
|
-
*
|
|
55581
|
-
*
|
|
55582
|
-
*
|
|
55583
|
-
*
|
|
55584
|
-
*
|
|
55585
|
-
*
|
|
55586
|
-
*
|
|
55587
|
-
|
|
55588
|
-
|
|
55589
|
-
|
|
55590
|
-
|
|
55591
|
-
|
|
55592
|
-
* Narrow (mobile) step-column width. On a phone the wide 21rem column would
|
|
55593
|
-
* occlude nearly the whole viewport, so the single-node layout pins a slimmer
|
|
55594
|
-
* sticky step column and lets the device columns scroll horizontally beside it
|
|
55595
|
-
* (both fully readable) rather than cramming every column into the viewport.
|
|
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.
|
|
55596
55563
|
*/
|
|
55597
|
-
|
|
55598
|
-
|
|
55599
|
-
|
|
55600
|
-
return gate.effectiveOn ? "on" : "off";
|
|
55601
|
-
}
|
|
55564
|
+
/** The sticky step column. */
|
|
55565
|
+
var STEP_COL_REM = 18;
|
|
55566
|
+
var STEP_COL_PX = STEP_COL_REM * 16;
|
|
55602
55567
|
/**
|
|
55603
|
-
*
|
|
55604
|
-
* the
|
|
55605
|
-
*
|
|
55568
|
+
* Narrow (mobile) step-column width. On a phone the wide column would occlude
|
|
55569
|
+
* nearly the whole viewport, so the single-node layout pins a slimmer sticky
|
|
55570
|
+
* step column and lets the device columns scroll horizontally beside it.
|
|
55606
55571
|
*/
|
|
55607
|
-
|
|
55608
|
-
|
|
55609
|
-
|
|
55610
|
-
|
|
55611
|
-
|
|
55612
|
-
|
|
55613
|
-
|
|
55614
|
-
|
|
55615
|
-
|
|
55616
|
-
|
|
55617
|
-
|
|
55618
|
-
|
|
55619
|
-
|
|
55620
|
-
|
|
55621
|
-
|
|
55622
|
-
|
|
55623
|
-
|
|
55624
|
-
|
|
55625
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "w-px self-stretch bg-border" }),
|
|
55626
|
-
seg("off", "Off", false, "bg-foreground-subtle/70 text-white")
|
|
55627
|
-
]
|
|
55628
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
55629
|
-
className: "text-[10px] leading-none text-foreground-subtle",
|
|
55630
|
-
children: active === "default" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
55631
|
-
"= default (",
|
|
55632
|
-
gate.defaultOn ? "on" : "off",
|
|
55633
|
-
")"
|
|
55634
|
-
] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: ["overridden · default ", gate.defaultOn ? "on" : "off"] })
|
|
55635
|
-
})]
|
|
55636
|
-
});
|
|
55637
|
-
}
|
|
55638
|
-
function ZoneBadge() {
|
|
55639
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
55640
|
-
className: "flex flex-col gap-1",
|
|
55641
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
55642
|
-
className: "inline-flex w-fit items-center gap-1 rounded-md border border-violet-500/40 bg-violet-500/10 px-2 py-1 text-[11px] font-medium text-violet-400",
|
|
55643
|
-
children: "Zones"
|
|
55644
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
55645
|
-
className: "text-[10px] leading-none text-foreground-subtle",
|
|
55646
|
-
children: "driven by package zones"
|
|
55647
|
-
})]
|
|
55648
|
-
});
|
|
55572
|
+
var STEP_COL_SINGLE_REM = 11;
|
|
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
|
+
}
|
|
55649
55590
|
}
|
|
55650
|
-
function
|
|
55651
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
55652
|
-
|
|
55653
|
-
|
|
55654
|
-
children:
|
|
55655
|
-
|
|
55656
|
-
|
|
55657
|
-
|
|
55658
|
-
|
|
55591
|
+
function StepCell({ node, depth, r, tabbable, selected, badge, onClick, sticky }) {
|
|
55592
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
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
|
+
})
|
|
55659
55608
|
});
|
|
55660
55609
|
}
|
|
55661
55610
|
/**
|
|
@@ -55685,76 +55634,179 @@ function NodeSelectorBar({ nodes, selected, onSelect }) {
|
|
|
55685
55634
|
role: "tab",
|
|
55686
55635
|
"aria-selected": isSelected,
|
|
55687
55636
|
onClick: () => onSelect(a.agentNodeId),
|
|
55688
|
-
title: `${a.agentNodeId} · ${a.engineLabel}`,
|
|
55637
|
+
title: `${a.columns[0]?.nodeLabel ?? a.agentNodeId} · ${a.engineLabel}`,
|
|
55689
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"),
|
|
55690
|
-
children: a.agentNodeId
|
|
55639
|
+
children: a.columns[0]?.nodeLabel ?? a.agentNodeId
|
|
55691
55640
|
}, a.agentNodeId);
|
|
55692
55641
|
})
|
|
55693
55642
|
})
|
|
55694
55643
|
]
|
|
55695
55644
|
});
|
|
55696
55645
|
}
|
|
55697
|
-
|
|
55646
|
+
var MatrixGrid = (0, react$1.forwardRef)(function MatrixGrid({ tree, agents, getCellState, onCellClick, onStepClick, stepBadge, selectedCell, single, reserveRightPx, label }, ref) {
|
|
55698
55647
|
const rows = (0, react$1.useMemo)(() => flattenTree(tree), [tree]);
|
|
55699
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
|
+
};
|
|
55700
55737
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
55738
|
+
ref: boxRef,
|
|
55701
55739
|
className: "overflow-auto rounded border border-border",
|
|
55702
55740
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
55703
|
-
className: "
|
|
55704
|
-
|
|
55705
|
-
|
|
55706
|
-
|
|
55707
|
-
|
|
55708
|
-
|
|
55709
|
-
}
|
|
55710
|
-
|
|
55711
|
-
|
|
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",
|
|
55712
55752
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
55713
|
-
|
|
55714
|
-
|
|
55715
|
-
|
|
55716
|
-
|
|
55717
|
-
|
|
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));
|
|
55718
55799
|
})]
|
|
55719
|
-
},
|
|
55720
|
-
|
|
55721
|
-
|
|
55722
|
-
|
|
55723
|
-
|
|
55724
|
-
|
|
55725
|
-
getCellState,
|
|
55726
|
-
onCellClick,
|
|
55727
|
-
selectedCell,
|
|
55728
|
-
onSetGate,
|
|
55729
|
-
gateBusy,
|
|
55730
|
-
hideGate
|
|
55731
|
-
}, node.addonId))
|
|
55732
|
-
]
|
|
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
|
+
})]
|
|
55733
55806
|
})
|
|
55734
55807
|
});
|
|
55735
|
-
}
|
|
55736
|
-
function
|
|
55737
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepGateCell, {
|
|
55738
|
-
node,
|
|
55739
|
-
depth,
|
|
55740
|
-
gate,
|
|
55741
|
-
busy: gateBusy,
|
|
55742
|
-
onSet: (next) => onSetGate(node.addonId, next),
|
|
55743
|
-
hideGate
|
|
55744
|
-
}), agents.map((a) => {
|
|
55745
|
-
const state = getCellState(node.addonId, a);
|
|
55746
|
-
const isSelected = selectedCell !== null && selectedCell.addonId === node.addonId && agentColumnKey(selectedCell) === agentColumnKey(a);
|
|
55747
|
-
const actionable = state.kind === "enabled" || state.kind === "skip";
|
|
55748
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
55749
|
-
type: "button",
|
|
55750
|
-
disabled: !actionable,
|
|
55751
|
-
onClick: () => onCellClick(node.addonId, a),
|
|
55752
|
-
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")),
|
|
55753
|
-
children: renderCellContent(state)
|
|
55754
|
-
}, agentColumnKey(a));
|
|
55755
|
-
})] });
|
|
55756
|
-
}
|
|
55757
|
-
function DeviceStepMatrix({ tree, agents, gateFor, getCellState, onCellClick, selectedCell, onSetGate, gateBusy, fallbackNodeId, onFallbackNodeChange, hideGate }) {
|
|
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) {
|
|
55758
55810
|
const containerRef = (0, react$1.useRef)(null);
|
|
55759
55811
|
const [width, setWidth] = (0, react$1.useState)(0);
|
|
55760
55812
|
(0, react$1.useLayoutEffect)(() => {
|
|
@@ -55772,6 +55824,15 @@ function DeviceStepMatrix({ tree, agents, gateFor, getCellState, onCellClick, se
|
|
|
55772
55824
|
if (nodes.length === 0) return null;
|
|
55773
55825
|
return nodes.find((n) => n.agentNodeId === fallbackNodeId) ?? nodes[0] ?? null;
|
|
55774
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
|
+
]);
|
|
55775
55836
|
const shownAgents = single && selectedNode ? selectedNode.columns : agents;
|
|
55776
55837
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
55777
55838
|
ref: containerRef,
|
|
@@ -55780,20 +55841,21 @@ function DeviceStepMatrix({ tree, agents, gateFor, getCellState, onCellClick, se
|
|
|
55780
55841
|
nodes,
|
|
55781
55842
|
selected: selectedNode.agentNodeId,
|
|
55782
55843
|
onSelect: onFallbackNodeChange
|
|
55783
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MatrixGrid
|
|
55844
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MatrixGrid, {
|
|
55845
|
+
ref,
|
|
55784
55846
|
tree,
|
|
55785
55847
|
agents: shownAgents,
|
|
55786
|
-
gateFor,
|
|
55787
55848
|
getCellState,
|
|
55788
55849
|
onCellClick,
|
|
55850
|
+
onStepClick,
|
|
55851
|
+
stepBadge,
|
|
55789
55852
|
selectedCell,
|
|
55790
|
-
onSetGate,
|
|
55791
|
-
gateBusy,
|
|
55792
55853
|
single: single && selectedNode !== null,
|
|
55793
|
-
|
|
55854
|
+
reserveRightPx,
|
|
55855
|
+
label
|
|
55794
55856
|
})]
|
|
55795
55857
|
});
|
|
55796
|
-
}
|
|
55858
|
+
});
|
|
55797
55859
|
//#endregion
|
|
55798
55860
|
//#region src/composites/discovery-panel.tsx
|
|
55799
55861
|
function DiscoveryPanel({ deviceId, className = "", onOpenChild }) {
|
|
@@ -56732,43 +56794,13 @@ function PhaseIcon({ phase, ...props }) {
|
|
|
56732
56794
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { ...props });
|
|
56733
56795
|
}
|
|
56734
56796
|
//#endregion
|
|
56735
|
-
//#region src/lib/validate-template.ts
|
|
56736
|
-
function validateTemplate(steps, schema) {
|
|
56737
|
-
const availableAddonIds = new Set(schema.slots.flatMap((s) => s.addons.map((a) => a.id)));
|
|
56738
|
-
const warnings = [];
|
|
56739
|
-
function validateStep(step) {
|
|
56740
|
-
if (!availableAddonIds.has(step.addonId)) {
|
|
56741
|
-
warnings.push(`Addon "${step.addonId}" is no longer available — step removed`);
|
|
56742
|
-
return null;
|
|
56743
|
-
}
|
|
56744
|
-
const addon = schema.slots.flatMap((s) => s.addons).find((a) => a.id === step.addonId);
|
|
56745
|
-
let modelId = step.modelId;
|
|
56746
|
-
if (addon && !addon.models.some((m) => m.id === modelId)) {
|
|
56747
|
-
const fallback = addon.defaultModelId;
|
|
56748
|
-
warnings.push(`Model "${modelId}" not available for ${step.addonId} — using "${fallback}"`);
|
|
56749
|
-
modelId = fallback;
|
|
56750
|
-
}
|
|
56751
|
-
const validChildren = step.children.map((c) => validateStep(c)).filter((c) => c !== null);
|
|
56752
|
-
return {
|
|
56753
|
-
...step,
|
|
56754
|
-
modelId,
|
|
56755
|
-
children: validChildren
|
|
56756
|
-
};
|
|
56757
|
-
}
|
|
56758
|
-
const validSteps = steps.map((s) => validateStep(s)).filter((s) => s !== null);
|
|
56759
|
-
return {
|
|
56760
|
-
valid: warnings.length === 0,
|
|
56761
|
-
steps: validSteps,
|
|
56762
|
-
warnings
|
|
56763
|
-
};
|
|
56764
|
-
}
|
|
56765
|
-
//#endregion
|
|
56766
56797
|
//#region src/composites/pipeline-builder.tsx
|
|
56767
56798
|
/**
|
|
56768
|
-
* PipelineBuilder — orchestrates the pipeline step tree
|
|
56799
|
+
* PipelineBuilder — orchestrates the pipeline step tree.
|
|
56769
56800
|
*
|
|
56770
|
-
* Renders
|
|
56771
|
-
*
|
|
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.
|
|
56772
56804
|
*/
|
|
56773
56805
|
function buildSchemaMap(schema) {
|
|
56774
56806
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -56820,41 +56852,9 @@ function PlaceholderStep({ addon, onClick }) {
|
|
|
56820
56852
|
})
|
|
56821
56853
|
});
|
|
56822
56854
|
}
|
|
56823
|
-
function PipelineBuilder({ schema, steps, onChange,
|
|
56824
|
-
const stepExtraProps = (addonId) => ({
|
|
56825
|
-
toggleMode,
|
|
56826
|
-
hideModelAndSettings,
|
|
56827
|
-
allowAutoModel,
|
|
56828
|
-
...toggleMode === "threeState" ? {
|
|
56829
|
-
overrideState: overrides[addonId] !== void 0 ? overrides[addonId] : null,
|
|
56830
|
-
onOverrideChange: onOverrideChange ? (next) => onOverrideChange(addonId, next) : void 0,
|
|
56831
|
-
inheritedEnabled: inheritedEnabledByAddon[addonId]
|
|
56832
|
-
} : {}
|
|
56833
|
-
});
|
|
56855
|
+
function PipelineBuilder({ schema, steps, onChange, readOnly = false, excludeAddons = [], showOnlyEnabled = false, showSlotLabels = false }) {
|
|
56834
56856
|
const excluded = (0, react$1.useMemo)(() => new Set(excludeAddons), [excludeAddons]);
|
|
56835
56857
|
const schemaMap = (0, react$1.useMemo)(() => buildSchemaMap(schema), [schema]);
|
|
56836
|
-
const [warnings, setWarnings] = (0, react$1.useState)([]);
|
|
56837
|
-
const dirty = selectedTemplateId ? JSON.stringify(steps) !== JSON.stringify(templates.find((t) => t.id === selectedTemplateId)?.steps) : false;
|
|
56838
|
-
function handleSelectTemplate(e) {
|
|
56839
|
-
const id = e.target.value || null;
|
|
56840
|
-
if (id) {
|
|
56841
|
-
const tpl = templates.find((t) => t.id === id);
|
|
56842
|
-
if (tpl) setWarnings([...validateTemplate(tpl.steps, schema).warnings]);
|
|
56843
|
-
} else setWarnings([]);
|
|
56844
|
-
onSelectTemplate(id);
|
|
56845
|
-
}
|
|
56846
|
-
function handleSave() {
|
|
56847
|
-
if (selectedTemplateId) onUpdateTemplate(selectedTemplateId, steps);
|
|
56848
|
-
}
|
|
56849
|
-
function handleSaveAs() {
|
|
56850
|
-
const name = window.prompt("Template name:");
|
|
56851
|
-
if (name?.trim()) onSaveTemplate(name.trim(), steps);
|
|
56852
|
-
}
|
|
56853
|
-
function handleDelete() {
|
|
56854
|
-
if (!selectedTemplateId) return;
|
|
56855
|
-
const tpl = templates.find((t) => t.id === selectedTemplateId);
|
|
56856
|
-
if (tpl && window.confirm(`Delete template "${tpl.name}"?`)) onDeleteTemplate(selectedTemplateId);
|
|
56857
|
-
}
|
|
56858
56858
|
/**
|
|
56859
56859
|
* When a step is updated, auto-enable the entire ancestor chain if a
|
|
56860
56860
|
* child/grandchild was just enabled. This ensures enabling face-recognition
|
|
@@ -56937,8 +56937,7 @@ function PipelineBuilder({ schema, steps, onChange, templates, selectedTemplateI
|
|
|
56937
56937
|
allSchemas: schemaMap,
|
|
56938
56938
|
onChange: handleStepChange,
|
|
56939
56939
|
onDelete: readOnly ? void 0 : (id) => onChange(steps.filter((s) => s.addonId !== id)),
|
|
56940
|
-
readOnly
|
|
56941
|
-
...stepExtraProps(step.addonId)
|
|
56940
|
+
readOnly
|
|
56942
56941
|
}), (step.children.length > 0 || childPlaceholders.length > 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
56943
56942
|
className: "ml-4 pl-3 border-l border-border/60 space-y-1.5",
|
|
56944
56943
|
children: [
|
|
@@ -56967,8 +56966,7 @@ function PipelineBuilder({ schema, steps, onChange, templates, selectedTemplateI
|
|
|
56967
56966
|
children: step.children.filter((c) => c.addonId !== id)
|
|
56968
56967
|
});
|
|
56969
56968
|
},
|
|
56970
|
-
readOnly
|
|
56971
|
-
...stepExtraProps(child.addonId)
|
|
56969
|
+
readOnly
|
|
56972
56970
|
}), (child.children.length > 0 || childChildPlaceholders.length > 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
56973
56971
|
className: "ml-4 pl-3 border-l border-border/40 space-y-1.5",
|
|
56974
56972
|
children: [
|
|
@@ -56999,8 +56997,7 @@ function PipelineBuilder({ schema, steps, onChange, templates, selectedTemplateI
|
|
|
56999
56997
|
} : c)
|
|
57000
56998
|
});
|
|
57001
56999
|
},
|
|
57002
|
-
readOnly
|
|
57003
|
-
...stepExtraProps(grandchild.addonId)
|
|
57000
|
+
readOnly
|
|
57004
57001
|
}, grandchild.addonId)),
|
|
57005
57002
|
!readOnly && childChildPlaceholders.map((addon) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlaceholderStep, {
|
|
57006
57003
|
addon,
|
|
@@ -57027,104 +57024,42 @@ function PipelineBuilder({ schema, steps, onChange, templates, selectedTemplateI
|
|
|
57027
57024
|
})]
|
|
57028
57025
|
}, step.addonId);
|
|
57029
57026
|
}
|
|
57030
|
-
|
|
57031
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57027
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57032
57028
|
className: "space-y-4",
|
|
57033
|
-
children:
|
|
57034
|
-
|
|
57035
|
-
|
|
57036
|
-
|
|
57037
|
-
|
|
57038
|
-
|
|
57039
|
-
|
|
57040
|
-
|
|
57041
|
-
|
|
57042
|
-
|
|
57043
|
-
|
|
57044
|
-
|
|
57045
|
-
|
|
57046
|
-
|
|
57047
|
-
|
|
57048
|
-
|
|
57049
|
-
|
|
57050
|
-
|
|
57051
|
-
|
|
57052
|
-
|
|
57053
|
-
|
|
57054
|
-
|
|
57055
|
-
|
|
57056
|
-
|
|
57057
|
-
|
|
57058
|
-
|
|
57059
|
-
|
|
57060
|
-
|
|
57061
|
-
|
|
57062
|
-
|
|
57063
|
-
|
|
57064
|
-
|
|
57065
|
-
|
|
57066
|
-
|
|
57067
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopyPlus, { className: "h-4 w-4" })
|
|
57068
|
-
}),
|
|
57069
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
57070
|
-
onClick: handleDelete,
|
|
57071
|
-
disabled: !selectedTemplateId || readOnly,
|
|
57072
|
-
title: "Delete",
|
|
57073
|
-
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"),
|
|
57074
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Trash2, { className: "h-4 w-4" })
|
|
57075
|
-
})
|
|
57076
|
-
]
|
|
57077
|
-
})
|
|
57078
|
-
}),
|
|
57079
|
-
warnings.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57080
|
-
className: "rounded-lg border border-amber-500/30 bg-amber-500/5 p-3 text-xs text-amber-400 space-y-1",
|
|
57081
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57082
|
-
className: "flex items-center justify-between",
|
|
57083
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57084
|
-
className: "font-medium",
|
|
57085
|
-
children: "Template loaded with warnings:"
|
|
57086
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
57087
|
-
onClick: () => setWarnings([]),
|
|
57088
|
-
className: "text-amber-400/60 hover:text-amber-400",
|
|
57089
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(X, { className: "h-3.5 w-3.5" })
|
|
57090
|
-
})]
|
|
57091
|
-
}), warnings.map((w, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: ["• ", w] }, i))]
|
|
57092
|
-
}),
|
|
57093
|
-
rootSlots.map((slot) => {
|
|
57094
|
-
const allSlotSteps = steps.filter((s) => s.slot === slot.id && !excluded.has(s.addonId));
|
|
57095
|
-
const slotSteps = showOnlyEnabled ? allSlotSteps.filter((s) => s.enabled) : allSlotSteps;
|
|
57096
|
-
const missingRootAddons = showOnlyEnabled ? [] : slot.addons.filter((a) => !existingIds.has(a.id) && !excluded.has(a.id));
|
|
57097
|
-
const groups = /* @__PURE__ */ new Map();
|
|
57098
|
-
const ungrouped = [];
|
|
57099
|
-
for (const step of slotSteps) if (step.group) {
|
|
57100
|
-
if (!groups.has(step.group)) groups.set(step.group, []);
|
|
57101
|
-
groups.get(step.group).push(step);
|
|
57102
|
-
} else ungrouped.push(step);
|
|
57103
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57104
|
-
className: "space-y-2",
|
|
57105
|
-
children: [
|
|
57106
|
-
showSlotLabels && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
57107
|
-
className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/50",
|
|
57108
|
-
children: ["Slot: ", slot.label]
|
|
57109
|
-
}),
|
|
57110
|
-
ungrouped.map((step) => renderStep(step)),
|
|
57111
|
-
[...groups.entries()].map(([groupName, groupSteps]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57112
|
-
className: "space-y-2 rounded-lg border border-border/40 p-2",
|
|
57113
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57114
|
-
className: "text-[10px] font-semibold uppercase tracking-widest text-foreground-subtle/50 px-1",
|
|
57115
|
-
children: groupName
|
|
57116
|
-
}), groupSteps.map((step) => renderStep(step))]
|
|
57117
|
-
}, groupName)),
|
|
57118
|
-
!readOnly && missingRootAddons.map((addon) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlaceholderStep, {
|
|
57119
|
-
addon,
|
|
57120
|
-
onClick: () => {
|
|
57121
|
-
onChange([...steps, createDefaultStep(addon)]);
|
|
57122
|
-
}
|
|
57123
|
-
}, addon.id))
|
|
57124
|
-
]
|
|
57125
|
-
}, slot.id);
|
|
57126
|
-
})
|
|
57127
|
-
]
|
|
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
|
+
})
|
|
57128
57063
|
});
|
|
57129
57064
|
}
|
|
57130
57065
|
//#endregion
|
|
@@ -57160,179 +57095,6 @@ function PipelineRuntimeSelector({ options, value, onChange }) {
|
|
|
57160
57095
|
});
|
|
57161
57096
|
}
|
|
57162
57097
|
//#endregion
|
|
57163
|
-
//#region src/composites/pipeline-tree-matrix.tsx
|
|
57164
|
-
var STEP_COL_REM = 18;
|
|
57165
|
-
function QuickToggle({ enabled, onChange, disabled }) {
|
|
57166
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
57167
|
-
type: "button",
|
|
57168
|
-
role: "switch",
|
|
57169
|
-
"aria-checked": enabled,
|
|
57170
|
-
disabled,
|
|
57171
|
-
onClick: (e) => {
|
|
57172
|
-
e.stopPropagation();
|
|
57173
|
-
onChange(!enabled);
|
|
57174
|
-
},
|
|
57175
|
-
className: cn("relative inline-flex h-3.5 w-7 shrink-0 items-center rounded-full transition-colors", enabled ? "bg-emerald-500" : "bg-foreground-subtle/30", disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"),
|
|
57176
|
-
title: enabled ? "Click to disable" : "Click to enable",
|
|
57177
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: cn("inline-block h-3 w-3 rounded-full bg-white shadow transition-transform", enabled ? "translate-x-3.5" : "translate-x-0.5") })
|
|
57178
|
-
});
|
|
57179
|
-
}
|
|
57180
|
-
function GridRow({ node, depth, agents, getCellState, onCellClick, selectedCell, toggleProps }) {
|
|
57181
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57182
|
-
className: "sticky left-0 z-10 bg-surface px-3 py-2 border-b border-border text-xs flex items-start gap-2 min-w-0",
|
|
57183
|
-
style: { paddingLeft: `${12 + depth * 14}px` },
|
|
57184
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepLabel, { node }), toggleProps && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57185
|
-
className: "pl-2 pt-0.5 shrink-0",
|
|
57186
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuickToggle, {
|
|
57187
|
-
enabled: toggleProps.enabled,
|
|
57188
|
-
onChange: toggleProps.onChange,
|
|
57189
|
-
disabled: toggleProps.disabled
|
|
57190
|
-
})
|
|
57191
|
-
})]
|
|
57192
|
-
}), agents.map((a) => {
|
|
57193
|
-
const state = getCellState(node.addonId, a);
|
|
57194
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
57195
|
-
type: "button",
|
|
57196
|
-
onClick: () => onCellClick(node.addonId, a),
|
|
57197
|
-
className: cellButtonClass(state, selectedCell !== null && selectedCell.addonId === node.addonId && agentColumnKey(selectedCell) === agentColumnKey(a), "min-w-0 overflow-hidden px-3 py-1.5 border-b border-l border-border"),
|
|
57198
|
-
children: renderCellContent(state)
|
|
57199
|
-
}, agentColumnKey(a));
|
|
57200
|
-
})] });
|
|
57201
|
-
}
|
|
57202
|
-
/**
|
|
57203
|
-
* Wide layout: horizontally self-scrolling matrix. The scroll lives INSIDE
|
|
57204
|
-
* this `overflow-auto` box (sticky first column + sticky header row) so the
|
|
57205
|
-
* page body never scrolls sideways. Column tracks are fixed px widths so the
|
|
57206
|
-
* grid is static regardless of content.
|
|
57207
|
-
*/
|
|
57208
|
-
function MatrixGrid({ rows, agents, getCellState, onCellClick, selectedCell, onToggleEnabled }) {
|
|
57209
|
-
const gridTemplate = `${STEP_COL_REM}rem repeat(${agents.length}, 13rem)`;
|
|
57210
|
-
const showToggle = onToggleEnabled !== void 0 && agents.length === 1;
|
|
57211
|
-
const onlyAgent = agents[0];
|
|
57212
|
-
const groups = groupAgentColumns(agents);
|
|
57213
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57214
|
-
className: "overflow-auto border border-border rounded",
|
|
57215
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57216
|
-
className: "grid w-max",
|
|
57217
|
-
style: { gridTemplateColumns: gridTemplate },
|
|
57218
|
-
children: [
|
|
57219
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57220
|
-
className: "sticky top-0 left-0 z-20 bg-muted/60 px-3 py-2 text-[10px] uppercase tracking-widest text-foreground-subtle border-b border-border",
|
|
57221
|
-
children: "Step"
|
|
57222
|
-
}),
|
|
57223
|
-
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", {
|
|
57224
|
-
className: "sticky top-0 z-10 min-w-0 bg-muted/60 px-3 py-2 border-b border-l border-border",
|
|
57225
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57226
|
-
className: "text-xs font-semibold text-foreground truncate",
|
|
57227
|
-
children: col.agentNodeId
|
|
57228
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57229
|
-
className: "text-[10px] text-foreground-subtle truncate",
|
|
57230
|
-
children: col.engineLabel
|
|
57231
|
-
})]
|
|
57232
|
-
}, agentColumnKey(col)))),
|
|
57233
|
-
rows.map(({ node, depth }) => {
|
|
57234
|
-
const cellState = showToggle && onlyAgent !== void 0 ? getCellState(node.addonId, onlyAgent) : null;
|
|
57235
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GridRow, {
|
|
57236
|
-
node,
|
|
57237
|
-
depth,
|
|
57238
|
-
agents,
|
|
57239
|
-
getCellState,
|
|
57240
|
-
onCellClick,
|
|
57241
|
-
selectedCell,
|
|
57242
|
-
toggleProps: showToggle && cellState !== null ? {
|
|
57243
|
-
enabled: cellState.kind === "enabled",
|
|
57244
|
-
onChange: (next) => onToggleEnabled?.(node.addonId, next),
|
|
57245
|
-
disabled: cellState.kind === "na"
|
|
57246
|
-
} : void 0
|
|
57247
|
-
}, node.addonId);
|
|
57248
|
-
})
|
|
57249
|
-
]
|
|
57250
|
-
})
|
|
57251
|
-
});
|
|
57252
|
-
}
|
|
57253
|
-
/**
|
|
57254
|
-
* Narrow layout: one card per agent column, each listing every step stacked
|
|
57255
|
-
* vertically. Feature-parity with the matrix — same `onCellClick`, same
|
|
57256
|
-
* `renderCellContent`, same single-agent `onToggleEnabled`.
|
|
57257
|
-
*/
|
|
57258
|
-
function StackedCards({ rows, agents, getCellState, onCellClick, selectedCell, onToggleEnabled }) {
|
|
57259
|
-
const showToggle = onToggleEnabled !== void 0 && agents.length === 1;
|
|
57260
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57261
|
-
className: "space-y-3",
|
|
57262
|
-
children: agents.map((a) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57263
|
-
className: "border border-border rounded overflow-hidden",
|
|
57264
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57265
|
-
className: "bg-muted/60 px-3 py-2 border-b border-border",
|
|
57266
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57267
|
-
className: "text-xs font-semibold text-foreground truncate",
|
|
57268
|
-
children: a.deviceLabel ? `${a.agentNodeId} · ${a.deviceLabel}` : a.agentNodeId
|
|
57269
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57270
|
-
className: "text-[10px] text-foreground-subtle truncate",
|
|
57271
|
-
children: a.engineLabel
|
|
57272
|
-
})]
|
|
57273
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", { children: rows.map(({ node, depth }) => {
|
|
57274
|
-
const state = getCellState(node.addonId, a);
|
|
57275
|
-
const isSelected = selectedCell !== null && selectedCell.addonId === node.addonId && agentColumnKey(selectedCell) === agentColumnKey(a);
|
|
57276
|
-
const toggleProps = showToggle && state !== null ? {
|
|
57277
|
-
enabled: state.kind === "enabled",
|
|
57278
|
-
onChange: (next) => onToggleEnabled?.(node.addonId, next),
|
|
57279
|
-
disabled: state.kind === "na"
|
|
57280
|
-
} : void 0;
|
|
57281
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
57282
|
-
className: "border-b border-border last:border-b-0",
|
|
57283
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57284
|
-
className: "flex items-center gap-2",
|
|
57285
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
57286
|
-
type: "button",
|
|
57287
|
-
onClick: () => onCellClick(node.addonId, a),
|
|
57288
|
-
className: cellButtonClass(state, isSelected, "flex flex-1 min-w-0 items-start justify-between gap-3 px-3 py-2"),
|
|
57289
|
-
style: { paddingLeft: `${12 + depth * 14}px` },
|
|
57290
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepLabel, { node }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57291
|
-
className: "shrink-0 max-w-[45%] pt-0.5",
|
|
57292
|
-
children: renderCellContent(state)
|
|
57293
|
-
})]
|
|
57294
|
-
}), toggleProps && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57295
|
-
className: "pr-3 shrink-0",
|
|
57296
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuickToggle, {
|
|
57297
|
-
enabled: toggleProps.enabled,
|
|
57298
|
-
onChange: toggleProps.onChange,
|
|
57299
|
-
disabled: toggleProps.disabled
|
|
57300
|
-
})
|
|
57301
|
-
})]
|
|
57302
|
-
})
|
|
57303
|
-
}, node.addonId);
|
|
57304
|
-
}) })]
|
|
57305
|
-
}, agentColumnKey(a)))
|
|
57306
|
-
});
|
|
57307
|
-
}
|
|
57308
|
-
function PipelineTreeMatrix({ tree, agents, getCellState, onCellClick, selectedCell, onToggleEnabled }) {
|
|
57309
|
-
const rows = (0, react$1.useMemo)(() => flattenTree(tree), [tree]);
|
|
57310
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57311
|
-
className: "@container/matrix w-full",
|
|
57312
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57313
|
-
className: "@2xl/matrix:hidden",
|
|
57314
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StackedCards, {
|
|
57315
|
-
rows,
|
|
57316
|
-
agents,
|
|
57317
|
-
getCellState,
|
|
57318
|
-
onCellClick,
|
|
57319
|
-
selectedCell,
|
|
57320
|
-
onToggleEnabled
|
|
57321
|
-
})
|
|
57322
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57323
|
-
className: "hidden @2xl/matrix:block",
|
|
57324
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MatrixGrid, {
|
|
57325
|
-
rows,
|
|
57326
|
-
agents,
|
|
57327
|
-
getCellState,
|
|
57328
|
-
onCellClick,
|
|
57329
|
-
selectedCell,
|
|
57330
|
-
onToggleEnabled
|
|
57331
|
-
})
|
|
57332
|
-
})]
|
|
57333
|
-
});
|
|
57334
|
-
}
|
|
57335
|
-
//#endregion
|
|
57336
57098
|
//#region src/composites/provider-badge.tsx
|
|
57337
57099
|
var providerConfig = {
|
|
57338
57100
|
onvif: {
|
|
@@ -57776,89 +57538,6 @@ function validateSelector(selector) {
|
|
|
57776
57538
|
}
|
|
57777
57539
|
}
|
|
57778
57540
|
//#endregion
|
|
57779
|
-
//#region src/composites/slide-over-panel.tsx
|
|
57780
|
-
/**
|
|
57781
|
-
* SlideOverPanel — a detail surface anchored to context on desktop and to the
|
|
57782
|
-
* viewport on mobile.
|
|
57783
|
-
*
|
|
57784
|
-
* - **Desktop (`md+`):** an in-container, NON-modal slide-over pinned to the
|
|
57785
|
-
* right edge of the nearest positioned ancestor. The host reserves space with
|
|
57786
|
-
* a matching `md:pr-*` so the panel sits beside its content (e.g. the
|
|
57787
|
-
* multi-node pipeline matrix), not over it.
|
|
57788
|
-
* - **Mobile (`< md`):** a MODAL bottom sheet pinned to the viewport
|
|
57789
|
-
* (`position: fixed`). It never derives its size from the surrounding
|
|
57790
|
-
* container, so it can't stretch across a horizontally-laid-out node matrix
|
|
57791
|
-
* and overflow the page — the previous behaviour when it was a fixed-width
|
|
57792
|
-
* `absolute` panel anchored to the wide matrix wrapper.
|
|
57793
|
-
*
|
|
57794
|
-
* The whole desktop⇄mobile transform is pure Tailwind responsive classes on one
|
|
57795
|
-
* element — no layout-shift on hydration, no portal. JS is used only to lock
|
|
57796
|
-
* body scroll behind the mobile sheet and to wire Escape-to-close.
|
|
57797
|
-
*/
|
|
57798
|
-
function SlideOverPanel({ open, onClose, title, subtitle, footer, children, widthClass = "md:w-[360px]" }) {
|
|
57799
|
-
const isMobile = useIsMobile();
|
|
57800
|
-
(0, react$1.useEffect)(() => {
|
|
57801
|
-
if (!open) return;
|
|
57802
|
-
const onEsc = (e) => {
|
|
57803
|
-
if (e.key === "Escape") onClose();
|
|
57804
|
-
};
|
|
57805
|
-
window.addEventListener("keydown", onEsc);
|
|
57806
|
-
return () => window.removeEventListener("keydown", onEsc);
|
|
57807
|
-
}, [open, onClose]);
|
|
57808
|
-
(0, react$1.useEffect)(() => {
|
|
57809
|
-
if (!open || !isMobile) return;
|
|
57810
|
-
const previous = document.body.style.overflow;
|
|
57811
|
-
document.body.style.overflow = "hidden";
|
|
57812
|
-
return () => {
|
|
57813
|
-
document.body.style.overflow = previous;
|
|
57814
|
-
};
|
|
57815
|
-
}, [open, isMobile]);
|
|
57816
|
-
if (!open) return null;
|
|
57817
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57818
|
-
className: "fixed inset-0 z-40 bg-black/50 md:hidden",
|
|
57819
|
-
onClick: onClose,
|
|
57820
|
-
"aria-hidden": "true"
|
|
57821
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
|
|
57822
|
-
className: cn("fixed inset-x-0 bottom-0 z-50 flex flex-col", "max-h-[85dvh] rounded-t-2xl border-t border-border", "bg-surface shadow-2xl pb-[env(safe-area-inset-bottom)]", "md:absolute md:inset-x-auto md:top-0 md:right-0 md:bottom-0 md:z-20", "md:max-h-none md:rounded-none md:border-t-0 md:border-l md:pb-0", widthClass),
|
|
57823
|
-
role: "dialog",
|
|
57824
|
-
"aria-modal": isMobile ? "true" : "false",
|
|
57825
|
-
children: [
|
|
57826
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57827
|
-
className: "flex shrink-0 justify-center pt-2 pb-1 md:hidden",
|
|
57828
|
-
"aria-hidden": "true",
|
|
57829
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "h-1 w-9 rounded-full bg-foreground-subtle/30" })
|
|
57830
|
-
}),
|
|
57831
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
57832
|
-
className: "flex shrink-0 items-start justify-between gap-2 px-4 py-3 border-b border-border",
|
|
57833
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57834
|
-
className: "min-w-0",
|
|
57835
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57836
|
-
className: "text-sm font-semibold text-foreground truncate",
|
|
57837
|
-
children: title
|
|
57838
|
-
}), subtitle !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57839
|
-
className: "text-[11px] text-foreground-subtle mt-0.5 truncate",
|
|
57840
|
-
children: subtitle
|
|
57841
|
-
})]
|
|
57842
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
57843
|
-
type: "button",
|
|
57844
|
-
onClick: onClose,
|
|
57845
|
-
className: "p-1 rounded hover:bg-muted text-foreground-subtle hover:text-foreground",
|
|
57846
|
-
"aria-label": "Close panel",
|
|
57847
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(X, { className: "w-4 h-4" })
|
|
57848
|
-
})]
|
|
57849
|
-
}),
|
|
57850
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57851
|
-
className: "flex-1 min-h-0 overflow-y-auto overscroll-contain px-4 py-3",
|
|
57852
|
-
children
|
|
57853
|
-
}),
|
|
57854
|
-
footer !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("footer", {
|
|
57855
|
-
className: "flex shrink-0 items-center gap-2 justify-end px-4 py-3 border-t border-border",
|
|
57856
|
-
children: footer
|
|
57857
|
-
})
|
|
57858
|
-
]
|
|
57859
|
-
})] });
|
|
57860
|
-
}
|
|
57861
|
-
//#endregion
|
|
57862
57541
|
//#region src/composites/snapshot-button.tsx
|
|
57863
57542
|
/**
|
|
57864
57543
|
* SnapshotButton — operator-facing manual snapshot trigger.
|
|
@@ -58044,51 +57723,501 @@ function StepTimings({ timings, totalMs, className }) {
|
|
|
58044
57723
|
});
|
|
58045
57724
|
}
|
|
58046
57725
|
//#endregion
|
|
58047
|
-
//#region src/composites/step-
|
|
58048
|
-
|
|
58049
|
-
|
|
58050
|
-
|
|
58051
|
-
|
|
58052
|
-
|
|
58053
|
-
|
|
58054
|
-
|
|
58055
|
-
|
|
58056
|
-
|
|
58057
|
-
|
|
57726
|
+
//#region src/composites/step-inspector/accelerator-tabs.tsx
|
|
57727
|
+
function AcceleratorTabs({ items, selectedId, onSelect }) {
|
|
57728
|
+
const onKeyDown = (e, index) => {
|
|
57729
|
+
const delta = e.key === "ArrowRight" ? 1 : e.key === "ArrowLeft" ? -1 : 0;
|
|
57730
|
+
if (delta === 0) return;
|
|
57731
|
+
e.preventDefault();
|
|
57732
|
+
const next = items[(index + delta + items.length) % items.length];
|
|
57733
|
+
if (next !== void 0) onSelect(next.id);
|
|
57734
|
+
};
|
|
57735
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57736
|
+
role: "tablist",
|
|
57737
|
+
"aria-label": "Accelerator",
|
|
57738
|
+
className: "-mx-1 flex min-w-0 flex-nowrap gap-1 overflow-x-auto px-1 pb-0.5",
|
|
57739
|
+
children: items.map((item, index) => {
|
|
57740
|
+
const selected = item.id === selectedId;
|
|
57741
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
57742
|
+
type: "button",
|
|
57743
|
+
role: "tab",
|
|
57744
|
+
"aria-selected": selected,
|
|
57745
|
+
"aria-label": item.marked ? `${item.label} (camera override)` : item.label,
|
|
57746
|
+
tabIndex: selected ? 0 : -1,
|
|
57747
|
+
onClick: () => onSelect(item.id),
|
|
57748
|
+
onKeyDown: (e) => onKeyDown(e, index),
|
|
57749
|
+
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"),
|
|
57750
|
+
children: [item.label, item.marked && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57751
|
+
className: "h-1.5 w-1.5 rounded-full bg-amber-500",
|
|
57752
|
+
"aria-hidden": true
|
|
57753
|
+
})]
|
|
57754
|
+
}, item.id);
|
|
57755
|
+
})
|
|
57756
|
+
});
|
|
57757
|
+
}
|
|
57758
|
+
//#endregion
|
|
57759
|
+
//#region src/composites/step-inspector/gate-control.tsx
|
|
57760
|
+
/**
|
|
57761
|
+
* GateControl — whether ONE camera runs a step: Auto (the catalog default) /
|
|
57762
|
+
* On / Off. A per-camera, node-independent switch, written at once (it is not
|
|
57763
|
+
* part of the inspector's draft). `package-detection` is driven by package
|
|
57764
|
+
* zones and shows a badge instead.
|
|
57765
|
+
*
|
|
57766
|
+
* Segments are 36 px tall on a phone and 32 px from `md` up — the matrix this
|
|
57767
|
+
* replaced drew 26 px ones.
|
|
57768
|
+
*/
|
|
57769
|
+
function activeChoice(gate) {
|
|
57770
|
+
if (!gate.explicit) return "auto";
|
|
57771
|
+
return gate.effectiveOn ? "on" : "off";
|
|
57772
|
+
}
|
|
57773
|
+
function GateControl({ gate, busy = false, onSet }) {
|
|
57774
|
+
if (gate.kind === "zone-driven") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57775
|
+
className: "inline-flex items-center rounded-md border border-violet-500/40 bg-violet-500/10 px-2 py-1 text-[11px] font-medium text-violet-400",
|
|
57776
|
+
title: "Runs where this camera has package zones — set on the Zones tab",
|
|
57777
|
+
children: "Zones"
|
|
57778
|
+
});
|
|
57779
|
+
const active = activeChoice(gate);
|
|
57780
|
+
const segment = (choice, label, value, on) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
58058
57781
|
type: "button",
|
|
58059
|
-
|
|
58060
|
-
|
|
58061
|
-
|
|
58062
|
-
|
|
58063
|
-
|
|
58064
|
-
|
|
58065
|
-
|
|
58066
|
-
|
|
58067
|
-
|
|
58068
|
-
|
|
58069
|
-
|
|
58070
|
-
|
|
57782
|
+
role: "radio",
|
|
57783
|
+
"aria-checked": active === choice,
|
|
57784
|
+
disabled: busy,
|
|
57785
|
+
onClick: () => onSet(value),
|
|
57786
|
+
className: cn("h-9 min-w-12 px-3 text-xs font-medium transition-colors md:h-8", active === choice ? on : "text-foreground-subtle hover:bg-muted/60", busy && "cursor-not-allowed opacity-50"),
|
|
57787
|
+
children: label
|
|
57788
|
+
});
|
|
57789
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57790
|
+
className: "flex items-center gap-2",
|
|
57791
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57792
|
+
role: "radiogroup",
|
|
57793
|
+
"aria-label": "Runs on this camera",
|
|
57794
|
+
title: `Catalog default: ${gate.defaultOn ? "on" : "off"}`,
|
|
57795
|
+
className: "inline-flex overflow-hidden rounded-md border border-border",
|
|
57796
|
+
children: [
|
|
57797
|
+
segment("auto", "Auto", null, "bg-primary/15 text-primary"),
|
|
57798
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57799
|
+
className: "w-px self-stretch bg-border",
|
|
57800
|
+
"aria-hidden": true
|
|
57801
|
+
}),
|
|
57802
|
+
segment("on", "On", true, "bg-emerald-500 text-white"),
|
|
57803
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57804
|
+
className: "w-px self-stretch bg-border",
|
|
57805
|
+
"aria-hidden": true
|
|
57806
|
+
}),
|
|
57807
|
+
segment("off", "Off", false, "bg-foreground-subtle/70 text-white")
|
|
57808
|
+
]
|
|
57809
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
57810
|
+
className: "hidden text-[10px] text-foreground-subtle @3xl/inspector:inline",
|
|
57811
|
+
children: ["default: ", gate.defaultOn ? "on" : "off"]
|
|
58071
57812
|
})]
|
|
58072
|
-
})
|
|
58073
|
-
|
|
58074
|
-
|
|
58075
|
-
|
|
58076
|
-
|
|
58077
|
-
|
|
58078
|
-
|
|
57813
|
+
});
|
|
57814
|
+
}
|
|
57815
|
+
/** The gate as a short list-row badge. */
|
|
57816
|
+
function GateBadge({ gate }) {
|
|
57817
|
+
if (gate.kind === "zone-driven") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57818
|
+
className: "shrink-0 text-[9px] font-semibold uppercase text-violet-400",
|
|
57819
|
+
children: "zones"
|
|
57820
|
+
});
|
|
57821
|
+
const on = gate.effectiveOn;
|
|
57822
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57823
|
+
className: cn("shrink-0 rounded px-1.5 py-px text-[9px] font-semibold uppercase", on ? "bg-emerald-500/15 text-emerald-500" : "bg-muted text-foreground-subtle", gate.explicit && "ring-1 ring-inset ring-current"),
|
|
57824
|
+
title: gate.explicit ? "Set for this camera" : "Catalog default",
|
|
57825
|
+
children: on ? "on" : "off"
|
|
57826
|
+
});
|
|
58079
57827
|
}
|
|
58080
|
-
|
|
57828
|
+
//#endregion
|
|
57829
|
+
//#region src/composites/step-inspector/segmented-choice.tsx
|
|
57830
|
+
/**
|
|
57831
|
+
* SegmentedChoice — a small, labelled single-choice group (a radiogroup of
|
|
57832
|
+
* buttons): which layer the inspector's settings edit ("All accelerators" /
|
|
57833
|
+
* "Only hub · NPU"). 36 px tall on a phone, 28 px from `md` up.
|
|
57834
|
+
*/
|
|
57835
|
+
function SegmentedChoice({ label, options, value, onChange }) {
|
|
57836
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57837
|
+
role: "radiogroup",
|
|
57838
|
+
"aria-label": label,
|
|
57839
|
+
className: "flex flex-wrap items-center gap-1",
|
|
57840
|
+
children: options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
57841
|
+
type: "button",
|
|
57842
|
+
role: "radio",
|
|
57843
|
+
"aria-checked": value === option.value,
|
|
57844
|
+
onClick: () => onChange(option.value),
|
|
57845
|
+
className: cn("h-9 rounded-md border px-2.5 text-[11px] font-medium transition-colors md:h-7", value === option.value ? "border-primary/50 bg-primary/15 text-primary" : "border-border text-foreground-subtle hover:bg-surface-hover hover:text-foreground"),
|
|
57846
|
+
children: option.label
|
|
57847
|
+
}, option.value))
|
|
57848
|
+
});
|
|
57849
|
+
}
|
|
57850
|
+
//#endregion
|
|
57851
|
+
//#region src/composites/step-inspector/step-detail-dock.tsx
|
|
57852
|
+
/**
|
|
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.
|
|
57856
|
+
*
|
|
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.
|
|
57865
|
+
*
|
|
57866
|
+
* Phone: a full-screen `Dialog` sheet.
|
|
57867
|
+
*/
|
|
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
|
+
};
|
|
57880
|
+
}
|
|
57881
|
+
function scrollParentOf(el) {
|
|
57882
|
+
for (let p = el?.parentElement ?? null; p !== null; p = p.parentElement) {
|
|
57883
|
+
const { overflowY } = getComputedStyle(p);
|
|
57884
|
+
if (overflowY === "auto" || overflowY === "scroll") return p;
|
|
57885
|
+
}
|
|
57886
|
+
return null;
|
|
57887
|
+
}
|
|
57888
|
+
function useDockBox(anchor, enabled) {
|
|
57889
|
+
const [box, setBox] = (0, react$1.useState)(null);
|
|
57890
|
+
(0, react$1.useLayoutEffect)(() => {
|
|
57891
|
+
if (!enabled) return;
|
|
57892
|
+
const scroller = scrollParentOf(anchor.current);
|
|
57893
|
+
const measure = () => {
|
|
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
|
|
57902
|
+
}));
|
|
57903
|
+
};
|
|
57904
|
+
measure();
|
|
57905
|
+
const RO = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measure);
|
|
57906
|
+
if (scroller) RO?.observe(scroller);
|
|
57907
|
+
window.addEventListener("resize", measure);
|
|
57908
|
+
return () => {
|
|
57909
|
+
RO?.disconnect();
|
|
57910
|
+
window.removeEventListener("resize", measure);
|
|
57911
|
+
};
|
|
57912
|
+
}, [anchor, enabled]);
|
|
57913
|
+
return box;
|
|
57914
|
+
}
|
|
57915
|
+
function StepDetailDock({ open, anchor, onClose, label, children }) {
|
|
57916
|
+
const isMobile = useIsMobile();
|
|
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
|
|
57927
|
+
})
|
|
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
|
+
});
|
|
57947
|
+
}
|
|
57948
|
+
/** "‹ Steps" — the phone sheet's way back to the matrix. */
|
|
57949
|
+
function BackToStepsButton({ onClick }) {
|
|
57950
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
57951
|
+
type: "button",
|
|
57952
|
+
onClick,
|
|
57953
|
+
className: "inline-flex h-9 items-center gap-0.5 rounded-md border border-border px-2 text-xs text-foreground hover:bg-surface-hover",
|
|
57954
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronLeft, {
|
|
57955
|
+
className: "h-3.5 w-3.5",
|
|
57956
|
+
"aria-hidden": true
|
|
57957
|
+
}), "Steps"]
|
|
57958
|
+
});
|
|
57959
|
+
}
|
|
57960
|
+
//#endregion
|
|
57961
|
+
//#region src/composites/step-inspector/step-inspector-panel.tsx
|
|
57962
|
+
/**
|
|
57963
|
+
* StepInspectorPanel — the detail pane of the step inspector: a pinned header
|
|
57964
|
+
* (the step, its gate, the accelerator tabs), ONE scrolling body, and a pinned
|
|
57965
|
+
* save bar. Nothing in it depends on the page's scroll position — the slide-over
|
|
57966
|
+
* it replaced was anchored to the matrix block and scrolled away with it.
|
|
57967
|
+
*/
|
|
57968
|
+
function SaveBar({ dirtyCount, saving, status, error, onSave, onDiscard }) {
|
|
57969
|
+
const dirty = dirtyCount > 0;
|
|
57970
|
+
const message = saving ? "Saving…" : dirty ? `${dirtyCount} unsaved change${dirtyCount === 1 ? "" : "s"}` : status === "saved" ? "Saved" : status === "error" ? error ?? "Save failed" : "No unsaved changes";
|
|
57971
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
|
|
57972
|
+
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",
|
|
57973
|
+
children: [
|
|
57974
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57975
|
+
role: "status",
|
|
57976
|
+
className: cn("min-w-0 flex-1 truncate text-[11px]", status === "error" && !dirty ? "text-danger" : dirty ? "text-amber-500" : "text-foreground-subtle"),
|
|
57977
|
+
children: message
|
|
57978
|
+
}),
|
|
57979
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
57980
|
+
variant: "outline",
|
|
57981
|
+
size: "md",
|
|
57982
|
+
disabled: !dirty || saving,
|
|
57983
|
+
onClick: onDiscard,
|
|
57984
|
+
children: "Discard"
|
|
57985
|
+
}),
|
|
57986
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
57987
|
+
variant: "primary",
|
|
57988
|
+
size: "md",
|
|
57989
|
+
disabled: !dirty || saving,
|
|
57990
|
+
onClick: onSave,
|
|
57991
|
+
children: "Save"
|
|
57992
|
+
})
|
|
57993
|
+
]
|
|
57994
|
+
});
|
|
57995
|
+
}
|
|
57996
|
+
var StepInspectorPanel = (0, react$1.forwardRef)(function StepInspectorPanel({ title, subtitle, headerActions, headerExtra, leading, saveBar, onEscape, children, className }, headingRef) {
|
|
57997
|
+
const headingId = (0, react$1.useId)();
|
|
57998
|
+
const onKeyDown = (e) => {
|
|
57999
|
+
if (e.key !== "Escape" || onEscape === void 0) return;
|
|
58000
|
+
e.stopPropagation();
|
|
58001
|
+
onEscape();
|
|
58002
|
+
};
|
|
58003
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
58004
|
+
"aria-labelledby": headingId,
|
|
58005
|
+
onKeyDown,
|
|
58006
|
+
className: cn("flex min-h-0 flex-1 flex-col overflow-hidden bg-surface", className),
|
|
58007
|
+
children: [
|
|
58008
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
58009
|
+
className: "shrink-0 space-y-2 border-b border-border px-4 py-2.5",
|
|
58010
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58011
|
+
className: "flex flex-wrap items-start gap-x-3 gap-y-2",
|
|
58012
|
+
children: [
|
|
58013
|
+
leading,
|
|
58014
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58015
|
+
className: "min-w-[9rem] flex-1",
|
|
58016
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
58017
|
+
id: headingId,
|
|
58018
|
+
ref: headingRef,
|
|
58019
|
+
tabIndex: -1,
|
|
58020
|
+
className: "truncate text-sm font-semibold text-foreground outline-none focus-visible:ring-2 focus-visible:ring-primary rounded",
|
|
58021
|
+
children: title
|
|
58022
|
+
}), subtitle !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
58023
|
+
className: "mt-0.5 text-[11px] text-foreground-subtle",
|
|
58024
|
+
children: subtitle
|
|
58025
|
+
})]
|
|
58026
|
+
}),
|
|
58027
|
+
headerActions
|
|
58028
|
+
]
|
|
58029
|
+
}), headerExtra]
|
|
58030
|
+
}),
|
|
58031
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
58032
|
+
"data-inspector-body": true,
|
|
58033
|
+
className: "min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 py-3",
|
|
58034
|
+
children
|
|
58035
|
+
}),
|
|
58036
|
+
saveBar !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SaveBar, { ...saveBar })
|
|
58037
|
+
]
|
|
58038
|
+
});
|
|
58039
|
+
});
|
|
58040
|
+
//#endregion
|
|
58041
|
+
//#region src/composites/step-inspector/step-model-row.tsx
|
|
58042
|
+
var SOURCE_TONE = {
|
|
58043
|
+
camera: "border-primary/40 bg-primary/10 text-primary",
|
|
58044
|
+
node: "border-border bg-muted/40 text-foreground",
|
|
58045
|
+
default: "border-border/60 text-foreground-subtle",
|
|
58046
|
+
cluster: "border-primary/40 bg-primary/5 text-primary"
|
|
58047
|
+
};
|
|
58048
|
+
function StepModelRow({ label, format, modelId, source, sourceLabel, models, defaultModelId, expanded, onToggle, onChangeModel, onReset, resetLabel = "Reset", note, extra, changed = false, disabled = false }) {
|
|
58049
|
+
const pickable = models.length > 0 && note === void 0;
|
|
58050
|
+
const canExpand = pickable || extra !== void 0;
|
|
58051
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58052
|
+
className: cn("rounded-md border border-border", expanded && "bg-muted/10"),
|
|
58053
|
+
children: [
|
|
58054
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58055
|
+
className: cn(LIST_ROW, "space-y-1"),
|
|
58056
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58057
|
+
className: "flex flex-wrap items-center gap-x-2 gap-y-1 text-xs",
|
|
58058
|
+
children: [
|
|
58059
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58060
|
+
className: "font-medium text-foreground",
|
|
58061
|
+
children: label
|
|
58062
|
+
}),
|
|
58063
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58064
|
+
className: "text-[10px] text-foreground-subtle",
|
|
58065
|
+
children: format
|
|
58066
|
+
}),
|
|
58067
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58068
|
+
className: cn("ml-auto rounded border px-1.5 py-px text-[10px] font-medium", SOURCE_TONE[source]),
|
|
58069
|
+
children: sourceLabel
|
|
58070
|
+
})
|
|
58071
|
+
]
|
|
58072
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58073
|
+
className: "flex items-center gap-2",
|
|
58074
|
+
children: [
|
|
58075
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
58076
|
+
className: "min-w-0 flex-1 truncate font-mono text-xs text-foreground",
|
|
58077
|
+
children: [modelId ?? "—", changed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58078
|
+
className: "ml-1 text-amber-500",
|
|
58079
|
+
children: "•"
|
|
58080
|
+
})]
|
|
58081
|
+
}),
|
|
58082
|
+
onReset !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
58083
|
+
type: "button",
|
|
58084
|
+
className: "inline-flex items-center justify-center gap-1 rounded border border-border bg-surface px-1.5 py-0.5 sm:px-2 sm:py-1 text-[10px] sm:text-[11px] font-medium text-foreground hover:bg-surface-hover transition-colors disabled:opacity-40",
|
|
58085
|
+
disabled,
|
|
58086
|
+
onClick: onReset,
|
|
58087
|
+
children: resetLabel
|
|
58088
|
+
}),
|
|
58089
|
+
canExpand && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
58090
|
+
type: "button",
|
|
58091
|
+
className: cn("inline-flex items-center justify-center gap-1 rounded border border-border bg-surface px-1.5 py-0.5 sm:px-2 sm:py-1 text-[10px] sm:text-[11px] font-medium text-foreground hover:bg-surface-hover transition-colors disabled:opacity-40", "min-h-9 md:min-h-0"),
|
|
58092
|
+
"aria-expanded": expanded,
|
|
58093
|
+
"aria-label": `Change model for ${label}`,
|
|
58094
|
+
disabled,
|
|
58095
|
+
onClick: onToggle,
|
|
58096
|
+
children: expanded ? "Done" : pickable ? "Change" : "Route"
|
|
58097
|
+
})
|
|
58098
|
+
]
|
|
58099
|
+
})]
|
|
58100
|
+
}),
|
|
58101
|
+
note !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
58102
|
+
className: "border-t border-border/60 px-3 py-1.5 text-[10px] leading-snug text-foreground-subtle",
|
|
58103
|
+
children: note
|
|
58104
|
+
}),
|
|
58105
|
+
expanded && canExpand && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58106
|
+
className: "space-y-3 border-t border-border/60 px-3 py-3",
|
|
58107
|
+
children: [pickable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelPicker, {
|
|
58108
|
+
models,
|
|
58109
|
+
value: modelId ?? "",
|
|
58110
|
+
defaultModelId,
|
|
58111
|
+
disabled,
|
|
58112
|
+
onChange: onChangeModel
|
|
58113
|
+
}), extra]
|
|
58114
|
+
})
|
|
58115
|
+
]
|
|
58116
|
+
});
|
|
58117
|
+
}
|
|
58118
|
+
//#endregion
|
|
58119
|
+
//#region src/composites/step-inspector/step-model-list.tsx
|
|
58120
|
+
/**
|
|
58121
|
+
* StepModelList — the model section of the step inspector.
|
|
58122
|
+
*
|
|
58123
|
+
* While nothing is pinned, edited or worth a note, the accelerators are
|
|
58124
|
+
* summarised by model — one line per distinct model, naming the accelerators
|
|
58125
|
+
* that run it ("yolov9m-320-int8 · hub iGPU, hub NPU, little-unraid iGPU") —
|
|
58126
|
+
* with "Per accelerator" to open one `StepModelRow` each. Four near-identical
|
|
58127
|
+
* rows pushed the settings, the thing most edits are about, below the fold.
|
|
58128
|
+
* A pin, a pending change or a note (no build here, root-supplied) shows every
|
|
58129
|
+
* row, so nothing that differs from the default is ever folded away.
|
|
58130
|
+
*/
|
|
58131
|
+
/** True when the rows can be summarised: nothing pinned here, edited, or noted. */
|
|
58132
|
+
function modelRowsAreFoldable(rows) {
|
|
58133
|
+
return rows.length >= 2 && rows.every((r) => r.note === void 0 && r.onReset === void 0 && r.changed !== true && !r.expanded);
|
|
58134
|
+
}
|
|
58135
|
+
function groupByModel(rows) {
|
|
58136
|
+
const groups = [];
|
|
58137
|
+
for (const row of rows) {
|
|
58138
|
+
const modelId = row.modelId ?? "—";
|
|
58139
|
+
const index = groups.findIndex((g) => g.modelId === modelId && g.sourceLabel === row.sourceLabel);
|
|
58140
|
+
const existing = groups[index];
|
|
58141
|
+
if (existing !== void 0) groups[index] = {
|
|
58142
|
+
...existing,
|
|
58143
|
+
labels: [...existing.labels, row.label]
|
|
58144
|
+
};
|
|
58145
|
+
else groups.push({
|
|
58146
|
+
modelId,
|
|
58147
|
+
labels: [row.label],
|
|
58148
|
+
sourceLabel: row.sourceLabel
|
|
58149
|
+
});
|
|
58150
|
+
}
|
|
58151
|
+
return groups;
|
|
58152
|
+
}
|
|
58153
|
+
function StepModelList({ rows }) {
|
|
58154
|
+
const [open, setOpen] = (0, react$1.useState)(false);
|
|
58155
|
+
if (!open && modelRowsAreFoldable(rows)) {
|
|
58156
|
+
const groups = groupByModel(rows);
|
|
58157
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58158
|
+
className: cn("rounded-md border border-border", LIST_ROW),
|
|
58159
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
58160
|
+
className: "space-y-1",
|
|
58161
|
+
children: groups.map((g) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
58162
|
+
className: "flex flex-wrap items-baseline gap-x-2 text-xs",
|
|
58163
|
+
children: [
|
|
58164
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58165
|
+
className: "font-mono text-foreground",
|
|
58166
|
+
children: g.modelId
|
|
58167
|
+
}),
|
|
58168
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58169
|
+
className: "min-w-0 flex-1 text-[11px] text-foreground-subtle",
|
|
58170
|
+
children: g.labels.length === rows.length ? `all ${rows.length} accelerators` : g.labels.join(", ")
|
|
58171
|
+
}),
|
|
58172
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58173
|
+
className: "rounded border border-border px-1.5 py-px text-[10px] font-medium text-foreground-subtle",
|
|
58174
|
+
children: g.sourceLabel
|
|
58175
|
+
})
|
|
58176
|
+
]
|
|
58177
|
+
}, `${g.modelId}|${g.sourceLabel}`))
|
|
58178
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
58179
|
+
className: "mt-1.5 flex justify-end",
|
|
58180
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
58181
|
+
type: "button",
|
|
58182
|
+
className: cn(BTN_COMPACT, "min-h-9 md:min-h-0"),
|
|
58183
|
+
"aria-expanded": false,
|
|
58184
|
+
onClick: () => setOpen(true),
|
|
58185
|
+
children: "Per accelerator"
|
|
58186
|
+
})
|
|
58187
|
+
})]
|
|
58188
|
+
});
|
|
58189
|
+
}
|
|
58081
58190
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
58082
|
-
className:
|
|
58083
|
-
children:
|
|
58084
|
-
node,
|
|
58085
|
-
depth: 0,
|
|
58086
|
-
selectedAddonId,
|
|
58087
|
-
onSelect,
|
|
58088
|
-
agentDots
|
|
58089
|
-
}, node.addonId))
|
|
58191
|
+
className: "space-y-2",
|
|
58192
|
+
children: rows.map(({ id, ...row }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepModelRow, { ...row }, id))
|
|
58090
58193
|
});
|
|
58091
58194
|
}
|
|
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
|
|
58220
|
+
//#region src/composites/step-tree.ts
|
|
58092
58221
|
/**
|
|
58093
58222
|
* Helper: build a StepTreeNode[] from a `PipelineSchema` by chaining on
|
|
58094
58223
|
* `inputClasses` ⇔ `outputClasses` compatibility. Mirrors the orchestrator
|
|
@@ -61033,6 +61162,7 @@ var SceneMonitorEditor = (0, react$1.lazy)(() => Promise.resolve().then(() => re
|
|
|
61033
61162
|
/** Lazy-wrapped `PrivacyMaskSettings` — code-split off the main bundle. */
|
|
61034
61163
|
var PrivacyMaskSettings = (0, react$1.lazy)(() => Promise.resolve().then(() => require("./PrivacyMaskSettings-BqjXsXZR.cjs")).then((m) => ({ default: m.PrivacyMaskSettings })));
|
|
61035
61164
|
//#endregion
|
|
61165
|
+
exports.AcceleratorTabs = AcceleratorTabs;
|
|
61036
61166
|
exports.AddonGlobalSettingsForm = AddonGlobalSettingsForm;
|
|
61037
61167
|
exports.AdminLinkQuickAction = AdminLinkQuickAction;
|
|
61038
61168
|
exports.AgentStepEditor = AgentStepEditor;
|
|
@@ -61050,6 +61180,7 @@ exports.BTN_COMPACT = BTN_COMPACT;
|
|
|
61050
61180
|
exports.BTN_COMPACT_DANGER = BTN_COMPACT_DANGER;
|
|
61051
61181
|
exports.BTN_COMPACT_PRIMARY = BTN_COMPACT_PRIMARY;
|
|
61052
61182
|
exports.BTN_COMPACT_WARNING = BTN_COMPACT_WARNING;
|
|
61183
|
+
exports.BackToStepsButton = BackToStepsButton;
|
|
61053
61184
|
exports.Badge = Badge;
|
|
61054
61185
|
exports.BatteryBadge = BatteryBadge;
|
|
61055
61186
|
exports.BottomSheet = BottomSheet;
|
|
@@ -61171,6 +61302,8 @@ exports.FormField = FormField$1;
|
|
|
61171
61302
|
exports.GRID_GAP = GRID_GAP;
|
|
61172
61303
|
exports.GRID_PAIRED = GRID_PAIRED;
|
|
61173
61304
|
exports.GRID_QUICK_STATS = GRID_QUICK_STATS;
|
|
61305
|
+
exports.GateBadge = GateBadge;
|
|
61306
|
+
exports.GateControl = GateControl;
|
|
61174
61307
|
exports.Grid2x2 = Grid2x2;
|
|
61175
61308
|
exports.GripTrack = GripTrack;
|
|
61176
61309
|
exports.GroupedModelSelector = GroupedModelSelector;
|
|
@@ -61198,6 +61331,7 @@ exports.LIST_ROW = LIST_ROW;
|
|
|
61198
61331
|
exports.Label = Label;
|
|
61199
61332
|
exports.LawnMowerHeroCard = LawnMowerHeroCard;
|
|
61200
61333
|
exports.LawnMowerInlineControl = LawnMowerInlineControl;
|
|
61334
|
+
exports.LayeredSettingsForm = LayeredSettingsForm;
|
|
61201
61335
|
exports.LightHeroCard = LightHeroCard;
|
|
61202
61336
|
exports.LightInlineControl = LightInlineControl;
|
|
61203
61337
|
exports.LockHeroCard = LockHeroCard;
|
|
@@ -61245,7 +61379,6 @@ exports.PhaseIcon = PhaseIcon;
|
|
|
61245
61379
|
exports.PipelineBuilder = PipelineBuilder;
|
|
61246
61380
|
exports.PipelineRuntimeSelector = PipelineRuntimeSelector;
|
|
61247
61381
|
exports.PipelineStep = PipelineStep;
|
|
61248
|
-
exports.PipelineTreeMatrix = PipelineTreeMatrix;
|
|
61249
61382
|
exports.PlaceholderChipField = PlaceholderChipField;
|
|
61250
61383
|
exports.PlayerOverlaysProvider = PlayerOverlaysProvider;
|
|
61251
61384
|
exports.Popover = Popover;
|
|
@@ -61268,6 +61401,8 @@ exports.RecordingPanel = RecordingPanel;
|
|
|
61268
61401
|
exports.ResponseLog = ResponseLog;
|
|
61269
61402
|
exports.RoleDisplayDefaultsFromServer = RoleDisplayDefaultsFromServer;
|
|
61270
61403
|
exports.RoleDisplayDefaultsProvider = RoleDisplayDefaultsProvider;
|
|
61404
|
+
exports.RootModelSourceControl = RootModelSourceControl;
|
|
61405
|
+
exports.RootSuppliedModelNote = RootSuppliedModelNote;
|
|
61271
61406
|
exports.RotateCcw = RotateCcw;
|
|
61272
61407
|
exports.RunReport = RunReport;
|
|
61273
61408
|
exports.SCENE_CAPTURE_VARIANTS = SCENE_CAPTURE_VARIANTS;
|
|
@@ -61285,12 +61420,14 @@ exports.SPLIT_PANEL_OUTER = SPLIT_PANEL_OUTER;
|
|
|
61285
61420
|
exports.SPLIT_PANEL_SIDE = SPLIT_PANEL_SIDE;
|
|
61286
61421
|
exports.STACK_GAP = STACK_GAP;
|
|
61287
61422
|
exports.STATE_COLOR = STATE_COLOR;
|
|
61423
|
+
exports.STEP_DOCK_WIDTH_PX = STEP_DOCK_WIDTH_PX;
|
|
61288
61424
|
exports.SceneMonitorEditor = SceneMonitorEditor;
|
|
61289
61425
|
exports.ScopePicker = ScopePicker;
|
|
61290
61426
|
exports.ScriptHeroCard = ScriptHeroCard;
|
|
61291
61427
|
exports.ScriptInlineControl = ScriptInlineControl;
|
|
61292
61428
|
exports.ScrollArea = ScrollArea;
|
|
61293
61429
|
exports.SectionCard = SectionCard;
|
|
61430
|
+
exports.SegmentedChoice = SegmentedChoice;
|
|
61294
61431
|
exports.Select = Select;
|
|
61295
61432
|
exports.SelectedChip = SelectedChip;
|
|
61296
61433
|
exports.SemanticBadge = SemanticBadge;
|
|
@@ -61302,7 +61439,6 @@ exports.SettingRow = SettingRow;
|
|
|
61302
61439
|
exports.Sidebar = Sidebar;
|
|
61303
61440
|
exports.SidebarItem = SidebarItem;
|
|
61304
61441
|
exports.Skeleton = Skeleton;
|
|
61305
|
-
exports.SlideOverPanel = SlideOverPanel;
|
|
61306
61442
|
exports.SlideToggle = SlideToggle;
|
|
61307
61443
|
exports.SnapshotButton = SnapshotButton;
|
|
61308
61444
|
exports.SortHeaderButton = SortHeaderButton;
|
|
@@ -61310,8 +61446,11 @@ exports.Square = Square;
|
|
|
61310
61446
|
exports.StatCard = StatCard;
|
|
61311
61447
|
exports.StateValuesStream = StateValuesStream;
|
|
61312
61448
|
exports.StatusBadge = StatusBadge;
|
|
61449
|
+
exports.StepDetailDock = StepDetailDock;
|
|
61450
|
+
exports.StepInspectorPanel = StepInspectorPanel;
|
|
61451
|
+
exports.StepModelList = StepModelList;
|
|
61452
|
+
exports.StepModelRow = StepModelRow;
|
|
61313
61453
|
exports.StepTimings = StepTimings;
|
|
61314
|
-
exports.StepTreeMaster = StepTreeMaster;
|
|
61315
61454
|
exports.Stepper = Stepper;
|
|
61316
61455
|
exports.StreamBrokerSelector = StreamBrokerSelector;
|
|
61317
61456
|
exports.StreamDebugOverlay = StreamDebugOverlay;
|
|
@@ -61358,6 +61497,8 @@ exports.WidgetPanel = WidgetPanel;
|
|
|
61358
61497
|
exports.WidgetRegistryProvider = WidgetRegistryProvider;
|
|
61359
61498
|
exports.WidgetSlot = WidgetSlot;
|
|
61360
61499
|
exports.ZoneEditingProvider = ZoneEditingProvider;
|
|
61500
|
+
exports.acceleratorLabel = acceleratorLabel;
|
|
61501
|
+
exports.acceleratorShortLabel = acceleratorShortLabel;
|
|
61361
61502
|
exports.agentColumnKey = agentColumnKey;
|
|
61362
61503
|
exports.allDeviceTypeFilterOptions = allDeviceTypeFilterOptions;
|
|
61363
61504
|
exports.applyFleetQueryDefaults = applyFleetQueryDefaults;
|
|
@@ -61365,6 +61506,7 @@ exports.applyPickerFilters = applyPickerFilters;
|
|
|
61365
61506
|
exports.ariaSortForColumn = ariaSortForColumn;
|
|
61366
61507
|
exports.badgeFor = badgeFor;
|
|
61367
61508
|
exports.badgeFor$1 = badgeFor;
|
|
61509
|
+
exports.bindsToModel = bindsToModel;
|
|
61368
61510
|
exports.buildPeerConnectionConfig = buildPeerConnectionConfig;
|
|
61369
61511
|
exports.buildStepTreeFromSchema = buildStepTreeFromSchema;
|
|
61370
61512
|
exports.catalogHasDraft = catalogHasDraft;
|
|
@@ -61389,6 +61531,7 @@ exports.createTheme = require_theme_index.createTheme$1;
|
|
|
61389
61531
|
exports.cursorFractionFor = cursorFractionFor;
|
|
61390
61532
|
exports.darkColors = require_theme_index.darkColors$1;
|
|
61391
61533
|
exports.dataTableFacets = dataTableFacets;
|
|
61534
|
+
exports.defaultOfField = defaultOfField;
|
|
61392
61535
|
exports.defaultTheme = require_theme_index.defaultTheme$1;
|
|
61393
61536
|
exports.deriveDeviceKind = deriveDeviceKind;
|
|
61394
61537
|
exports.deriveWebrtcDebugStats = deriveWebrtcDebugStats;
|
|
@@ -61403,6 +61546,7 @@ exports.deviceTypeLabel = deviceTypeLabel;
|
|
|
61403
61546
|
exports.deviceTypeMeta = deviceTypeMeta;
|
|
61404
61547
|
exports.deviceTypeMetaOf = deviceTypeMetaOf;
|
|
61405
61548
|
exports.devicesToOptions = devicesToOptions;
|
|
61549
|
+
exports.dockBox = dockBox;
|
|
61406
61550
|
exports.draftFromQuery = draftFromQuery;
|
|
61407
61551
|
exports.emptyDevicesWidens = emptyDevicesWidens;
|
|
61408
61552
|
exports.emptySessionRun = emptySessionRun;
|
|
@@ -61420,6 +61564,7 @@ exports.fleetViewOf = fleetViewOf;
|
|
|
61420
61564
|
exports.formatControlDateTime = formatControlDateTime;
|
|
61421
61565
|
exports.formatLastSeen = formatLastSeen;
|
|
61422
61566
|
exports.formatNumeric = formatNumeric;
|
|
61567
|
+
exports.formatSettingValue = formatSettingValue;
|
|
61423
61568
|
exports.freezeVerdict = freezeVerdict;
|
|
61424
61569
|
exports.fuzzyMatch = fuzzyMatch;
|
|
61425
61570
|
exports.getClassColor = getClassColor;
|
|
@@ -61443,6 +61588,7 @@ exports.isAbsentProvider$1 = isAbsentProvider;
|
|
|
61443
61588
|
exports.isDataTableFilterActive = isDataTableFilterActive;
|
|
61444
61589
|
exports.isFieldVisible = isFieldVisible;
|
|
61445
61590
|
exports.isGroupExpanded = isGroupExpanded;
|
|
61591
|
+
exports.isValueField = isValueField;
|
|
61446
61592
|
exports.lawnMowerActivityMeta = lawnMowerActivityMeta;
|
|
61447
61593
|
exports.lightColors = require_theme_index.lightColors$1;
|
|
61448
61594
|
exports.loadRemoteBundle = loadRemoteBundle;
|
|
@@ -61450,8 +61596,10 @@ exports.makeScrubBridge = makeScrubBridge;
|
|
|
61450
61596
|
exports.metadataEntries = metadataEntries;
|
|
61451
61597
|
exports.metadataString = metadataString;
|
|
61452
61598
|
exports.mirror = mirror;
|
|
61599
|
+
exports.modelRowsAreFoldable = modelRowsAreFoldable;
|
|
61453
61600
|
exports.mountAddonPage = mountAddonPage;
|
|
61454
61601
|
exports.mountPath = mountPath;
|
|
61602
|
+
exports.nextGridPosition = nextGridPosition;
|
|
61455
61603
|
exports.nextReconnectAction = nextReconnectAction;
|
|
61456
61604
|
exports.nextSort = nextSort;
|
|
61457
61605
|
exports.nextSortDirection = nextSortDirection;
|
|
@@ -61507,9 +61655,11 @@ exports.rootSourceState = rootSourceState;
|
|
|
61507
61655
|
exports.routerBasename = routerBasename;
|
|
61508
61656
|
exports.sameLocationSuggestions = sameLocationSuggestions;
|
|
61509
61657
|
exports.scrubReducer = scrubReducer;
|
|
61658
|
+
exports.searchSteps = searchSteps;
|
|
61510
61659
|
exports.selectPickerGroup = selectPickerGroup;
|
|
61511
61660
|
exports.selectedDeviceOptions = selectedDeviceOptions;
|
|
61512
61661
|
exports.serializeRecordedCommand = serializeRecordedCommand;
|
|
61662
|
+
exports.settingsModelIdFor = settingsModelIdFor;
|
|
61513
61663
|
exports.shouldCommit = shouldCommit;
|
|
61514
61664
|
exports.shouldEmit = shouldEmit;
|
|
61515
61665
|
exports.shouldShowAddNewFooter = shouldShowAddNewFooter;
|
|
@@ -61521,7 +61671,9 @@ exports.sortRowsByColumn = sortRowsByColumn;
|
|
|
61521
61671
|
exports.statusIcons = statusIcons;
|
|
61522
61672
|
exports.statusLine = statusLine;
|
|
61523
61673
|
exports.statusLine$1 = statusLine;
|
|
61674
|
+
exports.stepFieldDomId = stepFieldDomId;
|
|
61524
61675
|
exports.stepHasModelForFormat = stepHasModelForFormat;
|
|
61676
|
+
exports.stepLocalFields = stepLocalFields;
|
|
61525
61677
|
exports.stepModelOptions = stepModelOptions;
|
|
61526
61678
|
exports.stripParentNamePrefix = stripParentNamePrefix;
|
|
61527
61679
|
exports.summarizeHubCandidates = summarizeHubCandidates;
|
|
@@ -62267,7 +62419,6 @@ exports.usePipelineExecutorCacheFrameInPool = usePipelineExecutorCacheFrameInPoo
|
|
|
62267
62419
|
exports.usePipelineExecutorClearDeviceOverrides = usePipelineExecutorClearDeviceOverrides;
|
|
62268
62420
|
exports.usePipelineExecutorClearModelBoundDeviceOverrides = usePipelineExecutorClearModelBoundDeviceOverrides;
|
|
62269
62421
|
exports.usePipelineExecutorDeleteModel = usePipelineExecutorDeleteModel;
|
|
62270
|
-
exports.usePipelineExecutorDeleteTemplate = usePipelineExecutorDeleteTemplate;
|
|
62271
62422
|
exports.usePipelineExecutorDownloadModel = usePipelineExecutorDownloadModel;
|
|
62272
62423
|
exports.usePipelineExecutorGetAddonModels = usePipelineExecutorGetAddonModels;
|
|
62273
62424
|
exports.usePipelineExecutorGetAudioCapabilities = usePipelineExecutorGetAudioCapabilities;
|
|
@@ -62291,22 +62442,18 @@ exports.usePipelineExecutorInferCached = usePipelineExecutorInferCached;
|
|
|
62291
62442
|
exports.usePipelineExecutorKillEngine = usePipelineExecutorKillEngine;
|
|
62292
62443
|
exports.usePipelineExecutorListLoadedEngines = usePipelineExecutorListLoadedEngines;
|
|
62293
62444
|
exports.usePipelineExecutorListReferenceImages = usePipelineExecutorListReferenceImages;
|
|
62294
|
-
exports.usePipelineExecutorListTemplates = usePipelineExecutorListTemplates;
|
|
62295
62445
|
exports.usePipelineExecutorRearmInferenceDevice = usePipelineExecutorRearmInferenceDevice;
|
|
62296
62446
|
exports.usePipelineExecutorRunAudioTest = usePipelineExecutorRunAudioTest;
|
|
62297
62447
|
exports.usePipelineExecutorRunPipeline = usePipelineExecutorRunPipeline;
|
|
62298
62448
|
exports.usePipelineExecutorRunPipelineBatch = usePipelineExecutorRunPipelineBatch;
|
|
62299
|
-
exports.usePipelineExecutorSaveTemplate = usePipelineExecutorSaveTemplate;
|
|
62300
62449
|
exports.usePipelineExecutorSetVideoPipelineSteps = usePipelineExecutorSetVideoPipelineSteps;
|
|
62301
62450
|
exports.usePipelineExecutorSpinEngine = usePipelineExecutorSpinEngine;
|
|
62302
62451
|
exports.usePipelineExecutorUncacheFrame = usePipelineExecutorUncacheFrame;
|
|
62303
|
-
exports.usePipelineExecutorUpdateTemplate = usePipelineExecutorUpdateTemplate;
|
|
62304
62452
|
exports.usePipelineExecutorValidatePipeline = usePipelineExecutorValidatePipeline;
|
|
62305
62453
|
exports.usePipelineOrchestratorApplyAutoTune = usePipelineOrchestratorApplyAutoTune;
|
|
62306
62454
|
exports.usePipelineOrchestratorApplyDeviceSettingsPatch = usePipelineOrchestratorApplyDeviceSettingsPatch;
|
|
62307
62455
|
exports.usePipelineOrchestratorAssignAudio = usePipelineOrchestratorAssignAudio;
|
|
62308
62456
|
exports.usePipelineOrchestratorAssignPipeline = usePipelineOrchestratorAssignPipeline;
|
|
62309
|
-
exports.usePipelineOrchestratorDeleteTemplate = usePipelineOrchestratorDeleteTemplate;
|
|
62310
62457
|
exports.usePipelineOrchestratorGetAgentLoad = usePipelineOrchestratorGetAgentLoad;
|
|
62311
62458
|
exports.usePipelineOrchestratorGetAgentSettings = usePipelineOrchestratorGetAgentSettings;
|
|
62312
62459
|
exports.usePipelineOrchestratorGetAudioAssignment = usePipelineOrchestratorGetAudioAssignment;
|
|
@@ -62332,7 +62479,6 @@ exports.usePipelineOrchestratorGetPipelineDevicePin = usePipelineOrchestratorGet
|
|
|
62332
62479
|
exports.usePipelineOrchestratorGetPlacementStats = usePipelineOrchestratorGetPlacementStats;
|
|
62333
62480
|
exports.usePipelineOrchestratorGetReconcileFromDiskStatus = usePipelineOrchestratorGetReconcileFromDiskStatus;
|
|
62334
62481
|
exports.usePipelineOrchestratorListAgentSettings = usePipelineOrchestratorListAgentSettings;
|
|
62335
|
-
exports.usePipelineOrchestratorListTemplates = usePipelineOrchestratorListTemplates;
|
|
62336
62482
|
exports.usePipelineOrchestratorPauseForStorageMigration = usePipelineOrchestratorPauseForStorageMigration;
|
|
62337
62483
|
exports.usePipelineOrchestratorPlanAutoTune = usePipelineOrchestratorPlanAutoTune;
|
|
62338
62484
|
exports.usePipelineOrchestratorRebalance = usePipelineOrchestratorRebalance;
|
|
@@ -62342,7 +62488,6 @@ exports.usePipelineOrchestratorRequestOccupancyLook = usePipelineOrchestratorReq
|
|
|
62342
62488
|
exports.usePipelineOrchestratorResetNodePipelineDefaults = usePipelineOrchestratorResetNodePipelineDefaults;
|
|
62343
62489
|
exports.usePipelineOrchestratorResolvePipeline = usePipelineOrchestratorResolvePipeline;
|
|
62344
62490
|
exports.usePipelineOrchestratorResumeForStorageMigration = usePipelineOrchestratorResumeForStorageMigration;
|
|
62345
|
-
exports.usePipelineOrchestratorSaveTemplate = usePipelineOrchestratorSaveTemplate;
|
|
62346
62491
|
exports.usePipelineOrchestratorSetAgentCapabilities = usePipelineOrchestratorSetAgentCapabilities;
|
|
62347
62492
|
exports.usePipelineOrchestratorSetAgentDecodeLimits = usePipelineOrchestratorSetAgentDecodeLimits;
|
|
62348
62493
|
exports.usePipelineOrchestratorSetAgentInferenceDevices = usePipelineOrchestratorSetAgentInferenceDevices;
|
|
@@ -62361,7 +62506,6 @@ exports.usePipelineOrchestratorSetShareSource = usePipelineOrchestratorSetShareS
|
|
|
62361
62506
|
exports.usePipelineOrchestratorSuggestShares = usePipelineOrchestratorSuggestShares;
|
|
62362
62507
|
exports.usePipelineOrchestratorUnassignAudio = usePipelineOrchestratorUnassignAudio;
|
|
62363
62508
|
exports.usePipelineOrchestratorUnassignPipeline = usePipelineOrchestratorUnassignPipeline;
|
|
62364
|
-
exports.usePipelineOrchestratorUpdateTemplate = usePipelineOrchestratorUpdateTemplate;
|
|
62365
62509
|
exports.usePipelineRunnerAttachCamera = usePipelineRunnerAttachCamera;
|
|
62366
62510
|
exports.usePipelineRunnerDetachCamera = usePipelineRunnerDetachCamera;
|
|
62367
62511
|
exports.usePipelineRunnerGetAllCameraMetrics = usePipelineRunnerGetAllCameraMetrics;
|
|
@@ -62737,4 +62881,5 @@ exports.waterHeaterTint = waterHeaterTint;
|
|
|
62737
62881
|
exports.weatherConditionMeta = weatherConditionMeta;
|
|
62738
62882
|
exports.weatherTint = weatherTint;
|
|
62739
62883
|
exports.withResolvedParentDetail = withResolvedParentDetail;
|
|
62884
|
+
exports.withUseRootModel = withUseRootModel;
|
|
62740
62885
|
exports.writeStoredGroupBy = writeStoredGroupBy;
|