@camstack/ui-library 1.2.247 → 1.2.249

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
@@ -29,9 +29,9 @@ let _trpc_react_query = require("@trpc/react-query");
29
29
  _trpc_react_query = __toESM(_trpc_react_query, 1);
30
30
  let react_jsx_runtime = require("react/jsx-runtime");
31
31
  react_jsx_runtime = __toESM(react_jsx_runtime, 1);
32
+ let _camstack_types = require("@camstack/types");
32
33
  let react_dom = require("react-dom");
33
34
  react_dom = __toESM(react_dom, 1);
34
- let _camstack_types = require("@camstack/types");
35
35
  let _tanstack_react_query = require("@tanstack/react-query");
36
36
  _tanstack_react_query = __toESM(_tanstack_react_query, 1);
37
37
  let react_dom_client = require("react-dom/client");
@@ -6466,151 +6466,903 @@ function useClusterNodes() {
6466
6466
  };
6467
6467
  }
6468
6468
  //#endregion
6469
- //#region src/generated/system-hooks.ts
6469
+ //#region src/composites/device-meta.ts
6470
+ var DEVICE_TYPE_META = {
6471
+ [_camstack_types.DeviceType.Camera]: {
6472
+ icon: Camera,
6473
+ label: "Camera"
6474
+ },
6475
+ [_camstack_types.DeviceType.Hub]: {
6476
+ icon: Network,
6477
+ label: "Hub"
6478
+ },
6479
+ [_camstack_types.DeviceType.Light]: {
6480
+ icon: Lightbulb,
6481
+ label: "Light"
6482
+ },
6483
+ [_camstack_types.DeviceType.Siren]: {
6484
+ icon: Siren,
6485
+ label: "Siren"
6486
+ },
6487
+ [_camstack_types.DeviceType.Switch]: {
6488
+ icon: ToggleLeft,
6489
+ label: "Switch"
6490
+ },
6491
+ [_camstack_types.DeviceType.Sensor]: {
6492
+ icon: Activity,
6493
+ label: "Sensor"
6494
+ },
6495
+ [_camstack_types.DeviceType.Thermostat]: {
6496
+ icon: Thermometer,
6497
+ label: "Thermostat"
6498
+ },
6499
+ [_camstack_types.DeviceType.Climate]: {
6500
+ icon: AirVent,
6501
+ label: "Air conditioner"
6502
+ },
6503
+ [_camstack_types.DeviceType.Button]: {
6504
+ icon: CircleDot,
6505
+ label: "Button"
6506
+ },
6507
+ [_camstack_types.DeviceType.EventEmitter]: {
6508
+ icon: Zap,
6509
+ label: "Event Emitter"
6510
+ },
6511
+ [_camstack_types.DeviceType.Update]: {
6512
+ icon: RefreshCw,
6513
+ label: "Update"
6514
+ },
6515
+ [_camstack_types.DeviceType.Generic]: {
6516
+ icon: Box,
6517
+ label: "Generic"
6518
+ },
6519
+ [_camstack_types.DeviceType.Notifier]: {
6520
+ icon: Bell,
6521
+ label: "Notifier"
6522
+ },
6523
+ [_camstack_types.DeviceType.Script]: {
6524
+ icon: ScrollText,
6525
+ label: "Script"
6526
+ },
6527
+ [_camstack_types.DeviceType.Automation]: {
6528
+ icon: Workflow,
6529
+ label: "Automation"
6530
+ },
6531
+ [_camstack_types.DeviceType.Lock]: {
6532
+ icon: Lock,
6533
+ label: "Lock"
6534
+ },
6535
+ [_camstack_types.DeviceType.Cover]: {
6536
+ icon: Blinds,
6537
+ label: "Cover"
6538
+ },
6539
+ [_camstack_types.DeviceType.Valve]: {
6540
+ icon: Pipette,
6541
+ label: "Valve"
6542
+ },
6543
+ [_camstack_types.DeviceType.Humidifier]: {
6544
+ icon: Waves,
6545
+ label: "Humidifier"
6546
+ },
6547
+ [_camstack_types.DeviceType.WaterHeater]: {
6548
+ icon: Bath,
6549
+ label: "Water heater"
6550
+ },
6551
+ [_camstack_types.DeviceType.Fan]: {
6552
+ icon: Fan,
6553
+ label: "Fan"
6554
+ },
6555
+ [_camstack_types.DeviceType.MediaPlayer]: {
6556
+ icon: MonitorPlay,
6557
+ label: "Media player"
6558
+ },
6559
+ [_camstack_types.DeviceType.AlarmPanel]: {
6560
+ icon: ShieldAlert,
6561
+ label: "Alarm panel"
6562
+ },
6563
+ [_camstack_types.DeviceType.Control]: {
6564
+ icon: SlidersHorizontal,
6565
+ label: "Control"
6566
+ },
6567
+ [_camstack_types.DeviceType.Presence]: {
6568
+ icon: MapPin,
6569
+ label: "Presence"
6570
+ },
6571
+ [_camstack_types.DeviceType.Weather]: {
6572
+ icon: CloudSun,
6573
+ label: "Weather"
6574
+ },
6575
+ [_camstack_types.DeviceType.Vacuum]: {
6576
+ icon: Bot,
6577
+ label: "Vacuum"
6578
+ },
6579
+ [_camstack_types.DeviceType.LawnMower]: {
6580
+ icon: Tractor,
6581
+ label: "Lawn mower"
6582
+ },
6583
+ [_camstack_types.DeviceType.Container]: {
6584
+ icon: Box,
6585
+ label: "Container"
6586
+ },
6587
+ [_camstack_types.DeviceType.Image]: {
6588
+ icon: Image,
6589
+ label: "Image"
6590
+ },
6591
+ [_camstack_types.DeviceType.PetFeeder]: {
6592
+ icon: Bone,
6593
+ label: "Pet feeder"
6594
+ }
6595
+ };
6596
+ var DEVICE_ROLE_META = {
6597
+ [_camstack_types.DeviceRole.Siren]: {
6598
+ icon: Siren,
6599
+ label: "Siren"
6600
+ },
6601
+ [_camstack_types.DeviceRole.Floodlight]: {
6602
+ icon: Lightbulb,
6603
+ label: "Floodlight"
6604
+ },
6605
+ [_camstack_types.DeviceRole.Spotlight]: {
6606
+ icon: Flashlight,
6607
+ label: "Spotlight"
6608
+ },
6609
+ [_camstack_types.DeviceRole.PirSensor]: {
6610
+ icon: Radar,
6611
+ label: "PIR sensor"
6612
+ },
6613
+ [_camstack_types.DeviceRole.Chime]: {
6614
+ icon: Bell,
6615
+ label: "Chime"
6616
+ },
6617
+ [_camstack_types.DeviceRole.Autotrack]: {
6618
+ icon: Crosshair,
6619
+ label: "Auto-track"
6620
+ },
6621
+ [_camstack_types.DeviceRole.Nightvision]: {
6622
+ icon: Moon,
6623
+ label: "Night vision"
6624
+ },
6625
+ [_camstack_types.DeviceRole.PrivacyMask]: {
6626
+ icon: EyeOff,
6627
+ label: "Privacy mask"
6628
+ },
6629
+ [_camstack_types.DeviceRole.Doorbell]: {
6630
+ icon: DoorClosed,
6631
+ label: "Doorbell"
6632
+ },
6633
+ [_camstack_types.DeviceRole.BinaryHelper]: {
6634
+ icon: ToggleLeft,
6635
+ label: "Binary helper"
6636
+ },
6637
+ [_camstack_types.DeviceRole.MotionSensor]: {
6638
+ icon: Footprints,
6639
+ label: "Motion sensor"
6640
+ },
6641
+ [_camstack_types.DeviceRole.ContactSensor]: {
6642
+ icon: DoorOpen,
6643
+ label: "Contact sensor"
6644
+ },
6645
+ [_camstack_types.DeviceRole.LeakSensor]: {
6646
+ icon: Droplet,
6647
+ label: "Leak sensor"
6648
+ },
6649
+ [_camstack_types.DeviceRole.SmokeSensor]: {
6650
+ icon: AlarmSmoke,
6651
+ label: "Smoke sensor"
6652
+ },
6653
+ [_camstack_types.DeviceRole.COSensor]: {
6654
+ icon: Wind,
6655
+ label: "CO sensor"
6656
+ },
6657
+ [_camstack_types.DeviceRole.GasSensor]: {
6658
+ icon: Flame,
6659
+ label: "Gas sensor"
6660
+ },
6661
+ [_camstack_types.DeviceRole.TamperSensor]: {
6662
+ icon: ShieldAlert,
6663
+ label: "Tamper sensor"
6664
+ },
6665
+ [_camstack_types.DeviceRole.VibrationSensor]: {
6666
+ icon: Vibrate,
6667
+ label: "Vibration sensor"
6668
+ },
6669
+ [_camstack_types.DeviceRole.ConnectivitySensor]: {
6670
+ icon: Wifi,
6671
+ label: "Connectivity"
6672
+ },
6673
+ [_camstack_types.DeviceRole.SoundSensor]: {
6674
+ icon: Volume2,
6675
+ label: "Sound sensor"
6676
+ },
6677
+ [_camstack_types.DeviceRole.BinarySensor]: {
6678
+ icon: ToggleLeft,
6679
+ label: "Binary sensor"
6680
+ },
6681
+ [_camstack_types.DeviceRole.TemperatureSensor]: {
6682
+ icon: Thermometer,
6683
+ label: "Temperature"
6684
+ },
6685
+ [_camstack_types.DeviceRole.HumiditySensor]: {
6686
+ icon: Droplets,
6687
+ label: "Humidity"
6688
+ },
6689
+ [_camstack_types.DeviceRole.AmbientLightSensor]: {
6690
+ icon: Sun,
6691
+ label: "Ambient light"
6692
+ },
6693
+ [_camstack_types.DeviceRole.PressureSensor]: {
6694
+ icon: Gauge,
6695
+ label: "Pressure"
6696
+ },
6697
+ [_camstack_types.DeviceRole.WindSensor]: {
6698
+ icon: Wind,
6699
+ label: "Wind"
6700
+ },
6701
+ [_camstack_types.DeviceRole.RainSensor]: {
6702
+ icon: CloudRain,
6703
+ label: "Rain"
6704
+ },
6705
+ [_camstack_types.DeviceRole.UvSensor]: {
6706
+ icon: SunMedium,
6707
+ label: "UV index"
6708
+ },
6709
+ [_camstack_types.DeviceRole.SolarRadiationSensor]: {
6710
+ icon: Sunrise,
6711
+ label: "Solar radiation"
6712
+ },
6713
+ [_camstack_types.DeviceRole.SoilMoistureSensor]: {
6714
+ icon: Sprout,
6715
+ label: "Soil moisture"
6716
+ },
6717
+ [_camstack_types.DeviceRole.PowerSensor]: {
6718
+ icon: Zap,
6719
+ label: "Power"
6720
+ },
6721
+ [_camstack_types.DeviceRole.EnergySensor]: {
6722
+ icon: Zap,
6723
+ label: "Energy"
6724
+ },
6725
+ [_camstack_types.DeviceRole.VoltageSensor]: {
6726
+ icon: Zap,
6727
+ label: "Voltage"
6728
+ },
6729
+ [_camstack_types.DeviceRole.CurrentSensor]: {
6730
+ icon: Zap,
6731
+ label: "Current"
6732
+ },
6733
+ [_camstack_types.DeviceRole.AirQualitySensor]: {
6734
+ icon: Wind,
6735
+ label: "Air quality"
6736
+ },
6737
+ [_camstack_types.DeviceRole.BatterySensor]: {
6738
+ icon: Gauge,
6739
+ label: "Battery"
6740
+ },
6741
+ [_camstack_types.DeviceRole.NumericSensor]: {
6742
+ icon: Hash,
6743
+ label: "Numeric sensor"
6744
+ },
6745
+ [_camstack_types.DeviceRole.EnumSensor]: {
6746
+ icon: List,
6747
+ label: "Enum sensor"
6748
+ },
6749
+ [_camstack_types.DeviceRole.DateTimeSensor]: {
6750
+ icon: Clock,
6751
+ label: "Date / time"
6752
+ },
6753
+ [_camstack_types.DeviceRole.GenericSensor]: {
6754
+ icon: Gauge,
6755
+ label: "Sensor"
6756
+ },
6757
+ [_camstack_types.DeviceRole.NumericControl]: {
6758
+ icon: SlidersHorizontal,
6759
+ label: "Numeric control"
6760
+ },
6761
+ [_camstack_types.DeviceRole.SelectControl]: {
6762
+ icon: ListChecks,
6763
+ label: "Select control"
6764
+ },
6765
+ [_camstack_types.DeviceRole.TextControl]: {
6766
+ icon: Type,
6767
+ label: "Text control"
6768
+ },
6769
+ [_camstack_types.DeviceRole.DateTimeControl]: {
6770
+ icon: Clock,
6771
+ label: "Date / time control"
6772
+ },
6773
+ [_camstack_types.DeviceRole.MobilePushNotifier]: {
6774
+ icon: Smartphone,
6775
+ label: "Mobile push"
6776
+ },
6777
+ [_camstack_types.DeviceRole.MessagingNotifier]: {
6778
+ icon: MessageSquare,
6779
+ label: "Messaging"
6780
+ },
6781
+ [_camstack_types.DeviceRole.EmailNotifier]: {
6782
+ icon: Mail,
6783
+ label: "Email"
6784
+ },
6785
+ [_camstack_types.DeviceRole.GenericNotifier]: {
6786
+ icon: Bell,
6787
+ label: "Notifier"
6788
+ }
6789
+ };
6790
+ var DEVICE_TYPE_VALUES$1 = new Set(Object.values(_camstack_types.DeviceType));
6791
+ var DEVICE_ROLE_VALUES = new Set(Object.values(_camstack_types.DeviceRole));
6792
+ function isDeviceType(value) {
6793
+ return DEVICE_TYPE_VALUES$1.has(value);
6794
+ }
6795
+ function isDeviceRole(value) {
6796
+ return DEVICE_ROLE_VALUES.has(value);
6797
+ }
6798
+ /** Meta for a known `DeviceType`. */
6799
+ function deviceTypeMeta(type) {
6800
+ return DEVICE_TYPE_META[type];
6801
+ }
6802
+ /** Meta for a known `DeviceRole`. */
6803
+ function deviceRoleMeta(role) {
6804
+ return DEVICE_ROLE_META[role];
6805
+ }
6806
+ var FALLBACK_DEVICE_TYPE_META = DEVICE_TYPE_META[_camstack_types.DeviceType.Generic];
6807
+ var FALLBACK_DEVICE_ROLE_META = DEVICE_ROLE_META[_camstack_types.DeviceRole.GenericSensor];
6470
6808
  /**
6471
- * Per-method React-Query hooks for every `scope: 'system'` capability.
6809
+ * Runtime-string-safe lookup: device data crosses process / network boundaries
6810
+ * as plain strings, so an unrecognised value falls back to the `Generic` meta
6811
+ * instead of throwing.
6472
6812
  *
6473
- * Each hook is a thin pass-through to `trpc.<cap>.<method>.useQuery` /
6474
- * `.useMutation` / `.useSubscription` so the typed inference + RQ
6475
- * cache/dedup keep working — the generator only contributes a consistent
6476
- * name and stable export surface so admin-ui pages can write
6477
- * `useAddonsList()` instead of `trpc.addons.list.useQuery()`.
6813
+ * This also guards mixed-version module-federation loads where the host
6814
+ * `@camstack/types` enum has a new member before the loaded ui-library bundle
6815
+ * has the matching `DEVICE_TYPE_META` entry.
6816
+ */
6817
+ function deviceTypeMetaOf(type) {
6818
+ if (!isDeviceType(type)) return FALLBACK_DEVICE_TYPE_META;
6819
+ return DEVICE_TYPE_META[type] ?? FALLBACK_DEVICE_TYPE_META;
6820
+ }
6821
+ /** Runtime-string-safe role lookup; unknown roles fall back to `GenericSensor`. */
6822
+ function deviceRoleMetaOf(role) {
6823
+ if (!isDeviceRole(role)) return FALLBACK_DEVICE_ROLE_META;
6824
+ return DEVICE_ROLE_META[role] ?? FALLBACK_DEVICE_ROLE_META;
6825
+ }
6826
+ /**
6827
+ * Icons an operator may pick for a per-device `display.icon` override (and for
6828
+ * a per-role default), keyed by a stable kebab-case name. Named imports only —
6829
+ * no dynamic indexing into lucide, so the bundle carries exactly this set.
6830
+ * Unknown keys resolve to `undefined` (caller falls through to the next level).
6478
6831
  *
6479
- * Push-subscription methods (`kind: 'subscription'`) emit a typeof
6480
- * alias over `useSubscription`, so the call shape is
6481
- * `useToastOnToast(input, { onData: cb })` — same as the underlying
6482
- * tRPC hook. The eventBus-backed `useLiveEvent` (see
6483
- * `../hooks/use-live-event.ts`) covers events that don't ride a tRPC
6484
- * subscription; the two surfaces coexist by design.
6832
+ * The vocabulary is the one the VIEWER draws from too (its own icon module,
6833
+ * keyed by these same strings): a key added here that the phone cannot draw
6834
+ * falls through to the next resolution level there, which reads as "my change
6835
+ * did nothing". So the KEY is the contract — a stable kebab-case string, never
6836
+ * a glyph identity — and `device-meta.test.ts` pins that every key resolves
6837
+ * and that the set never loses a key an operator may already have persisted.
6838
+ *
6839
+ * Grouped by what an operator is naming: a device, a light or appliance, a
6840
+ * sensor or the weather it reads, a room, or what moves through it. `gauge2`
6841
+ * is a legacy alias kept for rows that persisted it.
6485
6842
  */
