@camstack/ui-library 1.2.119 → 1.2.121
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/composites/cap-settings/NavigationPanel.d.ts +2 -0
- package/dist/composites/cap-settings/index.d.ts +1 -0
- package/dist/composites/index.d.ts +3 -0
- package/dist/composites/network-link-badge.d.ts +10 -0
- package/dist/generated/system-hooks.d.ts +24 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/use-device-network-link.d.ts +10 -0
- package/dist/hooks/use-navigation.d.ts +26 -0
- package/dist/index.cjs +660 -7
- package/dist/index.js +645 -9
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -799,6 +799,42 @@ var Bus = createLucideIcon("bus", [
|
|
|
799
799
|
* This source code is licensed under the ISC license.
|
|
800
800
|
* See the LICENSE file in the root directory of this source tree.
|
|
801
801
|
*/
|
|
802
|
+
var Cable = createLucideIcon("cable", [
|
|
803
|
+
["path", {
|
|
804
|
+
d: "M17 19a1 1 0 0 1-1-1v-2a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2a1 1 0 0 1-1 1z",
|
|
805
|
+
key: "trhst0"
|
|
806
|
+
}],
|
|
807
|
+
["path", {
|
|
808
|
+
d: "M17 21v-2",
|
|
809
|
+
key: "ds4u3f"
|
|
810
|
+
}],
|
|
811
|
+
["path", {
|
|
812
|
+
d: "M19 14V6.5a1 1 0 0 0-7 0v11a1 1 0 0 1-7 0V10",
|
|
813
|
+
key: "1mo9zo"
|
|
814
|
+
}],
|
|
815
|
+
["path", {
|
|
816
|
+
d: "M21 21v-2",
|
|
817
|
+
key: "eo0ou"
|
|
818
|
+
}],
|
|
819
|
+
["path", {
|
|
820
|
+
d: "M3 5V3",
|
|
821
|
+
key: "1k5hjh"
|
|
822
|
+
}],
|
|
823
|
+
["path", {
|
|
824
|
+
d: "M4 10a2 2 0 0 1-2-2V6a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2a2 2 0 0 1-2 2z",
|
|
825
|
+
key: "1dd30t"
|
|
826
|
+
}],
|
|
827
|
+
["path", {
|
|
828
|
+
d: "M7 5V3",
|
|
829
|
+
key: "1t1388"
|
|
830
|
+
}]
|
|
831
|
+
]);
|
|
832
|
+
/**
|
|
833
|
+
* @license lucide-react v0.576.0 - ISC
|
|
834
|
+
*
|
|
835
|
+
* This source code is licensed under the ISC license.
|
|
836
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
837
|
+
*/
|
|
802
838
|
var Camera = createLucideIcon("camera", [["path", {
|
|
803
839
|
d: "M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z",
|
|
804
840
|
key: "18u6gg"
|
|
@@ -1854,6 +1890,34 @@ var Flame = createLucideIcon("flame", [["path", {
|
|
|
1854
1890
|
* This source code is licensed under the ISC license.
|
|
1855
1891
|
* See the LICENSE file in the root directory of this source tree.
|
|
1856
1892
|
*/
|
|
1893
|
+
var FlashlightOff = createLucideIcon("flashlight-off", [
|
|
1894
|
+
["path", {
|
|
1895
|
+
d: "M11.652 6H18",
|
|
1896
|
+
key: "voqkpr"
|
|
1897
|
+
}],
|
|
1898
|
+
["path", {
|
|
1899
|
+
d: "M12 13v1",
|
|
1900
|
+
key: "176q98"
|
|
1901
|
+
}],
|
|
1902
|
+
["path", {
|
|
1903
|
+
d: "M16 16v4a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-8a4 4 0 0 0-.8-2.4l-.6-.8A3 3 0 0 1 6 7V6",
|
|
1904
|
+
key: "dzyf92"
|
|
1905
|
+
}],
|
|
1906
|
+
["path", {
|
|
1907
|
+
d: "m2 2 20 20",
|
|
1908
|
+
key: "1ooewy"
|
|
1909
|
+
}],
|
|
1910
|
+
["path", {
|
|
1911
|
+
d: "M7.649 2H17a1 1 0 0 1 1 1v4a3 3 0 0 1-.6 1.8l-.6.8a4 4 0 0 0-.55 1.007",
|
|
1912
|
+
key: "1hvcfn"
|
|
1913
|
+
}]
|
|
1914
|
+
]);
|
|
1915
|
+
/**
|
|
1916
|
+
* @license lucide-react v0.576.0 - ISC
|
|
1917
|
+
*
|
|
1918
|
+
* This source code is licensed under the ISC license.
|
|
1919
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
1920
|
+
*/
|
|
1857
1921
|
var Flashlight = createLucideIcon("flashlight", [
|
|
1858
1922
|
["path", {
|
|
1859
1923
|
d: "M12 13v1",
|
|
@@ -2045,6 +2109,30 @@ var Hammer = createLucideIcon("hammer", [
|
|
|
2045
2109
|
* This source code is licensed under the ISC license.
|
|
2046
2110
|
* See the LICENSE file in the root directory of this source tree.
|
|
2047
2111
|
*/
|
|
2112
|
+
var Hand = createLucideIcon("hand", [
|
|
2113
|
+
["path", {
|
|
2114
|
+
d: "M18 11V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2",
|
|
2115
|
+
key: "1fvzgz"
|
|
2116
|
+
}],
|
|
2117
|
+
["path", {
|
|
2118
|
+
d: "M14 10V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2v2",
|
|
2119
|
+
key: "1kc0my"
|
|
2120
|
+
}],
|
|
2121
|
+
["path", {
|
|
2122
|
+
d: "M10 10.5V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2v8",
|
|
2123
|
+
key: "10h0bg"
|
|
2124
|
+
}],
|
|
2125
|
+
["path", {
|
|
2126
|
+
d: "M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15",
|
|
2127
|
+
key: "1s1gnw"
|
|
2128
|
+
}]
|
|
2129
|
+
]);
|
|
2130
|
+
/**
|
|
2131
|
+
* @license lucide-react v0.576.0 - ISC
|
|
2132
|
+
*
|
|
2133
|
+
* This source code is licensed under the ISC license.
|
|
2134
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
2135
|
+
*/
|
|
2048
2136
|
var HardDrive = createLucideIcon("hard-drive", [
|
|
2049
2137
|
["path", {
|
|
2050
2138
|
d: "M10 16h.01",
|
|
@@ -3161,6 +3249,44 @@ var Radar = createLucideIcon("radar", [
|
|
|
3161
3249
|
* This source code is licensed under the ISC license.
|
|
3162
3250
|
* See the LICENSE file in the root directory of this source tree.
|
|
3163
3251
|
*/
|
|
3252
|
+
var RadioTower = createLucideIcon("radio-tower", [
|
|
3253
|
+
["path", {
|
|
3254
|
+
d: "M4.9 16.1C1 12.2 1 5.8 4.9 1.9",
|
|
3255
|
+
key: "s0qx1y"
|
|
3256
|
+
}],
|
|
3257
|
+
["path", {
|
|
3258
|
+
d: "M7.8 4.7a6.14 6.14 0 0 0-.8 7.5",
|
|
3259
|
+
key: "1idnkw"
|
|
3260
|
+
}],
|
|
3261
|
+
["circle", {
|
|
3262
|
+
cx: "12",
|
|
3263
|
+
cy: "9",
|
|
3264
|
+
r: "2",
|
|
3265
|
+
key: "1092wv"
|
|
3266
|
+
}],
|
|
3267
|
+
["path", {
|
|
3268
|
+
d: "M16.2 4.8c2 2 2.26 5.11.8 7.47",
|
|
3269
|
+
key: "ojru2q"
|
|
3270
|
+
}],
|
|
3271
|
+
["path", {
|
|
3272
|
+
d: "M19.1 1.9a9.96 9.96 0 0 1 0 14.1",
|
|
3273
|
+
key: "rhi7fg"
|
|
3274
|
+
}],
|
|
3275
|
+
["path", {
|
|
3276
|
+
d: "M9.5 18h5",
|
|
3277
|
+
key: "mfy3pd"
|
|
3278
|
+
}],
|
|
3279
|
+
["path", {
|
|
3280
|
+
d: "m8 22 4-11 4 11",
|
|
3281
|
+
key: "25yftu"
|
|
3282
|
+
}]
|
|
3283
|
+
]);
|
|
3284
|
+
/**
|
|
3285
|
+
* @license lucide-react v0.576.0 - ISC
|
|
3286
|
+
*
|
|
3287
|
+
* This source code is licensed under the ISC license.
|
|
3288
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
3289
|
+
*/
|
|
3164
3290
|
var Radio = createLucideIcon("radio", [
|
|
3165
3291
|
["path", {
|
|
3166
3292
|
d: "M16.247 7.761a6 6 0 0 1 0 8.478",
|
|
@@ -4212,6 +4338,19 @@ var Type = createLucideIcon("type", [
|
|
|
4212
4338
|
* This source code is licensed under the ISC license.
|
|
4213
4339
|
* See the LICENSE file in the root directory of this source tree.
|
|
4214
4340
|
*/
|
|
4341
|
+
var Undo2 = createLucideIcon("undo-2", [["path", {
|
|
4342
|
+
d: "M9 14 4 9l5-5",
|
|
4343
|
+
key: "102s5s"
|
|
4344
|
+
}], ["path", {
|
|
4345
|
+
d: "M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11",
|
|
4346
|
+
key: "f3b9sd"
|
|
4347
|
+
}]]);
|
|
4348
|
+
/**
|
|
4349
|
+
* @license lucide-react v0.576.0 - ISC
|
|
4350
|
+
*
|
|
4351
|
+
* This source code is licensed under the ISC license.
|
|
4352
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
4353
|
+
*/
|
|
4215
4354
|
var Unlink2 = createLucideIcon("unlink-2", [["path", {
|
|
4216
4355
|
d: "M15 7h2a5 5 0 0 1 0 10h-2m-6 0H7A5 5 0 0 1 7 7h2",
|
|
4217
4356
|
key: "1re2ne"
|
|
@@ -4419,6 +4558,39 @@ var Waves = createLucideIcon("waves", [
|
|
|
4419
4558
|
* This source code is licensed under the ISC license.
|
|
4420
4559
|
* See the LICENSE file in the root directory of this source tree.
|
|
4421
4560
|
*/
|
|
4561
|
+
var WifiHigh = createLucideIcon("wifi-high", [
|
|
4562
|
+
["path", {
|
|
4563
|
+
d: "M12 20h.01",
|
|
4564
|
+
key: "zekei9"
|
|
4565
|
+
}],
|
|
4566
|
+
["path", {
|
|
4567
|
+
d: "M5 12.859a10 10 0 0 1 14 0",
|
|
4568
|
+
key: "1x1e6c"
|
|
4569
|
+
}],
|
|
4570
|
+
["path", {
|
|
4571
|
+
d: "M8.5 16.429a5 5 0 0 1 7 0",
|
|
4572
|
+
key: "1bycff"
|
|
4573
|
+
}]
|
|
4574
|
+
]);
|
|
4575
|
+
/**
|
|
4576
|
+
* @license lucide-react v0.576.0 - ISC
|
|
4577
|
+
*
|
|
4578
|
+
* This source code is licensed under the ISC license.
|
|
4579
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
4580
|
+
*/
|
|
4581
|
+
var WifiLow = createLucideIcon("wifi-low", [["path", {
|
|
4582
|
+
d: "M12 20h.01",
|
|
4583
|
+
key: "zekei9"
|
|
4584
|
+
}], ["path", {
|
|
4585
|
+
d: "M8.5 16.429a5 5 0 0 1 7 0",
|
|
4586
|
+
key: "1bycff"
|
|
4587
|
+
}]]);
|
|
4588
|
+
/**
|
|
4589
|
+
* @license lucide-react v0.576.0 - ISC
|
|
4590
|
+
*
|
|
4591
|
+
* This source code is licensed under the ISC license.
|
|
4592
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
4593
|
+
*/
|
|
4422
4594
|
var WifiOff = createLucideIcon("wifi-off", [
|
|
4423
4595
|
["path", {
|
|
4424
4596
|
d: "M12 20h.01",
|
|
@@ -4455,6 +4627,16 @@ var WifiOff = createLucideIcon("wifi-off", [
|
|
|
4455
4627
|
* This source code is licensed under the ISC license.
|
|
4456
4628
|
* See the LICENSE file in the root directory of this source tree.
|
|
4457
4629
|
*/
|
|
4630
|
+
var WifiZero = createLucideIcon("wifi-zero", [["path", {
|
|
4631
|
+
d: "M12 20h.01",
|
|
4632
|
+
key: "zekei9"
|
|
4633
|
+
}]]);
|
|
4634
|
+
/**
|
|
4635
|
+
* @license lucide-react v0.576.0 - ISC
|
|
4636
|
+
*
|
|
4637
|
+
* This source code is licensed under the ISC license.
|
|
4638
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
4639
|
+
*/
|
|
4458
4640
|
var Wifi = createLucideIcon("wifi", [
|
|
4459
4641
|
["path", {
|
|
4460
4642
|
d: "M12 20h.01",
|
|
@@ -5805,6 +5987,28 @@ var useMqttBrokerGetStatus = trpc.mqttBroker.getStatus.useQuery;
|
|
|
5805
5987
|
var useNativeObjectDetectionSetEnabled = trpc.nativeObjectDetection.setEnabled.useMutation;
|
|
5806
5988
|
/** Generated alias around `trpc.nativeObjectDetection.getStatus.useQuery`. */
|
|
5807
5989
|
var useNativeObjectDetectionGetStatus = trpc.nativeObjectDetection.getStatus.useQuery;
|
|
5990
|
+
/** Generated alias around `trpc.navigation.move.useMutation`. */
|
|
5991
|
+
var useNavigationMove = trpc.navigation.move.useMutation;
|
|
5992
|
+
/** Generated alias around `trpc.navigation.stop.useMutation`. */
|
|
5993
|
+
var useNavigationStop = trpc.navigation.stop.useMutation;
|
|
5994
|
+
/** Generated alias around `trpc.navigation.goToPoint.useMutation`. */
|
|
5995
|
+
var useNavigationGoToPoint = trpc.navigation.goToPoint.useMutation;
|
|
5996
|
+
/** Generated alias around `trpc.navigation.listActions.useQuery`. */
|
|
5997
|
+
var useNavigationListActions = trpc.navigation.listActions.useQuery;
|
|
5998
|
+
/** Generated alias around `trpc.navigation.runAction.useMutation`. */
|
|
5999
|
+
var useNavigationRunAction = trpc.navigation.runAction.useMutation;
|
|
6000
|
+
/** Generated alias around `trpc.navigation.playSound.useMutation`. */
|
|
6001
|
+
var useNavigationPlaySound = trpc.navigation.playSound.useMutation;
|
|
6002
|
+
/** Generated alias around `trpc.navigation.setLightOn.useMutation`. */
|
|
6003
|
+
var useNavigationSetLightOn = trpc.navigation.setLightOn.useMutation;
|
|
6004
|
+
/** Generated alias around `trpc.navigation.setLightMode.useMutation`. */
|
|
6005
|
+
var useNavigationSetLightMode = trpc.navigation.setLightMode.useMutation;
|
|
6006
|
+
/** Generated alias around `trpc.navigation.setLightLevel.useMutation`. */
|
|
6007
|
+
var useNavigationSetLightLevel = trpc.navigation.setLightLevel.useMutation;
|
|
6008
|
+
/** Generated alias around `trpc.navigation.getFeatures.useQuery`. */
|
|
6009
|
+
var useNavigationGetFeatures = trpc.navigation.getFeatures.useQuery;
|
|
6010
|
+
/** Generated alias around `trpc.navigation.getStatus.useQuery`. */
|
|
6011
|
+
var useNavigationGetStatus = trpc.navigation.getStatus.useQuery;
|
|
5808
6012
|
/** Generated alias around `trpc.networkAccess.start.useMutation`. */
|
|
5809
6013
|
var useNetworkAccessStart = trpc.networkAccess.start.useMutation;
|
|
5810
6014
|
/** Generated alias around `trpc.networkAccess.stop.useMutation`. */
|
|
@@ -5815,6 +6019,8 @@ var useNetworkAccessGetEndpoint = trpc.networkAccess.getEndpoint.useQuery;
|
|
|
5815
6019
|
var useNetworkAccessGetStatus = trpc.networkAccess.getStatus.useQuery;
|
|
5816
6020
|
/** Generated alias around `trpc.networkAccess.listEndpoints.useQuery`. */
|
|
5817
6021
|
var useNetworkAccessListEndpoints = trpc.networkAccess.listEndpoints.useQuery;
|
|
6022
|
+
/** Generated alias around `trpc.networkLink.getStatus.useQuery`. */
|
|
6023
|
+
var useNetworkLinkGetStatus = trpc.networkLink.getStatus.useQuery;
|
|
5818
6024
|
/** Generated alias around `trpc.networkQuality.getDeviceStats.useQuery`. */
|
|
5819
6025
|
var useNetworkQualityGetDeviceStats = trpc.networkQuality.getDeviceStats.useQuery;
|
|
5820
6026
|
/** Generated alias around `trpc.networkQuality.getAllStats.useQuery`. */
|
|
@@ -15870,6 +16076,373 @@ function ConsumablesPanel({ deviceId }) {
|
|
|
15870
16076
|
});
|
|
15871
16077
|
}
|
|
15872
16078
|
//#endregion
|
|
16079
|
+
//#region src/hooks/use-navigation.ts
|
|
16080
|
+
/**
|
|
16081
|
+
* useNavigation — drive a `navigation` robot camera from the UI.
|
|
16082
|
+
*
|
|
16083
|
+
* The cap's `move` is MOMENTARY: fluid driving is the UI sending a nudge at
|
|
16084
|
+
* 1 Hz while a direction is held and ONE `stop` on release, exactly like the
|
|
16085
|
+
* robot app's joystick. This hook owns that cadence (`startDrive` /
|
|
16086
|
+
* `stopDrive`); the provider forwards each nudge to a single drive write.
|
|
16087
|
+
*
|
|
16088
|
+
* Everything else is a plain call plus the data the panel renders from:
|
|
16089
|
+
* `features` (which primitives are on), `actions` (the enabled dictionary)
|
|
16090
|
+
* and the live `status` slice off the device proxy.
|
|
16091
|
+
*/
|
|
16092
|
+
/** The cadence the robot app drives at while a direction is held. */
|
|
16093
|
+
var NAVIGATION_DRIVE_INTERVAL_MS = 1e3;
|
|
16094
|
+
var DIRECTION_VECTORS$1 = {
|
|
16095
|
+
forward: {
|
|
16096
|
+
pan: 0,
|
|
16097
|
+
tilt: 1
|
|
16098
|
+
},
|
|
16099
|
+
left: {
|
|
16100
|
+
pan: -1,
|
|
16101
|
+
tilt: 0
|
|
16102
|
+
},
|
|
16103
|
+
right: {
|
|
16104
|
+
pan: 1,
|
|
16105
|
+
tilt: 0
|
|
16106
|
+
},
|
|
16107
|
+
spin: {
|
|
16108
|
+
pan: 0,
|
|
16109
|
+
tilt: -1
|
|
16110
|
+
}
|
|
16111
|
+
};
|
|
16112
|
+
function isAbsentProvider$1(err) {
|
|
16113
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16114
|
+
return /not available|no provider|NOT_FOUND|PRECONDITION/i.test(message);
|
|
16115
|
+
}
|
|
16116
|
+
function useNavigation(trpc, deviceId, hookOptions) {
|
|
16117
|
+
const enabled = hookOptions?.enabled ?? true;
|
|
16118
|
+
const nav = useDeviceProxy(trpc, enabled ? deviceId : null)?.navigation;
|
|
16119
|
+
const status = useDeviceState(trpc, enabled ? deviceId : null, (s) => s.navigation);
|
|
16120
|
+
const [features, setFeatures] = (0, react$1.useState)(null);
|
|
16121
|
+
const [actions, setActions] = (0, react$1.useState)([]);
|
|
16122
|
+
const [busy, setBusy] = (0, react$1.useState)(false);
|
|
16123
|
+
const [error, setError] = (0, react$1.useState)(null);
|
|
16124
|
+
const [driving, setDriving] = (0, react$1.useState)(null);
|
|
16125
|
+
const driveTimer = (0, react$1.useRef)(null);
|
|
16126
|
+
(0, react$1.useEffect)(() => {
|
|
16127
|
+
if (!enabled || !nav) return;
|
|
16128
|
+
let cancelled = false;
|
|
16129
|
+
(async () => {
|
|
16130
|
+
try {
|
|
16131
|
+
const [nextFeatures, nextActions] = await Promise.all([nav.getFeatures({}), nav.listActions({})]);
|
|
16132
|
+
if (cancelled) return;
|
|
16133
|
+
setFeatures(nextFeatures);
|
|
16134
|
+
setActions(nextActions);
|
|
16135
|
+
} catch (err) {
|
|
16136
|
+
if (cancelled || isAbsentProvider$1(err)) return;
|
|
16137
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
16138
|
+
}
|
|
16139
|
+
})();
|
|
16140
|
+
return () => {
|
|
16141
|
+
cancelled = true;
|
|
16142
|
+
};
|
|
16143
|
+
}, [nav, enabled]);
|
|
16144
|
+
const wrap = (0, react$1.useCallback)(async (fn) => {
|
|
16145
|
+
if (!enabled || !nav) return;
|
|
16146
|
+
setError(null);
|
|
16147
|
+
setBusy(true);
|
|
16148
|
+
try {
|
|
16149
|
+
await fn();
|
|
16150
|
+
} catch (err) {
|
|
16151
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
16152
|
+
} finally {
|
|
16153
|
+
setBusy(false);
|
|
16154
|
+
}
|
|
16155
|
+
}, [enabled, nav]);
|
|
16156
|
+
const clearTimer = (0, react$1.useCallback)(() => {
|
|
16157
|
+
if (driveTimer.current !== null) {
|
|
16158
|
+
clearInterval(driveTimer.current);
|
|
16159
|
+
driveTimer.current = null;
|
|
16160
|
+
}
|
|
16161
|
+
}, []);
|
|
16162
|
+
const stopDrive = (0, react$1.useCallback)(async () => {
|
|
16163
|
+
clearTimer();
|
|
16164
|
+
setDriving(null);
|
|
16165
|
+
if (!nav) return;
|
|
16166
|
+
await wrap(() => nav.stop({}));
|
|
16167
|
+
}, [
|
|
16168
|
+
clearTimer,
|
|
16169
|
+
nav,
|
|
16170
|
+
wrap
|
|
16171
|
+
]);
|
|
16172
|
+
const startDrive = (0, react$1.useCallback)((direction) => {
|
|
16173
|
+
if (!nav) return;
|
|
16174
|
+
clearTimer();
|
|
16175
|
+
setDriving(direction);
|
|
16176
|
+
const vector = DIRECTION_VECTORS$1[direction];
|
|
16177
|
+
const nudge = () => {
|
|
16178
|
+
nav.move({
|
|
16179
|
+
pan: vector.pan,
|
|
16180
|
+
tilt: vector.tilt
|
|
16181
|
+
}).catch((err) => {
|
|
16182
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
16183
|
+
});
|
|
16184
|
+
};
|
|
16185
|
+
nudge();
|
|
16186
|
+
driveTimer.current = setInterval(nudge, NAVIGATION_DRIVE_INTERVAL_MS);
|
|
16187
|
+
}, [clearTimer, nav]);
|
|
16188
|
+
(0, react$1.useEffect)(() => {
|
|
16189
|
+
return () => {
|
|
16190
|
+
if (driveTimer.current !== null) {
|
|
16191
|
+
clearInterval(driveTimer.current);
|
|
16192
|
+
driveTimer.current = null;
|
|
16193
|
+
nav?.stop({}).catch(() => void 0);
|
|
16194
|
+
}
|
|
16195
|
+
};
|
|
16196
|
+
}, [nav]);
|
|
16197
|
+
const runAction = (0, react$1.useCallback)((actionId) => wrap(async () => nav?.runAction({ actionId })), [nav, wrap]);
|
|
16198
|
+
const playSound = (0, react$1.useCallback)((soundId) => wrap(async () => nav?.playSound({ soundId })), [nav, wrap]);
|
|
16199
|
+
const setLightOn = (0, react$1.useCallback)((on) => wrap(async () => nav?.setLightOn({ on })), [nav, wrap]);
|
|
16200
|
+
const setLightMode = (0, react$1.useCallback)((mode, level) => wrap(async () => nav?.setLightMode(level === void 0 ? { mode } : {
|
|
16201
|
+
mode,
|
|
16202
|
+
level
|
|
16203
|
+
})), [nav, wrap]);
|
|
16204
|
+
const setLightLevel = (0, react$1.useCallback)((level) => wrap(async () => nav?.setLightLevel({ level })), [nav, wrap]);
|
|
16205
|
+
return {
|
|
16206
|
+
features,
|
|
16207
|
+
actions,
|
|
16208
|
+
status: status ?? null,
|
|
16209
|
+
startDrive,
|
|
16210
|
+
stopDrive,
|
|
16211
|
+
runAction,
|
|
16212
|
+
playSound,
|
|
16213
|
+
setLightOn,
|
|
16214
|
+
setLightMode,
|
|
16215
|
+
setLightLevel,
|
|
16216
|
+
busy,
|
|
16217
|
+
error,
|
|
16218
|
+
driving
|
|
16219
|
+
};
|
|
16220
|
+
}
|
|
16221
|
+
//#endregion
|
|
16222
|
+
//#region src/composites/cap-settings/NavigationPanel.tsx
|
|
16223
|
+
/** The cap's icon HINTS are lucide names; unknown hints fall back to a spark. */
|
|
16224
|
+
var ICONS = {
|
|
16225
|
+
play: Play,
|
|
16226
|
+
pause: Pause,
|
|
16227
|
+
hand: Hand,
|
|
16228
|
+
home: House,
|
|
16229
|
+
bell: Bell,
|
|
16230
|
+
crosshair: Crosshair,
|
|
16231
|
+
"paw-print": PawPrint,
|
|
16232
|
+
footprints: Footprints,
|
|
16233
|
+
droplets: Droplets,
|
|
16234
|
+
"trash-2": Trash2,
|
|
16235
|
+
flashlight: Flashlight,
|
|
16236
|
+
"flashlight-off": FlashlightOff,
|
|
16237
|
+
cat: Cat,
|
|
16238
|
+
dog: Dog,
|
|
16239
|
+
clock: Clock
|
|
16240
|
+
};
|
|
16241
|
+
function iconFor(hint) {
|
|
16242
|
+
return ICONS[hint] ?? Sparkles;
|
|
16243
|
+
}
|
|
16244
|
+
var DRIVE_BUTTONS = [
|
|
16245
|
+
{
|
|
16246
|
+
direction: "left",
|
|
16247
|
+
label: "Turn left",
|
|
16248
|
+
Icon: RotateCcw
|
|
16249
|
+
},
|
|
16250
|
+
{
|
|
16251
|
+
direction: "forward",
|
|
16252
|
+
label: "Forward",
|
|
16253
|
+
Icon: ArrowUp
|
|
16254
|
+
},
|
|
16255
|
+
{
|
|
16256
|
+
direction: "right",
|
|
16257
|
+
label: "Turn right",
|
|
16258
|
+
Icon: RotateCw
|
|
16259
|
+
},
|
|
16260
|
+
{
|
|
16261
|
+
direction: "spin",
|
|
16262
|
+
label: "Turn around",
|
|
16263
|
+
Icon: Undo2
|
|
16264
|
+
}
|
|
16265
|
+
];
|
|
16266
|
+
var BUTTON = "inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-[10px] font-medium transition-colors bg-surface-elevated/40 text-foreground-subtle hover:bg-surface-hover hover:text-foreground disabled:opacity-50";
|
|
16267
|
+
var BUTTON_ON = "bg-success/15 text-success hover:bg-success/25";
|
|
16268
|
+
function EntryButton({ entry, disabled, onRun }) {
|
|
16269
|
+
const Glyph = iconFor(entry.icon);
|
|
16270
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
16271
|
+
type: "button",
|
|
16272
|
+
className: BUTTON,
|
|
16273
|
+
disabled,
|
|
16274
|
+
onClick: () => onRun(entry),
|
|
16275
|
+
title: entry.label,
|
|
16276
|
+
"aria-label": entry.label,
|
|
16277
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Glyph, { className: "h-3.5 w-3.5" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: entry.label })]
|
|
16278
|
+
});
|
|
16279
|
+
}
|
|
16280
|
+
function NavigationPanel({ deviceId }) {
|
|
16281
|
+
const system = useSystem();
|
|
16282
|
+
const ready = Number.isFinite(deviceId);
|
|
16283
|
+
const nav = useNavigation(system.trpcClient, ready ? deviceId : 0, { enabled: ready });
|
|
16284
|
+
if (!ready) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16285
|
+
className: "flex items-center justify-center h-full text-[11px] text-foreground-subtle italic",
|
|
16286
|
+
children: [
|
|
16287
|
+
"Device #",
|
|
16288
|
+
deviceId,
|
|
16289
|
+
" not loaded."
|
|
16290
|
+
]
|
|
16291
|
+
});
|
|
16292
|
+
const features = nav.features;
|
|
16293
|
+
const actions = nav.actions.filter((entry) => entry.enabled && entry.kind === "action");
|
|
16294
|
+
const sounds = nav.actions.filter((entry) => entry.enabled && entry.kind === "sound");
|
|
16295
|
+
const status = nav.status;
|
|
16296
|
+
const manual = status?.lightMode === "manual";
|
|
16297
|
+
const run = (entry) => {
|
|
16298
|
+
if (entry.kind === "sound") {
|
|
16299
|
+
if (entry.soundId !== void 0) nav.playSound(entry.soundId);
|
|
16300
|
+
return;
|
|
16301
|
+
}
|
|
16302
|
+
nav.runAction(entry.id);
|
|
16303
|
+
};
|
|
16304
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(WidgetPanel, {
|
|
16305
|
+
title: "Navigation",
|
|
16306
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Navigation, { className: "h-3.5 w-3.5 text-foreground-subtle" }),
|
|
16307
|
+
bodyClassName: "p-3 flex flex-col gap-3 overflow-y-auto",
|
|
16308
|
+
children: [
|
|
16309
|
+
status && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16310
|
+
className: "flex flex-wrap items-center gap-1.5 text-[10px]",
|
|
16311
|
+
children: [
|
|
16312
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16313
|
+
className: "rounded-full bg-surface-elevated/60 px-2 py-0.5 text-foreground",
|
|
16314
|
+
children: status.mode
|
|
16315
|
+
}),
|
|
16316
|
+
status.following && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16317
|
+
className: "rounded-full bg-info/15 px-2 py-0.5 text-info",
|
|
16318
|
+
children: "following"
|
|
16319
|
+
}),
|
|
16320
|
+
status.flash && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16321
|
+
className: "rounded-full bg-warning/15 px-2 py-0.5 text-warning",
|
|
16322
|
+
children: "light on"
|
|
16323
|
+
})
|
|
16324
|
+
]
|
|
16325
|
+
}),
|
|
16326
|
+
features?.move && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
16327
|
+
className: "flex flex-col gap-1.5",
|
|
16328
|
+
children: [
|
|
16329
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16330
|
+
className: "text-[10.5px] font-semibold text-foreground",
|
|
16331
|
+
children: "Drive (hold)"
|
|
16332
|
+
}),
|
|
16333
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16334
|
+
className: "grid grid-cols-4 gap-1.5",
|
|
16335
|
+
children: DRIVE_BUTTONS.map(({ direction, label, Icon }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16336
|
+
type: "button",
|
|
16337
|
+
className: cn(BUTTON, "justify-center select-none", nav.driving === direction && BUTTON_ON),
|
|
16338
|
+
"aria-label": label,
|
|
16339
|
+
title: label,
|
|
16340
|
+
onPointerDown: () => nav.startDrive(direction),
|
|
16341
|
+
onPointerUp: () => void nav.stopDrive(),
|
|
16342
|
+
onPointerLeave: () => {
|
|
16343
|
+
if (nav.driving === direction) nav.stopDrive();
|
|
16344
|
+
},
|
|
16345
|
+
onPointerCancel: () => void nav.stopDrive(),
|
|
16346
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { className: "h-4 w-4" })
|
|
16347
|
+
}, direction))
|
|
16348
|
+
}),
|
|
16349
|
+
features.stop && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
16350
|
+
type: "button",
|
|
16351
|
+
className: cn(BUTTON, "justify-center"),
|
|
16352
|
+
onClick: () => void nav.stopDrive(),
|
|
16353
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hand, { className: "h-3.5 w-3.5" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Stop" })]
|
|
16354
|
+
})
|
|
16355
|
+
]
|
|
16356
|
+
}),
|
|
16357
|
+
features?.runAction && actions.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
16358
|
+
className: "flex flex-col gap-1.5",
|
|
16359
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16360
|
+
className: "text-[10.5px] font-semibold text-foreground",
|
|
16361
|
+
children: "Actions"
|
|
16362
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16363
|
+
className: "flex flex-wrap gap-1.5",
|
|
16364
|
+
children: actions.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EntryButton, {
|
|
16365
|
+
entry,
|
|
16366
|
+
disabled: nav.busy,
|
|
16367
|
+
onRun: run
|
|
16368
|
+
}, entry.id))
|
|
16369
|
+
})]
|
|
16370
|
+
}),
|
|
16371
|
+
features?.playSound && sounds.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
16372
|
+
className: "flex flex-col gap-1.5",
|
|
16373
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16374
|
+
className: "text-[10.5px] font-semibold text-foreground",
|
|
16375
|
+
children: "Sounds"
|
|
16376
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16377
|
+
className: "flex flex-wrap gap-1.5",
|
|
16378
|
+
children: sounds.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EntryButton, {
|
|
16379
|
+
entry,
|
|
16380
|
+
disabled: nav.busy,
|
|
16381
|
+
onRun: run
|
|
16382
|
+
}, entry.id))
|
|
16383
|
+
})]
|
|
16384
|
+
}),
|
|
16385
|
+
features?.light && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
16386
|
+
className: "flex flex-col gap-1.5 border-t border-border/40 pt-2",
|
|
16387
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16388
|
+
className: "flex items-center justify-between",
|
|
16389
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16390
|
+
className: "text-[10.5px] font-semibold text-foreground",
|
|
16391
|
+
children: "Fill light"
|
|
16392
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
16393
|
+
type: "button",
|
|
16394
|
+
className: cn(BUTTON, status?.flash && BUTTON_ON),
|
|
16395
|
+
"aria-pressed": status?.flash === true,
|
|
16396
|
+
disabled: nav.busy,
|
|
16397
|
+
onClick: () => void nav.setLightOn(!(status?.flash === true)),
|
|
16398
|
+
children: [status?.flash ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Flashlight, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FlashlightOff, { className: "h-3.5 w-3.5" }), status?.flash ? "On" : "Off"]
|
|
16399
|
+
})]
|
|
16400
|
+
}), features.lightMode && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16401
|
+
className: "flex items-center gap-2",
|
|
16402
|
+
children: [
|
|
16403
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
16404
|
+
"aria-label": "Light mode",
|
|
16405
|
+
className: "rounded-md bg-surface-elevated/40 px-2 py-1 text-[10px] text-foreground",
|
|
16406
|
+
value: status?.lightMode ?? "auto",
|
|
16407
|
+
disabled: nav.busy,
|
|
16408
|
+
onChange: (event) => {
|
|
16409
|
+
const mode = event.target.value === "manual" ? "manual" : "auto";
|
|
16410
|
+
nav.setLightMode(mode, mode === "manual" ? status?.lightLevel : void 0);
|
|
16411
|
+
},
|
|
16412
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
16413
|
+
value: "auto",
|
|
16414
|
+
children: "Auto"
|
|
16415
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
16416
|
+
value: "manual",
|
|
16417
|
+
children: "Manual"
|
|
16418
|
+
})]
|
|
16419
|
+
}),
|
|
16420
|
+
manual && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
16421
|
+
type: "range",
|
|
16422
|
+
"aria-label": "Light level",
|
|
16423
|
+
className: "flex-1",
|
|
16424
|
+
min: _camstack_types.NAVIGATION_LIGHT_LEVEL_MIN,
|
|
16425
|
+
max: _camstack_types.NAVIGATION_LIGHT_LEVEL_MAX,
|
|
16426
|
+
step: 1,
|
|
16427
|
+
value: status?.lightLevel ?? _camstack_types.NAVIGATION_LIGHT_LEVEL_MAX,
|
|
16428
|
+
disabled: nav.busy,
|
|
16429
|
+
onChange: (event) => void nav.setLightLevel(Number(event.target.value))
|
|
16430
|
+
}),
|
|
16431
|
+
manual && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16432
|
+
className: "text-[10px] text-foreground-subtle w-8 text-right",
|
|
16433
|
+
children: status?.lightLevel ?? _camstack_types.NAVIGATION_LIGHT_LEVEL_MAX
|
|
16434
|
+
})
|
|
16435
|
+
]
|
|
16436
|
+
})]
|
|
16437
|
+
}),
|
|
16438
|
+
nav.error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16439
|
+
className: "text-[10px] text-danger",
|
|
16440
|
+
children: nav.error
|
|
16441
|
+
})
|
|
16442
|
+
]
|
|
16443
|
+
});
|
|
16444
|
+
}
|
|
16445
|
+
//#endregion
|
|
15873
16446
|
//#region src/lib/cap-error.ts
|
|
15874
16447
|
/**
|
|
15875
16448
|
* Shared capability-error utilities for the admin UI.
|
|
@@ -18695,6 +19268,7 @@ var HOST_WIDGETS = {
|
|
|
18695
19268
|
"host/motion-zones-grid": (0, react$1.lazy)(() => Promise.resolve().then(() => require("./MotionZonesSettings-BP9p0pFB.cjs")).then((m) => ({ default: m.MotionZonesSettings }))),
|
|
18696
19269
|
"host/privacy-mask-grid": (0, react$1.lazy)(() => Promise.resolve().then(() => require("./PrivacyMaskSettings-Oo7gyB4E.cjs")).then((m) => ({ default: m.PrivacyMaskSettings }))),
|
|
18697
19270
|
"host/ptz-panel": PtzPanel,
|
|
19271
|
+
"host/navigation-panel": NavigationPanel,
|
|
18698
19272
|
"host/consumables-panel": ConsumablesPanel,
|
|
18699
19273
|
"host/ptz-autotrack": AutotrackSection,
|
|
18700
19274
|
"host/recording-panel": RecordingPanel
|
|
@@ -24486,6 +25060,49 @@ function BatteryBadge({ status, variant = "compact", className }) {
|
|
|
24486
25060
|
});
|
|
24487
25061
|
}
|
|
24488
25062
|
//#endregion
|
|
25063
|
+
//#region src/composites/network-link-badge.tsx
|
|
25064
|
+
/**
|
|
25065
|
+
* NetworkLinkBadge — compact link indicator for device rows and headers.
|
|
25066
|
+
*
|
|
25067
|
+
* A wire glyph for ethernet, a wifi glyph with the signal for wireless, an
|
|
25068
|
+
* antenna for cellular. Renders nothing for an `unknown` link — that is the
|
|
25069
|
+
* absence of a read, not a link — and shows no percentage when the signal is
|
|
25070
|
+
* `null` (a wire has none; a wireless link not yet read must not sit at 0 %).
|
|
25071
|
+
*
|
|
25072
|
+
* Self-contained on data: the caller passes the `network-link` slice from
|
|
25073
|
+
* `useDeviceNetworkLink` and drops the badge in unconditionally.
|
|
25074
|
+
*/
|
|
25075
|
+
/** Monotonic — more signal, more arcs. */
|
|
25076
|
+
function pickWifiIcon(signalPercent) {
|
|
25077
|
+
if (signalPercent === null) return Wifi;
|
|
25078
|
+
if (signalPercent <= 20) return WifiZero;
|
|
25079
|
+
if (signalPercent <= 50) return WifiLow;
|
|
25080
|
+
if (signalPercent <= 80) return WifiHigh;
|
|
25081
|
+
return Wifi;
|
|
25082
|
+
}
|
|
25083
|
+
function signalColor(signalPercent) {
|
|
25084
|
+
if (signalPercent === null) return "text-foreground-subtle";
|
|
25085
|
+
if (signalPercent <= 20) return "text-rose-400";
|
|
25086
|
+
if (signalPercent <= 50) return "text-amber-400";
|
|
25087
|
+
return "text-foreground-subtle";
|
|
25088
|
+
}
|
|
25089
|
+
function NetworkLinkBadge({ status, className }) {
|
|
25090
|
+
if (!status || status.type === "unknown") return null;
|
|
25091
|
+
if (status.type === "ethernet") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
25092
|
+
className: cn("inline-flex items-center gap-1 text-[10px] font-medium text-foreground-subtle", className),
|
|
25093
|
+
title: "Network: ethernet",
|
|
25094
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Cable, { className: "h-3.5 w-3.5" })
|
|
25095
|
+
});
|
|
25096
|
+
const Icon = status.type === "cellular" ? RadioTower : pickWifiIcon(status.signalPercent);
|
|
25097
|
+
const signal = status.signalPercent === null ? null : Math.round(status.signalPercent);
|
|
25098
|
+
const title = `Network: ${status.type}` + (status.ssid ? ` (${status.ssid})` : "") + (signal === null ? "" : ` · signal ${signal}%`);
|
|
25099
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
25100
|
+
className: cn("inline-flex items-center gap-1 text-[10px] font-medium", signalColor(status.signalPercent), className),
|
|
25101
|
+
title,
|
|
25102
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { className: "h-3.5 w-3.5" }), signal !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [signal, "%"] })]
|
|
25103
|
+
});
|
|
25104
|
+
}
|
|
25105
|
+
//#endregion
|
|
24489
25106
|
//#region src/composites/breadcrumb.tsx
|
|
24490
25107
|
/**
|
|
24491
25108
|
* Breadcrumb navigation component.
|
|
@@ -42027,6 +42644,19 @@ function useDeviceBattery(trpc, deviceId) {
|
|
|
42027
42644
|
return useDeviceState(trpc, deviceId, (s) => s.battery) ?? null;
|
|
42028
42645
|
}
|
|
42029
42646
|
//#endregion
|
|
42647
|
+
//#region src/hooks/use-device-network-link.ts
|
|
42648
|
+
/**
|
|
42649
|
+
* useDeviceNetworkLink — the device's own network link (medium + signal).
|
|
42650
|
+
*
|
|
42651
|
+
* Same shape as `useDeviceBattery`: a thin wrapper over `useDeviceState`,
|
|
42652
|
+
* fed by the kernel runtime-state mirror (one fetch on first subscribe, then
|
|
42653
|
+
* push updates on every `network-link` slice write). Returns `null` when no
|
|
42654
|
+
* provider has emitted yet — a device without the cap simply has no badge.
|
|
42655
|
+
*/
|
|
42656
|
+
function useDeviceNetworkLink(trpc, deviceId) {
|
|
42657
|
+
return useDeviceState(trpc, deviceId, (s) => s.networkLink) ?? null;
|
|
42658
|
+
}
|
|
42659
|
+
//#endregion
|
|
42030
42660
|
//#region src/hooks/use-device-snapshot.ts
|
|
42031
42661
|
/**
|
|
42032
42662
|
* useDeviceSnapshotImage — a device's snapshot as a plain image URL.
|
|
@@ -42200,17 +42830,23 @@ function DeviceItemPreview({ trpc, device, status, enabled, showStatusPills = fa
|
|
|
42200
42830
|
const isBatteryOperated = (device.features ?? []).includes("battery-operated");
|
|
42201
42831
|
const wantPills = showStatusPills && isBatteryOperated && !device.disabled;
|
|
42202
42832
|
const batteryStatus = useDeviceBattery(trpc, wantPills ? device.id : null);
|
|
42203
|
-
|
|
42833
|
+
const wantLinkPill = showStatusPills && !device.disabled;
|
|
42834
|
+
const linkStatus = useDeviceNetworkLink(trpc, wantLinkPill ? device.id : null);
|
|
42835
|
+
if (!enabled && !wantPills && !(wantLinkPill && linkStatus)) return null;
|
|
42204
42836
|
const offline = status === "offline" || status === "disabled";
|
|
42205
42837
|
const placeholder = status === "offline" ? "Offline — no snapshot" : status === "disabled" ? "Disabled" : "No snapshot yet";
|
|
42206
42838
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
42207
42839
|
className: "inline-flex items-center gap-1.5 flex-nowrap whitespace-nowrap",
|
|
42208
|
-
children: [
|
|
42209
|
-
|
|
42210
|
-
|
|
42211
|
-
|
|
42212
|
-
|
|
42213
|
-
|
|
42840
|
+
children: [
|
|
42841
|
+
wantPills && batteryStatus && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BatteryBadge, { status: batteryStatus }),
|
|
42842
|
+
wantLinkPill && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NetworkLinkBadge, { status: linkStatus }),
|
|
42843
|
+
enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HoverZoomImage, {
|
|
42844
|
+
src: offline ? null : snapshot.src,
|
|
42845
|
+
alt: device.name,
|
|
42846
|
+
placeholder,
|
|
42847
|
+
thumbClassName: "h-7 w-7 rounded"
|
|
42848
|
+
})
|
|
42849
|
+
]
|
|
42214
42850
|
});
|
|
42215
42851
|
}
|
|
42216
42852
|
//#endregion
|
|
@@ -49664,6 +50300,9 @@ exports.Mic = Mic;
|
|
|
49664
50300
|
exports.MobileDrawer = MobileDrawer;
|
|
49665
50301
|
exports.ModelPicker = ModelPicker;
|
|
49666
50302
|
exports.MotionZonesSettings = MotionZonesSettings;
|
|
50303
|
+
exports.NAVIGATION_DRIVE_INTERVAL_MS = NAVIGATION_DRIVE_INTERVAL_MS;
|
|
50304
|
+
exports.NavigationPanel = NavigationPanel;
|
|
50305
|
+
exports.NetworkLinkBadge = NetworkLinkBadge;
|
|
49667
50306
|
exports.NodeMultiSelectField = NodeMultiSelectField;
|
|
49668
50307
|
exports.NodePicker = NodePicker;
|
|
49669
50308
|
exports.NodeSelectField = NodeSelectField;
|
|
@@ -50181,6 +50820,7 @@ exports.useDeviceManagerTestField = useDeviceManagerTestField;
|
|
|
50181
50820
|
exports.useDeviceManagerUpdateConfig = useDeviceManagerUpdateConfig;
|
|
50182
50821
|
exports.useDeviceManagerUpdateDeviceField = useDeviceManagerUpdateDeviceField;
|
|
50183
50822
|
exports.useDeviceManagerUpdateDeviceFieldsBatch = useDeviceManagerUpdateDeviceFieldsBatch;
|
|
50823
|
+
exports.useDeviceNetworkLink = useDeviceNetworkLink;
|
|
50184
50824
|
exports.useDeviceOpsGetConfigEntries = useDeviceOpsGetConfigEntries;
|
|
50185
50825
|
exports.useDeviceOpsGetRawState = useDeviceOpsGetRawState;
|
|
50186
50826
|
exports.useDeviceOpsGetSettingsSchema = useDeviceOpsGetSettingsSchema;
|
|
@@ -50381,11 +51021,24 @@ exports.useMqttBrokerStopEmbeddedBroker = useMqttBrokerStopEmbeddedBroker;
|
|
|
50381
51021
|
exports.useMqttBrokerTestConnection = useMqttBrokerTestConnection;
|
|
50382
51022
|
exports.useNativeObjectDetectionGetStatus = useNativeObjectDetectionGetStatus;
|
|
50383
51023
|
exports.useNativeObjectDetectionSetEnabled = useNativeObjectDetectionSetEnabled;
|
|
51024
|
+
exports.useNavigation = useNavigation;
|
|
51025
|
+
exports.useNavigationGetFeatures = useNavigationGetFeatures;
|
|
51026
|
+
exports.useNavigationGetStatus = useNavigationGetStatus;
|
|
51027
|
+
exports.useNavigationGoToPoint = useNavigationGoToPoint;
|
|
51028
|
+
exports.useNavigationListActions = useNavigationListActions;
|
|
51029
|
+
exports.useNavigationMove = useNavigationMove;
|
|
51030
|
+
exports.useNavigationPlaySound = useNavigationPlaySound;
|
|
51031
|
+
exports.useNavigationRunAction = useNavigationRunAction;
|
|
51032
|
+
exports.useNavigationSetLightLevel = useNavigationSetLightLevel;
|
|
51033
|
+
exports.useNavigationSetLightMode = useNavigationSetLightMode;
|
|
51034
|
+
exports.useNavigationSetLightOn = useNavigationSetLightOn;
|
|
51035
|
+
exports.useNavigationStop = useNavigationStop;
|
|
50384
51036
|
exports.useNetworkAccessGetEndpoint = useNetworkAccessGetEndpoint;
|
|
50385
51037
|
exports.useNetworkAccessGetStatus = useNetworkAccessGetStatus;
|
|
50386
51038
|
exports.useNetworkAccessListEndpoints = useNetworkAccessListEndpoints;
|
|
50387
51039
|
exports.useNetworkAccessStart = useNetworkAccessStart;
|
|
50388
51040
|
exports.useNetworkAccessStop = useNetworkAccessStop;
|
|
51041
|
+
exports.useNetworkLinkGetStatus = useNetworkLinkGetStatus;
|
|
50389
51042
|
exports.useNetworkQualityGetAllStats = useNetworkQualityGetAllStats;
|
|
50390
51043
|
exports.useNetworkQualityGetDeviceStats = useNetworkQualityGetDeviceStats;
|
|
50391
51044
|
exports.useNetworkQualityReportClientStats = useNetworkQualityReportClientStats;
|