@camstack/ui-library 1.2.290 → 1.2.292

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.
Files changed (89) hide show
  1. package/dist/{MaskShapeCanvas-DI4BY7W2.js → MaskShapeCanvas-C9cJlxvG.js} +3 -1
  2. package/dist/{MaskShapeCanvas-BByN3jvt.cjs → MaskShapeCanvas-NCNwu9_5.cjs} +3 -1
  3. package/dist/{MotionZonesSettings-CiYjd-yl.js → MotionZonesSettings-Bv8XzCa3.js} +45 -48
  4. package/dist/{MotionZonesSettings-BK9R9NIr.cjs → MotionZonesSettings-CPRH536-.cjs} +44 -47
  5. package/dist/{PrivacyMaskSettings-DuXUCUev.cjs → PrivacyMaskSettings-BUWvVF_n.cjs} +33 -42
  6. package/dist/{PrivacyMaskSettings-BmRdUrPp.js → PrivacyMaskSettings-BlkbESYj.js} +34 -43
  7. package/dist/{SceneMonitorEditor-COgAjtq5.cjs → SceneMonitorEditor-D_jkzQ2o.cjs} +189 -86
  8. package/dist/{SceneMonitorEditor-J4Ix_0tu.js → SceneMonitorEditor-i3-nXAEU.js} +190 -87
  9. package/dist/composites/addon-global-settings-form.d.ts +0 -12
  10. package/dist/composites/cap-settings/AutotrackSection.d.ts +0 -16
  11. package/dist/composites/cap-settings/CameraGridLayoutEditor.d.ts +0 -21
  12. package/dist/composites/cap-settings/ConsumablesPanel.d.ts +0 -18
  13. package/dist/composites/cap-settings/MaskShapeCanvas.d.ts +0 -102
  14. package/dist/composites/cap-settings/MotionZonesSettings.d.ts +0 -38
  15. package/dist/composites/cap-settings/SceneReferenceGallery.d.ts +0 -38
  16. package/dist/composites/cap-settings/scene-monitor-copy.d.ts +12 -6
  17. package/dist/composites/clips/clip-source-state.d.ts +3 -2
  18. package/dist/composites/clips/clip-thumbnail-fetch.d.ts +2 -1
  19. package/dist/composites/clips/clip-tile-media.d.ts +3 -2
  20. package/dist/composites/clips/clip-view-state.d.ts +2 -1
  21. package/dist/composites/confirm-dialog.d.ts +0 -62
  22. package/dist/composites/data-table.d.ts +2 -11
  23. package/dist/composites/detection-canvas.d.ts +0 -9
  24. package/dist/composites/detection-result-tree.d.ts +0 -12
  25. package/dist/composites/device-controls/container-primary-hero.d.ts +0 -17
  26. package/dist/composites/device-controls/humidifier-control.d.ts +0 -17
  27. package/dist/composites/device-controls/lawn-mower-control.d.ts +2 -1
  28. package/dist/composites/device-controls/pet-feeder-panel.d.ts +0 -18
  29. package/dist/composites/device-controls/script-automation-controls.d.ts +2 -1
  30. package/dist/composites/device-controls/sensor-hero-card.d.ts +0 -18
  31. package/dist/composites/device-controls/update-control.d.ts +0 -13
  32. package/dist/composites/device-controls/vacuum-control.d.ts +2 -1
  33. package/dist/composites/device-controls/valve-control.d.ts +2 -1
  34. package/dist/composites/device-controls/weather-control.d.ts +2 -1
  35. package/dist/composites/device-item/actions.d.ts +0 -22
  36. package/dist/composites/device-item/helpers.d.ts +2 -1
  37. package/dist/composites/device-item/index.d.ts +0 -9
  38. package/dist/composites/device-list/batch-actions-bar.d.ts +0 -11
  39. package/dist/composites/device-list/filter-chips.d.ts +0 -13
  40. package/dist/composites/device-list/page-size-select.d.ts +2 -1
  41. package/dist/composites/device-list/url-state.d.ts +2 -3
  42. package/dist/composites/device-meta.d.ts +14 -7
  43. package/dist/composites/device-picker/DevicePicker.d.ts +9 -1
  44. package/dist/composites/device-picker/device-filter-bar.d.ts +2 -1
  45. package/dist/composites/device-picker/device-parent.d.ts +2 -1
  46. package/dist/composites/device-picker/device-selection-rows.d.ts +6 -1
  47. package/dist/composites/device-selector-picker.d.ts +25 -1
  48. package/dist/composites/device-step-matrix.d.ts +0 -26
  49. package/dist/composites/error-box.d.ts +0 -15
  50. package/dist/composites/event-stream.d.ts +2 -1
  51. package/dist/composites/extensions/extensions-view-state.d.ts +2 -1
  52. package/dist/composites/hls-quality.d.ts +3 -2
  53. package/dist/composites/index.d.ts +0 -2
  54. package/dist/composites/notification-targets-field.d.ts +2 -2
  55. package/dist/composites/option-select/OptionSelect.d.ts +6 -0
  56. package/dist/composites/pipeline-matrix-shared.d.ts +2 -1
  57. package/dist/composites/provider-entries/AddProviderEntryDialog.d.ts +1 -1
  58. package/dist/composites/provider-entries/ProviderEntryPills.d.ts +1 -1
  59. package/dist/composites/ptz-overlay.d.ts +0 -19
  60. package/dist/composites/qr-code.d.ts +0 -14
  61. package/dist/composites/scope-picker.d.ts +2 -1
  62. package/dist/composites/step-inspector/layered-settings-form.d.ts +0 -11
  63. package/dist/composites/step-inspector/step-inspector-panel.d.ts +0 -6
  64. package/dist/composites/stream-broker-selector.d.ts +0 -21
  65. package/dist/composites/training/TrainingBaseModelPicker.d.ts +0 -10
  66. package/dist/composites/training/retrain-review-badge.d.ts +2 -1
  67. package/dist/composites/webrtc-diagnostics/endpoint-test.d.ts +2 -1
  68. package/dist/composites/webrtc-diagnostics/nat-mapping.d.ts +2 -1
  69. package/dist/composites/webrtc-diagnostics/session-run.d.ts +2 -1
  70. package/dist/composites/webrtc-diagnostics/session-verdict.d.ts +2 -1
  71. package/dist/contexts/ui-text.d.ts +49 -0
  72. package/dist/generated/system-hooks.d.ts +4 -0
  73. package/dist/i18n/addon-i18n.d.ts +46 -0
  74. package/dist/i18n/index.d.ts +2 -0
  75. package/dist/i18n/use-addon-t.d.ts +57 -0
  76. package/dist/index.cjs +3309 -2368
  77. package/dist/index.d.ts +2 -0
  78. package/dist/index.js +3297 -2368
  79. package/dist/lib/format-last-seen.d.ts +15 -1
  80. package/dist/lib/phase-config.d.ts +9 -3
  81. package/dist/lib/pipeline-mirror.d.ts +2 -1
  82. package/package.json +1 -1
  83. package/dist/composites/data-table/data-table-header.d.ts +0 -11
  84. package/dist/composites/data-table/data-table-pagination.d.ts +0 -10
  85. package/dist/composites/data-table/data-table-row.d.ts +0 -11
  86. package/dist/composites/data-table/data-table.d.ts +0 -3
  87. package/dist/composites/data-table/index.d.ts +0 -2
  88. package/dist/composites/data-table/types.d.ts +0 -34
  89. package/dist/composites/data-table-layout.d.ts +0 -31