6486
- /** Generated alias around `trpc.accessories.setChildHidden.useMutation`. */
6487
- var useAccessoriesSetChildHidden = trpc.accessories.setChildHidden.useMutation;
6488
- /** Generated alias around `trpc.accessories.getStatus.useQuery`. */
6489
- var useAccessoriesGetStatus = trpc.accessories.getStatus.useQuery;
6490
- /** Generated alias around `trpc.addonPages.listPages.useQuery`. */
6491
- var useAddonPagesListPages = trpc.addonPages.listPages.useQuery;
6492
- /** Generated alias around `trpc.addons.list.useQuery`. */
6493
- var useAddonsList = trpc.addons.list.useQuery;
6494
- /** Generated alias around `trpc.addons.getLogs.useQuery`. */
6495
- var useAddonsGetLogs = trpc.addons.getLogs.useQuery;
6496
- /** Generated alias around `trpc.addons.listPackages.useQuery`. */
6497
- var useAddonsListPackages = trpc.addons.listPackages.useQuery;
6498
- /** Generated alias around `trpc.addons.installPackage.useMutation`. */
6499
- var useAddonsInstallPackage = trpc.addons.installPackage.useMutation;
6500
- /** Generated alias around `trpc.addons.installFromWorkspace.useMutation`. */
6501
- var useAddonsInstallFromWorkspace = trpc.addons.installFromWorkspace.useMutation;
6502
- /** Generated alias around `trpc.addons.isWorkspaceAvailable.useQuery`. */
6503
- var useAddonsIsWorkspaceAvailable = trpc.addons.isWorkspaceAvailable.useQuery;
6504
- /** Generated alias around `trpc.addons.listWorkspacePackages.useQuery`. */
6505
- var useAddonsListWorkspacePackages = trpc.addons.listWorkspacePackages.useQuery;
6506
- /** Generated alias around `trpc.addons.uninstallPackage.useMutation`. */
6507
- var useAddonsUninstallPackage = trpc.addons.uninstallPackage.useMutation;
6508
- /** Generated alias around `trpc.addons.reloadPackages.useMutation`. */
6509
- var useAddonsReloadPackages = trpc.addons.reloadPackages.useMutation;
6510
- /** Generated alias around `trpc.addons.searchAvailable.useQuery`. */
6511
- var useAddonsSearchAvailable = trpc.addons.searchAvailable.useQuery;
6512
- /** Generated alias around `trpc.addons.listUpdates.useQuery`. */
6513
- var useAddonsListUpdates = trpc.addons.listUpdates.useQuery;
6514
- /** Generated alias around `trpc.addons.updatePackage.useMutation`. */
6515
- var useAddonsUpdatePackage = trpc.addons.updatePackage.useMutation;
6516
- /** Generated alias around `trpc.addons.rollbackPackage.useMutation`. */
6517
- var useAddonsRollbackPackage = trpc.addons.rollbackPackage.useMutation;
6518
- /** Generated alias around `trpc.addons.forceRefresh.useMutation`. */
6519
- var useAddonsForceRefresh = trpc.addons.forceRefresh.useMutation;
6520
- /** Generated alias around `trpc.addons.restartServer.useMutation`. */
6521
- var useAddonsRestartServer = trpc.addons.restartServer.useMutation;
6522
- /** Generated alias around `trpc.addons.getLastRestart.useQuery`. */
6523
- var useAddonsGetLastRestart = trpc.addons.getLastRestart.useQuery;
6524
- /** Generated alias around `trpc.addons.listFrameworkPackages.useQuery`. */
6525
- var useAddonsListFrameworkPackages = trpc.addons.listFrameworkPackages.useQuery;
6526
- /** Generated alias around `trpc.addons.listCapabilityProviders.useQuery`. */
6527
- var useAddonsListCapabilityProviders = trpc.addons.listCapabilityProviders.useQuery;
6528
- /** Generated alias around `trpc.addons.setCapabilityProviderEnabled.useMutation`. */
6529
- var useAddonsSetCapabilityProviderEnabled = trpc.addons.setCapabilityProviderEnabled.useMutation;
6530
- /** Generated alias around `trpc.addons.getVersions.useQuery`. */
6531
- var useAddonsGetVersions = trpc.addons.getVersions.useQuery;
6532
- /** Generated alias around `trpc.addons.restartAddon.useMutation`. */
6533
- var useAddonsRestartAddon = trpc.addons.restartAddon.useMutation;
6534
- /** Generated alias around `trpc.addons.retryLoad.useMutation`. */
6535
- var useAddonsRetryLoad = trpc.addons.retryLoad.useMutation;
6536
- /** Generated alias around `trpc.addons.getAutoUpdateSettings.useQuery`. */
6537
- var useAddonsGetAutoUpdateSettings = trpc.addons.getAutoUpdateSettings.useQuery;
6538
- /** Generated alias around `trpc.addons.setAutoUpdateSettings.useMutation`. */
6539
- var useAddonsSetAutoUpdateSettings = trpc.addons.setAutoUpdateSettings.useMutation;
6540
- /** Generated alias around `trpc.addons.getAddonAutoUpdate.useQuery`. */
6541
- var useAddonsGetAddonAutoUpdate = trpc.addons.getAddonAutoUpdate.useQuery;
6542
- /** Generated alias around `trpc.addons.setAddonAutoUpdate.useMutation`. */
6543
- var useAddonsSetAddonAutoUpdate = trpc.addons.setAddonAutoUpdate.useMutation;
6544
- /** Generated alias around `trpc.addons.applyAutoUpdateToAll.useMutation`. */
6545
- var useAddonsApplyAutoUpdateToAll = trpc.addons.applyAutoUpdateToAll.useMutation;
6546
- /** Generated alias around `trpc.addons.custom.useMutation`. */
6547
- var useAddonsCustom = trpc.addons.custom.useMutation;
6548
- /** Generated alias around `trpc.addons.startJob.useMutation`. */
6549
- var useAddonsStartJob = trpc.addons.startJob.useMutation;
6550
- /** Generated alias around `trpc.addons.getJob.useQuery`. */
6551
- var useAddonsGetJob = trpc.addons.getJob.useQuery;
6552
- /** Generated alias around `trpc.addons.listJobs.useQuery`. */
6553
- var useAddonsListJobs = trpc.addons.listJobs.useQuery;
6554
- /** Generated alias around `trpc.addons.cancelJob.useMutation`. */
6555
- var useAddonsCancelJob = trpc.addons.cancelJob.useMutation;
6556
- /** Generated alias around `trpc.addons.onAddonLogs.useSubscription`. */
6557
- var useAddonsOnAddonLogs = trpc.addons.onAddonLogs.useSubscription;
6558
- /** Generated alias around `trpc.addonSettings.getGlobalSettings.useQuery`. */
6559
- var useAddonSettingsGetGlobalSettings = trpc.addonSettings.getGlobalSettings.useQuery;
6560
- /** Generated alias around `trpc.addonSettings.updateGlobalSettings.useMutation`. */
6561
- var useAddonSettingsUpdateGlobalSettings = trpc.addonSettings.updateGlobalSettings.useMutation;
6562
- /** Generated alias around `trpc.addonSettings.getIntegrationSettings.useQuery`. */
6563
- var useAddonSettingsGetIntegrationSettings = trpc.addonSettings.getIntegrationSettings.useQuery;
6564
- /** Generated alias around `trpc.addonSettings.getDeviceSettings.useQuery`. */
6565
- var useAddonSettingsGetDeviceSettings = trpc.addonSettings.getDeviceSettings.useQuery;
6566
- /** Generated alias around `trpc.addonSettings.updateDeviceSettings.useMutation`. */
6567
- var useAddonSettingsUpdateDeviceSettings = trpc.addonSettings.updateDeviceSettings.useMutation;
6568
- /** Generated alias around `trpc.addonWidgets.listWidgets.useQuery`. */
6569
- var useAddonWidgetsListWidgets = trpc.addonWidgets.listWidgets.useQuery;
6570
- /** Generated alias around `trpc.airQualitySensor.getStatus.useQuery`. */
6571
- var useAirQualitySensorGetStatus = trpc.airQualitySensor.getStatus.useQuery;
6572
- /** Generated alias around `trpc.alarmPanel.arm.useMutation`. */
6573
- var useAlarmPanelArm = trpc.alarmPanel.arm.useMutation;
6574
- /** Generated alias around `trpc.alarmPanel.disarm.useMutation`. */
6575
- var useAlarmPanelDisarm = trpc.alarmPanel.disarm.useMutation;
6576
- /** Generated alias around `trpc.alarmPanel.trigger.useMutation`. */
6577
- var useAlarmPanelTrigger = trpc.alarmPanel.trigger.useMutation;
6578
- /** Generated alias around `trpc.alarmPanel.getStatus.useQuery`. */
6579
- var useAlarmPanelGetStatus = trpc.alarmPanel.getStatus.useQuery;
6580
- /** Generated alias around `trpc.alerts.emit.useMutation`. */
6581
- var useAlertsEmit = trpc.alerts.emit.useMutation;
6582
- /** Generated alias around `trpc.alerts.update.useMutation`. */
6583
- var useAlertsUpdate = trpc.alerts.update.useMutation;
6584
- /** Generated alias around `trpc.alerts.list.useQuery`. */
6585
- var useAlertsList = trpc.alerts.list.useQuery;
6586
- /** Generated alias around `trpc.alerts.getUnreadCount.useQuery`. */
6587
- var useAlertsGetUnreadCount = trpc.alerts.getUnreadCount.useQuery;
6588
- /** Generated alias around `trpc.alerts.markRead.useMutation`. */
6589
- var useAlertsMarkRead = trpc.alerts.markRead.useMutation;
6590
- /** Generated alias around `trpc.alerts.markAllRead.useMutation`. */
6591
- var useAlertsMarkAllRead = trpc.alerts.markAllRead.useMutation;
6592
- /** Generated alias around `trpc.alerts.dismiss.useMutation`. */
6593
- var useAlertsDismiss = trpc.alerts.dismiss.useMutation;
6594
- /** Generated alias around `trpc.ambientLightSensor.getStatus.useQuery`. */
6595
- var useAmbientLightSensorGetStatus = trpc.ambientLightSensor.getStatus.useQuery;
6596
- /** Generated alias around `trpc.audioAnalysis.resolveDeviceSettings.useQuery`. */
6597
- var useAudioAnalysisResolveDeviceSettings = trpc.audioAnalysis.resolveDeviceSettings.useQuery;
6598
- /** Generated alias around `trpc.audioAnalysis.getDeviceSettingsContribution.useQuery`. */
6599
- var useAudioAnalysisGetDeviceSettingsContribution = trpc.audioAnalysis.getDeviceSettingsContribution.useQuery;
6600
- /** Generated alias around `trpc.audioAnalysis.getDeviceLiveContribution.useQuery`. */
6601
- var useAudioAnalysisGetDeviceLiveContribution = trpc.audioAnalysis.getDeviceLiveContribution.useQuery;
6602
- /** Generated alias around `trpc.audioAnalysis.applyDeviceSettingsPatch.useMutation`. */
6603
- var useAudioAnalysisApplyDeviceSettingsPatch = trpc.audioAnalysis.applyDeviceSettingsPatch.useMutation;
6604
- /** Generated alias around `trpc.audioAnalyzer.analyseChunk.useMutation`. */
6605
- var useAudioAnalyzerAnalyseChunk = trpc.audioAnalyzer.analyseChunk.useMutation;
6606
- /** Generated alias around `trpc.audioAnalyzer.classify.useQuery`. */
6607
- var useAudioAnalyzerClassify = trpc.audioAnalyzer.classify.useQuery;
6608
- /** Generated alias around `trpc.audioAnalyzer.attachDevice.useMutation`. */
6609
- var useAudioAnalyzerAttachDevice = trpc.audioAnalyzer.attachDevice.useMutation;
6610
- /** Generated alias around `trpc.audioAnalyzer.detachDevice.useMutation`. */
6611
- var useAudioAnalyzerDetachDevice = trpc.audioAnalyzer.detachDevice.useMutation;
6612
- /** Generated alias around `trpc.audioAnalyzer.isReady.useQuery`. */
6613
- var useAudioAnalyzerIsReady = trpc.audioAnalyzer.isReady.useQuery;
6843
+ var DISPLAY_ICON_REGISTRY = {
6844
+ camera: Camera,
6845
+ network: Network,
6846
+ "toggle-left": ToggleLeft,
6847
+ "toggle-right": ToggleRight,
6848
+ "circle-dot": CircleDot,
6849
+ "refresh-cw": RefreshCw,
6850
+ box: Box,
6851
+ "scroll-text": ScrollText,
6852
+ workflow: Workflow,
6853
+ lock: Lock,
6854
+ "lock-open": LockOpen,
6855
+ blinds: Blinds,
6856
+ "air-vent": AirVent,
6857
+ "monitor-play": MonitorPlay,
6858
+ "sliders-horizontal": SlidersHorizontal,
6859
+ "map-pin": MapPin,
6860
+ bot: Bot,
6861
+ tractor: Tractor,
6862
+ image: Image,
6863
+ bone: Bone,
6864
+ "door-closed": DoorClosed,
6865
+ "door-open": DoorOpen,
6866
+ crosshair: Crosshair,
6867
+ eye: Eye,
6868
+ "eye-off": EyeOff,
6869
+ smartphone: Smartphone,
6870
+ phone: Phone,
6871
+ tablet: Tablet,
6872
+ laptop: Laptop,
6873
+ monitor: Monitor,
6874
+ tv: Tv,
6875
+ speaker: Speaker,
6876
+ printer: Printer,
6877
+ projector: Projector,
6878
+ "gamepad-2": Gamepad2,
6879
+ radio: Radio,
6880
+ router: Router,
6881
+ bluetooth: Bluetooth,
6882
+ cpu: Cpu,
6883
+ "hard-drive": HardDrive,
6884
+ server: Server,
6885
+ mic: Mic,
6886
+ "mic-off": MicOff,
6887
+ video: Video,
6888
+ "video-off": VideoOff,
6889
+ music: Music,
6890
+ plug: Plug,
6891
+ "plug-zap": PlugZap,
6892
+ power: Power,
6893
+ "power-off": PowerOff,
6894
+ cable: Cable,
6895
+ battery: Battery,
6896
+ "battery-charging": BatteryCharging,
6897
+ "battery-full": BatteryFull,
6898
+ "battery-low": BatteryLow,
6899
+ key: Key,
6900
+ "key-round": KeyRound,
6901
+ mailbox: Mailbox,
6902
+ package: Package,
6903
+ shield: Shield,
6904
+ "shield-check": ShieldCheck,
6905
+ "shield-off": ShieldOff,
6906
+ lightbulb: Lightbulb,
6907
+ lamp: Lamp,
6908
+ "lamp-ceiling": LampCeiling,
6909
+ "lamp-desk": LampDesk,
6910
+ "lamp-floor": LampFloor,
6911
+ flashlight: Flashlight,
6912
+ heater: Heater,
6913
+ snowflake: Snowflake,
6914
+ "thermometer-sun": ThermometerSun,
6915
+ "thermometer-snowflake": ThermometerSnowflake,
6916
+ refrigerator: Refrigerator,
6917
+ microwave: Microwave,
6918
+ "washing-machine": WashingMachine,
6919
+ "cooking-pot": CookingPot,
6920
+ utensils: Utensils,
6921
+ thermometer: Thermometer,
6922
+ droplets: Droplets,
6923
+ droplet: Droplet,
6924
+ gauge: Gauge,
6925
+ gauge2: Gauge,
6926
+ sun: Sun,
6927
+ "sun-medium": SunMedium,
6928
+ sunrise: Sunrise,
6929
+ moon: Moon,
6930
+ wind: Wind,
6931
+ cloud: Cloud,
6932
+ "cloud-rain": CloudRain,
6933
+ "cloud-sun": CloudSun,
6934
+ waves: Waves,
6935
+ sprout: Sprout,
6936
+ leaf: Leaf,
6937
+ flower: Flower,
6938
+ zap: Zap,
6939
+ flame: Flame,
6940
+ bell: Bell,
6941
+ "bell-ring": BellRing,
6942
+ "bell-off": BellOff,
6943
+ siren: Siren,
6944
+ radar: Radar,
6945
+ activity: Activity,
6946
+ hash: Hash,
6947
+ clock: Clock,
6948
+ timer: Timer,
6949
+ calendar: Calendar,
6950
+ pipette: Pipette,
6951
+ fan: Fan,
6952
+ bath: Bath,
6953
+ footprints: Footprints,
6954
+ "alarm-smoke": AlarmSmoke,
6955
+ "shield-alert": ShieldAlert,
6956
+ vibrate: Vibrate,
6957
+ wifi: Wifi,
6958
+ "wifi-off": WifiOff,
6959
+ "volume-2": Volume2,
6960
+ "volume-x": VolumeX,
6961
+ heart: Heart,
6962
+ "heart-pulse": HeartPulse,
6963
+ star: Star,
6964
+ sparkles: Sparkles,
6965
+ house: House,
6966
+ sofa: Sofa,
6967
+ "bed-double": BedDouble,
6968
+ bed: Bed,
6969
+ warehouse: Warehouse,
6970
+ trees: Trees,
6971
+ "tree-pine": TreePine,
6972
+ fence: Fence,
6973
+ store: Store,
6974
+ building: Building,
6975
+ "building-2": Building2,
6976
+ mountain: Mountain,
6977
+ umbrella: Umbrella,
6978
+ dumbbell: Dumbbell,
6979
+ wrench: Wrench,
6980
+ hammer: Hammer,
6981
+ drill: Drill,
6982
+ "waves-ladder": WavesLadder,
6983
+ "circle-parking": CircleParking,
6984
+ briefcase: Briefcase,
6985
+ route: Route,
6986
+ car: Car,
6987
+ bike: Bike,
6988
+ bus: Bus,
6989
+ truck: Truck,
6990
+ plane: Plane,
6991
+ "train-front": TrainFront,
6992
+ sailboat: Sailboat,
6993
+ dog: Dog,
6994
+ cat: Cat,
6995
+ bird: Bird,
6996
+ "paw-print": PawPrint,
6997
+ baby: Baby,
6998
+ user: User,
6999
+ users: Users,
7000
+ "person-standing": PersonStanding
7001
+ };
7002
+ /** Resolve a `display.icon` name to a lucide component, or `undefined` when the
7003
+ * name is not in the curated `DISPLAY_ICON_REGISTRY`. */
7004
+ function resolveDisplayIcon(name) {
7005
+ return Object.hasOwn(DISPLAY_ICON_REGISTRY, name) ? DISPLAY_ICON_REGISTRY[name] : void 0;
7006
+ }
7007
+ /**
7008
+ * EXHAUSTIVE device-type filter options — one entry per `DeviceType` member,
7009
+ * derived from `DEVICE_TYPE_META` so the set is always complete and a newly
7010
+ * added `DeviceType` automatically appears in every type filter (Devices page,
7011
+ * adoption modal, …) without touching any consumer.
7012
+ *
7013
+ * The previous per-consumer logic built options from the types PRESENT in the
7014
+ * list (or a hand-maintained subset), so recently-added types (vacuum, lawn
7015
+ * mower, valve, humidifier, water heater, weather, image, …) were silently
7016
+ * missing from the dropdown. This is the single source of truth instead.
7017
+ *
7018
+ * `id` is the raw `DeviceType` string (matches the filtering predicate, which
7019
+ * compares against `device.type`); `label` + `icon` come from the meta map.
7020
+ * Ordered by `DeviceType` declaration order for a stable, intentional layout.
7021
+ */
7022
+ var ALL_DEVICE_TYPE_FILTER_OPTIONS = Object.values(_camstack_types.DeviceType).map((type) => {
7023
+ const meta = deviceTypeMetaOf(type);
7024
+ return {
7025
+ id: type,
7026
+ label: meta.label,
7027
+ icon: meta.icon
7028
+ };
7029
+ });
7030
+ function allDeviceTypeFilterOptions() {
7031
+ return ALL_DEVICE_TYPE_FILTER_OPTIONS;
7032
+ }
7033
+ //#endregion
7034
+ //#region src/composites/icon-picker.tsx
7035
+ /**
7036
+ * icon-picker — the ONE control for choosing an icon, wherever admin-ui asks.
7037
+ *
7038
+ * There were two: a device one over `DISPLAY_ICON_REGISTRY` and a room one
7039
+ * over `LOCATION_ICON_IDS`, identical apart from the vocabulary they walked.
7040
+ * That was tolerable while the device set was 56 glyphs in a flat grid. It is
7041
+ * not at 157: a picker that only lets the operator HUNT is a picker they stop
7042
+ * using, and the two copies were already drifting — only one of them had an
7043
+ * i18n'd label.
7044
+ *
7045
+ * So the vocabulary, the grouping and the search all come from
7046
+ * `@camstack/types`' `display-icon-vocabulary`, which is the same module the
7047
+ * viewer's sheet searches. What is left here is the web half: a popover, a
7048
+ * grid, and a text field.
7049
+ *
7050
+ * ## What it does NOT own
7051
+ *
7052
+ * - **The glyphs.** `resolveGlyph` is a prop, because this package's table is
7053
+ * `lucide-react` and the viewer's is `lucide-react-native`. A key this build
7054
+ * cannot draw is SKIPPED rather than rendered as a hole — an empty tile
7055
+ * reads as a rendering bug, not as a missing choice.
7056
+ * - **The words.** Every string is a prop. This package has no i18n (one file
7057
+ * in it imports react-i18next, and that is the exception), and a control
7058
+ * that reached for a translation would force it on every consumer.
7059
+ * - **The write.** `onPick` is called and that is all; whether it is a form
7060
+ * value or an immediate mutation is the caller's business, as it already was
7061
+ * in both pickers this replaces.
7062
+ *
7063
+ * ## The clear tile is never filtered away
7064
+ *
7065
+ * The "inherit" / "no icon" choice sits above the sections and ignores the
7066
+ * search. A query that hid it would strand the operator on a glyph they picked
7067
+ * by accident with no way back to the default — the one action a picker must
7068
+ * always offer.
7069
+ */
7070
+ function IconPicker({ value, onPick, resolveGlyph, fallbackGlyph, labels, only, extraWords, clearGlyph, labelFor, disabled = false }) {
7071
+ const [open, setOpen] = (0, react$1.useState)(false);
7072
+ const [query, setQuery] = (0, react$1.useState)("");
7073
+ const allowed = (0, react$1.useMemo)(() => only === void 0 ? null : new Set(only), [only]);
7074
+ const sections = (0, react$1.useMemo)(() => {
7075
+ const hits = (0, _camstack_types.searchDisplayIcons)(query, extraWords);
7076
+ const byGroup = /* @__PURE__ */ new Map();
7077
+ for (const hit of hits) {
7078
+ const { key, group } = hit.entry;
7079
+ if (allowed !== null && !allowed.has(key)) continue;
7080
+ if (resolveGlyph(key) === void 0) continue;
7081
+ const bucket = byGroup.get(group);
7082
+ if (bucket === void 0) byGroup.set(group, [key]);
7083
+ else bucket.push(key);
7084
+ }
7085
+ return _camstack_types.DISPLAY_ICON_GROUPS.flatMap((group) => {
7086
+ const keys = byGroup.get(group);
7087
+ return keys === void 0 || keys.length === 0 ? [] : [{
7088
+ group,
7089
+ keys
7090
+ }];
7091
+ });
7092
+ }, [
7093
+ query,
7094
+ extraWords,
7095
+ allowed,
7096
+ resolveGlyph
7097
+ ]);
7098
+ const Current = (value === null ? void 0 : resolveGlyph(value)) ?? fallbackGlyph;
7099
+ const choose = async (next) => {
7100
+ setOpen(false);
7101
+ setQuery("");
7102
+ await onPick(next);
7103
+ };
7104
+ const Clear = clearGlyph ?? fallbackGlyph;
7105
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7106
+ className: "relative flex shrink-0 items-center gap-2",
7107
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7108
+ type: "button",
7109
+ disabled,
7110
+ onClick: () => setOpen((o) => !o),
7111
+ "aria-expanded": open,
7112
+ "aria-label": labels.trigger,
7113
+ title: labels.trigger,
7114
+ className: "rounded border border-border p-1 text-foreground-muted hover:bg-surface-hover disabled:opacity-50",
7115
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Current, { className: "h-4 w-4" })
7116
+ }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7117
+ type: "button",
7118
+ "aria-label": labels.close,
7119
+ onClick: () => setOpen(false),
7120
+ className: "fixed inset-0 z-10 cursor-default"
7121
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7122
+ role: "group",
7123
+ "aria-label": labels.panel,
7124
+ className: "absolute left-0 top-full z-20 mt-1 max-h-80 w-72 overflow-y-auto rounded-md border border-border bg-surface p-2 shadow-lg",
7125
+ children: [
7126
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
7127
+ type: "search",
7128
+ value: query,
7129
+ autoFocus: true,
7130
+ onChange: (e) => setQuery(e.target.value),
7131
+ placeholder: labels.searchPlaceholder,
7132
+ "aria-label": labels.searchPlaceholder,
7133
+ className: "mb-2 w-full rounded border border-border bg-surface-raised px-2 py-1 text-xs text-foreground placeholder:text-foreground-subtle"
7134
+ }),
7135
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
7136
+ type: "button",
7137
+ onClick: () => void choose(null),
7138
+ "aria-pressed": value === null,
7139
+ className: "mb-2 flex w-full items-center gap-1.5 rounded px-1.5 py-1 text-[11px] text-foreground-subtle hover:bg-surface-hover",
7140
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Clear, { className: "h-3.5 w-3.5" }), labels.clear]
7141
+ }),
7142
+ sections.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7143
+ className: "px-1.5 py-2 text-[11px] text-foreground-subtle",
7144
+ children: labels.noMatches
7145
+ }) : sections.map(({ group, keys }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
7146
+ className: "mb-2 last:mb-0",
7147
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
7148
+ className: "px-1.5 pb-1 text-[10px] uppercase tracking-wide text-foreground-subtle",
7149
+ children: labels.groups[group]
7150
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7151
+ className: "grid grid-cols-7 gap-1",
7152
+ children: keys.map((key) => {
7153
+ const Glyph = resolveGlyph(key);
7154
+ if (Glyph === void 0) return null;
7155
+ const selected = value === key;
7156
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7157
+ type: "button",
7158
+ onClick: () => void choose(key),
7159
+ "aria-pressed": selected,
7160
+ "aria-label": labelFor?.(key) ?? key,
7161
+ title: labelFor?.(key) ?? key,
7162
+ className: selected ? "flex items-center justify-center rounded border border-primary bg-primary/10 p-1.5 text-foreground" : "flex items-center justify-center rounded border border-transparent p-1.5 text-foreground-muted hover:bg-surface-hover",
7163
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Glyph, { className: "h-4 w-4" })
7164
+ }, key);
7165
+ })
7166
+ })]
7167
+ }, group))
7168
+ ]
7169
+ })] })]
7170
+ });
7171
+ }
7172
+ //#endregion
7173
+ //#region src/composites/config-icon-field.tsx
7174
+ /**
7175
+ * The control behind a `type: 'icon'` settings field (D780).
7176
+ *
7177
+ * The shared `<IconPicker>` over the display-icon vocabulary, with the device
7178
+ * glyph table — the same control and the same keys the device and room
7179
+ * pickers use. An icon field always draws something, so clearing is "back to
7180
+ * the field's default", and a stored value the vocabulary does not know (a
7181
+ * free-text lucide name from before D780) is drawn as that default.
7182
+ */
7183
+ function labelsFor(label) {
7184
+ return {
7185
+ trigger: `Choose an icon for ${label}`,
7186
+ panel: `${label} icon`,
7187
+ close: "Close",
7188
+ searchPlaceholder: "Search icons",
7189
+ clear: "Default icon",
7190
+ noMatches: "No icon matches",
7191
+ groups: {
7192
+ device: "Devices",
7193
+ lighting: "Lighting",
7194
+ sensing: "Sensing",
7195
+ place: "Places",
7196
+ life: "Life"
7197
+ }
7198
+ };
7199
+ }
7200
+ function ConfigIconFieldControl({ label, value, defaultKey, disabled, onChange }) {
7201
+ const key = (0, _camstack_types.resolveDisplayIconKey)(value, defaultKey);
7202
+ const DefaultGlyph = resolveDisplayIcon(defaultKey) ?? ShieldCheck;
7203
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7204
+ className: "flex items-center gap-2",
7205
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconPicker, {
7206
+ value: key === "" ? null : key,
7207
+ onPick: (next) => onChange(next ?? defaultKey),
7208
+ resolveGlyph: resolveDisplayIcon,
7209
+ fallbackGlyph: DefaultGlyph,
7210
+ labels: labelsFor(label),
7211
+ disabled
7212
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7213
+ className: "text-[11px] text-foreground-subtle font-mono",
7214
+ children: key
7215
+ })]
7216
+ });
7217
+ }
7218
+ //#endregion
7219
+ //#region src/generated/system-hooks.ts
7220
+ /**
7221
+ * Per-method React-Query hooks for every `scope: 'system'` capability.
7222
+ *
7223
+ * Each hook is a thin pass-through to `trpc.<cap>.<method>.useQuery` /
7224
+ * `.useMutation` / `.useSubscription` so the typed inference + RQ
7225
+ * cache/dedup keep working — the generator only contributes a consistent
7226
+ * name and stable export surface so admin-ui pages can write
7227
+ * `useAddonsList()` instead of `trpc.addons.list.useQuery()`.
7228
+ *
7229
+ * Push-subscription methods (`kind: 'subscription'`) emit a typeof
7230
+ * alias over `useSubscription`, so the call shape is
7231
+ * `useToastOnToast(input, { onData: cb })` — same as the underlying
7232
+ * tRPC hook. The eventBus-backed `useLiveEvent` (see
7233
+ * `../hooks/use-live-event.ts`) covers events that don't ride a tRPC
7234
+ * subscription; the two surfaces coexist by design.
7235
+ */
7236
+ /** Generated alias around `trpc.accessories.setChildHidden.useMutation`. */
7237
+ var useAccessoriesSetChildHidden = trpc.accessories.setChildHidden.useMutation;
7238
+ /** Generated alias around `trpc.accessories.getStatus.useQuery`. */
7239
+ var useAccessoriesGetStatus = trpc.accessories.getStatus.useQuery;
7240
+ /** Generated alias around `trpc.addonPages.listPages.useQuery`. */
7241
+ var useAddonPagesListPages = trpc.addonPages.listPages.useQuery;
7242
+ /** Generated alias around `trpc.addons.list.useQuery`. */
7243
+ var useAddonsList = trpc.addons.list.useQuery;
7244
+ /** Generated alias around `trpc.addons.getLogs.useQuery`. */
7245
+ var useAddonsGetLogs = trpc.addons.getLogs.useQuery;
7246
+ /** Generated alias around `trpc.addons.listPackages.useQuery`. */
7247
+ var useAddonsListPackages = trpc.addons.listPackages.useQuery;
7248
+ /** Generated alias around `trpc.addons.installPackage.useMutation`. */
7249
+ var useAddonsInstallPackage = trpc.addons.installPackage.useMutation;
7250
+ /** Generated alias around `trpc.addons.installFromWorkspace.useMutation`. */
7251
+ var useAddonsInstallFromWorkspace = trpc.addons.installFromWorkspace.useMutation;
7252
+ /** Generated alias around `trpc.addons.isWorkspaceAvailable.useQuery`. */
7253
+ var useAddonsIsWorkspaceAvailable = trpc.addons.isWorkspaceAvailable.useQuery;
7254
+ /** Generated alias around `trpc.addons.listWorkspacePackages.useQuery`. */
7255
+ var useAddonsListWorkspacePackages = trpc.addons.listWorkspacePackages.useQuery;
7256
+ /** Generated alias around `trpc.addons.uninstallPackage.useMutation`. */
7257
+ var useAddonsUninstallPackage = trpc.addons.uninstallPackage.useMutation;
7258
+ /** Generated alias around `trpc.addons.listInstallations.useQuery`. */
7259
+ var useAddonsListInstallations = trpc.addons.listInstallations.useQuery;
7260
+ /** Generated alias around `trpc.addons.reloadPackages.useMutation`. */
7261
+ var useAddonsReloadPackages = trpc.addons.reloadPackages.useMutation;
7262
+ /** Generated alias around `trpc.addons.searchAvailable.useQuery`. */
7263
+ var useAddonsSearchAvailable = trpc.addons.searchAvailable.useQuery;
7264
+ /** Generated alias around `trpc.addons.listUpdates.useQuery`. */
7265
+ var useAddonsListUpdates = trpc.addons.listUpdates.useQuery;
7266
+ /** Generated alias around `trpc.addons.updatePackage.useMutation`. */
7267
+ var useAddonsUpdatePackage = trpc.addons.updatePackage.useMutation;
7268
+ /** Generated alias around `trpc.addons.rollbackPackage.useMutation`. */
7269
+ var useAddonsRollbackPackage = trpc.addons.rollbackPackage.useMutation;
7270
+ /** Generated alias around `trpc.addons.forceRefresh.useMutation`. */
7271
+ var useAddonsForceRefresh = trpc.addons.forceRefresh.useMutation;
7272
+ /** Generated alias around `trpc.addons.restartServer.useMutation`. */
7273
+ var useAddonsRestartServer = trpc.addons.restartServer.useMutation;
7274
+ /** Generated alias around `trpc.addons.getLastRestart.useQuery`. */
7275
+ var useAddonsGetLastRestart = trpc.addons.getLastRestart.useQuery;
7276
+ /** Generated alias around `trpc.addons.listFrameworkPackages.useQuery`. */
7277
+ var useAddonsListFrameworkPackages = trpc.addons.listFrameworkPackages.useQuery;
7278
+ /** Generated alias around `trpc.addons.listCapabilityProviders.useQuery`. */
7279
+ var useAddonsListCapabilityProviders = trpc.addons.listCapabilityProviders.useQuery;
7280
+ /** Generated alias around `trpc.addons.setCapabilityProviderEnabled.useMutation`. */
7281
+ var useAddonsSetCapabilityProviderEnabled = trpc.addons.setCapabilityProviderEnabled.useMutation;
7282
+ /** Generated alias around `trpc.addons.getVersions.useQuery`. */
7283
+ var useAddonsGetVersions = trpc.addons.getVersions.useQuery;
7284
+ /** Generated alias around `trpc.addons.restartAddon.useMutation`. */
7285
+ var useAddonsRestartAddon = trpc.addons.restartAddon.useMutation;
7286
+ /** Generated alias around `trpc.addons.retryLoad.useMutation`. */
7287
+ var useAddonsRetryLoad = trpc.addons.retryLoad.useMutation;
7288
+ /** Generated alias around `trpc.addons.getAutoUpdateSettings.useQuery`. */
7289
+ var useAddonsGetAutoUpdateSettings = trpc.addons.getAutoUpdateSettings.useQuery;
7290
+ /** Generated alias around `trpc.addons.setAutoUpdateSettings.useMutation`. */
7291
+ var useAddonsSetAutoUpdateSettings = trpc.addons.setAutoUpdateSettings.useMutation;
7292
+ /** Generated alias around `trpc.addons.getAddonAutoUpdate.useQuery`. */
7293
+ var useAddonsGetAddonAutoUpdate = trpc.addons.getAddonAutoUpdate.useQuery;
7294
+ /** Generated alias around `trpc.addons.setAddonAutoUpdate.useMutation`. */
7295
+ var useAddonsSetAddonAutoUpdate = trpc.addons.setAddonAutoUpdate.useMutation;
7296
+ /** Generated alias around `trpc.addons.applyAutoUpdateToAll.useMutation`. */
7297
+ var useAddonsApplyAutoUpdateToAll = trpc.addons.applyAutoUpdateToAll.useMutation;
7298
+ /** Generated alias around `trpc.addons.custom.useMutation`. */
7299
+ var useAddonsCustom = trpc.addons.custom.useMutation;
7300
+ /** Generated alias around `trpc.addons.startJob.useMutation`. */
7301
+ var useAddonsStartJob = trpc.addons.startJob.useMutation;
7302
+ /** Generated alias around `trpc.addons.getJob.useQuery`. */
7303
+ var useAddonsGetJob = trpc.addons.getJob.useQuery;
7304
+ /** Generated alias around `trpc.addons.listJobs.useQuery`. */
7305
+ var useAddonsListJobs = trpc.addons.listJobs.useQuery;
7306
+ /** Generated alias around `trpc.addons.cancelJob.useMutation`. */
7307
+ var useAddonsCancelJob = trpc.addons.cancelJob.useMutation;
7308
+ /** Generated alias around `trpc.addons.onAddonLogs.useSubscription`. */
7309
+ var useAddonsOnAddonLogs = trpc.addons.onAddonLogs.useSubscription;
7310
+ /** Generated alias around `trpc.addonSettings.getGlobalSettings.useQuery`. */
7311
+ var useAddonSettingsGetGlobalSettings = trpc.addonSettings.getGlobalSettings.useQuery;
7312
+ /** Generated alias around `trpc.addonSettings.updateGlobalSettings.useMutation`. */
7313
+ var useAddonSettingsUpdateGlobalSettings = trpc.addonSettings.updateGlobalSettings.useMutation;
7314
+ /** Generated alias around `trpc.addonSettings.getIntegrationSettings.useQuery`. */
7315
+ var useAddonSettingsGetIntegrationSettings = trpc.addonSettings.getIntegrationSettings.useQuery;
7316
+ /** Generated alias around `trpc.addonSettings.getDeviceSettings.useQuery`. */
7317
+ var useAddonSettingsGetDeviceSettings = trpc.addonSettings.getDeviceSettings.useQuery;
7318
+ /** Generated alias around `trpc.addonSettings.updateDeviceSettings.useMutation`. */
7319
+ var useAddonSettingsUpdateDeviceSettings = trpc.addonSettings.updateDeviceSettings.useMutation;
7320
+ /** Generated alias around `trpc.addonWidgets.listWidgets.useQuery`. */
7321
+ var useAddonWidgetsListWidgets = trpc.addonWidgets.listWidgets.useQuery;
7322
+ /** Generated alias around `trpc.airQualitySensor.getStatus.useQuery`. */
7323
+ var useAirQualitySensorGetStatus = trpc.airQualitySensor.getStatus.useQuery;
7324
+ /** Generated alias around `trpc.alarmPanel.arm.useMutation`. */
7325
+ var useAlarmPanelArm = trpc.alarmPanel.arm.useMutation;
7326
+ /** Generated alias around `trpc.alarmPanel.disarm.useMutation`. */
7327
+ var useAlarmPanelDisarm = trpc.alarmPanel.disarm.useMutation;
7328
+ /** Generated alias around `trpc.alarmPanel.trigger.useMutation`. */
7329
+ var useAlarmPanelTrigger = trpc.alarmPanel.trigger.useMutation;
7330
+ /** Generated alias around `trpc.alarmPanel.getStatus.useQuery`. */
7331
+ var useAlarmPanelGetStatus = trpc.alarmPanel.getStatus.useQuery;
7332
+ /** Generated alias around `trpc.alerts.emit.useMutation`. */
7333
+ var useAlertsEmit = trpc.alerts.emit.useMutation;
7334
+ /** Generated alias around `trpc.alerts.update.useMutation`. */
7335
+ var useAlertsUpdate = trpc.alerts.update.useMutation;
7336
+ /** Generated alias around `trpc.alerts.list.useQuery`. */
7337
+ var useAlertsList = trpc.alerts.list.useQuery;
7338
+ /** Generated alias around `trpc.alerts.getUnreadCount.useQuery`. */
7339
+ var useAlertsGetUnreadCount = trpc.alerts.getUnreadCount.useQuery;
7340
+ /** Generated alias around `trpc.alerts.markRead.useMutation`. */
7341
+ var useAlertsMarkRead = trpc.alerts.markRead.useMutation;
7342
+ /** Generated alias around `trpc.alerts.markAllRead.useMutation`. */
7343
+ var useAlertsMarkAllRead = trpc.alerts.markAllRead.useMutation;
7344
+ /** Generated alias around `trpc.alerts.dismiss.useMutation`. */
7345
+ var useAlertsDismiss = trpc.alerts.dismiss.useMutation;
7346
+ /** Generated alias around `trpc.ambientLightSensor.getStatus.useQuery`. */
7347
+ var useAmbientLightSensorGetStatus = trpc.ambientLightSensor.getStatus.useQuery;
7348
+ /** Generated alias around `trpc.audioAnalysis.resolveDeviceSettings.useQuery`. */
7349
+ var useAudioAnalysisResolveDeviceSettings = trpc.audioAnalysis.resolveDeviceSettings.useQuery;
7350
+ /** Generated alias around `trpc.audioAnalysis.getDeviceSettingsContribution.useQuery`. */
7351
+ var useAudioAnalysisGetDeviceSettingsContribution = trpc.audioAnalysis.getDeviceSettingsContribution.useQuery;
7352
+ /** Generated alias around `trpc.audioAnalysis.getDeviceLiveContribution.useQuery`. */
7353
+ var useAudioAnalysisGetDeviceLiveContribution = trpc.audioAnalysis.getDeviceLiveContribution.useQuery;
7354
+ /** Generated alias around `trpc.audioAnalysis.applyDeviceSettingsPatch.useMutation`. */
7355
+ var useAudioAnalysisApplyDeviceSettingsPatch = trpc.audioAnalysis.applyDeviceSettingsPatch.useMutation;
7356
+ /** Generated alias around `trpc.audioAnalyzer.analyseChunk.useMutation`. */
7357
+ var useAudioAnalyzerAnalyseChunk = trpc.audioAnalyzer.analyseChunk.useMutation;
7358
+ /** Generated alias around `trpc.audioAnalyzer.classify.useQuery`. */
7359
+ var useAudioAnalyzerClassify = trpc.audioAnalyzer.classify.useQuery;
7360
+ /** Generated alias around `trpc.audioAnalyzer.attachDevice.useMutation`. */
7361
+ var useAudioAnalyzerAttachDevice = trpc.audioAnalyzer.attachDevice.useMutation;
7362
+ /** Generated alias around `trpc.audioAnalyzer.detachDevice.useMutation`. */
7363
+ var useAudioAnalyzerDetachDevice = trpc.audioAnalyzer.detachDevice.useMutation;
7364
+ /** Generated alias around `trpc.audioAnalyzer.isReady.useQuery`. */
7365
+ var useAudioAnalyzerIsReady = trpc.audioAnalyzer.isReady.useQuery;
6614
7366
  /** Generated alias around `trpc.audioAnalyzer.dispose.useMutation`. */
