@camstack/ui-library 1.2.51 → 1.2.53

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.
@@ -26,13 +26,23 @@
26
26
  * and Night (IR), each forcing a fresh snapshot. The variant is DETECTED
27
27
  * from the frame and shown rather than asked; the buttons exist so the
28
28
  * operator can say "capture this as the night reference" while standing
29
- * outside at 22:00.
30
- * 3. **Read the coverage line.** "Daylight ✓ · Night (IR) ✗ — reports 'can't
31
- * tell' in night (ir)". This feature's failure mode is silence that reads as
32
- * "nothing has happened", and that one line is the difference between a
33
- * five-minute fix and a session of guessing.
29
+ * outside at 22:00 — which is exactly why **Night (IR) is labelled
30
+ * optional**. Almost nobody does that, and a scene captured only in daylight
31
+ * is a configuration, not a defect: under the default
32
+ * `onUncoveredCondition: 'skip'` the infrared hours are simply sat out.
33
+ * 3. **Read the coverage line.** "Daylight ✓ · Night (IR) ✗ — optional; checks
34
+ * in night (ir) are skipped". This feature's failure mode is silence that
35
+ * reads as "nothing has happened", and that one line is the difference
36
+ * between a five-minute fix and a session of guessing — but it must say what
37
+ * HAPPENS, not complain. It used to end "reports 'can't tell'", which made a
38
+ * correctly configured scene look broken every night.
34
39
  * 4. **Reset.** Clears the flag AND re-captures the baseline for the current
35
40
  * light, because the bin never goes back in exactly the same spot.
41
+ * 5. **Settings.** Every knob the model has actually carried since the provider
42
+ * shipped — how often it looks, how closely it has to match, how many
43
+ * agreeing checks a change costs, what a missing light means, which boolean
44
+ * drives notifications, and whether a vision model adjudicates a flip. None
45
+ * of it was reachable before; see `SceneSettingsForm`.
36
46
  *
37
47
  * Live state arrives through the cap's `status.kind: 'push'` runtime slice, so
38
48
  * the card updates itself without polling.
@@ -0,0 +1,36 @@
1
+ import { SceneMonitor, SceneReference } from '@camstack/types';
2
+ /** The hub-proxied prefix for the pipeline-analytics event-media data plane. */
3
+ export declare const SCENE_MEDIA_BASE = "/addon/pipeline-analytics/event-media";
4
+ /** One reference, with the coordinates `deleteReference` addresses it by. */
5
+ export interface SceneReferenceTile {
6
+ readonly stateId: string;
7
+ /** Index into THAT state's `references` array — the cap's own addressing. */
8
+ readonly index: number;
9
+ readonly reference: SceneReference;
10
+ }
11
+ export interface SceneReferenceGroup {
12
+ readonly condition: string;
13
+ readonly tiles: readonly SceneReferenceTile[];
14
+ }
15
+ /**
16
+ * Flatten `states[].references[]` into per-condition groups while keeping each
17
+ * reference's `(stateId, index)` — the pair `deleteReference` takes.
18
+ *
19
+ * The index is deliberately the position in the state's OWN array rather than
20
+ * anything derived from the grouping: an index computed over the grouped view
21
+ * would address a different reference the moment a scene holds two conditions,
22
+ * and the operator would delete the wrong photograph with no way to tell.
23
+ */
24
+ export declare function groupReferences(monitor: SceneMonitor): readonly SceneReferenceGroup[];
25
+ /** Total references across every state — the count that decides whether a
26
+ * delete leaves the scene with no idea what "normal" looks like. */
27
+ export declare function totalReferences(monitor: SceneMonitor): number;
28
+ export interface SceneReferenceGalleryProps {
29
+ readonly monitor: SceneMonitor;
30
+ readonly disabled: boolean;
31
+ readonly onDelete: (input: {
32
+ stateId: string;
33
+ index: number;
34
+ }) => void;
35
+ }
36
+ export declare function SceneReferenceGallery({ monitor, disabled, onDelete }: SceneReferenceGalleryProps): import("react").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { SceneCheck, SceneMonitor, SceneUncoveredPolicy } from '@camstack/types';
2
+ /** The subset of `updateScene`'s patch this form can produce. */
3
+ export interface SceneSettingsPatch {
4
+ readonly label?: string;
5
+ readonly enabled?: boolean;
6
+ readonly triggerMode?: SceneMonitor['triggerMode'];
7
+ readonly checkIntervalSec?: number;
8
+ readonly check?: SceneCheck;
9
+ readonly quietSeconds?: number;
10
+ readonly minObservationSpacingSec?: number;
11
+ readonly emit?: SceneMonitor['emit'];
12
+ readonly autoRestore?: boolean;
13
+ readonly onUncoveredCondition?: SceneUncoveredPolicy;
14
+ readonly confirm?: SceneMonitor['confirm'] | null;
15
+ }
16
+ export interface SceneSettingsFormProps {
17
+ readonly monitor: SceneMonitor;
18
+ readonly disabled: boolean;
19
+ readonly onPatch: (patch: SceneSettingsPatch) => void;
20
+ }
21
+ export declare function SceneSettingsForm({ monitor, disabled, onPatch }: SceneSettingsFormProps): import("react").JSX.Element;
@@ -1,26 +1,58 @@
1
1
  import { SceneMonitor } from '@camstack/types';
