@camstack/ui-library 1.2.321 → 1.2.323
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/detection-colors.d.ts +18 -8
- package/dist/composites/device-picker/DevicePicker.d.ts +14 -2
- package/dist/composites/device-picker/index.d.ts +1 -1
- package/dist/composites/display-palette-context.d.ts +26 -0
- package/dist/composites/index.d.ts +1 -0
- package/dist/generated/system-hooks.d.ts +6 -0
- package/dist/index.cjs +269 -82
- package/dist/index.js +265 -84
- package/dist/primitives/tabs-list-scroll.d.ts +19 -0
- package/dist/primitives/tabs.d.ts +2 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -11061,6 +11061,12 @@ var useDishwasherSetOption = trpc.dishwasher.setOption.useMutation;
|
|
|
11061
11061
|
var useDishwasherGetOptions = trpc.dishwasher.getOptions.useQuery;
|
|
11062
11062
|
/** Generated alias around `trpc.dishwasher.getStatus.useQuery`. */
|
|
11063
11063
|
var useDishwasherGetStatus = trpc.dishwasher.getStatus.useQuery;
|
|
11064
|
+
/** Generated alias around `trpc.displayPalette.getPalette.useQuery`. */
|
|
11065
|
+
var useDisplayPaletteGetPalette = trpc.displayPalette.getPalette.useQuery;
|
|
11066
|
+
/** Generated alias around `trpc.displayPalette.listPresets.useQuery`. */
|
|
11067
|
+
var useDisplayPaletteListPresets = trpc.displayPalette.listPresets.useQuery;
|
|
11068
|
+
/** Generated alias around `trpc.displayPalette.setPalette.useMutation`. */
|
|
11069
|
+
var useDisplayPaletteSetPalette = trpc.displayPalette.setPalette.useMutation;
|
|
11064
11070
|
/** Generated alias around `trpc.doorbell.getStatus.useQuery`. */
|
|
11065
11071
|
var useDoorbellGetStatus = trpc.doorbell.getStatus.useQuery;
|
|
11066
11072
|
/** Generated alias around `trpc.enumSensor.getStatus.useQuery`. */
|
|
@@ -33143,6 +33149,143 @@ var Switch = (0, react$1.forwardRef)(({ className, checked, onCheckedChange, lab
|
|
|
33143
33149
|
});
|
|
33144
33150
|
Switch.displayName = "Switch";
|
|
33145
33151
|
//#endregion
|
|
33152
|
+
//#region src/primitives/tabs-list-scroll.tsx
|
|
33153
|
+
/** Scroll offsets and sizes — jsdom defaults are 0, so tests set these explicitly. */
|
|
33154
|
+
function readTabStripOverflow(el) {
|
|
33155
|
+
const epsilon = 1;
|
|
33156
|
+
const { scrollLeft, scrollWidth, clientWidth } = el;
|
|
33157
|
+
const hasOverflow = scrollWidth > clientWidth + epsilon;
|
|
33158
|
+
return {
|
|
33159
|
+
overflowLeft: hasOverflow && scrollLeft > epsilon,
|
|
33160
|
+
overflowRight: hasOverflow && scrollLeft + clientWidth < scrollWidth - epsilon
|
|
33161
|
+
};
|
|
33162
|
+
}
|
|
33163
|
+
function scrollTabIntoViewNearest(tab) {
|
|
33164
|
+
if (typeof tab.scrollIntoView === "function") tab.scrollIntoView({
|
|
33165
|
+
inline: "nearest",
|
|
33166
|
+
block: "nearest"
|
|
33167
|
+
});
|
|
33168
|
+
}
|
|
33169
|
+
function scrollStripByOneTab(el, direction) {
|
|
33170
|
+
const tabs = el.querySelectorAll("[role=\"tab\"]");
|
|
33171
|
+
if (tabs.length === 0) return;
|
|
33172
|
+
const scrollLeft = el.scrollLeft;
|
|
33173
|
+
if (direction === "right") {
|
|
33174
|
+
for (const tab of tabs) if (tab.offsetLeft + tab.offsetWidth > scrollLeft + 1) {
|
|
33175
|
+
scrollTabIntoViewNearest(tab);
|
|
33176
|
+
return;
|
|
33177
|
+
}
|
|
33178
|
+
return;
|
|
33179
|
+
}
|
|
33180
|
+
for (let i = tabs.length - 1; i >= 0; i--) {
|
|
33181
|
+
const tab = tabs[i];
|
|
33182
|
+
if (tab === void 0) continue;
|
|
33183
|
+
if (tab.offsetLeft < scrollLeft - 1) {
|
|
33184
|
+
scrollTabIntoViewNearest(tab);
|
|
33185
|
+
return;
|
|
33186
|
+
}
|
|
33187
|
+
}
|
|
33188
|
+
}
|
|
33189
|
+
function scrollActiveTabIntoView(el) {
|
|
33190
|
+
const active = el.querySelector("[role=\"tab\"][aria-selected=\"true\"]");
|
|
33191
|
+
if (active !== null && typeof active.scrollIntoView === "function") active.scrollIntoView({
|
|
33192
|
+
inline: "nearest",
|
|
33193
|
+
block: "nearest"
|
|
33194
|
+
});
|
|
33195
|
+
}
|
|
33196
|
+
/**
|
|
33197
|
+
* Horizontally scrollable tab strip with edge fades when tabs are hidden and,
|
|
33198
|
+
* on fine-pointer devices, chevron buttons that scroll one tab at a time.
|
|
33199
|
+
* Touch keeps native swipe — no chevrons on coarse pointers.
|
|
33200
|
+
*/
|
|
33201
|
+
function TabsListScrollShell({ activeTabKey, listClassName, className, children, ...props }) {
|
|
33202
|
+
const scrollRef = (0, react$1.useRef)(null);
|
|
33203
|
+
const [overflow, setOverflow] = (0, react$1.useState)({
|
|
33204
|
+
overflowLeft: false,
|
|
33205
|
+
overflowRight: false
|
|
33206
|
+
});
|
|
33207
|
+
const [finePointer, setFinePointer] = (0, react$1.useState)(false);
|
|
33208
|
+
const remeasure = (0, react$1.useCallback)(() => {
|
|
33209
|
+
const el = scrollRef.current;
|
|
33210
|
+
if (el === null) return;
|
|
33211
|
+
setOverflow(readTabStripOverflow(el));
|
|
33212
|
+
}, []);
|
|
33213
|
+
(0, react$1.useEffect)(() => {
|
|
33214
|
+
remeasure();
|
|
33215
|
+
const el = scrollRef.current;
|
|
33216
|
+
if (el === null) return;
|
|
33217
|
+
el.addEventListener("scroll", remeasure, { passive: true });
|
|
33218
|
+
const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(remeasure) : null;
|
|
33219
|
+
ro?.observe(el);
|
|
33220
|
+
for (const child of el.children) ro?.observe(child);
|
|
33221
|
+
return () => {
|
|
33222
|
+
el.removeEventListener("scroll", remeasure);
|
|
33223
|
+
ro?.disconnect();
|
|
33224
|
+
};
|
|
33225
|
+
}, [remeasure, children]);
|
|
33226
|
+
(0, react$1.useEffect)(() => {
|
|
33227
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
33228
|
+
const mq = window.matchMedia("(hover: hover) and (pointer: fine)");
|
|
33229
|
+
const apply = () => {
|
|
33230
|
+
setFinePointer(mq.matches);
|
|
33231
|
+
};
|
|
33232
|
+
apply();
|
|
33233
|
+
mq.addEventListener("change", apply);
|
|
33234
|
+
return () => mq.removeEventListener("change", apply);
|
|
33235
|
+
}, []);
|
|
33236
|
+
(0, react$1.useEffect)(() => {
|
|
33237
|
+
const el = scrollRef.current;
|
|
33238
|
+
if (el === null || activeTabKey === void 0) return;
|
|
33239
|
+
scrollActiveTabIntoView(el);
|
|
33240
|
+
remeasure();
|
|
33241
|
+
}, [activeTabKey, remeasure]);
|
|
33242
|
+
const scrollBy = (direction) => {
|
|
33243
|
+
const el = scrollRef.current;
|
|
33244
|
+
if (el === null) return;
|
|
33245
|
+
scrollStripByOneTab(el, direction);
|
|
33246
|
+
};
|
|
33247
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33248
|
+
className: cn("relative min-w-0", className),
|
|
33249
|
+
"data-tab-strip-overflow-left": overflow.overflowLeft ? "" : void 0,
|
|
33250
|
+
"data-tab-strip-overflow-right": overflow.overflowRight ? "" : void 0,
|
|
33251
|
+
children: [
|
|
33252
|
+
overflow.overflowLeft && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
33253
|
+
"aria-hidden": true,
|
|
33254
|
+
className: "pointer-events-none absolute inset-y-0 left-0 z-[1] w-8 bg-gradient-to-r from-background to-transparent"
|
|
33255
|
+
}),
|
|
33256
|
+
overflow.overflowRight && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
33257
|
+
"aria-hidden": true,
|
|
33258
|
+
className: "pointer-events-none absolute inset-y-0 right-0 z-[1] w-8 bg-gradient-to-l from-background to-transparent"
|
|
33259
|
+
}),
|
|
33260
|
+
finePointer && overflow.overflowLeft && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
|
|
33261
|
+
type: "button",
|
|
33262
|
+
variant: "ghost",
|
|
33263
|
+
size: "sm",
|
|
33264
|
+
icon: ChevronLeft,
|
|
33265
|
+
"aria-label": "Scroll tabs left",
|
|
33266
|
+
className: "absolute left-0 top-1/2 z-[2] -translate-y-1/2 bg-background/80 shadow-sm",
|
|
33267
|
+
onClick: () => scrollBy("left")
|
|
33268
|
+
}),
|
|
33269
|
+
finePointer && overflow.overflowRight && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
|
|
33270
|
+
type: "button",
|
|
33271
|
+
variant: "ghost",
|
|
33272
|
+
size: "sm",
|
|
33273
|
+
icon: ChevronRight,
|
|
33274
|
+
"aria-label": "Scroll tabs right",
|
|
33275
|
+
className: "absolute right-0 top-1/2 z-[2] -translate-y-1/2 bg-background/80 shadow-sm",
|
|
33276
|
+
onClick: () => scrollBy("right")
|
|
33277
|
+
}),
|
|
33278
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
33279
|
+
ref: scrollRef,
|
|
33280
|
+
role: "tablist",
|
|
33281
|
+
className: listClassName,
|
|
33282
|
+
...props,
|
|
33283
|
+
children
|
|
33284
|
+
})
|
|
33285
|
+
]
|
|
33286
|
+
});
|
|
33287
|
+
}
|
|
33288
|
+
//#endregion
|
|
33146
33289
|
//#region src/primitives/tabs.tsx
|
|
33147
33290
|
var TabsContext = (0, react$1.createContext)(null);
|
|
33148
33291
|
function useTabsContext() {
|
|
@@ -33187,17 +33330,18 @@ function Tabs({ value: controlledValue, onValueChange, defaultValue = "", appear
|
|
|
33187
33330
|
* pinned to `hidden` makes it impossible here.
|
|
33188
33331
|
*/
|
|
33189
33332
|
function TabsList({ className, ...props }) {
|
|
33190
|
-
const { appearance } = useTabsContext();
|
|
33191
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
33192
|
-
|
|
33193
|
-
|
|
33333
|
+
const { appearance, value } = useTabsContext();
|
|
33334
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TabsListScrollShell, {
|
|
33335
|
+
activeTabKey: value,
|
|
33336
|
+
listClassName: cn("flex flex-row overflow-x-auto overflow-y-hidden", appearance === "pills" ? "gap-1 border-b border-border-subtle py-1" : "border-b border-border-subtle", className),
|
|
33194
33337
|
...props
|
|
33195
33338
|
});
|
|
33196
33339
|
}
|
|
33197
|
-
function TabsTrigger({ value, className, ...props }) {
|
|
33340
|
+
var TabsTrigger = (0, react$1.forwardRef)(function TabsTrigger({ value, className, ...props }, ref) {
|
|
33198
33341
|
const { value: activeValue, setValue, appearance } = useTabsContext();
|
|
33199
33342
|
const isActive = value === activeValue;
|
|
33200
33343
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
33344
|
+
ref,
|
|
33201
33345
|
type: "button",
|
|
33202
33346
|
role: "tab",
|
|
33203
33347
|
"aria-selected": isActive,
|
|
@@ -33207,7 +33351,7 @@ function TabsTrigger({ value, className, ...props }) {
|
|
|
33207
33351
|
onClick: () => setValue(value),
|
|
33208
33352
|
...props
|
|
33209
33353
|
});
|
|
33210
|
-
}
|
|
33354
|
+
});
|
|
33211
33355
|
function TabsContent({ value, className, ...props }) {
|
|
33212
33356
|
const { value: activeValue } = useTabsContext();
|
|
33213
33357
|
if (value !== activeValue) return null;
|
|
@@ -39971,58 +40115,66 @@ function useDataTableFilter(rows, config, initialState = EMPTY_DATA_TABLE_FILTER
|
|
|
39971
40115
|
//#endregion
|
|
39972
40116
|
//#region src/composites/detection-colors.ts
|
|
39973
40117
|
/**
|
|
39974
|
-
* Detection class
|
|
39975
|
-
* taxonomy dictionary in `@camstack/types` (`EVENT_TAXONOMY`). Colors are no
|
|
39976
|
-
* longer duplicated here; this module only adds the UI-only classes the
|
|
39977
|
-
* taxonomy doesn't model (face / plate) and the deterministic hash fallback.
|
|
40118
|
+
* Detection class colours — the bundled display palette (D939).
|
|
39978
40119
|
*
|
|
39979
|
-
*
|
|
39980
|
-
*
|
|
40120
|
+
* A mounted {@link DisplayPaletteProvider} paints the cluster's effective
|
|
40121
|
+
* palette instead. There is no second hex table here.
|
|
39981
40122
|
*/
|
|
39982
|
-
|
|
39983
|
-
|
|
39984
|
-
|
|
39985
|
-
|
|
39986
|
-
|
|
40123
|
+
var BUNDLED$1 = (0, _camstack_types.bundledCamstack)();
|
|
40124
|
+
/** Marks that are palette entries and not detection classes. */
|
|
40125
|
+
var MARK_IDS = new Set([
|
|
40126
|
+
"live",
|
|
40127
|
+
"recording",
|
|
40128
|
+
"heatmapScrub"
|
|
40129
|
+
]);
|
|
39987
40130
|
/**
|
|
39988
|
-
* Fixed
|
|
39989
|
-
*
|
|
39990
|
-
* a named export for back-compat with existing consumers.
|
|
40131
|
+
* Fixed colours for known classes, from the bundled CamStack preset.
|
|
40132
|
+
* Kept for callers that read the map directly.
|
|
39991
40133
|
*/
|
|
39992
|
-
var CLASS_COLORS = Object.freeze(
|
|
39993
|
-
|
|
39994
|
-
|
|
39995
|
-
|
|
39996
|
-
|
|
39997
|
-
|
|
39998
|
-
|
|
39999
|
-
|
|
40000
|
-
|
|
40001
|
-
|
|
40002
|
-
|
|
40003
|
-
|
|
40004
|
-
|
|
40005
|
-
|
|
40006
|
-
|
|
40007
|
-
|
|
40008
|
-
|
|
40009
|
-
|
|
40010
|
-
|
|
40011
|
-
|
|
40012
|
-
|
|
40013
|
-
|
|
40014
|
-
|
|
40015
|
-
|
|
40016
|
-
|
|
40017
|
-
|
|
40018
|
-
|
|
40019
|
-
|
|
40020
|
-
|
|
40021
|
-
|
|
40022
|
-
|
|
40023
|
-
|
|
40024
|
-
|
|
40025
|
-
|
|
40134
|
+
var CLASS_COLORS = Object.freeze(Object.fromEntries(_camstack_types.PALETTE_ENTRY_IDS.filter((id) => !MARK_IDS.has(id)).map((id) => [id, (0, _camstack_types.paintHex)(BUNDLED$1.swatches, BUNDLED$1.fallbacks, id)])));
|
|
40135
|
+
function firstFallback(fallbacks) {
|
|
40136
|
+
const hex = fallbacks[0];
|
|
40137
|
+
if (hex === void 0) throw new Error("display palette has no fallback colour");
|
|
40138
|
+
return hex;
|
|
40139
|
+
}
|
|
40140
|
+
/** Primary colour when nothing else matches — the first bundled fallback. */
|
|
40141
|
+
var DEFAULT_COLOR = firstFallback(BUNDLED$1.fallbacks);
|
|
40142
|
+
/**
|
|
40143
|
+
* Colour for a detection class. Custom overrides win, then the palette
|
|
40144
|
+
* (the bundled preset when none is passed). Unknown classes hash into
|
|
40145
|
+
* the palette's fallbacks.
|
|
40146
|
+
*/
|
|
40147
|
+
function getClassColor(className, customColors, palette) {
|
|
40148
|
+
const direct = customColors?.[className];
|
|
40149
|
+
if (direct !== void 0 && direct.length > 0) return direct;
|
|
40150
|
+
const lower = className.toLowerCase();
|
|
40151
|
+
const lowered = customColors?.[lower];
|
|
40152
|
+
if (lowered !== void 0 && lowered.length > 0) return lowered;
|
|
40153
|
+
return (0, _camstack_types.paintHex)(palette?.swatches ?? BUNDLED$1.swatches, palette?.fallbacks ?? BUNDLED$1.fallbacks, className);
|
|
40154
|
+
}
|
|
40155
|
+
//#endregion
|
|
40156
|
+
//#region src/composites/display-palette-context.tsx
|
|
40157
|
+
/**
|
|
40158
|
+
* The cluster palette, when a host has read `display-palette` (D939).
|
|
40159
|
+
* No provider, a pending read, or an unknown store all paint the bundled
|
|
40160
|
+
* CamStack preset. Only `ready` replaces it. Callers that must say the
|
|
40161
|
+
* store was unreadable read {@link useDisplayPaletteView}.
|
|
40162
|
+
*/
|
|
40163
|
+
var BUNDLED = (0, _camstack_types.bundledCamstack)();
|
|
40164
|
+
var DisplayPaletteContext = (0, react$1.createContext)({ status: "absent" });
|
|
40165
|
+
function DisplayPaletteProvider({ value, children }) {
|
|
40166
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DisplayPaletteContext.Provider, {
|
|
40167
|
+
value,
|
|
40168
|
+
children
|
|
40169
|
+
});
|
|
40170
|
+
}
|
|
40171
|
+
function useDisplayPaletteView() {
|
|
40172
|
+
return (0, react$1.useContext)(DisplayPaletteContext);
|
|
40173
|
+
}
|
|
40174
|
+
function useClassPainter() {
|
|
40175
|
+
const view = (0, react$1.useContext)(DisplayPaletteContext);
|
|
40176
|
+
const palette = view.status === "ready" ? view.palette : BUNDLED;
|
|
40177
|
+
return (className, customColors) => getClassColor(className, customColors, palette);
|
|
40026
40178
|
}
|
|
40027
40179
|
//#endregion
|
|
40028
40180
|
//#region src/composites/detection-canvas.tsx
|
|
@@ -40037,8 +40189,9 @@ function getClassColor(className, customColors) {
|
|
|
40037
40189
|
*/
|
|
40038
40190
|
function DetectionCanvas({ src, imageWidth, imageHeight, detections = [], classColors, aspectRatio, className, placeholder, showConfidence = true, minConfidence = 0, borderWidth = 2 }) {
|
|
40039
40191
|
const t = useUiText();
|
|
40192
|
+
const paint = useClassPainter();
|
|
40040
40193
|
function getColor(cls) {
|
|
40041
|
-
return
|
|
40194
|
+
return paint(cls, classColors);
|
|
40042
40195
|
}
|
|
40043
40196
|
const ratio = aspectRatio ?? (imageWidth && imageHeight ? `${imageWidth}/${imageHeight}` : "16/9");
|
|
40044
40197
|
const filtered = detections.filter((d) => d.score >= minConfidence);
|
|
@@ -40095,6 +40248,7 @@ function DetectionCanvas({ src, imageWidth, imageHeight, detections = [], classC
|
|
|
40095
40248
|
});
|
|
40096
40249
|
}
|
|
40097
40250
|
function BoundingBox({ detection, imageWidth, imageHeight, color, showConfidence, borderWidth, children }) {
|
|
40251
|
+
const paint = useClassPainter();
|
|
40098
40252
|
const { x, y, width, height } = detection.bbox;
|
|
40099
40253
|
const labelsElement = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
40100
40254
|
className: "absolute left-0 flex flex-col items-start gap-px",
|
|
@@ -40108,7 +40262,7 @@ function BoundingBox({ detection, imageWidth, imageHeight, color, showConfidence
|
|
|
40108
40262
|
children: [detection.macroClass, showConfidence && ` ${(detection.score * 100).toFixed(0)}%`]
|
|
40109
40263
|
}), detection.labels.map((l, k) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
40110
40264
|
className: "text-[9px] font-semibold px-1 rounded-sm whitespace-nowrap text-white",
|
|
40111
|
-
style: { backgroundColor:
|
|
40265
|
+
style: { backgroundColor: paint(l.label) },
|
|
40112
40266
|
children: [
|
|
40113
40267
|
l.label,
|
|
40114
40268
|
" ",
|
|
@@ -40179,6 +40333,7 @@ function MaskOverlay({ mask, maskWidth, maskHeight, bbox, imageWidth, imageHeigh
|
|
|
40179
40333
|
});
|
|
40180
40334
|
}
|
|
40181
40335
|
function ChildBoundingBox({ child, parentBbox, color, showConfidence }) {
|
|
40336
|
+
const paint = useClassPainter();
|
|
40182
40337
|
if (parentBbox.width <= 0 || parentBbox.height <= 0) return null;
|
|
40183
40338
|
const relLeft = (child.bbox.x - parentBbox.x) / parentBbox.width * 100;
|
|
40184
40339
|
const relTop = (child.bbox.y - parentBbox.y) / parentBbox.height * 100;
|
|
@@ -40207,7 +40362,7 @@ function ChildBoundingBox({ child, parentBbox, color, showConfidence }) {
|
|
|
40207
40362
|
children: [child.macroClass, showConfidence && ` ${(child.score * 100).toFixed(0)}%`]
|
|
40208
40363
|
}), child.labels.map((l, k) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
40209
40364
|
className: "text-[8px] font-semibold px-0.5 rounded-sm whitespace-nowrap text-white",
|
|
40210
|
-
style: { backgroundColor:
|
|
40365
|
+
style: { backgroundColor: paint(l.label) },
|
|
40211
40366
|
children: [
|
|
40212
40367
|
l.label,
|
|
40213
40368
|
" ",
|
|
@@ -40385,9 +40540,10 @@ function MotionZones({ motion }) {
|
|
|
40385
40540
|
* one a detection box can never answer.
|
|
40386
40541
|
*/
|
|
40387
40542
|
function StationaryBoxes({ boxes }) {
|
|
40543
|
+
const paint = useClassPainter();
|
|
40388
40544
|
const now = Date.now();
|
|
40389
40545
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: boxes.map((b) => {
|
|
40390
|
-
const color =
|
|
40546
|
+
const color = paint(b.className);
|
|
40391
40547
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
40392
40548
|
"data-census-box": "",
|
|
40393
40549
|
className: "absolute",
|
|
@@ -40418,6 +40574,7 @@ function StationaryBoxes({ boxes }) {
|
|
|
40418
40574
|
}) });
|
|
40419
40575
|
}
|
|
40420
40576
|
function DetectionBoxes({ detections, frameWidth, frameHeight }) {
|
|
40577
|
+
const paint = useClassPainter();
|
|
40421
40578
|
if (frameWidth === 0 || frameHeight === 0) return null;
|
|
40422
40579
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: detections.map((d) => {
|
|
40423
40580
|
const { x, y, width: bw, height: bh } = d.bbox;
|
|
@@ -40425,7 +40582,7 @@ function DetectionBoxes({ detections, frameWidth, frameHeight }) {
|
|
|
40425
40582
|
const top = y / frameHeight * 100;
|
|
40426
40583
|
const width = bw / frameWidth * 100;
|
|
40427
40584
|
const height = bh / frameHeight * 100;
|
|
40428
|
-
const color =
|
|
40585
|
+
const color = paint(d.macroClass);
|
|
40429
40586
|
const parts = [`${d.macroClass} ${(d.score * 100).toFixed(0)}%`];
|
|
40430
40587
|
for (const l of d.labels) parts.push(l.label);
|
|
40431
40588
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -40500,7 +40657,8 @@ function DetectionResultTree({ detections, classColors, className, hiddenKeys, o
|
|
|
40500
40657
|
});
|
|
40501
40658
|
}
|
|
40502
40659
|
function DetectionNode({ detection, children_, colors, hiddenKeys, onToggleVisibility }) {
|
|
40503
|
-
const
|
|
40660
|
+
const paint = useClassPainter();
|
|
40661
|
+
const color = paint(detection.macroClass, colors);
|
|
40504
40662
|
const isVisible = !hiddenKeys?.has(detection.id);
|
|
40505
40663
|
const mask = detection.debug?.mask;
|
|
40506
40664
|
const maskWidth = detection.debug?.maskWidth;
|
|
@@ -40565,8 +40723,8 @@ function DetectionNode({ detection, children_, colors, hiddenKeys, onToggleVisib
|
|
|
40565
40723
|
children: detection.labels.map((l, k) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
40566
40724
|
className: "inline-flex items-center gap-1 text-[10px] font-medium px-1.5 py-0.5 rounded-full",
|
|
40567
40725
|
style: {
|
|
40568
|
-
backgroundColor:
|
|
40569
|
-
color:
|
|
40726
|
+
backgroundColor: paint(l.label, colors) + "20",
|
|
40727
|
+
color: paint(l.label, colors)
|
|
40570
40728
|
},
|
|
40571
40729
|
children: [
|
|
40572
40730
|
l.label,
|
|
@@ -40595,10 +40753,11 @@ function DetectionNode({ detection, children_, colors, hiddenKeys, onToggleVisib
|
|
|
40595
40753
|
});
|
|
40596
40754
|
}
|
|
40597
40755
|
function ChildrenTree({ items, colors, hiddenKeys, onToggleVisibility }) {
|
|
40756
|
+
const paint = useClassPainter();
|
|
40598
40757
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
40599
40758
|
className: "ml-4 mt-1.5 space-y-1.5 border-l-2 border-border pl-3",
|
|
40600
40759
|
children: items.map((child) => {
|
|
40601
|
-
const childColor =
|
|
40760
|
+
const childColor = paint(child.macroClass, colors);
|
|
40602
40761
|
const isVisible = !hiddenKeys?.has(child.id);
|
|
40603
40762
|
const mask = child.debug?.mask;
|
|
40604
40763
|
const maskWidth = child.debug?.maskWidth;
|
|
@@ -40642,8 +40801,8 @@ function ChildrenTree({ items, colors, hiddenKeys, onToggleVisibility }) {
|
|
|
40642
40801
|
children: child.labels.map((l, k) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
40643
40802
|
className: "inline-flex items-center gap-0.5 text-[9px] font-medium px-1 py-0.5 rounded-full",
|
|
40644
40803
|
style: {
|
|
40645
|
-
backgroundColor:
|
|
40646
|
-
color:
|
|
40804
|
+
backgroundColor: paint(l.label, colors) + "20",
|
|
40805
|
+
color: paint(l.label, colors)
|
|
40647
40806
|
},
|
|
40648
40807
|
children: [
|
|
40649
40808
|
l.label,
|
|
@@ -40661,6 +40820,7 @@ function ChildrenTree({ items, colors, hiddenKeys, onToggleVisibility }) {
|
|
|
40661
40820
|
}
|
|
40662
40821
|
function AlternateLabelsSection({ alternateLabels, colors }) {
|
|
40663
40822
|
const [expanded, setExpanded] = (0, react$1.useState)(false);
|
|
40823
|
+
const paint = useClassPainter();
|
|
40664
40824
|
const t = useUiText();
|
|
40665
40825
|
const sources = Object.keys(alternateLabels);
|
|
40666
40826
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -40688,8 +40848,8 @@ function AlternateLabelsSection({ alternateLabels, colors }) {
|
|
|
40688
40848
|
children: labels.map((l, k) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
40689
40849
|
className: "inline-flex items-center gap-0.5 text-[9px] px-1 py-0.5 rounded-full",
|
|
40690
40850
|
style: {
|
|
40691
|
-
backgroundColor:
|
|
40692
|
-
color:
|
|
40851
|
+
backgroundColor: paint(source, colors) + "10",
|
|
40852
|
+
color: paint(source, colors),
|
|
40693
40853
|
opacity: Math.max(.4, l.score)
|
|
40694
40854
|
},
|
|
40695
40855
|
children: [l.label, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
@@ -65327,7 +65487,7 @@ function noItemsSentence(noun, emptyMeansAll, t) {
|
|
|
65327
65487
|
noun: pickerNounLabel(noun, t)
|
|
65328
65488
|
});
|
|
65329
65489
|
}
|
|
65330
|
-
function DevicePicker({ devices: devicesIn, listState, fleet, value, onChange, emptyMeansAll = false, savedHadDevices = false, noun = "camera", groupTitle: groupTitleProp, emptyLabel, annotate, disabled = false, testId, showAll = false, lockedIds, lockedNote: lockedNoteProp }) {
|
|
65490
|
+
function DevicePicker({ devices: devicesIn, listState, fleet, value, onChange, selection = "multiple", groupBy: pinnedGroupBy, emptyMeansAll = false, savedHadDevices = false, noun = "camera", groupTitle: groupTitleProp, emptyLabel, annotate, disabled = false, testId, showAll = false, lockedIds, lockedNote: lockedNoteProp }) {
|
|
65331
65491
|
const t = useUiText();
|
|
65332
65492
|
const groupTitle = groupTitleProp ?? ((group) => deviceGroupTitle(group, t));
|
|
65333
65493
|
const lockedNote = lockedNoteProp ?? t("uiLibrary.devicePicker.alreadyAdded", "Already added");
|
|
@@ -65360,7 +65520,7 @@ function DevicePicker({ devices: devicesIn, listState, fleet, value, onChange, e
|
|
|
65360
65520
|
children: [
|
|
65361
65521
|
listState === "pending" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DevicesLoadingLine, { noun: plural }),
|
|
65362
65522
|
listState === "failed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DevicesFailedLine, { noun: noun === "camera" ? "devices" : plural }),
|
|
65363
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
65523
|
+
!(selection === "single" && chips.length === 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
65364
65524
|
className: "flex flex-wrap items-center gap-1",
|
|
65365
65525
|
children: [
|
|
65366
65526
|
chips.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -65404,7 +65564,9 @@ function DevicePicker({ devices: devicesIn, listState, fleet, value, onChange, e
|
|
|
65404
65564
|
groupTitle,
|
|
65405
65565
|
noun,
|
|
65406
65566
|
disabled,
|
|
65407
|
-
showAll
|
|
65567
|
+
showAll,
|
|
65568
|
+
selection,
|
|
65569
|
+
...pinnedGroupBy !== void 0 ? { pinnedGroupBy } : {}
|
|
65408
65570
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PickerPanel, {
|
|
65409
65571
|
devices,
|
|
65410
65572
|
value,
|
|
@@ -65415,7 +65577,8 @@ function DevicePicker({ devices: devicesIn, listState, fleet, value, onChange, e
|
|
|
65415
65577
|
groupTitle,
|
|
65416
65578
|
noun,
|
|
65417
65579
|
disabled,
|
|
65418
|
-
showAll
|
|
65580
|
+
showAll,
|
|
65581
|
+
selection
|
|
65419
65582
|
})),
|
|
65420
65583
|
widening && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DevicesWidenWarning, { noun })
|
|
65421
65584
|
]
|
|
@@ -65426,13 +65589,23 @@ function DevicePicker({ devices: devicesIn, listState, fleet, value, onChange, e
|
|
|
65426
65589
|
* read below happens only then (for Zone grouping and the Zone filter).
|
|
65427
65590
|
*/
|
|
65428
65591
|
function GroupedPickerPanel(props) {
|
|
65429
|
-
const
|
|
65592
|
+
const stored = useStoredGroupBy();
|
|
65593
|
+
const [localGroupBy, setLocalGroupBy] = (0, react$1.useState)(null);
|
|
65594
|
+
const pinned = props.pinnedGroupBy;
|
|
65595
|
+
const groupBy = pinned !== void 0 ? localGroupBy ?? pinned : stored.groupBy;
|
|
65596
|
+
const onGroupByChange = (next) => {
|
|
65597
|
+
if (pinned !== void 0) {
|
|
65598
|
+
setLocalGroupBy(next);
|
|
65599
|
+
return;
|
|
65600
|
+
}
|
|
65601
|
+
stored.setGroupBy(next);
|
|
65602
|
+
};
|
|
65430
65603
|
const zones = usePickerSiteZones(true);
|
|
65431
65604
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PickerPanel, {
|
|
65432
65605
|
...props,
|
|
65433
65606
|
grouping: {
|
|
65434
65607
|
groupBy,
|
|
65435
|
-
onGroupByChange
|
|
65608
|
+
onGroupByChange,
|
|
65436
65609
|
zones
|
|
65437
65610
|
}
|
|
65438
65611
|
});
|
|
@@ -65450,8 +65623,9 @@ var NO_ZONES = {
|
|
|
65450
65623
|
zones: []
|
|
65451
65624
|
};
|
|
65452
65625
|
/** The searchable, grouped list — rendered one page at a time. */
|
|
65453
|
-
function PickerPanel({ devices, value, onChange, annotate, groupTitle, noun, disabled, locked, lockedNote, showAll, grouping }) {
|
|
65626
|
+
function PickerPanel({ devices, value, onChange, annotate, groupTitle, noun, disabled, locked, lockedNote, showAll, selection, grouping }) {
|
|
65454
65627
|
const t = useUiText();
|
|
65628
|
+
const radioName = (0, react$1.useId)();
|
|
65455
65629
|
const [filters, setFilters] = (0, react$1.useState)(PICKER_NO_FILTERS);
|
|
65456
65630
|
const [toggles, setToggles] = (0, react$1.useState)(/* @__PURE__ */ new Map());
|
|
65457
65631
|
const [shown, setShown] = (0, react$1.useState)(60);
|
|
@@ -65525,7 +65699,10 @@ function PickerPanel({ devices, value, onChange, annotate, groupTitle, noun, dis
|
|
|
65525
65699
|
}),
|
|
65526
65700
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
65527
65701
|
className: "mt-1 text-[10px] text-foreground-subtle",
|
|
65528
|
-
children: t("uiLibrary.devicePicker.
|
|
65702
|
+
children: selection === "single" ? t("uiLibrary.devicePicker.shownOfSingle", "{{shown}} of {{total}} shown", {
|
|
65703
|
+
shown: matches.length,
|
|
65704
|
+
total: devices.length
|
|
65705
|
+
}) : t("uiLibrary.devicePicker.shownOf", "{{shown}} of {{total}} shown · {{selected}} selected", {
|
|
65529
65706
|
shown: matches.length,
|
|
65530
65707
|
total: devices.length,
|
|
65531
65708
|
selected: value.length
|
|
@@ -65547,7 +65724,9 @@ function PickerPanel({ devices, value, onChange, annotate, groupTitle, noun, dis
|
|
|
65547
65724
|
groupBy: shownBy,
|
|
65548
65725
|
disabled,
|
|
65549
65726
|
locked,
|
|
65550
|
-
lockedNote
|
|
65727
|
+
lockedNote,
|
|
65728
|
+
selection,
|
|
65729
|
+
radioName
|
|
65551
65730
|
}, row.key)), page.remaining > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
65552
65731
|
type: "button",
|
|
65553
65732
|
className: "mt-1 w-full rounded border border-border py-1 text-[11px] text-foreground-subtle hover:bg-surface-hover",
|
|
@@ -65577,8 +65756,9 @@ function secondLine(device, groupBy) {
|
|
|
65577
65756
|
].filter((part) => part !== null);
|
|
65578
65757
|
return parts.length > 0 ? parts.join(" · ") : void 0;
|
|
65579
65758
|
}
|
|
65580
|
-
function PickerRowView({ row, value, onChange, onToggleGroup, annotate, groupTitle, groupBy, disabled, locked, lockedNote }) {
|
|
65759
|
+
function PickerRowView({ row, value, onChange, onToggleGroup, annotate, groupTitle, groupBy, disabled, locked, lockedNote, selection, radioName }) {
|
|
65581
65760
|
const t = useUiText();
|
|
65761
|
+
const single = selection === "single";
|
|
65582
65762
|
if (row.kind === "header") {
|
|
65583
65763
|
const { group, expanded } = row;
|
|
65584
65764
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -65596,7 +65776,7 @@ function PickerRowView({ row, value, onChange, onToggleGroup, annotate, groupTit
|
|
|
65596
65776
|
className: "truncate",
|
|
65597
65777
|
children: groupTitle(group)
|
|
65598
65778
|
}),
|
|
65599
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
65779
|
+
!single && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
65600
65780
|
className: "font-normal normal-case",
|
|
65601
65781
|
children: [
|
|
65602
65782
|
"(",
|
|
@@ -65608,14 +65788,14 @@ function PickerRowView({ row, value, onChange, onToggleGroup, annotate, groupTit
|
|
|
65608
65788
|
})
|
|
65609
65789
|
]
|
|
65610
65790
|
}),
|
|
65611
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
65791
|
+
!single && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
65612
65792
|
type: "button",
|
|
65613
65793
|
disabled,
|
|
65614
65794
|
className: "text-[10px] text-primary hover:underline disabled:opacity-50",
|
|
65615
65795
|
onClick: () => onChange(selectPickerGroup(value, group)),
|
|
65616
65796
|
children: t("uiLibrary.devicePicker.all", "All")
|
|
65617
65797
|
}),
|
|
65618
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
65798
|
+
!single && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
65619
65799
|
type: "button",
|
|
65620
65800
|
disabled,
|
|
65621
65801
|
className: "text-[10px] text-foreground-subtle hover:underline disabled:opacity-50",
|
|
@@ -65632,10 +65812,11 @@ function PickerRowView({ row, value, onChange, onToggleGroup, annotate, groupTit
|
|
|
65632
65812
|
className: "flex cursor-pointer items-center gap-2.5 px-3 py-1 text-xs text-foreground hover:bg-surface-hover",
|
|
65633
65813
|
"data-testid": "device-picker-row",
|
|
65634
65814
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
65635
|
-
type: "checkbox",
|
|
65815
|
+
type: single ? "radio" : "checkbox",
|
|
65816
|
+
...single ? { name: radioName } : {},
|
|
65636
65817
|
checked: row.selected || isLocked,
|
|
65637
65818
|
disabled: disabled || isLocked,
|
|
65638
|
-
onChange: () => onChange(togglePickerDevice(value, row.device.id)),
|
|
65819
|
+
onChange: () => onChange(single ? [row.device.id] : togglePickerDevice(value, row.device.id)),
|
|
65639
65820
|
className: "accent-primary",
|
|
65640
65821
|
"data-device-id": String(row.device.id)
|
|
65641
65822
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
@@ -68633,6 +68814,7 @@ exports.DialogHeader = DialogHeader;
|
|
|
68633
68814
|
exports.DialogTitle = DialogTitle;
|
|
68634
68815
|
exports.DialogTrigger = DialogTrigger;
|
|
68635
68816
|
exports.DiscoveryPanel = DiscoveryPanel;
|
|
68817
|
+
exports.DisplayPaletteProvider = DisplayPaletteProvider;
|
|
68636
68818
|
exports.Dog = Dog;
|
|
68637
68819
|
exports.DoorClosed = DoorClosed;
|
|
68638
68820
|
exports.DoorOpen = DoorOpen;
|
|
@@ -69534,6 +69716,7 @@ exports.useCameraStreamsGetRtspEntries = useCameraStreamsGetRtspEntries;
|
|
|
69534
69716
|
exports.useCameraStreamsPickStream = useCameraStreamsPickStream;
|
|
69535
69717
|
exports.useCapabilityPresence = useCapabilityPresence;
|
|
69536
69718
|
exports.useCarbonMonoxideGetStatus = useCarbonMonoxideGetStatus;
|
|
69719
|
+
exports.useClassPainter = useClassPainter;
|
|
69537
69720
|
exports.useClimateControlGetStatus = useClimateControlGetStatus;
|
|
69538
69721
|
exports.useClimateControlSetFanMode = useClimateControlSetFanMode;
|
|
69539
69722
|
exports.useClimateControlSetMode = useClimateControlSetMode;
|
|
@@ -69802,6 +69985,10 @@ exports.useDishwasherSetVarioSpeedPlus = useDishwasherSetVarioSpeedPlus;
|
|
|
69802
69985
|
exports.useDishwasherStart = useDishwasherStart;
|
|
69803
69986
|
exports.useDishwasherStop = useDishwasherStop;
|
|
69804
69987
|
exports.useDisplayIconResolver = useDisplayIconResolver;
|
|
69988
|
+
exports.useDisplayPaletteGetPalette = useDisplayPaletteGetPalette;
|
|
69989
|
+
exports.useDisplayPaletteListPresets = useDisplayPaletteListPresets;
|
|
69990
|
+
exports.useDisplayPaletteSetPalette = useDisplayPaletteSetPalette;
|
|
69991
|
+
exports.useDisplayPaletteView = useDisplayPaletteView;
|
|
69805
69992
|
exports.useDoorbellEvents = useDoorbellEvents;
|
|
69806
69993
|
exports.useDoorbellGetStatus = useDoorbellGetStatus;
|
|
69807
69994
|
exports.useEnumSensorGetStatus = useEnumSensorGetStatus;
|