6615
7367
  var useAudioAnalyzerDispose = trpc.audioAnalyzer.dispose.useMutation;
6616
7368
  /** Generated alias around `trpc.audioAnalyzer.reprobeAudioEngine.useMutation`. */
@@ -7801,6 +8553,10 @@ var usePipelineAnalyticsCompleteRetrainTrack = trpc.pipelineAnalytics.completeRe
7801
8553
  var usePipelineAnalyticsRestageRetrainTrack = trpc.pipelineAnalytics.restageRetrainTrack.useMutation;
7802
8554
  /** Generated alias around `trpc.pipelineAnalytics.getRetrainExportUrl.useQuery`. */
7803
8555
  var usePipelineAnalyticsGetRetrainExportUrl = trpc.pipelineAnalytics.getRetrainExportUrl.useQuery;
8556
+ /** Generated alias around `trpc.pipelineAnalytics.setRetrainFrameComplete.useMutation`. */
8557
+ var usePipelineAnalyticsSetRetrainFrameComplete = trpc.pipelineAnalytics.setRetrainFrameComplete.useMutation;
8558
+ /** Generated alias around `trpc.pipelineAnalytics.writeRetrainArchive.useMutation`. */
8559
+ var usePipelineAnalyticsWriteRetrainArchive = trpc.pipelineAnalytics.writeRetrainArchive.useMutation;
7804
8560
  /** Generated alias around `trpc.pipelineAnalytics.getEventMedia.useQuery`. */