@@ -1,3 +1,4 @@
1
+ import { useUiText } from "./index.js";
1
2
  import { memo, useCallback, useEffect, useRef, useState } from "react";
2
3
  import { jsx, jsxs } from "react/jsx-runtime";
3
4
  import { Arrow, Circle, Group, Layer, Line, Rect, Stage, Text } from "react-konva";
@@ -171,6 +172,7 @@ function isPointShape(shape) {
171
172
  return shape.kind === "polygon" || shape.kind === "line";
172
173
  }
173
174
  function MaskShapeCanvasImpl({ items, selectedId, onSelect, onShapeChange, onDrawComplete, drawingKind, supportedShapes, transparent = false, backdrop, polygonVertices, grid }) {
175
+ const t = useUiText();
174
176
  const containerRef = useRef(null);
175
177
  const [size, setSize] = useState({
176
178
  w: 800,
@@ -508,7 +510,7 @@ function MaskShapeCanvasImpl({ items, selectedId, onSelect, onShapeChange, onDra
508
510
  }),
509
511
  isDrawing ? /* @__PURE__ */ jsx("div", {
510
512
  className: "px-3 py-1.5 bg-zinc-800/80 text-[11px] text-zinc-400 border-t border-zinc-700",
511
- children: drawingKind === "line" ? "Click to set the start point, click again to complete the line" : drawingKind === "rect" ? "Click to drop a rectangle, then drag the corner to resize" : drawingKind === "grid" ? "Click to add the grid, then paint cells" : drawPoints.length < FLOOR_POLYGON_VERTICES ? "Click to add points (min 3). Double-click or click the first point to close." : "Continue adding points. Double-click or click the first point to close."
513
+ children: drawingKind === "line" ? t("uiLibrary.maskShapeCanvas.hintLine", "Click to set the start point, click again to complete the line") : drawingKind === "rect" ? t("uiLibrary.maskShapeCanvas.hintRect", "Click to drop a rectangle, then drag the corner to resize") : drawingKind === "grid" ? t("uiLibrary.maskShapeCanvas.hintGrid", "Click to add the grid, then paint cells") : drawPoints.length < FLOOR_POLYGON_VERTICES ? t("uiLibrary.maskShapeCanvas.hintPoints", "Click to add points (min 3). Double-click or click the first point to close.") : t("uiLibrary.maskShapeCanvas.hintMore", "Continue adding points. Double-click or click the first point to close.")
512
514
  }) : null
513
515
  ]
514
516
  });
@@ -1,3 +1,4 @@
1
+ const require_index = require("./index.cjs");
1
2
  let react = require("react");
2
3
  let react_jsx_runtime = require("react/jsx-runtime");
3
4
  let react_konva = require("react-konva");
@@ -171,6 +172,7 @@ function isPointShape(shape) {
171
172
  return shape.kind === "polygon" || shape.kind === "line";
172
173
  }
