@camstack/ui-library 1.0.2 → 1.0.3
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/MaskShapeCanvas-BByN3jvt.cjs +913 -0
- package/dist/MaskShapeCanvas-DI4BY7W2.js +913 -0
- package/dist/MotionZonesSettings-C1EEbk2V.js +438 -0
- package/dist/MotionZonesSettings-Ci1mzrki.cjs +438 -0
- package/dist/PrivacyMaskSettings-APgPLF7p.js +384 -0
- package/dist/PrivacyMaskSettings-yC-UPYPg.cjs +384 -0
- package/dist/composites/index.d.ts +6 -2
- package/dist/hls-CckKbjjD.cjs +28320 -0
- package/dist/hls-CfgsaJjd.js +28320 -0
- package/dist/index.cjs +410 -30407
- package/dist/index.d.ts +7 -0
- package/dist/index.js +404 -30408
- package/package.json +1 -1
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
const require_index = require("./index.cjs");
|
|
2
|
+
const require_MaskShapeCanvas = require("./MaskShapeCanvas-BByN3jvt.cjs");
|
|
3
|
+
let react = require("react");
|
|
4
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
5
|
+
/**
|
|
6
|
+
* @license lucide-react v0.576.0 - ISC
|
|
7
|
+
*
|
|
8
|
+
* This source code is licensed under the ISC license.
|
|
9
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
10
|
+
*/
|
|
11
|
+
var Hexagon = require_index.createLucideIcon("hexagon", [["path", {
|
|
12
|
+
d: "M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z",
|
|
13
|
+
key: "yt0hxn"
|
|
14
|
+
}]]);
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/composites/cap-settings/PrivacyMaskSettings.tsx
|
|
17
|
+
/**
|
|
18
|
+
* PrivacyMaskSettings — management surface for the on-camera
|
|
19
|
+
* `privacy-mask` capability.
|
|
20
|
+
*
|
|
21
|
+
* The cap was reshaped from a boolean cell-grid into a SHAPE model: up to
|
|
22
|
+
* `maxRegions` zones, each a rectangle or a free polygon (normalized 0..1
|
|
23
|
+
* of the camera frame). This component owns the draft zone list and edits
|
|
24
|
+
* it via the shared `MaskShapeCanvas` drawing-plane, painted over the live
|
|
25
|
+
* frame only while editing.
|
|
26
|
+
*
|
|
27
|
+
* Structure:
|
|
28
|
+
* - The editable SHAPES are painted over the live frame by the shared
|
|
29
|
+
* `MaskShapeCanvas`, registered as a player-overlay layer ONLY while
|
|
30
|
+
* "Edit mask" is toggled on (OFF by default — nothing draws on mount).
|
|
31
|
+
* - Every CONTROL lives in the panel: edit-mode toggle, master enable,
|
|
32
|
+
* Add rect / Add polygon (gated by `supportedShapes` + `maxRegions`,
|
|
33
|
+
* they arm the canvas' draw mode), active-count display, Revert, Save.
|
|
34
|
+
*
|
|
35
|
+
* Registered as host widget `host/privacy-mask-grid` (see
|
|
36
|
+
* `widgets/host-widgets.ts`) so the D14 device-config cap framework mounts
|
|
37
|
+
* it on the Image tab. Self-gates with `isAbsentProvider` to a friendly
|
|
38
|
+
* "not supported" message when the camera has no privacy-mask cap.
|
|
39
|
+
*/
|
|
40
|
+
var PRIVACY_MASK_OVERLAY_ORDER = 120;
|
|
41
|
+
var OVERLAY_ID = "privacy-mask";
|
|
42
|
+
var BTN_NEUTRAL = "rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-medium text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors";
|
|
43
|
+
var BTN_PRIMARY = "rounded-md border border-primary/50 bg-primary/15 px-2.5 py-1 text-[11px] font-medium text-primary hover:bg-primary/25 disabled:opacity-40 transition-colors";
|
|
44
|
+
/** Narrow the full MaskShape union to a privacy-mask shape (rect|polygon). */
|
|
45
|
+
function toPrivacyMaskShape(shape) {
|
|
46
|
+
if (shape.kind === "rect" || shape.kind === "polygon") return shape;
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
/** Next free 0-based integer id not already used by a draft region. */
|
|
50
|
+
function nextRegionId(regions) {
|
|
51
|
+
const used = new Set(regions.map((r) => r.id));
|
|
52
|
+
let id = 0;
|
|
53
|
+
while (used.has(id)) id += 1;
|
|
54
|
+
return id;
|
|
55
|
+
}
|
|
56
|
+
/** Structural equality for a draft vs. committed region list (order-sensitive). */
|
|
57
|
+
function regionsEqual(a, b) {
|
|
58
|
+
if (a.length !== b.length) return false;
|
|
59
|
+
for (let i = 0; i < a.length; i += 1) if (JSON.stringify(a[i]) !== JSON.stringify(b[i])) return false;
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
function PrivacyMaskSettings({ deviceId }) {
|
|
63
|
+
const dev = require_index.useDeviceProxy$1(require_index.useSystem$1().trpcClient, deviceId);
|
|
64
|
+
const [options, setOptions] = (0, react.useState)(null);
|
|
65
|
+
const [unsupported, setUnsupported] = (0, react.useState)(false);
|
|
66
|
+
const [draft, setDraft] = (0, react.useState)(null);
|
|
67
|
+
const [committed, setCommitted] = (0, react.useState)(null);
|
|
68
|
+
const [saving, setSaving] = (0, react.useState)(false);
|
|
69
|
+
const [editing, setEditing] = (0, react.useState)(false);
|
|
70
|
+
const [selectedId, setSelectedId] = (0, react.useState)(null);
|
|
71
|
+
const [drawingKind, setDrawingKind] = (0, react.useState)(null);
|
|
72
|
+
const seededRef = (0, react.useRef)(false);
|
|
73
|
+
(0, react.useEffect)(() => {
|
|
74
|
+
if (!dev) return void 0;
|
|
75
|
+
let cancelled = false;
|
|
76
|
+
seededRef.current = false;
|
|
77
|
+
setOptions(null);
|
|
78
|
+
setUnsupported(false);
|
|
79
|
+
setDraft(null);
|
|
80
|
+
setCommitted(null);
|
|
81
|
+
setEditing(false);
|
|
82
|
+
setSelectedId(null);
|
|
83
|
+
setDrawingKind(null);
|
|
84
|
+
(async () => {
|
|
85
|
+
try {
|
|
86
|
+
const opts = await dev.privacyMask?.getOptions({});
|
|
87
|
+
if (cancelled) return;
|
|
88
|
+
if (!opts) throw new Error("device proxy not ready");
|
|
89
|
+
setOptions(opts);
|
|
90
|
+
if (seededRef.current) return;
|
|
91
|
+
const status = await dev.privacyMask?.getStatus({});
|
|
92
|
+
if (cancelled) return;
|
|
93
|
+
if (!status) throw new Error("device proxy not ready");
|
|
94
|
+
seededRef.current = true;
|
|
95
|
+
const seed = {
|
|
96
|
+
enabled: status.enabled,
|
|
97
|
+
regions: status.regions
|
|
98
|
+
};
|
|
99
|
+
setCommitted(seed);
|
|
100
|
+
setDraft(seed);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
if (cancelled) return;
|
|
103
|
+
if (require_index.isAbsentProvider$1(err)) setUnsupported(true);
|
|
104
|
+
else console.error("Privacy Mask load failed", err);
|
|
105
|
+
}
|
|
106
|
+
})();
|
|
107
|
+
return () => {
|
|
108
|
+
cancelled = true;
|
|
109
|
+
};
|
|
110
|
+
}, [dev]);
|
|
111
|
+
const dirty = (0, react.useMemo)(() => draft !== null && committed !== null && (draft.enabled !== committed.enabled || !regionsEqual(draft.regions, committed.regions)), [draft, committed]);
|
|
112
|
+
const activeCount = draft ? draft.regions.length : 0;
|
|
113
|
+
const maxRegions = options ? options.maxRegions : 0;
|
|
114
|
+
const maxRegionsRef = (0, react.useRef)(0);
|
|
115
|
+
(0, react.useEffect)(() => {
|
|
116
|
+
maxRegionsRef.current = maxRegions;
|
|
117
|
+
}, [maxRegions]);
|
|
118
|
+
const atCapacity = maxRegions > 0 && activeCount >= maxRegions;
|
|
119
|
+
const toggleEnabled = (0, react.useCallback)(() => {
|
|
120
|
+
setDraft((prev) => prev ? {
|
|
121
|
+
...prev,
|
|
122
|
+
enabled: !prev.enabled
|
|
123
|
+
} : prev);
|
|
124
|
+
}, []);
|
|
125
|
+
const onSelect = (0, react.useCallback)((id) => {
|
|
126
|
+
setSelectedId(typeof id === "number" ? id : null);
|
|
127
|
+
}, []);
|
|
128
|
+
const items = (0, react.useMemo)(() => draft ? draft.regions.map((r) => ({
|
|
129
|
+
id: r.id,
|
|
130
|
+
shape: r.shape,
|
|
131
|
+
enabled: r.enabled,
|
|
132
|
+
label: `Zone ${String(r.id)}`
|
|
133
|
+
})) : [], [draft]);
|
|
134
|
+
const onShapeChange = (0, react.useCallback)((id, shape) => {
|
|
135
|
+
const next = toPrivacyMaskShape(shape);
|
|
136
|
+
if (!next) return;
|
|
137
|
+
setDraft((prev) => prev ? {
|
|
138
|
+
...prev,
|
|
139
|
+
regions: prev.regions.map((r) => r.id === id ? {
|
|
140
|
+
...r,
|
|
141
|
+
shape: next
|
|
142
|
+
} : r)
|
|
143
|
+
} : prev);
|
|
144
|
+
}, []);
|
|
145
|
+
const onDrawComplete = (0, react.useCallback)((shape) => {
|
|
146
|
+
const next = toPrivacyMaskShape(shape);
|
|
147
|
+
if (!next) return;
|
|
148
|
+
setDrawingKind(null);
|
|
149
|
+
setDraft((prev) => {
|
|
150
|
+
if (!prev) return prev;
|
|
151
|
+
const limit = maxRegionsRef.current;
|
|
152
|
+
if (limit > 0 && prev.regions.length >= limit) return prev;
|
|
153
|
+
const id = nextRegionId(prev.regions);
|
|
154
|
+
const region = {
|
|
155
|
+
id,
|
|
156
|
+
enabled: true,
|
|
157
|
+
shape: next
|
|
158
|
+
};
|
|
159
|
+
setSelectedId(id);
|
|
160
|
+
return {
|
|
161
|
+
...prev,
|
|
162
|
+
regions: [...prev.regions, region]
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
|
+
}, []);
|
|
166
|
+
const armDraw = (0, react.useCallback)((kind) => {
|
|
167
|
+
setEditing(true);
|
|
168
|
+
setSelectedId(null);
|
|
169
|
+
setDrawingKind(kind);
|
|
170
|
+
}, []);
|
|
171
|
+
const selectRegion = (0, react.useCallback)((id) => {
|
|
172
|
+
setEditing(true);
|
|
173
|
+
setDrawingKind(null);
|
|
174
|
+
setSelectedId(id);
|
|
175
|
+
}, []);
|
|
176
|
+
const deleteRegion = (0, react.useCallback)((id) => {
|
|
177
|
+
setSelectedId((cur) => cur === id ? null : cur);
|
|
178
|
+
setDraft((prev) => prev ? {
|
|
179
|
+
...prev,
|
|
180
|
+
regions: prev.regions.filter((r) => r.id !== id)
|
|
181
|
+
} : prev);
|
|
182
|
+
}, []);
|
|
183
|
+
const revert = (0, react.useCallback)(() => {
|
|
184
|
+
setDrawingKind(null);
|
|
185
|
+
setSelectedId(null);
|
|
186
|
+
if (committed) setDraft({
|
|
187
|
+
enabled: committed.enabled,
|
|
188
|
+
regions: committed.regions
|
|
189
|
+
});
|
|
190
|
+
}, [committed]);
|
|
191
|
+
const save = (0, react.useCallback)(async () => {
|
|
192
|
+
if (!dev || !draft) return;
|
|
193
|
+
setSaving(true);
|
|
194
|
+
try {
|
|
195
|
+
await dev.privacyMask?.setMask({ patch: {
|
|
196
|
+
enabled: draft.enabled,
|
|
197
|
+
regions: [...draft.regions]
|
|
198
|
+
} });
|
|
199
|
+
const fresh = await dev.privacyMask?.getStatus({});
|
|
200
|
+
if (fresh) {
|
|
201
|
+
const norm = {
|
|
202
|
+
enabled: fresh.enabled,
|
|
203
|
+
regions: fresh.regions
|
|
204
|
+
};
|
|
205
|
+
setCommitted(norm);
|
|
206
|
+
setDraft(norm);
|
|
207
|
+
}
|
|
208
|
+
} catch (err) {
|
|
209
|
+
console.error("Privacy Mask save failed", err);
|
|
210
|
+
} finally {
|
|
211
|
+
setSaving(false);
|
|
212
|
+
}
|
|
213
|
+
}, [dev, draft]);
|
|
214
|
+
const toggleEditing = (0, react.useCallback)(() => {
|
|
215
|
+
setEditing((v) => {
|
|
216
|
+
if (v) {
|
|
217
|
+
setDrawingKind(null);
|
|
218
|
+
setSelectedId(null);
|
|
219
|
+
}
|
|
220
|
+
return !v;
|
|
221
|
+
});
|
|
222
|
+
}, []);
|
|
223
|
+
const supportedShapes = options?.supportedShapes ?? [];
|
|
224
|
+
require_index.usePlayerOverlayLayer((0, react.useMemo)(() => editing && !unsupported && options && draft ? {
|
|
225
|
+
id: OVERLAY_ID,
|
|
226
|
+
order: PRIVACY_MASK_OVERLAY_ORDER,
|
|
227
|
+
node: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_MaskShapeCanvas.MaskShapeCanvas, {
|
|
228
|
+
transparent: true,
|
|
229
|
+
items,
|
|
230
|
+
supportedShapes,
|
|
231
|
+
polygonVertices: options.polygonVertices,
|
|
232
|
+
selectedId,
|
|
233
|
+
onSelect,
|
|
234
|
+
onShapeChange,
|
|
235
|
+
onDrawComplete,
|
|
236
|
+
drawingKind
|
|
237
|
+
})
|
|
238
|
+
} : null, [
|
|
239
|
+
editing,
|
|
240
|
+
unsupported,
|
|
241
|
+
options,
|
|
242
|
+
draft,
|
|
243
|
+
items,
|
|
244
|
+
supportedShapes,
|
|
245
|
+
selectedId,
|
|
246
|
+
onSelect,
|
|
247
|
+
onShapeChange,
|
|
248
|
+
onDrawComplete,
|
|
249
|
+
drawingKind
|
|
250
|
+
]));
|
|
251
|
+
const supportsRect = options?.supportedShapes.includes("rect") ?? false;
|
|
252
|
+
const supportsPolygon = options?.supportedShapes.includes("polygon") ?? false;
|
|
253
|
+
const noShapes = options !== null && (options.maxRegions <= 0 || options.supportedShapes.length === 0);
|
|
254
|
+
const ready = !unsupported && !noShapes && options !== null && draft !== null;
|
|
255
|
+
if (!dev) return null;
|
|
256
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.WidgetPanel, {
|
|
257
|
+
title: "Privacy Mask",
|
|
258
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.EyeOff, { className: "h-3.5 w-3.5 text-foreground-subtle" }),
|
|
259
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
260
|
+
className: "flex flex-col gap-3",
|
|
261
|
+
children: unsupported || noShapes ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
262
|
+
className: `${require_index.TEXT_HINT} leading-relaxed`,
|
|
263
|
+
children: "This camera doesn't support an on-board privacy mask."
|
|
264
|
+
}) : !ready ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
265
|
+
className: `${require_index.TEXT_HINT} leading-relaxed`,
|
|
266
|
+
children: "Loading the camera's privacy mask…"
|
|
267
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
268
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
269
|
+
className: `${require_index.TEXT_HINT} leading-relaxed`,
|
|
270
|
+
children: [
|
|
271
|
+
"Toggle ",
|
|
272
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
|
|
273
|
+
className: "text-foreground",
|
|
274
|
+
children: "Edit mask"
|
|
275
|
+
}),
|
|
276
|
+
" to draw blanked-out zones on the live frame, then ",
|
|
277
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
|
|
278
|
+
className: "text-foreground",
|
|
279
|
+
children: "Save"
|
|
280
|
+
}),
|
|
281
|
+
" to push them to the camera. Drag a rectangle to move, its corner to resize; drag polygon vertices, click an edge midpoint to add one, or right-click a vertex to remove it."
|
|
282
|
+
]
|
|
283
|
+
}),
|
|
284
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
285
|
+
className: "flex items-center gap-2 flex-wrap",
|
|
286
|
+
children: [
|
|
287
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
288
|
+
type: "button",
|
|
289
|
+
onClick: toggleEditing,
|
|
290
|
+
disabled: saving,
|
|
291
|
+
"aria-pressed": editing,
|
|
292
|
+
className: editing ? BTN_PRIMARY : BTN_NEUTRAL,
|
|
293
|
+
children: editing ? "Done editing" : "Edit mask"
|
|
294
|
+
}),
|
|
295
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
296
|
+
type: "button",
|
|
297
|
+
onClick: toggleEnabled,
|
|
298
|
+
disabled: saving,
|
|
299
|
+
"aria-pressed": draft.enabled,
|
|
300
|
+
className: draft.enabled ? BTN_PRIMARY : BTN_NEUTRAL,
|
|
301
|
+
children: draft.enabled ? "Mask on" : "Mask off"
|
|
302
|
+
}),
|
|
303
|
+
supportsRect && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
304
|
+
type: "button",
|
|
305
|
+
onClick: () => armDraw("rect"),
|
|
306
|
+
disabled: saving || atCapacity,
|
|
307
|
+
"aria-pressed": drawingKind === "rect",
|
|
308
|
+
className: drawingKind === "rect" ? BTN_PRIMARY : BTN_NEUTRAL,
|
|
309
|
+
title: atCapacity ? "Maximum zones reached" : "Add a rectangle zone",
|
|
310
|
+
children: "+ Rect"
|
|
311
|
+
}),
|
|
312
|
+
supportsPolygon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
313
|
+
type: "button",
|
|
314
|
+
onClick: () => armDraw("polygon"),
|
|
315
|
+
disabled: saving || atCapacity,
|
|
316
|
+
"aria-pressed": drawingKind === "polygon",
|
|
317
|
+
className: drawingKind === "polygon" ? BTN_PRIMARY : BTN_NEUTRAL,
|
|
318
|
+
title: atCapacity ? "Maximum zones reached" : "Add a polygon zone",
|
|
319
|
+
children: "+ Polygon"
|
|
320
|
+
}),
|
|
321
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
322
|
+
className: `${require_index.TEXT_HINT} ml-1 tabular-nums`,
|
|
323
|
+
children: [
|
|
324
|
+
activeCount,
|
|
325
|
+
" / ",
|
|
326
|
+
maxRegions,
|
|
327
|
+
" zones"
|
|
328
|
+
]
|
|
329
|
+
}),
|
|
330
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "flex-1" }),
|
|
331
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
332
|
+
type: "button",
|
|
333
|
+
onClick: revert,
|
|
334
|
+
disabled: saving || !dirty,
|
|
335
|
+
className: "rounded-md border border-border bg-surface px-2 py-1 text-[11px] text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors",
|
|
336
|
+
children: "Revert"
|
|
337
|
+
}),
|
|
338
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
339
|
+
type: "button",
|
|
340
|
+
onClick: () => void save(),
|
|
341
|
+
disabled: saving || !dirty,
|
|
342
|
+
className: BTN_PRIMARY,
|
|
343
|
+
children: saving ? "Saving…" : "Save"
|
|
344
|
+
})
|
|
345
|
+
]
|
|
346
|
+
}),
|
|
347
|
+
activeCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
348
|
+
className: "flex flex-col gap-1",
|
|
349
|
+
children: draft.regions.map((r) => {
|
|
350
|
+
const selected = selectedId === r.id;
|
|
351
|
+
const ShapeIcon = r.shape.kind === "polygon" ? Hexagon : require_index.Square;
|
|
352
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
353
|
+
className: `flex items-center gap-2 rounded-md border px-2 py-1 transition-colors ${selected ? "border-primary/50 bg-primary/10" : "border-border bg-surface"}`,
|
|
354
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
355
|
+
type: "button",
|
|
356
|
+
onClick: () => selectRegion(r.id),
|
|
357
|
+
disabled: saving,
|
|
358
|
+
className: "flex flex-1 items-center gap-2 text-left text-[11px] font-medium text-foreground-subtle hover:text-foreground disabled:opacity-40 transition-colors",
|
|
359
|
+
children: [
|
|
360
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShapeIcon, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
361
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["Zone ", r.id] }),
|
|
362
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
363
|
+
className: "text-foreground-faint capitalize",
|
|
364
|
+
children: r.shape.kind
|
|
365
|
+
})
|
|
366
|
+
]
|
|
367
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
368
|
+
type: "button",
|
|
369
|
+
onClick: () => deleteRegion(r.id),
|
|
370
|
+
disabled: saving,
|
|
371
|
+
"aria-label": `Delete zone ${String(r.id)}`,
|
|
372
|
+
title: "Delete zone",
|
|
373
|
+
className: "inline-flex h-6 w-6 items-center justify-center rounded border border-border bg-surface text-foreground-subtle hover:border-red-400/40 hover:bg-red-500/10 hover:text-red-400 disabled:opacity-40 transition-colors",
|
|
374
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.Trash2, { className: "h-3.5 w-3.5" })
|
|
375
|
+
})]
|
|
376
|
+
}, r.id);
|
|
377
|
+
})
|
|
378
|
+
}) : null
|
|
379
|
+
] })
|
|
380
|
+
})
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
//#endregion
|
|
384
|
+
exports.PrivacyMaskSettings = PrivacyMaskSettings;
|
|
@@ -118,6 +118,10 @@ export { WidgetSlot } from './widget-slot';
|
|
|
118
118
|
export type { WidgetSlotProps } from './widget-slot';
|
|
119
119
|
export { ScopePicker, validateScopes } from './scope-picker';
|
|
120
120
|
export type { ScopeAccess } from './scope-picker';
|
|
121
|
-
export { PtzPanel
|
|
122
|
-
export
|
|
121
|
+
export { PtzPanel } from './cap-settings/PtzPanel';
|
|
122
|
+
export { ConsumablesPanel } from './cap-settings/ConsumablesPanel';
|
|
123
|
+
export { AutotrackSection } from './cap-settings/AutotrackSection';
|
|
124
|
+
export { RecordingPanel } from './cap-settings/RecordingPanel';
|
|
125
|
+
export type { CapSettingsComponentProps } from './cap-settings/index';
|
|
126
|
+
export type { MaskShapeItem, MaskShapeCanvasProps } from './cap-settings/MaskShapeCanvas';
|
|
123
127
|
export * from './widget-panel.js';
|