@camstack/ui-library 1.1.38 → 1.1.39

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.
@@ -1,4 +1,4 @@
1
- import { RecordingRule, RecordingMode, RecordingConfig, RecordingStorageMode, RecordingSchedule, RecordingTriggers, RecordingRetention, CamProfile } from '@camstack/types';
1
+ import { RecordingRule, RecordingMode, RecordingConfig, RecordingStorageMode, RecordingSchedule, RecordingTriggers, RecordingRetention, ScrubThumbnailPreset, CamProfile } from '@camstack/types';
2
2
  export declare function emptyRule(): RecordingRule;
3
3
  export declare function isTriggerMode(mode: RecordingMode): boolean;
4
4
  export declare function withRuleAt(rules: readonly RecordingRule[], index: number, patch: Partial<RecordingRule>): RecordingRule[];
@@ -18,6 +18,8 @@ export interface RecordingFormState {
18
18
  readonly preBufferSec?: number;
19
19
  readonly postBufferSec?: number;
20
20
  readonly retention?: RecordingRetention;
21
+ /** Per-camera scrub-thumbnail fidelity preset (absent = standard). */
22
+ readonly scrubThumbnails?: ScrubThumbnailPreset;
21
23
  }
22
24
  /** Read a persisted/migrated config into the Basic form state. `mode` is read
23
25
  * directly; a legacy mode-less config falls back to continuous/off by `enabled`. */
package/dist/index.cjs CHANGED
@@ -33416,7 +33416,8 @@ function formStateFromConfig(cfg) {
33416
33416
  triggers: cfg.triggers ?? {},
33417
33417
  preBufferSec: cfg.preBufferSec,
33418
33418
  postBufferSec: cfg.postBufferSec,
33419
- retention: cfg.retention
33419
+ retention: cfg.retention,
33420
+ scrubThumbnails: cfg.scrubThumbnails
33420
33421
  };
33421
33422
  }