173
174
  function MaskShapeCanvasImpl({ items, selectedId, onSelect, onShapeChange, onDrawComplete, drawingKind, supportedShapes, transparent = false, backdrop, polygonVertices, grid }) {
175
+ const t = require_index.useUiText();
174
176
  const containerRef = (0, react.useRef)(null);
175
177
  const [size, setSize] = (0, react.useState)({
176
178
  w: 800,
@@ -508,7 +510,7 @@ function MaskShapeCanvasImpl({ items, selectedId, onSelect, onShapeChange, onDra
508
510
  }),
509
511
  isDrawing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
510
512
  className: "px-3 py-1.5 bg-zinc-800/80 text-[11px] text-zinc-400 border-t border-zinc-700",
511
- children: drawingKind === "line" ? "Click to set the start point, click again to complete the line" : drawingKind === "rect" ? "Click to drop a rectangle, then drag the corner to resize" : drawingKind === "grid" ? "Click to add the grid, then paint cells" : drawPoints.length < FLOOR_POLYGON_VERTICES ? "Click to add points (min 3). Double-click or click the first point to close." : "Continue adding points. Double-click or click the first point to close."
513
+ children: drawingKind === "line" ? t("uiLibrary.maskShapeCanvas.hintLine", "Click to set the start point, click again to complete the line") : drawingKind === "rect" ? t("uiLibrary.maskShapeCanvas.hintRect", "Click to drop a rectangle, then drag the corner to resize") : drawingKind === "grid" ? t("uiLibrary.maskShapeCanvas.hintGrid", "Click to add the grid, then paint cells") : drawPoints.length < FLOOR_POLYGON_VERTICES ? t("uiLibrary.maskShapeCanvas.hintPoints", "Click to add points (min 3). Double-click or click the first point to close.") : t("uiLibrary.maskShapeCanvas.hintMore", "Continue adding points. Double-click or click the first point to close.")
512
514
  }) : null
513
515
  ]
514
516
  });
@@ -1,5 +1,5 @@
1
- import { TEXT_HINT, WidgetPanel, _n as Grid2x2, isAbsentProvider, useDeviceProxy, usePlayerOverlayLayer, useSystem } from "./index.js";
2
- import { MaskShapeCanvas } from "./MaskShapeCanvas-DI4BY7W2.js";
1
+ import { TEXT_HINT, UiTrans, WidgetPanel, _n as Grid2x2, isAbsentProvider, useDeviceProxy, usePlayerOverlayLayer, useSystem, useUiText } from "./index.js";
2
+ import { MaskShapeCanvas } from "./MaskShapeCanvas-C9cJlxvG.js";
3
3
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
5
5
  //#region src/composites/cap-settings/MotionZonesSettings.tsx
@@ -119,6 +119,7 @@ function expandCells(uiCells, cw, ch, m) {
119
119
  return out;
120
120
  }
