@camstack/ui-library 1.2.238 → 1.2.239
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/model-bound-slider.d.ts +28 -0
- package/dist/index.cjs +150 -12
- package/dist/index.js +151 -13
- package/package.json +1 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ConfigField, ConfigNumberField, ConfigSliderField } from '@camstack/types';
|
|
2
|
+
/** A numeric field this control can render. */
|
|
3
|
+
export type ModelBoundNumericField = ConfigSliderField | ConfigNumberField;
|
|
4
|
+
export interface ModelBoundSliderProps {
|
|
5
|
+
readonly field: ModelBoundNumericField;
|
|
6
|
+
/** The model the edited row runs — pinned, or its effective default. */
|
|
7
|
+
readonly modelId: string;
|
|
8
|
+
readonly settings: Readonly<Record<string, unknown>> | undefined;
|
|
9
|
+
readonly disabled: boolean;
|
|
10
|
+
readonly onChangeSettings: (next: Record<string, unknown>) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function ModelBoundSlider({ field, modelId, settings, disabled, onChangeSettings, }: ModelBoundSliderProps): import("react").JSX.Element;
|
|
13
|
+
/** The keys of the model-bound fields of a schema, groups flattened. */
|
|
14
|
+
export declare function modelBoundKeysOf(fields: readonly ConfigField[]): string[];
|
|
15
|
+
export interface ResetModelDefaultsButtonProps {
|
|
16
|
+
readonly fields: readonly ConfigField[];
|
|
17
|
+
/** The model the edited row runs — pinned, or its effective default. */
|
|
18
|
+
readonly modelId: string;
|
|
19
|
+
readonly settings: Readonly<Record<string, unknown>> | undefined;
|
|
20
|
+
readonly disabled?: boolean;
|
|
21
|
+
readonly onReset: (next: Record<string, unknown>) => void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* "Reset thresholds to model defaults" — every model-bound override in force
|
|
25
|
+
* for `modelId`, cleared in ONE settings change (one save). Rendered only when
|
|
26
|
+
* there is something to reset. An override stored for another model is kept.
|
|
27
|
+
*/
|
|
28
|
+
export declare function ResetModelDefaultsButton({ fields, modelId, settings, disabled, onReset, }: ResetModelDefaultsButtonProps): import("react").JSX.Element | null;
|
package/dist/index.cjs
CHANGED
|
@@ -28970,6 +28970,106 @@ function ModelPicker({ models, value, onChange, disabled, defaultModelId }) {
|
|
|
28970
28970
|
});
|
|
28971
28971
|
}
|
|
28972
28972
|
//#endregion
|
|
28973
|
+
//#region src/composites/model-bound-slider.tsx
|
|
28974
|
+
var SOURCE_LABEL = {
|
|
28975
|
+
override: "Override for this model",
|
|
28976
|
+
"model-default": "Model default",
|
|
28977
|
+
"global-default": "Default — not tuned for this model"
|
|
28978
|
+
};
|
|
28979
|
+
function formatNumber(value, scale) {
|
|
28980
|
+
return (scale !== void 0 ? value / scale : value).toFixed(2).replace(/\.?0+$/, "");
|
|
28981
|
+
}
|
|
28982
|
+
function ModelBoundSlider({ field, modelId, settings, disabled, onChangeSettings }) {
|
|
28983
|
+
const modelDefault = field.modelApplicability?.defaultByModel?.[modelId];
|
|
28984
|
+
const state = (0, _camstack_types.resolveModelBoundSetting)(settings, field.key, modelId.length > 0 ? modelId : void 0, modelDefault, field.default);
|
|
28985
|
+
const fallback = typeof field.default === "number" ? field.default : field.min ?? 0;
|
|
28986
|
+
const current = typeof state.value === "number" ? state.value : fallback;
|
|
28987
|
+
const scale = field.type === "slider" ? field.displayScale : void 0;
|
|
28988
|
+
const unit = "unit" in field ? field.unit : void 0;
|
|
28989
|
+
const stale = state.inapplicableOverride;
|
|
28990
|
+
const hasStoredValue = settings?.[field.key] !== void 0;
|
|
28991
|
+
const handleChange = (value) => {
|
|
28992
|
+
onChangeSettings(modelId.length > 0 ? (0, _camstack_types.writeModelBoundSetting)(settings, field.key, value, modelId) : {
|
|
28993
|
+
...settings,
|
|
28994
|
+
[field.key]: value
|
|
28995
|
+
});
|
|
28996
|
+
};
|
|
28997
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
28998
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
28999
|
+
className: "flex items-center justify-between mb-1",
|
|
29000
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29001
|
+
className: "text-[10px] font-medium text-foreground-subtle uppercase tracking-wide",
|
|
29002
|
+
children: field.label
|
|
29003
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
29004
|
+
className: "text-xs font-medium text-foreground tabular-nums",
|
|
29005
|
+
children: [formatNumber(current, scale), unit ? ` ${unit}` : ""]
|
|
29006
|
+
})]
|
|
29007
|
+
}),
|
|
29008
|
+
field.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
29009
|
+
className: "text-[10px] text-foreground-subtle/60 mb-1.5",
|
|
29010
|
+
children: field.description
|
|
29011
|
+
}),
|
|
29012
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
29013
|
+
type: "range",
|
|
29014
|
+
min: field.min ?? 0,
|
|
29015
|
+
max: field.max ?? 1,
|
|
29016
|
+
step: field.step ?? .01,
|
|
29017
|
+
value: current,
|
|
29018
|
+
disabled: disabled || field.disabled,
|
|
29019
|
+
onChange: (e) => handleChange(Number(e.target.value)),
|
|
29020
|
+
className: "w-full accent-primary h-1.5"
|
|
29021
|
+
}),
|
|
29022
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29023
|
+
className: "mt-1 flex items-center justify-between gap-2 text-[10px]",
|
|
29024
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29025
|
+
"data-testid": "model-bound-source",
|
|
29026
|
+
className: state.source === "override" ? "text-primary font-medium" : "text-foreground-subtle",
|
|
29027
|
+
children: SOURCE_LABEL[state.source]
|
|
29028
|
+
}), hasStoredValue && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
29029
|
+
type: "button",
|
|
29030
|
+
disabled,
|
|
29031
|
+
className: "text-foreground-subtle hover:text-foreground disabled:opacity-50",
|
|
29032
|
+
onClick: () => onChangeSettings((0, _camstack_types.clearModelBoundSetting)(settings, field.key)),
|
|
29033
|
+
children: "Reset to model default"
|
|
29034
|
+
})]
|
|
29035
|
+
}),
|
|
29036
|
+
stale !== void 0 && typeof stale.value === "number" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
29037
|
+
className: "mt-1 text-[10px] text-amber-400/90",
|
|
29038
|
+
children: [
|
|
29039
|
+
"The override ",
|
|
29040
|
+
formatNumber(stale.value, scale),
|
|
29041
|
+
" was set for ",
|
|
29042
|
+
stale.modelId,
|
|
29043
|
+
" and does not apply to this model."
|
|
29044
|
+
]
|
|
29045
|
+
})
|
|
29046
|
+
] });
|
|
29047
|
+
}
|
|
29048
|
+
/** The keys of the model-bound fields of a schema, groups flattened. */
|
|
29049
|
+
function modelBoundKeysOf(fields) {
|
|
29050
|
+
return fields.flatMap((field) => {
|
|
29051
|
+
if (field.type === "group") return modelBoundKeysOf(field.fields);
|
|
29052
|
+
return "key" in field && "bindsToModel" in field && field.bindsToModel === true ? [field.key] : [];
|
|
29053
|
+
});
|
|
29054
|
+
}
|
|
29055
|
+
/**
|
|
29056
|
+
* "Reset thresholds to model defaults" — every model-bound override in force
|
|
29057
|
+
* for `modelId`, cleared in ONE settings change (one save). Rendered only when
|
|
29058
|
+
* there is something to reset. An override stored for another model is kept.
|
|
29059
|
+
*/
|
|
29060
|
+
function ResetModelDefaultsButton({ fields, modelId, settings, disabled, onReset }) {
|
|
29061
|
+
const keys = modelBoundKeysOf(fields);
|
|
29062
|
+
const model = modelId.length > 0 ? modelId : void 0;
|
|
29063
|
+
if (keys.length === 0 || !(0, _camstack_types.hasModelBoundOverrides)(settings, keys, model)) return null;
|
|
29064
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
29065
|
+
type: "button",
|
|
29066
|
+
disabled,
|
|
29067
|
+
className: "text-[10px] text-primary hover:underline disabled:opacity-50",
|
|
29068
|
+
onClick: () => onReset((0, _camstack_types.clearModelBoundOverrides)(settings, keys, model)),
|
|
29069
|
+
children: "Reset thresholds to model defaults"
|
|
29070
|
+
});
|
|
29071
|
+
}
|
|
29072
|
+
//#endregion
|
|
28973
29073
|
//#region src/composites/pipeline-step.tsx
|
|
28974
29074
|
/**
|
|
28975
29075
|
* PipelineStep — single step card in the pipeline builder.
|
|
@@ -29227,6 +29327,16 @@ function ConfigSchemaFieldControl({ field, allFields, step, disabled, modelScope
|
|
|
29227
29327
|
onChange: handleChange
|
|
29228
29328
|
});
|
|
29229
29329
|
}
|
|
29330
|
+
if ((field.type === "slider" || field.type === "number") && field.bindsToModel === true) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelBoundSlider, {
|
|
29331
|
+
field,
|
|
29332
|
+
modelId: step.modelId,
|
|
29333
|
+
settings: step.settings,
|
|
29334
|
+
disabled,
|
|
29335
|
+
onChangeSettings: (settings) => onChange({
|
|
29336
|
+
...step,
|
|
29337
|
+
settings
|
|
29338
|
+
})
|
|
29339
|
+
});
|
|
29230
29340
|
if (field.type === "slider" || field.type === "number") {
|
|
29231
29341
|
const raw = step.settings?.[field.key];
|
|
29232
29342
|
const fallback = typeof field.default === "number" ? field.default : field.min ?? 0;
|
|
@@ -29485,7 +29595,19 @@ function ClusterSettingsHint({ addonId }) {
|
|
|
29485
29595
|
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."
|
|
29486
29596
|
});
|
|
29487
29597
|
}
|
|
29488
|
-
|
|
29598
|
+
/**
|
|
29599
|
+
* The model the step's settings are read and written for — what a model-bound
|
|
29600
|
+
* field (D738) binds to. While the ROOT supplies the step's fine label (D741),
|
|
29601
|
+
* that is the root model: the step's threshold then gates the root's label, on
|
|
29602
|
+
* the root's score scale. Otherwise the step's own model: its pin, else the
|
|
29603
|
+
* model the row really runs (`effectiveModelId` — the device's hardware-aware
|
|
29604
|
+
* default when unpinned), else the step's flat catalog default.
|
|
29605
|
+
*/
|
|
29606
|
+
function settingsModelIdFor(addon, cfg, effectiveModelId, rootModel) {
|
|
29607
|
+
if (rootModel !== void 0 && rootSourceState(addon.id, cfg.settings, rootModel)?.effective) return rootModel.modelId;
|
|
29608
|
+
return cfg.modelId ?? effectiveModelId ?? addon.defaultModelId;
|
|
29609
|
+
}
|
|
29610
|
+
function buildDisplayStep(addon, cfg, modelId) {
|
|
29489
29611
|
return {
|
|
29490
29612
|
addonId: addon.id,
|
|
29491
29613
|
addonName: addon.name,
|
|
@@ -29493,7 +29615,7 @@ function buildDisplayStep(addon, cfg) {
|
|
|
29493
29615
|
inputClasses: [...addon.inputClasses],
|
|
29494
29616
|
outputClasses: [...addon.outputClasses],
|
|
29495
29617
|
enabled: cfg.enabled,
|
|
29496
|
-
modelId
|
|
29618
|
+
modelId,
|
|
29497
29619
|
children: [],
|
|
29498
29620
|
settings: { ...cfg.settings }
|
|
29499
29621
|
};
|
|
@@ -29558,13 +29680,21 @@ function AgentModeEditor({ addon, agentDefault, modelsForFormat, engineFormat, e
|
|
|
29558
29680
|
})] }),
|
|
29559
29681
|
localSchema.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29560
29682
|
className: "pt-2 border-t border-border/50 space-y-3",
|
|
29561
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
29562
|
-
className: "
|
|
29563
|
-
children: "
|
|
29683
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29684
|
+
className: "flex items-center justify-between",
|
|
29685
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
29686
|
+
className: "text-[10px] uppercase tracking-widest text-foreground-subtle",
|
|
29687
|
+
children: "Settings"
|
|
29688
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResetModelDefaultsButton, {
|
|
29689
|
+
fields: localSchema,
|
|
29690
|
+
modelId: settingsModelIdFor(addon, agentDefault, effectiveModelId, rootModel),
|
|
29691
|
+
settings: agentDefault.settings,
|
|
29692
|
+
onReset: (settings) => update({ settings })
|
|
29693
|
+
})]
|
|
29564
29694
|
}), localSchema.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigSchemaField, {
|
|
29565
29695
|
field,
|
|
29566
29696
|
allFields: localSchema,
|
|
29567
|
-
step: buildDisplayStep(addon, agentDefault),
|
|
29697
|
+
step: buildDisplayStep(addon, agentDefault, settingsModelIdFor(addon, agentDefault, effectiveModelId, rootModel)),
|
|
29568
29698
|
disabled: false,
|
|
29569
29699
|
onChange: (next) => update({ settings: { ...next.settings } })
|
|
29570
29700
|
}, field.key))]
|
|
@@ -29660,11 +29790,19 @@ function DeviceModeEditor({ addon, agentDefault, agentNodeId, currentPatch, mode
|
|
|
29660
29790
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
29661
29791
|
className: "text-[10px] uppercase tracking-widest text-foreground-subtle",
|
|
29662
29792
|
children: "Settings"
|
|
29663
|
-
}), settingsOverridden ? /* @__PURE__ */ (0, react_jsx_runtime.
|
|
29664
|
-
|
|
29665
|
-
|
|
29666
|
-
|
|
29667
|
-
|
|
29793
|
+
}), settingsOverridden ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
29794
|
+
className: "flex items-center gap-3",
|
|
29795
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResetModelDefaultsButton, {
|
|
29796
|
+
fields: localSchema,
|
|
29797
|
+
modelId: settingsModelIdFor(addon, effective, inheritedModelId, rootModel),
|
|
29798
|
+
settings: currentPatch?.settings,
|
|
29799
|
+
onReset: (settings) => setSettings(settings)
|
|
29800
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
29801
|
+
type: "button",
|
|
29802
|
+
className: "text-[10px] text-foreground-subtle hover:text-foreground",
|
|
29803
|
+
onClick: () => setSettings(void 0),
|
|
29804
|
+
children: "Clear"
|
|
29805
|
+
})]
|
|
29668
29806
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
29669
29807
|
type: "button",
|
|
29670
29808
|
className: "text-[10px] text-primary hover:underline",
|
|
@@ -29675,7 +29813,7 @@ function DeviceModeEditor({ addon, agentDefault, agentNodeId, currentPatch, mode
|
|
|
29675
29813
|
localSchema.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigSchemaField, {
|
|
29676
29814
|
field,
|
|
29677
29815
|
allFields: localSchema,
|
|
29678
|
-
step: buildDisplayStep(addon, effective),
|
|
29816
|
+
step: buildDisplayStep(addon, effective, settingsModelIdFor(addon, effective, inheritedModelId, rootModel)),
|
|
29679
29817
|
disabled: !settingsOverridden,
|
|
29680
29818
|
onChange: (next) => setSettings({ ...next.settings })
|
|
29681
29819
|
}, field.key)),
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import * as __mfHostReactDOMClient from "react-dom/client";
|
|
|
13
13
|
import { createRoot } from "react-dom/client";
|
|
14
14
|
import * as __mfHostTrpcClient from "@trpc/client";
|
|
15
15
|
import { createTRPCClient, createWSClient, httpLink, splitLink, wsLink } from "@trpc/client";
|
|
16
|
-
import { ALL_CAPABILITY_DEFINITIONS, CAM_PROFILE_ORDER, CLIP_STILL_DISPOSITION_HEADER, CLIP_STILL_REASON_CODE_HEADER, CLIP_STILL_REASON_HEADER, CONTAINER_CHILD_PRIORITY, DEFAULT_EVENTS_BAND_BUFFER_SEC, DEFAULT_EVENT_COLOR, DEVICE_TYPE_CONTROL_KIND, DISPLAY_ICON_GROUPS, DeviceExportStatusSchema, DeviceFeature, DeviceRole, DeviceType, EVENT_TAXONOMY, EXTENSION_SLOTS, EXTENSION_SLOT_IDS, EventCategory, ExposedDeviceSchema, FIRST_LEVEL_MACRO_CLASSES, KNOWN_CAP_NAMES, MAX_SENSOR_TRIGGER_DEVICES, MODEL_PROVIDER_IDS, NAVIGATION_LIGHT_LEVEL_MAX, NAVIGATION_LIGHT_LEVEL_MIN, PET_FEEDER_MANUAL_FEED_MAX, PET_FEEDER_MANUAL_FEED_MIN, RECORDING_AUDIO_TRIGGER_OFFERED_DBFS, RECORDING_SOURCE_CAMSTACK_ADDON, RecordingConfigSchema, RecordingStatusSchema, SOURCE_CAP_ACTIVE_FIELD, STATIONARY_OVERLAY_STYLE, TIMEZONES, USE_ROOT_MODEL_SETTING_KEY, buildModelVariantGroups, canConvertUnit, clusterStepSettingFieldsFor, createDeviceProxy, createLazyTrpcSource, createSliceHandle, deriveRecordingMode, describeModelVariant, findTimezone, formatParkedFor, isRootSuppliableStepId, normalizeUnit, publicEventCategories, resolveContainerPrimaryChild, resolveDeviceControlKind, resolveRecordingProfiles, resolveRootSupply, resolveVariantModelId, resolveViewableDeviceIds, searchDisplayIcons, selectAssignedProfileSlots, tryConvertUnit, unitsForDimension } from "@camstack/types";
|
|
16
|
+
import { ALL_CAPABILITY_DEFINITIONS, CAM_PROFILE_ORDER, CLIP_STILL_DISPOSITION_HEADER, CLIP_STILL_REASON_CODE_HEADER, CLIP_STILL_REASON_HEADER, CONTAINER_CHILD_PRIORITY, DEFAULT_EVENTS_BAND_BUFFER_SEC, DEFAULT_EVENT_COLOR, DEVICE_TYPE_CONTROL_KIND, DISPLAY_ICON_GROUPS, DeviceExportStatusSchema, DeviceFeature, DeviceRole, DeviceType, EVENT_TAXONOMY, EXTENSION_SLOTS, EXTENSION_SLOT_IDS, EventCategory, ExposedDeviceSchema, FIRST_LEVEL_MACRO_CLASSES, KNOWN_CAP_NAMES, MAX_SENSOR_TRIGGER_DEVICES, MODEL_PROVIDER_IDS, NAVIGATION_LIGHT_LEVEL_MAX, NAVIGATION_LIGHT_LEVEL_MIN, PET_FEEDER_MANUAL_FEED_MAX, PET_FEEDER_MANUAL_FEED_MIN, RECORDING_AUDIO_TRIGGER_OFFERED_DBFS, RECORDING_SOURCE_CAMSTACK_ADDON, RecordingConfigSchema, RecordingStatusSchema, SOURCE_CAP_ACTIVE_FIELD, STATIONARY_OVERLAY_STYLE, TIMEZONES, USE_ROOT_MODEL_SETTING_KEY, buildModelVariantGroups, canConvertUnit, clearModelBoundOverrides, clearModelBoundSetting, clusterStepSettingFieldsFor, createDeviceProxy, createLazyTrpcSource, createSliceHandle, deriveRecordingMode, describeModelVariant, findTimezone, formatParkedFor, hasModelBoundOverrides, isRootSuppliableStepId, normalizeUnit, publicEventCategories, resolveContainerPrimaryChild, resolveDeviceControlKind, resolveModelBoundSetting, resolveRecordingProfiles, resolveRootSupply, resolveVariantModelId, resolveViewableDeviceIds, searchDisplayIcons, selectAssignedProfileSlots, tryConvertUnit, unitsForDimension, writeModelBoundSetting } from "@camstack/types";
|
|
17
17
|
import { createSystem } from "@camstack/sdk";
|
|
18
18
|
import { z } from "zod";
|
|
19
19
|
//#region ../../node_modules/lucide-react/dist/esm/shared/src/utils/mergeClasses.js
|
|
@@ -28946,6 +28946,106 @@ function ModelPicker({ models, value, onChange, disabled, defaultModelId }) {
|
|
|
28946
28946
|
});
|
|
28947
28947
|
}
|
|
28948
28948
|
//#endregion
|
|
28949
|
+
//#region src/composites/model-bound-slider.tsx
|
|
28950
|
+
var SOURCE_LABEL = {
|
|
28951
|
+
override: "Override for this model",
|
|
28952
|
+
"model-default": "Model default",
|
|
28953
|
+
"global-default": "Default — not tuned for this model"
|
|
28954
|
+
};
|
|
28955
|
+
function formatNumber(value, scale) {
|
|
28956
|
+
return (scale !== void 0 ? value / scale : value).toFixed(2).replace(/\.?0+$/, "");
|
|
28957
|
+
}
|
|
28958
|
+
function ModelBoundSlider({ field, modelId, settings, disabled, onChangeSettings }) {
|
|
28959
|
+
const modelDefault = field.modelApplicability?.defaultByModel?.[modelId];
|
|
28960
|
+
const state = resolveModelBoundSetting(settings, field.key, modelId.length > 0 ? modelId : void 0, modelDefault, field.default);
|
|
28961
|
+
const fallback = typeof field.default === "number" ? field.default : field.min ?? 0;
|
|
28962
|
+
const current = typeof state.value === "number" ? state.value : fallback;
|
|
28963
|
+
const scale = field.type === "slider" ? field.displayScale : void 0;
|
|
28964
|
+
const unit = "unit" in field ? field.unit : void 0;
|
|
28965
|
+
const stale = state.inapplicableOverride;
|
|
28966
|
+
const hasStoredValue = settings?.[field.key] !== void 0;
|
|
28967
|
+
const handleChange = (value) => {
|
|
28968
|
+
onChangeSettings(modelId.length > 0 ? writeModelBoundSetting(settings, field.key, value, modelId) : {
|
|
28969
|
+
...settings,
|
|
28970
|
+
[field.key]: value
|
|
28971
|
+
});
|
|
28972
|
+
};
|
|
28973
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
28974
|
+
/* @__PURE__ */ jsxs("div", {
|
|
28975
|
+
className: "flex items-center justify-between mb-1",
|
|
28976
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
28977
|
+
className: "text-[10px] font-medium text-foreground-subtle uppercase tracking-wide",
|
|
28978
|
+
children: field.label
|
|
28979
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
28980
|
+
className: "text-xs font-medium text-foreground tabular-nums",
|
|
28981
|
+
children: [formatNumber(current, scale), unit ? ` ${unit}` : ""]
|
|
28982
|
+
})]
|
|
28983
|
+
}),
|
|
28984
|
+
field.description && /* @__PURE__ */ jsx("p", {
|
|
28985
|
+
className: "text-[10px] text-foreground-subtle/60 mb-1.5",
|
|
28986
|
+
children: field.description
|
|
28987
|
+
}),
|
|
28988
|
+
/* @__PURE__ */ jsx("input", {
|
|
28989
|
+
type: "range",
|
|
28990
|
+
min: field.min ?? 0,
|
|
28991
|
+
max: field.max ?? 1,
|
|
28992
|
+
step: field.step ?? .01,
|
|
28993
|
+
value: current,
|
|
28994
|
+
disabled: disabled || field.disabled,
|
|
28995
|
+
onChange: (e) => handleChange(Number(e.target.value)),
|
|
28996
|
+
className: "w-full accent-primary h-1.5"
|
|
28997
|
+
}),
|
|
28998
|
+
/* @__PURE__ */ jsxs("div", {
|
|
28999
|
+
className: "mt-1 flex items-center justify-between gap-2 text-[10px]",
|
|
29000
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
29001
|
+
"data-testid": "model-bound-source",
|
|
29002
|
+
className: state.source === "override" ? "text-primary font-medium" : "text-foreground-subtle",
|
|
29003
|
+
children: SOURCE_LABEL[state.source]
|
|
29004
|
+
}), hasStoredValue && /* @__PURE__ */ jsx("button", {
|
|
29005
|
+
type: "button",
|
|
29006
|
+
disabled,
|
|
29007
|
+
className: "text-foreground-subtle hover:text-foreground disabled:opacity-50",
|
|
29008
|
+
onClick: () => onChangeSettings(clearModelBoundSetting(settings, field.key)),
|
|
29009
|
+
children: "Reset to model default"
|
|
29010
|
+
})]
|
|
29011
|
+
}),
|
|
29012
|
+
stale !== void 0 && typeof stale.value === "number" && /* @__PURE__ */ jsxs("p", {
|
|
29013
|
+
className: "mt-1 text-[10px] text-amber-400/90",
|
|
29014
|
+
children: [
|
|
29015
|
+
"The override ",
|
|
29016
|
+
formatNumber(stale.value, scale),
|
|
29017
|
+
" was set for ",
|
|
29018
|
+
stale.modelId,
|
|
29019
|
+
" and does not apply to this model."
|
|
29020
|
+
]
|
|
29021
|
+
})
|
|
29022
|
+
] });
|
|
29023
|
+
}
|
|
29024
|
+
/** The keys of the model-bound fields of a schema, groups flattened. */
|
|
29025
|
+
function modelBoundKeysOf(fields) {
|
|
29026
|
+
return fields.flatMap((field) => {
|
|
29027
|
+
if (field.type === "group") return modelBoundKeysOf(field.fields);
|
|
29028
|
+
return "key" in field && "bindsToModel" in field && field.bindsToModel === true ? [field.key] : [];
|
|
29029
|
+
});
|
|
29030
|
+
}
|
|
29031
|
+
/**
|
|
29032
|
+
* "Reset thresholds to model defaults" — every model-bound override in force
|
|
29033
|
+
* for `modelId`, cleared in ONE settings change (one save). Rendered only when
|
|
29034
|
+
* there is something to reset. An override stored for another model is kept.
|
|
29035
|
+
*/
|
|
29036
|
+
function ResetModelDefaultsButton({ fields, modelId, settings, disabled, onReset }) {
|
|
29037
|
+
const keys = modelBoundKeysOf(fields);
|
|
29038
|
+
const model = modelId.length > 0 ? modelId : void 0;
|
|
29039
|
+
if (keys.length === 0 || !hasModelBoundOverrides(settings, keys, model)) return null;
|
|
29040
|
+
return /* @__PURE__ */ jsx("button", {
|
|
29041
|
+
type: "button",
|
|
29042
|
+
disabled,
|
|
29043
|
+
className: "text-[10px] text-primary hover:underline disabled:opacity-50",
|
|
29044
|
+
onClick: () => onReset(clearModelBoundOverrides(settings, keys, model)),
|
|
29045
|
+
children: "Reset thresholds to model defaults"
|
|
29046
|
+
});
|
|
29047
|
+
}
|
|
29048
|
+
//#endregion
|
|
28949
29049
|
//#region src/composites/pipeline-step.tsx
|
|
28950
29050
|
/**
|
|
28951
29051
|
* PipelineStep — single step card in the pipeline builder.
|
|
@@ -29203,6 +29303,16 @@ function ConfigSchemaFieldControl({ field, allFields, step, disabled, modelScope
|
|
|
29203
29303
|
onChange: handleChange
|
|
29204
29304
|
});
|
|
29205
29305
|
}
|
|
29306
|
+
if ((field.type === "slider" || field.type === "number") && field.bindsToModel === true) return /* @__PURE__ */ jsx(ModelBoundSlider, {
|
|
29307
|
+
field,
|
|
29308
|
+
modelId: step.modelId,
|
|
29309
|
+
settings: step.settings,
|
|
29310
|
+
disabled,
|
|
29311
|
+
onChangeSettings: (settings) => onChange({
|
|
29312
|
+
...step,
|
|
29313
|
+
settings
|
|
29314
|
+
})
|
|
29315
|
+
});
|
|
29206
29316
|
if (field.type === "slider" || field.type === "number") {
|
|
29207
29317
|
const raw = step.settings?.[field.key];
|
|
29208
29318
|
const fallback = typeof field.default === "number" ? field.default : field.min ?? 0;
|
|
@@ -29461,7 +29571,19 @@ function ClusterSettingsHint({ addonId }) {
|
|
|
29461
29571
|
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."
|
|
29462
29572
|
});
|
|
29463
29573
|
}
|
|
29464
|
-
|
|
29574
|
+
/**
|
|
29575
|
+
* The model the step's settings are read and written for — what a model-bound
|
|
29576
|
+
* field (D738) binds to. While the ROOT supplies the step's fine label (D741),
|
|
29577
|
+
* that is the root model: the step's threshold then gates the root's label, on
|
|
29578
|
+
* the root's score scale. Otherwise the step's own model: its pin, else the
|
|
29579
|
+
* model the row really runs (`effectiveModelId` — the device's hardware-aware
|
|
29580
|
+
* default when unpinned), else the step's flat catalog default.
|
|
29581
|
+
*/
|
|
29582
|
+
function settingsModelIdFor(addon, cfg, effectiveModelId, rootModel) {
|
|
29583
|
+
if (rootModel !== void 0 && rootSourceState(addon.id, cfg.settings, rootModel)?.effective) return rootModel.modelId;
|
|
29584
|
+
return cfg.modelId ?? effectiveModelId ?? addon.defaultModelId;
|
|
29585
|
+
}
|
|
29586
|
+
function buildDisplayStep(addon, cfg, modelId) {
|
|
29465
29587
|
return {
|
|
29466
29588
|
addonId: addon.id,
|
|
29467
29589
|
addonName: addon.name,
|
|
@@ -29469,7 +29591,7 @@ function buildDisplayStep(addon, cfg) {
|
|
|
29469
29591
|
inputClasses: [...addon.inputClasses],
|
|
29470
29592
|
outputClasses: [...addon.outputClasses],
|
|
29471
29593
|
enabled: cfg.enabled,
|
|
29472
|
-
modelId
|
|
29594
|
+
modelId,
|
|
29473
29595
|
children: [],
|
|
29474
29596
|
settings: { ...cfg.settings }
|
|
29475
29597
|
};
|
|
@@ -29534,13 +29656,21 @@ function AgentModeEditor({ addon, agentDefault, modelsForFormat, engineFormat, e
|
|
|
29534
29656
|
})] }),
|
|
29535
29657
|
localSchema.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
29536
29658
|
className: "pt-2 border-t border-border/50 space-y-3",
|
|
29537
|
-
children: [/* @__PURE__ */
|
|
29538
|
-
className: "
|
|
29539
|
-
children: "
|
|
29659
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
29660
|
+
className: "flex items-center justify-between",
|
|
29661
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
29662
|
+
className: "text-[10px] uppercase tracking-widest text-foreground-subtle",
|
|
29663
|
+
children: "Settings"
|
|
29664
|
+
}), /* @__PURE__ */ jsx(ResetModelDefaultsButton, {
|
|
29665
|
+
fields: localSchema,
|
|
29666
|
+
modelId: settingsModelIdFor(addon, agentDefault, effectiveModelId, rootModel),
|
|
29667
|
+
settings: agentDefault.settings,
|
|
29668
|
+
onReset: (settings) => update({ settings })
|
|
29669
|
+
})]
|
|
29540
29670
|
}), localSchema.map((field) => /* @__PURE__ */ jsx(ConfigSchemaField, {
|
|
29541
29671
|
field,
|
|
29542
29672
|
allFields: localSchema,
|
|
29543
|
-
step: buildDisplayStep(addon, agentDefault),
|
|
29673
|
+
step: buildDisplayStep(addon, agentDefault, settingsModelIdFor(addon, agentDefault, effectiveModelId, rootModel)),
|
|
29544
29674
|
disabled: false,
|
|
29545
29675
|
onChange: (next) => update({ settings: { ...next.settings } })
|
|
29546
29676
|
}, field.key))]
|
|
@@ -29636,11 +29766,19 @@ function DeviceModeEditor({ addon, agentDefault, agentNodeId, currentPatch, mode
|
|
|
29636
29766
|
children: [/* @__PURE__ */ jsx("div", {
|
|
29637
29767
|
className: "text-[10px] uppercase tracking-widest text-foreground-subtle",
|
|
29638
29768
|
children: "Settings"
|
|
29639
|
-
}), settingsOverridden ? /* @__PURE__ */
|
|
29640
|
-
|
|
29641
|
-
|
|
29642
|
-
|
|
29643
|
-
|
|
29769
|
+
}), settingsOverridden ? /* @__PURE__ */ jsxs("div", {
|
|
29770
|
+
className: "flex items-center gap-3",
|
|
29771
|
+
children: [/* @__PURE__ */ jsx(ResetModelDefaultsButton, {
|
|
29772
|
+
fields: localSchema,
|
|
29773
|
+
modelId: settingsModelIdFor(addon, effective, inheritedModelId, rootModel),
|
|
29774
|
+
settings: currentPatch?.settings,
|
|
29775
|
+
onReset: (settings) => setSettings(settings)
|
|
29776
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
29777
|
+
type: "button",
|
|
29778
|
+
className: "text-[10px] text-foreground-subtle hover:text-foreground",
|
|
29779
|
+
onClick: () => setSettings(void 0),
|
|
29780
|
+
children: "Clear"
|
|
29781
|
+
})]
|
|
29644
29782
|
}) : /* @__PURE__ */ jsx("button", {
|
|
29645
29783
|
type: "button",
|
|
29646
29784
|
className: "text-[10px] text-primary hover:underline",
|
|
@@ -29651,7 +29789,7 @@ function DeviceModeEditor({ addon, agentDefault, agentNodeId, currentPatch, mode
|
|
|
29651
29789
|
localSchema.map((field) => /* @__PURE__ */ jsx(ConfigSchemaField, {
|
|
29652
29790
|
field,
|
|
29653
29791
|
allFields: localSchema,
|
|
29654
|
-
step: buildDisplayStep(addon, effective),
|
|
29792
|
+
step: buildDisplayStep(addon, effective, settingsModelIdFor(addon, effective, inheritedModelId, rootModel)),
|
|
29655
29793
|
disabled: !settingsOverridden,
|
|
29656
29794
|
onChange: (next) => setSettings({ ...next.settings })
|
|
29657
29795
|
}, field.key)),
|