7805
8561
  var usePipelineAnalyticsGetEventMedia = trpc.pipelineAnalytics.getEventMedia.useQuery;
7806
8562
  /** Generated alias around `trpc.pipelineAnalytics.getTrackMedia.useQuery`. */
@@ -8589,6 +9345,14 @@ var useUserManagementOauthExchangeCode = trpc.userManagement.oauthExchangeCode.u
8589
9345
  var useUserManagementOauthRefresh = trpc.userManagement.oauthRefresh.useMutation;
8590
9346
  /** Generated alias around `trpc.userManagement.oauthVerifyAccessToken.useQuery`. */
8591
9347
  var useUserManagementOauthVerifyAccessToken = trpc.userManagement.oauthVerifyAccessToken.useQuery;
9348
+ /** Generated alias around `trpc.userManagement.listExternalIdentities.useQuery`. */
9349
+ var useUserManagementListExternalIdentities = trpc.userManagement.listExternalIdentities.useQuery;
9350
+ /** Generated alias around `trpc.userManagement.findUserByExternalIdentity.useQuery`. */
9351
+ var useUserManagementFindUserByExternalIdentity = trpc.userManagement.findUserByExternalIdentity.useQuery;
9352
+ /** Generated alias around `trpc.userManagement.linkExternalIdentity.useMutation`. */
9353
+ var useUserManagementLinkExternalIdentity = trpc.userManagement.linkExternalIdentity.useMutation;
9354
+ /** Generated alias around `trpc.userManagement.unlinkExternalIdentity.useMutation`. */
9355
+ var useUserManagementUnlinkExternalIdentity = trpc.userManagement.unlinkExternalIdentity.useMutation;
8592
9356
  /** Generated alias around `trpc.userManagement.listOauthSessions.useQuery`. */