121
121
  function MotionZonesSettings({ deviceId }) {
122
+ const t = useUiText();
122
123
  const dev = useDeviceProxy(useSystem().trpcClient, deviceId);
123
124
  const [options, setOptions] = useState(null);
124
125
  const [unsupported, setUnsupported] = useState(false);
@@ -292,41 +293,31 @@ function MotionZonesSettings({ deviceId }) {
292
293
  const ready = !unsupported && options !== null && uiCells !== null;
293
294
  if (!dev) return null;
294
295
  return /* @__PURE__ */ jsx(WidgetPanel, {
295
- title: "Motion Zones",
296
+ title: t("uiLibrary.motionZones.title", "Motion Zones"),
296
297
  icon: /* @__PURE__ */ jsx(Grid2x2, { className: "h-3.5 w-3.5 text-foreground-subtle" }),
297
298
  children: /* @__PURE__ */ jsx("div", {
298
299
  className: "flex flex-col gap-3",
299
300
  children: unsupported ? /* @__PURE__ */ jsx("p", {
300
301
  className: `${TEXT_HINT} leading-relaxed`,
301
- children: "This camera doesn't expose an on-board motion zones grid."
302
+ children: t("uiLibrary.motionZones.unsupported", "This camera doesn't expose an on-board motion zones grid.")
302
303
  }) : !ready ? /* @__PURE__ */ jsx("p", {
303
304
  className: `${TEXT_HINT} leading-relaxed`,
304
- children: "Loading the camera's grid…"
305
- }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("p", {
305
+ children: t("uiLibrary.motionZones.loading", "Loading the camera's grid…")
306
+ }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("p", {
306
307
  className: `${TEXT_HINT} leading-relaxed`,
307
- children: [
308
- "Toggle ",
309
- /* @__PURE__ */ jsx("strong", {
310
- className: "text-foreground",
311
- children: "Edit grid"
312
- }),
313
- " to paint the region directly on the live frame, then ",
314
- /* @__PURE__ */ jsx("strong", {
315
- className: "text-foreground",
316
- children: "Save"
317
- }),
318
- " to push the mask to the camera. Pick a bigger",
319
- " ",
320
- /* @__PURE__ */ jsx("strong", {
321
- className: "text-foreground",
322
- children: "Cell size"
323
- }),
324
- " for quicker, broad-stroke painting — it's resampled to the camera's native ",
325
- options.grid.width,
326
- "×",
327
- options.grid.height,
328
- " grid on save (×1 is the finest)."
329
- ]
308
+ children: /* @__PURE__ */ jsx(UiTrans, {
309
+ uiKey: "uiLibrary.motionZones.intro",
310
+ fallback: "Toggle <0>Edit grid</0> to paint the region directly on the live frame, then <1>Save</1> to push the mask to the camera. Pick a bigger <2>Cell size</2> for quicker, broad-stroke painting — it's resampled to the camera's native {{width}}×{{height}} grid on save (×1 is the finest).",
311
+ components: [
312
+ /* @__PURE__ */ jsx("strong", { className: "text-foreground" }, "e"),
313
+ /* @__PURE__ */ jsx("strong", { className: "text-foreground" }, "s"),
314
+ /* @__PURE__ */ jsx("strong", { className: "text-foreground" }, "c")
315
+ ],
316
+ values: {
317
+ width: options.grid.width,
318
+ height: options.grid.height
319
+ }
320
+ })
330
321
  }), /* @__PURE__ */ jsxs("div", {
331
322
  className: "flex items-center gap-2 flex-wrap",
332
323
  children: [
@@ -336,58 +327,64 @@ function MotionZonesSettings({ deviceId }) {
336
327
  disabled: saving,
337
328
  "aria-pressed": editing,
338
329
  className: editing ? BTN_PRIMARY : BTN_NEUTRAL,
339
- children: editing ? "Done editing" : "Edit grid"
330
+ children: editing ? t("uiLibrary.motionZones.doneEditing", "Done editing") : t("uiLibrary.motionZones.editGrid", "Edit grid")
340
331
  }),
341
332
  /* @__PURE__ */ jsx("button", {
342
333
  type: "button",
343
334
  onClick: () => setAll(true),
344
335
  disabled: saving,
345
336
  className: BTN_NEUTRAL,
346
- children: "All on"
337
+ children: t("uiLibrary.motionZones.allOn", "All on")
347
338
  }),
348
339
  /* @__PURE__ */ jsx("button", {
349
340
  type: "button",
350
341
  onClick: () => setAll(false),
351
342
  disabled: saving,
352
343
  className: BTN_NEUTRAL,
353
- children: "All off"
344
+ children: t("uiLibrary.motionZones.allOff", "All off")
354
345
  }),
355
346
  /* @__PURE__ */ jsx("button", {
356
347
  type: "button",
357
348
  onClick: invert,
358
349
  disabled: saving,
359
350
  className: BTN_NEUTRAL,
360
- children: "Invert"
351
+ children: t("uiLibrary.motionZones.invert", "Invert")
361
352
  }),
362
353
  /* @__PURE__ */ jsxs("div", {
363
354
  className: "flex items-center gap-1 ml-1",
364
355
  role: "group",
365
- "aria-label": "Cell size",
356
+ "aria-label": t("uiLibrary.motionZones.cellSize", "Cell size"),
366
357
  children: [/* @__PURE__ */ jsx("span", {
367
358
  className: `${TEXT_HINT} mr-0.5`,
368
- children: "Cell size"
359
+ children: t("uiLibrary.motionZones.cellSize", "Cell size")
369
360
  }), GRID_MULTIPLIERS.map((m) => /* @__PURE__ */ jsxs("button", {
370
361
  type: "button",
371
362
  onClick: () => changeMultiplier(m),
372
363
  disabled: saving,
373
364
  "aria-pressed": multiplier === m,
374
- title: m === 1 ? `Camera grid ${cw}×${ch} (finest)` : `${coarseDim(cw, m)}×${coarseDim(ch, m)} painting grid · cells ×${m} bigger`,
365
+ title: m === 1 ? t("uiLibrary.motionZones.cameraGrid", "Camera grid {{width}}×{{height}} (finest)", {
366
+ width: cw,
367
+ height: ch
368
+ }) : t("uiLibrary.motionZones.paintingGrid", "{{width}}×{{height}} painting grid · cells ×{{factor}} bigger", {
369
+ width: coarseDim(cw, m),
370
+ height: coarseDim(ch, m),
371
+ factor: m
372
+ }),
375
373
  className: multiplier === m ? BTN_PRIMARY : BTN_NEUTRAL,
376
374
  children: ["×", m]
377
375
  }, m))]
378
376
  }),
379
377
  /* @__PURE__ */ jsxs("span", {
380
378
  className: `${TEXT_HINT} ml-1 tabular-nums`,
381
- children: [
382
- activeCount,
383
- " / ",
384
- cameraTotal,
385
- " cells · ",
386
- options.grid.width,
387
- "×",
388
- options.grid.height,
389
- multiplier !== 1 ? ` · paint ${uiWidth}×${uiHeight}` : ""
390
- ]
379
+ children: [t("uiLibrary.motionZones.cells", "{{active}} / {{total}} cells · {{width}}×{{height}}", {
380
+ active: activeCount,
381
+ total: cameraTotal,
382
+ width: options.grid.width,
383
+ height: options.grid.height
384
+ }), multiplier !== 1 ? t("uiLibrary.motionZones.paintSuffix", " · paint {{width}}×{{height}}", {
385
+ width: uiWidth,
386
+ height: uiHeight
387
+ }) : ""]
391
388
  }),
392
389
  /* @__PURE__ */ jsx("span", { className: "flex-1" }),
393
390
  /* @__PURE__ */ jsx("button", {
@@ -395,14 +392,14 @@ function MotionZonesSettings({ deviceId }) {
395
392
  onClick: revert,
396
393
  disabled: saving || !dirty,
397
394
  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",
398
- children: "Revert"
395
+ children: t("uiLibrary.motionZones.revert", "Revert")
399
396
  }),
400
397
  /* @__PURE__ */ jsx("button", {
401
398
  type: "button",
402
399
  onClick: () => void save(),
403
400
  disabled: saving || !dirty,
404
401
  className: BTN_PRIMARY,
405
- children: saving ? "Saving…" : "Save"
402
+ children: saving ? t("uiLibrary.motionZones.saving", "Saving…") : t("uiLibrary.motionZones.save", "Save")
406
403
  })
407
404
  ]
408
405
  })] })
@@ -1,5 +1,5 @@
1
1
  const require_index = require("./index.cjs");
2
- const require_MaskShapeCanvas = require("./MaskShapeCanvas-BByN3jvt.cjs");
2
+ const require_MaskShapeCanvas = require("./MaskShapeCanvas-NCNwu9_5.cjs");
3
3
  let react = require("react");
4
4
  let react_jsx_runtime = require("react/jsx-runtime");
5
5
  //#region src/composites/cap-settings/MotionZonesSettings.tsx
@@ -119,6 +119,7 @@ function expandCells(uiCells, cw, ch, m) {
119
119
  return out;
120
120
  }
121
121
  function MotionZonesSettings({ deviceId }) {
122
+ const t = require_index.useUiText();
122
123
  const dev = require_index.useDeviceProxy$1(require_index.useSystem$1().trpcClient, deviceId);
123
124
  const [options, setOptions] = (0, react.useState)(null);
124
125
  const [unsupported, setUnsupported] = (0, react.useState)(false);
@@ -292,41 +293,31 @@ function MotionZonesSettings({ deviceId }) {
292
293
  const ready = !unsupported && options !== null && uiCells !== null;
293
294
  if (!dev) return null;
294
295
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.WidgetPanel, {
295
- title: "Motion Zones",
296
+ title: t("uiLibrary.motionZones.title", "Motion Zones"),
296
297
  icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.Grid2x2, { className: "h-3.5 w-3.5 text-foreground-subtle" }),
297
298
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
298
299
  className: "flex flex-col gap-3",
299
300
  children: unsupported ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
300
301
  className: `${require_index.TEXT_HINT} leading-relaxed`,
301
- children: "This camera doesn't expose an on-board motion zones grid."
302
+ children: t("uiLibrary.motionZones.unsupported", "This camera doesn't expose an on-board motion zones grid.")
302
303
  }) : !ready ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
303
304
  className: `${require_index.TEXT_HINT} leading-relaxed`,
304
- children: "Loading the camera's grid…"
305
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
305
+ children: t("uiLibrary.motionZones.loading", "Loading the camera's grid…")
306
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
306
307
  className: `${require_index.TEXT_HINT} leading-relaxed`,
307
- children: [
308
- "Toggle ",
309
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
310
- className: "text-foreground",
311
- children: "Edit grid"
312
- }),
313
- " to paint the region directly on the live frame, then ",
314
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
315
- className: "text-foreground",
316
- children: "Save"
317
- }),
318
- " to push the mask to the camera. Pick a bigger",
319
- " ",
320
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
321
- className: "text-foreground",
322
- children: "Cell size"
323
- }),
324
- " for quicker, broad-stroke painting — it's resampled to the camera's native ",
325
- options.grid.width,
326
- "×",
327
- options.grid.height,
328
- " grid on save (×1 is the finest)."
329
- ]
308
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.UiTrans, {
309
+ uiKey: "uiLibrary.motionZones.intro",
310
+ fallback: "Toggle <0>Edit grid</0> to paint the region directly on the live frame, then <1>Save</1> to push the mask to the camera. Pick a bigger <2>Cell size</2> for quicker, broad-stroke painting — it's resampled to the camera's native {{width}}×{{height}} grid on save (×1 is the finest).",
311
+ components: [
312
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { className: "text-foreground" }, "e"),
313
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { className: "text-foreground" }, "s"),
314
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { className: "text-foreground" }, "c")
315
+ ],
316
+ values: {
317
+ width: options.grid.width,
318
+ height: options.grid.height
319
+ }
320
+ })
330
321
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
331
322
  className: "flex items-center gap-2 flex-wrap",