2
- /** The lighting variants an operator is asked to capture. The resolver can also
3
- * report `night`, `dawn` and `dusk`; those are shown when present but never
4
- * demanded, because most installs only ever see day and IR. */
2
+ /** The lighting variants an operator is OFFERED. The resolver can also report
3
+ * `night`, `dawn` and `dusk`; those are shown when present but never demanded,
4
+ * because most installs only ever see day and IR. */
5
5
  export declare const SCENE_CAPTURE_VARIANTS: readonly ["day", "ir"];
6
6
  export type SceneCaptureVariant = (typeof SCENE_CAPTURE_VARIANTS)[number];
7
+ /**
8
+ * The one variant that is genuinely optional.
9
+ *
10
+ * Nobody stands outside at 22:00 to photograph a wheelie bin in infrared, and
11
+ * plenty of scenes are daytime questions that never need a night reference at
12
+ * all. Every sentence about IR in this file is written from that premise: a
13
+ * missing night reference is a CHOICE the scene accommodates, never a fault it
14
+ * reports. The engine backs that up — under the default
15
+ * `onUncoveredCondition: 'skip'` the night checks are simply not made.
16
+ */
17
+ export declare const SCENE_OPTIONAL_VARIANT: SceneCaptureVariant;
7
18
  export declare const VARIANT_LABEL: Readonly<Record<string, string>>;
8
19
  export declare function variantLabel(condition: string): string;
9
20
  /**
10
- * "day ✓ · ir ✗ — this scene reports UNKNOWN at night."
21
+ * "Daylight ✓ · Night (IR) ✗ — optional; checks in night (ir) are skipped."
11
22
  *
12
23
  * Converts the silent cross-condition fallback this engine used to have into an
13
24
  * operator-visible fact. Without it, a scene with only a daylight reference
14
25
  * simply stops answering after sunset and nothing anywhere says why.
26
+ *
27
+ * It used to end *"reports 'can't tell' in night (ir)"*, which was accurate and
28
+ * read as a defect: the operator saw a ✗ and a complaint for a scene that was
29
+ * configured exactly as they meant it. The missing half is now named as what it
30
+ * is — the part you do not have to do — and the sentence says what actually
31
+ * happens instead, which differs by the scene's own policy.
15
32
  */
16
33
  export declare function coverageLine(monitor: SceneMonitor): string;
