@camstack/ui-library 1.2.61 → 1.2.63

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/index.cjs CHANGED
@@ -7161,6 +7161,115 @@ function useCustomFieldRenderer(type) {
7161
7161
  return (0, react$1.useContext)(CustomFieldRenderersContext)[type] ?? null;
7162
7162
  }
7163
7163
  //#endregion
7164
+ //#region src/composites/event-kind-visuals.tsx
7165
+ /**
7166
+ * Event-kind VISUALS — the UI-side half of the unified taxonomy dictionary
7167
+ * (`@camstack/types` `EVENT_TAXONOMY` owns the serializable data:
7168
+ * color / iconId / labelKey; this owns `iconId → lucide component` and
7169
+ * `labelKey → t()`).
7170
+ *
7171
+ * Every UI that renders an event kind (timeline, events tree, chips,
7172
+ * detection canvas/tree) resolves its glyph + translated label here — no
7173
+ * per-component hardcoded icon or label.
7174
+ */
7175
+ /**
7176
+ * `iconId` → lucide component. Built ONLY from statically-imported icons
7177
+ * (bundle-safe). Unknown ids resolve to the neutral `Tag` fallback.
7178
+ */
7179
+ var EVENT_KIND_ICONS = {
7180
+ motion: Activity,
7181
+ audio: Volume2,
7182
+ person: User,
7183
+ vehicle: Car,
7184
+ animal: PawPrint,
7185
+ package: Package,
7186
+ sensor: Radio,
7187
+ control: SlidersHorizontal,
7188
+ car: Car,
7189
+ truck: Truck,
7190
+ bus: Bus,
7191
+ motorcycle: Bike,
7192
+ bicycle: Bike,
7193
+ airplane: Plane,
7194
+ boat: Sailboat,
7195
+ train: TrainFront,
7196
+ bird: Bird,
7197
+ cat: Cat,
7198
+ dog: Dog,
7199
+ horse: PawPrint,
7200
+ sheep: PawPrint,
7201
+ cow: PawPrint,
7202
+ elephant: PawPrint,
7203
+ bear: PawPrint,
7204
+ zebra: PawPrint,
7205
+ giraffe: PawPrint,
7206
+ door: DoorOpen,
7207
+ pir: Radar,
7208
+ smoke: Flame,
7209
+ water: Droplet,
7210
+ gas: Wind,
7211
+ vibration: Vibrate,
7212
+ tamper: ShieldAlert,
7213
+ presence: UserCheck,
7214
+ generic: Tag,
7215
+ lock: Lock,
7216
+ siren: Siren,
7217
+ switch: ToggleRight,
7218
+ doorbell: BellRing,
7219
+ button: CircleDot,
7220
+ "audio-speech": MessageSquare,
7221
+ "audio-scream": Megaphone,
7222
+ "audio-crying": Baby,
7223
+ "audio-laughter": Laugh,
7224
+ "audio-music": Music,
7225
+ "audio-dog": Dog,
7226
+ "audio-cat": Cat,
7227
+ "audio-bird": Bird,
7228
+ "audio-animal": PawPrint,
7229
+ "audio-alarm": BellRing,
7230
+ "audio-doorbell": BellRing,
7231
+ "audio-glass_breaking": TriangleAlert,
7232
+ "audio-gunshot": Crosshair,
7233
+ "audio-vehicle": Car,
7234
+ "audio-siren": Siren,
7235
+ "audio-fire": Flame,
7236
+ "audio-water": Droplets,
7237
+ "audio-wind": Wind,
7238
+ "audio-door": DoorOpen,
7239
+ "audio-footsteps": Footprints,
7240
+ "audio-crowd": Users,
7241
+ "audio-telephone": Phone,
7242
+ "audio-engine": Cog,
7243
+ "audio-tools": Hammer,
7244
+ "audio-silence": VolumeX
7245
+ };
7246
+ /** Resolve an `iconId` to a lucide component (falls back to `Tag`). */
7247
+ function resolveEventKindIcon(iconId) {
7248
+ if (Object.hasOwn(EVENT_KIND_ICONS, iconId)) return EVENT_KIND_ICONS[iconId];
7249
+ if (iconId.startsWith("audio-")) return Volume2;
7250
+ return Tag;
7251
+ }
7252
+ /**
7253
+ * Translated label for an event kind — `t(labelKey)` when a translation
7254
+ * exists, else the English `label` fallback. Robust to a missing key: an
7255
+ * i18n backend that echoes the key unchanged (react-i18next default) is
7256
+ * treated as "no translation" and the fallback label is returned, so the UI
7257
+ * never renders a raw `eventKind.*` key.
7258
+ */
7259
+ function eventKindLabel(descriptor, t) {
7260
+ if (t === void 0) return descriptor.label;
7261
+ const translated = t(descriptor.labelKey);
7262
+ return translated === descriptor.labelKey || translated.length === 0 ? descriptor.label : translated;
7263
+ }
7264
+ /** Small convenience component: renders the lucide glyph for an `iconId`. */
7265
+ function EventKindGlyph({ iconId, size = 16, color, className }) {
7266
+ return (0, react$1.createElement)(resolveEventKindIcon(iconId), {
7267
+ size,
7268
+ color,
7269
+ className
7270
+ });
7271
+ }
7272
+ //#endregion
7164
7273
  //#region src/composites/confirm-dialog.tsx