332
323
  children: [
@@ -336,58 +327,64 @@ function MotionZonesSettings({ deviceId }) {
336
327
  disabled: saving,
337
328
  "aria-pressed": editing,
338
329
  className: editing ? BTN_PRIMARY : BTN_NEUTRAL,
339
- children: editing ? "Done editing" : "Edit grid"
330
+ children: editing ? t("uiLibrary.motionZones.doneEditing", "Done editing") : t("uiLibrary.motionZones.editGrid", "Edit grid")
340
331
  }),
341
332
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
342
333
  type: "button",
343
334
  onClick: () => setAll(true),
344
335
  disabled: saving,
345
336
  className: BTN_NEUTRAL,
346
- children: "All on"
337
+ children: t("uiLibrary.motionZones.allOn", "All on")
347
338
  }),
348
339
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
349
340
  type: "button",
350
341
  onClick: () => setAll(false),
351
342
  disabled: saving,
352
343
  className: BTN_NEUTRAL,
353
- children: "All off"
344
+ children: t("uiLibrary.motionZones.allOff", "All off")
354
345
  }),
355
346
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
356
347
  type: "button",
357
348
  onClick: invert,
358
349
  disabled: saving,
359
350
  className: BTN_NEUTRAL,
360
- children: "Invert"
351
+ children: t("uiLibrary.motionZones.invert", "Invert")
361
352
  }),