8593
9357
  var useUserManagementListOauthSessions = trpc.userManagement.listOauthSessions.useQuery;
8594
9358
  /** Generated alias around `trpc.userManagement.revokeOauthSession.useMutation`. */
@@ -13981,7 +14745,7 @@ function DialogSurface({ state, onClose }) {
13981
14745
  setError(null);
13982
14746
  }
13983
14747
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
13984
- className: "mt-2 text-xs text-foreground-subtle leading-relaxed",
14748
+ className: "mt-2 text-xs text-foreground-subtle leading-relaxed whitespace-pre-line",
13985
14749
  children: state.options.message
13986
14750
  });
13987
14751
  const footer = state.kind === "alert" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -27164,6 +27928,23 @@ function ColorField({ field, value, onChange, disabled, translationFn }) {
27164
27928
  })]
27165
27929
  });
27166
27930
  }
27931
+ function IconField({ field, value, onChange, disabled, translationFn }) {
27932
+ const defaultKey = typeof field.default === "string" ? field.default : "";
27933
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldWrapper, {
27934
+ label: field.label,
27935
+ description: field.description,
27936
+ required: field.required,
27937
+ span: field.span,
27938
+ translationFn,
27939
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfigIconFieldControl, {
27940
+ label: resolveLabel(field.label, translationFn) ?? field.label,
27941
+ value,
27942
+ defaultKey,
27943
+ disabled: disabled || field.disabled,
27944
+ onChange
27945
+ })
27946
+ });
27947
+ }
27167
27948
  function ProbeField({ field, value, onChange, disabled, translationFn, onTestField, onAction, externalProbe }) {
27168
27949
  const [localStatus, setLocalStatus] = (0, react$1.useState)("idle");
27169
27950
  const [localResult, setLocalResult] = (0, react$1.useState)(null);
@@ -27798,8 +28579,16 @@ function AddonActionButtonField({ field, values, disabled, onAction }) {
27798
28579
  const [loading, setLoading] = (0, react$1.useState)(false);
27799
28580
  const [status, setStatus] = (0, react$1.useState)(null);
27800
28581
  const confirm = useConfirm();
28582
+ const prompt = usePrompt();
27801
28583
  const deviceId = useDeviceId();
27802
28584
  const handleClick = async () => {
28585
+ const asked = field.prompt ? await prompt({
28586
+ title: field.prompt.title ?? field.label,
28587
+ ...field.prompt.message !== void 0 ? { message: field.prompt.message } : {},
28588
+ ...field.prompt.placeholder !== void 0 ? { placeholder: field.prompt.placeholder } : {},
28589
+ confirmLabel: field.buttonLabel
28590
+ }) : null;
28591
+ if (field.prompt && asked === null) return;
27803
28592
  if (field.confirmMessage) {
27804
28593
  if (!await confirm({
27805
28594
  title: field.label,
@@ -27810,6 +28599,7 @@ function AddonActionButtonField({ field, values, disabled, onAction }) {
27810
28599
  }
27811
28600
  if (!onAction) return;
27812
28601
  const params = resolveActionParams(field, values);
28602
+ if (field.prompt && asked !== null) params[field.prompt.param] = asked;
27813
28603
  if (field.includeDeviceId === true && deviceId !== null && deviceId !== void 0) params["deviceId"] = deviceId;
27814
28604
  setLoading(true);
27815
28605
  setStatus(null);
@@ -28270,6 +29060,13 @@ function FormField({ field, values, onChange, disabled, translationFn, onTestFie
28270
29060
  disabled,
28271
29061
  translationFn
28272
29062
  });
29063
+ case "icon": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconField, {
29064
+ field,
29065
+ value,
29066
+ onChange: handleChange,
29067
+ disabled,
29068
+ translationFn
29069
+ });
28273
29070
  case "node-select": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NodeSelectField, {
28274
29071
  field,
28275
29072
  value,
@@ -49744,7 +50541,7 @@ var CONTROL_FILLS = {
49744
50541
  [_camstack_types.DeviceType.Image]: false,
49745
50542
  [_camstack_types.DeviceType.PetFeeder]: false
49746
50543
  };
49747
- var DEVICE_TYPE_VALUES$1 = new Set(Object.values(_camstack_types.DeviceType));
50544
+ var DEVICE_TYPE_VALUES = new Set(Object.values(_camstack_types.DeviceType));
49748
50545
  /**
49749
50546
  * Runtime-string-safe lookup: a device row's `type` crosses the wire as a plain
49750
50547
  * string. The type→control-kind DECISION is single-sourced in `@camstack/types`
@@ -49770,7 +50567,7 @@ function resolveDeviceControl(type) {
49770
50567
  */
49771
50568
  function resolveControlAlign(type, placement) {
49772
50569
  if (placement === "table") return "fit";
49773
- return DEVICE_TYPE_VALUES$1.has(type) && CONTROL_FILLS[type] ? FILL : placement === "center" ? CENTER : RIGHT;
50570
+ return DEVICE_TYPE_VALUES.has(type) && CONTROL_FILLS[type] ? FILL : placement === "center" ? CENTER : RIGHT;
49774
50571
  }
49775
50572
  //#endregion
49776
50573
  //#region src/lib/format-last-seen.ts
@@ -50098,571 +50895,6 @@ function DeviceBatchActionsBar(props) {
50098
50895
  })] });
50099
50896
  }
50100
50897
  //#endregion
50101
- //#region src/composites/device-meta.ts
50102
- var DEVICE_TYPE_META = {
50103
- [_camstack_types.DeviceType.Camera]: {
50104
- icon: Camera,
50105
- label: "Camera"
50106
- },
50107
- [_camstack_types.DeviceType.Hub]: {
50108
- icon: Network,
50109
- label: "Hub"
50110
- },
50111
- [_camstack_types.DeviceType.Light]: {
50112
- icon: Lightbulb,
50113
- label: "Light"
50114
- },
50115
- [_camstack_types.DeviceType.Siren]: {
50116
- icon: Siren,
50117
- label: "Siren"
50118
- },
50119
- [_camstack_types.DeviceType.Switch]: {
50120
- icon: ToggleLeft,
50121
- label: "Switch"
50122
- },
50123
- [_camstack_types.DeviceType.Sensor]: {
50124
- icon: Activity,
50125
- label: "Sensor"
50126
- },
50127
- [_camstack_types.DeviceType.Thermostat]: {
50128
- icon: Thermometer,
50129
- label: "Thermostat"
50130
- },
50131
- [_camstack_types.DeviceType.Climate]: {
50132
- icon: AirVent,
50133
- label: "Air conditioner"
50134
- },
50135
- [_camstack_types.DeviceType.Button]: {
50136
- icon: CircleDot,
50137
- label: "Button"
50138
- },
50139
- [_camstack_types.DeviceType.EventEmitter]: {
50140
- icon: Zap,
50141
- label: "Event Emitter"
50142
- },
50143
- [_camstack_types.DeviceType.Update]: {
50144
- icon: RefreshCw,
50145
- label: "Update"
50146
- },
50147
- [_camstack_types.DeviceType.Generic]: {
50148
- icon: Box,
50149
- label: "Generic"
50150
- },
50151
- [_camstack_types.DeviceType.Notifier]: {
50152
- icon: Bell,
50153
- label: "Notifier"
50154
- },
50155
- [_camstack_types.DeviceType.Script]: {
50156
- icon: ScrollText,
50157
- label: "Script"
50158
- },
50159
- [_camstack_types.DeviceType.Automation]: {
50160
- icon: Workflow,
50161
- label: "Automation"
50162
- },
50163
- [_camstack_types.DeviceType.Lock]: {
50164
- icon: Lock,
50165
- label: "Lock"
50166
- },
50167
- [_camstack_types.DeviceType.Cover]: {
50168
- icon: Blinds,
50169
- label: "Cover"
50170
- },
50171
- [_camstack_types.DeviceType.Valve]: {
50172
- icon: Pipette,
50173
- label: "Valve"
50174
- },
50175
- [_camstack_types.DeviceType.Humidifier]: {
50176
- icon: Waves,
50177
- label: "Humidifier"
50178
- },
50179
- [_camstack_types.DeviceType.WaterHeater]: {
50180
- icon: Bath,
50181
- label: "Water heater"
50182
- },
50183
- [_camstack_types.DeviceType.Fan]: {
50184
- icon: Fan,
50185
- label: "Fan"
50186
- },
50187
- [_camstack_types.DeviceType.MediaPlayer]: {
50188
- icon: MonitorPlay,
50189
- label: "Media player"
50190
- },
50191
- [_camstack_types.DeviceType.AlarmPanel]: {
50192
- icon: ShieldAlert,
50193
- label: "Alarm panel"
50194
- },
50195
- [_camstack_types.DeviceType.Control]: {
50196
- icon: SlidersHorizontal,
50197
- label: "Control"
50198
- },
50199
- [_camstack_types.DeviceType.Presence]: {
50200
- icon: MapPin,
50201
- label: "Presence"
50202
- },
50203
- [_camstack_types.DeviceType.Weather]: {
50204
- icon: CloudSun,
50205
- label: "Weather"
50206
- },
50207
- [_camstack_types.DeviceType.Vacuum]: {
50208
- icon: Bot,
50209
- label: "Vacuum"
50210
- },
50211
- [_camstack_types.DeviceType.LawnMower]: {
50212
- icon: Tractor,
50213
- label: "Lawn mower"
50214
- },
50215
- [_camstack_types.DeviceType.Container]: {
50216
- icon: Box,
50217
- label: "Container"
50218
- },
50219
- [_camstack_types.DeviceType.Image]: {
50220
- icon: Image,
50221
- label: "Image"
50222
- },
50223
- [_camstack_types.DeviceType.PetFeeder]: {
50224
- icon: Bone,
50225
- label: "Pet feeder"
50226
- }
50227
- };
50228
- var DEVICE_ROLE_META = {
50229
- [_camstack_types.DeviceRole.Siren]: {
50230
- icon: Siren,
50231
- label: "Siren"
50232
- },
50233
- [_camstack_types.DeviceRole.Floodlight]: {
50234
- icon: Lightbulb,
50235
- label: "Floodlight"
50236
- },
50237
- [_camstack_types.DeviceRole.Spotlight]: {
50238
- icon: Flashlight,
50239
- label: "Spotlight"
50240
- },
50241
- [_camstack_types.DeviceRole.PirSensor]: {
50242
- icon: Radar,
50243
- label: "PIR sensor"
50244
- },
50245
- [_camstack_types.DeviceRole.Chime]: {
50246
- icon: Bell,
50247
- label: "Chime"
50248
- },
50249
- [_camstack_types.DeviceRole.Autotrack]: {
50250
- icon: Crosshair,
50251
- label: "Auto-track"
50252
- },
50253
- [_camstack_types.DeviceRole.Nightvision]: {
50254
- icon: Moon,
50255
- label: "Night vision"
50256
- },
50257
- [_camstack_types.DeviceRole.PrivacyMask]: {
50258
- icon: EyeOff,
50259
- label: "Privacy mask"
50260
- },
50261
- [_camstack_types.DeviceRole.Doorbell]: {
50262
- icon: DoorClosed,
50263
- label: "Doorbell"
50264
- },
50265
- [_camstack_types.DeviceRole.BinaryHelper]: {
50266
- icon: ToggleLeft,
50267
- label: "Binary helper"
50268
- },
50269
- [_camstack_types.DeviceRole.MotionSensor]: {
50270
- icon: Footprints,
50271
- label: "Motion sensor"
50272
- },
50273
- [_camstack_types.DeviceRole.ContactSensor]: {
50274
- icon: DoorOpen,
50275
- label: "Contact sensor"
50276
- },
50277
- [_camstack_types.DeviceRole.LeakSensor]: {
50278
- icon: Droplet,
50279
- label: "Leak sensor"
50280
- },
50281
- [_camstack_types.DeviceRole.SmokeSensor]: {
50282
- icon: AlarmSmoke,
50283
- label: "Smoke sensor"
50284
- },
50285
- [_camstack_types.DeviceRole.COSensor]: {
50286
- icon: Wind,
50287
- label: "CO sensor"
50288
- },
50289
- [_camstack_types.DeviceRole.GasSensor]: {
50290
- icon: Flame,
50291
- label: "Gas sensor"
50292
- },
50293
- [_camstack_types.DeviceRole.TamperSensor]: {
50294
- icon: ShieldAlert,
50295
- label: "Tamper sensor"
50296
- },
50297
- [_camstack_types.DeviceRole.VibrationSensor]: {
50298
- icon: Vibrate,
50299
- label: "Vibration sensor"
50300
- },
50301
- [_camstack_types.DeviceRole.ConnectivitySensor]: {
50302
- icon: Wifi,
50303
- label: "Connectivity"
50304
- },
50305
- [_camstack_types.DeviceRole.SoundSensor]: {
50306
- icon: Volume2,
50307
- label: "Sound sensor"
50308
- },
50309
- [_camstack_types.DeviceRole.BinarySensor]: {
50310
- icon: ToggleLeft,
50311
- label: "Binary sensor"
50312
- },
50313
- [_camstack_types.DeviceRole.TemperatureSensor]: {
50314
- icon: Thermometer,
50315
- label: "Temperature"
50316
- },
50317
- [_camstack_types.DeviceRole.HumiditySensor]: {
50318
- icon: Droplets,
50319
- label: "Humidity"
50320
- },
50321
- [_camstack_types.DeviceRole.AmbientLightSensor]: {
50322
- icon: Sun,
50323
- label: "Ambient light"
50324
- },
50325
- [_camstack_types.DeviceRole.PressureSensor]: {
50326
- icon: Gauge,
50327
- label: "Pressure"
50328
- },
50329
- [_camstack_types.DeviceRole.WindSensor]: {
50330
- icon: Wind,
50331
- label: "Wind"
50332
- },
50333
- [_camstack_types.DeviceRole.RainSensor]: {
50334
- icon: CloudRain,
50335
- label: "Rain"
50336
- },
50337
- [_camstack_types.DeviceRole.UvSensor]: {
50338
- icon: SunMedium,
50339
- label: "UV index"
50340
- },
50341
- [_camstack_types.DeviceRole.SolarRadiationSensor]: {
50342
- icon: Sunrise,
50343
- label: "Solar radiation"
50344
- },
50345
- [_camstack_types.DeviceRole.SoilMoistureSensor]: {
50346
- icon: Sprout,
50347
- label: "Soil moisture"
50348
- },
50349
- [_camstack_types.DeviceRole.PowerSensor]: {
50350
- icon: Zap,
50351
- label: "Power"
50352
- },
50353
- [_camstack_types.DeviceRole.EnergySensor]: {
50354
- icon: Zap,
50355
- label: "Energy"
50356
- },
50357
- [_camstack_types.DeviceRole.VoltageSensor]: {
50358
- icon: Zap,
50359
- label: "Voltage"
50360
- },
50361
- [_camstack_types.DeviceRole.CurrentSensor]: {
50362
- icon: Zap,
50363
- label: "Current"
50364
- },
50365
- [_camstack_types.DeviceRole.AirQualitySensor]: {
50366
- icon: Wind,
50367
- label: "Air quality"
50368
- },
50369
- [_camstack_types.DeviceRole.BatterySensor]: {
50370
- icon: Gauge,
50371
- label: "Battery"
50372
- },
50373
- [_camstack_types.DeviceRole.NumericSensor]: {
50374
- icon: Hash,
50375
- label: "Numeric sensor"
50376
- },
50377
- [_camstack_types.DeviceRole.EnumSensor]: {
50378
- icon: List,
50379
- label: "Enum sensor"
50380
- },
50381
- [_camstack_types.DeviceRole.DateTimeSensor]: {
50382
- icon: Clock,
50383
- label: "Date / time"
50384
- },
50385
- [_camstack_types.DeviceRole.GenericSensor]: {
50386
- icon: Gauge,
50387
- label: "Sensor"
50388
- },
50389
- [_camstack_types.DeviceRole.NumericControl]: {
50390
- icon: SlidersHorizontal,
50391
- label: "Numeric control"
50392
- },
50393
- [_camstack_types.DeviceRole.SelectControl]: {
50394
- icon: ListChecks,
50395
- label: "Select control"
50396
- },
50397
- [_camstack_types.DeviceRole.TextControl]: {
50398
- icon: Type,
50399
- label: "Text control"
50400
- },
50401
- [_camstack_types.DeviceRole.DateTimeControl]: {
50402
- icon: Clock,
50403
- label: "Date / time control"
50404
- },
50405
- [_camstack_types.DeviceRole.MobilePushNotifier]: {
50406
- icon: Smartphone,
50407
- label: "Mobile push"
50408
- },
50409
- [_camstack_types.DeviceRole.MessagingNotifier]: {
50410
- icon: MessageSquare,
50411
- label: "Messaging"
50412
- },
50413
- [_camstack_types.DeviceRole.EmailNotifier]: {
50414
- icon: Mail,
50415
- label: "Email"
50416
- },
50417
- [_camstack_types.DeviceRole.GenericNotifier]: {
50418
- icon: Bell,
50419
- label: "Notifier"
50420
- }
50421
- };
50422
- var DEVICE_TYPE_VALUES = new Set(Object.values(_camstack_types.DeviceType));
50423
- var DEVICE_ROLE_VALUES = new Set(Object.values(_camstack_types.DeviceRole));
50424
- function isDeviceType(value) {
50425
- return DEVICE_TYPE_VALUES.has(value);
50426
- }
50427
- function isDeviceRole(value) {
50428
- return DEVICE_ROLE_VALUES.has(value);
50429
- }
50430
- /** Meta for a known `DeviceType`. */
50431
- function deviceTypeMeta(type) {
50432
- return DEVICE_TYPE_META[type];
50433
- }
50434
- /** Meta for a known `DeviceRole`. */
50435
- function deviceRoleMeta(role) {
50436
- return DEVICE_ROLE_META[role];
50437
- }
50438
- var FALLBACK_DEVICE_TYPE_META = DEVICE_TYPE_META[_camstack_types.DeviceType.Generic];
50439
- var FALLBACK_DEVICE_ROLE_META = DEVICE_ROLE_META[_camstack_types.DeviceRole.GenericSensor];
50440
- /**
50441
- * Runtime-string-safe lookup: device data crosses process / network boundaries
50442
- * as plain strings, so an unrecognised value falls back to the `Generic` meta
50443
- * instead of throwing.
50444
- *
50445
- * This also guards mixed-version module-federation loads where the host
50446
- * `@camstack/types` enum has a new member before the loaded ui-library bundle
50447
- * has the matching `DEVICE_TYPE_META` entry.
50448
- */
50449
- function deviceTypeMetaOf(type) {
50450
- if (!isDeviceType(type)) return FALLBACK_DEVICE_TYPE_META;
50451
- return DEVICE_TYPE_META[type] ?? FALLBACK_DEVICE_TYPE_META;
50452
- }
50453
- /** Runtime-string-safe role lookup; unknown roles fall back to `GenericSensor`. */
50454
- function deviceRoleMetaOf(role) {
50455
- if (!isDeviceRole(role)) return FALLBACK_DEVICE_ROLE_META;
50456
- return DEVICE_ROLE_META[role] ?? FALLBACK_DEVICE_ROLE_META;
50457
- }
50458
- /**
50459
- * Icons an operator may pick for a per-device `display.icon` override (and for
50460
- * a per-role default), keyed by a stable kebab-case name. Named imports only —
50461
- * no dynamic indexing into lucide, so the bundle carries exactly this set.
50462
- * Unknown keys resolve to `undefined` (caller falls through to the next level).
50463
- *
50464
- * The vocabulary is the one the VIEWER draws from too (its own icon module,
50465
- * keyed by these same strings): a key added here that the phone cannot draw
50466
- * falls through to the next resolution level there, which reads as "my change
50467
- * did nothing". So the KEY is the contract — a stable kebab-case string, never
50468
- * a glyph identity — and `device-meta.test.ts` pins that every key resolves
50469
- * and that the set never loses a key an operator may already have persisted.
50470
- *
50471
- * Grouped by what an operator is naming: a device, a light or appliance, a
50472
- * sensor or the weather it reads, a room, or what moves through it. `gauge2`
50473
- * is a legacy alias kept for rows that persisted it.
50474
- */
50475
- var DISPLAY_ICON_REGISTRY = {
50476
- camera: Camera,
50477
- network: Network,
50478
- "toggle-left": ToggleLeft,
50479
- "toggle-right": ToggleRight,
50480
- "circle-dot": CircleDot,
50481
- "refresh-cw": RefreshCw,
50482
- box: Box,
50483
- "scroll-text": ScrollText,
50484
- workflow: Workflow,
50485
- lock: Lock,
50486
- "lock-open": LockOpen,
50487
- blinds: Blinds,
50488
- "air-vent": AirVent,
50489
- "monitor-play": MonitorPlay,
50490
- "sliders-horizontal": SlidersHorizontal,
50491
- "map-pin": MapPin,
50492
- bot: Bot,
50493
- tractor: Tractor,
50494
- image: Image,
50495
- bone: Bone,
50496
- "door-closed": DoorClosed,
50497
- "door-open": DoorOpen,
50498
- crosshair: Crosshair,
50499
- eye: Eye,
50500
- "eye-off": EyeOff,
50501
- smartphone: Smartphone,
50502
- phone: Phone,
50503
- tablet: Tablet,
50504
- laptop: Laptop,
50505
- monitor: Monitor,
50506
- tv: Tv,
50507
- speaker: Speaker,
50508
- printer: Printer,
50509
- projector: Projector,
50510
- "gamepad-2": Gamepad2,
50511
- radio: Radio,
50512
- router: Router,
50513
- bluetooth: Bluetooth,
50514
- cpu: Cpu,
50515
- "hard-drive": HardDrive,
50516
- server: Server,
50517
- mic: Mic,
50518
- "mic-off": MicOff,
50519
- video: Video,
50520
- "video-off": VideoOff,
50521
- music: Music,
50522
- plug: Plug,
50523
- "plug-zap": PlugZap,
50524
- power: Power,
50525
- "power-off": PowerOff,
50526
- cable: Cable,
50527
- battery: Battery,
50528
- "battery-charging": BatteryCharging,
50529
- "battery-full": BatteryFull,
50530
- "battery-low": BatteryLow,
50531
- key: Key,
50532
- "key-round": KeyRound,
50533
- mailbox: Mailbox,
50534
- package: Package,
50535
- shield: Shield,
50536
- "shield-check": ShieldCheck,
50537
- "shield-off": ShieldOff,
50538
- lightbulb: Lightbulb,
50539
- lamp: Lamp,
50540
- "lamp-ceiling": LampCeiling,
50541
- "lamp-desk": LampDesk,
50542
- "lamp-floor": LampFloor,
50543
- flashlight: Flashlight,
50544
- heater: Heater,
50545
- snowflake: Snowflake,
50546
- "thermometer-sun": ThermometerSun,
50547
- "thermometer-snowflake": ThermometerSnowflake,
50548
- refrigerator: Refrigerator,
50549
- microwave: Microwave,
50550
- "washing-machine": WashingMachine,
50551
- "cooking-pot": CookingPot,
50552
- utensils: Utensils,
50553
- thermometer: Thermometer,
50554
- droplets: Droplets,
50555
- droplet: Droplet,
50556
- gauge: Gauge,
50557
- gauge2: Gauge,
50558
- sun: Sun,
50559
- "sun-medium": SunMedium,
50560
- sunrise: Sunrise,
50561
- moon: Moon,
50562
- wind: Wind,
50563
- cloud: Cloud,
50564
- "cloud-rain": CloudRain,
50565
- "cloud-sun": CloudSun,
50566
- waves: Waves,
50567
- sprout: Sprout,
50568
- leaf: Leaf,
50569
- flower: Flower,
50570
- zap: Zap,
50571
- flame: Flame,
50572
- bell: Bell,
50573
- "bell-ring": BellRing,
50574
- "bell-off": BellOff,
50575
- siren: Siren,
50576
- radar: Radar,
50577
- activity: Activity,
50578
- hash: Hash,
50579
- clock: Clock,
50580
- timer: Timer,
50581
- calendar: Calendar,
50582
- pipette: Pipette,
50583
- fan: Fan,
50584
- bath: Bath,
50585
- footprints: Footprints,
50586
- "alarm-smoke": AlarmSmoke,
50587
- "shield-alert": ShieldAlert,
50588
- vibrate: Vibrate,
50589
- wifi: Wifi,
50590
- "wifi-off": WifiOff,
50591
- "volume-2": Volume2,
50592
- "volume-x": VolumeX,
50593
- heart: Heart,
50594
- "heart-pulse": HeartPulse,
50595
- star: Star,
50596
- sparkles: Sparkles,
50597
- house: House,
50598
- sofa: Sofa,
50599
- "bed-double": BedDouble,
50600
- bed: Bed,
50601
- warehouse: Warehouse,
50602
- trees: Trees,
50603
- "tree-pine": TreePine,
50604
- fence: Fence,
50605
- store: Store,
50606
- building: Building,
50607
- "building-2": Building2,
50608
- mountain: Mountain,
50609
- umbrella: Umbrella,
50610
- dumbbell: Dumbbell,
50611
- wrench: Wrench,
50612
- hammer: Hammer,
50613
- drill: Drill,
50614
- "waves-ladder": WavesLadder,
50615
- "circle-parking": CircleParking,
50616
- briefcase: Briefcase,
50617
- route: Route,
50618
- car: Car,
50619
- bike: Bike,
50620
- bus: Bus,
50621
- truck: Truck,
50622
- plane: Plane,
50623
- "train-front": TrainFront,
50624
- sailboat: Sailboat,
50625
- dog: Dog,
50626
- cat: Cat,
50627
- bird: Bird,
50628
- "paw-print": PawPrint,
50629
- baby: Baby,
50630
- user: User,
50631
- users: Users,
50632
- "person-standing": PersonStanding
50633
- };
50634
- /** Resolve a `display.icon` name to a lucide component, or `undefined` when the
50635
- * name is not in the curated `DISPLAY_ICON_REGISTRY`. */
50636
- function resolveDisplayIcon(name) {
50637
- return Object.hasOwn(DISPLAY_ICON_REGISTRY, name) ? DISPLAY_ICON_REGISTRY[name] : void 0;
50638
- }
50639
- /**
50640
- * EXHAUSTIVE device-type filter options — one entry per `DeviceType` member,
50641
- * derived from `DEVICE_TYPE_META` so the set is always complete and a newly
50642
- * added `DeviceType` automatically appears in every type filter (Devices page,
50643
- * adoption modal, …) without touching any consumer.
50644
- *
50645
- * The previous per-consumer logic built options from the types PRESENT in the
50646
- * list (or a hand-maintained subset), so recently-added types (vacuum, lawn
50647
- * mower, valve, humidifier, water heater, weather, image, …) were silently
50648
- * missing from the dropdown. This is the single source of truth instead.
50649
- *
50650
- * `id` is the raw `DeviceType` string (matches the filtering predicate, which
50651
- * compares against `device.type`); `label` + `icon` come from the meta map.
50652
- * Ordered by `DeviceType` declaration order for a stable, intentional layout.
50653
- */
50654
- var ALL_DEVICE_TYPE_FILTER_OPTIONS = Object.values(_camstack_types.DeviceType).map((type) => {
50655
- const meta = deviceTypeMetaOf(type);
50656
- return {
50657
- id: type,
50658
- label: meta.label,
50659
- icon: meta.icon
50660
- };
50661
- });
50662
- function allDeviceTypeFilterOptions() {
50663
- return ALL_DEVICE_TYPE_FILTER_OPTIONS;
50664
- }
50665
- //#endregion
50666
50898
  //#region src/hooks/use-debounced-string.ts
50667
50899
  /**
50668
50900
  * useDebouncedString — return `value` after `delay` ms have passed
@@ -50681,6 +50913,59 @@ function useDebouncedString(value, delayMs) {
50681
50913
  return debounced;
50682
50914
  }
50683
50915
  //#endregion
50916
+ //#region src/hooks/use-persisted-page-size.ts
50917
+ /**
50918
+ * usePersistedPageSize — a "rows per page" choice remembered per viewer.
50919
+ *
50920
+ * The caller names the storage key, the allowed sizes and the default. Each
50921
+ * surface with its own default or option set keeps its own key, so one
50922
+ * surface's choice never lands on another that cannot show it (the device
50923
+ * list's global key stays `useDeviceListPageSize`'s, with its own options).
50924
+ *
50925
+ * `PAGE_SIZE_ALL` (0) is the "All" option: one page holding every row.
50926
+ *
50927
+ * Storage is a convenience, never a requirement: every read and write is
50928
+ * guarded, and a blocked / private-mode / missing storage simply means the
50929
+ * choice lasts until the page is left. A stored value that is not one of the
50930
+ * options (an older option set, a hand-edited value) is ignored.
50931
+ */
50932
+ /** The "All" option — one page holding every row. */
50933
+ var PAGE_SIZE_ALL = 0;
50934
+ function readStored(key, options, fallback) {
50935
+ if (key === null || typeof window === "undefined") return fallback;
50936
+ try {
50937
+ const raw = window.localStorage.getItem(key);
50938
+ if (raw === null) return fallback;
50939
+ const parsed = Number.parseInt(raw, 10);
50940
+ return Number.isFinite(parsed) && options.includes(parsed) ? parsed : fallback;
50941
+ } catch {
50942
+ return fallback;
50943
+ }
50944
+ }
50945
+ function writeStored(key, value) {
50946
+ if (key === null || typeof window === "undefined") return;
50947
+ try {
50948
+ window.localStorage.setItem(key, String(value));
50949
+ } catch {}
50950
+ }
50951
+ /** @returns `[size, setSize]`; `setSize` ignores a size that is not an option. */
50952
+ function usePersistedPageSize({ storageKey, options, defaultSize }) {
50953
+ const seed = options.includes(defaultSize) ? defaultSize : options[0] ?? defaultSize;
50954
+ const [size, setSize] = (0, react$1.useState)(() => readStored(storageKey, options, seed));
50955
+ return [size, (0, react$1.useCallback)((next) => {
50956
+ if (!options.includes(next)) return;
50957
+ setSize(next);
50958
+ writeStored(storageKey, next);
50959
+ }, [options, storageKey])];
50960
+ }
50961
+ /**
50962
+ * The number of rows one page holds for a chosen size: "All" is every row
50963
+ * (at least 1, so the pager arithmetic never divides by zero).
50964
+ */
50965
+ function effectivePageSize(size, totalItems) {
50966
+ return size === 0 ? Math.max(1, totalItems) : size;
50967
+ }
50968
+ //#endregion
50684
50969
  //#region src/hooks/use-device-list-page-size.ts
50685
50970
  /**
50686
50971
  * useDeviceListPageSize — global page-size preference for the device-list
@@ -50691,8 +50976,9 @@ function useDebouncedString(value, delayMs) {
50691
50976
  * value is validated against {@link DEVICE_LIST_PAGE_SIZE_OPTIONS}; any
50692
50977
  * garbage / out-of-range value falls back to the provided default.
50693
50978
  *
50694
- * SSR / jsdom-safe: every `window` access is guarded so the hook can run in
50695
- * a non-browser environment without throwing.
50979
+ * A thin binding of `usePersistedPageSize` to that key and option set; a
50980
+ * surface with a different default or options uses the generic hook with a
50981
+ * key of its own.
50696
50982
  */
50697
50983
  /** Selectable per-page sizes. The table default (50) is intentionally one of them. */
50698
50984
  var DEVICE_LIST_PAGE_SIZE_OPTIONS = [
@@ -50703,33 +50989,6 @@ var DEVICE_LIST_PAGE_SIZE_OPTIONS = [
50703
50989
  ];
50704
50990
  /** Single global key shared across every device-list surface. */
50705
50991
  var DEVICE_LIST_PAGE_SIZE_KEY = "camstack.device-list.pageSize";
50706
- function isAllowed(value) {
50707
- return DEVICE_LIST_PAGE_SIZE_OPTIONS.includes(value);
50708
- }
50709
- /**
50710
- * Normalize a caller-supplied `defaultSize` to an allowed option, falling back
50711
- * to the canonical default so a stray value can never seed a phantom size.
50712
- */
50713
- function safeDefault(defaultSize) {
50714
- return isAllowed(defaultSize) ? defaultSize : 50;
50715
- }
50716
- function readStored(defaultSize) {
50717
- if (typeof window === "undefined") return defaultSize;
50718
- try {
50719
- const raw = window.localStorage.getItem(DEVICE_LIST_PAGE_SIZE_KEY);
50720
- if (raw === null) return defaultSize;
50721
- const parsed = Number.parseInt(raw, 10);
50722
- return Number.isFinite(parsed) && isAllowed(parsed) ? parsed : defaultSize;
50723
- } catch {
50724
- return defaultSize;
50725
- }
50726
- }
50727
- function writeStored(value) {
50728
- if (typeof window === "undefined") return;
50729
- try {
50730
- window.localStorage.setItem(DEVICE_LIST_PAGE_SIZE_KEY, String(value));
50731
- } catch {}
50732
- }
50733
50992
  /**
50734
50993
  * @param defaultSize fallback when nothing valid is stored. If it is not itself
50735
50994
  * a member of {@link DEVICE_LIST_PAGE_SIZE_OPTIONS}, it is replaced by
@@ -50737,13 +50996,11 @@ function writeStored(value) {
50737
50996
  * @returns `[size, setSize]` — `setSize` ignores values outside the allowed set.
50738
50997
  */
50739
50998
  function useDeviceListPageSize(defaultSize) {
50740
- const seed = safeDefault(defaultSize);
50741
- const [size, setSize] = (0, react$1.useState)(() => readStored(seed));
50742
- return [size, (0, react$1.useCallback)((next) => {
50743
- if (!isAllowed(next)) return;
50744
- setSize(next);
50745
- writeStored(next);
50746
- }, [])];
50999
+ return usePersistedPageSize({
51000
+ storageKey: DEVICE_LIST_PAGE_SIZE_KEY,
51001
+ options: DEVICE_LIST_PAGE_SIZE_OPTIONS,
51002
+ defaultSize: DEVICE_LIST_PAGE_SIZE_OPTIONS.includes(defaultSize) ? defaultSize : 50
51003
+ });
50747
51004
  }
50748
51005
  //#endregion
50749
51006
  //#region src/composites/device-item/helpers.ts
@@ -53041,6 +53298,29 @@ function groupTopLevelAndAccessories(devices, options) {
53041
53298
  };
53042
53299
  }
53043
53300
  //#endregion
53301
+ //#region src/composites/device-list/page-size-select.tsx
53302
+ function pageSizeLabel(size) {
53303
+ return size === 0 ? "All" : String(size);
53304
+ }
53305
+ function PageSizeSelect({ value, options, onChange, ariaLabel }) {
53306
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
53307
+ className: "inline-flex items-center gap-1.5",
53308
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
53309
+ "aria-hidden": "true",
53310
+ children: "Per page"
53311
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
53312
+ "aria-label": ariaLabel,
53313
+ value,
53314
+ onChange: (e) => onChange(Number.parseInt(e.target.value, 10)),
53315
+ className: "h-7 px-1.5 rounded-md border border-border bg-surface text-[11px] text-foreground focus:outline-none focus:ring-1 focus:ring-primary/40",
53316
+ children: options.map((opt) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
53317
+ value: opt,
53318
+ children: pageSizeLabel(opt)
53319
+ }, opt))
53320
+ })]
53321
+ });
53322
+ }
53323
+ //#endregion
53044
53324
  //#region src/composites/device-list/multi-select.tsx