7165
7274
  var ConfirmContext = createSharedContext("camstack:confirm-dialog", null);
7166
7275
  function useConfirm() {
@@ -19727,13 +19836,20 @@ function MultiSelectField({ field, value, onChange, disabled, translationFn }) {
19727
19836
  checked ? "border-primary bg-primary/10 text-primary" : "border-border bg-background text-foreground hover:bg-surface",
19728
19837
  disabled || field.disabled ? "opacity-50 cursor-not-allowed" : ""
19729
19838
  ].join(" "),
19730
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
19731
- type: "checkbox",
19732
- className: "sr-only",
19733
- checked,
19734
- disabled: disabled || field.disabled,
19735
- onChange: () => toggle(opt.value)
19736
- }), opt.label]
19839
+ children: [
19840
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
19841
+ type: "checkbox",
19842
+ className: "sr-only",
19843
+ checked,
19844
+ disabled: disabled || field.disabled,
19845
+ onChange: () => toggle(opt.value)
19846
+ }),
19847
+ opt.iconId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EventKindGlyph, {
19848
+ iconId: opt.iconId,
19849
+ size: 13
19850
+ }),
19851
+ opt.label
19852
+ ]
19737
19853
  }, opt.value);
19738
19854
  })
19739
19855
  })
@@ -21615,7 +21731,7 @@ function GroupedModelSelector({ catalog, value, onChange, disabled }) {
21615
21731
  const resolutions = [...new Set(tier.options.map((o) => o.resolution))].sort((a, b) => (b ?? Infinity) - (a ?? Infinity));
21616
21732
  const selectedResolution = resolutions.includes(current?.resolution) ? current?.resolution : resolutions[0];
21617
21733
  const variantOptions = tier.options.filter((o) => o.resolution === selectedResolution);
21618
- const resLabel = (r) => r === void 0 ? "640 · native" : `${r}`;
21734
+ const resLabel = (r) => r === void 0 ? "native" : `${r}`;
21619
21735
  const selectTier = (tierId) => {
21620
21736
  const t = family.tiers.find((x) => x.tier === tierId);
21621
21737
  if (!t) return;
@@ -21659,7 +21775,7 @@ function GroupedModelSelector({ catalog, value, onChange, disabled }) {
21659
21775
  className: "grid grid-cols-2 gap-1.5 sm:grid-cols-4",
21660
21776
  children: family.tiers.map((t) => {
21661
21777
  const active = t.tier === tier.tier;
21662
- const base = t.options.find((o) => o.modelId === t.baseModelId) ?? t.options[0];
21778
+ const quoted = t.options.find((o) => o.resolution === selectedResolution && o.precision === "fp32") ?? t.options.find((o) => o.modelId === t.baseModelId) ?? t.options[0];
21663
21779
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
21664
21780
  type: "button",
21665
21781
  disabled,
@@ -21675,7 +21791,7 @@ function GroupedModelSelector({ catalog, value, onChange, disabled }) {
21675
21791
  className: "text-[10px] text-foreground-subtle",
21676
21792
  children: [
21677
21793
  "~",
21678
- base?.sizeMB ?? 0,
21794
+ quoted?.sizeMB ?? 0,
21679
21795
  " MB"
21680
21796
  ]
21681
21797
  })]
@@ -21975,12 +22091,15 @@ function ConfigMultiSelect({ label, description, value, options, disabled, onCha
21975
22091
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
21976
22092
  className: "flex flex-wrap gap-1.5",
21977
22093
  children: options.map((o) => {
21978
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
22094
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
21979
22095
  type: "button",
21980
22096
  disabled,
21981
22097
  onClick: () => toggle(o.value),
21982
- className: cn("text-[10px] font-medium px-2 py-1 rounded-md border transition-colors", value.includes(o.value) ? "bg-primary/20 text-primary border-primary/40" : "bg-surface text-foreground-subtle border-border hover:border-primary/30", disabled && "opacity-50 cursor-not-allowed"),
21983
- children: o.label
22098
+ className: cn("inline-flex items-center gap-1 text-[10px] font-medium px-2 py-1 rounded-md border transition-colors", value.includes(o.value) ? "bg-primary/20 text-primary border-primary/40" : "bg-surface text-foreground-subtle border-border hover:border-primary/30", disabled && "opacity-50 cursor-not-allowed"),
22099
+ children: [o.iconId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EventKindGlyph, {
22100
+ iconId: o.iconId,
22101
+ size: 12
22102
+ }), o.label]
21984
22103
  }, o.value);
21985
22104
  })
21986
22105
  })