362
353
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
363
354
  className: "flex items-center gap-1 ml-1",
364
355
  role: "group",
365
- "aria-label": "Cell size",
356
+ "aria-label": t("uiLibrary.motionZones.cellSize", "Cell size"),
366
357
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
367
358
  className: `${require_index.TEXT_HINT} mr-0.5`,
368
- children: "Cell size"
359
+ children: t("uiLibrary.motionZones.cellSize", "Cell size")
369
360
  }), GRID_MULTIPLIERS.map((m) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
370
361
  type: "button",
371
362
  onClick: () => changeMultiplier(m),
372
363
  disabled: saving,
373
364
  "aria-pressed": multiplier === m,
374
- title: m === 1 ? `Camera grid ${cw}×${ch} (finest)` : `${coarseDim(cw, m)}×${coarseDim(ch, m)} painting grid · cells ×${m} bigger`,
365
+ title: m === 1 ? t("uiLibrary.motionZones.cameraGrid", "Camera grid {{width}}×{{height}} (finest)", {
366
+ width: cw,
367
+ height: ch
368
+ }) : t("uiLibrary.motionZones.paintingGrid", "{{width}}×{{height}} painting grid · cells ×{{factor}} bigger", {
369
+ width: coarseDim(cw, m),
370
+ height: coarseDim(ch, m),
371
+ factor: m
372
+ }),
375
373
  className: multiplier === m ? BTN_PRIMARY : BTN_NEUTRAL,
376
374
  children: ["×", m]
377
375
  }, m))]
378
376
  }),
379
377
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
380
378
  className: `${require_index.TEXT_HINT} ml-1 tabular-nums`,
381
- children: [
382
- activeCount,
383
- " / ",
384
- cameraTotal,
385
- " cells · ",
386
- options.grid.width,
387
- "×",
388
- options.grid.height,
389
- multiplier !== 1 ? ` · paint ${uiWidth}×${uiHeight}` : ""
390
- ]
379
+ children: [t("uiLibrary.motionZones.cells", "{{active}} / {{total}} cells · {{width}}×{{height}}", {
380
+ active: activeCount,
381
+ total: cameraTotal,
382
+ width: options.grid.width,
383
+ height: options.grid.height
384
+ }), multiplier !== 1 ? t("uiLibrary.motionZones.paintSuffix", " · paint {{width}}×{{height}}", {
385
+ width: uiWidth,
386
+ height: uiHeight
387
+ }) : ""]
391
388
  }),
392
389
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "flex-1" }),
393
390
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -395,14 +392,14 @@ function MotionZonesSettings({ deviceId }) {
395
392
  onClick: revert,
396
393
  disabled: saving || !dirty,
397
394
  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",
398
- children: "Revert"
395
+ children: t("uiLibrary.motionZones.revert", "Revert")
399
396
  }),
400
397
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
401
398
  type: "button",
402
399
  onClick: () => void save(),
403
400
  disabled: saving || !dirty,
404
401
  className: BTN_PRIMARY,
405
- children: saving ? "Saving…" : "Save"
402
+ children: saving ? t("uiLibrary.motionZones.saving", "Saving…") : t("uiLibrary.motionZones.save", "Save")
406
403
  })
407
404
  ]
408
405
  })] })
@@ -1,6 +1,6 @@
1
1
  const require_mic_off = require("./mic-off-4nCMfrMI.cjs");
2
2
  const require_index = require("./index.cjs");
3
- const require_MaskShapeCanvas = require("./MaskShapeCanvas-BByN3jvt.cjs");
3
+ const require_MaskShapeCanvas = require("./MaskShapeCanvas-NCNwu9_5.cjs");
4
4
  let react = require("react");
5
5
  let react_jsx_runtime = require("react/jsx-runtime");
6
6
  //#region src/composites/cap-settings/privacy-mask-audio.ts
@@ -53,6 +53,7 @@ function regionsEqual(a, b) {
53
53
  return true;
54
54
  }