53045
53325
  /**
53046
53326
  * Compact multi-select dropdown — a single chip-sized trigger ("Label · N")
@@ -53955,23 +54235,11 @@ function DeviceModeList(props) {
53955
54235
  totalItems,
53956
54236
  " ",
53957
54237
  totalItems === 1 ? "device" : "devices"
53958
- ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
53959
- className: "inline-flex items-center gap-1.5",
53960
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
53961
- "aria-hidden": "true",
53962
- children: "Per page"
53963
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
53964
- "aria-label": "Devices per page",
53965
- value: tablePageSize,
53966
- onChange: (e) => {
53967
- setTablePageSize(Number.parseInt(e.target.value, 10));
53968
- },
53969
- className: "h-7 px-1.5 rounded-md border border-border bg-surface text-[11px] text-foreground focus:outline-none focus:ring-1 focus:ring-primary/40",
53970
- children: DEVICE_LIST_PAGE_SIZE_OPTIONS.map((opt) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
53971
- value: opt,
53972
- children: opt
53973
- }, opt))
53974
- })]
54238
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PageSizeSelect, {
54239
+ ariaLabel: "Devices per page",
54240
+ value: tablePageSize,
54241
+ options: DEVICE_LIST_PAGE_SIZE_OPTIONS,
54242
+ onChange: setTablePageSize
53975
54243
  })]
53976
54244
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pagination, {
53977
54245
  currentPage: safePage,
@@ -54015,6 +54283,7 @@ function useDeviceListRealtimeSync() {
54015
54283
  }
54016
54284
  //#endregion
54017
54285
  //#region src/composites/device-list/generic-mode.tsx
54286
+ var FIXED_PAGE_SIZE = 100;
54018
54287
  /**
54019
54288
  * Generic render-prop list: search + pagination chrome shared with the device
54020
54289
  * list, over an arbitrary item type. Local (non-URL) state — intended for
@@ -54022,7 +54291,12 @@ function useDeviceListRealtimeSync() {
54022
54291
  * `renderItem`.
54023
54292
  */