@@ -44672,115 +44791,6 @@ function DoorbellRecentPanel({ history, limit = 5, latestIsFresh, className }) {
44672
44791
  });
44673
44792
  }
44674
44793
  //#endregion
44675
- //#region src/composites/event-kind-visuals.tsx
44676
- /**
44677
- * Event-kind VISUALS — the UI-side half of the unified taxonomy dictionary
44678
- * (`@camstack/types` `EVENT_TAXONOMY` owns the serializable data:
44679
- * color / iconId / labelKey; this owns `iconId → lucide component` and
44680
- * `labelKey → t()`).
44681
- *
44682
- * Every UI that renders an event kind (timeline, events tree, chips,
44683
- * detection canvas/tree) resolves its glyph + translated label here — no
44684
- * per-component hardcoded icon or label.
44685
- */
44686
- /**
44687
- * `iconId` → lucide component. Built ONLY from statically-imported icons
44688
- * (bundle-safe). Unknown ids resolve to the neutral `Tag` fallback.
44689
- */
44690
- var EVENT_KIND_ICONS = {
44691
- motion: Activity,
44692
- audio: Volume2,
44693
- person: User,
44694
- vehicle: Car,
44695
- animal: PawPrint,
44696
- package: Package,
44697
- sensor: Radio,
44698
- control: SlidersHorizontal,
44699
- car: Car,
44700
- truck: Truck,
44701
- bus: Bus,
44702
- motorcycle: Bike,
44703
- bicycle: Bike,
44704
- airplane: Plane,
44705
- boat: Sailboat,
44706
- train: TrainFront,
44707
- bird: Bird,
44708
- cat: Cat,
44709
- dog: Dog,
44710
- horse: PawPrint,
44711
- sheep: PawPrint,
44712
- cow: PawPrint,
44713
- elephant: PawPrint,
44714
- bear: PawPrint,
44715
- zebra: PawPrint,
44716
- giraffe: PawPrint,
44717
- door: DoorOpen,
44718
- pir: Radar,
44719
- smoke: Flame,
44720
- water: Droplet,
44721
- gas: Wind,
44722
- vibration: Vibrate,
44723
- tamper: ShieldAlert,
44724
- presence: UserCheck,
44725
- generic: Tag,
44726
- lock: Lock,
44727
- siren: Siren,
44728
- switch: ToggleRight,
44729
- doorbell: BellRing,
44730
- button: CircleDot,
44731
- "audio-speech": MessageSquare,
44732
- "audio-scream": Megaphone,
44733
- "audio-crying": Baby,
44734
- "audio-laughter": Laugh,
44735
- "audio-music": Music,
44736
- "audio-dog": Dog,
44737
- "audio-cat": Cat,
44738
- "audio-bird": Bird,
44739
- "audio-animal": PawPrint,
44740
- "audio-alarm": BellRing,
44741
- "audio-doorbell": BellRing,
44742
- "audio-glass_breaking": TriangleAlert,
44743
- "audio-gunshot": Crosshair,
44744
- "audio-vehicle": Car,
44745
- "audio-siren": Siren,
44746
- "audio-fire": Flame,
44747
- "audio-water": Droplets,
44748
- "audio-wind": Wind,
44749
- "audio-door": DoorOpen,
44750
- "audio-footsteps": Footprints,
44751
- "audio-crowd": Users,
44752
- "audio-telephone": Phone,
44753
- "audio-engine": Cog,
44754
- "audio-tools": Hammer,
44755
- "audio-silence": VolumeX
44756
- };
44757
- /** Resolve an `iconId` to a lucide component (falls back to `Tag`). */
44758
- function resolveEventKindIcon(iconId) {
44759
- if (Object.hasOwn(EVENT_KIND_ICONS, iconId)) return EVENT_KIND_ICONS[iconId];
44760
- if (iconId.startsWith("audio-")) return Volume2;
44761
- return Tag;
44762
- }
44763
- /**
44764
- * Translated label for an event kind — `t(labelKey)` when a translation
44765
- * exists, else the English `label` fallback. Robust to a missing key: an
44766
- * i18n backend that echoes the key unchanged (react-i18next default) is
44767
- * treated as "no translation" and the fallback label is returned, so the UI
44768
- * never renders a raw `eventKind.*` key.
44769
- */
44770
- function eventKindLabel(descriptor, t) {
44771
- if (t === void 0) return descriptor.label;
44772
- const translated = t(descriptor.labelKey);
44773
- return translated === descriptor.labelKey || translated.length === 0 ? descriptor.label : translated;
44774
- }
44775
- /** Small convenience component: renders the lucide glyph for an `iconId`. */
44776
- function EventKindGlyph({ iconId, size = 16, color, className }) {
44777
- return (0, react$1.createElement)(resolveEventKindIcon(iconId), {
44778
- size,
44779
- color,
44780
- className
44781
- });
44782
- }
44783
- //#endregion
44784
44794
  //#region src/composites/filter-bar.tsx