55
55
  function PrivacyMaskSettings({ deviceId }) {
56
+ const t = require_index.useUiText();
56
57
  const dev = require_index.useDeviceProxy$1(require_index.useSystem$1().trpcClient, deviceId);
57
58
  const [options, setOptions] = (0, react.useState)(null);
58
59
  const [unsupported, setUnsupported] = (0, react.useState)(false);
@@ -144,8 +145,8 @@ function PrivacyMaskSettings({ deviceId }) {
144
145
  id: r.id,
145
146
  shape: r.shape,
146
147
  enabled: r.enabled,
147
- label: `Zone ${String(r.id)}`
148
- })) : [], [draft]);
148
+ label: t("uiLibrary.privacyMaskSettings.zoneN", "Zone {{id}}", { id: r.id })
149
+ })) : [], [draft, t]);
149
150
  const onShapeChange = (0, react.useCallback)((id, shape) => {
150
151
  const next = toPrivacyMaskShape(shape);
151
152
  if (!next) return;
@@ -290,35 +291,27 @@ function PrivacyMaskSettings({ deviceId }) {
290
291
  const ready = !unsupported && !noShapes && !unreadable && options !== null && draft !== null;
291
292
  if (!dev) return null;
292
293
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.WidgetPanel, {
293
- title: "Privacy Mask",
294
+ title: t("uiLibrary.privacyMaskSettings.title", "Privacy Mask"),
294
295
  icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.EyeOff, { className: "h-3.5 w-3.5 text-foreground-subtle" }),
295
296
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
296
297
  className: "flex flex-col gap-3",
297
298
  children: [unsupported || noShapes ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
298
299
  className: `${require_index.TEXT_HINT} leading-relaxed`,
299
- children: "This camera doesn't support an on-board privacy mask."
300
+ children: t("uiLibrary.privacyMaskSettings.unsupported", "This camera doesn't support an on-board privacy mask.")
300
301
  }) : unreadable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
301
302
  className: `${require_index.TEXT_HINT} leading-relaxed`,
302
- children: "This camera hasn't answered, so what it currently masks is unknown — the editor stays closed rather than showing an empty frame you could save over. A battery camera answers once it wakes; otherwise check that it is reachable."
303
+ children: t("uiLibrary.privacyMaskSettings.unreadable", "This camera hasn't answered, so what it currently masks is unknown — the editor stays closed rather than showing an empty frame you could save over. A battery camera answers once it wakes; otherwise check that it is reachable.")
303
304
  }) : !ready ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
304
305
  className: `${require_index.TEXT_HINT} leading-relaxed`,
305
- children: "Loading the camera's privacy mask…"
306
+ children: t("uiLibrary.privacyMaskSettings.loading", "Loading the camera's privacy mask…")
306
307
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
307
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
308
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
308
309
  className: `${require_index.TEXT_HINT} leading-relaxed`,
309
- children: [
310
- "Toggle ",
311
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
312
- className: "text-foreground",
313
- children: "Edit mask"
314
- }),
315
- " to draw blanked-out zones on the live frame, then ",
316
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
317
- className: "text-foreground",
318
- children: "Save"
319
- }),
320
- " 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."
321
- ]
310
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.UiTrans, {
311
+ uiKey: "uiLibrary.privacyMaskSettings.help",
312
+ fallback: "Toggle <0>Edit mask</0> to draw blanked-out zones on the live frame, then <1>Save</1> 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.",
313
+ components: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { className: "text-foreground" }, "edit"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { className: "text-foreground" }, "save")]
314
+ })
322
315
  }),
323
316
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
324
317
  className: "flex items-center gap-2 flex-wrap",
@@ -329,7 +322,7 @@ function PrivacyMaskSettings({ deviceId }) {
329
322
  disabled: saving,
330
323
  "aria-pressed": editing,
331
324
  className: editing ? BTN_PRIMARY : BTN_NEUTRAL,
332
- children: editing ? "Done editing" : "Edit mask"
325
+ children: editing ? t("uiLibrary.privacyMaskSettings.doneEditing", "Done editing") : t("uiLibrary.privacyMaskSettings.editMask", "Edit mask")
333
326
  }),
334
327
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
335
328
  type: "button",
@@ -337,7 +330,7 @@ function PrivacyMaskSettings({ deviceId }) {
337
330
  disabled: saving,
338
331
  "aria-pressed": draft.enabled,
339
332
  className: draft.enabled ? BTN_PRIMARY : BTN_NEUTRAL,
340
- children: draft.enabled ? "Mask on" : "Mask off"
333
+ children: draft.enabled ? t("uiLibrary.privacyMaskSettings.maskOn", "Mask on") : t("uiLibrary.privacyMaskSettings.maskOff", "Mask off")
341
334
  }),
342
335
  supportsRect && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
343
336
  type: "button",
@@ -345,8 +338,8 @@ function PrivacyMaskSettings({ deviceId }) {
345
338
  disabled: saving || atCapacity,
346
339
  "aria-pressed": drawingKind === "rect",
347
340
  className: drawingKind === "rect" ? BTN_PRIMARY : BTN_NEUTRAL,
348
- title: atCapacity ? "Maximum zones reached" : "Add a rectangle zone",
349
- children: "+ Rect"
341
+ title: atCapacity ? t("uiLibrary.privacyMaskSettings.maxZones", "Maximum zones reached") : t("uiLibrary.privacyMaskSettings.addRect", "Add a rectangle zone"),
342
+ children: t("uiLibrary.privacyMaskSettings.rectButton", "+ Rect")
350
343
  }),
351
344
  supportsPolygon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
352
345
  type: "button",
@@ -354,17 +347,15 @@ function PrivacyMaskSettings({ deviceId }) {
354
347
  disabled: saving || atCapacity,
355
348
  "aria-pressed": drawingKind === "polygon",
356
349
  className: drawingKind === "polygon" ? BTN_PRIMARY : BTN_NEUTRAL,
357
- title: atCapacity ? "Maximum zones reached" : "Add a polygon zone",
358
- children: "+ Polygon"
350
+ title: atCapacity ? t("uiLibrary.privacyMaskSettings.maxZones", "Maximum zones reached") : t("uiLibrary.privacyMaskSettings.addPolygon", "Add a polygon zone"),
351
+ children: t("uiLibrary.privacyMaskSettings.polygonButton", "+ Polygon")
359
352
  }),