54024
54293
  function GenericModeList(props) {
54025
- const { items, getKey, getSearchText, renderItem, getFilterType, mapFilterOption, filterOptions, filterLabel = "Type", initialTypeFilters = [], columns, renderExpanded, isExpanded, rowClassName, onRowClick, isLoading = false, searchPlaceholder = "Search", emptyLabel = "No items found.", pageSize = 100, toolbar } = props;
54294
+ const { items, getKey, getSearchText, renderItem, getFilterType, mapFilterOption, filterOptions, filterLabel = "Type", initialTypeFilters = [], columns, renderExpanded, isExpanded, rowClassName, onRowClick, isLoading = false, searchPlaceholder = "Search", emptyLabel = "No items found.", pageSize: fixedPageSize = FIXED_PAGE_SIZE, pageSizeSelector, toolbar } = props;
54295
+ const [chosenPageSize, setChosenPageSize] = usePersistedPageSize(pageSizeSelector ?? {
54296
+ storageKey: null,
54297
+ options: [fixedPageSize],
54298
+ defaultSize: fixedPageSize
54299
+ });
54026
54300
  const [searchInput, setSearchInput] = (0, react$1.useState)("");
54027
54301
  const debouncedSearch = useDebouncedString(searchInput, 300);
54028
54302
  const [typeFilters, setTypeFilters] = (0, react$1.useState)(() => [...initialTypeFilters]);
@@ -54071,6 +54345,7 @@ function GenericModeList(props) {
54071
54345
  getFilterType
54072
54346
  ]);
54073
54347
  const totalItems = filtered.length;
54348
+ const pageSize = pageSizeSelector ? effectivePageSize(chosenPageSize, totalItems) : fixedPageSize;
54074
54349
  const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
54075
54350
  const safePage = Math.min(page, totalPages);
54076
54351
  const startIdx = (safePage - 1) * pageSize;
@@ -54154,7 +54429,23 @@ function GenericModeList(props) {
54154
54429
  }),
54155
54430
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
54156
54431
  className: "shrink-0",
54157
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pagination, {
54432
+ children: pageSizeSelector && totalItems > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
54433
+ className: "flex items-center justify-between gap-4 flex-wrap pt-2 text-[11px] text-foreground-subtle",
54434
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
54435
+ className: "inline-flex items-center gap-2",
54436
+ children: [totalItems <= pageSize && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [totalItems, " total"] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PageSizeSelect, {
54437
+ ariaLabel: "Rows per page",
54438
+ value: chosenPageSize,
54439
+ options: pageSizeSelector.options,
54440
+ onChange: setChosenPageSize
54441
+ })]
54442
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pagination, {
54443
+ currentPage: safePage,
54444
+ totalItems,
54445
+ pageSize,
54446
+ onPageChange: setPage
54447
+ })]
54448
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pagination, {
54158
54449
  currentPage: safePage,
54159
54450
  totalItems,
54160
54451
  pageSize,
@@ -54283,6 +54574,24 @@ var DeviceItemRowSchema = zod.z.object({
54283
54574
  });
54284
54575
  var SupportedKindsSchema = zod.z.array(zod.z.string());
54285
54576
  var STATUS_POLL_INTERVAL_MS = 1e4;
54577
+ /** Where the viewer's "rows per page" choice for this table is remembered. */
54578
+ var DEVICE_EXPORT_PAGE_SIZE_KEY = "camstack.device-export.pageSize";
54579
+ /**
54580
+ * The exposed-devices table's page sizes. Its own key and options, not the
54581
+ * Devices page's (25/50/100/200, default 50): the operator asked for a short
54582
+ * default here (25) and an "All" for a quick scan of every exportable device.
54583
+ */
54584
+ var DEVICE_EXPORT_PAGE_SIZE = {
54585
+ options: [
54586
+ 10,
54587
+ 25,
54588
+ 50,
54589
+ 100,
54590
+ 0
54591
+ ],
54592
+ defaultSize: 25,
54593
+ storageKey: DEVICE_EXPORT_PAGE_SIZE_KEY
54594
+ };
54286
54595
  /**
54287
54596
  * The fleet-list poll. Deliberately the same 30 s as the window
54288
54597
  * `applyFleetQueryDefaults` registers for `deviceManager.listAll` (D274) —
@@ -54367,6 +54676,35 @@ function SetupSection({ setup }) {
54367
54676
  })]
54368
54677
  });
54369
54678
  }
54679
+ /** Parse `getStatus` against the canonical cap schema (loose wire output). */
54680
+ function parseStatus(data) {
54681
+ const parsed = _camstack_types.DeviceExportStatusSchema.safeParse(data);
54682
+ return parsed.success ? parsed.data : null;
54683
+ }
54684
+ /**
54685
+ * The exporter's error and Setup block on their own — for a page that shows
54686
+ * them above its settings while the devices table sits at the bottom. Renders
54687
+ * nothing when the exporter reports neither. Shares `getStatus`'s query key
54688
+ * with the panel, so mounting both costs one request.
54689
+ */
54690
+ function DeviceExportSetup({ addonId }) {
54691
+ const statusQuery = useDeviceExportGetStatus({
54692
+ nodeId: "hub",
54693
+ addonId
54694
+ }, {
54695
+ refetchInterval: STATUS_POLL_INTERVAL_MS,
54696
+ retry: false
54697
+ });
54698
+ const status = (0, react$1.useMemo)(() => parseStatus(statusQuery.data), [statusQuery.data]);
54699
+ if (!status?.error && !status?.setup) return null;
54700
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
54701
+ className: "rounded-lg border border-border bg-surface overflow-hidden",
54702
+ children: [status.error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
54703
+ className: "px-2 sm:px-4 py-3 border-b border-border",
54704
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorBox, { message: status.error })
54705
+ }), status.setup && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SetupSection, { setup: status.setup })]
54706
+ });
54707
+ }
54370
54708
  /**
54371
54709
  * Per-row Expose/Exposed toggle. Reflects whether the row's device is exposed
54372
54710
  * to THIS exporter and flips it through the SAME runner the bulk buttons use —
@@ -54445,7 +54783,7 @@ function BulkSummary({ outcome, onDismiss }) {
54445
54783
  * Generic device-export panel. Addon-agnostic — only the `device-export`
54446
54784
  * cap drives it.
54447
54785
  */
54448
- function DeviceExportPanel({ addonId, onOpenDevice }) {
54786
+ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
54449
54787
  const queryClient = (0, _tanstack_react_query.useQueryClient)();
54450
54788
  const statusQuery = useDeviceExportGetStatus({
54451
54789
  nodeId: "hub",
@@ -54471,10 +54809,7 @@ function DeviceExportPanel({ addonId, onOpenDevice }) {
54471
54809
  }, [queryClient]);
54472
54810
  const exposeMutation = useDeviceExportExposeDevice({ onSuccess: invalidate });
54473
54811
  const unexposeMutation = useDeviceExportUnexposeDevice({ onSuccess: invalidate });
54474
- const status = (0, react$1.useMemo)(() => {
54475
- const parsed = _camstack_types.DeviceExportStatusSchema.safeParse(statusQuery.data);
54476
- return parsed.success ? parsed.data : null;
54477
- }, [statusQuery.data]);
54812
+ const status = (0, react$1.useMemo)(() => parseStatus(statusQuery.data), [statusQuery.data]);
54478
54813
  const exposed = (0, react$1.useMemo)(() => {
54479
54814
  const parsed = ExposedDeviceArraySchema.safeParse(exposedQuery.data);
54480
54815
  return parsed.success ? parsed.data : [];
@@ -54697,11 +55032,11 @@ function DeviceExportPanel({ addonId, onOpenDevice }) {
54697
55032
  ]
54698
55033
  }), loading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoaderCircle, { className: "h-3.5 w-3.5 animate-spin text-foreground-subtle" })]
54699
55034
  }),
54700
- status?.error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55035
+ showSetup && status?.error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
54701
55036
  className: "px-2 sm:px-4 py-3 border-b border-border",
54702
55037
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorBox, { message: status.error })
54703
55038
  }),