33422
33423
  /** Serialize the Basic form state back to the wire config. `enabled` is derived
@@ -33432,7 +33433,8 @@ function configFromFormState(state) {
33432
33433
  triggers: Object.keys(state.triggers).length > 0 ? state.triggers : void 0,
33433
33434
  preBufferSec: state.preBufferSec,
33434
33435
  postBufferSec: state.postBufferSec,
33435
- retention: state.retention
33436
+ retention: state.retention,
33437
+ scrubThumbnails: state.scrubThumbnails
33436
33438
  };
33437
33439
  }
33438
33440
  /**
@@ -33855,7 +33857,8 @@ function RecordingSettings({ initial, saving, onSave }) {
33855
33857
  const [common, setCommon] = (0, react$1.useState)({
33856
33858
  profiles: initial.profiles,
33857
33859
  segmentSeconds: initial.segmentSeconds,
33858
- retention: initial.retention
33860
+ retention: initial.retention,
33861
+ scrubThumbnails: initial.scrubThumbnails
33859
33862
  });
33860
33863
  const ret = common.retention ?? {};
33861
33864
  const setRet = (patch) => setCommon({
@@ -33866,6 +33869,10 @@ function RecordingSettings({ initial, saving, onSave }) {
33866
33869
  }
33867
33870
  });
33868
33871
  const numOrUndef = (v) => v === "" ? void 0 : Number(v);
33872
+ const parsePreset = (v) => {
33873
+ const parsed = _camstack_types.ScrubThumbnailPresetSchema.safeParse(v);
33874
+ return parsed.success ? parsed.data : _camstack_types.DEFAULT_SCRUB_THUMBNAIL_PRESET;
33875
+ };
33869
33876
  const toggleProfile = (p) => {
33870
33877
  const cur = common.profiles ?? [...PROFILES];
33871
33878
  const next = cur.includes(p) ? cur.filter((x) => x !== p) : [...cur, p];
@@ -33890,14 +33897,16 @@ function RecordingSettings({ initial, saving, onSave }) {
33890
33897
  ...base,
33891
33898
  profiles: common.profiles,
33892
33899
  segmentSeconds: common.segmentSeconds,
33893
- retention: common.retention
33900
+ retention: common.retention,
33901
+ scrubThumbnails: common.scrubThumbnails
33894
33902
  }));
33895
33903
  else onSave({
33896
33904
  enabled: advEnabled,
33897
33905
  rules,
33898
33906
  profiles: common.profiles ? [...common.profiles] : void 0,
33899
33907
  segmentSeconds: common.segmentSeconds,
33900
- retention: common.retention
33908
+ retention: common.retention,
33909
+ scrubThumbnails: common.scrubThumbnails
33901
33910
  });
33902
33911
  };
33903
33912
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -34036,37 +34045,62 @@ function RecordingSettings({ initial, saving, onSave }) {
34036
34045
  }),
34037
34046
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
34038
34047
  className: "flex flex-wrap items-center gap-4 text-xs",
34039
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
34040
- className: "flex items-center gap-2",
34041
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
34042
- className: "text-foreground-subtle",
34043
- children: "Profiles:"
34044
- }), PROFILES.map((p) => {
34045
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
34046
- type: "button",
34047
- onClick: () => toggleProfile(p),
34048
- className: `rounded px-2 py-1 ${common.profiles == null || common.profiles.includes(p) ? "bg-primary text-primary-foreground" : "bg-surface-hover text-foreground-subtle"}`,
34049
- children: p
34050
- }, p);
34051
- })]
34052
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
34053
- className: "flex items-center gap-1",
34054
- children: [
34055
- "Segment",
34056
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
34057
- type: "number",
34058
- min: 1,
34059
- value: common.segmentSeconds ?? "",
34060
- placeholder: "default",
34048
+ children: [
34049
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
34050
+ className: "flex items-center gap-2",
34051
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
34052
+ className: "text-foreground-subtle",
34053
+ children: "Profiles:"
34054
+ }), PROFILES.map((p) => {
34055
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
34056
+ type: "button",
34057
+ onClick: () => toggleProfile(p),
34058
+ className: `rounded px-2 py-1 ${common.profiles == null || common.profiles.includes(p) ? "bg-primary text-primary-foreground" : "bg-surface-hover text-foreground-subtle"}`,
34059
+ children: p
34060
+ }, p);
34061
+ })]
34062
+ }),
34063
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
34064
+ className: "flex items-center gap-1",
34065
+ children: [
34066
+ "Segment",
34067
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
34068
+ type: "number",
34069
+ min: 1,
34070
+ value: common.segmentSeconds ?? "",
34071
+ placeholder: "default",
34072
+ onChange: (e) => setCommon({
34073
+ ...common,
34074
+ segmentSeconds: numOrUndef(e.target.value)
34075
+ }),
34076
+ className: "w-16 rounded-md border border-border bg-background px-1 py-1 text-xs"
34077
+ }),
34078
+ "s"
34079
+ ]
34080
+ }),
34081
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
34082
+ className: "flex items-center gap-1",
34083
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
34084
+ className: "text-foreground-subtle",
34085
+ children: "Scrub preview quality"
34086
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
34087
+ value: common.scrubThumbnails ?? _camstack_types.DEFAULT_SCRUB_THUMBNAIL_PRESET,
34061
34088
  onChange: (e) => setCommon({
34062
34089
  ...common,
34063
- segmentSeconds: numOrUndef(e.target.value)
34090
+ scrubThumbnails: parsePreset(e.target.value)
34064
34091
  }),
34065
- className: "w-16 rounded-md border border-border bg-background px-1 py-1 text-xs"
34066
- }),
34067
- "s"
34068
- ]
34069
- })]
34092
+ className: "rounded-md border border-border bg-background px-1 py-1 text-xs",
34093
+ children: _camstack_types.SCRUB_THUMBNAIL_PRESET_ORDER.map((p) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
34094
+ value: p,
34095
+ children: _camstack_types.SCRUB_THUMBNAIL_PRESET_LABELS[p]
34096
+ }, p))
34097
+ })]
34098
+ })
34099
+ ]
34100
+ }),
34101
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
34102
+ className: "text-[11px] text-foreground-subtle",
34103
+ children: "Timeline scrub-preview resolution + quality. Higher = crisper previews but larger sprite sheets. Applies to NEW recordings only — existing preview sheets keep their original resolution."
34070
34104
  }),
34071
34105
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
34072
34106
  className: "mb-1.5 text-[11px] font-semibold uppercase tracking-wider text-foreground-subtle",
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import * as __mfHostReact from "react";
3
3
  import React, { Fragment, Suspense, createContext, createElement, forwardRef, lazy, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
4
4
  import * as __mfHostReactJsxRuntime from "react/jsx-runtime";
5
5
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
6
- import { ALL_CAPABILITY_DEFINITIONS, DeviceExportStatusSchema, DeviceFeature, DeviceRole, DeviceType, EventCategory, ExposedDeviceSchema, KNOWN_CAP_NAMES, PET_FEEDER_MANUAL_FEED_MAX, PET_FEEDER_MANUAL_FEED_MIN, RecordingConfigSchema, RecordingStatusSchema, SourceInfoSchema, TIMEZONES, buildModelVariantGroups, canConvertUnit, createDeviceProxy, createLazyTrpcSource, createSliceHandle, describeModelVariant, findTimezone, resolveVariantModelId, tryConvertUnit } from "@camstack/types";
6
+ import { ALL_CAPABILITY_DEFINITIONS, DEFAULT_SCRUB_THUMBNAIL_PRESET, DeviceExportStatusSchema, DeviceFeature, DeviceRole, DeviceType, EventCategory, ExposedDeviceSchema, KNOWN_CAP_NAMES, PET_FEEDER_MANUAL_FEED_MAX, PET_FEEDER_MANUAL_FEED_MIN, RecordingConfigSchema, RecordingStatusSchema, SCRUB_THUMBNAIL_PRESET_LABELS, SCRUB_THUMBNAIL_PRESET_ORDER, ScrubThumbnailPresetSchema, SourceInfoSchema, TIMEZONES, buildModelVariantGroups, canConvertUnit, createDeviceProxy, createLazyTrpcSource, createSliceHandle, describeModelVariant, findTimezone, resolveVariantModelId, tryConvertUnit } from "@camstack/types";
7
7
  import * as __mfHostReactDOM from "react-dom";
8
8
  import { createPortal } from "react-dom";
9
9
  import * as __mfHostReactDOMClient from "react-dom/client";
@@ -33392,7 +33392,8 @@ function formStateFromConfig(cfg) {
33392
33392
  triggers: cfg.triggers ?? {},
33393
33393
  preBufferSec: cfg.preBufferSec,
33394
33394
  postBufferSec: cfg.postBufferSec,
33395
- retention: cfg.retention
33395
+ retention: cfg.retention,
33396
+ scrubThumbnails: cfg.scrubThumbnails
33396
33397
  };
33397
33398
  }
33398
33399
  /** Serialize the Basic form state back to the wire config. `enabled` is derived
@@ -33408,7 +33409,8 @@ function configFromFormState(state) {
33408
33409
  triggers: Object.keys(state.triggers).length > 0 ? state.triggers : void 0,
33409
33410
  preBufferSec: state.preBufferSec,
33410
33411
  postBufferSec: state.postBufferSec,
33411
- retention: state.retention
33412
+ retention: state.retention,
33413
+ scrubThumbnails: state.scrubThumbnails
33412
33414
  };
33413
33415
  }
33414
33416
  /**
@@ -33831,7 +33833,8 @@ function RecordingSettings({ initial, saving, onSave }) {
33831
33833
  const [common, setCommon] = useState({
33832
33834
  profiles: initial.profiles,
33833
33835
  segmentSeconds: initial.segmentSeconds,
33834
- retention: initial.retention
33836
+ retention: initial.retention,
33837
+ scrubThumbnails: initial.scrubThumbnails
33835
33838
  });
33836
33839
  const ret = common.retention ?? {};
33837
33840
  const setRet = (patch) => setCommon({
@@ -33842,6 +33845,10 @@ function RecordingSettings({ initial, saving, onSave }) {
33842
33845
  }
33843
33846
  });
33844
33847
  const numOrUndef = (v) => v === "" ? void 0 : Number(v);
33848
+ const parsePreset = (v) => {
33849
+ const parsed = ScrubThumbnailPresetSchema.safeParse(v);
33850
+ return parsed.success ? parsed.data : DEFAULT_SCRUB_THUMBNAIL_PRESET;
33851
+ };
33845
33852
  const toggleProfile = (p) => {
33846
33853
  const cur = common.profiles ?? [...PROFILES];
33847
33854
  const next = cur.includes(p) ? cur.filter((x) => x !== p) : [...cur, p];
@@ -33866,14 +33873,16 @@ function RecordingSettings({ initial, saving, onSave }) {
33866
33873
  ...base,
33867
33874
  profiles: common.profiles,
33868
33875
  segmentSeconds: common.segmentSeconds,
33869
- retention: common.retention
33876
+ retention: common.retention,
33877
+ scrubThumbnails: common.scrubThumbnails
33870
33878
  }));
33871
33879
  else onSave({
33872
33880
  enabled: advEnabled,
33873
33881
  rules,
33874
33882
  profiles: common.profiles ? [...common.profiles] : void 0,
33875
33883
  segmentSeconds: common.segmentSeconds,
33876
- retention: common.retention
33884
+ retention: common.retention,
33885
+ scrubThumbnails: common.scrubThumbnails
33877
33886
  });
33878
33887
  };
33879
33888
  return /* @__PURE__ */ jsxs("div", {
@@ -34012,37 +34021,62 @@ function RecordingSettings({ initial, saving, onSave }) {
34012
34021
  }),
34013
34022
  /* @__PURE__ */ jsxs("div", {
34014
34023
  className: "flex flex-wrap items-center gap-4 text-xs",
34015
- children: [/* @__PURE__ */ jsxs("div", {
34016
- className: "flex items-center gap-2",
34017
- children: [/* @__PURE__ */ jsx("span", {
34018
- className: "text-foreground-subtle",
34019
- children: "Profiles:"
34020
- }), PROFILES.map((p) => {
34021
- return /* @__PURE__ */ jsx("button", {
34022
- type: "button",
34023
- onClick: () => toggleProfile(p),
34024
- className: `rounded px-2 py-1 ${common.profiles == null || common.profiles.includes(p) ? "bg-primary text-primary-foreground" : "bg-surface-hover text-foreground-subtle"}`,
34025
- children: p
34026
- }, p);
34027
- })]
34028
- }), /* @__PURE__ */ jsxs("label", {
34029
- className: "flex items-center gap-1",
34030
- children: [
34031
- "Segment",
34032
- /* @__PURE__ */ jsx("input", {
34033
- type: "number",
34034
- min: 1,
34035
- value: common.segmentSeconds ?? "",
34036
- placeholder: "default",
34024
+ children: [
34025
+ /* @__PURE__ */ jsxs("div", {
34026
+ className: "flex items-center gap-2",
34027
+ children: [/* @__PURE__ */ jsx("span", {
34028
+ className: "text-foreground-subtle",
34029
+ children: "Profiles:"
34030
+ }), PROFILES.map((p) => {
34031
+ return /* @__PURE__ */ jsx("button", {
34032
+ type: "button",
34033
+ onClick: () => toggleProfile(p),
34034
+ className: `rounded px-2 py-1 ${common.profiles == null || common.profiles.includes(p) ? "bg-primary text-primary-foreground" : "bg-surface-hover text-foreground-subtle"}`,
34035
+ children: p
34036
+ }, p);
34037
+ })]
34038
+ }),
34039
+ /* @__PURE__ */ jsxs("label", {
34040
+ className: "flex items-center gap-1",
34041
+ children: [
34042
+ "Segment",
34043
+ /* @__PURE__ */ jsx("input", {
34044
+ type: "number",
34045
+ min: 1,
34046
+ value: common.segmentSeconds ?? "",
34047
+ placeholder: "default",
34048
+ onChange: (e) => setCommon({
34049
+ ...common,
34050
+ segmentSeconds: numOrUndef(e.target.value)
34051
+ }),
34052
+ className: "w-16 rounded-md border border-border bg-background px-1 py-1 text-xs"
34053
+ }),
34054
+ "s"
34055
+ ]
34056
+ }),
34057
+ /* @__PURE__ */ jsxs("label", {
34058
+ className: "flex items-center gap-1",
34059
+ children: [/* @__PURE__ */ jsx("span", {
34060
+ className: "text-foreground-subtle",
34061
+ children: "Scrub preview quality"
34062
+ }), /* @__PURE__ */ jsx("select", {
34063
+ value: common.scrubThumbnails ?? DEFAULT_SCRUB_THUMBNAIL_PRESET,
34037
34064
  onChange: (e) => setCommon({
34038
34065
  ...common,
34039
- segmentSeconds: numOrUndef(e.target.value)
34066
+ scrubThumbnails: parsePreset(e.target.value)
34040
34067
  }),
34041
- className: "w-16 rounded-md border border-border bg-background px-1 py-1 text-xs"
34042
- }),
34043
- "s"
34044
- ]
34045
- })]
34068
+ className: "rounded-md border border-border bg-background px-1 py-1 text-xs",
34069
+ children: SCRUB_THUMBNAIL_PRESET_ORDER.map((p) => /* @__PURE__ */ jsx("option", {
34070
+ value: p,
34071
+ children: SCRUB_THUMBNAIL_PRESET_LABELS[p]
34072
+ }, p))
34073
+ })]
34074
+ })
34075
+ ]
34076
+ }),
34077
+ /* @__PURE__ */ jsx("p", {
34078
+ className: "text-[11px] text-foreground-subtle",
34079
+ children: "Timeline scrub-preview resolution + quality. Higher = crisper previews but larger sprite sheets. Applies to NEW recordings only — existing preview sheets keep their original resolution."
34046
34080
  }),
34047
34081
  /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
34048
34082
  className: "mb-1.5 text-[11px] font-semibold uppercase tracking-wider text-foreground-subtle",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/ui-library",
3
- "version": "1.1.38",
3
+ "version": "1.1.39",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",