@camstack/ui-library 1.2.244 → 1.2.245
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/agent-step-editor.d.ts +41 -16
- package/dist/composites/device-step-matrix.d.ts +2 -25
- package/dist/composites/index.d.ts +8 -11
- package/dist/composites/model-bound-slider.d.ts +8 -1
- package/dist/composites/pipeline-builder.d.ts +1 -28
- package/dist/composites/pipeline-matrix-shared.d.ts +1 -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 +11 -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-chips.d.ts +11 -0
- package/dist/composites/step-inspector/step-inspector-layout.d.ts +23 -0
- package/dist/composites/step-inspector/step-inspector-panel.d.ts +26 -0
- package/dist/composites/step-inspector/step-list.d.ts +39 -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-tree-master.d.ts → step-tree.d.ts} +0 -12
- package/dist/index.cjs +1336 -1016
- package/dist/index.js +1314 -1016
- 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/index.cjs
CHANGED
|
@@ -28746,32 +28746,6 @@ function AddonGlobalSettingsForm({ trpc, addonId, nodeId, title, disabled, onAft
|
|
|
28746
28746
|
});
|
|
28747
28747
|
}
|
|
28748
28748
|
//#endregion
|
|
28749
|
-
//#region src/primitives/collapsible-card.tsx
|
|
28750
|
-
function CollapsibleCard({ expanded, onExpandedChange, header, children, dimmed = false, hideChevron = false, className }) {
|
|
28751
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
28752
|
-
className: cn("rounded-lg border border-border bg-surface overflow-hidden", dimmed && "opacity-[0.45]", className),
|
|
28753
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
28754
|
-
role: "button",
|
|
28755
|
-
tabIndex: 0,
|
|
28756
|
-
className: "flex items-center gap-2.5 px-3 py-2.5 cursor-pointer select-none",
|
|
28757
|
-
onClick: () => onExpandedChange(!expanded),
|
|
28758
|
-
onKeyDown: (e) => {
|
|
28759
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
28760
|
-
e.preventDefault();
|
|
28761
|
-
onExpandedChange(!expanded);
|
|
28762
|
-
}
|
|
28763
|
-
},
|
|
28764
|
-
children: [!hideChevron && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
28765
|
-
className: "text-foreground-subtle shrink-0",
|
|
28766
|
-
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" })
|
|
28767
|
-
}), header]
|
|
28768
|
-
}), expanded && children && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
28769
|
-
className: "border-t border-border px-3 py-3",
|
|
28770
|
-
children
|
|
28771
|
-
})]
|
|
28772
|
-
});
|
|
28773
|
-
}
|
|
28774
|
-
//#endregion
|
|
28775
28749
|
//#region src/composites/grouped-model-selector.tsx
|
|
28776
28750
|
/**
|
|
28777
28751
|
* GroupedModelSelector — the shared Family → Tier → Variant model picker.
|
|
@@ -29052,6 +29026,257 @@ function ModelPicker({ models, value, onChange, disabled, defaultModelId }) {
|
|
|
29052
29026
|
});
|
|
29053
29027
|
}
|
|
29054
29028
|
//#endregion
|
|
29029
|
+
//#region src/composites/resolve-step-default-model.ts
|
|
29030
|
+
function resolveStepDefaultModel(catalog, format) {
|
|
29031
|
+
const hasFormatBuild = (modelId) => catalog.models.find((m) => m.id === modelId)?.formats[format] !== void 0;
|
|
29032
|
+
const declared = catalog.defaultModelIdByFormat?.[format];
|
|
29033
|
+
if (declared !== void 0 && hasFormatBuild(declared)) return declared;
|
|
29034
|
+
if (hasFormatBuild(catalog.defaultModelId)) return catalog.defaultModelId;
|
|
29035
|
+
const available = catalog.models.filter((m) => m.formats[format] !== void 0 && m.legacy !== true);
|
|
29036
|
+
if (available.length === 0) return catalog.defaultModelId;
|
|
29037
|
+
return [...available].toSorted((a, b) => (a.formats[format]?.sizeMB ?? Infinity) - (b.formats[format]?.sizeMB ?? Infinity))[0].id;
|
|
29038
|
+
}
|
|
29039
|
+
/**
|
|
29040
|
+
* The model a matrix CELL shows as this step's effective default on `device`.
|
|
29041
|
+
*
|
|
29042
|
+
* The device's ELECTED model (`device.defaultModelId`, the per-device
|
|
29043
|
+
* object-detection election) applies ONLY when this step's catalog actually
|
|
29044
|
+
* contains it — `slot === 'detector'` alone is too wide, because
|
|
29045
|
+
* package-detection shares the detector slot with the root step. Bit live
|
|
29046
|
+
* 2026-08-08: rfdetr-package became the package step's default and every
|
|
29047
|
+
* matrix cell still showed the device's yolo as the "selected" model, because
|
|
29048
|
+
* the election was applied slot-wide. Membership is the principled gate: an
|
|
29049
|
+
* elected model this step cannot run is never displayed for it.
|
|
29050
|
+
*/
|
|
29051
|
+
function resolveEffectiveDefaultModel(catalog, device) {
|
|
29052
|
+
const format = device?.format;
|
|
29053
|
+
if (format === void 0) return void 0;
|
|
29054
|
+
const elected = device?.defaultModelId;
|
|
29055
|
+
return catalog.slot === "detector" && elected !== void 0 && catalog.models.some((m) => m.id === elected) ? elected : resolveStepDefaultModel(catalog, format);
|
|
29056
|
+
}
|
|
29057
|
+
/**
|
|
29058
|
+
* The models a step may run on an engine `format` — the ONE derivation of the
|
|
29059
|
+
* option list. An unknown format yields NOTHING rather than everything: a
|
|
29060
|
+
* picker that offered a model with no build for the device would write a pin
|
|
29061
|
+
* the runner cannot honour.
|
|
29062
|
+
*/
|
|
29063
|
+
function stepModelOptions(models, format) {
|
|
29064
|
+
if (format === void 0) return [];
|
|
29065
|
+
return models.filter((m) => m.formats[format] !== void 0);
|
|
29066
|
+
}
|
|
29067
|
+
/** Can this step run on `format` at all? (`false` ⇒ the caller renders "⚠ skip"/"—".) */
|
|
29068
|
+
function stepHasModelForFormat(models, format) {
|
|
29069
|
+
return stepModelOptions(models, format).length > 0;
|
|
29070
|
+
}
|
|
29071
|
+
/**
|
|
29072
|
+
* The model a step ACTUALLY runs for a (device, camera) — the whole effective
|
|
29073
|
+
* chain in one place: per-camera override → per-(node,device) base → the
|
|
29074
|
+
* resolved default (`resolveEffectiveDefaultModel`).
|
|
29075
|
+
*
|
|
29076
|
+
* Every surface calls this: the matrix cell, and the drawer's picker seed. They
|
|
29077
|
+
* are the same number, and the only way to keep them the same number is to have
|
|
29078
|
+
* one function produce it. A surface that owns fewer layers omits them (the node
|
|
29079
|
+
* matrix passes no `override`) — it does NOT get its own resolver.
|
|
29080
|
+
*/
|
|
29081
|
+
function resolveEffectiveStepModel(input) {
|
|
29082
|
+
return input.override ?? input.base ?? resolveEffectiveDefaultModel(input.catalog, input.device);
|
|
29083
|
+
}
|
|
29084
|
+
//#endregion
|
|
29085
|
+
//#region src/composites/root-model-source.tsx
|
|
29086
|
+
/**
|
|
29087
|
+
* "Use the root model" — the step editor's control for a step the camera's
|
|
29088
|
+
* ROOT detection model can supply (D730).
|
|
29089
|
+
*
|
|
29090
|
+
* The flag (`useRootModel`, in the step's own settings) is an operator INTENT
|
|
29091
|
+
* kept beside the step's model: checking it never changes the model, and a
|
|
29092
|
+
* root that cannot supply the step leaves the step running its own model while
|
|
29093
|
+
* the flag waits, inactive, for a root that can. Whether it is in force is
|
|
29094
|
+
* `resolveRootSupply` — the same function the runner decides with.
|
|
29095
|
+
*/
|
|
29096
|
+
/** The editor's view of the flag for one step, or `null` for a step no root can supply. */
|
|
29097
|
+
function rootSourceState(stepId, settings, rootModel) {
|
|
29098
|
+
if (!(0, _camstack_types.isRootSuppliableStepId)(stepId)) return null;
|
|
29099
|
+
return (0, _camstack_types.resolveRootSupply)(stepId, settings, rootModel?.supplies);
|
|
29100
|
+
}
|
|
29101
|
+
/** The settings bag with the flag set — every other key untouched. */
|
|
29102
|
+
function withUseRootModel(settings, value) {
|
|
29103
|
+
return {
|
|
29104
|
+
...settings,
|
|
29105
|
+
[_camstack_types.USE_ROOT_MODEL_SETTING_KEY]: value
|
|
29106
|
+
};
|
|
29107
|
+
}
|
|
29108
|
+
/** What the operator must know about a supply, beyond "it is on". */
|
|
29109
|
+
function supplyNotes(supply) {
|
|
29110
|
+
if (supply.kind === "label") {
|
|
29111
|
+
const notes = supply.uncovered.length > 0 ? [`Not produced by this model: ${supply.uncovered.join(", ")}.`] : [];
|
|
29112
|
+
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."];
|
|
29113
|
+
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."];
|
|
29114
|
+
}
|
|
29115
|
+
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."];
|
|
29116
|
+
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."];
|
|
29117
|
+
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."];
|
|
29118
|
+
}
|
|
29119
|
+
/** The reason an intended supply is not in force, in the operator's words. */
|
|
29120
|
+
function inactiveText(state, rootModel) {
|
|
29121
|
+
switch (state.inactiveReason) {
|
|
29122
|
+
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).`;
|
|
29123
|
+
case "root-does-not-declare":
|
|
29124
|
+
case void 0: return `the current root model (${rootModel.name}) cannot supply this.`;
|
|
29125
|
+
}
|
|
29126
|
+
}
|
|
29127
|
+
function RootModelSourceControl({ stepId, settings, rootModel, disabled, onChange }) {
|
|
29128
|
+
if (rootModel === void 0) return null;
|
|
29129
|
+
const state = rootSourceState(stepId, settings, rootModel);
|
|
29130
|
+
if (state === null) return null;
|
|
29131
|
+
const inactiveNote = state.inactive ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29132
|
+
className: "rounded bg-muted/40 px-3 py-2 text-[11px] text-foreground-subtle",
|
|
29133
|
+
"data-testid": "root-model-inactive",
|
|
29134
|
+
children: [
|
|
29135
|
+
"Use the root model: inactive — ",
|
|
29136
|
+
inactiveText(state, rootModel),
|
|
29137
|
+
" The step runs its own model."
|
|
29138
|
+
]
|
|
29139
|
+
}) : null;
|
|
29140
|
+
if (state.supply === void 0) return inactiveNote;
|
|
29141
|
+
const checked = state.intent;
|
|
29142
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29143
|
+
className: "space-y-1",
|
|
29144
|
+
"data-testid": "root-model-source",
|
|
29145
|
+
children: [
|
|
29146
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
29147
|
+
className: "flex items-center gap-2 text-xs text-foreground",
|
|
29148
|
+
children: [
|
|
29149
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
29150
|
+
type: "checkbox",
|
|
29151
|
+
checked,
|
|
29152
|
+
disabled,
|
|
29153
|
+
onChange: (e) => onChange(e.target.checked)
|
|
29154
|
+
}),
|
|
29155
|
+
"Use the root model (",
|
|
29156
|
+
rootModel.name,
|
|
29157
|
+
")"
|
|
29158
|
+
]
|
|
29159
|
+
}),
|
|
29160
|
+
inactiveNote,
|
|
29161
|
+
state.effective && supplyNotes(state.supply).map((note) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
29162
|
+
className: "text-[10px] leading-snug text-foreground-subtle",
|
|
29163
|
+
children: note
|
|
29164
|
+
}, note))
|
|
29165
|
+
]
|
|
29166
|
+
});
|
|
29167
|
+
}
|
|
29168
|
+
//#endregion
|
|
29169
|
+
//#region src/lib/responsive.ts
|
|
29170
|
+
/**
|
|
29171
|
+
* Responsive design tokens — three-tier system.
|
|
29172
|
+
*
|
|
29173
|
+
* The admin-ui targets three width tiers; every component should pick
|
|
29174
|
+
* the right token from this file instead of hand-coding breakpoints.
|
|
29175
|
+
* Adopting the tokens uniformly is what makes the app feel consistent
|
|
29176
|
+
* across operator devices (mobile / tablet / desktop) and what unlocks
|
|
29177
|
+
* targeted tuning (e.g. shrink every label by 1px in one place).
|
|
29178
|
+
*
|
|
29179
|
+
* Tier definitions (Tailwind breakpoints):
|
|
29180
|
+
*
|
|
29181
|
+
* • **L1 — Compact** (`< 640px`, default = mobile portrait)
|
|
29182
|
+
* Single column, tight padding, smallest text, icon-only buttons
|
|
29183
|
+
* where labels can be inferred. The operator can still see every
|
|
29184
|
+
* stat / action — no horizontal scroll, no clipped text. The user
|
|
29185
|
+
* directive: "molta informazione in poco spazio quando la width è
|
|
29186
|
+
* bassa." [a lot of information in little space when the width is low.]
|
|
29187
|
+
*
|
|
29188
|
+
* • **L2 — Standard** (`sm` — `640–1023px`, tablet + small laptop)
|
|
29189
|
+
* 2-column grids, side panels become visible, button labels return.
|
|
29190
|
+
* Targets ~768px–1024px viewports — the most common operator sizes.
|
|
29191
|
+
*
|
|
29192
|
+
* • **L3 — Spacious** (`lg+` — `≥ 1024px`, desktop)
|
|
29193
|
+
* 4-column grids, generous padding, full-width header text,
|
|
29194
|
+
* side-by-side composite layouts. The operator has elbow room.
|
|
29195
|
+
*
|
|
29196
|
+
* Conventions:
|
|
29197
|
+
* - **Always start from L1** — the bare class is always the compact
|
|
29198
|
+
* variant, then `sm:` adds room at 640px, `lg:` at 1024px. No
|
|
29199
|
+
* desktop-first breakpoints; no max-width queries.
|
|
29200
|
+
* - **Token strings are static** — kept as `as const` so callers can
|
|
29201
|
+
* compose them via `cn()` / template literals without running a
|
|
29202
|
+
* class generator at runtime.
|
|
29203
|
+
* - **Pair the token with a comment** — the comment in the file
|
|
29204
|
+
* names every breakpoint it crosses, so a reader can predict the
|
|
29205
|
+
* layout without going to Tailwind docs.
|
|
29206
|
+
*/
|
|
29207
|
+
/** Container padding for a card-style section (header + body). */
|
|
29208
|
+
var SECTION_CARD = "rounded-lg border border-border bg-surface overflow-hidden";
|
|
29209
|
+
/** Header strip inside a card (between border-b lines). */
|
|
29210
|
+
var SECTION_HEADER = "border-b border-border px-3 sm:px-4 py-1.5 sm:py-2";
|
|
29211
|
+
/** Body padding for a card-style section. */
|
|
29212
|
+
var SECTION_BODY = "p-2 sm:p-3 lg:p-4";
|
|
29213
|
+
/** Gap between cards in a vertical stack. */
|
|
29214
|
+
var STACK_GAP = "space-y-3 sm:space-y-4";
|
|
29215
|
+
/** Gap between cards in a grid (4 cells / row at lg). */
|
|
29216
|
+
var GRID_GAP = "gap-2 sm:gap-3";
|
|
29217
|
+
/** Row padding inside a divided list (e.g. InfoRow). */
|
|
29218
|
+
var LIST_ROW = "px-3 sm:px-4 py-1.5 sm:py-2";
|
|
29219
|
+
/** 1-up at L1, 2-up at L2, 4-up at L3 — quick-stat row. */
|
|
29220
|
+
var GRID_QUICK_STATS = "grid grid-cols-2 sm:grid-cols-2 lg:grid-cols-4";
|
|
29221
|
+
/** 1-up at L1, 2-up at L2+ — paired panels (motion + detection rules). */
|
|
29222
|
+
var GRID_PAIRED = "grid grid-cols-1 lg:grid-cols-2";
|
|
29223
|
+
/** Side-by-side layout that stacks below md (canvas + side panel). */
|
|
29224
|
+
var SPLIT_PANEL_OUTER = "flex flex-col-reverse md:flex-row";
|
|
29225
|
+
/** Sidebar narrow lane in a SPLIT_PANEL_OUTER layout. */
|
|
29226
|
+
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";
|
|
29227
|
+
/**
|
|
29228
|
+
* The tier boundary at which a settings label/value row stops stacking.
|
|
29229
|
+
*
|
|
29230
|
+
* Below it (L1 — compact) the row is ONE column: label on its own line, value
|
|
29231
|
+
* on the next at full width. Above it the row is label-left / value-right as
|
|
29232
|
+
* before. The two-column form needs `SETTING_ROW_LABEL`'s lane plus a readable
|
|
29233
|
+
* value beside it; a phone has neither, and the failure mode is not "cramped"
|
|
29234
|
+
* but "the value is the part that gets cut" — the one thing the operator
|
|
29235
|
+
* opened the page to read.
|
|
29236
|
+
*/
|
|
29237
|
+
var SETTING_ROW_STACK_BREAKPOINT = "sm";
|
|
29238
|
+
/** Outer row: single column at L1, label/value columns from `sm` up. */
|
|
29239
|
+
var SETTING_ROW = "flex min-w-0 flex-col gap-0.5 py-1.5 sm:flex-row sm:items-center";
|
|
29240
|
+
/** Label lane. Full width (and free to wrap) at L1; a fixed lane from `sm` up. */
|
|
29241
|
+
var SETTING_ROW_LABEL = "min-w-0 text-[11px] leading-tight text-foreground-subtle sm:w-32 sm:shrink-0 sm:pr-2";
|
|
29242
|
+
/**
|
|
29243
|
+
* Value lane — the value text plus its trailing affordances (copy / reveal).
|
|
29244
|
+
* `w-full` at L1 so the value owns the whole row once the label is above it;
|
|
29245
|
+
* `sm:flex-1` makes it share the row again from `sm` up.
|
|
29246
|
+
*/
|
|
29247
|
+
var SETTING_ROW_VALUE = "flex w-full min-w-0 items-center gap-1 sm:flex-1";
|
|
29248
|
+
/**
|
|
29249
|
+
* The value text itself. `break-all` lets an opaque token (a key, a URL) wrap
|
|
29250
|
+
* at L1 instead of being cut mid-word; `sm:truncate` restores the single-line
|
|
29251
|
+
* desktop form, where the row is wide enough for truncation to be a choice
|
|
29252
|
+
* rather than data loss.
|
|
29253
|
+
*/
|
|
29254
|
+
var SETTING_ROW_VALUE_TEXT = "min-w-0 break-all text-xs text-foreground sm:truncate";
|
|
29255
|
+
/** Section header label (uppercase tracking-wider). */
|
|
29256
|
+
var TEXT_SECTION_LABEL = "text-[10px] sm:text-[11px] font-semibold text-foreground uppercase tracking-wider";
|
|
29257
|
+
/** Field label inside a row. */
|
|
29258
|
+
var TEXT_FIELD_LABEL = "text-[11px] sm:text-xs text-foreground";
|
|
29259
|
+
/** Sub-label / hint text. */
|
|
29260
|
+
var TEXT_HINT = "text-[10px] text-foreground-subtle";
|
|
29261
|
+
/** Primary value (a metric, badge, …). */
|
|
29262
|
+
var TEXT_VALUE = "text-[11px] sm:text-xs font-medium text-foreground";
|
|
29263
|
+
/** Big metric value (StatCard). */
|
|
29264
|
+
var TEXT_METRIC = "text-base sm:text-lg font-semibold leading-tight text-foreground";
|
|
29265
|
+
/** Default compact button. */
|
|
29266
|
+
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";
|
|
29267
|
+
/** Primary compact button. */
|
|
29268
|
+
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";
|
|
29269
|
+
/** Danger compact button. */
|
|
29270
|
+
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";
|
|
29271
|
+
/** Warning compact button (e.g. cancel-drawing). */
|
|
29272
|
+
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";
|
|
29273
|
+
/** Compact text/select/number input. */
|
|
29274
|
+
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";
|
|
29275
|
+
/** Chip-style toggle (zone / class selectors in ZoneRulesEditor). */
|
|
29276
|
+
var CHIP_BASE = "inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-[10px] font-medium transition-colors";
|
|
29277
|
+
var CHIP_ACTIVE = "border-primary/50 bg-primary/15 text-primary";
|
|
29278
|
+
var CHIP_INACTIVE = "border-border bg-surface text-foreground-subtle hover:bg-surface-hover";
|
|
29279
|
+
//#endregion
|
|
29055
29280
|
//#region src/composites/model-bound-slider.tsx
|
|
29056
29281
|
var SOURCE_LABEL = {
|
|
29057
29282
|
override: "Override for this model",
|
|
@@ -29061,7 +29286,7 @@ var SOURCE_LABEL = {
|
|
|
29061
29286
|
function formatNumber(value, scale) {
|
|
29062
29287
|
return (scale !== void 0 ? value / scale : value).toFixed(2).replace(/\.?0+$/, "");
|
|
29063
29288
|
}
|
|
29064
|
-
function ModelBoundSlider({ field, modelId, settings, disabled, onChangeSettings }) {
|
|
29289
|
+
function ModelBoundSlider({ field, modelId, settings, disabled, onChangeSettings, hideSourceRow = false }) {
|
|
29065
29290
|
const modelDefault = field.modelApplicability?.defaultByModel?.[modelId];
|
|
29066
29291
|
const state = (0, _camstack_types.resolveModelBoundSetting)(settings, field.key, modelId.length > 0 ? modelId : void 0, modelDefault, field.default);
|
|
29067
29292
|
const fallback = typeof field.default === "number" ? field.default : field.min ?? 0;
|
|
@@ -29101,7 +29326,7 @@ function ModelBoundSlider({ field, modelId, settings, disabled, onChangeSettings
|
|
|
29101
29326
|
onChange: (e) => handleChange(Number(e.target.value)),
|
|
29102
29327
|
className: "w-full accent-primary h-1.5"
|
|
29103
29328
|
}),
|
|
29104
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29329
|
+
!hideSourceRow && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29105
29330
|
className: "mt-1 flex items-center justify-between gap-2 text-[10px]",
|
|
29106
29331
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29107
29332
|
"data-testid": "model-bound-source",
|
|
@@ -29152,6 +29377,32 @@ function ResetModelDefaultsButton({ fields, modelId, settings, disabled, onReset
|
|
|
29152
29377
|
});
|
|
29153
29378
|
}
|
|
29154
29379
|
//#endregion
|
|
29380
|
+
//#region src/primitives/collapsible-card.tsx
|
|
29381
|
+
function CollapsibleCard({ expanded, onExpandedChange, header, children, dimmed = false, hideChevron = false, className }) {
|
|
29382
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29383
|
+
className: cn("rounded-lg border border-border bg-surface overflow-hidden", dimmed && "opacity-[0.45]", className),
|
|
29384
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29385
|
+
role: "button",
|
|
29386
|
+
tabIndex: 0,
|
|
29387
|
+
className: "flex items-center gap-2.5 px-3 py-2.5 cursor-pointer select-none",
|
|
29388
|
+
onClick: () => onExpandedChange(!expanded),
|
|
29389
|
+
onKeyDown: (e) => {
|
|
29390
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
29391
|
+
e.preventDefault();
|
|
29392
|
+
onExpandedChange(!expanded);
|
|
29393
|
+
}
|
|
29394
|
+
},
|
|
29395
|
+
children: [!hideChevron && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29396
|
+
className: "text-foreground-subtle shrink-0",
|
|
29397
|
+
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" })
|
|
29398
|
+
}), header]
|
|
29399
|
+
}), expanded && children && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
29400
|
+
className: "border-t border-border px-3 py-3",
|
|
29401
|
+
children
|
|
29402
|
+
})]
|
|
29403
|
+
});
|
|
29404
|
+
}
|
|
29405
|
+
//#endregion
|
|
29155
29406
|
//#region src/composites/pipeline-step.tsx
|
|
29156
29407
|
/**
|
|
29157
29408
|
* PipelineStep — single step card in the pipeline builder.
|
|
@@ -29163,124 +29414,77 @@ function ResetModelDefaultsButton({ fields, modelId, settings, disabled, onReset
|
|
|
29163
29414
|
function modelsForStep(schema) {
|
|
29164
29415
|
return schema?.models ?? [];
|
|
29165
29416
|
}
|
|
29166
|
-
function PipelineStep({ step, schema, allSchemas: _allSchemas, depth: _depth = 0, onChange, onDelete: _onDelete, readOnly = false
|
|
29417
|
+
function PipelineStep({ step, schema, allSchemas: _allSchemas, depth: _depth = 0, onChange, onDelete: _onDelete, readOnly = false }) {
|
|
29167
29418
|
const [expanded, setExpanded] = (0, react$1.useState)(false);
|
|
29168
29419
|
const models = modelsForStep(schema);
|
|
29169
|
-
|
|
29170
|
-
if (models.length > 0 && !isAutoSentinel && !models.some((m) => m.id === step.modelId) && !readOnly) {
|
|
29420
|
+
if (models.length > 0 && !models.some((m) => m.id === step.modelId) && !readOnly) {
|
|
29171
29421
|
const fallback = (schema?.defaultModelId ? models.find((m) => m.id === schema.defaultModelId) : null) ?? models[0];
|
|
29172
29422
|
if (fallback && fallback.id !== step.modelId) queueMicrotask(() => onChange({
|
|
29173
29423
|
...step,
|
|
29174
29424
|
modelId: fallback.id
|
|
29175
29425
|
}));
|
|
29176
29426
|
}
|
|
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
|
-
] });
|
|
29427
|
+
const header = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29428
|
+
className: "flex-1 min-w-0",
|
|
29429
|
+
children: [
|
|
29430
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29431
|
+
className: "text-[10px] uppercase tracking-wider font-medium text-foreground-subtle/60 block leading-none",
|
|
29432
|
+
children: step.slot
|
|
29433
|
+
}),
|
|
29434
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29435
|
+
className: "text-sm font-semibold text-foreground truncate block leading-tight",
|
|
29436
|
+
children: step.addonName
|
|
29437
|
+
}),
|
|
29438
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29439
|
+
className: "flex items-center gap-1 mt-0.5 flex-wrap",
|
|
29440
|
+
children: [
|
|
29441
|
+
step.inputClasses.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29442
|
+
className: "text-[9px] uppercase font-semibold tracking-wide px-1.5 py-0.5 rounded bg-blue-500/12 text-blue-400",
|
|
29443
|
+
children: c
|
|
29444
|
+
}, c)),
|
|
29445
|
+
step.inputClasses.length > 0 && step.outputClasses.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29446
|
+
className: "text-foreground-subtle/40 text-[10px]",
|
|
29447
|
+
children: "→"
|
|
29448
|
+
}),
|
|
29449
|
+
step.outputClasses.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29450
|
+
className: "text-[9px] uppercase font-semibold tracking-wide px-1.5 py-0.5 rounded bg-green-500/12 text-green-400",
|
|
29451
|
+
children: c
|
|
29452
|
+
}, c))
|
|
29453
|
+
]
|
|
29454
|
+
})
|
|
29455
|
+
]
|
|
29456
|
+
}), !readOnly && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
29457
|
+
onClick: (e) => {
|
|
29458
|
+
e.stopPropagation();
|
|
29459
|
+
onChange({
|
|
29460
|
+
...step,
|
|
29461
|
+
enabled: !step.enabled
|
|
29462
|
+
});
|
|
29463
|
+
},
|
|
29464
|
+
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"),
|
|
29465
|
+
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") })
|
|
29466
|
+
})] });
|
|
29245
29467
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CollapsibleCard, {
|
|
29246
29468
|
expanded,
|
|
29247
29469
|
onExpandedChange: setExpanded,
|
|
29248
29470
|
dimmed: !step.enabled,
|
|
29249
29471
|
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", {
|
|
29472
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29254
29473
|
className: "space-y-3",
|
|
29255
29474
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29256
29475
|
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
|
-
})
|
|
29476
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
29477
|
+
className: "block text-[10px] font-medium text-foreground-subtle uppercase tracking-wide",
|
|
29478
|
+
children: "Model"
|
|
29479
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelPicker, {
|
|
29480
|
+
models,
|
|
29481
|
+
value: step.modelId,
|
|
29482
|
+
disabled: readOnly,
|
|
29483
|
+
onChange: (id) => onChange({
|
|
29484
|
+
...step,
|
|
29485
|
+
modelId: id
|
|
29282
29486
|
})
|
|
29283
|
-
]
|
|
29487
|
+
})]
|
|
29284
29488
|
}), schema?.configSchema?.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigSchemaField, {
|
|
29285
29489
|
field,
|
|
29286
29490
|
allFields: schema.configSchema ?? [],
|
|
@@ -29347,7 +29551,7 @@ function resolveSettingValue(key, step, allFields) {
|
|
|
29347
29551
|
const sibling = allFields.find((f) => "key" in f && f.key === key);
|
|
29348
29552
|
return sibling && "default" in sibling ? sibling.default : void 0;
|
|
29349
29553
|
}
|
|
29350
|
-
function ConfigSchemaField({ field, allFields, step, disabled, onChange }) {
|
|
29554
|
+
function ConfigSchemaField({ field, allFields, step, disabled, onChange, hideModelBoundSourceRow }) {
|
|
29351
29555
|
const applicability = "modelApplicability" in field ? field.modelApplicability : void 0;
|
|
29352
29556
|
const inapplicableReason = applicability?.inapplicable?.[step.modelId];
|
|
29353
29557
|
const control = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigSchemaFieldControl, {
|
|
@@ -29356,7 +29560,8 @@ function ConfigSchemaField({ field, allFields, step, disabled, onChange }) {
|
|
|
29356
29560
|
step,
|
|
29357
29561
|
disabled: disabled || inapplicableReason !== void 0,
|
|
29358
29562
|
modelScopedOptions: applicability?.optionsByModel?.[step.modelId],
|
|
29359
|
-
onChange
|
|
29563
|
+
onChange,
|
|
29564
|
+
hideModelBoundSourceRow: hideModelBoundSourceRow === true
|
|
29360
29565
|
});
|
|
29361
29566
|
if (inapplicableReason === void 0) return control;
|
|
29362
29567
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -29367,7 +29572,7 @@ function ConfigSchemaField({ field, allFields, step, disabled, onChange }) {
|
|
|
29367
29572
|
})]
|
|
29368
29573
|
});
|
|
29369
29574
|
}
|
|
29370
|
-
function ConfigSchemaFieldControl({ field, allFields, step, disabled, modelScopedOptions, onChange }) {
|
|
29575
|
+
function ConfigSchemaFieldControl({ field, allFields, step, disabled, modelScopedOptions, onChange, hideModelBoundSourceRow }) {
|
|
29371
29576
|
const showWhen = field.type !== "separator" && field.type !== "info" ? field.showWhen : void 0;
|
|
29372
29577
|
if (showWhen) {
|
|
29373
29578
|
const { field: condField, equals, notEquals, in: inList, notIn } = showWhen;
|
|
@@ -29417,7 +29622,8 @@ function ConfigSchemaFieldControl({ field, allFields, step, disabled, modelScope
|
|
|
29417
29622
|
onChangeSettings: (settings) => onChange({
|
|
29418
29623
|
...step,
|
|
29419
29624
|
settings
|
|
29420
|
-
})
|
|
29625
|
+
}),
|
|
29626
|
+
hideSourceRow: hideModelBoundSourceRow
|
|
29421
29627
|
});
|
|
29422
29628
|
if (field.type === "slider" || field.type === "number") {
|
|
29423
29629
|
const raw = step.settings?.[field.key];
|
|
@@ -29503,204 +29709,247 @@ function ConfigSchemaFieldControl({ field, allFields, step, disabled, modelScope
|
|
|
29503
29709
|
});
|
|
29504
29710
|
return null;
|
|
29505
29711
|
}
|
|
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
29712
|
//#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;
|
|
29713
|
+
//#region src/composites/step-inspector/setting-values.ts
|
|
29714
|
+
function isValueField(field) {
|
|
29715
|
+
return field.type !== "separator" && field.type !== "info" && field.type !== "group";
|
|
29531
29716
|
}
|
|
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);
|
|
29717
|
+
/** True for a D738 field whose value is stored with the model it was made for. */
|
|
29718
|
+
function bindsToModel(field) {
|
|
29719
|
+
return (field.type === "slider" || field.type === "number") && field.bindsToModel === true;
|
|
29549
29720
|
}
|
|
29550
29721
|
/**
|
|
29551
|
-
*
|
|
29552
|
-
*
|
|
29553
|
-
*
|
|
29554
|
-
* the runner cannot honour.
|
|
29722
|
+
* What runs when no layer stores the field: the running model's default for a
|
|
29723
|
+
* model-bound field that the model declares one for (D738), else the field's
|
|
29724
|
+
* own default. `fromModel` says which.
|
|
29555
29725
|
*/
|
|
29556
|
-
function
|
|
29557
|
-
|
|
29558
|
-
|
|
29726
|
+
function defaultOfField(field, modelId) {
|
|
29727
|
+
const byModel = "modelApplicability" in field ? field.modelApplicability?.defaultByModel?.[modelId] : void 0;
|
|
29728
|
+
if (byModel !== void 0) return {
|
|
29729
|
+
value: byModel,
|
|
29730
|
+
fromModel: true
|
|
29731
|
+
};
|
|
29732
|
+
return {
|
|
29733
|
+
value: "default" in field ? field.default : void 0,
|
|
29734
|
+
fromModel: false
|
|
29735
|
+
};
|
|
29559
29736
|
}
|
|
29560
|
-
|
|
29561
|
-
|
|
29562
|
-
return stepModelOptions(models, format).length > 0;
|
|
29737
|
+
function trimNumber(n) {
|
|
29738
|
+
return n.toFixed(2).replace(/\.?0+$/, "");
|
|
29563
29739
|
}
|
|
29564
|
-
/**
|
|
29565
|
-
|
|
29566
|
-
|
|
29567
|
-
|
|
29568
|
-
|
|
29569
|
-
|
|
29570
|
-
|
|
29571
|
-
|
|
29572
|
-
|
|
29573
|
-
|
|
29574
|
-
|
|
29575
|
-
|
|
29740
|
+
/** One stored value as the operator reads it (`0.65`, `on`, `Person, Vehicle`, `—`). */
|
|
29741
|
+
function formatSettingValue(field, value) {
|
|
29742
|
+
if (value === void 0 || value === null) return "—";
|
|
29743
|
+
if (typeof value === "boolean") return value ? "on" : "off";
|
|
29744
|
+
if (typeof value === "number") {
|
|
29745
|
+
const scale = field.type === "slider" ? field.displayScale : void 0;
|
|
29746
|
+
const unit = "unit" in field && typeof field.unit === "string" ? ` ${field.unit}` : "";
|
|
29747
|
+
return `${trimNumber(scale !== void 0 ? value / scale : value)}${unit}`;
|
|
29748
|
+
}
|
|
29749
|
+
const options = "options" in field && Array.isArray(field.options) ? field.options : [];
|
|
29750
|
+
const labelOf = (v) => {
|
|
29751
|
+
const hit = options.find((o) => o.value === v);
|
|
29752
|
+
return hit ? hit.label : String(v);
|
|
29753
|
+
};
|
|
29754
|
+
if (Array.isArray(value)) return value.length === 0 ? "none" : value.map(labelOf).join(", ");
|
|
29755
|
+
if (typeof value === "string") return labelOf(value);
|
|
29756
|
+
return JSON.stringify(value);
|
|
29576
29757
|
}
|
|
29577
29758
|
//#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);
|
|
29759
|
+
//#region src/composites/step-inspector/layered-settings-form.tsx
|
|
29760
|
+
var SOURCE_TONE$1 = {
|
|
29761
|
+
accelerator: "border-amber-500/40 bg-amber-500/10 text-amber-500",
|
|
29762
|
+
camera: "border-primary/40 bg-primary/10 text-primary",
|
|
29763
|
+
node: "border-border bg-muted/40 text-foreground",
|
|
29764
|
+
default: "border-border/60 text-foreground-subtle"
|
|
29765
|
+
};
|
|
29766
|
+
/** DOM id of a field's box — the target of a search hit. */
|
|
29767
|
+
function stepFieldDomId(key) {
|
|
29768
|
+
return `step-field-${key}`;
|
|
29593
29769
|
}
|
|
29594
|
-
|
|
29595
|
-
function withUseRootModel(settings, value) {
|
|
29770
|
+
function displayStep(addon, modelId, settings) {
|
|
29596
29771
|
return {
|
|
29597
|
-
|
|
29598
|
-
|
|
29772
|
+
addonId: addon.id,
|
|
29773
|
+
addonName: addon.name,
|
|
29774
|
+
slot: addon.slot,
|
|
29775
|
+
inputClasses: [...addon.inputClasses],
|
|
29776
|
+
outputClasses: [...addon.outputClasses],
|
|
29777
|
+
enabled: true,
|
|
29778
|
+
modelId,
|
|
29779
|
+
children: [],
|
|
29780
|
+
settings: { ...settings }
|
|
29599
29781
|
};
|
|
29600
29782
|
}
|
|
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
|
-
|
|
29783
|
+
var LAYER_IDS = [
|
|
29784
|
+
"accelerator",
|
|
29785
|
+
"camera",
|
|
29786
|
+
"node"
|
|
29787
|
+
];
|
|
29788
|
+
/** "Camera · node 0.65 · model default 0.78 · [Reset to node]" under one field. */
|
|
29789
|
+
function SettingProvenance({ field, layers, target, labels, boundKeys, modelId, pinned, disabled, onReset, action }) {
|
|
29790
|
+
const effective = (0, _camstack_types.effectiveStepSettings)(layers);
|
|
29791
|
+
const fallback = defaultOfField(field, modelId);
|
|
29792
|
+
const defaultLabel = fallback.fromModel ? "model default" : "default";
|
|
29793
|
+
const stored = (0, _camstack_types.stepSettingSource)(layers, field.key, boundKeys);
|
|
29794
|
+
const bound = bindsToModel(field);
|
|
29795
|
+
const state = bound ? (0, _camstack_types.resolveModelBoundSetting)(effective, field.key, modelId, fallback.value, fallback.value) : null;
|
|
29796
|
+
const source = state === null || state.source === "override" ? stored : "default";
|
|
29797
|
+
const sourceText = source === "default" ? defaultLabel : labels[source] ?? source;
|
|
29798
|
+
const levels = LAYER_IDS.filter((id) => id !== source && labels[id] !== void 0).filter((id) => layers[id]?.[field.key] !== void 0).map((id) => {
|
|
29799
|
+
const binding = bound ? layers[id]?.[(0, _camstack_types.modelBindingKey)(field.key)] : void 0;
|
|
29800
|
+
const forOther = typeof binding === "string" && binding !== modelId ? ` (for ${binding})` : "";
|
|
29801
|
+
return `${labels[id]} ${formatSettingValue(field, layers[id]?.[field.key])}${forOther}`;
|
|
29802
|
+
});
|
|
29803
|
+
const withDefault = source === "default" ? levels : [...levels, `${defaultLabel} ${formatSettingValue(field, fallback.value)}`];
|
|
29804
|
+
const targetStores = (0, _camstack_types.stepSettingSource)({ [target]: layers[target] }, field.key, boundKeys) !== "default";
|
|
29805
|
+
const below = LAYER_IDS.slice(LAYER_IDS.indexOf(target) + 1).find((id) => labels[id] !== void 0 && layers[id]?.[field.key] !== void 0);
|
|
29806
|
+
const resetTo = below !== void 0 ? labels[below] ?? below : defaultLabel;
|
|
29807
|
+
const belowSource = stored === "default" ? "default" : (0, _camstack_types.stepSettingSource)({
|
|
29808
|
+
...layers,
|
|
29809
|
+
[stored]: void 0
|
|
29810
|
+
}, field.key, boundKeys);
|
|
29811
|
+
const pinnedAt = belowSource === "default" ? defaultLabel : labels[belowSource] ?? belowSource;
|
|
29812
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29813
|
+
className: "mt-1 flex flex-wrap items-center gap-x-2 gap-y-1 text-[10px]",
|
|
29627
29814
|
children: [
|
|
29628
|
-
|
|
29629
|
-
|
|
29630
|
-
|
|
29815
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29816
|
+
"data-testid": "setting-source",
|
|
29817
|
+
className: cn("rounded border px-1.5 py-px font-medium", SOURCE_TONE$1[source]),
|
|
29818
|
+
children: sourceText
|
|
29819
|
+
}),
|
|
29820
|
+
withDefault.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29821
|
+
"data-testid": "setting-levels",
|
|
29822
|
+
className: "text-foreground-subtle",
|
|
29823
|
+
children: withDefault.join(" · ")
|
|
29824
|
+
}),
|
|
29825
|
+
pinned && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
29826
|
+
className: "rounded border border-amber-500/40 px-1.5 py-px text-amber-500",
|
|
29827
|
+
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.",
|
|
29828
|
+
children: [
|
|
29829
|
+
"pinned at the ",
|
|
29830
|
+
pinnedAt.toLowerCase(),
|
|
29831
|
+
" value"
|
|
29832
|
+
]
|
|
29833
|
+
}),
|
|
29834
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
29835
|
+
className: "ml-auto flex items-center gap-2",
|
|
29836
|
+
children: [action, targetStores && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
29837
|
+
type: "button",
|
|
29838
|
+
disabled,
|
|
29839
|
+
onClick: onReset,
|
|
29840
|
+
className: "rounded px-1 text-foreground-subtle hover:bg-muted hover:text-foreground disabled:opacity-50",
|
|
29841
|
+
children: ["Reset to ", resetTo.toLowerCase()]
|
|
29842
|
+
})]
|
|
29843
|
+
})
|
|
29631
29844
|
]
|
|
29632
|
-
})
|
|
29633
|
-
|
|
29634
|
-
|
|
29845
|
+
});
|
|
29846
|
+
}
|
|
29847
|
+
function LayeredSettingsForm({ addon, fields, layers, target, labels, modelId, onChangeLayer, rootModel, pinnedKeys = [], fieldActions, headerActions, highlightKey = null, disabled = false }) {
|
|
29848
|
+
const boundKeys = (0, _camstack_types.collectModelBoundKeys)(fields);
|
|
29849
|
+
const effective = (0, _camstack_types.effectiveStepSettings)(layers);
|
|
29850
|
+
const targetLayer = layers[target];
|
|
29851
|
+
const step = displayStep(addon, modelId, effective);
|
|
29852
|
+
const rootState = rootSourceState(addon.id, effective, rootModel);
|
|
29635
29853
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29636
|
-
className: "space-y-
|
|
29637
|
-
"data-testid": "root-model-source",
|
|
29854
|
+
className: "space-y-2 text-xs",
|
|
29638
29855
|
children: [
|
|
29639
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
29640
|
-
className: "flex items-center gap-2
|
|
29856
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29857
|
+
className: "flex flex-wrap items-center gap-2",
|
|
29641
29858
|
children: [
|
|
29642
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
29643
|
-
|
|
29644
|
-
|
|
29645
|
-
disabled,
|
|
29646
|
-
onChange: (e) => onChange(e.target.checked)
|
|
29859
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
29860
|
+
className: TEXT_SECTION_LABEL,
|
|
29861
|
+
children: "Settings"
|
|
29647
29862
|
}),
|
|
29648
|
-
|
|
29649
|
-
|
|
29650
|
-
|
|
29863
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
29864
|
+
className: TEXT_HINT,
|
|
29865
|
+
children: ["editing: ", labels[target] ?? target]
|
|
29866
|
+
}),
|
|
29867
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
29868
|
+
className: "ml-auto flex flex-wrap items-center gap-3",
|
|
29869
|
+
children: [headerActions, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResetModelDefaultsButton, {
|
|
29870
|
+
fields,
|
|
29871
|
+
modelId,
|
|
29872
|
+
settings: targetLayer,
|
|
29873
|
+
disabled,
|
|
29874
|
+
onReset: (next) => onChangeLayer(next)
|
|
29875
|
+
})]
|
|
29876
|
+
})
|
|
29651
29877
|
]
|
|
29652
29878
|
}),
|
|
29653
|
-
|
|
29654
|
-
|
|
29655
|
-
|
|
29656
|
-
|
|
29657
|
-
|
|
29879
|
+
rootState !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RootModelSourceControl, {
|
|
29880
|
+
stepId: addon.id,
|
|
29881
|
+
settings: effective,
|
|
29882
|
+
rootModel,
|
|
29883
|
+
disabled,
|
|
29884
|
+
onChange: (v) => onChangeLayer((0, _camstack_types.withFieldFrom)(targetLayer, _camstack_types.USE_ROOT_MODEL_SETTING_KEY, withUseRootModel(effective, v), []))
|
|
29885
|
+
}),
|
|
29886
|
+
fields.map((field) => {
|
|
29887
|
+
if (!isValueField(field)) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigSchemaField, {
|
|
29888
|
+
field,
|
|
29889
|
+
allFields: fields,
|
|
29890
|
+
step,
|
|
29891
|
+
disabled,
|
|
29892
|
+
onChange: () => {}
|
|
29893
|
+
}, field.key);
|
|
29894
|
+
const highlighted = highlightKey === field.key;
|
|
29895
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29896
|
+
id: stepFieldDomId(field.key),
|
|
29897
|
+
"data-field-key": field.key,
|
|
29898
|
+
className: cn("rounded-md border border-transparent px-2 py-1 -mx-2", highlighted && "border-primary/50 bg-primary/5"),
|
|
29899
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigSchemaField, {
|
|
29900
|
+
field,
|
|
29901
|
+
allFields: fields,
|
|
29902
|
+
step,
|
|
29903
|
+
disabled,
|
|
29904
|
+
hideModelBoundSourceRow: true,
|
|
29905
|
+
onChange: (next) => onChangeLayer((0, _camstack_types.withFieldFrom)(targetLayer, field.key, next.settings ?? {}, boundKeys))
|
|
29906
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingProvenance, {
|
|
29907
|
+
field,
|
|
29908
|
+
layers,
|
|
29909
|
+
target,
|
|
29910
|
+
labels,
|
|
29911
|
+
boundKeys,
|
|
29912
|
+
modelId,
|
|
29913
|
+
pinned: pinnedKeys.includes(field.key),
|
|
29914
|
+
disabled,
|
|
29915
|
+
onReset: () => onChangeLayer((0, _camstack_types.withoutField)(targetLayer, field.key, boundKeys)),
|
|
29916
|
+
action: fieldActions?.(field.key, (0, _camstack_types.stepSettingSource)(layers, field.key, boundKeys))
|
|
29917
|
+
})]
|
|
29918
|
+
}, field.key);
|
|
29919
|
+
})
|
|
29658
29920
|
]
|
|
29659
29921
|
});
|
|
29660
29922
|
}
|
|
29661
29923
|
//#endregion
|
|
29662
29924
|
//#region src/composites/agent-step-editor.tsx
|
|
29663
29925
|
/**
|
|
29664
|
-
*
|
|
29665
|
-
*
|
|
29666
|
-
*
|
|
29926
|
+
* A step's node-local fields: the catalog schema minus the knobs the CLUSTER
|
|
29927
|
+
* owns (`clusterStepSettingFieldsFor`). Those still exist on the schema so the
|
|
29928
|
+
* runner can hydrate `child.settings`, but writing them per node or per camera
|
|
29667
29929
|
* would be a second store the overlay silently ignores.
|
|
29668
29930
|
*/
|
|
29669
|
-
function
|
|
29931
|
+
function stepLocalFields(addon) {
|
|
29670
29932
|
const clusterKeys = new Set((0, _camstack_types.clusterStepSettingFieldsFor)(addon.id).map((field) => field.key));
|
|
29671
29933
|
return (addon.configSchema ?? []).filter((field) => !clusterKeys.has(field.key));
|
|
29672
29934
|
}
|
|
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
29935
|
/**
|
|
29681
|
-
* The model
|
|
29936
|
+
* The model a step's settings are read and written for — what a model-bound
|
|
29682
29937
|
* field (D738) binds to. While the ROOT supplies the step's fine label (D741),
|
|
29683
29938
|
* that is the root model: the step's threshold then gates the root's label, on
|
|
29684
29939
|
* the root's score scale. Otherwise the step's own model: its pin, else the
|
|
29685
29940
|
* model the row really runs (`effectiveModelId` — the device's hardware-aware
|
|
29686
29941
|
* default when unpinned), else the step's flat catalog default.
|
|
29687
29942
|
*/
|
|
29688
|
-
function settingsModelIdFor(addon,
|
|
29689
|
-
if (rootModel !== void 0 && rootSourceState(addon.id,
|
|
29690
|
-
return
|
|
29943
|
+
function settingsModelIdFor(addon, modelId, settings, rootModel) {
|
|
29944
|
+
if (rootModel !== void 0 && rootSourceState(addon.id, settings, rootModel)?.effective) return rootModel.modelId;
|
|
29945
|
+
return modelId ?? addon.defaultModelId;
|
|
29691
29946
|
}
|
|
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
|
-
};
|
|
29947
|
+
function ClusterSettingsHint({ addonId }) {
|
|
29948
|
+
if ((0, _camstack_types.clusterStepSettingFieldsFor)(addonId).length === 0) return null;
|
|
29949
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
29950
|
+
className: "text-[10px] leading-snug text-foreground-subtle",
|
|
29951
|
+
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."
|
|
29952
|
+
});
|
|
29704
29953
|
}
|
|
29705
29954
|
/**
|
|
29706
29955
|
* Shown in place of the model picker while the root model supplies the step:
|
|
@@ -29721,233 +29970,57 @@ function RootSuppliedModelNote({ rootModel, stepModelId }) {
|
|
|
29721
29970
|
]
|
|
29722
29971
|
});
|
|
29723
29972
|
}
|
|
29724
|
-
|
|
29725
|
-
|
|
29726
|
-
|
|
29727
|
-
|
|
29728
|
-
|
|
29729
|
-
|
|
29730
|
-
|
|
29731
|
-
}
|
|
29732
|
-
|
|
29973
|
+
/**
|
|
29974
|
+
* AgentStepEditor — one step on ONE accelerator: its model and its settings,
|
|
29975
|
+
* as a single node-level layer. Model Studio's "settings for this installed
|
|
29976
|
+
* model" dialog uses it. The pipeline pages use the step inspector, which lays
|
|
29977
|
+
* the same parts out across accelerators and layers (D754/D756); the settings
|
|
29978
|
+
* form here is that inspector's `LayeredSettingsForm` with one layer.
|
|
29979
|
+
*/
|
|
29980
|
+
function AgentStepEditor({ addon, agentDefault, onChangeAgentConfig, engineFormat, effectiveModelId, rootModel }) {
|
|
29981
|
+
const modelsForFormat = (0, react$1.useMemo)(() => stepModelOptions(addon.models, engineFormat), [addon.models, engineFormat]);
|
|
29733
29982
|
const update = (patch) => {
|
|
29734
29983
|
onChangeAgentConfig?.({
|
|
29735
29984
|
...agentDefault,
|
|
29736
29985
|
...patch
|
|
29737
29986
|
});
|
|
29738
29987
|
};
|
|
29739
|
-
const
|
|
29988
|
+
const fields = stepLocalFields(addon);
|
|
29740
29989
|
const formatDefault = addon.defaultModelIdByFormat?.[engineFormat] ?? addon.defaultModelId;
|
|
29741
29990
|
const rootSupplied = rootSourceState(addon.id, agentDefault.settings, rootModel)?.effective === true;
|
|
29991
|
+
const runningModel = agentDefault.modelId ?? effectiveModelId;
|
|
29742
29992
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29743
29993
|
className: "space-y-4 text-xs",
|
|
29744
29994
|
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
29995
|
rootSupplied && rootModel !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RootSuppliedModelNote, {
|
|
29752
29996
|
rootModel,
|
|
29753
|
-
stepModelId:
|
|
29997
|
+
stepModelId: runningModel
|
|
29754
29998
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
29755
29999
|
className: "text-[10px] uppercase tracking-widest text-foreground-subtle mb-1",
|
|
29756
30000
|
children: "Model"
|
|
29757
30001
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelPicker, {
|
|
29758
30002
|
models: modelsForFormat,
|
|
29759
|
-
value:
|
|
30003
|
+
value: runningModel ?? "",
|
|
29760
30004
|
defaultModelId: formatDefault,
|
|
29761
30005
|
onChange: (modelId) => update({ modelId })
|
|
29762
30006
|
})] }),
|
|
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))]
|
|
30007
|
+
(fields.length > 0 || rootSourceState(addon.id, agentDefault.settings, rootModel)) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
30008
|
+
className: "pt-2 border-t border-border/50",
|
|
30009
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LayeredSettingsForm, {
|
|
30010
|
+
addon,
|
|
30011
|
+
fields,
|
|
30012
|
+
layers: { node: agentDefault.settings },
|
|
30013
|
+
target: "node",
|
|
30014
|
+
labels: { node: "This node" },
|
|
30015
|
+
modelId: settingsModelIdFor(addon, runningModel, agentDefault.settings, rootModel),
|
|
30016
|
+
...rootModel !== void 0 ? { rootModel } : {},
|
|
30017
|
+
onChangeLayer: (settings) => update({ settings })
|
|
30018
|
+
})
|
|
29783
30019
|
}),
|
|
29784
30020
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClusterSettingsHint, { addonId: addon.id })
|
|
29785
30021
|
]
|
|
29786
30022
|
});
|
|
29787
30023
|
}
|
|
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
30024
|
//#endregion
|
|
29952
30025
|
//#region src/primitives/icon-button.tsx
|
|
29953
30026
|
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 +33935,6 @@ async function waitForIceGathering(pc) {
|
|
|
33862
33935
|
});
|
|
33863
33936
|
}
|
|
33864
33937
|
//#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
33938
|
//#region src/composites/data-table-layout.ts
|
|
33977
33939
|
/**
|
|
33978
33940
|
* At this many columns and above, `auto` switches a narrow viewport to cards.
|
|
@@ -55565,16 +55527,16 @@ function cellButtonClass(state, isSelected, extra) {
|
|
|
55565
55527
|
//#endregion
|
|
55566
55528
|
//#region src/composites/device-step-matrix.tsx
|
|
55567
55529
|
/**
|
|
55568
|
-
* DeviceStepMatrix — the
|
|
55569
|
-
*
|
|
55530
|
+
* DeviceStepMatrix — the READ-ONLY "Compare" view of the step inspector
|
|
55531
|
+
* (D756): one hierarchical steps × accelerators matrix showing what every
|
|
55532
|
+
* accelerator runs for every step, side by side.
|
|
55570
55533
|
*
|
|
55571
|
-
* Column 1 (STICKY): the indented step tree
|
|
55572
|
-
*
|
|
55573
|
-
*
|
|
55574
|
-
*
|
|
55575
|
-
*
|
|
55576
|
-
*
|
|
55577
|
-
* but the node has no compatible model, or "—" when N/A.
|
|
55534
|
+
* Column 1 (STICKY): the indented step tree (slot + name + class-flow chips).
|
|
55535
|
+
* Columns 2..N (one per accelerator): the model chip, "⚠ skip" when the step
|
|
55536
|
+
* runs on the camera but the accelerator has no compatible model, or "—".
|
|
55537
|
+
* A click selects that (step, accelerator) in the inspector — the matrix
|
|
55538
|
+
* itself edits nothing (it used to host a slide-over editor that scrolled
|
|
55539
|
+
* away with the page).
|
|
55578
55540
|
*
|
|
55579
55541
|
* Responsive, container-measured (never a global media query, so it also works
|
|
55580
55542
|
* embedded):
|
|
@@ -55585,77 +55547,20 @@ function cellButtonClass(state, isSelected, extra) {
|
|
|
55585
55547
|
* single-node view: a node selector picks ONE node and the matrix renders
|
|
55586
55548
|
* `step-tree × that-one-node`.
|
|
55587
55549
|
*/
|
|
55588
|
-
/**
|
|
55589
|
-
var STEP_COL_REM
|
|
55590
|
-
var STEP_COL_PX = STEP_COL_REM
|
|
55550
|
+
/** The sticky step column. */
|
|
55551
|
+
var STEP_COL_REM = 18;
|
|
55552
|
+
var STEP_COL_PX = STEP_COL_REM * 16;
|
|
55591
55553
|
/**
|
|
55592
|
-
* Narrow (mobile) step-column width. On a phone the wide
|
|
55593
|
-
*
|
|
55594
|
-
*
|
|
55595
|
-
* (both fully readable) rather than cramming every column into the viewport.
|
|
55554
|
+
* Narrow (mobile) step-column width. On a phone the wide column would occlude
|
|
55555
|
+
* nearly the whole viewport, so the single-node layout pins a slimmer sticky
|
|
55556
|
+
* step column and lets the device columns scroll horizontally beside it.
|
|
55596
55557
|
*/
|
|
55597
55558
|
var STEP_COL_SINGLE_REM = 11;
|
|
55598
|
-
function
|
|
55599
|
-
|
|
55600
|
-
|
|
55601
|
-
}
|
|
55602
|
-
/**
|
|
55603
|
-
* Tri-state segmented control. `Default` inherits the catalog value (shown in
|
|
55604
|
-
* the caption); `On`/`Off` pin an explicit per-camera override. Touch-friendly
|
|
55605
|
-
* 28px segments.
|
|
55606
|
-
*/
|
|
55607
|
-
function GateControl({ gate, busy, onSet }) {
|
|
55608
|
-
const active = activeChoice(gate);
|
|
55609
|
-
const seg = (choice, label, value, activeClass) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
55610
|
-
type: "button",
|
|
55611
|
-
"aria-pressed": active === choice,
|
|
55612
|
-
disabled: busy,
|
|
55613
|
-
onClick: () => onSet(value),
|
|
55614
|
-
className: cn("flex-1 px-2 py-1 text-[11px] font-medium leading-none transition-colors", "min-h-[26px]", active === choice ? activeClass : "text-foreground-subtle hover:bg-muted/60", busy && "opacity-50 cursor-not-allowed"),
|
|
55615
|
-
children: label
|
|
55616
|
-
});
|
|
55617
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
55618
|
-
className: "flex flex-col gap-1",
|
|
55619
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
55620
|
-
className: "inline-flex w-fit max-w-full self-start overflow-hidden rounded-md border border-border",
|
|
55621
|
-
children: [
|
|
55622
|
-
seg("default", "Auto", null, "bg-primary/15 text-primary"),
|
|
55623
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "w-px self-stretch bg-border" }),
|
|
55624
|
-
seg("on", "On", true, "bg-emerald-500 text-white"),
|
|
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
|
-
});
|
|
55649
|
-
}
|
|
55650
|
-
function StepGateCell({ node, depth, gate, busy, onSet, hideGate }) {
|
|
55651
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
55652
|
-
className: "sticky left-0 z-10 flex flex-col gap-2 border-b border-border bg-surface px-3 py-2.5 text-xs min-w-0",
|
|
55559
|
+
function StepCell({ node, depth }) {
|
|
55560
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
55561
|
+
className: "sticky left-0 z-10 flex flex-col gap-2 border-b border-border bg-surface px-3 py-2 text-xs min-w-0",
|
|
55653
55562
|
style: { paddingLeft: `${12 + depth * 14}px` },
|
|
55654
|
-
children:
|
|
55655
|
-
gate,
|
|
55656
|
-
busy,
|
|
55657
|
-
onSet
|
|
55658
|
-
})]
|
|
55563
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepLabel, { node })
|
|
55659
55564
|
});
|
|
55660
55565
|
}
|
|
55661
55566
|
/**
|
|
@@ -55694,18 +55599,18 @@ function NodeSelectorBar({ nodes, selected, onSelect }) {
|
|
|
55694
55599
|
]
|
|
55695
55600
|
});
|
|
55696
55601
|
}
|
|
55697
|
-
function MatrixGrid
|
|
55602
|
+
function MatrixGrid({ tree, agents, getCellState, onCellClick, selectedCell, single }) {
|
|
55698
55603
|
const rows = (0, react$1.useMemo)(() => flattenTree(tree), [tree]);
|
|
55699
55604
|
const groups = (0, react$1.useMemo)(() => groupAgentColumns(agents), [agents]);
|
|
55700
55605
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
55701
55606
|
className: "overflow-auto rounded border border-border",
|
|
55702
55607
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
55703
55608
|
className: "grid w-max",
|
|
55704
|
-
style: { gridTemplateColumns: `${single ? STEP_COL_SINGLE_REM : STEP_COL_REM
|
|
55609
|
+
style: { gridTemplateColumns: `${single ? STEP_COL_SINGLE_REM : STEP_COL_REM}rem repeat(${agents.length}, 13rem)` },
|
|
55705
55610
|
children: [
|
|
55706
55611
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
55707
55612
|
className: "sticky left-0 top-0 z-20 border-b border-border bg-muted/60 px-3 py-2 text-[10px] uppercase tracking-widest text-foreground-subtle",
|
|
55708
|
-
children:
|
|
55613
|
+
children: "Step"
|
|
55709
55614
|
}),
|
|
55710
55615
|
groups.map((group) => group.grouped ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupedAgentHeader, { group }, group.agentNodeId) : group.columns.map((col) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
55711
55616
|
className: "sticky top-0 z-10 min-w-0 border-b border-l border-border bg-muted/60 px-3 py-2",
|
|
@@ -55721,26 +55626,18 @@ function MatrixGrid$1({ tree, agents, gateFor, getCellState, onCellClick, select
|
|
|
55721
55626
|
node,
|
|
55722
55627
|
depth,
|
|
55723
55628
|
agents,
|
|
55724
|
-
gate: gateFor(node.addonId),
|
|
55725
55629
|
getCellState,
|
|
55726
55630
|
onCellClick,
|
|
55727
|
-
selectedCell
|
|
55728
|
-
onSetGate,
|
|
55729
|
-
gateBusy,
|
|
55730
|
-
hideGate
|
|
55631
|
+
selectedCell
|
|
55731
55632
|
}, node.addonId))
|
|
55732
55633
|
]
|
|
55733
55634
|
})
|
|
55734
55635
|
});
|
|
55735
55636
|
}
|
|
55736
|
-
function MatrixRow({ node, depth, agents,
|
|
55737
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
55637
|
+
function MatrixRow({ node, depth, agents, getCellState, onCellClick, selectedCell }) {
|
|
55638
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepCell, {
|
|
55738
55639
|
node,
|
|
55739
|
-
depth
|
|
55740
|
-
gate,
|
|
55741
|
-
busy: gateBusy,
|
|
55742
|
-
onSet: (next) => onSetGate(node.addonId, next),
|
|
55743
|
-
hideGate
|
|
55640
|
+
depth
|
|
55744
55641
|
}), agents.map((a) => {
|
|
55745
55642
|
const state = getCellState(node.addonId, a);
|
|
55746
55643
|
const isSelected = selectedCell !== null && selectedCell.addonId === node.addonId && agentColumnKey(selectedCell) === agentColumnKey(a);
|
|
@@ -55754,7 +55651,7 @@ function MatrixRow({ node, depth, agents, gate, getCellState, onCellClick, selec
|
|
|
55754
55651
|
}, agentColumnKey(a));
|
|
55755
55652
|
})] });
|
|
55756
55653
|
}
|
|
55757
|
-
function DeviceStepMatrix({ tree, agents,
|
|
55654
|
+
function DeviceStepMatrix({ tree, agents, getCellState, onCellClick, selectedCell, fallbackNodeId, onFallbackNodeChange }) {
|
|
55758
55655
|
const containerRef = (0, react$1.useRef)(null);
|
|
55759
55656
|
const [width, setWidth] = (0, react$1.useState)(0);
|
|
55760
55657
|
(0, react$1.useLayoutEffect)(() => {
|
|
@@ -55780,17 +55677,13 @@ function DeviceStepMatrix({ tree, agents, gateFor, getCellState, onCellClick, se
|
|
|
55780
55677
|
nodes,
|
|
55781
55678
|
selected: selectedNode.agentNodeId,
|
|
55782
55679
|
onSelect: onFallbackNodeChange
|
|
55783
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MatrixGrid
|
|
55680
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MatrixGrid, {
|
|
55784
55681
|
tree,
|
|
55785
55682
|
agents: shownAgents,
|
|
55786
|
-
gateFor,
|
|
55787
55683
|
getCellState,
|
|
55788
55684
|
onCellClick,
|
|
55789
55685
|
selectedCell,
|
|
55790
|
-
|
|
55791
|
-
gateBusy,
|
|
55792
|
-
single: single && selectedNode !== null,
|
|
55793
|
-
hideGate
|
|
55686
|
+
single: single && selectedNode !== null
|
|
55794
55687
|
})]
|
|
55795
55688
|
});
|
|
55796
55689
|
}
|
|
@@ -56820,17 +56713,7 @@ function PlaceholderStep({ addon, onClick }) {
|
|
|
56820
56713
|
})
|
|
56821
56714
|
});
|
|
56822
56715
|
}
|
|
56823
|
-
function PipelineBuilder({ schema, steps, onChange, templates, selectedTemplateId, onSelectTemplate, onSaveTemplate, onUpdateTemplate, onDeleteTemplate, readOnly = false, excludeAddons = [], hideTemplates = false, showOnlyEnabled = false, showSlotLabels = false
|
|
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
|
-
});
|
|
56716
|
+
function PipelineBuilder({ schema, steps, onChange, templates, selectedTemplateId, onSelectTemplate, onSaveTemplate, onUpdateTemplate, onDeleteTemplate, readOnly = false, excludeAddons = [], hideTemplates = false, showOnlyEnabled = false, showSlotLabels = false }) {
|
|
56834
56717
|
const excluded = (0, react$1.useMemo)(() => new Set(excludeAddons), [excludeAddons]);
|
|
56835
56718
|
const schemaMap = (0, react$1.useMemo)(() => buildSchemaMap(schema), [schema]);
|
|
56836
56719
|
const [warnings, setWarnings] = (0, react$1.useState)([]);
|
|
@@ -56937,8 +56820,7 @@ function PipelineBuilder({ schema, steps, onChange, templates, selectedTemplateI
|
|
|
56937
56820
|
allSchemas: schemaMap,
|
|
56938
56821
|
onChange: handleStepChange,
|
|
56939
56822
|
onDelete: readOnly ? void 0 : (id) => onChange(steps.filter((s) => s.addonId !== id)),
|
|
56940
|
-
readOnly
|
|
56941
|
-
...stepExtraProps(step.addonId)
|
|
56823
|
+
readOnly
|
|
56942
56824
|
}), (step.children.length > 0 || childPlaceholders.length > 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
56943
56825
|
className: "ml-4 pl-3 border-l border-border/60 space-y-1.5",
|
|
56944
56826
|
children: [
|
|
@@ -56967,8 +56849,7 @@ function PipelineBuilder({ schema, steps, onChange, templates, selectedTemplateI
|
|
|
56967
56849
|
children: step.children.filter((c) => c.addonId !== id)
|
|
56968
56850
|
});
|
|
56969
56851
|
},
|
|
56970
|
-
readOnly
|
|
56971
|
-
...stepExtraProps(child.addonId)
|
|
56852
|
+
readOnly
|
|
56972
56853
|
}), (child.children.length > 0 || childChildPlaceholders.length > 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
56973
56854
|
className: "ml-4 pl-3 border-l border-border/40 space-y-1.5",
|
|
56974
56855
|
children: [
|
|
@@ -56999,8 +56880,7 @@ function PipelineBuilder({ schema, steps, onChange, templates, selectedTemplateI
|
|
|
56999
56880
|
} : c)
|
|
57000
56881
|
});
|
|
57001
56882
|
},
|
|
57002
|
-
readOnly
|
|
57003
|
-
...stepExtraProps(grandchild.addonId)
|
|
56883
|
+
readOnly
|
|
57004
56884
|
}, grandchild.addonId)),
|
|
57005
56885
|
!readOnly && childChildPlaceholders.map((addon) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlaceholderStep, {
|
|
57006
56886
|
addon,
|
|
@@ -57160,179 +57040,6 @@ function PipelineRuntimeSelector({ options, value, onChange }) {
|
|
|
57160
57040
|
});
|
|
57161
57041
|
}
|
|
57162
57042
|
//#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
57043
|
//#region src/composites/provider-badge.tsx
|
|
57337
57044
|
var providerConfig = {
|
|
57338
57045
|
onvif: {
|
|
@@ -57776,89 +57483,6 @@ function validateSelector(selector) {
|
|
|
57776
57483
|
}
|
|
57777
57484
|
}
|
|
57778
57485
|
//#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
57486
|
//#region src/composites/snapshot-button.tsx
|
|
57863
57487
|
/**
|
|
57864
57488
|
* SnapshotButton — operator-facing manual snapshot trigger.
|
|
@@ -58044,51 +57668,725 @@ function StepTimings({ timings, totalMs, className }) {
|
|
|
58044
57668
|
});
|
|
58045
57669
|
}
|
|
58046
57670
|
//#endregion
|
|
58047
|
-
//#region src/composites/step-
|
|
58048
|
-
|
|
58049
|
-
|
|
58050
|
-
|
|
58051
|
-
|
|
58052
|
-
|
|
58053
|
-
|
|
58054
|
-
|
|
58055
|
-
|
|
58056
|
-
|
|
58057
|
-
|
|
57671
|
+
//#region src/composites/step-inspector/accelerator-tabs.tsx
|
|
57672
|
+
function AcceleratorTabs({ items, selectedId, onSelect }) {
|
|
57673
|
+
const onKeyDown = (e, index) => {
|
|
57674
|
+
const delta = e.key === "ArrowRight" ? 1 : e.key === "ArrowLeft" ? -1 : 0;
|
|
57675
|
+
if (delta === 0) return;
|
|
57676
|
+
e.preventDefault();
|
|
57677
|
+
const next = items[(index + delta + items.length) % items.length];
|
|
57678
|
+
if (next !== void 0) onSelect(next.id);
|
|
57679
|
+
};
|
|
57680
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57681
|
+
role: "tablist",
|
|
57682
|
+
"aria-label": "Accelerator",
|
|
57683
|
+
className: "-mx-1 flex min-w-0 flex-nowrap gap-1 overflow-x-auto px-1 pb-0.5",
|
|
57684
|
+
children: items.map((item, index) => {
|
|
57685
|
+
const selected = item.id === selectedId;
|
|
57686
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
57687
|
+
type: "button",
|
|
57688
|
+
role: "tab",
|
|
57689
|
+
"aria-selected": selected,
|
|
57690
|
+
"aria-label": item.marked ? `${item.label} (camera override)` : item.label,
|
|
57691
|
+
tabIndex: selected ? 0 : -1,
|
|
57692
|
+
onClick: () => onSelect(item.id),
|
|
57693
|
+
onKeyDown: (e) => onKeyDown(e, index),
|
|
57694
|
+
className: cn("inline-flex h-9 shrink-0 items-center gap-1 whitespace-nowrap rounded-md border px-2.5 text-[11px] font-medium transition-colors md:h-7", selected ? "border-primary/50 bg-primary/15 text-primary" : "border-border text-foreground-subtle hover:bg-surface-hover hover:text-foreground"),
|
|
57695
|
+
children: [item.label, item.marked && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57696
|
+
className: "h-1.5 w-1.5 rounded-full bg-amber-500",
|
|
57697
|
+
"aria-hidden": true
|
|
57698
|
+
})]
|
|
57699
|
+
}, item.id);
|
|
57700
|
+
})
|
|
57701
|
+
});
|
|
57702
|
+
}
|
|
57703
|
+
//#endregion
|
|
57704
|
+
//#region src/composites/step-inspector/gate-control.tsx
|
|
57705
|
+
/**
|
|
57706
|
+
* GateControl — whether ONE camera runs a step: Auto (the catalog default) /
|
|
57707
|
+
* On / Off. A per-camera, node-independent switch, written at once (it is not
|
|
57708
|
+
* part of the inspector's draft). `package-detection` is driven by package
|
|
57709
|
+
* zones and shows a badge instead.
|
|
57710
|
+
*
|
|
57711
|
+
* Segments are 36 px tall on a phone and 32 px from `md` up — the matrix this
|
|
57712
|
+
* replaced drew 26 px ones.
|
|
57713
|
+
*/
|
|
57714
|
+
function activeChoice(gate) {
|
|
57715
|
+
if (!gate.explicit) return "auto";
|
|
57716
|
+
return gate.effectiveOn ? "on" : "off";
|
|
57717
|
+
}
|
|
57718
|
+
function GateControl({ gate, busy = false, onSet }) {
|
|
57719
|
+
if (gate.kind === "zone-driven") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57720
|
+
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",
|
|
57721
|
+
title: "Runs where this camera has package zones — set on the Zones tab",
|
|
57722
|
+
children: "Zones"
|
|
57723
|
+
});
|
|
57724
|
+
const active = activeChoice(gate);
|
|
57725
|
+
const segment = (choice, label, value, on) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
58058
57726
|
type: "button",
|
|
58059
|
-
|
|
58060
|
-
|
|
58061
|
-
|
|
58062
|
-
|
|
58063
|
-
|
|
58064
|
-
|
|
58065
|
-
|
|
58066
|
-
|
|
58067
|
-
|
|
58068
|
-
|
|
58069
|
-
|
|
58070
|
-
|
|
57727
|
+
role: "radio",
|
|
57728
|
+
"aria-checked": active === choice,
|
|
57729
|
+
disabled: busy,
|
|
57730
|
+
onClick: () => onSet(value),
|
|
57731
|
+
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"),
|
|
57732
|
+
children: label
|
|
57733
|
+
});
|
|
57734
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57735
|
+
className: "flex items-center gap-2",
|
|
57736
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57737
|
+
role: "radiogroup",
|
|
57738
|
+
"aria-label": "Runs on this camera",
|
|
57739
|
+
title: `Catalog default: ${gate.defaultOn ? "on" : "off"}`,
|
|
57740
|
+
className: "inline-flex overflow-hidden rounded-md border border-border",
|
|
57741
|
+
children: [
|
|
57742
|
+
segment("auto", "Auto", null, "bg-primary/15 text-primary"),
|
|
57743
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57744
|
+
className: "w-px self-stretch bg-border",
|
|
57745
|
+
"aria-hidden": true
|
|
57746
|
+
}),
|
|
57747
|
+
segment("on", "On", true, "bg-emerald-500 text-white"),
|
|
57748
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57749
|
+
className: "w-px self-stretch bg-border",
|
|
57750
|
+
"aria-hidden": true
|
|
57751
|
+
}),
|
|
57752
|
+
segment("off", "Off", false, "bg-foreground-subtle/70 text-white")
|
|
57753
|
+
]
|
|
57754
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
57755
|
+
className: "hidden text-[10px] text-foreground-subtle @3xl/inspector:inline",
|
|
57756
|
+
children: ["default: ", gate.defaultOn ? "on" : "off"]
|
|
58071
57757
|
})]
|
|
58072
|
-
})
|
|
58073
|
-
|
|
58074
|
-
|
|
58075
|
-
|
|
58076
|
-
|
|
58077
|
-
|
|
58078
|
-
|
|
57758
|
+
});
|
|
57759
|
+
}
|
|
57760
|
+
/** The gate as a short list-row badge. */
|
|
57761
|
+
function GateBadge({ gate }) {
|
|
57762
|
+
if (gate.kind === "zone-driven") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57763
|
+
className: "shrink-0 text-[9px] font-semibold uppercase text-violet-400",
|
|
57764
|
+
children: "zones"
|
|
57765
|
+
});
|
|
57766
|
+
const on = gate.effectiveOn;
|
|
57767
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57768
|
+
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"),
|
|
57769
|
+
title: gate.explicit ? "Set for this camera" : "Catalog default",
|
|
57770
|
+
children: on ? "on" : "off"
|
|
57771
|
+
});
|
|
57772
|
+
}
|
|
57773
|
+
//#endregion
|
|
57774
|
+
//#region src/composites/step-inspector/segmented-choice.tsx
|
|
57775
|
+
/**
|
|
57776
|
+
* SegmentedChoice — a small, labelled single-choice group (a radiogroup of
|
|
57777
|
+
* buttons): which layer the inspector's settings edit ("All accelerators" /
|
|
57778
|
+
* "Only hub · NPU"). 36 px tall on a phone, 28 px from `md` up.
|
|
57779
|
+
*/
|
|
57780
|
+
function SegmentedChoice({ label, options, value, onChange }) {
|
|
57781
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57782
|
+
role: "radiogroup",
|
|
57783
|
+
"aria-label": label,
|
|
57784
|
+
className: "flex flex-wrap items-center gap-1",
|
|
57785
|
+
children: options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
57786
|
+
type: "button",
|
|
57787
|
+
role: "radio",
|
|
57788
|
+
"aria-checked": value === option.value,
|
|
57789
|
+
onClick: () => onChange(option.value),
|
|
57790
|
+
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"),
|
|
57791
|
+
children: option.label
|
|
57792
|
+
}, option.value))
|
|
57793
|
+
});
|
|
57794
|
+
}
|
|
57795
|
+
//#endregion
|
|
57796
|
+
//#region src/composites/step-inspector/step-chips.tsx
|
|
57797
|
+
function chipText(state) {
|
|
57798
|
+
switch (state.kind) {
|
|
57799
|
+
case "enabled":
|
|
57800
|
+
if (state.suppliedByRoot !== void 0) return "root";
|
|
57801
|
+
return state.jumpTo !== void 0 ? `→ ${state.jumpTo}` : state.modelId;
|
|
57802
|
+
case "skip": return state.jumpTo !== void 0 ? `→ ${state.jumpTo}` : "skip";
|
|
57803
|
+
case "disabled": return "off";
|
|
57804
|
+
case "na": return "—";
|
|
57805
|
+
}
|
|
57806
|
+
}
|
|
57807
|
+
/** What a chip MEANS — the full model id, not its shortened text. */
|
|
57808
|
+
function chipKey(state) {
|
|
57809
|
+
return state.kind === "enabled" ? `${state.modelId}|${chipText(state)}` : chipText(state);
|
|
57810
|
+
}
|
|
57811
|
+
function chipTone(state, selected) {
|
|
57812
|
+
if (selected) return "border-primary/60 text-primary";
|
|
57813
|
+
if (state.kind === "skip" && state.jumpTo === void 0) return "border-amber-500/40 text-amber-500";
|
|
57814
|
+
if (state.kind === "na" || state.kind === "disabled") return "border-border/60 text-foreground-subtle/70";
|
|
57815
|
+
return "border-border text-foreground-subtle";
|
|
57816
|
+
}
|
|
57817
|
+
function StepChips({ chips, selectedId }) {
|
|
57818
|
+
const first = chips[0];
|
|
57819
|
+
if (first !== void 0 && chips.length > 1 && chips.every((c) => chipKey(c.state) === chipKey(first.state) && !(c.state.kind === "enabled" && c.state.overridden === true) && chipTone(c.state, c.id === selectedId) === chipTone(first.state, first.id === selectedId)) && first !== void 0) {
|
|
57820
|
+
const text = chipText(first.state);
|
|
57821
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57822
|
+
className: "flex flex-wrap gap-1",
|
|
57823
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
57824
|
+
title: `${text} on ${chips.map((c) => c.label).join(", ")}`,
|
|
57825
|
+
className: cn("inline-flex max-w-[12rem] items-center gap-1 truncate rounded border px-1 font-mono text-[9px] leading-4", chipTone(first.state, first.id === selectedId)),
|
|
57826
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57827
|
+
className: "truncate",
|
|
57828
|
+
children: text
|
|
57829
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
57830
|
+
className: "shrink-0 font-sans text-foreground-subtle/80",
|
|
57831
|
+
children: ["×", chips.length]
|
|
57832
|
+
})]
|
|
57833
|
+
})
|
|
57834
|
+
});
|
|
57835
|
+
}
|
|
57836
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57837
|
+
className: "flex flex-wrap gap-1",
|
|
57838
|
+
children: chips.map((chip) => {
|
|
57839
|
+
const overridden = chip.state.kind === "enabled" && chip.state.overridden === true;
|
|
57840
|
+
const text = chipText(chip.state);
|
|
57841
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
57842
|
+
title: `${chip.label}: ${chip.state.kind === "enabled" ? chip.state.modelId : text}${overridden ? " (camera override)" : ""}`,
|
|
57843
|
+
className: cn("inline-flex max-w-[9rem] items-center gap-0.5 truncate rounded border px-1 font-mono text-[9px] leading-4", chipTone(chip.state, chip.id === selectedId)),
|
|
57844
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57845
|
+
className: "truncate",
|
|
57846
|
+
children: text
|
|
57847
|
+
}), overridden && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57848
|
+
className: "text-amber-500",
|
|
57849
|
+
"aria-hidden": true,
|
|
57850
|
+
children: "•"
|
|
57851
|
+
})]
|
|
57852
|
+
}, chip.id);
|
|
57853
|
+
})
|
|
57854
|
+
});
|
|
57855
|
+
}
|
|
57856
|
+
//#endregion
|
|
57857
|
+
//#region src/composites/step-inspector/step-inspector-layout.tsx
|
|
57858
|
+
/**
|
|
57859
|
+
* StepInspectorLayout — the master–detail frame of the step inspector.
|
|
57860
|
+
*
|
|
57861
|
+
* Desktop: the step list and the inspector side by side, each scrolling on its
|
|
57862
|
+
* own, in a block whose height FILLS the page's scroll container below it. So
|
|
57863
|
+
* neither pane depends on the page scroll position: the inspector's header and
|
|
57864
|
+
* save bar are always on screen, and the selected step stays visible in the
|
|
57865
|
+
* list. (The slide-over this replaced was absolutely positioned inside the
|
|
57866
|
+
* matrix block — its title and its Save button scrolled away with the page.)
|
|
57867
|
+
* In a narrow container (the camera page beside its live view, a tablet) the
|
|
57868
|
+
* two panes stack: the list on top, a third of the height.
|
|
57869
|
+
*
|
|
57870
|
+
* Phone: the list flows with the page; the inspector opens as a full-screen
|
|
57871
|
+
* sheet with "‹ Steps" to go back.
|
|
57872
|
+
*/
|
|
57873
|
+
/** The tallest the inspector block can be without making its scroll container scroll. */
|
|
57874
|
+
function fillHeightPx(input) {
|
|
57875
|
+
const room = input.scrollerHeight - input.paddingBottom - input.offsetInScroller;
|
|
57876
|
+
return Math.max(input.minPx, Math.floor(room));
|
|
57877
|
+
}
|
|
57878
|
+
function scrollParentOf(el) {
|
|
57879
|
+
for (let p = el.parentElement; p !== null; p = p.parentElement) {
|
|
57880
|
+
const { overflowY } = getComputedStyle(p);
|
|
57881
|
+
if (overflowY === "auto" || overflowY === "scroll") return p;
|
|
57882
|
+
}
|
|
57883
|
+
return null;
|
|
57884
|
+
}
|
|
57885
|
+
/** Height of the block so that it ends at the bottom of its scroll container. */
|
|
57886
|
+
function useFillHeight(enabled, minPx) {
|
|
57887
|
+
const ref = (0, react$1.useRef)(null);
|
|
57888
|
+
const [height, setHeight] = (0, react$1.useState)(null);
|
|
57889
|
+
(0, react$1.useLayoutEffect)(() => {
|
|
57890
|
+
const el = ref.current;
|
|
57891
|
+
if (!enabled || el === null) return;
|
|
57892
|
+
const scroller = scrollParentOf(el);
|
|
57893
|
+
const measure = () => {
|
|
57894
|
+
const box = scroller ?? document.documentElement;
|
|
57895
|
+
const style = getComputedStyle(box);
|
|
57896
|
+
const top = scroller ? scroller.getBoundingClientRect().top : 0;
|
|
57897
|
+
const scrollTop = scroller ? scroller.scrollTop : window.scrollY;
|
|
57898
|
+
setHeight(fillHeightPx({
|
|
57899
|
+
scrollerHeight: scroller ? scroller.clientHeight : window.innerHeight,
|
|
57900
|
+
paddingBottom: Number.parseFloat(style.paddingBottom) || 0,
|
|
57901
|
+
offsetInScroller: el.getBoundingClientRect().top - top + scrollTop,
|
|
57902
|
+
minPx
|
|
57903
|
+
}));
|
|
57904
|
+
};
|
|
57905
|
+
measure();
|
|
57906
|
+
const RO = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measure);
|
|
57907
|
+
if (scroller) RO?.observe(scroller);
|
|
57908
|
+
if (el.parentElement) RO?.observe(el.parentElement);
|
|
57909
|
+
window.addEventListener("resize", measure);
|
|
57910
|
+
return () => {
|
|
57911
|
+
RO?.disconnect();
|
|
57912
|
+
window.removeEventListener("resize", measure);
|
|
57913
|
+
};
|
|
57914
|
+
}, [enabled, minPx]);
|
|
57915
|
+
return {
|
|
57916
|
+
ref,
|
|
57917
|
+
height
|
|
57918
|
+
};
|
|
58079
57919
|
}
|
|
58080
|
-
function
|
|
57920
|
+
function StepInspectorLayout({ list, detail, mobileDetailOpen, onMobileClose, minHeightPx = 420, className }) {
|
|
57921
|
+
const isMobile = useIsMobile();
|
|
57922
|
+
const { ref, height } = useFillHeight(!isMobile, minHeightPx);
|
|
57923
|
+
if (isMobile) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57924
|
+
className: cn("space-y-3", className),
|
|
57925
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57926
|
+
className: SECTION_CARD,
|
|
57927
|
+
children: list
|
|
57928
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dialog, {
|
|
57929
|
+
open: mobileDetailOpen,
|
|
57930
|
+
onOpenChange: (open) => !open && onMobileClose(),
|
|
57931
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogContent, {
|
|
57932
|
+
placement: "sheet-on-mobile",
|
|
57933
|
+
scroll: "body",
|
|
57934
|
+
className: "h-[100dvh] max-h-none w-full max-w-full rounded-none border-0 p-0 pt-[env(safe-area-inset-top)] pb-[env(safe-area-inset-bottom)]",
|
|
57935
|
+
children: mobileDetailOpen && detail
|
|
57936
|
+
})
|
|
57937
|
+
})]
|
|
57938
|
+
});
|
|
58081
57939
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
58082
|
-
|
|
58083
|
-
|
|
58084
|
-
|
|
58085
|
-
|
|
58086
|
-
|
|
58087
|
-
|
|
58088
|
-
|
|
58089
|
-
|
|
57940
|
+
ref,
|
|
57941
|
+
className: cn("@container/inspector", className),
|
|
57942
|
+
style: height !== null ? { height } : void 0,
|
|
57943
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57944
|
+
className: cn("grid h-full min-h-0 gap-3", "grid-cols-1 grid-rows-[minmax(9rem,32%)_minmax(0,1fr)]", "@xl/inspector:grid-cols-[minmax(14rem,19rem)_minmax(0,1fr)] @xl/inspector:grid-rows-1"),
|
|
57945
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57946
|
+
className: cn(SECTION_CARD, "flex min-h-0 flex-col"),
|
|
57947
|
+
children: list
|
|
57948
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
57949
|
+
className: cn(SECTION_CARD, "flex min-h-0 flex-col"),
|
|
57950
|
+
children: detail
|
|
57951
|
+
})]
|
|
57952
|
+
})
|
|
57953
|
+
});
|
|
57954
|
+
}
|
|
57955
|
+
/** "‹ Steps" — the phone sheet's way back to the list. */
|
|
57956
|
+
function BackToStepsButton({ onClick }) {
|
|
57957
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
57958
|
+
type: "button",
|
|
57959
|
+
onClick,
|
|
57960
|
+
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",
|
|
57961
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronLeft, {
|
|
57962
|
+
className: "h-3.5 w-3.5",
|
|
57963
|
+
"aria-hidden": true
|
|
57964
|
+
}), "Steps"]
|
|
58090
57965
|
});
|
|
58091
57966
|
}
|
|
57967
|
+
//#endregion
|
|
57968
|
+
//#region src/composites/step-inspector/step-inspector-panel.tsx
|
|
57969
|
+
/**
|
|
57970
|
+
* StepInspectorPanel — the detail pane of the step inspector: a pinned header
|
|
57971
|
+
* (the step, its gate, the accelerator tabs), ONE scrolling body, and a pinned
|
|
57972
|
+
* save bar. Nothing in it depends on the page's scroll position — the slide-over
|
|
57973
|
+
* it replaced was anchored to the matrix block and scrolled away with it.
|
|
57974
|
+
*/
|
|
57975
|
+
function SaveBar({ dirtyCount, saving, status, error, onSave, onDiscard }) {
|
|
57976
|
+
const dirty = dirtyCount > 0;
|
|
57977
|
+
const message = saving ? "Saving…" : dirty ? `${dirtyCount} unsaved change${dirtyCount === 1 ? "" : "s"}` : status === "saved" ? "Saved" : status === "error" ? error ?? "Save failed" : "No unsaved changes";
|
|
57978
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
|
|
57979
|
+
className: "flex shrink-0 items-center gap-2 border-t border-border bg-surface px-4 py-2 pb-[max(0.5rem,env(safe-area-inset-bottom))] md:pb-2",
|
|
57980
|
+
children: [
|
|
57981
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
57982
|
+
role: "status",
|
|
57983
|
+
className: cn("min-w-0 flex-1 truncate text-[11px]", status === "error" && !dirty ? "text-danger" : dirty ? "text-amber-500" : "text-foreground-subtle"),
|
|
57984
|
+
children: message
|
|
57985
|
+
}),
|
|
57986
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
57987
|
+
variant: "outline",
|
|
57988
|
+
size: "md",
|
|
57989
|
+
disabled: !dirty || saving,
|
|
57990
|
+
onClick: onDiscard,
|
|
57991
|
+
children: "Discard"
|
|
57992
|
+
}),
|
|
57993
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
57994
|
+
variant: "primary",
|
|
57995
|
+
size: "md",
|
|
57996
|
+
disabled: !dirty || saving,
|
|
57997
|
+
onClick: onSave,
|
|
57998
|
+
children: "Save"
|
|
57999
|
+
})
|
|
58000
|
+
]
|
|
58001
|
+
});
|
|
58002
|
+
}
|
|
58003
|
+
var StepInspectorPanel = (0, react$1.forwardRef)(function StepInspectorPanel({ title, subtitle, headerActions, headerExtra, leading, saveBar, onEscape, children, className }, headingRef) {
|
|
58004
|
+
const headingId = (0, react$1.useId)();
|
|
58005
|
+
const onKeyDown = (e) => {
|
|
58006
|
+
if (e.key !== "Escape" || onEscape === void 0) return;
|
|
58007
|
+
e.stopPropagation();
|
|
58008
|
+
onEscape();
|
|
58009
|
+
};
|
|
58010
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
58011
|
+
"aria-labelledby": headingId,
|
|
58012
|
+
onKeyDown,
|
|
58013
|
+
className: cn("flex min-h-0 flex-1 flex-col overflow-hidden bg-surface", className),
|
|
58014
|
+
children: [
|
|
58015
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
58016
|
+
className: "shrink-0 space-y-2 border-b border-border px-4 py-2.5",
|
|
58017
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58018
|
+
className: "flex flex-wrap items-start gap-x-3 gap-y-2",
|
|
58019
|
+
children: [
|
|
58020
|
+
leading,
|
|
58021
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58022
|
+
className: "min-w-[9rem] flex-1",
|
|
58023
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
58024
|
+
id: headingId,
|
|
58025
|
+
ref: headingRef,
|
|
58026
|
+
tabIndex: -1,
|
|
58027
|
+
className: "truncate text-sm font-semibold text-foreground outline-none focus-visible:ring-2 focus-visible:ring-primary rounded",
|
|
58028
|
+
children: title
|
|
58029
|
+
}), subtitle !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
58030
|
+
className: "mt-0.5 text-[11px] text-foreground-subtle",
|
|
58031
|
+
children: subtitle
|
|
58032
|
+
})]
|
|
58033
|
+
}),
|
|
58034
|
+
headerActions
|
|
58035
|
+
]
|
|
58036
|
+
}), headerExtra]
|
|
58037
|
+
}),
|
|
58038
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
58039
|
+
"data-inspector-body": true,
|
|
58040
|
+
className: "min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 py-3",
|
|
58041
|
+
children
|
|
58042
|
+
}),
|
|
58043
|
+
saveBar !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SaveBar, { ...saveBar })
|
|
58044
|
+
]
|
|
58045
|
+
});
|
|
58046
|
+
});
|
|
58047
|
+
//#endregion
|
|
58048
|
+
//#region src/composites/step-inspector/step-list.tsx
|
|
58049
|
+
/**
|
|
58050
|
+
* StepList — the master pane of the step inspector: every detection step of the
|
|
58051
|
+
* tree, one compact row each, as ONE tab stop.
|
|
58052
|
+
*
|
|
58053
|
+
* Keyboard (a listbox with a roving tabindex): ↑/↓/Home/End move the selection,
|
|
58054
|
+
* Enter or → hands focus to the inspector, `/` jumps to the search box. The
|
|
58055
|
+
* search matches a step's name or any of its field labels, so a question about
|
|
58056
|
+
* a knob ("face size", "person") finds the step that owns it; Enter in the box
|
|
58057
|
+
* selects the first hit and says which field it matched.
|
|
58058
|
+
*/
|
|
58059
|
+
function matches(text, tokens) {
|
|
58060
|
+
const lower = text.toLowerCase();
|
|
58061
|
+
return tokens.every((t) => lower.includes(t));
|
|
58062
|
+
}
|
|
58063
|
+
/** Rows matching `query` (all tokens in the name, or all in one field label). */
|
|
58064
|
+
function searchSteps(rows, query) {
|
|
58065
|
+
const tokens = query.trim().toLowerCase().split(/\s+/).filter(Boolean);
|
|
58066
|
+
if (tokens.length === 0) return rows.map((row) => ({ row }));
|
|
58067
|
+
const hits = [];
|
|
58068
|
+
for (const row of rows) {
|
|
58069
|
+
if (matches(row.name, tokens)) {
|
|
58070
|
+
hits.push({ row });
|
|
58071
|
+
continue;
|
|
58072
|
+
}
|
|
58073
|
+
const field = row.fieldLabels.find((l) => matches(l, tokens));
|
|
58074
|
+
if (field !== void 0) hits.push({
|
|
58075
|
+
row,
|
|
58076
|
+
matchedField: field
|
|
58077
|
+
});
|
|
58078
|
+
}
|
|
58079
|
+
return hits;
|
|
58080
|
+
}
|
|
58081
|
+
var StepList = (0, react$1.forwardRef)(function StepList({ rows, selectedId, onSelect, onActivate, onPick, label, className }, ref) {
|
|
58082
|
+
const [query, setQuery] = (0, react$1.useState)("");
|
|
58083
|
+
const listRef = (0, react$1.useRef)(null);
|
|
58084
|
+
const searchRef = (0, react$1.useRef)(null);
|
|
58085
|
+
const hits = (0, react$1.useMemo)(() => searchSteps(rows, query), [rows, query]);
|
|
58086
|
+
const tabStopId = hits.some((h) => h.row.addonId === selectedId) ? selectedId : hits[0]?.row.addonId ?? null;
|
|
58087
|
+
const pendingFocus = (0, react$1.useRef)(null);
|
|
58088
|
+
const tryFocus = (addonId) => {
|
|
58089
|
+
const el = listRef.current?.querySelector(`[data-step-id="${addonId}"]`);
|
|
58090
|
+
if (!el) return false;
|
|
58091
|
+
el.focus();
|
|
58092
|
+
return true;
|
|
58093
|
+
};
|
|
58094
|
+
const focusRow = (addonId) => {
|
|
58095
|
+
if (addonId === null) return;
|
|
58096
|
+
pendingFocus.current = tryFocus(addonId) ? null : addonId;
|
|
58097
|
+
};
|
|
58098
|
+
(0, react$1.useLayoutEffect)(() => {
|
|
58099
|
+
if (pendingFocus.current !== null && tryFocus(pendingFocus.current)) pendingFocus.current = null;
|
|
58100
|
+
});
|
|
58101
|
+
(0, react$1.useImperativeHandle)(ref, () => ({ focusSelected: () => focusRow(selectedId) }), [selectedId]);
|
|
58102
|
+
const move = (index) => {
|
|
58103
|
+
const target = hits[Math.max(0, Math.min(hits.length - 1, index))];
|
|
58104
|
+
if (target === void 0) return;
|
|
58105
|
+
onSelect(target.row.addonId, target.matchedField);
|
|
58106
|
+
focusRow(target.row.addonId);
|
|
58107
|
+
};
|
|
58108
|
+
const onRowKeyDown = (e, index, hit) => {
|
|
58109
|
+
switch (e.key) {
|
|
58110
|
+
case "ArrowDown":
|
|
58111
|
+
e.preventDefault();
|
|
58112
|
+
move(index + 1);
|
|
58113
|
+
return;
|
|
58114
|
+
case "ArrowUp":
|
|
58115
|
+
e.preventDefault();
|
|
58116
|
+
move(index - 1);
|
|
58117
|
+
return;
|
|
58118
|
+
case "Home":
|
|
58119
|
+
e.preventDefault();
|
|
58120
|
+
move(0);
|
|
58121
|
+
return;
|
|
58122
|
+
case "End":
|
|
58123
|
+
e.preventDefault();
|
|
58124
|
+
move(hits.length - 1);
|
|
58125
|
+
return;
|
|
58126
|
+
case "Enter":
|
|
58127
|
+
case "ArrowRight":
|
|
58128
|
+
e.preventDefault();
|
|
58129
|
+
onActivate(hit.row.addonId);
|
|
58130
|
+
return;
|
|
58131
|
+
case "/":
|
|
58132
|
+
e.preventDefault();
|
|
58133
|
+
searchRef.current?.focus();
|
|
58134
|
+
return;
|
|
58135
|
+
default:
|
|
58136
|
+
}
|
|
58137
|
+
};
|
|
58138
|
+
const onSearchKeyDown = (e) => {
|
|
58139
|
+
if (e.key === "Enter") {
|
|
58140
|
+
e.preventDefault();
|
|
58141
|
+
const first = hits[0];
|
|
58142
|
+
if (first !== void 0) {
|
|
58143
|
+
onSelect(first.row.addonId, first.matchedField);
|
|
58144
|
+
onPick?.(first.row.addonId);
|
|
58145
|
+
focusRow(first.row.addonId);
|
|
58146
|
+
}
|
|
58147
|
+
} else if (e.key === "Escape") {
|
|
58148
|
+
e.preventDefault();
|
|
58149
|
+
setQuery("");
|
|
58150
|
+
focusRow(selectedId);
|
|
58151
|
+
} else if (e.key === "ArrowDown") {
|
|
58152
|
+
e.preventDefault();
|
|
58153
|
+
focusRow(tabStopId);
|
|
58154
|
+
}
|
|
58155
|
+
};
|
|
58156
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58157
|
+
className: cn("flex min-h-0 flex-col", className),
|
|
58158
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58159
|
+
className: "relative shrink-0 border-b border-border p-1.5",
|
|
58160
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Search, {
|
|
58161
|
+
className: "pointer-events-none absolute left-3.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-foreground-subtle",
|
|
58162
|
+
"aria-hidden": true
|
|
58163
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
58164
|
+
ref: searchRef,
|
|
58165
|
+
type: "search",
|
|
58166
|
+
value: query,
|
|
58167
|
+
onChange: (e) => setQuery(e.target.value),
|
|
58168
|
+
onKeyDown: onSearchKeyDown,
|
|
58169
|
+
placeholder: "Find a step or setting /",
|
|
58170
|
+
"aria-label": "Find a step or setting",
|
|
58171
|
+
className: "h-9 w-full rounded-md border border-border bg-background pl-8 pr-2 text-xs text-foreground outline-none focus:border-primary focus:ring-1 focus:ring-primary/30 md:h-8"
|
|
58172
|
+
})]
|
|
58173
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
|
|
58174
|
+
ref: listRef,
|
|
58175
|
+
role: "listbox",
|
|
58176
|
+
"aria-label": label,
|
|
58177
|
+
className: "min-h-0 flex-1 overflow-y-auto py-1",
|
|
58178
|
+
children: [hits.map((hit, index) => {
|
|
58179
|
+
const { row } = hit;
|
|
58180
|
+
const selected = row.addonId === selectedId;
|
|
58181
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
58182
|
+
role: "option",
|
|
58183
|
+
"aria-selected": selected,
|
|
58184
|
+
tabIndex: row.addonId === tabStopId ? 0 : -1,
|
|
58185
|
+
"data-step-id": row.addonId,
|
|
58186
|
+
onClick: () => {
|
|
58187
|
+
onSelect(row.addonId, hit.matchedField);
|
|
58188
|
+
onPick?.(row.addonId);
|
|
58189
|
+
},
|
|
58190
|
+
onKeyDown: (e) => onRowKeyDown(e, index, hit),
|
|
58191
|
+
className: cn("cursor-pointer border-l-2 py-1 pl-2.5 pr-3 outline-none transition-colors", "focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary", selected ? "border-primary bg-primary/10" : "border-transparent hover:bg-muted/40"),
|
|
58192
|
+
children: [
|
|
58193
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58194
|
+
className: "flex min-w-0 items-baseline gap-2",
|
|
58195
|
+
children: [
|
|
58196
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58197
|
+
className: "w-[4.5rem] shrink-0 truncate",
|
|
58198
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SlotHeading, { slot: row.slot })
|
|
58199
|
+
}),
|
|
58200
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
58201
|
+
className: cn("min-w-0 flex-1 truncate text-xs font-medium", selected ? "text-foreground" : "text-foreground/90"),
|
|
58202
|
+
style: { paddingLeft: `${row.depth * 12}px` },
|
|
58203
|
+
children: [row.depth > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58204
|
+
className: "mr-1 text-foreground-subtle/60",
|
|
58205
|
+
"aria-hidden": true,
|
|
58206
|
+
children: "└"
|
|
58207
|
+
}), row.name]
|
|
58208
|
+
}),
|
|
58209
|
+
row.status
|
|
58210
|
+
]
|
|
58211
|
+
}),
|
|
58212
|
+
hit.matchedField !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58213
|
+
className: "mt-0.5 truncate pl-[5rem] text-[10px] text-primary",
|
|
58214
|
+
children: ["↳ ", hit.matchedField]
|
|
58215
|
+
}),
|
|
58216
|
+
row.detail !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
58217
|
+
className: "mt-0.5 pl-[5rem]",
|
|
58218
|
+
children: row.detail
|
|
58219
|
+
})
|
|
58220
|
+
]
|
|
58221
|
+
}, row.addonId);
|
|
58222
|
+
}), hits.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
58223
|
+
className: "px-3 py-4 text-center text-[11px] text-foreground-subtle",
|
|
58224
|
+
children: [
|
|
58225
|
+
"No step matches “",
|
|
58226
|
+
query,
|
|
58227
|
+
"”."
|
|
58228
|
+
]
|
|
58229
|
+
})]
|
|
58230
|
+
})]
|
|
58231
|
+
});
|
|
58232
|
+
});
|
|
58233
|
+
//#endregion
|
|
58234
|
+
//#region src/composites/step-inspector/step-model-row.tsx
|
|
58235
|
+
var SOURCE_TONE = {
|
|
58236
|
+
camera: "border-primary/40 bg-primary/10 text-primary",
|
|
58237
|
+
node: "border-border bg-muted/40 text-foreground",
|
|
58238
|
+
default: "border-border/60 text-foreground-subtle",
|
|
58239
|
+
cluster: "border-primary/40 bg-primary/5 text-primary"
|
|
58240
|
+
};
|
|
58241
|
+
function StepModelRow({ label, format, modelId, source, sourceLabel, models, defaultModelId, expanded, onToggle, onChangeModel, onReset, resetLabel = "Reset", note, extra, changed = false, disabled = false }) {
|
|
58242
|
+
const pickable = models.length > 0 && note === void 0;
|
|
58243
|
+
const canExpand = pickable || extra !== void 0;
|
|
58244
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58245
|
+
className: cn("rounded-md border border-border", expanded && "bg-muted/10"),
|
|
58246
|
+
children: [
|
|
58247
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58248
|
+
className: cn(LIST_ROW, "space-y-1"),
|
|
58249
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58250
|
+
className: "flex flex-wrap items-center gap-x-2 gap-y-1 text-xs",
|
|
58251
|
+
children: [
|
|
58252
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58253
|
+
className: "font-medium text-foreground",
|
|
58254
|
+
children: label
|
|
58255
|
+
}),
|
|
58256
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58257
|
+
className: "text-[10px] text-foreground-subtle",
|
|
58258
|
+
children: format
|
|
58259
|
+
}),
|
|
58260
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58261
|
+
className: cn("ml-auto rounded border px-1.5 py-px text-[10px] font-medium", SOURCE_TONE[source]),
|
|
58262
|
+
children: sourceLabel
|
|
58263
|
+
})
|
|
58264
|
+
]
|
|
58265
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58266
|
+
className: "flex items-center gap-2",
|
|
58267
|
+
children: [
|
|
58268
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
58269
|
+
className: "min-w-0 flex-1 truncate font-mono text-xs text-foreground",
|
|
58270
|
+
children: [modelId ?? "—", changed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58271
|
+
className: "ml-1 text-amber-500",
|
|
58272
|
+
children: "•"
|
|
58273
|
+
})]
|
|
58274
|
+
}),
|
|
58275
|
+
onReset !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
58276
|
+
type: "button",
|
|
58277
|
+
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",
|
|
58278
|
+
disabled,
|
|
58279
|
+
onClick: onReset,
|
|
58280
|
+
children: resetLabel
|
|
58281
|
+
}),
|
|
58282
|
+
canExpand && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
58283
|
+
type: "button",
|
|
58284
|
+
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"),
|
|
58285
|
+
"aria-expanded": expanded,
|
|
58286
|
+
"aria-label": `Change model for ${label}`,
|
|
58287
|
+
disabled,
|
|
58288
|
+
onClick: onToggle,
|
|
58289
|
+
children: expanded ? "Done" : pickable ? "Change" : "Route"
|
|
58290
|
+
})
|
|
58291
|
+
]
|
|
58292
|
+
})]
|
|
58293
|
+
}),
|
|
58294
|
+
note !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
58295
|
+
className: "border-t border-border/60 px-3 py-1.5 text-[10px] leading-snug text-foreground-subtle",
|
|
58296
|
+
children: note
|
|
58297
|
+
}),
|
|
58298
|
+
expanded && canExpand && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58299
|
+
className: "space-y-3 border-t border-border/60 px-3 py-3",
|
|
58300
|
+
children: [pickable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelPicker, {
|
|
58301
|
+
models,
|
|
58302
|
+
value: modelId ?? "",
|
|
58303
|
+
defaultModelId,
|
|
58304
|
+
disabled,
|
|
58305
|
+
onChange: onChangeModel
|
|
58306
|
+
}), extra]
|
|
58307
|
+
})
|
|
58308
|
+
]
|
|
58309
|
+
});
|
|
58310
|
+
}
|
|
58311
|
+
//#endregion
|
|
58312
|
+
//#region src/composites/step-inspector/step-model-list.tsx
|
|
58313
|
+
/**
|
|
58314
|
+
* StepModelList — the model section of the step inspector.
|
|
58315
|
+
*
|
|
58316
|
+
* While nothing is pinned, edited or worth a note, the accelerators are
|
|
58317
|
+
* summarised by model — one line per distinct model, naming the accelerators
|
|
58318
|
+
* that run it ("yolov9m-320-int8 · hub iGPU, hub NPU, little-unraid iGPU") —
|
|
58319
|
+
* with "Per accelerator" to open one `StepModelRow` each. Four near-identical
|
|
58320
|
+
* rows pushed the settings, the thing most edits are about, below the fold.
|
|
58321
|
+
* A pin, a pending change or a note (no build here, root-supplied) shows every
|
|
58322
|
+
* row, so nothing that differs from the default is ever folded away.
|
|
58323
|
+
*/
|
|
58324
|
+
/** True when the rows can be summarised: nothing pinned here, edited, or noted. */
|
|
58325
|
+
function modelRowsAreFoldable(rows) {
|
|
58326
|
+
return rows.length >= 2 && rows.every((r) => r.note === void 0 && r.onReset === void 0 && r.changed !== true && !r.expanded);
|
|
58327
|
+
}
|
|
58328
|
+
function groupByModel(rows) {
|
|
58329
|
+
const groups = [];
|
|
58330
|
+
for (const row of rows) {
|
|
58331
|
+
const modelId = row.modelId ?? "—";
|
|
58332
|
+
const index = groups.findIndex((g) => g.modelId === modelId && g.sourceLabel === row.sourceLabel);
|
|
58333
|
+
const existing = groups[index];
|
|
58334
|
+
if (existing !== void 0) groups[index] = {
|
|
58335
|
+
...existing,
|
|
58336
|
+
labels: [...existing.labels, row.label]
|
|
58337
|
+
};
|
|
58338
|
+
else groups.push({
|
|
58339
|
+
modelId,
|
|
58340
|
+
labels: [row.label],
|
|
58341
|
+
sourceLabel: row.sourceLabel
|
|
58342
|
+
});
|
|
58343
|
+
}
|
|
58344
|
+
return groups;
|
|
58345
|
+
}
|
|
58346
|
+
function StepModelList({ rows }) {
|
|
58347
|
+
const [open, setOpen] = (0, react$1.useState)(false);
|
|
58348
|
+
if (!open && modelRowsAreFoldable(rows)) {
|
|
58349
|
+
const groups = groupByModel(rows);
|
|
58350
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
58351
|
+
className: cn("rounded-md border border-border", LIST_ROW),
|
|
58352
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
58353
|
+
className: "space-y-1",
|
|
58354
|
+
children: groups.map((g) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
58355
|
+
className: "flex flex-wrap items-baseline gap-x-2 text-xs",
|
|
58356
|
+
children: [
|
|
58357
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58358
|
+
className: "font-mono text-foreground",
|
|
58359
|
+
children: g.modelId
|
|
58360
|
+
}),
|
|
58361
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58362
|
+
className: "min-w-0 flex-1 text-[11px] text-foreground-subtle",
|
|
58363
|
+
children: g.labels.length === rows.length ? `all ${rows.length} accelerators` : g.labels.join(", ")
|
|
58364
|
+
}),
|
|
58365
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58366
|
+
className: "rounded border border-border px-1.5 py-px text-[10px] font-medium text-foreground-subtle",
|
|
58367
|
+
children: g.sourceLabel
|
|
58368
|
+
})
|
|
58369
|
+
]
|
|
58370
|
+
}, `${g.modelId}|${g.sourceLabel}`))
|
|
58371
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
58372
|
+
className: "mt-1.5 flex justify-end",
|
|
58373
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
58374
|
+
type: "button",
|
|
58375
|
+
className: cn(BTN_COMPACT, "min-h-9 md:min-h-0"),
|
|
58376
|
+
"aria-expanded": false,
|
|
58377
|
+
onClick: () => setOpen(true),
|
|
58378
|
+
children: "Per accelerator"
|
|
58379
|
+
})
|
|
58380
|
+
})]
|
|
58381
|
+
});
|
|
58382
|
+
}
|
|
58383
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
58384
|
+
className: "space-y-2",
|
|
58385
|
+
children: rows.map(({ id, ...row }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepModelRow, { ...row }, id))
|
|
58386
|
+
});
|
|
58387
|
+
}
|
|
58388
|
+
//#endregion
|
|
58389
|
+
//#region src/composites/step-tree.ts
|
|
58092
58390
|
/**
|
|
58093
58391
|
* Helper: build a StepTreeNode[] from a `PipelineSchema` by chaining on
|
|
58094
58392
|
* `inputClasses` ⇔ `outputClasses` compatibility. Mirrors the orchestrator
|
|
@@ -61033,6 +61331,7 @@ var SceneMonitorEditor = (0, react$1.lazy)(() => Promise.resolve().then(() => re
|
|
|
61033
61331
|
/** Lazy-wrapped `PrivacyMaskSettings` — code-split off the main bundle. */
|
|
61034
61332
|
var PrivacyMaskSettings = (0, react$1.lazy)(() => Promise.resolve().then(() => require("./PrivacyMaskSettings-BqjXsXZR.cjs")).then((m) => ({ default: m.PrivacyMaskSettings })));
|
|
61035
61333
|
//#endregion
|
|
61334
|
+
exports.AcceleratorTabs = AcceleratorTabs;
|
|
61036
61335
|
exports.AddonGlobalSettingsForm = AddonGlobalSettingsForm;
|
|
61037
61336
|
exports.AdminLinkQuickAction = AdminLinkQuickAction;
|
|
61038
61337
|
exports.AgentStepEditor = AgentStepEditor;
|
|
@@ -61050,6 +61349,7 @@ exports.BTN_COMPACT = BTN_COMPACT;
|
|
|
61050
61349
|
exports.BTN_COMPACT_DANGER = BTN_COMPACT_DANGER;
|
|
61051
61350
|
exports.BTN_COMPACT_PRIMARY = BTN_COMPACT_PRIMARY;
|
|
61052
61351
|
exports.BTN_COMPACT_WARNING = BTN_COMPACT_WARNING;
|
|
61352
|
+
exports.BackToStepsButton = BackToStepsButton;
|
|
61053
61353
|
exports.Badge = Badge;
|
|
61054
61354
|
exports.BatteryBadge = BatteryBadge;
|
|
61055
61355
|
exports.BottomSheet = BottomSheet;
|
|
@@ -61171,6 +61471,8 @@ exports.FormField = FormField$1;
|
|
|
61171
61471
|
exports.GRID_GAP = GRID_GAP;
|
|
61172
61472
|
exports.GRID_PAIRED = GRID_PAIRED;
|
|
61173
61473
|
exports.GRID_QUICK_STATS = GRID_QUICK_STATS;
|
|
61474
|
+
exports.GateBadge = GateBadge;
|
|
61475
|
+
exports.GateControl = GateControl;
|
|
61174
61476
|
exports.Grid2x2 = Grid2x2;
|
|
61175
61477
|
exports.GripTrack = GripTrack;
|
|
61176
61478
|
exports.GroupedModelSelector = GroupedModelSelector;
|
|
@@ -61198,6 +61500,7 @@ exports.LIST_ROW = LIST_ROW;
|
|
|
61198
61500
|
exports.Label = Label;
|
|
61199
61501
|
exports.LawnMowerHeroCard = LawnMowerHeroCard;
|
|
61200
61502
|
exports.LawnMowerInlineControl = LawnMowerInlineControl;
|
|
61503
|
+
exports.LayeredSettingsForm = LayeredSettingsForm;
|
|
61201
61504
|
exports.LightHeroCard = LightHeroCard;
|
|
61202
61505
|
exports.LightInlineControl = LightInlineControl;
|
|
61203
61506
|
exports.LockHeroCard = LockHeroCard;
|
|
@@ -61245,7 +61548,6 @@ exports.PhaseIcon = PhaseIcon;
|
|
|
61245
61548
|
exports.PipelineBuilder = PipelineBuilder;
|
|
61246
61549
|
exports.PipelineRuntimeSelector = PipelineRuntimeSelector;
|
|
61247
61550
|
exports.PipelineStep = PipelineStep;
|
|
61248
|
-
exports.PipelineTreeMatrix = PipelineTreeMatrix;
|
|
61249
61551
|
exports.PlaceholderChipField = PlaceholderChipField;
|
|
61250
61552
|
exports.PlayerOverlaysProvider = PlayerOverlaysProvider;
|
|
61251
61553
|
exports.Popover = Popover;
|
|
@@ -61268,6 +61570,8 @@ exports.RecordingPanel = RecordingPanel;
|
|
|
61268
61570
|
exports.ResponseLog = ResponseLog;
|
|
61269
61571
|
exports.RoleDisplayDefaultsFromServer = RoleDisplayDefaultsFromServer;
|
|
61270
61572
|
exports.RoleDisplayDefaultsProvider = RoleDisplayDefaultsProvider;
|
|
61573
|
+
exports.RootModelSourceControl = RootModelSourceControl;
|
|
61574
|
+
exports.RootSuppliedModelNote = RootSuppliedModelNote;
|
|
61271
61575
|
exports.RotateCcw = RotateCcw;
|
|
61272
61576
|
exports.RunReport = RunReport;
|
|
61273
61577
|
exports.SCENE_CAPTURE_VARIANTS = SCENE_CAPTURE_VARIANTS;
|
|
@@ -61291,6 +61595,7 @@ exports.ScriptHeroCard = ScriptHeroCard;
|
|
|
61291
61595
|
exports.ScriptInlineControl = ScriptInlineControl;
|
|
61292
61596
|
exports.ScrollArea = ScrollArea;
|
|
61293
61597
|
exports.SectionCard = SectionCard;
|
|
61598
|
+
exports.SegmentedChoice = SegmentedChoice;
|
|
61294
61599
|
exports.Select = Select;
|
|
61295
61600
|
exports.SelectedChip = SelectedChip;
|
|
61296
61601
|
exports.SemanticBadge = SemanticBadge;
|
|
@@ -61302,7 +61607,6 @@ exports.SettingRow = SettingRow;
|
|
|
61302
61607
|
exports.Sidebar = Sidebar;
|
|
61303
61608
|
exports.SidebarItem = SidebarItem;
|
|
61304
61609
|
exports.Skeleton = Skeleton;
|
|
61305
|
-
exports.SlideOverPanel = SlideOverPanel;
|
|
61306
61610
|
exports.SlideToggle = SlideToggle;
|
|
61307
61611
|
exports.SnapshotButton = SnapshotButton;
|
|
61308
61612
|
exports.SortHeaderButton = SortHeaderButton;
|
|
@@ -61310,8 +61614,13 @@ exports.Square = Square;
|
|
|
61310
61614
|
exports.StatCard = StatCard;
|
|
61311
61615
|
exports.StateValuesStream = StateValuesStream;
|
|
61312
61616
|
exports.StatusBadge = StatusBadge;
|
|
61617
|
+
exports.StepChips = StepChips;
|
|
61618
|
+
exports.StepInspectorLayout = StepInspectorLayout;
|
|
61619
|
+
exports.StepInspectorPanel = StepInspectorPanel;
|
|
61620
|
+
exports.StepList = StepList;
|
|
61621
|
+
exports.StepModelList = StepModelList;
|
|
61622
|
+
exports.StepModelRow = StepModelRow;
|
|
61313
61623
|
exports.StepTimings = StepTimings;
|
|
61314
|
-
exports.StepTreeMaster = StepTreeMaster;
|
|
61315
61624
|
exports.Stepper = Stepper;
|
|
61316
61625
|
exports.StreamBrokerSelector = StreamBrokerSelector;
|
|
61317
61626
|
exports.StreamDebugOverlay = StreamDebugOverlay;
|
|
@@ -61365,6 +61674,7 @@ exports.applyPickerFilters = applyPickerFilters;
|
|
|
61365
61674
|
exports.ariaSortForColumn = ariaSortForColumn;
|
|
61366
61675
|
exports.badgeFor = badgeFor;
|
|
61367
61676
|
exports.badgeFor$1 = badgeFor;
|
|
61677
|
+
exports.bindsToModel = bindsToModel;
|
|
61368
61678
|
exports.buildPeerConnectionConfig = buildPeerConnectionConfig;
|
|
61369
61679
|
exports.buildStepTreeFromSchema = buildStepTreeFromSchema;
|
|
61370
61680
|
exports.catalogHasDraft = catalogHasDraft;
|
|
@@ -61389,6 +61699,7 @@ exports.createTheme = require_theme_index.createTheme$1;
|
|
|
61389
61699
|
exports.cursorFractionFor = cursorFractionFor;
|
|
61390
61700
|
exports.darkColors = require_theme_index.darkColors$1;
|
|
61391
61701
|
exports.dataTableFacets = dataTableFacets;
|
|
61702
|
+
exports.defaultOfField = defaultOfField;
|
|
61392
61703
|
exports.defaultTheme = require_theme_index.defaultTheme$1;
|
|
61393
61704
|
exports.deriveDeviceKind = deriveDeviceKind;
|
|
61394
61705
|
exports.deriveWebrtcDebugStats = deriveWebrtcDebugStats;
|
|
@@ -61408,6 +61719,7 @@ exports.emptyDevicesWidens = emptyDevicesWidens;
|
|
|
61408
61719
|
exports.emptySessionRun = emptySessionRun;
|
|
61409
61720
|
exports.ensureMfHostInit = ensureMfHostInit;
|
|
61410
61721
|
exports.eventKindLabel = eventKindLabel;
|
|
61722
|
+
exports.fillHeightPx = fillHeightPx;
|
|
61411
61723
|
exports.filterDataTableRows = filterDataTableRows;
|
|
61412
61724
|
exports.filterDeviceOptions = filterDeviceOptions;
|
|
61413
61725
|
exports.filterOptionSelectOptions = filterOptions;
|
|
@@ -61420,6 +61732,7 @@ exports.fleetViewOf = fleetViewOf;
|
|
|
61420
61732
|
exports.formatControlDateTime = formatControlDateTime;
|
|
61421
61733
|
exports.formatLastSeen = formatLastSeen;
|
|
61422
61734
|
exports.formatNumeric = formatNumeric;
|
|
61735
|
+
exports.formatSettingValue = formatSettingValue;
|
|
61423
61736
|
exports.freezeVerdict = freezeVerdict;
|
|
61424
61737
|
exports.fuzzyMatch = fuzzyMatch;
|
|
61425
61738
|
exports.getClassColor = getClassColor;
|
|
@@ -61443,6 +61756,7 @@ exports.isAbsentProvider$1 = isAbsentProvider;
|
|
|
61443
61756
|
exports.isDataTableFilterActive = isDataTableFilterActive;
|
|
61444
61757
|
exports.isFieldVisible = isFieldVisible;
|
|
61445
61758
|
exports.isGroupExpanded = isGroupExpanded;
|
|
61759
|
+
exports.isValueField = isValueField;
|
|
61446
61760
|
exports.lawnMowerActivityMeta = lawnMowerActivityMeta;
|
|
61447
61761
|
exports.lightColors = require_theme_index.lightColors$1;
|
|
61448
61762
|
exports.loadRemoteBundle = loadRemoteBundle;
|
|
@@ -61450,6 +61764,7 @@ exports.makeScrubBridge = makeScrubBridge;
|
|
|
61450
61764
|
exports.metadataEntries = metadataEntries;
|
|
61451
61765
|
exports.metadataString = metadataString;
|
|
61452
61766
|
exports.mirror = mirror;
|
|
61767
|
+
exports.modelRowsAreFoldable = modelRowsAreFoldable;
|
|
61453
61768
|
exports.mountAddonPage = mountAddonPage;
|
|
61454
61769
|
exports.mountPath = mountPath;
|
|
61455
61770
|
exports.nextReconnectAction = nextReconnectAction;
|
|
@@ -61507,9 +61822,11 @@ exports.rootSourceState = rootSourceState;
|
|
|
61507
61822
|
exports.routerBasename = routerBasename;
|
|
61508
61823
|
exports.sameLocationSuggestions = sameLocationSuggestions;
|
|
61509
61824
|
exports.scrubReducer = scrubReducer;
|
|
61825
|
+
exports.searchSteps = searchSteps;
|
|
61510
61826
|
exports.selectPickerGroup = selectPickerGroup;
|
|
61511
61827
|
exports.selectedDeviceOptions = selectedDeviceOptions;
|
|
61512
61828
|
exports.serializeRecordedCommand = serializeRecordedCommand;
|
|
61829
|
+
exports.settingsModelIdFor = settingsModelIdFor;
|
|
61513
61830
|
exports.shouldCommit = shouldCommit;
|
|
61514
61831
|
exports.shouldEmit = shouldEmit;
|
|
61515
61832
|
exports.shouldShowAddNewFooter = shouldShowAddNewFooter;
|
|
@@ -61521,7 +61838,9 @@ exports.sortRowsByColumn = sortRowsByColumn;
|
|
|
61521
61838
|
exports.statusIcons = statusIcons;
|
|
61522
61839
|
exports.statusLine = statusLine;
|
|
61523
61840
|
exports.statusLine$1 = statusLine;
|
|
61841
|
+
exports.stepFieldDomId = stepFieldDomId;
|
|
61524
61842
|
exports.stepHasModelForFormat = stepHasModelForFormat;
|
|
61843
|
+
exports.stepLocalFields = stepLocalFields;
|
|
61525
61844
|
exports.stepModelOptions = stepModelOptions;
|
|
61526
61845
|
exports.stripParentNamePrefix = stripParentNamePrefix;
|
|
61527
61846
|
exports.summarizeHubCandidates = summarizeHubCandidates;
|
|
@@ -62737,4 +63056,5 @@ exports.waterHeaterTint = waterHeaterTint;
|
|
|
62737
63056
|
exports.weatherConditionMeta = weatherConditionMeta;
|
|
62738
63057
|
exports.weatherTint = weatherTint;
|
|
62739
63058
|
exports.withResolvedParentDetail = withResolvedParentDetail;
|
|
63059
|
+
exports.withUseRootModel = withUseRootModel;
|
|
62740
63060
|
exports.writeStoredGroupBy = writeStoredGroupBy;
|