54704
- status?.setup && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SetupSection, { setup: status.setup }),
55039
+ showSetup && status?.setup && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SetupSection, { setup: status.setup }),
54705
55040
  outcome !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BulkSummary, {
54706
55041
  outcome,
54707
55042
  onDismiss: dismissOutcome
@@ -54721,6 +55056,7 @@ function DeviceExportPanel({ addonId, onOpenDevice }) {
54721
55056
  columns,
54722
55057
  onRowClick: onOpenDevice ? onRowClick : void 0,
54723
55058
  toolbar: selectionToolbar,
55059
+ pageSizeSelector: DEVICE_EXPORT_PAGE_SIZE,
54724
55060
  searchPlaceholder: "Search by name or stableId",
54725
55061
  emptyLabel: "No device matches this search."
54726
55062
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmptyState, {
@@ -54746,145 +55082,6 @@ function DeviceGrid({ children, minCardWidth = 220, gap = 3, className }) {
54746
55082
  });
54747
55083
  }
54748
55084
  //#endregion
54749
- //#region src/composites/icon-picker.tsx
54750
- /**
54751
- * icon-picker — the ONE control for choosing an icon, wherever admin-ui asks.
54752
- *
54753
- * There were two: a device one over `DISPLAY_ICON_REGISTRY` and a room one
54754
- * over `LOCATION_ICON_IDS`, identical apart from the vocabulary they walked.
54755
- * That was tolerable while the device set was 56 glyphs in a flat grid. It is
54756
- * not at 157: a picker that only lets the operator HUNT is a picker they stop
54757
- * using, and the two copies were already drifting — only one of them had an
54758
- * i18n'd label.
54759
- *
54760
- * So the vocabulary, the grouping and the search all come from
54761
- * `@camstack/types`' `display-icon-vocabulary`, which is the same module the
54762
- * viewer's sheet searches. What is left here is the web half: a popover, a
54763
- * grid, and a text field.
54764
- *
54765
- * ## What it does NOT own
54766
- *
54767
- * - **The glyphs.** `resolveGlyph` is a prop, because this package's table is
54768
- * `lucide-react` and the viewer's is `lucide-react-native`. A key this build
54769
- * cannot draw is SKIPPED rather than rendered as a hole — an empty tile
54770
- * reads as a rendering bug, not as a missing choice.
54771
- * - **The words.** Every string is a prop. This package has no i18n (one file
54772
- * in it imports react-i18next, and that is the exception), and a control
54773
- * that reached for a translation would force it on every consumer.
54774
- * - **The write.** `onPick` is called and that is all; whether it is a form
54775
- * value or an immediate mutation is the caller's business, as it already was
54776
- * in both pickers this replaces.
54777
- *
54778
- * ## The clear tile is never filtered away
54779
- *
54780
- * The "inherit" / "no icon" choice sits above the sections and ignores the
54781
- * search. A query that hid it would strand the operator on a glyph they picked
54782
- * by accident with no way back to the default — the one action a picker must
54783
- * always offer.
54784
- */
54785
- function IconPicker({ value, onPick, resolveGlyph, fallbackGlyph, labels, only, extraWords, clearGlyph, labelFor, disabled = false }) {
54786
- const [open, setOpen] = (0, react$1.useState)(false);
54787
- const [query, setQuery] = (0, react$1.useState)("");
54788
- const allowed = (0, react$1.useMemo)(() => only === void 0 ? null : new Set(only), [only]);
54789
- const sections = (0, react$1.useMemo)(() => {
54790
- const hits = (0, _camstack_types.searchDisplayIcons)(query, extraWords);
54791
- const byGroup = /* @__PURE__ */ new Map();
54792
- for (const hit of hits) {
54793
- const { key, group } = hit.entry;
54794
- if (allowed !== null && !allowed.has(key)) continue;
54795
- if (resolveGlyph(key) === void 0) continue;
54796
- const bucket = byGroup.get(group);
54797
- if (bucket === void 0) byGroup.set(group, [key]);
54798
- else bucket.push(key);
54799
- }
54800
- return _camstack_types.DISPLAY_ICON_GROUPS.flatMap((group) => {
54801
- const keys = byGroup.get(group);
54802
- return keys === void 0 || keys.length === 0 ? [] : [{
54803
- group,
54804
- keys
54805
- }];
54806
- });
54807
- }, [
54808
- query,
54809
- extraWords,
54810
- allowed,
54811
- resolveGlyph
54812
- ]);
54813
- const Current = (value === null ? void 0 : resolveGlyph(value)) ?? fallbackGlyph;
54814
- const choose = async (next) => {
54815
- setOpen(false);
54816
- setQuery("");
54817
- await onPick(next);
54818
- };
54819
- const Clear = clearGlyph ?? fallbackGlyph;
54820
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
54821
- className: "relative flex shrink-0 items-center gap-2",
54822
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
54823
- type: "button",
54824
- disabled,
54825
- onClick: () => setOpen((o) => !o),
54826
- "aria-expanded": open,
54827
- "aria-label": labels.trigger,
54828
- title: labels.trigger,
54829
- className: "rounded border border-border p-1 text-foreground-muted hover:bg-surface-hover disabled:opacity-50",
54830
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Current, { className: "h-4 w-4" })
54831
- }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
54832
- type: "button",
54833
- "aria-label": labels.close,
54834
- onClick: () => setOpen(false),
54835
- className: "fixed inset-0 z-10 cursor-default"
54836
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
54837
- role: "group",
54838
- "aria-label": labels.panel,
54839
- className: "absolute left-0 top-full z-20 mt-1 max-h-80 w-72 overflow-y-auto rounded-md border border-border bg-surface p-2 shadow-lg",
54840
- children: [
54841
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
54842
- type: "search",
54843
- value: query,
54844
- autoFocus: true,
54845
- onChange: (e) => setQuery(e.target.value),
54846
- placeholder: labels.searchPlaceholder,
54847
- "aria-label": labels.searchPlaceholder,
54848
- className: "mb-2 w-full rounded border border-border bg-surface-raised px-2 py-1 text-xs text-foreground placeholder:text-foreground-subtle"
54849
- }),
54850
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
54851
- type: "button",
54852
- onClick: () => void choose(null),
54853
- "aria-pressed": value === null,
54854
- className: "mb-2 flex w-full items-center gap-1.5 rounded px-1.5 py-1 text-[11px] text-foreground-subtle hover:bg-surface-hover",
54855
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Clear, { className: "h-3.5 w-3.5" }), labels.clear]
54856
- }),
54857
- sections.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
54858
- className: "px-1.5 py-2 text-[11px] text-foreground-subtle",
54859
- children: labels.noMatches
54860
- }) : sections.map(({ group, keys }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
54861
- className: "mb-2 last:mb-0",
54862
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
54863
- className: "px-1.5 pb-1 text-[10px] uppercase tracking-wide text-foreground-subtle",
54864
- children: labels.groups[group]
54865
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
54866
- className: "grid grid-cols-7 gap-1",
54867
- children: keys.map((key) => {
54868
- const Glyph = resolveGlyph(key);
54869
- if (Glyph === void 0) return null;
54870
- const selected = value === key;
54871
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
54872
- type: "button",
54873
- onClick: () => void choose(key),
54874
- "aria-pressed": selected,
54875
- "aria-label": labelFor?.(key) ?? key,
54876
- title: labelFor?.(key) ?? key,
54877
- className: selected ? "flex items-center justify-center rounded border border-primary bg-primary/10 p-1.5 text-foreground" : "flex items-center justify-center rounded border border-transparent p-1.5 text-foreground-muted hover:bg-surface-hover",
54878
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Glyph, { className: "h-4 w-4" })
54879
- }, key);
54880
- })
54881
- })]
54882
- }, group))
54883
- ]
54884
- })] })]
54885
- });
54886
- }
54887
- //#endregion
54888
55085
  //#region src/composites/device-selector-picker.tsx
54889
55086
  /**
54890
55087
  * <DeviceSelectorPicker> — the operator-facing editor for a v3
@@ -61085,6 +61282,7 @@ exports.CoverPanel = CoverPanel;
61085
61282
  exports.CustomFieldRenderersProvider = CustomFieldRenderersProvider;
61086
61283
  exports.DEFAULT_COLOR = DEFAULT_COLOR;
61087
61284
  exports.DEVICE_COLUMNS = DEVICE_COLUMNS;
61285
+ exports.DEVICE_EXPORT_PAGE_SIZE_KEY = DEVICE_EXPORT_PAGE_SIZE_KEY;
61088
61286
  exports.DEVICE_LIST_PAGE_SIZE_KEY = DEVICE_LIST_PAGE_SIZE_KEY;
61089
61287
  exports.DEVICE_LIST_PAGE_SIZE_OPTIONS = DEVICE_LIST_PAGE_SIZE_OPTIONS;
61090
61288
  exports.DEVICE_PICKER_GROUP_STORAGE_KEY = DEVICE_PICKER_GROUP_STORAGE_KEY;
@@ -61103,6 +61301,7 @@ exports.DeviceBatchToolbar = DeviceBatchToolbar;
61103
61301
  exports.DeviceCard = DeviceCard;
61104
61302
  exports.DeviceContextProvider = DeviceContextProvider;
61105
61303
  exports.DeviceExportPanel = DeviceExportPanel;
61304
+ exports.DeviceExportSetup = DeviceExportSetup;
61106
61305
  exports.DeviceFilterBar = DeviceFilterBar;
61107
61306
  exports.DeviceGrid = DeviceGrid;
61108
61307
  exports.DeviceItem = DeviceItem;
@@ -61216,6 +61415,7 @@ exports.NotificationTargetsField = NotificationTargetsField;
61216
61415
  exports.OPTION_SELECT_SEARCH_MIN_OPTIONS = OPTION_SELECT_SEARCH_MIN_OPTIONS;
61217
61416
  exports.OfflineBadge = OfflineBadge;
61218
61417
  exports.OptionSelect = OptionSelect;
61418
+ exports.PAGE_SIZE_ALL = PAGE_SIZE_ALL;
61219
61419
  exports.PHASE_CONFIG = PHASE_CONFIG;
61220
61420
  exports.PICKER_CHIP_LIMIT = PICKER_CHIP_LIMIT;
61221
61421
  exports.PICKER_DEFAULT_GROUP_BY = PICKER_DEFAULT_GROUP_BY;
@@ -61230,6 +61430,7 @@ exports.PICKER_WINDOW = PICKER_WINDOW;
61230
61430
  exports.PRIORITY = PRIORITY;
61231
61431
  exports.PTZOverlay = PTZOverlay;
61232
61432
  exports.PageHeader = PageHeader;
61433
+ exports.PageSizeSelect = PageSizeSelect;
61233
61434
  exports.PhaseIcon = PhaseIcon;
61234
61435
  exports.PipelineBuilder = PipelineBuilder;
61235
61436
  exports.PipelineRuntimeSelector = PipelineRuntimeSelector;
@@ -61402,6 +61603,7 @@ exports.deviceTypeMetaOf = deviceTypeMetaOf;
61402
61603
  exports.devicesToOptions = devicesToOptions;
61403
61604
  exports.dockBox = dockBox;
61404
61605
  exports.draftFromQuery = draftFromQuery;
61606
+ exports.effectivePageSize = effectivePageSize;
61405
61607
  exports.emptyDevicesWidens = emptyDevicesWidens;
61406
61608
  exports.emptySessionRun = emptySessionRun;
61407
61609
  exports.ensureMfHostInit = ensureMfHostInit;
@@ -61464,6 +61666,7 @@ exports.optionSelectSearchText = optionSearchText;
61464
61666
  exports.orphanDeviceLabel = orphanDeviceLabel;
61465
61667
  exports.overrideEntityIdFromLink = overrideEntityIdFromLink;
61466
61668
  exports.pageRows = pageRows;
61669
+ exports.pageSizeLabel = pageSizeLabel;
61467
61670
  exports.parentDetailOf = parentDetailOf;
61468
61671
  exports.parseIceCandidate = parseIceCandidate;
61469
61672
  exports.parsePickerGroupBy = parsePickerGroupBy;
@@ -61564,6 +61767,7 @@ exports.useAddonsIsWorkspaceAvailable = useAddonsIsWorkspaceAvailable;
61564
61767
  exports.useAddonsList = useAddonsList;
61565
61768
  exports.useAddonsListCapabilityProviders = useAddonsListCapabilityProviders;
61566
61769
  exports.useAddonsListFrameworkPackages = useAddonsListFrameworkPackages;
61770
+ exports.useAddonsListInstallations = useAddonsListInstallations;
61567
61771
  exports.useAddonsListJobs = useAddonsListJobs;
61568
61772
  exports.useAddonsListPackages = useAddonsListPackages;
61569
61773
  exports.useAddonsListUpdates = useAddonsListUpdates;
@@ -62173,6 +62377,7 @@ exports.useOsdManagerSetSlotBinding = useOsdManagerSetSlotBinding;
62173
62377
  exports.useOsdManagerUpsertTemplate = useOsdManagerUpsertTemplate;
62174
62378
  exports.useOsdSetOverlay = useOsdSetOverlay;
62175
62379
  exports.usePTZ = usePTZ;
62380
+ exports.usePersistedPageSize = usePersistedPageSize;
62176
62381
  exports.usePetFeederCallPet = usePetFeederCallPet;
62177
62382
  exports.usePetFeederCancelFeed = usePetFeederCancelFeed;
62178
62383
  exports.usePetFeederFeed = usePetFeederFeed;
@@ -62250,12 +62455,14 @@ exports.usePipelineAnalyticsResumeForStorageMigration = usePipelineAnalyticsResu
62250
62455
  exports.usePipelineAnalyticsSaveRetrainAnnotations = usePipelineAnalyticsSaveRetrainAnnotations;
62251
62456
  exports.usePipelineAnalyticsSearchObjectEvents = usePipelineAnalyticsSearchObjectEvents;
62252
62457
  exports.usePipelineAnalyticsSelectRetrainFrames = usePipelineAnalyticsSelectRetrainFrames;
62458
+ exports.usePipelineAnalyticsSetRetrainFrameComplete = usePipelineAnalyticsSetRetrainFrameComplete;
62253
62459
  exports.usePipelineAnalyticsSetTrackFlags = usePipelineAnalyticsSetTrackFlags;
62254
62460
  exports.usePipelineAnalyticsSetTrackPurity = usePipelineAnalyticsSetTrackPurity;
62255
62461
  exports.usePipelineAnalyticsSetTrackRelation = usePipelineAnalyticsSetTrackRelation;
62256
62462
  exports.usePipelineAnalyticsStartStorageMigrationMove = usePipelineAnalyticsStartStorageMigrationMove;
62257
62463
  exports.usePipelineAnalyticsWipeAllAnalytics = usePipelineAnalyticsWipeAllAnalytics;
62258
62464
  exports.usePipelineAnalyticsWipeObjectEmbeddings = usePipelineAnalyticsWipeObjectEmbeddings;
62465
+ exports.usePipelineAnalyticsWriteRetrainArchive = usePipelineAnalyticsWriteRetrainArchive;
62259
62466
  exports.usePipelineExecutorCacheFrameInPool = usePipelineExecutorCacheFrameInPool;
62260
62467
  exports.usePipelineExecutorClearDeviceOverrides = usePipelineExecutorClearDeviceOverrides;
62261
62468
  exports.usePipelineExecutorClearModelBoundDeviceOverrides = usePipelineExecutorClearModelBoundDeviceOverrides;
@@ -62637,8 +62844,11 @@ exports.useUserManagementCreateScopedToken = useUserManagementCreateScopedToken;
62637
62844
  exports.useUserManagementCreateUser = useUserManagementCreateUser;
62638
62845
  exports.useUserManagementDeleteUser = useUserManagementDeleteUser;
62639
62846
  exports.useUserManagementDisableTotp = useUserManagementDisableTotp;
62847
+ exports.useUserManagementFindUserByExternalIdentity = useUserManagementFindUserByExternalIdentity;
62640
62848
  exports.useUserManagementGetTotpStatus = useUserManagementGetTotpStatus;
62849
+ exports.useUserManagementLinkExternalIdentity = useUserManagementLinkExternalIdentity;
62641
62850
  exports.useUserManagementListApiKeys = useUserManagementListApiKeys;
62851
+ exports.useUserManagementListExternalIdentities = useUserManagementListExternalIdentities;
62642
62852
  exports.useUserManagementListOauthSessions = useUserManagementListOauthSessions;
62643
62853
  exports.useUserManagementListScopedTokens = useUserManagementListScopedTokens;
62644
62854
  exports.useUserManagementListUsers = useUserManagementListUsers;
@@ -62652,6 +62862,7 @@ exports.useUserManagementRevokeOauthSession = useUserManagementRevokeOauthSessio
62652
62862
  exports.useUserManagementRevokeScopedToken = useUserManagementRevokeScopedToken;
62653
62863
  exports.useUserManagementSetUserScopes = useUserManagementSetUserScopes;
62654
62864
  exports.useUserManagementSetupTotp = useUserManagementSetupTotp;
62865
+ exports.useUserManagementUnlinkExternalIdentity = useUserManagementUnlinkExternalIdentity;
62655
62866
  exports.useUserManagementUpdateUser = useUserManagementUpdateUser;
62656
62867
  exports.useUserManagementValidateApiKey = useUserManagementValidateApiKey;
62657
62868
  exports.useUserManagementValidateCredentials = useUserManagementValidateCredentials;