44785
44795
  function FilterBar({ filters, values, onChange, className }) {
44786
44796
  const isMobile = useIsMobile();
@@ -48360,6 +48370,8 @@ function useSystemMutation(select, opts) {
48360
48370
  * — the original admin-ui copy stayed put for backward compatibility
48361
48371
  * but new consumers should import from here.
48362
48372
  */
48373
+ /** Trailing coalesce for event-driven invalidation — see the comment inside. */
48374
+ var EVENT_INVALIDATION_COALESCE_MS = 1e3;
48363
48375
  function useEventInvalidation(queryKey, categories) {
48364
48376
  const system = useSystem();
48365
48377
  const queryClient = (0, _tanstack_react_query.useQueryClient)();
@@ -48373,12 +48385,20 @@ function useEventInvalidation(queryKey, categories) {
48373
48385
  const keyJson = JSON.stringify(queryKey);
48374
48386
  (0, react$1.useEffect)(() => {
48375
48387
  const subs = [];
48376
- const refresh = () => {
48388
+ let pending = null;
48389
+ const refreshNow = () => {
48377
48390
  const raw = JSON.parse(keyJson);
48378
48391
  const queryKeyToInvalidate = raw.length === 1 && Array.isArray(raw[0]) ? raw : [raw];
48379
48392
  queryClient.invalidateQueries({ queryKey: queryKeyToInvalidate });
48380
48393
  };
48381
- refresh();
48394
+ const refresh = () => {
48395
+ if (pending !== null) return;
48396
+ pending = setTimeout(() => {
48397
+ pending = null;
48398
+ refreshNow();
48399
+ }, EVENT_INVALIDATION_COALESCE_MS);
48400
+ };
48401
+ refreshNow();
48382
48402
  for (const category of key.split("|")) {
48383
48403
  if (!category) continue;
48384
48404
  try {
@@ -48386,6 +48406,7 @@ function useEventInvalidation(queryKey, categories) {
48386
48406
  } catch {}
48387
48407
  }
48388
48408
  return () => {
48409
+ if (pending !== null) clearTimeout(pending);
48389
48410
  for (const unsub of subs) unsub();
48390
48411
  };
48391
48412
  }, [
package/dist/index.js CHANGED
@@ -7137,6 +7137,115 @@ function useCustomFieldRenderer(type) {
7137
7137
  return useContext(CustomFieldRenderersContext)[type] ?? null;
7138
7138
  }
7139
7139
  //#endregion
7140
+ //#region src/composites/event-kind-visuals.tsx
7141
+ /**
7142
+ * Event-kind VISUALS — the UI-side half of the unified taxonomy dictionary
7143
+ * (`@camstack/types` `EVENT_TAXONOMY` owns the serializable data:
7144
+ * color / iconId / labelKey; this owns `iconId → lucide component` and
7145
+ * `labelKey → t()`).
7146
+ *
7147
+ * Every UI that renders an event kind (timeline, events tree, chips,
7148
+ * detection canvas/tree) resolves its glyph + translated label here — no
7149
+ * per-component hardcoded icon or label.
7150
+ */
7151
+ /**
7152
+ * `iconId` → lucide component. Built ONLY from statically-imported icons
7153
+ * (bundle-safe). Unknown ids resolve to the neutral `Tag` fallback.
7154
+ */
7155
+ var EVENT_KIND_ICONS = {
7156
+ motion: Activity,
7157
+ audio: Volume2,
7158
+ person: User,
7159
+ vehicle: Car,
7160
+ animal: PawPrint,
7161
+ package: Package,
7162
+ sensor: Radio,
7163
+ control: SlidersHorizontal,
7164
+ car: Car,
7165
+ truck: Truck,
7166
+ bus: Bus,
7167
+ motorcycle: Bike,
7168
+ bicycle: Bike,
7169
+ airplane: Plane,
7170
+ boat: Sailboat,
7171
+ train: TrainFront,
7172
+ bird: Bird,
7173
+ cat: Cat,
7174
+ dog: Dog,
7175
+ horse: PawPrint,
7176
+ sheep: PawPrint,
7177
+ cow: PawPrint,
7178
+ elephant: PawPrint,
7179
+ bear: PawPrint,
7180
+ zebra: PawPrint,
7181
+ giraffe: PawPrint,
7182
+ door: DoorOpen,
7183
+ pir: Radar,
7184
+ smoke: Flame,
7185
+ water: Droplet,
7186
+ gas: Wind,
7187
+ vibration: Vibrate,
7188
+ tamper: ShieldAlert,
7189
+ presence: UserCheck,
7190
+ generic: Tag,
7191
+ lock: Lock,
7192
+ siren: Siren,
7193
+ switch: ToggleRight,
7194
+ doorbell: BellRing,
7195
+ button: CircleDot,
7196
+ "audio-speech": MessageSquare,
7197
+ "audio-scream": Megaphone,
7198
+ "audio-crying": Baby,
7199
+ "audio-laughter": Laugh,
7200
+ "audio-music": Music,
7201
+ "audio-dog": Dog,
7202
+ "audio-cat": Cat,
7203
+ "audio-bird": Bird,
7204
+ "audio-animal": PawPrint,
7205
+ "audio-alarm": BellRing,
7206
+ "audio-doorbell": BellRing,
7207
+ "audio-glass_breaking": TriangleAlert,
7208
+ "audio-gunshot": Crosshair,
7209
+ "audio-vehicle": Car,
7210
+ "audio-siren": Siren,
7211
+ "audio-fire": Flame,
7212
+ "audio-water": Droplets,
7213
+ "audio-wind": Wind,
7214
+ "audio-door": DoorOpen,
7215
+ "audio-footsteps": Footprints,
7216
+ "audio-crowd": Users,
7217
+ "audio-telephone": Phone,
7218
+ "audio-engine": Cog,
7219
+ "audio-tools": Hammer,
7220
+ "audio-silence": VolumeX
7221
+ };
7222
+ /** Resolve an `iconId` to a lucide component (falls back to `Tag`). */
7223
+ function resolveEventKindIcon(iconId) {
7224
+ if (Object.hasOwn(EVENT_KIND_ICONS, iconId)) return EVENT_KIND_ICONS[iconId];
7225
+ if (iconId.startsWith("audio-")) return Volume2;
7226
+ return Tag;
7227
+ }
7228
+ /**
7229
+ * Translated label for an event kind — `t(labelKey)` when a translation
7230
+ * exists, else the English `label` fallback. Robust to a missing key: an
7231
+ * i18n backend that echoes the key unchanged (react-i18next default) is
7232
+ * treated as "no translation" and the fallback label is returned, so the UI
7233
+ * never renders a raw `eventKind.*` key.
7234
+ */
7235
+ function eventKindLabel(descriptor, t) {
7236
+ if (t === void 0) return descriptor.label;
7237
+ const translated = t(descriptor.labelKey);
7238
+ return translated === descriptor.labelKey || translated.length === 0 ? descriptor.label : translated;
7239
+ }
7240
+ /** Small convenience component: renders the lucide glyph for an `iconId`. */
7241
+ function EventKindGlyph({ iconId, size = 16, color, className }) {
7242
+ return createElement(resolveEventKindIcon(iconId), {
7243
+ size,
7244
+ color,
7245
+ className
7246
+ });
7247
+ }
7248
+ //#endregion
7140
7249
  //#region src/composites/confirm-dialog.tsx
7141
7250
  var ConfirmContext = createSharedContext("camstack:confirm-dialog", null);
7142
7251
  function useConfirm() {
@@ -19703,13 +19812,20 @@ function MultiSelectField({ field, value, onChange, disabled, translationFn }) {
19703
19812
  checked ? "border-primary bg-primary/10 text-primary" : "border-border bg-background text-foreground hover:bg-surface",
19704
19813
  disabled || field.disabled ? "opacity-50 cursor-not-allowed" : ""
19705
19814
  ].join(" "),
19706
- children: [/* @__PURE__ */ jsx("input", {
19707
- type: "checkbox",
19708
- className: "sr-only",
19709
- checked,
19710
- disabled: disabled || field.disabled,
19711
- onChange: () => toggle(opt.value)
19712
- }), opt.label]
19815
+ children: [
19816
+ /* @__PURE__ */ jsx("input", {
19817
+ type: "checkbox",
19818
+ className: "sr-only",
19819
+ checked,
19820
+ disabled: disabled || field.disabled,
19821
+ onChange: () => toggle(opt.value)
19822
+ }),
19823
+ opt.iconId !== void 0 && /* @__PURE__ */ jsx(EventKindGlyph, {
19824
+ iconId: opt.iconId,
19825
+ size: 13
19826
+ }),
19827
+ opt.label
19828
+ ]
19713
19829
  }, opt.value);
19714
19830
  })
19715
19831
  })
@@ -21591,7 +21707,7 @@ function GroupedModelSelector({ catalog, value, onChange, disabled }) {
21591
21707
  const resolutions = [...new Set(tier.options.map((o) => o.resolution))].sort((a, b) => (b ?? Infinity) - (a ?? Infinity));
21592
21708
  const selectedResolution = resolutions.includes(current?.resolution) ? current?.resolution : resolutions[0];
21593
21709
  const variantOptions = tier.options.filter((o) => o.resolution === selectedResolution);
21594
- const resLabel = (r) => r === void 0 ? "640 · native" : `${r}`;
21710
+ const resLabel = (r) => r === void 0 ? "native" : `${r}`;
21595
21711
  const selectTier = (tierId) => {
21596
21712
  const t = family.tiers.find((x) => x.tier === tierId);
21597
21713
  if (!t) return;
@@ -21635,7 +21751,7 @@ function GroupedModelSelector({ catalog, value, onChange, disabled }) {
21635
21751
  className: "grid grid-cols-2 gap-1.5 sm:grid-cols-4",
21636
21752
  children: family.tiers.map((t) => {
21637
21753
  const active = t.tier === tier.tier;
21638
- const base = t.options.find((o) => o.modelId === t.baseModelId) ?? t.options[0];
21754
+ const quoted = t.options.find((o) => o.resolution === selectedResolution && o.precision === "fp32") ?? t.options.find((o) => o.modelId === t.baseModelId) ?? t.options[0];
21639
21755
  return /* @__PURE__ */ jsxs("button", {
21640
21756
  type: "button",
21641
21757
  disabled,
@@ -21651,7 +21767,7 @@ function GroupedModelSelector({ catalog, value, onChange, disabled }) {
21651
21767
  className: "text-[10px] text-foreground-subtle",
21652
21768
  children: [
21653
21769
  "~",
21654
- base?.sizeMB ?? 0,
21770
+ quoted?.sizeMB ?? 0,
21655
21771
  " MB"
21656
21772
  ]
21657
21773
  })]
@@ -21951,12 +22067,15 @@ function ConfigMultiSelect({ label, description, value, options, disabled, onCha
21951
22067
  /* @__PURE__ */ jsx("div", {
21952
22068
  className: "flex flex-wrap gap-1.5",
21953
22069
  children: options.map((o) => {
21954
- return /* @__PURE__ */ jsx("button", {
22070
+ return /* @__PURE__ */ jsxs("button", {
21955
22071
  type: "button",
21956
22072
  disabled,
21957
22073
  onClick: () => toggle(o.value),
21958
- className: cn("text-[10px] font-medium px-2 py-1 rounded-md border transition-colors", value.includes(o.value) ? "bg-primary/20 text-primary border-primary/40" : "bg-surface text-foreground-subtle border-border hover:border-primary/30", disabled && "opacity-50 cursor-not-allowed"),
21959
- children: o.label
22074
+ className: cn("inline-flex items-center gap-1 text-[10px] font-medium px-2 py-1 rounded-md border transition-colors", value.includes(o.value) ? "bg-primary/20 text-primary border-primary/40" : "bg-surface text-foreground-subtle border-border hover:border-primary/30", disabled && "opacity-50 cursor-not-allowed"),
22075
+ children: [o.iconId !== void 0 && /* @__PURE__ */ jsx(EventKindGlyph, {
22076
+ iconId: o.iconId,
22077
+ size: 12
22078
+ }), o.label]
21960
22079
  }, o.value);
21961
22080
  })
21962
22081
  })
@@ -44648,115 +44767,6 @@ function DoorbellRecentPanel({ history, limit = 5, latestIsFresh, className }) {
44648
44767
  });
44649
44768
  }
44650
44769
  //#endregion
44651
- //#region src/composites/event-kind-visuals.tsx
44652
- /**
44653
- * Event-kind VISUALS — the UI-side half of the unified taxonomy dictionary
44654
- * (`@camstack/types` `EVENT_TAXONOMY` owns the serializable data:
44655
- * color / iconId / labelKey; this owns `iconId → lucide component` and
44656
- * `labelKey → t()`).
44657
- *
44658
- * Every UI that renders an event kind (timeline, events tree, chips,
44659
- * detection canvas/tree) resolves its glyph + translated label here — no
44660
- * per-component hardcoded icon or label.
44661
- */
44662
- /**
44663
- * `iconId` → lucide component. Built ONLY from statically-imported icons
44664
- * (bundle-safe). Unknown ids resolve to the neutral `Tag` fallback.
44665
- */
44666
- var EVENT_KIND_ICONS = {
44667
- motion: Activity,
44668
- audio: Volume2,
44669
- person: User,
44670
- vehicle: Car,
44671
- animal: PawPrint,
44672
- package: Package,
44673
- sensor: Radio,
44674
- control: SlidersHorizontal,
44675
- car: Car,
44676
- truck: Truck,
44677
- bus: Bus,
44678
- motorcycle: Bike,
44679
- bicycle: Bike,
44680
- airplane: Plane,
44681
- boat: Sailboat,
44682
- train: TrainFront,
44683
- bird: Bird,
44684
- cat: Cat,
44685
- dog: Dog,
44686
- horse: PawPrint,
44687
- sheep: PawPrint,
44688
- cow: PawPrint,
44689
- elephant: PawPrint,
44690
- bear: PawPrint,
44691
- zebra: PawPrint,
44692
- giraffe: PawPrint,
44693
- door: DoorOpen,
44694
- pir: Radar,
44695
- smoke: Flame,
44696
- water: Droplet,
44697
- gas: Wind,
44698
- vibration: Vibrate,
44699
- tamper: ShieldAlert,
44700
- presence: UserCheck,
44701
- generic: Tag,
44702
- lock: Lock,
44703
- siren: Siren,
44704
- switch: ToggleRight,
44705
- doorbell: BellRing,
44706
- button: CircleDot,
44707
- "audio-speech": MessageSquare,
44708
- "audio-scream": Megaphone,
44709
- "audio-crying": Baby,
44710
- "audio-laughter": Laugh,
44711
- "audio-music": Music,
44712
- "audio-dog": Dog,
44713
- "audio-cat": Cat,
44714
- "audio-bird": Bird,
44715
- "audio-animal": PawPrint,
44716
- "audio-alarm": BellRing,
44717
- "audio-doorbell": BellRing,
44718
- "audio-glass_breaking": TriangleAlert,
44719
- "audio-gunshot": Crosshair,
44720
- "audio-vehicle": Car,
44721
- "audio-siren": Siren,
44722
- "audio-fire": Flame,
44723
- "audio-water": Droplets,
44724
- "audio-wind": Wind,
44725
- "audio-door": DoorOpen,
44726
- "audio-footsteps": Footprints,
44727
- "audio-crowd": Users,
44728
- "audio-telephone": Phone,
44729
- "audio-engine": Cog,
44730
- "audio-tools": Hammer,
44731
- "audio-silence": VolumeX
44732
- };
44733
- /** Resolve an `iconId` to a lucide component (falls back to `Tag`). */
44734
- function resolveEventKindIcon(iconId) {
44735
- if (Object.hasOwn(EVENT_KIND_ICONS, iconId)) return EVENT_KIND_ICONS[iconId];
44736
- if (iconId.startsWith("audio-")) return Volume2;
44737
- return Tag;
44738
- }
44739
- /**
44740
- * Translated label for an event kind — `t(labelKey)` when a translation
44741
- * exists, else the English `label` fallback. Robust to a missing key: an
44742
- * i18n backend that echoes the key unchanged (react-i18next default) is
44743
- * treated as "no translation" and the fallback label is returned, so the UI
44744
- * never renders a raw `eventKind.*` key.
44745
- */
44746
- function eventKindLabel(descriptor, t) {
44747
- if (t === void 0) return descriptor.label;
44748
- const translated = t(descriptor.labelKey);
44749
- return translated === descriptor.labelKey || translated.length === 0 ? descriptor.label : translated;
44750
- }
44751
- /** Small convenience component: renders the lucide glyph for an `iconId`. */
44752
- function EventKindGlyph({ iconId, size = 16, color, className }) {
44753
- return createElement(resolveEventKindIcon(iconId), {
44754
- size,
44755
- color,
44756
- className
44757
- });
44758
- }
44759
- //#endregion
44760
44770
  //#region src/composites/filter-bar.tsx
44761
44771
  function FilterBar({ filters, values, onChange, className }) {
44762
44772
  const isMobile = useIsMobile();
@@ -48336,6 +48346,8 @@ function useSystemMutation(select, opts) {
48336
48346
  * — the original admin-ui copy stayed put for backward compatibility
48337
48347
  * but new consumers should import from here.
48338
48348
  */
48349
+ /** Trailing coalesce for event-driven invalidation — see the comment inside. */
48350
+ var EVENT_INVALIDATION_COALESCE_MS = 1e3;
48339
48351
  function useEventInvalidation(queryKey, categories) {
48340
48352
  const system = useSystem();
48341
48353
  const queryClient = useQueryClient();
@@ -48349,12 +48361,20 @@ function useEventInvalidation(queryKey, categories) {
48349
48361
  const keyJson = JSON.stringify(queryKey);
48350
48362
  useEffect(() => {
48351
48363
  const subs = [];
48352
- const refresh = () => {
48364
+ let pending = null;
48365
+ const refreshNow = () => {
48353
48366
  const raw = JSON.parse(keyJson);
48354
48367
  const queryKeyToInvalidate = raw.length === 1 && Array.isArray(raw[0]) ? raw : [raw];
48355
48368
  queryClient.invalidateQueries({ queryKey: queryKeyToInvalidate });
48356
48369
  };
48357
- refresh();
48370
+ const refresh = () => {
48371
+ if (pending !== null) return;
48372
+ pending = setTimeout(() => {
48373
+ pending = null;
48374
+ refreshNow();
48375
+ }, EVENT_INVALIDATION_COALESCE_MS);
48376
+ };
48377
+ refreshNow();
48358
48378
  for (const category of key.split("|")) {
48359
48379
  if (!category) continue;
48360
48380
  try {
@@ -48362,6 +48382,7 @@ function useEventInvalidation(queryKey, categories) {
48362
48382
  } catch {}
48363
48383
  }
48364
48384
  return () => {
48385
+ if (pending !== null) clearTimeout(pending);
48365
48386
  for (const unsub of subs) unsub();
48366
48387
  };
48367
48388
  }, [
@@ -32,7 +32,7 @@ export declare function DialogTrigger({ children, ...props }: HTMLAttributes<HTM
32
32
  */
33
33
  declare const contentVariants: (props?: ({
34
34
  width?: "sm" | "md" | "lg" | null | undefined;
35
- scroll?: "content" | "body" | null | undefined;
35
+ scroll?: "body" | "content" | null | undefined;
36
36
  placement?: "center" | "sheet-on-mobile" | null | undefined;
37
37
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
38
38
  export type DialogPlacement = NonNullable<VariantProps<typeof contentVariants>['placement']>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/ui-library",
3
- "version": "1.2.61",
3
+ "version": "1.2.63",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",