@camstack/ui-library 1.2.248 → 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,901 @@ 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.listInstallations.useQuery`. */
6509
- var useAddonsListInstallations = trpc.addons.listInstallations.useQuery;
6510
- /** Generated alias around `trpc.addons.reloadPackages.useMutation`. */
6511
- var useAddonsReloadPackages = trpc.addons.reloadPackages.useMutation;
6512
- /** Generated alias around `trpc.addons.searchAvailable.useQuery`. */
6513
- var useAddonsSearchAvailable = trpc.addons.searchAvailable.useQuery;
6514
- /** Generated alias around `trpc.addons.listUpdates.useQuery`. */
6515
- var useAddonsListUpdates = trpc.addons.listUpdates.useQuery;
6516
- /** Generated alias around `trpc.addons.updatePackage.useMutation`. */
6517
- var useAddonsUpdatePackage = trpc.addons.updatePackage.useMutation;
6518
- /** Generated alias around `trpc.addons.rollbackPackage.useMutation`. */
6519
- var useAddonsRollbackPackage = trpc.addons.rollbackPackage.useMutation;
6520
- /** Generated alias around `trpc.addons.forceRefresh.useMutation`. */
6521
- var useAddonsForceRefresh = trpc.addons.forceRefresh.useMutation;
6522
- /** Generated alias around `trpc.addons.restartServer.useMutation`. */
6523
- var useAddonsRestartServer = trpc.addons.restartServer.useMutation;
6524
- /** Generated alias around `trpc.addons.getLastRestart.useQuery`. */
6525
- var useAddonsGetLastRestart = trpc.addons.getLastRestart.useQuery;
6526
- /** Generated alias around `trpc.addons.listFrameworkPackages.useQuery`. */
6527
- var useAddonsListFrameworkPackages = trpc.addons.listFrameworkPackages.useQuery;
6528
- /** Generated alias around `trpc.addons.listCapabilityProviders.useQuery`. */
6529
- var useAddonsListCapabilityProviders = trpc.addons.listCapabilityProviders.useQuery;
6530
- /** Generated alias around `trpc.addons.setCapabilityProviderEnabled.useMutation`. */
6531
- var useAddonsSetCapabilityProviderEnabled = trpc.addons.setCapabilityProviderEnabled.useMutation;
6532
- /** Generated alias around `trpc.addons.getVersions.useQuery`. */
6533
- var useAddonsGetVersions = trpc.addons.getVersions.useQuery;
6534
- /** Generated alias around `trpc.addons.restartAddon.useMutation`. */
6535
- var useAddonsRestartAddon = trpc.addons.restartAddon.useMutation;
6536
- /** Generated alias around `trpc.addons.retryLoad.useMutation`. */
6537
- var useAddonsRetryLoad = trpc.addons.retryLoad.useMutation;
6538
- /** Generated alias around `trpc.addons.getAutoUpdateSettings.useQuery`. */
6539
- var useAddonsGetAutoUpdateSettings = trpc.addons.getAutoUpdateSettings.useQuery;
6540
- /** Generated alias around `trpc.addons.setAutoUpdateSettings.useMutation`. */
6541
- var useAddonsSetAutoUpdateSettings = trpc.addons.setAutoUpdateSettings.useMutation;
6542
- /** Generated alias around `trpc.addons.getAddonAutoUpdate.useQuery`. */
6543
- var useAddonsGetAddonAutoUpdate = trpc.addons.getAddonAutoUpdate.useQuery;
6544
- /** Generated alias around `trpc.addons.setAddonAutoUpdate.useMutation`. */
6545
- var useAddonsSetAddonAutoUpdate = trpc.addons.setAddonAutoUpdate.useMutation;
6546
- /** Generated alias around `trpc.addons.applyAutoUpdateToAll.useMutation`. */
6547
- var useAddonsApplyAutoUpdateToAll = trpc.addons.applyAutoUpdateToAll.useMutation;
6548
- /** Generated alias around `trpc.addons.custom.useMutation`. */
6549
- var useAddonsCustom = trpc.addons.custom.useMutation;
6550
- /** Generated alias around `trpc.addons.startJob.useMutation`. */
6551
- var useAddonsStartJob = trpc.addons.startJob.useMutation;
6552
- /** Generated alias around `trpc.addons.getJob.useQuery`. */
6553
- var useAddonsGetJob = trpc.addons.getJob.useQuery;
6554
- /** Generated alias around `trpc.addons.listJobs.useQuery`. */
6555
- var useAddonsListJobs = trpc.addons.listJobs.useQuery;
6556
- /** Generated alias around `trpc.addons.cancelJob.useMutation`. */
6557
- var useAddonsCancelJob = trpc.addons.cancelJob.useMutation;
6558
- /** Generated alias around `trpc.addons.onAddonLogs.useSubscription`. */
6559
- var useAddonsOnAddonLogs = trpc.addons.onAddonLogs.useSubscription;
6560
- /** Generated alias around `trpc.addonSettings.getGlobalSettings.useQuery`. */
6561
- var useAddonSettingsGetGlobalSettings = trpc.addonSettings.getGlobalSettings.useQuery;
6562
- /** Generated alias around `trpc.addonSettings.updateGlobalSettings.useMutation`. */
6563
- var useAddonSettingsUpdateGlobalSettings = trpc.addonSettings.updateGlobalSettings.useMutation;
6564
- /** Generated alias around `trpc.addonSettings.getIntegrationSettings.useQuery`. */
6565
- var useAddonSettingsGetIntegrationSettings = trpc.addonSettings.getIntegrationSettings.useQuery;
6566
- /** Generated alias around `trpc.addonSettings.getDeviceSettings.useQuery`. */
6567
- var useAddonSettingsGetDeviceSettings = trpc.addonSettings.getDeviceSettings.useQuery;
6568
- /** Generated alias around `trpc.addonSettings.updateDeviceSettings.useMutation`. */
6569
- var useAddonSettingsUpdateDeviceSettings = trpc.addonSettings.updateDeviceSettings.useMutation;
6570
- /** Generated alias around `trpc.addonWidgets.listWidgets.useQuery`. */
6571
- var useAddonWidgetsListWidgets = trpc.addonWidgets.listWidgets.useQuery;
6572
- /** Generated alias around `trpc.airQualitySensor.getStatus.useQuery`. */
6573
- var useAirQualitySensorGetStatus = trpc.airQualitySensor.getStatus.useQuery;
6574
- /** Generated alias around `trpc.alarmPanel.arm.useMutation`. */
6575
- var useAlarmPanelArm = trpc.alarmPanel.arm.useMutation;
6576
- /** Generated alias around `trpc.alarmPanel.disarm.useMutation`. */
6577
- var useAlarmPanelDisarm = trpc.alarmPanel.disarm.useMutation;
6578
- /** Generated alias around `trpc.alarmPanel.trigger.useMutation`. */
6579
- var useAlarmPanelTrigger = trpc.alarmPanel.trigger.useMutation;
6580
- /** Generated alias around `trpc.alarmPanel.getStatus.useQuery`. */
6581
- var useAlarmPanelGetStatus = trpc.alarmPanel.getStatus.useQuery;
6582
- /** Generated alias around `trpc.alerts.emit.useMutation`. */
6583
- var useAlertsEmit = trpc.alerts.emit.useMutation;
6584
- /** Generated alias around `trpc.alerts.update.useMutation`. */
6585
- var useAlertsUpdate = trpc.alerts.update.useMutation;
6586
- /** Generated alias around `trpc.alerts.list.useQuery`. */
6587
- var useAlertsList = trpc.alerts.list.useQuery;
6588
- /** Generated alias around `trpc.alerts.getUnreadCount.useQuery`. */
6589
- var useAlertsGetUnreadCount = trpc.alerts.getUnreadCount.useQuery;
6590
- /** Generated alias around `trpc.alerts.markRead.useMutation`. */
6591
- var useAlertsMarkRead = trpc.alerts.markRead.useMutation;
6592
- /** Generated alias around `trpc.alerts.markAllRead.useMutation`. */
6593
- var useAlertsMarkAllRead = trpc.alerts.markAllRead.useMutation;
6594
- /** Generated alias around `trpc.alerts.dismiss.useMutation`. */
6595
- var useAlertsDismiss = trpc.alerts.dismiss.useMutation;
6596
- /** Generated alias around `trpc.ambientLightSensor.getStatus.useQuery`. */
6597
- var useAmbientLightSensorGetStatus = trpc.ambientLightSensor.getStatus.useQuery;
6598
- /** Generated alias around `trpc.audioAnalysis.resolveDeviceSettings.useQuery`. */
6599
- var useAudioAnalysisResolveDeviceSettings = trpc.audioAnalysis.resolveDeviceSettings.useQuery;
6600
- /** Generated alias around `trpc.audioAnalysis.getDeviceSettingsContribution.useQuery`. */
6601
- var useAudioAnalysisGetDeviceSettingsContribution = trpc.audioAnalysis.getDeviceSettingsContribution.useQuery;
6602
- /** Generated alias around `trpc.audioAnalysis.getDeviceLiveContribution.useQuery`. */
6603
- var useAudioAnalysisGetDeviceLiveContribution = trpc.audioAnalysis.getDeviceLiveContribution.useQuery;
6604
- /** Generated alias around `trpc.audioAnalysis.applyDeviceSettingsPatch.useMutation`. */
6605
- var useAudioAnalysisApplyDeviceSettingsPatch = trpc.audioAnalysis.applyDeviceSettingsPatch.useMutation;
6606
- /** Generated alias around `trpc.audioAnalyzer.analyseChunk.useMutation`. */
6607
- var useAudioAnalyzerAnalyseChunk = trpc.audioAnalyzer.analyseChunk.useMutation;
6608
- /** Generated alias around `trpc.audioAnalyzer.classify.useQuery`. */
6609
- var useAudioAnalyzerClassify = trpc.audioAnalyzer.classify.useQuery;
6610
- /** Generated alias around `trpc.audioAnalyzer.attachDevice.useMutation`. */
6611
- var useAudioAnalyzerAttachDevice = trpc.audioAnalyzer.attachDevice.useMutation;
6612
- /** Generated alias around `trpc.audioAnalyzer.detachDevice.useMutation`. */
6613
- var useAudioAnalyzerDetachDevice = trpc.audioAnalyzer.detachDevice.useMutation;
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;
6614
7364
  /** Generated alias around `trpc.audioAnalyzer.isReady.useQuery`. */
6615
7365
  var useAudioAnalyzerIsReady = trpc.audioAnalyzer.isReady.useQuery;
6616
7366
  /** Generated alias around `trpc.audioAnalyzer.dispose.useMutation`. */
@@ -7803,6 +8553,10 @@ var usePipelineAnalyticsCompleteRetrainTrack = trpc.pipelineAnalytics.completeRe
7803
8553
  var usePipelineAnalyticsRestageRetrainTrack = trpc.pipelineAnalytics.restageRetrainTrack.useMutation;
7804
8554
  /** Generated alias around `trpc.pipelineAnalytics.getRetrainExportUrl.useQuery`. */
7805
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;
7806
8560
  /** Generated alias around `trpc.pipelineAnalytics.getEventMedia.useQuery`. */
7807
8561
  var usePipelineAnalyticsGetEventMedia = trpc.pipelineAnalytics.getEventMedia.useQuery;
7808
8562
  /** Generated alias around `trpc.pipelineAnalytics.getTrackMedia.useQuery`. */
@@ -8591,6 +9345,14 @@ var useUserManagementOauthExchangeCode = trpc.userManagement.oauthExchangeCode.u
8591
9345
  var useUserManagementOauthRefresh = trpc.userManagement.oauthRefresh.useMutation;
8592
9346
  /** Generated alias around `trpc.userManagement.oauthVerifyAccessToken.useQuery`. */
8593
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;
8594
9356
  /** Generated alias around `trpc.userManagement.listOauthSessions.useQuery`. */
8595
9357
  var useUserManagementListOauthSessions = trpc.userManagement.listOauthSessions.useQuery;
8596
9358
  /** Generated alias around `trpc.userManagement.revokeOauthSession.useMutation`. */
@@ -27166,6 +27928,23 @@ function ColorField({ field, value, onChange, disabled, translationFn }) {
27166
27928
  })]
27167
27929
  });
27168
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
+ }
27169
27948
  function ProbeField({ field, value, onChange, disabled, translationFn, onTestField, onAction, externalProbe }) {
27170
27949
  const [localStatus, setLocalStatus] = (0, react$1.useState)("idle");
27171
27950
  const [localResult, setLocalResult] = (0, react$1.useState)(null);
@@ -27800,8 +28579,16 @@ function AddonActionButtonField({ field, values, disabled, onAction }) {
27800
28579
  const [loading, setLoading] = (0, react$1.useState)(false);
27801
28580
  const [status, setStatus] = (0, react$1.useState)(null);
27802
28581
  const confirm = useConfirm();
28582
+ const prompt = usePrompt();
27803
28583
  const deviceId = useDeviceId();
27804
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;
27805
28592
  if (field.confirmMessage) {
27806
28593
  if (!await confirm({
27807
28594
  title: field.label,
@@ -27812,6 +28599,7 @@ function AddonActionButtonField({ field, values, disabled, onAction }) {
27812
28599
  }
27813
28600
  if (!onAction) return;
27814
28601
  const params = resolveActionParams(field, values);
28602
+ if (field.prompt && asked !== null) params[field.prompt.param] = asked;
27815
28603
  if (field.includeDeviceId === true && deviceId !== null && deviceId !== void 0) params["deviceId"] = deviceId;
27816
28604
  setLoading(true);
27817
28605
  setStatus(null);
@@ -28272,6 +29060,13 @@ function FormField({ field, values, onChange, disabled, translationFn, onTestFie
28272
29060
  disabled,
28273
29061
  translationFn
28274
29062
  });
29063
+ case "icon": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconField, {
29064
+ field,
29065
+ value,
29066
+ onChange: handleChange,
29067
+ disabled,
29068
+ translationFn
29069
+ });
28275
29070
  case "node-select": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NodeSelectField, {
28276
29071
  field,
28277
29072
  value,
@@ -49746,7 +50541,7 @@ var CONTROL_FILLS = {
49746
50541
  [_camstack_types.DeviceType.Image]: false,
49747
50542
  [_camstack_types.DeviceType.PetFeeder]: false
49748
50543
  };
49749
- var DEVICE_TYPE_VALUES$1 = new Set(Object.values(_camstack_types.DeviceType));
50544
+ var DEVICE_TYPE_VALUES = new Set(Object.values(_camstack_types.DeviceType));
49750
50545
  /**
49751
50546
  * Runtime-string-safe lookup: a device row's `type` crosses the wire as a plain
49752
50547
  * string. The type→control-kind DECISION is single-sourced in `@camstack/types`
@@ -49772,7 +50567,7 @@ function resolveDeviceControl(type) {
49772
50567
  */
49773
50568
  function resolveControlAlign(type, placement) {
49774
50569
  if (placement === "table") return "fit";
49775
- 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;
49776
50571
  }
49777
50572
  //#endregion
49778
50573
  //#region src/lib/format-last-seen.ts
@@ -50100,571 +50895,6 @@ function DeviceBatchActionsBar(props) {
50100
50895
  })] });
50101
50896
  }
50102
50897
  //#endregion
50103
- //#region src/composites/device-meta.ts
50104
- var DEVICE_TYPE_META = {
50105
- [_camstack_types.DeviceType.Camera]: {
50106
- icon: Camera,
50107
- label: "Camera"
50108
- },
50109
- [_camstack_types.DeviceType.Hub]: {
50110
- icon: Network,
50111
- label: "Hub"
50112
- },
50113
- [_camstack_types.DeviceType.Light]: {
50114
- icon: Lightbulb,
50115
- label: "Light"
50116
- },
50117
- [_camstack_types.DeviceType.Siren]: {
50118
- icon: Siren,
50119
- label: "Siren"
50120
- },
50121
- [_camstack_types.DeviceType.Switch]: {
50122
- icon: ToggleLeft,
50123
- label: "Switch"
50124
- },
50125
- [_camstack_types.DeviceType.Sensor]: {
50126
- icon: Activity,
50127
- label: "Sensor"
50128
- },
50129
- [_camstack_types.DeviceType.Thermostat]: {
50130
- icon: Thermometer,
50131
- label: "Thermostat"
50132
- },
50133
- [_camstack_types.DeviceType.Climate]: {
50134
- icon: AirVent,
50135
- label: "Air conditioner"
50136
- },
50137
- [_camstack_types.DeviceType.Button]: {
50138
- icon: CircleDot,
50139
- label: "Button"
50140
- },
50141
- [_camstack_types.DeviceType.EventEmitter]: {
50142
- icon: Zap,
50143
- label: "Event Emitter"
50144
- },
50145
- [_camstack_types.DeviceType.Update]: {
50146
- icon: RefreshCw,
50147
- label: "Update"
50148
- },
50149
- [_camstack_types.DeviceType.Generic]: {
50150
- icon: Box,
50151
- label: "Generic"
50152
- },
50153
- [_camstack_types.DeviceType.Notifier]: {
50154
- icon: Bell,
50155
- label: "Notifier"
50156
- },
50157
- [_camstack_types.DeviceType.Script]: {
50158
- icon: ScrollText,
50159
- label: "Script"
50160
- },
50161
- [_camstack_types.DeviceType.Automation]: {
50162
- icon: Workflow,
50163
- label: "Automation"
50164
- },
50165
- [_camstack_types.DeviceType.Lock]: {
50166
- icon: Lock,
50167
- label: "Lock"
50168
- },
50169
- [_camstack_types.DeviceType.Cover]: {
50170
- icon: Blinds,
50171
- label: "Cover"
50172
- },
50173
- [_camstack_types.DeviceType.Valve]: {
50174
- icon: Pipette,
50175
- label: "Valve"
50176
- },
50177
- [_camstack_types.DeviceType.Humidifier]: {
50178
- icon: Waves,
50179
- label: "Humidifier"
50180
- },
50181
- [_camstack_types.DeviceType.WaterHeater]: {
50182
- icon: Bath,
50183
- label: "Water heater"
50184
- },
50185
- [_camstack_types.DeviceType.Fan]: {
50186
- icon: Fan,
50187
- label: "Fan"
50188
- },
50189
- [_camstack_types.DeviceType.MediaPlayer]: {
50190
- icon: MonitorPlay,
50191
- label: "Media player"
50192
- },
50193
- [_camstack_types.DeviceType.AlarmPanel]: {
50194
- icon: ShieldAlert,
50195
- label: "Alarm panel"
50196
- },
50197
- [_camstack_types.DeviceType.Control]: {
50198
- icon: SlidersHorizontal,
50199
- label: "Control"
50200
- },
50201
- [_camstack_types.DeviceType.Presence]: {
50202
- icon: MapPin,
50203
- label: "Presence"
50204
- },
50205
- [_camstack_types.DeviceType.Weather]: {
50206
- icon: CloudSun,
50207
- label: "Weather"
50208
- },
50209
- [_camstack_types.DeviceType.Vacuum]: {
50210
- icon: Bot,
50211
- label: "Vacuum"
50212
- },
50213
- [_camstack_types.DeviceType.LawnMower]: {
50214
- icon: Tractor,
50215
- label: "Lawn mower"
50216
- },
50217
- [_camstack_types.DeviceType.Container]: {
50218
- icon: Box,
50219
- label: "Container"
50220
- },
50221
- [_camstack_types.DeviceType.Image]: {
50222
- icon: Image,
50223
- label: "Image"
50224
- },
50225
- [_camstack_types.DeviceType.PetFeeder]: {
50226
- icon: Bone,
50227
- label: "Pet feeder"
50228
- }
50229
- };
50230
- var DEVICE_ROLE_META = {
50231
- [_camstack_types.DeviceRole.Siren]: {
50232
- icon: Siren,
50233
- label: "Siren"
50234
- },
50235
- [_camstack_types.DeviceRole.Floodlight]: {
50236
- icon: Lightbulb,
50237
- label: "Floodlight"
50238
- },
50239
- [_camstack_types.DeviceRole.Spotlight]: {
50240
- icon: Flashlight,
50241
- label: "Spotlight"
50242
- },
50243
- [_camstack_types.DeviceRole.PirSensor]: {
50244
- icon: Radar,
50245
- label: "PIR sensor"
50246
- },
50247
- [_camstack_types.DeviceRole.Chime]: {
50248
- icon: Bell,
50249
- label: "Chime"
50250
- },
50251
- [_camstack_types.DeviceRole.Autotrack]: {
50252
- icon: Crosshair,
50253
- label: "Auto-track"
50254
- },
50255
- [_camstack_types.DeviceRole.Nightvision]: {
50256
- icon: Moon,
50257
- label: "Night vision"
50258
- },
50259
- [_camstack_types.DeviceRole.PrivacyMask]: {
50260
- icon: EyeOff,
50261
- label: "Privacy mask"
50262
- },
50263
- [_camstack_types.DeviceRole.Doorbell]: {
50264
- icon: DoorClosed,
50265
- label: "Doorbell"
50266
- },
50267
- [_camstack_types.DeviceRole.BinaryHelper]: {
50268
- icon: ToggleLeft,
50269
- label: "Binary helper"
50270
- },
50271
- [_camstack_types.DeviceRole.MotionSensor]: {
50272
- icon: Footprints,
50273
- label: "Motion sensor"
50274
- },
50275
- [_camstack_types.DeviceRole.ContactSensor]: {
50276
- icon: DoorOpen,
50277
- label: "Contact sensor"
50278
- },
50279
- [_camstack_types.DeviceRole.LeakSensor]: {
50280
- icon: Droplet,
50281
- label: "Leak sensor"
50282
- },
50283
- [_camstack_types.DeviceRole.SmokeSensor]: {
50284
- icon: AlarmSmoke,
50285
- label: "Smoke sensor"
50286
- },
50287
- [_camstack_types.DeviceRole.COSensor]: {
50288
- icon: Wind,
50289
- label: "CO sensor"
50290
- },
50291
- [_camstack_types.DeviceRole.GasSensor]: {
50292
- icon: Flame,
50293
- label: "Gas sensor"
50294
- },
50295
- [_camstack_types.DeviceRole.TamperSensor]: {
50296
- icon: ShieldAlert,
50297
- label: "Tamper sensor"
50298
- },
50299
- [_camstack_types.DeviceRole.VibrationSensor]: {
50300
- icon: Vibrate,
50301
- label: "Vibration sensor"
50302
- },
50303
- [_camstack_types.DeviceRole.ConnectivitySensor]: {
50304
- icon: Wifi,
50305
- label: "Connectivity"
50306
- },
50307
- [_camstack_types.DeviceRole.SoundSensor]: {
50308
- icon: Volume2,
50309
- label: "Sound sensor"
50310
- },
50311
- [_camstack_types.DeviceRole.BinarySensor]: {
50312
- icon: ToggleLeft,
50313
- label: "Binary sensor"
50314
- },
50315
- [_camstack_types.DeviceRole.TemperatureSensor]: {
50316
- icon: Thermometer,
50317
- label: "Temperature"
50318
- },
50319
- [_camstack_types.DeviceRole.HumiditySensor]: {
50320
- icon: Droplets,
50321
- label: "Humidity"
50322
- },
50323
- [_camstack_types.DeviceRole.AmbientLightSensor]: {
50324
- icon: Sun,
50325
- label: "Ambient light"
50326
- },
50327
- [_camstack_types.DeviceRole.PressureSensor]: {
50328
- icon: Gauge,
50329
- label: "Pressure"
50330
- },
50331
- [_camstack_types.DeviceRole.WindSensor]: {
50332
- icon: Wind,
50333
- label: "Wind"
50334
- },
50335
- [_camstack_types.DeviceRole.RainSensor]: {
50336
- icon: CloudRain,
50337
- label: "Rain"
50338
- },
50339
- [_camstack_types.DeviceRole.UvSensor]: {
50340
- icon: SunMedium,
50341
- label: "UV index"
50342
- },
50343
- [_camstack_types.DeviceRole.SolarRadiationSensor]: {
50344
- icon: Sunrise,
50345
- label: "Solar radiation"
50346
- },
50347
- [_camstack_types.DeviceRole.SoilMoistureSensor]: {
50348
- icon: Sprout,
50349
- label: "Soil moisture"
50350
- },
50351
- [_camstack_types.DeviceRole.PowerSensor]: {
50352
- icon: Zap,
50353
- label: "Power"
50354
- },
50355
- [_camstack_types.DeviceRole.EnergySensor]: {
50356
- icon: Zap,
50357
- label: "Energy"
50358
- },
50359
- [_camstack_types.DeviceRole.VoltageSensor]: {
50360
- icon: Zap,
50361
- label: "Voltage"
50362
- },
50363
- [_camstack_types.DeviceRole.CurrentSensor]: {
50364
- icon: Zap,
50365
- label: "Current"
50366
- },
50367
- [_camstack_types.DeviceRole.AirQualitySensor]: {
50368
- icon: Wind,
50369
- label: "Air quality"
50370
- },
50371
- [_camstack_types.DeviceRole.BatterySensor]: {
50372
- icon: Gauge,
50373
- label: "Battery"
50374
- },
50375
- [_camstack_types.DeviceRole.NumericSensor]: {
50376
- icon: Hash,
50377
- label: "Numeric sensor"
50378
- },
50379
- [_camstack_types.DeviceRole.EnumSensor]: {
50380
- icon: List,
50381
- label: "Enum sensor"
50382
- },
50383
- [_camstack_types.DeviceRole.DateTimeSensor]: {
50384
- icon: Clock,
50385
- label: "Date / time"
50386
- },
50387
- [_camstack_types.DeviceRole.GenericSensor]: {
50388
- icon: Gauge,
50389
- label: "Sensor"
50390
- },
50391
- [_camstack_types.DeviceRole.NumericControl]: {
50392
- icon: SlidersHorizontal,
50393
- label: "Numeric control"
50394
- },
50395
- [_camstack_types.DeviceRole.SelectControl]: {
50396
- icon: ListChecks,
50397
- label: "Select control"
50398
- },
50399
- [_camstack_types.DeviceRole.TextControl]: {
50400
- icon: Type,
50401
- label: "Text control"
50402
- },
50403
- [_camstack_types.DeviceRole.DateTimeControl]: {
50404
- icon: Clock,
50405
- label: "Date / time control"
50406
- },
50407
- [_camstack_types.DeviceRole.MobilePushNotifier]: {
50408
- icon: Smartphone,
50409
- label: "Mobile push"
50410
- },
50411
- [_camstack_types.DeviceRole.MessagingNotifier]: {
50412
- icon: MessageSquare,
50413
- label: "Messaging"
50414
- },
50415
- [_camstack_types.DeviceRole.EmailNotifier]: {
50416
- icon: Mail,
50417
- label: "Email"
50418
- },
50419
- [_camstack_types.DeviceRole.GenericNotifier]: {
50420
- icon: Bell,
50421
- label: "Notifier"
50422
- }
50423
- };
50424
- var DEVICE_TYPE_VALUES = new Set(Object.values(_camstack_types.DeviceType));
50425
- var DEVICE_ROLE_VALUES = new Set(Object.values(_camstack_types.DeviceRole));
50426
- function isDeviceType(value) {
50427
- return DEVICE_TYPE_VALUES.has(value);
50428
- }
50429
- function isDeviceRole(value) {
50430
- return DEVICE_ROLE_VALUES.has(value);
50431
- }
50432
- /** Meta for a known `DeviceType`. */
50433
- function deviceTypeMeta(type) {
50434
- return DEVICE_TYPE_META[type];
50435
- }
50436
- /** Meta for a known `DeviceRole`. */
50437
- function deviceRoleMeta(role) {
50438
- return DEVICE_ROLE_META[role];
50439
- }
50440
- var FALLBACK_DEVICE_TYPE_META = DEVICE_TYPE_META[_camstack_types.DeviceType.Generic];
50441
- var FALLBACK_DEVICE_ROLE_META = DEVICE_ROLE_META[_camstack_types.DeviceRole.GenericSensor];
50442
- /**
50443
- * Runtime-string-safe lookup: device data crosses process / network boundaries
50444
- * as plain strings, so an unrecognised value falls back to the `Generic` meta
50445
- * instead of throwing.
50446
- *
50447
- * This also guards mixed-version module-federation loads where the host
50448
- * `@camstack/types` enum has a new member before the loaded ui-library bundle
50449
- * has the matching `DEVICE_TYPE_META` entry.
50450
- */
50451
- function deviceTypeMetaOf(type) {
50452
- if (!isDeviceType(type)) return FALLBACK_DEVICE_TYPE_META;
50453
- return DEVICE_TYPE_META[type] ?? FALLBACK_DEVICE_TYPE_META;
50454
- }
50455
- /** Runtime-string-safe role lookup; unknown roles fall back to `GenericSensor`. */
50456
- function deviceRoleMetaOf(role) {
50457
- if (!isDeviceRole(role)) return FALLBACK_DEVICE_ROLE_META;
50458
- return DEVICE_ROLE_META[role] ?? FALLBACK_DEVICE_ROLE_META;
50459
- }
50460
- /**
50461
- * Icons an operator may pick for a per-device `display.icon` override (and for
50462
- * a per-role default), keyed by a stable kebab-case name. Named imports only —
50463
- * no dynamic indexing into lucide, so the bundle carries exactly this set.
50464
- * Unknown keys resolve to `undefined` (caller falls through to the next level).
50465
- *
50466
- * The vocabulary is the one the VIEWER draws from too (its own icon module,
50467
- * keyed by these same strings): a key added here that the phone cannot draw
50468
- * falls through to the next resolution level there, which reads as "my change
50469
- * did nothing". So the KEY is the contract — a stable kebab-case string, never
50470
- * a glyph identity — and `device-meta.test.ts` pins that every key resolves
50471
- * and that the set never loses a key an operator may already have persisted.
50472
- *
50473
- * Grouped by what an operator is naming: a device, a light or appliance, a
50474
- * sensor or the weather it reads, a room, or what moves through it. `gauge2`
50475
- * is a legacy alias kept for rows that persisted it.
50476
- */
50477
- var DISPLAY_ICON_REGISTRY = {
50478
- camera: Camera,
50479
- network: Network,
50480
- "toggle-left": ToggleLeft,
50481
- "toggle-right": ToggleRight,
50482
- "circle-dot": CircleDot,
50483
- "refresh-cw": RefreshCw,
50484
- box: Box,
50485
- "scroll-text": ScrollText,
50486
- workflow: Workflow,
50487
- lock: Lock,
50488
- "lock-open": LockOpen,
50489
- blinds: Blinds,
50490
- "air-vent": AirVent,
50491
- "monitor-play": MonitorPlay,
50492
- "sliders-horizontal": SlidersHorizontal,
50493
- "map-pin": MapPin,
50494
- bot: Bot,
50495
- tractor: Tractor,
50496
- image: Image,
50497
- bone: Bone,
50498
- "door-closed": DoorClosed,
50499
- "door-open": DoorOpen,
50500
- crosshair: Crosshair,
50501
- eye: Eye,
50502
- "eye-off": EyeOff,
50503
- smartphone: Smartphone,
50504
- phone: Phone,
50505
- tablet: Tablet,
50506
- laptop: Laptop,
50507
- monitor: Monitor,
50508
- tv: Tv,
50509
- speaker: Speaker,
50510
- printer: Printer,
50511
- projector: Projector,
50512
- "gamepad-2": Gamepad2,
50513
- radio: Radio,
50514
- router: Router,
50515
- bluetooth: Bluetooth,
50516
- cpu: Cpu,
50517
- "hard-drive": HardDrive,
50518
- server: Server,
50519
- mic: Mic,
50520
- "mic-off": MicOff,
50521
- video: Video,
50522
- "video-off": VideoOff,
50523
- music: Music,
50524
- plug: Plug,
50525
- "plug-zap": PlugZap,
50526
- power: Power,
50527
- "power-off": PowerOff,
50528
- cable: Cable,
50529
- battery: Battery,
50530
- "battery-charging": BatteryCharging,
50531
- "battery-full": BatteryFull,
50532
- "battery-low": BatteryLow,
50533
- key: Key,
50534
- "key-round": KeyRound,
50535
- mailbox: Mailbox,
50536
- package: Package,
50537
- shield: Shield,
50538
- "shield-check": ShieldCheck,
50539
- "shield-off": ShieldOff,
50540
- lightbulb: Lightbulb,
50541
- lamp: Lamp,
50542
- "lamp-ceiling": LampCeiling,
50543
- "lamp-desk": LampDesk,
50544
- "lamp-floor": LampFloor,
50545
- flashlight: Flashlight,
50546
- heater: Heater,
50547
- snowflake: Snowflake,
50548
- "thermometer-sun": ThermometerSun,
50549
- "thermometer-snowflake": ThermometerSnowflake,
50550
- refrigerator: Refrigerator,
50551
- microwave: Microwave,
50552
- "washing-machine": WashingMachine,
50553
- "cooking-pot": CookingPot,
50554
- utensils: Utensils,
50555
- thermometer: Thermometer,
50556
- droplets: Droplets,
50557
- droplet: Droplet,
50558
- gauge: Gauge,
50559
- gauge2: Gauge,
50560
- sun: Sun,
50561
- "sun-medium": SunMedium,
50562
- sunrise: Sunrise,
50563
- moon: Moon,
50564
- wind: Wind,
50565
- cloud: Cloud,
50566
- "cloud-rain": CloudRain,
50567
- "cloud-sun": CloudSun,
50568
- waves: Waves,
50569
- sprout: Sprout,
50570
- leaf: Leaf,
50571
- flower: Flower,
50572
- zap: Zap,
50573
- flame: Flame,
50574
- bell: Bell,
50575
- "bell-ring": BellRing,
50576
- "bell-off": BellOff,
50577
- siren: Siren,
50578
- radar: Radar,
50579
- activity: Activity,
50580
- hash: Hash,
50581
- clock: Clock,
50582
- timer: Timer,
50583
- calendar: Calendar,
50584
- pipette: Pipette,
50585
- fan: Fan,
50586
- bath: Bath,
50587
- footprints: Footprints,
50588
- "alarm-smoke": AlarmSmoke,
50589
- "shield-alert": ShieldAlert,
50590
- vibrate: Vibrate,
50591
- wifi: Wifi,
50592
- "wifi-off": WifiOff,
50593
- "volume-2": Volume2,
50594
- "volume-x": VolumeX,
50595
- heart: Heart,
50596
- "heart-pulse": HeartPulse,
50597
- star: Star,
50598
- sparkles: Sparkles,
50599
- house: House,
50600
- sofa: Sofa,
50601
- "bed-double": BedDouble,
50602
- bed: Bed,
50603
- warehouse: Warehouse,
50604
- trees: Trees,
50605
- "tree-pine": TreePine,
50606
- fence: Fence,
50607
- store: Store,
50608
- building: Building,
50609
- "building-2": Building2,
50610
- mountain: Mountain,
50611
- umbrella: Umbrella,
50612
- dumbbell: Dumbbell,
50613
- wrench: Wrench,
50614
- hammer: Hammer,
50615
- drill: Drill,
50616
- "waves-ladder": WavesLadder,
50617
- "circle-parking": CircleParking,
50618
- briefcase: Briefcase,
50619
- route: Route,
50620
- car: Car,
50621
- bike: Bike,
50622
- bus: Bus,
50623
- truck: Truck,
50624
- plane: Plane,
50625
- "train-front": TrainFront,
50626
- sailboat: Sailboat,
50627
- dog: Dog,
50628
- cat: Cat,
50629
- bird: Bird,
50630
- "paw-print": PawPrint,
50631
- baby: Baby,
50632
- user: User,
50633
- users: Users,
50634
- "person-standing": PersonStanding
50635
- };
50636
- /** Resolve a `display.icon` name to a lucide component, or `undefined` when the
50637
- * name is not in the curated `DISPLAY_ICON_REGISTRY`. */
50638
- function resolveDisplayIcon(name) {
50639
- return Object.hasOwn(DISPLAY_ICON_REGISTRY, name) ? DISPLAY_ICON_REGISTRY[name] : void 0;
50640
- }
50641
- /**
50642
- * EXHAUSTIVE device-type filter options — one entry per `DeviceType` member,
50643
- * derived from `DEVICE_TYPE_META` so the set is always complete and a newly
50644
- * added `DeviceType` automatically appears in every type filter (Devices page,
50645
- * adoption modal, …) without touching any consumer.
50646
- *
50647
- * The previous per-consumer logic built options from the types PRESENT in the
50648
- * list (or a hand-maintained subset), so recently-added types (vacuum, lawn
50649
- * mower, valve, humidifier, water heater, weather, image, …) were silently
50650
- * missing from the dropdown. This is the single source of truth instead.
50651
- *
50652
- * `id` is the raw `DeviceType` string (matches the filtering predicate, which
50653
- * compares against `device.type`); `label` + `icon` come from the meta map.
50654
- * Ordered by `DeviceType` declaration order for a stable, intentional layout.
50655
- */
50656
- var ALL_DEVICE_TYPE_FILTER_OPTIONS = Object.values(_camstack_types.DeviceType).map((type) => {
50657
- const meta = deviceTypeMetaOf(type);
50658
- return {
50659
- id: type,
50660
- label: meta.label,
50661
- icon: meta.icon
50662
- };
50663
- });
50664
- function allDeviceTypeFilterOptions() {
50665
- return ALL_DEVICE_TYPE_FILTER_OPTIONS;
50666
- }
50667
- //#endregion
50668
50898
  //#region src/hooks/use-debounced-string.ts
50669
50899
  /**
50670
50900
  * useDebouncedString — return `value` after `delay` ms have passed
@@ -50683,6 +50913,59 @@ function useDebouncedString(value, delayMs) {
50683
50913
  return debounced;
50684
50914
  }
50685
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
50686
50969
  //#region src/hooks/use-device-list-page-size.ts
50687
50970
  /**
50688
50971
  * useDeviceListPageSize — global page-size preference for the device-list
@@ -50693,8 +50976,9 @@ function useDebouncedString(value, delayMs) {
50693
50976
  * value is validated against {@link DEVICE_LIST_PAGE_SIZE_OPTIONS}; any
50694
50977
  * garbage / out-of-range value falls back to the provided default.
50695
50978
  *
50696
- * SSR / jsdom-safe: every `window` access is guarded so the hook can run in
50697
- * 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.
50698
50982
  */
50699
50983
  /** Selectable per-page sizes. The table default (50) is intentionally one of them. */
50700
50984
  var DEVICE_LIST_PAGE_SIZE_OPTIONS = [
@@ -50705,33 +50989,6 @@ var DEVICE_LIST_PAGE_SIZE_OPTIONS = [
50705
50989
  ];
50706
50990
  /** Single global key shared across every device-list surface. */
50707
50991
  var DEVICE_LIST_PAGE_SIZE_KEY = "camstack.device-list.pageSize";
50708
- function isAllowed(value) {
50709
- return DEVICE_LIST_PAGE_SIZE_OPTIONS.includes(value);
50710
- }
50711
- /**
50712
- * Normalize a caller-supplied `defaultSize` to an allowed option, falling back
50713
- * to the canonical default so a stray value can never seed a phantom size.
50714
- */
50715
- function safeDefault(defaultSize) {
50716
- return isAllowed(defaultSize) ? defaultSize : 50;
50717
- }
50718
- function readStored(defaultSize) {
50719
- if (typeof window === "undefined") return defaultSize;
50720
- try {
50721
- const raw = window.localStorage.getItem(DEVICE_LIST_PAGE_SIZE_KEY);
50722
- if (raw === null) return defaultSize;
50723
- const parsed = Number.parseInt(raw, 10);
50724
- return Number.isFinite(parsed) && isAllowed(parsed) ? parsed : defaultSize;
50725
- } catch {
50726
- return defaultSize;
50727
- }
50728
- }
50729
- function writeStored(value) {
50730
- if (typeof window === "undefined") return;
50731
- try {
50732
- window.localStorage.setItem(DEVICE_LIST_PAGE_SIZE_KEY, String(value));
50733
- } catch {}
50734
- }
50735
50992
  /**
50736
50993
  * @param defaultSize fallback when nothing valid is stored. If it is not itself
50737
50994
  * a member of {@link DEVICE_LIST_PAGE_SIZE_OPTIONS}, it is replaced by
@@ -50739,13 +50996,11 @@ function writeStored(value) {
50739
50996
  * @returns `[size, setSize]` — `setSize` ignores values outside the allowed set.
50740
50997
  */
50741
50998
  function useDeviceListPageSize(defaultSize) {
50742
- const seed = safeDefault(defaultSize);
50743
- const [size, setSize] = (0, react$1.useState)(() => readStored(seed));
50744
- return [size, (0, react$1.useCallback)((next) => {
50745
- if (!isAllowed(next)) return;
50746
- setSize(next);
50747
- writeStored(next);
50748
- }, [])];
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
+ });
50749
51004
  }
50750
51005
  //#endregion
50751
51006
  //#region src/composites/device-item/helpers.ts
@@ -53043,6 +53298,29 @@ function groupTopLevelAndAccessories(devices, options) {
53043
53298
  };
53044
53299
  }
53045
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
53046
53324
  //#region src/composites/device-list/multi-select.tsx
53047
53325
  /**
53048
53326
  * Compact multi-select dropdown — a single chip-sized trigger ("Label · N")
@@ -53957,23 +54235,11 @@ function DeviceModeList(props) {
53957
54235
  totalItems,
53958
54236
  " ",
53959
54237
  totalItems === 1 ? "device" : "devices"
53960
- ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
53961
- className: "inline-flex items-center gap-1.5",
53962
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
53963
- "aria-hidden": "true",
53964
- children: "Per page"
53965
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
53966
- "aria-label": "Devices per page",
53967
- value: tablePageSize,
53968
- onChange: (e) => {
53969
- setTablePageSize(Number.parseInt(e.target.value, 10));
53970
- },
53971
- 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",
53972
- children: DEVICE_LIST_PAGE_SIZE_OPTIONS.map((opt) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
53973
- value: opt,
53974
- children: opt
53975
- }, opt))
53976
- })]
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
53977
54243
  })]
53978
54244
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pagination, {
53979
54245
  currentPage: safePage,
@@ -54017,6 +54283,7 @@ function useDeviceListRealtimeSync() {
54017
54283
  }
54018
54284
  //#endregion
54019
54285
  //#region src/composites/device-list/generic-mode.tsx
54286
+ var FIXED_PAGE_SIZE = 100;
54020
54287
  /**
54021
54288
  * Generic render-prop list: search + pagination chrome shared with the device
54022
54289
  * list, over an arbitrary item type. Local (non-URL) state — intended for
@@ -54024,7 +54291,12 @@ function useDeviceListRealtimeSync() {
54024
54291
  * `renderItem`.
54025
54292
  */
54026
54293
  function GenericModeList(props) {
54027
- 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
+ });
54028
54300
  const [searchInput, setSearchInput] = (0, react$1.useState)("");
54029
54301
  const debouncedSearch = useDebouncedString(searchInput, 300);
54030
54302
  const [typeFilters, setTypeFilters] = (0, react$1.useState)(() => [...initialTypeFilters]);
@@ -54073,6 +54345,7 @@ function GenericModeList(props) {
54073
54345
  getFilterType
54074
54346
  ]);
54075
54347
  const totalItems = filtered.length;
54348
+ const pageSize = pageSizeSelector ? effectivePageSize(chosenPageSize, totalItems) : fixedPageSize;
54076
54349
  const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
54077
54350
  const safePage = Math.min(page, totalPages);
54078
54351
  const startIdx = (safePage - 1) * pageSize;
@@ -54156,7 +54429,23 @@ function GenericModeList(props) {
54156
54429
  }),
54157
54430
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
54158
54431
  className: "shrink-0",
54159
- 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, {
54160
54449
  currentPage: safePage,
54161
54450
  totalItems,
54162
54451
  pageSize,
@@ -54285,6 +54574,24 @@ var DeviceItemRowSchema = zod.z.object({
54285
54574
  });
54286
54575
  var SupportedKindsSchema = zod.z.array(zod.z.string());
54287
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
+ };
54288
54595
  /**
54289
54596
  * The fleet-list poll. Deliberately the same 30 s as the window
54290
54597
  * `applyFleetQueryDefaults` registers for `deviceManager.listAll` (D274) —
@@ -54369,6 +54676,35 @@ function SetupSection({ setup }) {
54369
54676
  })]
54370
54677
  });
54371
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
+ }
54372
54708
  /**
54373
54709
  * Per-row Expose/Exposed toggle. Reflects whether the row's device is exposed
54374
54710
  * to THIS exporter and flips it through the SAME runner the bulk buttons use —
@@ -54447,7 +54783,7 @@ function BulkSummary({ outcome, onDismiss }) {
54447
54783
  * Generic device-export panel. Addon-agnostic — only the `device-export`
54448
54784
  * cap drives it.
54449
54785
  */
54450
- function DeviceExportPanel({ addonId, onOpenDevice }) {
54786
+ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
54451
54787
  const queryClient = (0, _tanstack_react_query.useQueryClient)();
54452
54788
  const statusQuery = useDeviceExportGetStatus({
54453
54789
  nodeId: "hub",
@@ -54473,10 +54809,7 @@ function DeviceExportPanel({ addonId, onOpenDevice }) {
54473
54809
  }, [queryClient]);
54474
54810
  const exposeMutation = useDeviceExportExposeDevice({ onSuccess: invalidate });
54475
54811
  const unexposeMutation = useDeviceExportUnexposeDevice({ onSuccess: invalidate });
54476
- const status = (0, react$1.useMemo)(() => {
54477
- const parsed = _camstack_types.DeviceExportStatusSchema.safeParse(statusQuery.data);
54478
- return parsed.success ? parsed.data : null;
54479
- }, [statusQuery.data]);
54812
+ const status = (0, react$1.useMemo)(() => parseStatus(statusQuery.data), [statusQuery.data]);
54480
54813
  const exposed = (0, react$1.useMemo)(() => {
54481
54814
  const parsed = ExposedDeviceArraySchema.safeParse(exposedQuery.data);
54482
54815
  return parsed.success ? parsed.data : [];
@@ -54699,11 +55032,11 @@ function DeviceExportPanel({ addonId, onOpenDevice }) {
54699
55032
  ]
54700
55033
  }), loading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoaderCircle, { className: "h-3.5 w-3.5 animate-spin text-foreground-subtle" })]
54701
55034
  }),
54702
- status?.error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
55035
+ showSetup && status?.error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
54703
55036
  className: "px-2 sm:px-4 py-3 border-b border-border",
54704
55037
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorBox, { message: status.error })
54705
55038
  }),
54706
- 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 }),
54707
55040
  outcome !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BulkSummary, {
54708
55041
  outcome,
54709
55042
  onDismiss: dismissOutcome
@@ -54723,6 +55056,7 @@ function DeviceExportPanel({ addonId, onOpenDevice }) {
54723
55056
  columns,
54724
55057
  onRowClick: onOpenDevice ? onRowClick : void 0,
54725
55058
  toolbar: selectionToolbar,
55059
+ pageSizeSelector: DEVICE_EXPORT_PAGE_SIZE,
54726
55060
  searchPlaceholder: "Search by name or stableId",
54727
55061
  emptyLabel: "No device matches this search."
54728
55062
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmptyState, {
@@ -54748,145 +55082,6 @@ function DeviceGrid({ children, minCardWidth = 220, gap = 3, className }) {
54748
55082
  });
54749
55083
  }
54750
55084
  //#endregion
54751
- //#region src/composites/icon-picker.tsx
54752
- /**
54753
- * icon-picker — the ONE control for choosing an icon, wherever admin-ui asks.
54754
- *
54755
- * There were two: a device one over `DISPLAY_ICON_REGISTRY` and a room one
54756
- * over `LOCATION_ICON_IDS`, identical apart from the vocabulary they walked.
54757
- * That was tolerable while the device set was 56 glyphs in a flat grid. It is
54758
- * not at 157: a picker that only lets the operator HUNT is a picker they stop
54759
- * using, and the two copies were already drifting — only one of them had an
54760
- * i18n'd label.
54761
- *
54762
- * So the vocabulary, the grouping and the search all come from
54763
- * `@camstack/types`' `display-icon-vocabulary`, which is the same module the
54764
- * viewer's sheet searches. What is left here is the web half: a popover, a
54765
- * grid, and a text field.
54766
- *
54767
- * ## What it does NOT own
54768
- *
54769
- * - **The glyphs.** `resolveGlyph` is a prop, because this package's table is
54770
- * `lucide-react` and the viewer's is `lucide-react-native`. A key this build
54771
- * cannot draw is SKIPPED rather than rendered as a hole — an empty tile
54772
- * reads as a rendering bug, not as a missing choice.
54773
- * - **The words.** Every string is a prop. This package has no i18n (one file
54774
- * in it imports react-i18next, and that is the exception), and a control
54775
- * that reached for a translation would force it on every consumer.
54776
- * - **The write.** `onPick` is called and that is all; whether it is a form
54777
- * value or an immediate mutation is the caller's business, as it already was
54778
- * in both pickers this replaces.
54779
- *
54780
- * ## The clear tile is never filtered away
54781
- *
54782
- * The "inherit" / "no icon" choice sits above the sections and ignores the
54783
- * search. A query that hid it would strand the operator on a glyph they picked
54784
- * by accident with no way back to the default — the one action a picker must
54785
- * always offer.
54786
- */
54787
- function IconPicker({ value, onPick, resolveGlyph, fallbackGlyph, labels, only, extraWords, clearGlyph, labelFor, disabled = false }) {
54788
- const [open, setOpen] = (0, react$1.useState)(false);
54789
- const [query, setQuery] = (0, react$1.useState)("");
54790
- const allowed = (0, react$1.useMemo)(() => only === void 0 ? null : new Set(only), [only]);
54791
- const sections = (0, react$1.useMemo)(() => {
54792
- const hits = (0, _camstack_types.searchDisplayIcons)(query, extraWords);
54793
- const byGroup = /* @__PURE__ */ new Map();
54794
- for (const hit of hits) {
54795
- const { key, group } = hit.entry;
54796
- if (allowed !== null && !allowed.has(key)) continue;
54797
- if (resolveGlyph(key) === void 0) continue;
54798
- const bucket = byGroup.get(group);
54799
- if (bucket === void 0) byGroup.set(group, [key]);
54800
- else bucket.push(key);
54801
- }
54802
- return _camstack_types.DISPLAY_ICON_GROUPS.flatMap((group) => {
54803
- const keys = byGroup.get(group);
54804
- return keys === void 0 || keys.length === 0 ? [] : [{
54805
- group,
54806
- keys
54807
- }];
54808
- });
54809
- }, [
54810
- query,
54811
- extraWords,
54812
- allowed,
54813
- resolveGlyph
54814
- ]);
54815
- const Current = (value === null ? void 0 : resolveGlyph(value)) ?? fallbackGlyph;
54816
- const choose = async (next) => {
54817
- setOpen(false);
54818
- setQuery("");
54819
- await onPick(next);
54820
- };
54821
- const Clear = clearGlyph ?? fallbackGlyph;
54822
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
54823
- className: "relative flex shrink-0 items-center gap-2",
54824
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
54825
- type: "button",
54826
- disabled,
54827
- onClick: () => setOpen((o) => !o),
54828
- "aria-expanded": open,
54829
- "aria-label": labels.trigger,
54830
- title: labels.trigger,
54831
- className: "rounded border border-border p-1 text-foreground-muted hover:bg-surface-hover disabled:opacity-50",
54832
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Current, { className: "h-4 w-4" })
54833
- }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
54834
- type: "button",
54835
- "aria-label": labels.close,
54836
- onClick: () => setOpen(false),
54837
- className: "fixed inset-0 z-10 cursor-default"
54838
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
54839
- role: "group",
54840
- "aria-label": labels.panel,
54841
- 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",
54842
- children: [
54843
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
54844
- type: "search",
54845
- value: query,
54846
- autoFocus: true,
54847
- onChange: (e) => setQuery(e.target.value),
54848
- placeholder: labels.searchPlaceholder,
54849
- "aria-label": labels.searchPlaceholder,
54850
- className: "mb-2 w-full rounded border border-border bg-surface-raised px-2 py-1 text-xs text-foreground placeholder:text-foreground-subtle"
54851
- }),
54852
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
54853
- type: "button",
54854
- onClick: () => void choose(null),
54855
- "aria-pressed": value === null,
54856
- 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",
54857
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Clear, { className: "h-3.5 w-3.5" }), labels.clear]
54858
- }),
54859
- sections.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
54860
- className: "px-1.5 py-2 text-[11px] text-foreground-subtle",
54861
- children: labels.noMatches
54862
- }) : sections.map(({ group, keys }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
54863
- className: "mb-2 last:mb-0",
54864
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
54865
- className: "px-1.5 pb-1 text-[10px] uppercase tracking-wide text-foreground-subtle",
54866
- children: labels.groups[group]
54867
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
54868
- className: "grid grid-cols-7 gap-1",
54869
- children: keys.map((key) => {
54870
- const Glyph = resolveGlyph(key);
54871
- if (Glyph === void 0) return null;
54872
- const selected = value === key;
54873
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
54874
- type: "button",
54875
- onClick: () => void choose(key),
54876
- "aria-pressed": selected,
54877
- "aria-label": labelFor?.(key) ?? key,
54878
- title: labelFor?.(key) ?? key,
54879
- 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",
54880
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Glyph, { className: "h-4 w-4" })
54881
- }, key);
54882
- })
54883
- })]
54884
- }, group))
54885
- ]
54886
- })] })]
54887
- });
54888
- }
54889
- //#endregion
54890
55085
  //#region src/composites/device-selector-picker.tsx
54891
55086
  /**
54892
55087
  * <DeviceSelectorPicker> — the operator-facing editor for a v3
@@ -61087,6 +61282,7 @@ exports.CoverPanel = CoverPanel;
61087
61282
  exports.CustomFieldRenderersProvider = CustomFieldRenderersProvider;
61088
61283
  exports.DEFAULT_COLOR = DEFAULT_COLOR;
61089
61284
  exports.DEVICE_COLUMNS = DEVICE_COLUMNS;
61285
+ exports.DEVICE_EXPORT_PAGE_SIZE_KEY = DEVICE_EXPORT_PAGE_SIZE_KEY;
61090
61286
  exports.DEVICE_LIST_PAGE_SIZE_KEY = DEVICE_LIST_PAGE_SIZE_KEY;
61091
61287
  exports.DEVICE_LIST_PAGE_SIZE_OPTIONS = DEVICE_LIST_PAGE_SIZE_OPTIONS;
61092
61288
  exports.DEVICE_PICKER_GROUP_STORAGE_KEY = DEVICE_PICKER_GROUP_STORAGE_KEY;
@@ -61105,6 +61301,7 @@ exports.DeviceBatchToolbar = DeviceBatchToolbar;
61105
61301
  exports.DeviceCard = DeviceCard;
61106
61302
  exports.DeviceContextProvider = DeviceContextProvider;
61107
61303
  exports.DeviceExportPanel = DeviceExportPanel;
61304
+ exports.DeviceExportSetup = DeviceExportSetup;
61108
61305
  exports.DeviceFilterBar = DeviceFilterBar;
61109
61306
  exports.DeviceGrid = DeviceGrid;
61110
61307
  exports.DeviceItem = DeviceItem;
@@ -61218,6 +61415,7 @@ exports.NotificationTargetsField = NotificationTargetsField;
61218
61415
  exports.OPTION_SELECT_SEARCH_MIN_OPTIONS = OPTION_SELECT_SEARCH_MIN_OPTIONS;
61219
61416
  exports.OfflineBadge = OfflineBadge;
61220
61417
  exports.OptionSelect = OptionSelect;
61418
+ exports.PAGE_SIZE_ALL = PAGE_SIZE_ALL;
61221
61419
  exports.PHASE_CONFIG = PHASE_CONFIG;
61222
61420
  exports.PICKER_CHIP_LIMIT = PICKER_CHIP_LIMIT;
61223
61421
  exports.PICKER_DEFAULT_GROUP_BY = PICKER_DEFAULT_GROUP_BY;
@@ -61232,6 +61430,7 @@ exports.PICKER_WINDOW = PICKER_WINDOW;
61232
61430
  exports.PRIORITY = PRIORITY;
61233
61431
  exports.PTZOverlay = PTZOverlay;
61234
61432
  exports.PageHeader = PageHeader;
61433
+ exports.PageSizeSelect = PageSizeSelect;
61235
61434
  exports.PhaseIcon = PhaseIcon;
61236
61435
  exports.PipelineBuilder = PipelineBuilder;
61237
61436
  exports.PipelineRuntimeSelector = PipelineRuntimeSelector;
@@ -61404,6 +61603,7 @@ exports.deviceTypeMetaOf = deviceTypeMetaOf;
61404
61603
  exports.devicesToOptions = devicesToOptions;
61405
61604
  exports.dockBox = dockBox;
61406
61605
  exports.draftFromQuery = draftFromQuery;
61606
+ exports.effectivePageSize = effectivePageSize;
61407
61607
  exports.emptyDevicesWidens = emptyDevicesWidens;
61408
61608
  exports.emptySessionRun = emptySessionRun;
61409
61609
  exports.ensureMfHostInit = ensureMfHostInit;
@@ -61466,6 +61666,7 @@ exports.optionSelectSearchText = optionSearchText;
61466
61666
  exports.orphanDeviceLabel = orphanDeviceLabel;
61467
61667
  exports.overrideEntityIdFromLink = overrideEntityIdFromLink;
61468
61668
  exports.pageRows = pageRows;
61669
+ exports.pageSizeLabel = pageSizeLabel;
61469
61670
  exports.parentDetailOf = parentDetailOf;
61470
61671
  exports.parseIceCandidate = parseIceCandidate;
61471
61672
  exports.parsePickerGroupBy = parsePickerGroupBy;
@@ -62176,6 +62377,7 @@ exports.useOsdManagerSetSlotBinding = useOsdManagerSetSlotBinding;
62176
62377
  exports.useOsdManagerUpsertTemplate = useOsdManagerUpsertTemplate;
62177
62378
  exports.useOsdSetOverlay = useOsdSetOverlay;
62178
62379
  exports.usePTZ = usePTZ;
62380
+ exports.usePersistedPageSize = usePersistedPageSize;
62179
62381
  exports.usePetFeederCallPet = usePetFeederCallPet;
62180
62382
  exports.usePetFeederCancelFeed = usePetFeederCancelFeed;
62181
62383
  exports.usePetFeederFeed = usePetFeederFeed;
@@ -62253,12 +62455,14 @@ exports.usePipelineAnalyticsResumeForStorageMigration = usePipelineAnalyticsResu
62253
62455
  exports.usePipelineAnalyticsSaveRetrainAnnotations = usePipelineAnalyticsSaveRetrainAnnotations;
62254
62456
  exports.usePipelineAnalyticsSearchObjectEvents = usePipelineAnalyticsSearchObjectEvents;
62255
62457
  exports.usePipelineAnalyticsSelectRetrainFrames = usePipelineAnalyticsSelectRetrainFrames;
62458
+ exports.usePipelineAnalyticsSetRetrainFrameComplete = usePipelineAnalyticsSetRetrainFrameComplete;
62256
62459
  exports.usePipelineAnalyticsSetTrackFlags = usePipelineAnalyticsSetTrackFlags;
62257
62460
  exports.usePipelineAnalyticsSetTrackPurity = usePipelineAnalyticsSetTrackPurity;
62258
62461
  exports.usePipelineAnalyticsSetTrackRelation = usePipelineAnalyticsSetTrackRelation;
62259
62462
  exports.usePipelineAnalyticsStartStorageMigrationMove = usePipelineAnalyticsStartStorageMigrationMove;
62260
62463
  exports.usePipelineAnalyticsWipeAllAnalytics = usePipelineAnalyticsWipeAllAnalytics;
62261
62464
  exports.usePipelineAnalyticsWipeObjectEmbeddings = usePipelineAnalyticsWipeObjectEmbeddings;
62465
+ exports.usePipelineAnalyticsWriteRetrainArchive = usePipelineAnalyticsWriteRetrainArchive;
62262
62466
  exports.usePipelineExecutorCacheFrameInPool = usePipelineExecutorCacheFrameInPool;
62263
62467
  exports.usePipelineExecutorClearDeviceOverrides = usePipelineExecutorClearDeviceOverrides;
62264
62468
  exports.usePipelineExecutorClearModelBoundDeviceOverrides = usePipelineExecutorClearModelBoundDeviceOverrides;
@@ -62640,8 +62844,11 @@ exports.useUserManagementCreateScopedToken = useUserManagementCreateScopedToken;
62640
62844
  exports.useUserManagementCreateUser = useUserManagementCreateUser;
62641
62845
  exports.useUserManagementDeleteUser = useUserManagementDeleteUser;
62642
62846
  exports.useUserManagementDisableTotp = useUserManagementDisableTotp;
62847
+ exports.useUserManagementFindUserByExternalIdentity = useUserManagementFindUserByExternalIdentity;
62643
62848
  exports.useUserManagementGetTotpStatus = useUserManagementGetTotpStatus;
62849
+ exports.useUserManagementLinkExternalIdentity = useUserManagementLinkExternalIdentity;
62644
62850
  exports.useUserManagementListApiKeys = useUserManagementListApiKeys;
62851
+ exports.useUserManagementListExternalIdentities = useUserManagementListExternalIdentities;
62645
62852
  exports.useUserManagementListOauthSessions = useUserManagementListOauthSessions;
62646
62853
  exports.useUserManagementListScopedTokens = useUserManagementListScopedTokens;
62647
62854
  exports.useUserManagementListUsers = useUserManagementListUsers;
@@ -62655,6 +62862,7 @@ exports.useUserManagementRevokeOauthSession = useUserManagementRevokeOauthSessio
62655
62862
  exports.useUserManagementRevokeScopedToken = useUserManagementRevokeScopedToken;
62656
62863
  exports.useUserManagementSetUserScopes = useUserManagementSetUserScopes;
62657
62864
  exports.useUserManagementSetupTotp = useUserManagementSetupTotp;
62865
+ exports.useUserManagementUnlinkExternalIdentity = useUserManagementUnlinkExternalIdentity;
62658
62866
  exports.useUserManagementUpdateUser = useUserManagementUpdateUser;
62659
62867
  exports.useUserManagementValidateApiKey = useUserManagementValidateApiKey;
62660
62868
  exports.useUserManagementValidateCredentials = useUserManagementValidateCredentials;