360
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
353
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
361
354
  className: `${require_index.TEXT_HINT} ml-1 tabular-nums`,
362
- children: [
363
- activeCount,
364
- " / ",
365
- maxRegions,
366
- " zones"
367
- ]
355
+ children: t("uiLibrary.privacyMaskSettings.zoneCount", "{{active}} / {{max}} zones", {
356
+ active: activeCount,
357
+ max: maxRegions
358
+ })
368
359
  }),
369
360
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "flex-1" }),
370
361
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -372,14 +363,14 @@ function PrivacyMaskSettings({ deviceId }) {
372
363
  onClick: revert,
373
364
  disabled: saving || !dirty,
374
365
  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",
375
- children: "Revert"
366
+ children: t("uiLibrary.privacyMaskSettings.revert", "Revert")
376
367
  }),
377
368
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
378
369
  type: "button",
379
370
  onClick: () => void save(),
380
371
  disabled: saving || !dirty,
381
372
  className: BTN_PRIMARY,
382
- children: saving ? "Saving…" : "Save"
373
+ children: saving ? t("uiLibrary.privacyMaskSettings.saving", "Saving…") : t("uiLibrary.privacyMaskSettings.save", "Save")
383
374
  })
384
375
  ]
385
376
  }),
@@ -397,7 +388,7 @@ function PrivacyMaskSettings({ deviceId }) {
397
388
  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",
398
389
  children: [
399
390
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShapeIcon, { className: "h-3.5 w-3.5 shrink-0" }),
400
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["Zone ", r.id] }),
391
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("uiLibrary.privacyMaskSettings.zoneN", "Zone {{id}}", { id: r.id }) }),
401
392
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
402
393
  className: "text-foreground-faint capitalize",
403
394
  children: r.shape.kind
@@ -407,8 +398,8 @@ function PrivacyMaskSettings({ deviceId }) {
407
398
  type: "button",
408
399
  onClick: () => deleteRegion(r.id),
409
400
  disabled: saving,
410
- "aria-label": `Delete zone ${String(r.id)}`,
411
- title: "Delete zone",
401
+ "aria-label": t("uiLibrary.privacyMaskSettings.deleteZoneN", "Delete zone {{id}}", { id: r.id }),
402
+ title: t("uiLibrary.privacyMaskSettings.deleteZone", "Delete zone"),
412
403
  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",
413
404
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.Trash2, { className: "h-3.5 w-3.5" })
414
405
  })]
@@ -424,15 +415,15 @@ function PrivacyMaskSettings({ deviceId }) {
424
415
  "aria-checked": microphone.on === true,
425
416
  disabled: !microphone.available || pendingAudio !== null,
426
417
  onClick: () => void toggleMicrophone(),
427
- title: microphone.on === null ? "Microphone — state unknown; the camera did not answer." : microphone.on ? `The camera is capturing sound. Turning it off: ${microphone.costWhenOff}` : `The camera is silent. ${microphone.costWhenOff}`,
418
+ title: microphone.on === null ? t("uiLibrary.privacyMaskSettings.micTitleUnknown", "Microphone — state unknown; the camera did not answer.") : microphone.on ? t("uiLibrary.privacyMaskSettings.micTitleOn", "The camera is capturing sound. Turning it off: {{cost}}", { cost: microphone.costWhenOff }) : t("uiLibrary.privacyMaskSettings.micTitleOff", "The camera is silent. {{cost}}", { cost: microphone.costWhenOff }),
428
419
  className: microphone.on === false || microphone.on === null ? BTN_NEUTRAL : BTN_PRIMARY,
429
420
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
430
421
  className: "inline-flex items-center gap-1.5",
431
- children: [microphone.on === false ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_mic_off.MicOff, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.Mic, { className: "h-3.5 w-3.5" }), microphone.on === null ? "Microphone unknown" : microphone.on ? "Microphone on" : "Microphone off"]
422
+ children: [microphone.on === false ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_mic_off.MicOff, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.Mic, { className: "h-3.5 w-3.5" }), microphone.on === null ? t("uiLibrary.privacyMaskSettings.micUnknown", "Microphone unknown") : microphone.on ? t("uiLibrary.privacyMaskSettings.micOn", "Microphone on") : t("uiLibrary.privacyMaskSettings.micOff", "Microphone off")]
432
423
  })
433
424
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
434
425
  className: `text-[10px] text-foreground-subtle flex-1 leading-relaxed`,
435
- children: audioFailure !== null ? `Write failed: ${audioFailure}` : microphone.on === null ? "The camera did not report its microphone, so this state is unknown." : microphone.costWhenOff
426
+ children: audioFailure !== null ? t("uiLibrary.privacyMaskSettings.writeFailed", "Write failed: {{reason}}", { reason: audioFailure }) : microphone.on === null ? t("uiLibrary.privacyMaskSettings.micNotReported", "The camera did not report its microphone, so this state is unknown.") : microphone.costWhenOff
436
427
  })]
437
428
  })]
438
429
  })