34
+ /**
35
+ * The calm sentence for a scene that is sitting the current light out — `null`
36
+ * whenever it is checking normally.
37
+ *
38
+ * Separate from {@link statusLine} because it is not a verdict and must not
39
+ * read like one. A paused scene has not failed, has not lost its flag, and
40
+ * needs nothing from the operator; the only thing worth saying is which light
41
+ * it is waiting out and that it will pick itself back up.
42
+ */
43
+ export declare function suspensionLine(monitor: SceneMonitor): string | null;
17
44
  /**
18
45
  * The one line under a scene's name. It answers, in order: can it judge, what
19
46
  * does it say, and how long has it said it.
20
47
  */
21
48
  export declare function statusLine(monitor: SceneMonitor, now: number): string;
22
49
  /** The badge word. `latched` wins over `verdict`, because the latch is the
23
- * thing the operator asked to be told and it outlives the live state. */
50
+ * thing the operator asked to be told and it outlives the live state.
51
+ *
52
+ * A scene sitting out an uncovered light reads `Paused`, in the NEUTRAL tone —
53
+ * never `alarm`, and never the `danger` variant the page maps that to. A
54
+ * missing IR reference must not be able to paint a red badge on a scene that
55
+ * is doing exactly what it was configured to do. */
24
56
  export declare function badgeFor(monitor: SceneMonitor): {
25
57
  text: string;
26
58
  tone: 'alarm' | 'ok' | 'unknown';
@@ -759,6 +759,8 @@ export declare const useLlmListModelCatalog: typeof trpc.llm.listModelCatalog.us
759
759
  export declare const useLlmListRuntimeNodes: typeof trpc.llm.listRuntimeNodes.useQuery;
760
760
  /** Generated alias around `trpc.llm.listNodeModels.useQuery`. */
761
761
  export declare const useLlmListNodeModels: typeof trpc.llm.listNodeModels.useQuery;
762
+ /** Generated alias around `trpc.llm.resolveModelRef.useMutation`. */
763
+ export declare const useLlmResolveModelRef: typeof trpc.llm.resolveModelRef.useMutation;
762
764
  /** Generated alias around `trpc.llm.installModel.useMutation`. */
763
765
  export declare const useLlmInstallModel: typeof trpc.llm.installModel.useMutation;
764
766
  /** Generated alias around `trpc.llm.deleteModel.useMutation`. */
package/dist/index.cjs CHANGED
@@ -5673,6 +5673,8 @@ var useLlmListModelCatalog = trpc.llm.listModelCatalog.useQuery;
5673
5673
  var useLlmListRuntimeNodes = trpc.llm.listRuntimeNodes.useQuery;
5674
5674
  /** Generated alias around `trpc.llm.listNodeModels.useQuery`. */
5675
5675
  var useLlmListNodeModels = trpc.llm.listNodeModels.useQuery;
5676
+ /** Generated alias around `trpc.llm.resolveModelRef.useMutation`. */
5677
+ var useLlmResolveModelRef = trpc.llm.resolveModelRef.useMutation;
5676
5678
  /** Generated alias around `trpc.llm.installModel.useMutation`. */
5677
5679
  var useLlmInstallModel = trpc.llm.installModel.useMutation;
5678
5680
  /** Generated alias around `trpc.llm.deleteModel.useMutation`. */
@@ -22758,7 +22760,13 @@ var Skeleton = (0, react$1.forwardRef)(({ className, ...props }, ref) => {
22758
22760
  Skeleton.displayName = "Skeleton";
22759
22761
  //#endregion
22760
22762
  //#region src/primitives/input.tsx
22761
- var inputVariants = cva("h-7 w-full px-2.5 text-xs bg-surface border rounded-md text-foreground placeholder:text-foreground-subtle focus:outline-none focus:ring-1 focus:ring-primary transition-colors", {
22763
+ /**
22764
+ * `disabled:opacity-50 disabled:cursor-not-allowed` for the same reason as
22765
+ * `Select` — an explicit `bg-surface` / `text-foreground` overrides the greying
22766
+ * a browser would otherwise apply, leaving an inert field indistinguishable
22767
+ * from a live one. See `disabled-affordance.test.tsx`.
22768
+ */
22769
+ var inputVariants = cva("h-7 w-full px-2.5 text-xs bg-surface border rounded-md text-foreground placeholder:text-foreground-subtle focus:outline-none focus:ring-1 focus:ring-primary transition-colors disabled:opacity-50 disabled:cursor-not-allowed", {
22762
22770
  variants: { state: {
22763
22771
  default: "border-border",
22764
22772
  error: "border-danger"
@@ -22793,19 +22801,36 @@ var Input = (0, react$1.forwardRef)(({ className, state, leftSlot, rightSlot, ..
22793
22801
  Input.displayName = "Input";
22794
22802
  //#endregion
22795
22803
  //#region src/primitives/select.tsx
22796
- var Select = (0, react$1.forwardRef)(({ className, options, ...props }, ref) => {
22804
+ /**
22805
+ * A native `<select>` styled to match the rest of the field set.
22806
+ *
22807
+ * `appearance-none` plus an explicit `bg-surface` / `text-foreground` is what
22808
+ * makes it match — and it is also what USED to make a disabled one a lie. A
22809
+ * browser greys a disabled control by rendering its own background and text
22810
+ * colour; all three of those are overridden here, so a `disabled` dropdown
22811
+ * looked exactly like a live one and simply ignored the click. That is the
22812
+ * whole of the "the language selector does not open" report of 2026-08-15:
22813
+ * the AI section's switch was off, every control under it was correctly inert,
22814
+ * and nothing on screen said so.
22815
+ *
22816
+ * `disabled:` classes belong HERE and not on each call site: there are dozens
22817
+ * of `<Select disabled>` in this app and the one that gets forgotten is the one
22818
+ * that generates the ticket. Guarded by `disabled-affordance.test.tsx`.
22819
+ */
22820
+ var Select = (0, react$1.forwardRef)(({ className, options, disabled, ...props }, ref) => {
22797
22821
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
22798
22822
  className: "relative w-full",
22799
22823
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
22800
22824
  ref,
22801
- className: cn("h-7 w-full px-2.5 text-xs bg-surface border border-border rounded-md text-foreground appearance-none pr-7 focus:outline-none focus:ring-1 focus:ring-primary", className),
22825
+ disabled,
22826
+ className: cn("h-7 w-full px-2.5 text-xs bg-surface border border-border rounded-md text-foreground appearance-none pr-7 focus:outline-none focus:ring-1 focus:ring-primary", "disabled:opacity-50 disabled:cursor-not-allowed", className),
22802
22827
  ...props,
22803
22828
  children: options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
22804
22829
  value: option.value,
22805
22830
  children: option.label
22806
22831
  }, option.value))
22807
22832
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
22808
- className: "absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none text-foreground-muted",
22833
+ className: cn("absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none text-foreground-muted", disabled === true ? "opacity-50" : ""),
22809
22834
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronDown, { className: "h-3.5 w-3.5" })
22810
22835
  })]
22811
22836
  });
@@ -25567,9 +25592,9 @@ async function waitForIceGathering(pc) {
25567
25592
  }
25568
25593
  //#endregion
25569
25594
  //#region src/composites/cap-settings/scene-monitor-copy.ts
25570
- /** The lighting variants an operator is asked to capture. The resolver can also
25571
- * report `night`, `dawn` and `dusk`; those are shown when present but never
25572
- * demanded, because most installs only ever see day and IR. */
25595
+ /** The lighting variants an operator is OFFERED. The resolver can also report
25596
+ * `night`, `dawn` and `dusk`; those are shown when present but never demanded,
25597
+ * because most installs only ever see day and IR. */
25573
25598
  var SCENE_CAPTURE_VARIANTS = ["day", "ir"];
25574
25599
  var VARIANT_LABEL = {
25575
25600
  day: "Daylight",
@@ -25582,11 +25607,17 @@ function variantLabel(condition) {
25582
25607
  return VARIANT_LABEL[condition] ?? condition;
25583
25608
  }
25584
25609
  /**
25585
- * "day ✓ · ir ✗ — this scene reports UNKNOWN at night."
25610
+ * "Daylight ✓ · Night (IR) ✗ — optional; checks in night (ir) are skipped."
25586
25611
  *
25587
25612
  * Converts the silent cross-condition fallback this engine used to have into an
25588
25613
  * operator-visible fact. Without it, a scene with only a daylight reference
25589
25614
  * simply stops answering after sunset and nothing anywhere says why.
25615
+ *
25616
+ * It used to end *"reports 'can't tell' in night (ir)"*, which was accurate and
25617
+ * read as a defect: the operator saw a ✗ and a complaint for a scene that was
25618
+ * configured exactly as they meant it. The missing half is now named as what it
25619
+ * is — the part you do not have to do — and the sentence says what actually
25620
+ * happens instead, which differs by the scene's own policy.
25590
25621
  */
25591
25622
  function coverageLine(monitor) {
25592
25623
  const covered = new Set(monitor.coveredConditions);
@@ -25595,10 +25626,26 @@ function coverageLine(monitor) {
25595
25626
  for (const c of extra) parts.push(`${variantLabel(c)} ✓`);
25596
25627
  const missing = SCENE_CAPTURE_VARIANTS.filter((v) => !covered.has(v));
25597
25628
  if (missing.length === 0) return parts.join(" · ");
25598
- return `${parts.join(" · ")} — reports “can’t tell” in ${missing.map((v) => variantLabel(v).toLowerCase()).join(" and ")}`;
25629
+ if (covered.size === 0) return `${parts.join(" · ")} — nothing captured yet, so this scene is not watching.`;
25630
+ const names = missing.map((v) => variantLabel(v).toLowerCase()).join(" and ");
25631
+ return monitor.onUncoveredCondition === "judge-anyway" ? `${parts.join(" · ")} — ${names} judged against the references you do have.` : `${parts.join(" · ")} — optional; checks in ${names} are skipped.`;
25632
+ }
25633
+ /**
25634
+ * The calm sentence for a scene that is sitting the current light out — `null`
25635
+ * whenever it is checking normally.
25636
+ *
25637
+ * Separate from {@link statusLine} because it is not a verdict and must not
25638
+ * read like one. A paused scene has not failed, has not lost its flag, and
25639
+ * needs nothing from the operator; the only thing worth saying is which light
25640
+ * it is waiting out and that it will pick itself back up.
25641
+ */
25642
+ function suspensionLine(monitor) {
25643
+ if (monitor.suspendedCondition === null) return null;
25644
+ const light = variantLabel(monitor.suspendedCondition).toLowerCase();
25645
+ return `Paused — nothing was captured in ${light}, so checks are on hold until the light changes back. Capture ${light} if you want it watched around the clock.`;
25599
25646
  }
25600
25647
  var UNAVAILABLE_SENTENCE = {
25601
- "no-reference-for-condition": "can’t tell — nothing was captured in this light yet. Use “Capture now” when the camera is in it.",
25648
+ "no-reference-for-condition": "can’t tell — nothing has been captured for this scene yet. Press “Capture daylight” to photograph what normal looks like.",
25602
25649
  "view-shifted": "can’t tell — the camera view has moved since the reference was captured. Re-capture it.",
25603
25650
  "no-vision-profile": "can’t tell — the vision model is unreachable, so the change is on hold.",
25604
25651
  "encoder-model-changed": "can’t tell — the embedding model changed, so the stored references are unreadable. Re-capture them.",
@@ -25614,10 +25661,17 @@ function statusLine(monitor, now) {
25614
25661
  const since = monitor.verdict === "diverged" ? monitor.divergedAt : monitor.armedAt;
25615
25662
  const held = since !== null ? ` for ${humanDuration(Math.max(0, now - since))}` : "";
25616
25663
  if (monitor.latched) return monitor.verdict === "diverged" ? `CHANGED${held} — press Reset when you have dealt with it.` : `Back to normal, but still flagged as changed${held}. Press Reset to clear it.`;
25664
+ const paused = suspensionLine(monitor);
25665
+ if (paused !== null) return paused;
25617
25666
  return monitor.verdict === "diverged" ? `CHANGED${held}` : `Normal${held}`;
25618
25667
  }
25619
25668
  /** The badge word. `latched` wins over `verdict`, because the latch is the
25620
- * thing the operator asked to be told and it outlives the live state. */
25669
+ * thing the operator asked to be told and it outlives the live state.
25670
+ *
25671
+ * A scene sitting out an uncovered light reads `Paused`, in the NEUTRAL tone —
25672
+ * never `alarm`, and never the `danger` variant the page maps that to. A
25673
+ * missing IR reference must not be able to paint a red badge on a scene that
25674
+ * is doing exactly what it was configured to do. */
25621
25675
  function badgeFor(monitor) {
25622
25676
  if (monitor.states.length === 0) return {
25623
25677
  text: "Not armed",
@@ -25627,6 +25681,10 @@ function badgeFor(monitor) {
25627
25681
  text: "Changed",
25628
25682
  tone: "alarm"
25629
25683
  };
25684
+ if (monitor.suspendedCondition !== null) return {
25685
+ text: "Paused",
25686
+ tone: "unknown"
25687
+ };
25630
25688
  if (monitor.verdict === "unknown") return {
25631
25689
  text: "Can’t tell",
25632
25690
  tone: "unknown"
@@ -48647,7 +48705,7 @@ var MotionZonesSettings = (0, react$1.lazy)(() => Promise.resolve().then(() => r
48647
48705
  /** Lazy-wrapped `SceneMonitorEditor` — code-split off the main bundle for the
48648
48706
  * same reason: it mounts `MaskShapeCanvas`, which chains react-konva. Render
48649
48707
  * inside `<Suspense>`; the admin-ui `/scenes` dialog is its only caller. */
48650
- var SceneMonitorEditor = (0, react$1.lazy)(() => Promise.resolve().then(() => require("./SceneMonitorEditor-CfSrNccC.cjs")).then((m) => ({ default: m.SceneMonitorEditor })));
48708
+ var SceneMonitorEditor = (0, react$1.lazy)(() => Promise.resolve().then(() => require("./SceneMonitorEditor-DN2ILWCf.cjs")).then((m) => ({ default: m.SceneMonitorEditor })));
48651
48709
  /** Lazy-wrapped `PrivacyMaskSettings` — code-split off the main bundle. */
48652
48710
  var PrivacyMaskSettings = (0, react$1.lazy)(() => Promise.resolve().then(() => require("./PrivacyMaskSettings-Oo7gyB4E.cjs")).then((m) => ({ default: m.PrivacyMaskSettings })));
48653
48711
  //#endregion
@@ -48783,6 +48841,7 @@ exports.IconAction = IconAction;
48783
48841
  exports.IconButton = IconButton;
48784
48842
  exports.ImageHeroCard = ImageHeroCard;
48785
48843
  exports.ImageInlineControl = ImageInlineControl;
48844
+ exports.ImageOff = ImageOff;
48786
48845
  exports.ImageSelector = ImageSelector;
48787
48846
  exports.InferenceConfigSelector = InferenceConfigSelector;
48788
48847
  exports.Input = Input;
@@ -49429,6 +49488,7 @@ exports.useLlmListNodeModels = useLlmListNodeModels;
49429
49488
  exports.useLlmListProfileKinds = useLlmListProfileKinds;
49430
49489
  exports.useLlmListProfiles = useLlmListProfiles;
49431
49490
  exports.useLlmListRuntimeNodes = useLlmListRuntimeNodes;
49491
+ exports.useLlmResolveModelRef = useLlmResolveModelRef;
49432
49492
  exports.useLlmSetDefault = useLlmSetDefault;
49433
49493
  exports.useLlmStartRuntime = useLlmStartRuntime;
49434
49494
  exports.useLlmStopRuntime = useLlmStopRuntime;