@camstack/ui-library 1.2.349 → 1.2.351
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/CHANGELOG.md +18 -0
- package/dist/{SceneMonitorEditor-BgqJYVdM.js → SceneMonitorEditor-A1U5eCrC.js} +1 -1
- package/dist/composites/device-controls/pet-fountain-panel.d.ts +12 -0
- package/dist/composites/device-controls/types.d.ts +1 -1
- package/dist/composites/export-entity-rows.d.ts +33 -0
- package/dist/composites/sectioned-page.d.ts +3 -0
- package/dist/generated/system-hooks.d.ts +24 -0
- package/dist/{icons-CWmwnulO.cjs → icons-BJpFQcLH.cjs} +1 -14
- package/dist/{icons-Bd6sbDsv.js → icons-Cf1xnXkh.js} +1 -14
- package/dist/index.cjs +731 -81
- package/dist/index.js +721 -84
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1706,6 +1706,19 @@ var EllipsisVertical = require_createLucideIcon.createLucideIcon("ellipsis-verti
|
|
|
1706
1706
|
* This source code is licensed under the ISC license.
|
|
1707
1707
|
* See the LICENSE file in the root directory of this source tree.
|
|
1708
1708
|
*/
|
|
1709
|
+
var Eraser = require_createLucideIcon.createLucideIcon("eraser", [["path", {
|
|
1710
|
+
d: "M21 21H8a2 2 0 0 1-1.42-.587l-3.994-3.999a2 2 0 0 1 0-2.828l10-10a2 2 0 0 1 2.829 0l5.999 6a2 2 0 0 1 0 2.828L12.834 21",
|
|
1711
|
+
key: "g5wo59"
|
|
1712
|
+
}], ["path", {
|
|
1713
|
+
d: "m5.082 11.09 8.828 8.828",
|
|
1714
|
+
key: "1wx5vj"
|
|
1715
|
+
}]]);
|
|
1716
|
+
/**
|
|
1717
|
+
* @license lucide-react v0.576.0 - ISC
|
|
1718
|
+
*
|
|
1719
|
+
* This source code is licensed under the ISC license.
|
|
1720
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
1721
|
+
*/
|
|
1709
1722
|
var ExternalLink = require_createLucideIcon.createLucideIcon("external-link", [
|
|
1710
1723
|
["path", {
|
|
1711
1724
|
d: "M15 3h6v6",
|
|
@@ -5400,6 +5413,11 @@ var DEVICE_TYPE_META = {
|
|
|
5400
5413
|
labelKey: "uiLibrary.deviceMeta.type.petFeeder",
|
|
5401
5414
|
labelFallback: "Pet feeder"
|
|
5402
5415
|
},
|
|
5416
|
+
[_camstack_types.DeviceType.PetFountain]: {
|
|
5417
|
+
icon: Droplets,
|
|
5418
|
+
labelKey: "uiLibrary.deviceMeta.type.petFountain",
|
|
5419
|
+
labelFallback: "Pet fountain"
|
|
5420
|
+
},
|
|
5403
5421
|
[_camstack_types.DeviceType.Dishwasher]: {
|
|
5404
5422
|
icon: Waves,
|
|
5405
5423
|
labelKey: "uiLibrary.deviceMeta.type.dishwasher",
|
|
@@ -5900,7 +5918,7 @@ function lucideExportName(key) {
|
|
|
5900
5918
|
function loadIconGlyphs() {
|
|
5901
5919
|
if (fullTable !== void 0) return Promise.resolve();
|
|
5902
5920
|
if (failure$1 !== void 0) return Promise.reject(failure$1);
|
|
5903
|
-
pending ??= Promise.resolve().then(() => require("./icons-
|
|
5921
|
+
pending ??= Promise.resolve().then(() => require("./icons-BJpFQcLH.cjs")).then((module) => {
|
|
5904
5922
|
fullTable = module;
|
|
5905
5923
|
publish();
|
|
5906
5924
|
}, (error) => {
|
|
@@ -11026,6 +11044,14 @@ var useDeviceExportListExposedDevices = trpc.deviceExport.listExposedDevices.use
|
|
|
11026
11044
|
var useDeviceExportExposeDevice = trpc.deviceExport.exposeDevice.useMutation;
|
|
11027
11045
|
/** Generated alias around `trpc.deviceExport.unexposeDevice.useMutation`. */
|
|
11028
11046
|
var useDeviceExportUnexposeDevice = trpc.deviceExport.unexposeDevice.useMutation;
|
|
11047
|
+
/** Generated alias around `trpc.deviceExport.listSupportedEntityKinds.useQuery`. */
|
|
11048
|
+
var useDeviceExportListSupportedEntityKinds = trpc.deviceExport.listSupportedEntityKinds.useQuery;
|
|
11049
|
+
/** Generated alias around `trpc.deviceExport.listExposedEntities.useQuery`. */
|
|
11050
|
+
var useDeviceExportListExposedEntities = trpc.deviceExport.listExposedEntities.useQuery;
|
|
11051
|
+
/** Generated alias around `trpc.deviceExport.exposeEntity.useMutation`. */
|
|
11052
|
+
var useDeviceExportExposeEntity = trpc.deviceExport.exposeEntity.useMutation;
|
|
11053
|
+
/** Generated alias around `trpc.deviceExport.unexposeEntity.useMutation`. */
|
|
11054
|
+
var useDeviceExportUnexposeEntity = trpc.deviceExport.unexposeEntity.useMutation;
|
|
11029
11055
|
/** Generated alias around `trpc.deviceExport.getDeviceSettingsContribution.useQuery`. */
|
|
11030
11056
|
var useDeviceExportGetDeviceSettingsContribution = trpc.deviceExport.getDeviceSettingsContribution.useQuery;
|
|
11031
11057
|
/** Generated alias around `trpc.deviceExport.getDeviceLiveContribution.useQuery`. */
|
|
@@ -11928,6 +11954,18 @@ var usePetFeederSetFeedSound = trpc.petFeeder.setFeedSound.useMutation;
|
|
|
11928
11954
|
var usePetFeederSetVolume = trpc.petFeeder.setVolume.useMutation;
|
|
11929
11955
|
/** Generated alias around `trpc.petFeeder.getStatus.useQuery`. */
|
|
11930
11956
|
var usePetFeederGetStatus = trpc.petFeeder.getStatus.useQuery;
|
|
11957
|
+
/** Generated alias around `trpc.petFountain.setPump.useMutation`. */
|
|
11958
|
+
var usePetFountainSetPump = trpc.petFountain.setPump.useMutation;
|
|
11959
|
+
/** Generated alias around `trpc.petFountain.setPumpInterval.useMutation`. */
|
|
11960
|
+
var usePetFountainSetPumpInterval = trpc.petFountain.setPumpInterval.useMutation;
|
|
11961
|
+
/** Generated alias around `trpc.petFountain.setPumpOnMinutes.useMutation`. */
|
|
11962
|
+
var usePetFountainSetPumpOnMinutes = trpc.petFountain.setPumpOnMinutes.useMutation;
|
|
11963
|
+
/** Generated alias around `trpc.petFountain.setPumpOffMinutes.useMutation`. */
|
|
11964
|
+
var usePetFountainSetPumpOffMinutes = trpc.petFountain.setPumpOffMinutes.useMutation;
|
|
11965
|
+
/** Generated alias around `trpc.petFountain.resetDailyConsumption.useMutation`. */
|
|
11966
|
+
var usePetFountainResetDailyConsumption = trpc.petFountain.resetDailyConsumption.useMutation;
|
|
11967
|
+
/** Generated alias around `trpc.petFountain.getStatus.useQuery`. */
|
|
11968
|
+
var usePetFountainGetStatus = trpc.petFountain.getStatus.useQuery;
|
|
11931
11969
|
/** Generated alias around `trpc.pipelineAnalytics.getActiveTracks.useQuery`. */
|
|
11932
11970
|
var usePipelineAnalyticsGetActiveTracks = trpc.pipelineAnalytics.getActiveTracks.useQuery;
|
|
11933
11971
|
/** Generated alias around `trpc.pipelineAnalytics.getTrack.useQuery`. */
|
|
@@ -12576,6 +12614,10 @@ var useServerManagementApplyServerUpdate = trpc.serverManagement.applyServerUpda
|
|
|
12576
12614
|
var useServerManagementRollbackServerUpdate = trpc.serverManagement.rollbackServerUpdate.useMutation;
|
|
12577
12615
|
/** Generated alias around `trpc.serverManagement.restartServer.useMutation`. */
|
|
12578
12616
|
var useServerManagementRestartServer = trpc.serverManagement.restartServer.useMutation;
|
|
12617
|
+
/** Generated alias around `trpc.serverManagement.getLaunchOverrides.useQuery`. */
|
|
12618
|
+
var useServerManagementGetLaunchOverrides = trpc.serverManagement.getLaunchOverrides.useQuery;
|
|
12619
|
+
/** Generated alias around `trpc.serverManagement.setLaunchOverrides.useMutation`. */
|
|
12620
|
+
var useServerManagementSetLaunchOverrides = trpc.serverManagement.setLaunchOverrides.useMutation;
|
|
12579
12621
|
/** Generated alias around `trpc.settingsStore.get.useQuery`. */
|
|
12580
12622
|
var useSettingsStoreGet = trpc.settingsStore.get.useQuery;
|
|
12581
12623
|
/** Generated alias around `trpc.settingsStore.set.useMutation`. */
|
|
@@ -48791,6 +48833,7 @@ var CONTROL_CAP_NAMES = [
|
|
|
48791
48833
|
"media-player",
|
|
48792
48834
|
"control",
|
|
48793
48835
|
"pet-feeder",
|
|
48836
|
+
"pet-fountain",
|
|
48794
48837
|
"dishwasher"
|
|
48795
48838
|
];
|
|
48796
48839
|
//#endregion
|
|
@@ -53081,6 +53124,10 @@ var HOLD_DELAY_MS = 350;
|
|
|
53081
53124
|
var HOLD_INTERVAL_MS = 70;
|
|
53082
53125
|
var HOLD_ACCEL_AFTER = 8;
|
|
53083
53126
|
var HOLD_FAST_STEP = 5;
|
|
53127
|
+
/** The slice stores the bowl as a mass in kilograms (D944); the card draws grams. */
|
|
53128
|
+
function bowlGrams(kilograms) {
|
|
53129
|
+
return kilograms === null ? null : Math.round(kilograms * 1e3);
|
|
53130
|
+
}
|
|
53084
53131
|
function clampPortion(grams) {
|
|
53085
53132
|
return Math.min(_camstack_types.PET_FEEDER_MANUAL_FEED_MAX, Math.max(_camstack_types.PET_FEEDER_MANUAL_FEED_MIN, grams));
|
|
53086
53133
|
}
|
|
@@ -53132,7 +53179,7 @@ function PetFeederPanel({ trpc, deviceId, layout, optimistic }) {
|
|
|
53132
53179
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UtensilsCrossed, {
|
|
53133
53180
|
className: "h-3.5 w-3.5",
|
|
53134
53181
|
"aria-hidden": true
|
|
53135
|
-
}), display.bowlFood ?? "—"]
|
|
53182
|
+
}), bowlGrams(display.bowlFood) ?? "—"]
|
|
53136
53183
|
}),
|
|
53137
53184
|
onMains && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
53138
53185
|
className: "inline-flex items-center gap-0.5 text-xs text-foreground-subtle",
|
|
@@ -53178,7 +53225,7 @@ function PetFeederPanel({ trpc, deviceId, layout, optimistic }) {
|
|
|
53178
53225
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53179
53226
|
className: "flex items-center gap-4 rounded-lg border border-border/50 bg-surface-subtle/40 p-3",
|
|
53180
53227
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(BowlGauge, {
|
|
53181
|
-
grams: display.bowlFood,
|
|
53228
|
+
grams: bowlGrams(display.bowlFood),
|
|
53182
53229
|
low: display.lowFood,
|
|
53183
53230
|
eating: display.eating
|
|
53184
53231
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -53188,7 +53235,7 @@ function PetFeederPanel({ trpc, deviceId, layout, optimistic }) {
|
|
|
53188
53235
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
53189
53236
|
className: "text-3xl font-bold leading-none tabular-nums",
|
|
53190
53237
|
style: { color: display.lowFood ? KIBBLE_LOW : KIBBLE },
|
|
53191
|
-
children: display.bowlFood ?? "—"
|
|
53238
|
+
children: bowlGrams(display.bowlFood) ?? "—"
|
|
53192
53239
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
53193
53240
|
className: "text-sm text-foreground-subtle",
|
|
53194
53241
|
children: t("uiLibrary.petFeeder.inBowl", "in bowl")
|
|
@@ -53625,6 +53672,367 @@ function PetFeederHeroCard({ trpc, deviceId, features, optimistic }) {
|
|
|
53625
53672
|
});
|
|
53626
53673
|
}
|
|
53627
53674
|
//#endregion
|
|
53675
|
+
//#region src/composites/device-controls/pet-fountain-panel.tsx
|
|
53676
|
+
/**
|
|
53677
|
+
* Pet-fountain controls for a `DeviceType.PetFountain` device.
|
|
53678
|
+
*
|
|
53679
|
+
* Reads the `pet-fountain` cap status slice (water level and volume, empty,
|
|
53680
|
+
* pump and its duty cycle, the water drunk) and renders:
|
|
53681
|
+
* - PetFountainInlineControl (device-list row): the tank level and a pump
|
|
53682
|
+
* on/off toggle.
|
|
53683
|
+
* - PetFountainHeroCard (device-detail hero): the tank gauge with the level
|
|
53684
|
+
* and the volume first, what the pet drank today / in 24 h / this hour /
|
|
53685
|
+
* last time, the pump switch, its duty-cycle switch and the run / rest
|
|
53686
|
+
* minutes. The filter is a consumable: the page's Consumables section
|
|
53687
|
+
* draws it, never this card (D915).
|
|
53688
|
+
*
|
|
53689
|
+
* Every reading may be unknown (`null`) and is drawn as a dash, never as
|
|
53690
|
+
* zero. Stored volumes are litres and durations seconds (D944); the card
|
|
53691
|
+
* shows millilitres and minutes. Actions are gated on `available`.
|
|
53692
|
+
*/
|
|
53693
|
+
var LITRES_TO_ML = 1e3;
|
|
53694
|
+
var SECONDS_PER_MINUTE = 60;
|
|
53695
|
+
var MAX_PUMP_MINUTES = 240;
|
|
53696
|
+
function millilitres(litres) {
|
|
53697
|
+
return litres === null ? "—" : `${Math.round(litres * LITRES_TO_ML)} mL`;
|
|
53698
|
+
}
|
|
53699
|
+
function minutesOf(seconds) {
|
|
53700
|
+
return seconds === null ? null : Math.round(seconds / SECONDS_PER_MINUTE);
|
|
53701
|
+
}
|
|
53702
|
+
function PetFountainPanel({ trpc, deviceId, layout, optimistic }) {
|
|
53703
|
+
const t = useUiText();
|
|
53704
|
+
const { cap, slice, available } = useDeviceCapability({
|
|
53705
|
+
trpc,
|
|
53706
|
+
deviceId,
|
|
53707
|
+
cap: (d) => d.petFountain,
|
|
53708
|
+
slice: (d) => d.state.petFountain
|
|
53709
|
+
});
|
|
53710
|
+
const { display, applyOptimistic } = useOptimisticSlice(slice, { enabled: optimistic ?? true });
|
|
53711
|
+
if (cap === void 0 || display === void 0) return null;
|
|
53712
|
+
const busy = !available;
|
|
53713
|
+
const fault = display.error !== null && display.error !== "no_error" ? display.error : null;
|
|
53714
|
+
const pumpOn = display.pumpRunning === true;
|
|
53715
|
+
const level = display.waterLevel;
|
|
53716
|
+
const setPump = (on) => {
|
|
53717
|
+
applyOptimistic({ pumpRunning: on });
|
|
53718
|
+
cap.setPump({ on });
|
|
53719
|
+
};
|
|
53720
|
+
if (layout === "compact") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53721
|
+
className: "inline-flex items-center gap-2",
|
|
53722
|
+
children: [
|
|
53723
|
+
(fault !== null || display.empty === true) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriangleAlert, {
|
|
53724
|
+
className: "h-3.5 w-3.5 text-danger",
|
|
53725
|
+
"aria-label": fault ?? t("uiLibrary.petFountain.empty", "Tank empty")
|
|
53726
|
+
}),
|
|
53727
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
53728
|
+
className: cn("inline-flex items-center gap-1 text-xs font-medium tabular-nums", display.empty === true ? "text-danger" : "text-foreground"),
|
|
53729
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Droplet, {
|
|
53730
|
+
className: "h-3.5 w-3.5",
|
|
53731
|
+
"aria-hidden": true
|
|
53732
|
+
}), level === null ? "—" : `${Math.round(level)}%`]
|
|
53733
|
+
}),
|
|
53734
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
53735
|
+
onClick: (e) => e.stopPropagation(),
|
|
53736
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Switch, {
|
|
53737
|
+
checked: pumpOn,
|
|
53738
|
+
disabled: busy,
|
|
53739
|
+
"aria-label": t("uiLibrary.petFountain.pump", "Pump"),
|
|
53740
|
+
onCheckedChange: setPump
|
|
53741
|
+
})
|
|
53742
|
+
})
|
|
53743
|
+
]
|
|
53744
|
+
});
|
|
53745
|
+
const intervalOn = display.pumpIntervalEnabled === true;
|
|
53746
|
+
const onMinutes = minutesOf(display.pumpOnTime);
|
|
53747
|
+
const offMinutes = minutesOf(display.pumpOffTime);
|
|
53748
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53749
|
+
className: "flex flex-col items-stretch gap-3 rounded-lg border border-border bg-surface p-4",
|
|
53750
|
+
children: [
|
|
53751
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53752
|
+
className: "flex items-center gap-4 rounded-lg border border-border-subtle bg-background-elevated p-3",
|
|
53753
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TankGauge, {
|
|
53754
|
+
level,
|
|
53755
|
+
empty: display.empty === true
|
|
53756
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53757
|
+
className: "flex min-w-0 flex-1 flex-col gap-1.5",
|
|
53758
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53759
|
+
className: "flex items-baseline gap-2",
|
|
53760
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
53761
|
+
className: cn("text-3xl font-bold leading-none tabular-nums", display.empty === true ? "text-danger" : "text-info"),
|
|
53762
|
+
children: level === null ? "—" : `${Math.round(level)}%`
|
|
53763
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
53764
|
+
className: "text-sm tabular-nums text-foreground-muted",
|
|
53765
|
+
children: millilitres(display.waterVolume)
|
|
53766
|
+
})]
|
|
53767
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusLine, {
|
|
53768
|
+
empty: display.empty,
|
|
53769
|
+
pumpRunning: display.pumpRunning
|
|
53770
|
+
})]
|
|
53771
|
+
})]
|
|
53772
|
+
}),
|
|
53773
|
+
fault !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53774
|
+
className: "flex items-start gap-2 rounded-lg border border-danger/40 bg-danger/10 px-3 py-2 text-xs font-medium text-danger",
|
|
53775
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriangleAlert, {
|
|
53776
|
+
className: "mt-px h-4 w-4 shrink-0",
|
|
53777
|
+
"aria-hidden": true
|
|
53778
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
53779
|
+
className: "min-w-0 break-words",
|
|
53780
|
+
children: fault
|
|
53781
|
+
})]
|
|
53782
|
+
}),
|
|
53783
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Section$1, {
|
|
53784
|
+
label: t("uiLibrary.petFountain.consumption", "Water drunk"),
|
|
53785
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53786
|
+
className: "grid grid-cols-2 gap-2 sm:grid-cols-4",
|
|
53787
|
+
children: [
|
|
53788
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
53789
|
+
label: t("uiLibrary.petFountain.today", "Today"),
|
|
53790
|
+
value: display.todayDrink
|
|
53791
|
+
}),
|
|
53792
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
53793
|
+
label: t("uiLibrary.petFountain.last24h", "Last 24 h"),
|
|
53794
|
+
value: display.last24hDrink
|
|
53795
|
+
}),
|
|
53796
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
53797
|
+
label: t("uiLibrary.petFountain.thisHour", "This hour"),
|
|
53798
|
+
value: display.currentHourDrink
|
|
53799
|
+
}),
|
|
53800
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
53801
|
+
label: t("uiLibrary.petFountain.lastDrink", "Last drink"),
|
|
53802
|
+
value: display.lastDrink
|
|
53803
|
+
})
|
|
53804
|
+
]
|
|
53805
|
+
})
|
|
53806
|
+
}),
|
|
53807
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Section$1, {
|
|
53808
|
+
label: t("uiLibrary.petFountain.pumpSection", "Controls"),
|
|
53809
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53810
|
+
className: "flex flex-col gap-2",
|
|
53811
|
+
children: [
|
|
53812
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SwitchRow, {
|
|
53813
|
+
icon: pumpOn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Play, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pause, { className: "h-4 w-4" }),
|
|
53814
|
+
label: t("uiLibrary.petFountain.pump", "Pump"),
|
|
53815
|
+
checked: pumpOn,
|
|
53816
|
+
disabled: busy,
|
|
53817
|
+
onChange: setPump
|
|
53818
|
+
}),
|
|
53819
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SwitchRow, {
|
|
53820
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Timer, { className: "h-4 w-4" }),
|
|
53821
|
+
label: t("uiLibrary.petFountain.cycle", "On / off cycle"),
|
|
53822
|
+
checked: intervalOn,
|
|
53823
|
+
disabled: busy,
|
|
53824
|
+
onChange: (on) => {
|
|
53825
|
+
applyOptimistic({ pumpIntervalEnabled: on });
|
|
53826
|
+
cap.setPumpInterval({ on });
|
|
53827
|
+
}
|
|
53828
|
+
}),
|
|
53829
|
+
intervalOn && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53830
|
+
className: "flex flex-col gap-2",
|
|
53831
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MinutesStepper, {
|
|
53832
|
+
label: t("uiLibrary.petFountain.runMinutes", "Runs for"),
|
|
53833
|
+
minutes: onMinutes,
|
|
53834
|
+
disabled: busy,
|
|
53835
|
+
onChange: (minutes) => {
|
|
53836
|
+
applyOptimistic({ pumpOnTime: minutes * SECONDS_PER_MINUTE });
|
|
53837
|
+
cap.setPumpOnMinutes({ minutes });
|
|
53838
|
+
}
|
|
53839
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MinutesStepper, {
|
|
53840
|
+
label: t("uiLibrary.petFountain.restMinutes", "Rests for"),
|
|
53841
|
+
minutes: offMinutes,
|
|
53842
|
+
disabled: busy,
|
|
53843
|
+
onChange: (minutes) => {
|
|
53844
|
+
applyOptimistic({ pumpOffTime: minutes * SECONDS_PER_MINUTE });
|
|
53845
|
+
cap.setPumpOffMinutes({ minutes });
|
|
53846
|
+
}
|
|
53847
|
+
})]
|
|
53848
|
+
})
|
|
53849
|
+
]
|
|
53850
|
+
})
|
|
53851
|
+
}),
|
|
53852
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Button, {
|
|
53853
|
+
variant: "outline",
|
|
53854
|
+
size: "sm",
|
|
53855
|
+
disabled: busy,
|
|
53856
|
+
onClick: () => void cap.resetDailyConsumption({}),
|
|
53857
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Eraser, {
|
|
53858
|
+
className: "h-4 w-4",
|
|
53859
|
+
"aria-hidden": true
|
|
53860
|
+
}), t("uiLibrary.petFountain.clearDaily", "Clear daily count")]
|
|
53861
|
+
})
|
|
53862
|
+
]
|
|
53863
|
+
});
|
|
53864
|
+
}
|
|
53865
|
+
/** A tank outline whose water rises with the level; danger-coloured when empty. */
|
|
53866
|
+
function TankGauge({ level, empty }) {
|
|
53867
|
+
const t = useUiText();
|
|
53868
|
+
const top = 58 - Math.min(1, Math.max(0, (level ?? 0) / 100)) * 40;
|
|
53869
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
53870
|
+
width: 72,
|
|
53871
|
+
height: 72,
|
|
53872
|
+
viewBox: "0 0 72 72",
|
|
53873
|
+
role: "img",
|
|
53874
|
+
"aria-label": level === null ? t("uiLibrary.petFountain.levelUnknown", "Water level unknown") : t("uiLibrary.petFountain.levelPercent", "{{percent}}% water", { percent: Math.round(level) }),
|
|
53875
|
+
className: "shrink-0",
|
|
53876
|
+
children: [
|
|
53877
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("clipPath", {
|
|
53878
|
+
id: "pfn-tank-clip",
|
|
53879
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
53880
|
+
x: "18",
|
|
53881
|
+
y: "14",
|
|
53882
|
+
width: "36",
|
|
53883
|
+
height: "46",
|
|
53884
|
+
rx: "6"
|
|
53885
|
+
})
|
|
53886
|
+
}) }),
|
|
53887
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", {
|
|
53888
|
+
clipPath: "url(#pfn-tank-clip)",
|
|
53889
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
53890
|
+
x: "18",
|
|
53891
|
+
y: top,
|
|
53892
|
+
width: "36",
|
|
53893
|
+
height: "46",
|
|
53894
|
+
className: empty ? "fill-danger" : "fill-info",
|
|
53895
|
+
opacity: .75
|
|
53896
|
+
})
|
|
53897
|
+
}),
|
|
53898
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
53899
|
+
x: "18",
|
|
53900
|
+
y: "14",
|
|
53901
|
+
width: "36",
|
|
53902
|
+
height: "46",
|
|
53903
|
+
rx: "6",
|
|
53904
|
+
fill: "none",
|
|
53905
|
+
strokeWidth: "2.5",
|
|
53906
|
+
className: "stroke-border"
|
|
53907
|
+
}),
|
|
53908
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
53909
|
+
x: "28",
|
|
53910
|
+
y: "8",
|
|
53911
|
+
width: "16",
|
|
53912
|
+
height: "6",
|
|
53913
|
+
rx: "2",
|
|
53914
|
+
className: "fill-border"
|
|
53915
|
+
})
|
|
53916
|
+
]
|
|
53917
|
+
});
|
|
53918
|
+
}
|
|
53919
|
+
function StatusLine({ empty, pumpRunning }) {
|
|
53920
|
+
const t = useUiText();
|
|
53921
|
+
if (empty === true) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
53922
|
+
className: "inline-flex items-center gap-1 text-xs font-medium text-danger",
|
|
53923
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Droplet, {
|
|
53924
|
+
className: "h-3.5 w-3.5",
|
|
53925
|
+
"aria-hidden": true
|
|
53926
|
+
}), t("uiLibrary.petFountain.empty", "Tank empty")]
|
|
53927
|
+
});
|
|
53928
|
+
if (pumpRunning === true) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
53929
|
+
className: "inline-flex items-center gap-1 text-xs font-medium text-info",
|
|
53930
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Droplets, {
|
|
53931
|
+
className: "h-3.5 w-3.5",
|
|
53932
|
+
"aria-hidden": true
|
|
53933
|
+
}), t("uiLibrary.petFountain.pumpRunning", "Pump running")]
|
|
53934
|
+
});
|
|
53935
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
53936
|
+
className: "inline-flex items-center gap-1 text-xs text-foreground-subtle",
|
|
53937
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pause, {
|
|
53938
|
+
className: "h-3.5 w-3.5",
|
|
53939
|
+
"aria-hidden": true
|
|
53940
|
+
}), pumpRunning === false ? t("uiLibrary.petFountain.pumpIdle", "Pump idle") : t("uiLibrary.petFountain.pumpUnknown", "Pump state unknown")]
|
|
53941
|
+
});
|
|
53942
|
+
}
|
|
53943
|
+
function Section$1({ label, children }) {
|
|
53944
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53945
|
+
className: "flex flex-col gap-1",
|
|
53946
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
53947
|
+
className: "text-[10px] font-semibold uppercase tracking-wider text-foreground-subtle",
|
|
53948
|
+
children: label
|
|
53949
|
+
}), children]
|
|
53950
|
+
});
|
|
53951
|
+
}
|
|
53952
|
+
function Stat({ label, value }) {
|
|
53953
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53954
|
+
className: "flex flex-col rounded-lg border border-border-subtle bg-background-elevated px-3 py-2",
|
|
53955
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
53956
|
+
className: "text-[11px] text-foreground-subtle",
|
|
53957
|
+
children: label
|
|
53958
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
53959
|
+
className: "text-sm font-semibold tabular-nums text-foreground",
|
|
53960
|
+
children: millilitres(value)
|
|
53961
|
+
})]
|
|
53962
|
+
});
|
|
53963
|
+
}
|
|
53964
|
+
function SwitchRow({ icon, label, checked, disabled, onChange }) {
|
|
53965
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53966
|
+
className: "flex items-center justify-between gap-3 rounded-lg border border-border-subtle bg-background-elevated px-3 py-2",
|
|
53967
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
53968
|
+
className: "inline-flex items-center gap-2 text-sm text-foreground",
|
|
53969
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
53970
|
+
className: "text-foreground-subtle",
|
|
53971
|
+
children: icon
|
|
53972
|
+
}), label]
|
|
53973
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Switch, {
|
|
53974
|
+
checked,
|
|
53975
|
+
disabled,
|
|
53976
|
+
"aria-label": label,
|
|
53977
|
+
onCheckedChange: onChange
|
|
53978
|
+
})]
|
|
53979
|
+
});
|
|
53980
|
+
}
|
|
53981
|
+
function MinutesStepper({ label, minutes, disabled, onChange }) {
|
|
53982
|
+
const t = useUiText();
|
|
53983
|
+
const current = minutes ?? 0;
|
|
53984
|
+
const step = (delta) => onChange(Math.min(MAX_PUMP_MINUTES, Math.max(0, current + delta)));
|
|
53985
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53986
|
+
className: "flex items-center justify-between gap-2 rounded-lg border border-border-subtle bg-background-elevated px-3 py-2",
|
|
53987
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
53988
|
+
className: "text-sm text-foreground",
|
|
53989
|
+
children: label
|
|
53990
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53991
|
+
className: "flex items-center gap-2",
|
|
53992
|
+
children: [
|
|
53993
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
53994
|
+
variant: "outline",
|
|
53995
|
+
size: "sm",
|
|
53996
|
+
"aria-label": t("uiLibrary.petFountain.decrease", "Decrease {{label}}", { label }),
|
|
53997
|
+
disabled: disabled || minutes === null || current <= 0,
|
|
53998
|
+
onClick: () => step(-1),
|
|
53999
|
+
children: "−"
|
|
54000
|
+
}),
|
|
54001
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
54002
|
+
className: "w-14 text-center text-sm font-semibold tabular-nums text-foreground",
|
|
54003
|
+
children: minutes === null ? "—" : t("uiLibrary.petFountain.minutes", "{{count}} min", { count: current })
|
|
54004
|
+
}),
|
|
54005
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
54006
|
+
variant: "outline",
|
|
54007
|
+
size: "sm",
|
|
54008
|
+
"aria-label": t("uiLibrary.petFountain.increase", "Increase {{label}}", { label }),
|
|
54009
|
+
disabled: disabled || minutes === null || current >= MAX_PUMP_MINUTES,
|
|
54010
|
+
onClick: () => step(1),
|
|
54011
|
+
children: "+"
|
|
54012
|
+
})
|
|
54013
|
+
]
|
|
54014
|
+
})]
|
|
54015
|
+
});
|
|
54016
|
+
}
|
|
54017
|
+
function PetFountainInlineControl({ trpc, deviceId, features, optimistic }) {
|
|
54018
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PetFountainPanel, {
|
|
54019
|
+
trpc,
|
|
54020
|
+
deviceId,
|
|
54021
|
+
features,
|
|
54022
|
+
layout: "compact",
|
|
54023
|
+
optimistic
|
|
54024
|
+
});
|
|
54025
|
+
}
|
|
54026
|
+
function PetFountainHeroCard({ trpc, deviceId, features, optimistic }) {
|
|
54027
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PetFountainPanel, {
|
|
54028
|
+
trpc,
|
|
54029
|
+
deviceId,
|
|
54030
|
+
features,
|
|
54031
|
+
layout: "full",
|
|
54032
|
+
optimistic
|
|
54033
|
+
});
|
|
54034
|
+
}
|
|
54035
|
+
//#endregion
|
|
53628
54036
|
//#region src/composites/device-controls/dishwasher-control.tsx
|
|
53629
54037
|
/**
|
|
53630
54038
|
* Dishwasher controls for a `DeviceType.Dishwasher` device.
|
|
@@ -56747,6 +57155,10 @@ var CONTROL_KIND_COMPONENTS = {
|
|
|
56747
57155
|
InlineControl: PetFeederInlineControl,
|
|
56748
57156
|
HeroCard: PetFeederHeroCard
|
|
56749
57157
|
},
|
|
57158
|
+
"pet-fountain": {
|
|
57159
|
+
InlineControl: PetFountainInlineControl,
|
|
57160
|
+
HeroCard: PetFountainHeroCard
|
|
57161
|
+
},
|
|
56750
57162
|
dishwasher: {
|
|
56751
57163
|
InlineControl: DishwasherInlineControl,
|
|
56752
57164
|
HeroCard: DishwasherHeroCard
|
|
@@ -56828,6 +57240,7 @@ var CONTROL_FILLS = {
|
|
|
56828
57240
|
[_camstack_types.DeviceType.Container]: false,
|
|
56829
57241
|
[_camstack_types.DeviceType.Image]: false,
|
|
56830
57242
|
[_camstack_types.DeviceType.PetFeeder]: false,
|
|
57243
|
+
[_camstack_types.DeviceType.PetFountain]: false,
|
|
56831
57244
|
[_camstack_types.DeviceType.Dishwasher]: false,
|
|
56832
57245
|
[_camstack_types.DeviceType.Washer]: false,
|
|
56833
57246
|
[_camstack_types.DeviceType.Dryer]: false
|
|
@@ -60835,6 +61248,88 @@ function deselectShown(current, shown) {
|
|
|
60835
61248
|
return new Set([...current].filter((id) => !drop.has(id)));
|
|
60836
61249
|
}
|
|
60837
61250
|
//#endregion
|
|
61251
|
+
//#region src/composites/export-entity-rows.ts
|
|
61252
|
+
/**
|
|
61253
|
+
* The action and notification-target rows of the device export table (D968).
|
|
61254
|
+
*
|
|
61255
|
+
* An action and a notifier are list entries, never devices (D954): they are
|
|
61256
|
+
* merged into the SAME table as extra rows, built only from the kinds the
|
|
61257
|
+
* exporter answered. A kinds list that was not answered is UNKNOWN, not
|
|
61258
|
+
* "supports nothing" (D315) — it yields devices only and no filter entry, and
|
|
61259
|
+
* says nothing about what the exporter cannot do.
|
|
61260
|
+
*
|
|
61261
|
+
* Pure functions, so the rules are pinned without a DOM.
|
|
61262
|
+
*/
|
|
61263
|
+
/** The type-filter ids of the entity kinds, apart from every device type. */
|
|
61264
|
+
var ENTITY_FILTER_IDS = {
|
|
61265
|
+
action: "entity:action",
|
|
61266
|
+
notifier: "entity:notifier"
|
|
61267
|
+
};
|
|
61268
|
+
var KindsSchema = zod.z.array(_camstack_types.ExportEntityKindSchema);
|
|
61269
|
+
var NamedSchema = zod.z.array(zod.z.object({
|
|
61270
|
+
id: zod.z.string(),
|
|
61271
|
+
name: zod.z.string()
|
|
61272
|
+
}));
|
|
61273
|
+
var ExposedSchema = zod.z.array(_camstack_types.ExposedEntitySchema);
|
|
61274
|
+
/**
|
|
61275
|
+
* The non-device kinds the exporter answered, or null when it did not answer
|
|
61276
|
+
* (unknown — never an empty set).
|
|
61277
|
+
*/
|
|
61278
|
+
function supportedEntityKinds(data) {
|
|
61279
|
+
const parsed = KindsSchema.safeParse(data);
|
|
61280
|
+
if (!parsed.success) return null;
|
|
61281
|
+
const kinds = /* @__PURE__ */ new Set();
|
|
61282
|
+
for (const kind of parsed.data) if (kind === "action" || kind === "notifier") kinds.add(kind);
|
|
61283
|
+
return kinds;
|
|
61284
|
+
}
|
|
61285
|
+
function buildEntityRows(input) {
|
|
61286
|
+
const { kinds } = input;
|
|
61287
|
+
if (kinds === null) return [];
|
|
61288
|
+
const exposed = ExposedSchema.safeParse(input.exposed);
|
|
61289
|
+
const exposedActions = /* @__PURE__ */ new Set();
|
|
61290
|
+
const exposedTargets = /* @__PURE__ */ new Set();
|
|
61291
|
+
if (exposed.success) {
|
|
61292
|
+
for (const row of exposed.data) if (row.entity.kind === "action") exposedActions.add(row.entity.actionId);
|
|
61293
|
+
else if (row.entity.kind === "notifier") exposedTargets.add(row.entity.targetId);
|
|
61294
|
+
}
|
|
61295
|
+
const rows = [];
|
|
61296
|
+
if (kinds.has("action")) {
|
|
61297
|
+
const actions = NamedSchema.safeParse(input.actions);
|
|
61298
|
+
for (const a of actions.success ? actions.data : []) rows.push({
|
|
61299
|
+
kind: "action",
|
|
61300
|
+
key: `action:${a.id}`,
|
|
61301
|
+
ref: {
|
|
61302
|
+
kind: "action",
|
|
61303
|
+
actionId: a.id
|
|
61304
|
+
},
|
|
61305
|
+
name: a.name,
|
|
61306
|
+
exposed: exposedActions.has(a.id)
|
|
61307
|
+
});
|
|
61308
|
+
}
|
|
61309
|
+
if (kinds.has("notifier")) {
|
|
61310
|
+
const targets = NamedSchema.safeParse(input.targets);
|
|
61311
|
+
for (const t of targets.success ? targets.data : []) rows.push({
|
|
61312
|
+
kind: "notifier",
|
|
61313
|
+
key: `notifier:${t.id}`,
|
|
61314
|
+
ref: {
|
|
61315
|
+
kind: "notifier",
|
|
61316
|
+
targetId: t.id
|
|
61317
|
+
},
|
|
61318
|
+
name: t.name,
|
|
61319
|
+
exposed: exposedTargets.has(t.id)
|
|
61320
|
+
});
|
|
61321
|
+
}
|
|
61322
|
+
return rows;
|
|
61323
|
+
}
|
|
61324
|
+
/** The type-filter ids to add beside the device types: one per SUPPORTED kind. */
|
|
61325
|
+
function entityFilterOptionIds(kinds) {
|
|
61326
|
+
if (kinds === null) return [];
|
|
61327
|
+
const ids = [];
|
|
61328
|
+
if (kinds.has("action")) ids.push(ENTITY_FILTER_IDS.action);
|
|
61329
|
+
if (kinds.has("notifier")) ids.push(ENTITY_FILTER_IDS.notifier);
|
|
61330
|
+
return ids;
|
|
61331
|
+
}
|
|
61332
|
+
//#endregion
|
|
60838
61333
|
//#region src/composites/device-export-panel.tsx
|
|
60839
61334
|
/**
|
|
60840
61335
|
* DeviceExportPanel — generic, addon-agnostic surface for the
|
|
@@ -61054,19 +61549,28 @@ function ExportSelectCell({ device, checked, onToggle }) {
|
|
|
61054
61549
|
* of these live in one table, and "Expose" alone names none of them.
|
|
61055
61550
|
*/
|
|
61056
61551
|
function ExportRowToggle({ addonId, device, exposed, busy, onToggle }) {
|
|
61552
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExposeToggleButton, {
|
|
61553
|
+
addonId,
|
|
61554
|
+
label: useDeviceLabel(device, "compact"),
|
|
61555
|
+
exposed,
|
|
61556
|
+
busy,
|
|
61557
|
+
onToggle: (action) => onToggle(device, action)
|
|
61558
|
+
});
|
|
61559
|
+
}
|
|
61560
|
+
/** The Expose / Exposed button every row kind (device, action, notifier) shares. */
|
|
61561
|
+
function ExposeToggleButton({ addonId, label, exposed, busy, onToggle }) {
|
|
61057
61562
|
const t = useUiText();
|
|
61058
|
-
const deviceLabel = useDeviceLabel(device, "compact");
|
|
61059
61563
|
const handleClick = (e) => {
|
|
61060
61564
|
e.stopPropagation();
|
|
61061
61565
|
if (busy) return;
|
|
61062
|
-
onToggle(
|
|
61566
|
+
onToggle(exposed ? "unexpose" : "expose");
|
|
61063
61567
|
};
|
|
61064
61568
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
61065
61569
|
size: "sm",
|
|
61066
61570
|
variant: exposed ? "ghost" : "primary",
|
|
61067
61571
|
disabled: busy,
|
|
61068
61572
|
onClick: handleClick,
|
|
61069
|
-
"aria-label": exposed ? t("uiLibrary.deviceExport.unexposeDevice", "Unexpose {{name}}", { name:
|
|
61573
|
+
"aria-label": exposed ? t("uiLibrary.deviceExport.unexposeDevice", "Unexpose {{name}}", { name: label }) : t("uiLibrary.deviceExport.exposeDevice", "Expose {{name}}", { name: label }),
|
|
61070
61574
|
title: exposed ? t("uiLibrary.deviceExport.stopTitle", "Stop exposing to \"{{addon}}\"", { addon: addonId }) : t("uiLibrary.deviceExport.exposeTitle", "Expose to \"{{addon}}\"", { addon: addonId }),
|
|
61071
61575
|
children: exposed ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Unlink2, { className: "mr-1 h-3.5 w-3.5" }), t("uiLibrary.deviceExport.exposed", "Exposed")] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Share2, { className: "mr-1 h-3.5 w-3.5" }), t("uiLibrary.deviceExport.expose", "Expose")] })
|
|
61072
61576
|
});
|
|
@@ -61147,11 +61651,34 @@ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
|
|
|
61147
61651
|
addonId
|
|
61148
61652
|
}, { retry: false });
|
|
61149
61653
|
const devicesQuery = useDeviceManagerListAll({ projection: "slim" }, { refetchInterval: FLEET_POLL_INTERVAL_MS });
|
|
61654
|
+
const entityKindsQuery = useDeviceExportListSupportedEntityKinds({
|
|
61655
|
+
nodeId: "hub",
|
|
61656
|
+
addonId
|
|
61657
|
+
}, { retry: false });
|
|
61658
|
+
const entityKinds = (0, react$1.useMemo)(() => supportedEntityKinds(entityKindsQuery.data), [entityKindsQuery.data]);
|
|
61659
|
+
const exposedEntitiesQuery = useDeviceExportListExposedEntities({
|
|
61660
|
+
nodeId: "hub",
|
|
61661
|
+
addonId
|
|
61662
|
+
}, {
|
|
61663
|
+
refetchInterval: STATUS_POLL_INTERVAL_MS,
|
|
61664
|
+
retry: false,
|
|
61665
|
+
enabled: entityKinds !== null
|
|
61666
|
+
});
|
|
61667
|
+
const actionsQuery = useActionsList({}, {
|
|
61668
|
+
refetchInterval: FLEET_POLL_INTERVAL_MS,
|
|
61669
|
+
enabled: entityKinds?.has("action") === true
|
|
61670
|
+
});
|
|
61671
|
+
const targetsQuery = useNotificationOutputListTargets({}, {
|
|
61672
|
+
refetchInterval: FLEET_POLL_INTERVAL_MS,
|
|
61673
|
+
enabled: entityKinds?.has("notifier") === true
|
|
61674
|
+
});
|
|
61150
61675
|
const invalidate = (0, react$1.useCallback)(() => {
|
|
61151
61676
|
queryClient.invalidateQueries({ queryKey: [["deviceExport"]] });
|
|
61152
61677
|
}, [queryClient]);
|
|
61153
61678
|
const exposeMutation = useDeviceExportExposeDevice({ onSuccess: invalidate });
|
|
61154
61679
|
const unexposeMutation = useDeviceExportUnexposeDevice({ onSuccess: invalidate });
|
|
61680
|
+
const exposeEntityMutation = useDeviceExportExposeEntity({ onSuccess: invalidate });
|
|
61681
|
+
const unexposeEntityMutation = useDeviceExportUnexposeEntity({ onSuccess: invalidate });
|
|
61155
61682
|
const status = (0, react$1.useMemo)(() => parseStatus(statusQuery.data), [statusQuery.data]);
|
|
61156
61683
|
const exposed = (0, react$1.useMemo)(() => {
|
|
61157
61684
|
const parsed = ExposedDeviceArraySchema.safeParse(exposedQuery.data);
|
|
@@ -61181,10 +61708,37 @@ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
|
|
|
61181
61708
|
}
|
|
61182
61709
|
return rows;
|
|
61183
61710
|
}, [devicesQuery.data, supportedKinds]);
|
|
61711
|
+
const entityRows = (0, react$1.useMemo)(() => buildEntityRows({
|
|
61712
|
+
kinds: entityKinds,
|
|
61713
|
+
actions: actionsQuery.data,
|
|
61714
|
+
targets: targetsQuery.data,
|
|
61715
|
+
exposed: exposedEntitiesQuery.data
|
|
61716
|
+
}), [
|
|
61717
|
+
entityKinds,
|
|
61718
|
+
actionsQuery.data,
|
|
61719
|
+
targetsQuery.data,
|
|
61720
|
+
exposedEntitiesQuery.data
|
|
61721
|
+
]);
|
|
61722
|
+
const typeFilterOptions = (0, react$1.useMemo)(() => {
|
|
61723
|
+
const entityOptions = entityFilterOptionIds(entityKinds).map((id) => id === ENTITY_FILTER_IDS.action ? {
|
|
61724
|
+
id,
|
|
61725
|
+
label: t("uiLibrary.deviceExport.typeAction", "Action"),
|
|
61726
|
+
icon: Play
|
|
61727
|
+
} : {
|
|
61728
|
+
id,
|
|
61729
|
+
label: t("uiLibrary.deviceExport.typeNotifier", "Notifier"),
|
|
61730
|
+
icon: Bell
|
|
61731
|
+
});
|
|
61732
|
+
return [...supportedTypeOptions, ...entityOptions];
|
|
61733
|
+
}, [
|
|
61734
|
+
supportedTypeOptions,
|
|
61735
|
+
entityKinds,
|
|
61736
|
+
t
|
|
61737
|
+
]);
|
|
61184
61738
|
const linkState = status?.linkState ?? "unlinked";
|
|
61185
|
-
const loading = statusQuery.isLoading || exposedQuery.isLoading || kindsQuery.isLoading || devicesQuery.isLoading;
|
|
61739
|
+
const loading = statusQuery.isLoading || exposedQuery.isLoading || kindsQuery.isLoading || devicesQuery.isLoading || entityKindsQuery.isLoading;
|
|
61186
61740
|
const exposedCount = status?.exposedDeviceCount ?? exposedIds.size;
|
|
61187
|
-
const busy = exposeMutation.isPending || unexposeMutation.isPending;
|
|
61741
|
+
const busy = exposeMutation.isPending || unexposeMutation.isPending || exposeEntityMutation.isPending || unexposeEntityMutation.isPending;
|
|
61188
61742
|
const [selectedIds, setSelectedIds] = (0, react$1.useState)(() => /* @__PURE__ */ new Set());
|
|
61189
61743
|
const [outcome, setOutcome] = (0, react$1.useState)(null);
|
|
61190
61744
|
const [running, setRunning] = (0, react$1.useState)(false);
|
|
@@ -61251,10 +61805,71 @@ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
|
|
|
61251
61805
|
labelFor,
|
|
61252
61806
|
running
|
|
61253
61807
|
]);
|
|
61808
|
+
/**
|
|
61809
|
+
* Expose / unexpose ONE action or notification target. Same contract as the
|
|
61810
|
+
* device runner: the result is recorded either way and a refusal is named on
|
|
61811
|
+
* screen, with the entity (it has no device id to tag).
|
|
61812
|
+
*/
|
|
61813
|
+
const runEntityExport = (0, react$1.useCallback)(async (action, row) => {
|
|
61814
|
+
if (running) return;
|
|
61815
|
+
setRunning(true);
|
|
61816
|
+
setOutcome(null);
|
|
61817
|
+
const mutation = action === "expose" ? exposeEntityMutation : unexposeEntityMutation;
|
|
61818
|
+
const succeeded = [];
|
|
61819
|
+
const failed = [];
|
|
61820
|
+
try {
|
|
61821
|
+
await mutation.mutateAsync({
|
|
61822
|
+
entity: row.ref,
|
|
61823
|
+
nodeId: "hub",
|
|
61824
|
+
addonId
|
|
61825
|
+
});
|
|
61826
|
+
succeeded.push(row.name);
|
|
61827
|
+
} catch (err) {
|
|
61828
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
61829
|
+
console.error(`[device-export] ${action} ${row.kind} failed`, {
|
|
61830
|
+
tags: row.ref.kind === "action" ? { actionId: row.ref.actionId } : { targetId: row.ref.targetId },
|
|
61831
|
+
addonId,
|
|
61832
|
+
error: message
|
|
61833
|
+
});
|
|
61834
|
+
failed.push({
|
|
61835
|
+
label: row.name,
|
|
61836
|
+
message
|
|
61837
|
+
});
|
|
61838
|
+
}
|
|
61839
|
+
setOutcome({
|
|
61840
|
+
action,
|
|
61841
|
+
total: 1,
|
|
61842
|
+
succeeded,
|
|
61843
|
+
failed
|
|
61844
|
+
});
|
|
61845
|
+
setRunning(false);
|
|
61846
|
+
}, [
|
|
61847
|
+
addonId,
|
|
61848
|
+
exposeEntityMutation,
|
|
61849
|
+
unexposeEntityMutation,
|
|
61850
|
+
running
|
|
61851
|
+
]);
|
|
61254
61852
|
const [bulkFilters, setBulkFilters] = (0, react$1.useState)(NO_EXPORT_FILTERS);
|
|
61255
61853
|
const roleOptions = (0, react$1.useMemo)(() => roleFilterOptions(candidateDevices), [candidateDevices]);
|
|
61256
61854
|
const providerOptions = (0, react$1.useMemo)(() => providerFilterOptions(candidateDevices), [candidateDevices]);
|
|
61257
61855
|
const shownDevices = (0, react$1.useMemo)(() => applyExportFilters(candidateDevices, bulkFilters), [candidateDevices, bulkFilters]);
|
|
61856
|
+
const shownRows = (0, react$1.useMemo)(() => {
|
|
61857
|
+
const deviceRows = shownDevices.map((device) => ({
|
|
61858
|
+
kind: "device",
|
|
61859
|
+
key: String(device.id),
|
|
61860
|
+
device
|
|
61861
|
+
}));
|
|
61862
|
+
const entities = bulkFilters.roles.length > 0 || bulkFilters.providers.length > 0 ? [] : entityRows.map((entity) => ({
|
|
61863
|
+
kind: "entity",
|
|
61864
|
+
key: entity.key,
|
|
61865
|
+
entity
|
|
61866
|
+
}));
|
|
61867
|
+
return [...deviceRows, ...entities];
|
|
61868
|
+
}, [
|
|
61869
|
+
shownDevices,
|
|
61870
|
+
entityRows,
|
|
61871
|
+
bulkFilters
|
|
61872
|
+
]);
|
|
61258
61873
|
const setRoleFilter = (0, react$1.useCallback)((roles) => setBulkFilters((f) => ({
|
|
61259
61874
|
...f,
|
|
61260
61875
|
roles
|
|
@@ -61273,27 +61888,35 @@ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
|
|
|
61273
61888
|
const onRowToggle = (0, react$1.useCallback)((device, action) => {
|
|
61274
61889
|
runExport(action, [String(device.id)]);
|
|
61275
61890
|
}, [runExport]);
|
|
61276
|
-
const onRowClick = (0, react$1.useCallback)((
|
|
61277
|
-
|
|
61278
|
-
|
|
61279
|
-
const
|
|
61891
|
+
const onRowClick = (0, react$1.useCallback)((row) => {
|
|
61892
|
+
if (row.kind === "device") onOpenDevice?.(String(row.device.id));
|
|
61893
|
+
}, [onOpenDevice]);
|
|
61894
|
+
const getKey = (0, react$1.useCallback)((row) => row.key, []);
|
|
61895
|
+
const getSearchText = (0, react$1.useCallback)((row) => row.kind === "device" ? `${row.device.name} ${row.device.stableId}` : row.entity.name, []);
|
|
61896
|
+
const getFilterType = (0, react$1.useCallback)((row) => row.kind === "device" ? row.device.type : ENTITY_FILTER_IDS[row.entity.kind], []);
|
|
61280
61897
|
const columns = (0, react$1.useMemo)(() => [
|
|
61281
61898
|
{
|
|
61282
61899
|
key: "select",
|
|
61283
61900
|
header: "",
|
|
61284
61901
|
headerClassName: "w-8",
|
|
61285
|
-
cell: (
|
|
61286
|
-
device,
|
|
61287
|
-
checked: selectedIds.has(String(device.id)),
|
|
61288
|
-
onToggle: () => toggleSelection(String(device.id))
|
|
61289
|
-
})
|
|
61902
|
+
cell: (row) => row.kind === "device" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExportSelectCell, {
|
|
61903
|
+
device: row.device,
|
|
61904
|
+
checked: selectedIds.has(String(row.device.id)),
|
|
61905
|
+
onToggle: () => toggleSelection(String(row.device.id))
|
|
61906
|
+
}) : null
|
|
61290
61907
|
},
|
|
61291
61908
|
{
|
|
61292
61909
|
key: "type",
|
|
61293
61910
|
header: t("uiLibrary.deviceExport.colType", "Type"),
|
|
61294
61911
|
headerClassName: "w-32",
|
|
61295
|
-
cell: (
|
|
61296
|
-
const meta = deviceTypeMetaOf(device.type, t)
|
|
61912
|
+
cell: (row) => {
|
|
61913
|
+
const meta = row.kind === "device" ? deviceTypeMetaOf(row.device.type, t) : row.entity.kind === "action" ? {
|
|
61914
|
+
label: t("uiLibrary.deviceExport.typeAction", "Action"),
|
|
61915
|
+
icon: Play
|
|
61916
|
+
} : {
|
|
61917
|
+
label: t("uiLibrary.deviceExport.typeNotifier", "Notifier"),
|
|
61918
|
+
icon: Bell
|
|
61919
|
+
};
|
|
61297
61920
|
const TypeIcon = meta.icon;
|
|
61298
61921
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
61299
61922
|
className: "inline-flex items-center gap-1.5 text-[11px] text-foreground-subtle",
|
|
@@ -61304,10 +61927,13 @@ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
|
|
|
61304
61927
|
{
|
|
61305
61928
|
key: "name",
|
|
61306
61929
|
header: t("uiLibrary.deviceExport.colName", "Name"),
|
|
61307
|
-
cell: (
|
|
61308
|
-
device,
|
|
61930
|
+
cell: (row) => row.kind === "device" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeviceName, {
|
|
61931
|
+
device: row.device,
|
|
61309
61932
|
form: "compact",
|
|
61310
61933
|
className: "text-xs font-medium text-foreground"
|
|
61934
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
61935
|
+
className: "text-xs font-medium text-foreground",
|
|
61936
|
+
children: row.entity.name
|
|
61311
61937
|
})
|
|
61312
61938
|
},
|
|
61313
61939
|
{
|
|
@@ -61315,12 +61941,18 @@ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
|
|
|
61315
61941
|
header: t("uiLibrary.deviceExport.colExport", "Export"),
|
|
61316
61942
|
headerClassName: "w-32 text-right",
|
|
61317
61943
|
cellClassName: "text-right",
|
|
61318
|
-
cell: (
|
|
61944
|
+
cell: (row) => row.kind === "device" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExportRowToggle, {
|
|
61319
61945
|
addonId,
|
|
61320
|
-
device,
|
|
61321
|
-
exposed: exposedIds.has(String(device.id)),
|
|
61946
|
+
device: row.device,
|
|
61947
|
+
exposed: exposedIds.has(String(row.device.id)),
|
|
61322
61948
|
busy,
|
|
61323
61949
|
onToggle: onRowToggle
|
|
61950
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExposeToggleButton, {
|
|
61951
|
+
addonId,
|
|
61952
|
+
label: row.entity.name,
|
|
61953
|
+
exposed: row.entity.exposed,
|
|
61954
|
+
busy,
|
|
61955
|
+
onToggle: (action) => void runEntityExport(action, row.entity)
|
|
61324
61956
|
})
|
|
61325
61957
|
}
|
|
61326
61958
|
], [
|
|
@@ -61328,67 +61960,71 @@ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
|
|
|
61328
61960
|
busy,
|
|
61329
61961
|
exposedIds,
|
|
61330
61962
|
onRowToggle,
|
|
61963
|
+
runEntityExport,
|
|
61331
61964
|
selectedIds,
|
|
61332
61965
|
toggleSelection,
|
|
61333
61966
|
t
|
|
61334
61967
|
]);
|
|
61335
|
-
const renderToolbar = (0, react$1.useCallback)(({ filtered }) =>
|
|
61336
|
-
|
|
61337
|
-
|
|
61338
|
-
|
|
61339
|
-
|
|
61340
|
-
|
|
61341
|
-
|
|
61342
|
-
|
|
61343
|
-
|
|
61344
|
-
|
|
61345
|
-
label: t("uiLibrary.deviceExport.filterProvider", "Provider"),
|
|
61346
|
-
options: providerOptions,
|
|
61347
|
-
selected: bulkFilters.providers,
|
|
61348
|
-
onChange: setProviderFilter
|
|
61349
|
-
}),
|
|
61350
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
61351
|
-
size: "sm",
|
|
61352
|
-
variant: "secondary",
|
|
61353
|
-
disabled: filtered.length === 0,
|
|
61354
|
-
onClick: () => setSelectedIds((current) => selectShown(current, filtered)),
|
|
61355
|
-
children: t("uiLibrary.deviceExport.selectShown", "Select {{count}} shown", { count: filtered.length })
|
|
61356
|
-
}),
|
|
61357
|
-
selectedIds.size > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
61358
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
61359
|
-
size: "sm",
|
|
61360
|
-
variant: "ghost",
|
|
61361
|
-
onClick: () => setSelectedIds((current) => deselectShown(current, filtered)),
|
|
61362
|
-
children: t("uiLibrary.deviceExport.deselectShown", "Deselect shown")
|
|
61363
|
-
}),
|
|
61364
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
61365
|
-
"data-testid": "export-selection-count",
|
|
61366
|
-
className: "text-[11px] text-foreground-subtle",
|
|
61367
|
-
children: t("uiLibrary.deviceExport.selected", "{{count}} selected", { count: selectedIds.size })
|
|
61968
|
+
const renderToolbar = (0, react$1.useCallback)(({ filtered: filteredRows }) => {
|
|
61969
|
+
const filtered = filteredRows.flatMap((row) => row.kind === "device" ? [row.device] : []);
|
|
61970
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
61971
|
+
className: "inline-flex flex-wrap items-center gap-1.5",
|
|
61972
|
+
children: [
|
|
61973
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MultiSelectDropdown, {
|
|
61974
|
+
label: t("uiLibrary.deviceExport.filterRole", "Role"),
|
|
61975
|
+
options: roleOptions,
|
|
61976
|
+
selected: bulkFilters.roles,
|
|
61977
|
+
onChange: setRoleFilter
|
|
61368
61978
|
}),
|
|
61369
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
61370
|
-
|
|
61371
|
-
|
|
61372
|
-
|
|
61373
|
-
|
|
61374
|
-
children: t("uiLibrary.deviceExport.exposeN", "Expose {{count}}", { count: selectedIds.size })
|
|
61979
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MultiSelectDropdown, {
|
|
61980
|
+
label: t("uiLibrary.deviceExport.filterProvider", "Provider"),
|
|
61981
|
+
options: providerOptions,
|
|
61982
|
+
selected: bulkFilters.providers,
|
|
61983
|
+
onChange: setProviderFilter
|
|
61375
61984
|
}),
|
|
61376
61985
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
61377
61986
|
size: "sm",
|
|
61378
61987
|
variant: "secondary",
|
|
61379
|
-
disabled:
|
|
61380
|
-
onClick:
|
|
61381
|
-
children: t("uiLibrary.deviceExport.
|
|
61988
|
+
disabled: filtered.length === 0,
|
|
61989
|
+
onClick: () => setSelectedIds((current) => selectShown(current, filtered)),
|
|
61990
|
+
children: t("uiLibrary.deviceExport.selectShown", "Select {{count}} shown", { count: filtered.length })
|
|
61382
61991
|
}),
|
|
61383
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
61384
|
-
|
|
61385
|
-
|
|
61386
|
-
|
|
61387
|
-
|
|
61388
|
-
|
|
61389
|
-
|
|
61390
|
-
|
|
61391
|
-
|
|
61992
|
+
selectedIds.size > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
61993
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
61994
|
+
size: "sm",
|
|
61995
|
+
variant: "ghost",
|
|
61996
|
+
onClick: () => setSelectedIds((current) => deselectShown(current, filtered)),
|
|
61997
|
+
children: t("uiLibrary.deviceExport.deselectShown", "Deselect shown")
|
|
61998
|
+
}),
|
|
61999
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
62000
|
+
"data-testid": "export-selection-count",
|
|
62001
|
+
className: "text-[11px] text-foreground-subtle",
|
|
62002
|
+
children: t("uiLibrary.deviceExport.selected", "{{count}} selected", { count: selectedIds.size })
|
|
62003
|
+
}),
|
|
62004
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
62005
|
+
size: "sm",
|
|
62006
|
+
variant: "primary",
|
|
62007
|
+
disabled: busy,
|
|
62008
|
+
onClick: exposeSelected,
|
|
62009
|
+
children: t("uiLibrary.deviceExport.exposeN", "Expose {{count}}", { count: selectedIds.size })
|
|
62010
|
+
}),
|
|
62011
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
62012
|
+
size: "sm",
|
|
62013
|
+
variant: "secondary",
|
|
62014
|
+
disabled: busy,
|
|
62015
|
+
onClick: unexposeSelected,
|
|
62016
|
+
children: t("uiLibrary.deviceExport.unexposeN", "Unexpose {{count}}", { count: selectedIds.size })
|
|
62017
|
+
}),
|
|
62018
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
62019
|
+
size: "sm",
|
|
62020
|
+
variant: "ghost",
|
|
62021
|
+
onClick: clearSelection,
|
|
62022
|
+
children: t("uiLibrary.deviceExport.clear", "Clear")
|
|
62023
|
+
})
|
|
62024
|
+
] })
|
|
62025
|
+
]
|
|
62026
|
+
});
|
|
62027
|
+
}, [
|
|
61392
62028
|
t,
|
|
61393
62029
|
roleOptions,
|
|
61394
62030
|
providerOptions,
|
|
@@ -61437,16 +62073,16 @@ function DeviceExportPanel({ addonId, onOpenDevice, showSetup = true }) {
|
|
|
61437
62073
|
}),
|
|
61438
62074
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
61439
62075
|
className: "p-2 sm:p-3",
|
|
61440
|
-
children: loading && candidateDevices.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
62076
|
+
children: loading && candidateDevices.length === 0 && entityRows.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
61441
62077
|
className: "flex items-center gap-2 px-1 py-6 text-xs text-foreground-subtle",
|
|
61442
62078
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoaderCircle, { className: "h-3.5 w-3.5 animate-spin" }), t("uiLibrary.deviceExport.loading", "Loading exportable devices…")]
|
|
61443
|
-
}) : hasSupportedKinds && candidateDevices.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeviceList, {
|
|
62079
|
+
}) : hasSupportedKinds && candidateDevices.length > 0 || entityRows.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeviceList, {
|
|
61444
62080
|
mode: "generic",
|
|
61445
|
-
items:
|
|
62081
|
+
items: shownRows,
|
|
61446
62082
|
getKey,
|
|
61447
62083
|
getSearchText,
|
|
61448
62084
|
getFilterType,
|
|
61449
|
-
filterOptions:
|
|
62085
|
+
filterOptions: typeFilterOptions,
|
|
61450
62086
|
columns,
|
|
61451
62087
|
onRowClick: onOpenDevice ? onRowClick : void 0,
|
|
61452
62088
|
toolbar: renderToolbar,
|
|
@@ -63590,7 +64226,8 @@ function SectionedPage({ icon, title, subtitle, actions, sections, defaultSectio
|
|
|
63590
64226
|
tabKey: activeId,
|
|
63591
64227
|
fill,
|
|
63592
64228
|
children: fill ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
63593
|
-
className: "flex min-h-0 flex-1 flex-col",
|
|
64229
|
+
className: "flex min-h-0 flex-1 flex-col overflow-y-auto",
|
|
64230
|
+
"data-testid": "sectioned-page-body",
|
|
63594
64231
|
children: active?.content
|
|
63595
64232
|
}) : active?.content
|
|
63596
64233
|
});
|
|
@@ -71958,6 +72595,7 @@ exports.EmptyState = EmptyState;
|
|
|
71958
72595
|
exports.EndpointResultsCard = EndpointResultsCard;
|
|
71959
72596
|
exports.EndpointTestCard = EndpointTestCard;
|
|
71960
72597
|
exports.EntryJsonSchemaForm = EntryJsonSchemaForm;
|
|
72598
|
+
exports.Eraser = Eraser;
|
|
71961
72599
|
exports.ErrorBox = ErrorBox;
|
|
71962
72600
|
exports.EventKindGlyph = EventKindGlyph;
|
|
71963
72601
|
exports.EventStream = EventStream;
|
|
@@ -72974,12 +73612,16 @@ exports.useDeviceDiscoveryRefreshDiscovery = useDeviceDiscoveryRefreshDiscovery;
|
|
|
72974
73612
|
exports.useDeviceDiscoveryReleaseDevice = useDeviceDiscoveryReleaseDevice;
|
|
72975
73613
|
exports.useDeviceExportApplyDeviceSettingsPatch = useDeviceExportApplyDeviceSettingsPatch;
|
|
72976
73614
|
exports.useDeviceExportExposeDevice = useDeviceExportExposeDevice;
|
|
73615
|
+
exports.useDeviceExportExposeEntity = useDeviceExportExposeEntity;
|
|
72977
73616
|
exports.useDeviceExportGetDeviceLiveContribution = useDeviceExportGetDeviceLiveContribution;
|
|
72978
73617
|
exports.useDeviceExportGetDeviceSettingsContribution = useDeviceExportGetDeviceSettingsContribution;
|
|
72979
73618
|
exports.useDeviceExportGetStatus = useDeviceExportGetStatus;
|
|
72980
73619
|
exports.useDeviceExportListExposedDevices = useDeviceExportListExposedDevices;
|
|
73620
|
+
exports.useDeviceExportListExposedEntities = useDeviceExportListExposedEntities;
|
|
72981
73621
|
exports.useDeviceExportListSupportedDeviceKinds = useDeviceExportListSupportedDeviceKinds;
|
|
73622
|
+
exports.useDeviceExportListSupportedEntityKinds = useDeviceExportListSupportedEntityKinds;
|
|
72982
73623
|
exports.useDeviceExportUnexposeDevice = useDeviceExportUnexposeDevice;
|
|
73624
|
+
exports.useDeviceExportUnexposeEntity = useDeviceExportUnexposeEntity;
|
|
72983
73625
|
exports.useDeviceExtensionListCandidates = useDeviceExtensionListCandidates;
|
|
72984
73626
|
exports.useDeviceExtensionListSlots = useDeviceExtensionListSlots;
|
|
72985
73627
|
exports.useDeviceExtensionListSources = useDeviceExtensionListSources;
|
|
@@ -73470,6 +74112,12 @@ exports.usePetFeederSetChildLock = usePetFeederSetChildLock;
|
|
|
73470
74112
|
exports.usePetFeederSetFeedSound = usePetFeederSetFeedSound;
|
|
73471
74113
|
exports.usePetFeederSetIndicatorLight = usePetFeederSetIndicatorLight;
|
|
73472
74114
|
exports.usePetFeederSetVolume = usePetFeederSetVolume;
|
|
74115
|
+
exports.usePetFountainGetStatus = usePetFountainGetStatus;
|
|
74116
|
+
exports.usePetFountainResetDailyConsumption = usePetFountainResetDailyConsumption;
|
|
74117
|
+
exports.usePetFountainSetPump = usePetFountainSetPump;
|
|
74118
|
+
exports.usePetFountainSetPumpInterval = usePetFountainSetPumpInterval;
|
|
74119
|
+
exports.usePetFountainSetPumpOffMinutes = usePetFountainSetPumpOffMinutes;
|
|
74120
|
+
exports.usePetFountainSetPumpOnMinutes = usePetFountainSetPumpOnMinutes;
|
|
73473
74121
|
exports.usePickerSiteZones = usePickerSiteZones;
|
|
73474
74122
|
exports.usePipelineAnalyticsAddRetrainFrameFromRecording = usePipelineAnalyticsAddRetrainFrameFromRecording;
|
|
73475
74123
|
exports.usePipelineAnalyticsAddRetrainFramesToCollection = usePipelineAnalyticsAddRetrainFramesToCollection;
|
|
@@ -73802,9 +74450,11 @@ exports.useScrubController = useScrubController;
|
|
|
73802
74450
|
exports.useSearchFieldHotkey = useSearchFieldHotkey;
|
|
73803
74451
|
exports.useServerManagementApplyServerUpdate = useServerManagementApplyServerUpdate;
|
|
73804
74452
|
exports.useServerManagementCheckServerUpdate = useServerManagementCheckServerUpdate;
|
|
74453
|
+
exports.useServerManagementGetLaunchOverrides = useServerManagementGetLaunchOverrides;
|
|
73805
74454
|
exports.useServerManagementGetServerPackageStatus = useServerManagementGetServerPackageStatus;
|
|
73806
74455
|
exports.useServerManagementRestartServer = useServerManagementRestartServer;
|
|
73807
74456
|
exports.useServerManagementRollbackServerUpdate = useServerManagementRollbackServerUpdate;
|
|
74457
|
+
exports.useServerManagementSetLaunchOverrides = useServerManagementSetLaunchOverrides;
|
|
73808
74458
|
exports.useSettingsStoreAggregate = useSettingsStoreAggregate;
|
|
73809
74459
|
exports.useSettingsStoreCount = useSettingsStoreCount;
|
|
73810
74460
|
exports.useSettingsStoreDeclareCollection = useSettingsStoreDeclareCollection;
|