@camstack/addon-pipeline-orchestrator 1.2.324 → 1.2.326

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/_stub.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import { t as e } from "./_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-BiLBDmU5.mjs";
2
- import { n as t, r as n, t as r } from "./_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CdOR1t-0.mjs";
2
+ import { n as t, r as n, t as r } from "./_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-6CTaeZSL.mjs";
3
3
  import { a as i, c as a, i as o, n as s, o as c, r as l, s as u, t as d } from "./_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__react__loadShare__.js-a8tvh93M.mjs";
4
- import { C as f, E as p, S as m, T as h, _ as g, a as _, b as v, c as y, d as b, f as x, g as S, h as C, i as w, l as T, m as E, n as D, o as O, p as k, r as A, s as j, t as M, u as ee, v as N, w as P, x as F, y as I } from "./_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-iNnhcbuC.mjs";
5
- import { n as L, r as R, t as z } from "./_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-yD-rMaFX.mjs";
4
+ import { C as f, D as p, E as m, O as h, S as g, T as _, _ as v, a as y, b, c as x, d as S, f as C, g as w, h as T, i as E, l as D, m as O, n as k, o as A, p as j, r as M, s as N, t as P, u as F, v as I, w as L, x as R, y as z } from "./_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-y5F27gMS.mjs";
5
+ import { n as B, r as V, t as H } from "./_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-yD-rMaFX.mjs";
6
6
  //#region ../../node_modules/lucide-react/dist/esm/shared/src/utils/mergeClasses.js
7
- var B = (...e) => e.filter((e, t, n) => !!e && e.trim() !== "" && n.indexOf(e) === t).join(" ").trim(), V = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), te = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), H = (e) => {
8
- let t = te(e);
7
+ var U = (...e) => e.filter((e, t, n) => !!e && e.trim() !== "" && n.indexOf(e) === t).join(" ").trim(), W = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), G = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), K = (e) => {
8
+ let t = G(e);
9
9
  return t.charAt(0).toUpperCase() + t.slice(1);
10
- }, ne = {
10
+ }, ee = {
11
11
  xmlns: "http://www.w3.org/2000/svg",
12
12
  width: 24,
13
13
  height: 24,
@@ -17,34 +17,34 @@ var B = (...e) => e.filter((e, t, n) => !!e && e.trim() !== "" && n.indexOf(e) =
17
17
  strokeWidth: 2,
18
18
  strokeLinecap: "round",
19
19
  strokeLinejoin: "round"
20
- }, re = (e) => {
20
+ }, te = (e) => {
21
21
  for (let t in e) if (t.startsWith("aria-") || t === "role" || t === "title") return !0;
22
22
  return !1;
23
- }, ie = s(({ color: e = "currentColor", size: t = 24, strokeWidth: n = 2, absoluteStrokeWidth: r, className: i = "", children: a, iconNode: o, ...s }, c) => d("svg", {
23
+ }, ne = s(({ color: e = "currentColor", size: t = 24, strokeWidth: n = 2, absoluteStrokeWidth: r, className: i = "", children: a, iconNode: o, ...s }, c) => d("svg", {
24
24
  ref: c,
25
- ...ne,
25
+ ...ee,
26
26
  width: t,
27
27
  height: t,
28
28
  stroke: e,
29
29
  strokeWidth: r ? Number(n) * 24 / Number(t) : n,
30
- className: B("lucide", i),
31
- ...!a && !re(s) && { "aria-hidden": "true" },
30
+ className: U("lucide", i),
31
+ ...!a && !te(s) && { "aria-hidden": "true" },
32
32
  ...s
33
- }, [...o.map(([e, t]) => d(e, t)), ...Array.isArray(a) ? a : [a]])), U = (e, t) => {
34
- let n = s(({ className: n, ...r }, i) => d(ie, {
33
+ }, [...o.map(([e, t]) => d(e, t)), ...Array.isArray(a) ? a : [a]])), q = (e, t) => {
34
+ let n = s(({ className: n, ...r }, i) => d(ne, {
35
35
  ref: i,
36
36
  iconNode: t,
37
- className: B(`lucide-${V(H(e))}`, `lucide-${e}`, n),
37
+ className: U(`lucide-${W(K(e))}`, `lucide-${e}`, n),
38
38
  ...r
39
39
  }));
40
- return n.displayName = H(e), n;
41
- }, ae = U("activity", [["path", {
40
+ return n.displayName = K(e), n;
41
+ }, re = q("activity", [["path", {
42
42
  d: "M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",
43
43
  key: "169zse"
44
- }]]), oe = U("check", [["path", {
44
+ }]]), ie = q("check", [["path", {
45
45
  d: "M20 6 9 17l-5-5",
46
46
  key: "1gmf2c"
47
- }]]), W = U("cpu", [
47
+ }]]), ae = q("cpu", [
48
48
  ["path", {
49
49
  d: "M12 20v2",
50
50
  key: "1lh1kg"
@@ -109,7 +109,7 @@ var B = (...e) => e.filter((e, t, n) => !!e && e.trim() !== "" && n.indexOf(e) =
109
109
  rx: "1",
110
110
  key: "z9xiuo"
111
111
  }]
112
- ]), G = U("eye", [["path", {
112
+ ]), oe = q("eye", [["path", {
113
113
  d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",
114
114
  key: "1nclc0"
115
115
  }], ["circle", {
@@ -117,25 +117,25 @@ var B = (...e) => e.filter((e, t, n) => !!e && e.trim() !== "" && n.indexOf(e) =
117
117
  cy: "12",
118
118
  r: "3",
119
119
  key: "1v7zrd"
120
- }]]), K = U("gauge", [["path", {
120
+ }]]), se = q("gauge", [["path", {
121
121
  d: "m12 14 4-4",
122
122
  key: "9kzdfg"
123
123
  }], ["path", {
124
124
  d: "M3.34 19a10 10 0 1 1 17.32 0",
125
125
  key: "19p75a"
126
- }]]), q = U("hexagon", [["path", {
126
+ }]]), ce = q("hexagon", [["path", {
127
127
  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",
128
128
  key: "yt0hxn"
129
- }]]), se = U("minus", [["path", {
129
+ }]]), le = q("minus", [["path", {
130
130
  d: "M5 12h14",
131
131
  key: "1ays0h"
132
- }]]), ce = U("plus", [["path", {
132
+ }]]), ue = q("plus", [["path", {
133
133
  d: "M5 12h14",
134
134
  key: "1ays0h"
135
135
  }], ["path", {
136
136
  d: "M12 5v14",
137
137
  key: "s699le"
138
- }]]), le = U("save", [
138
+ }]]), de = q("save", [
139
139
  ["path", {
140
140
  d: "M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z",
141
141
  key: "1c8476"
@@ -148,7 +148,7 @@ var B = (...e) => e.filter((e, t, n) => !!e && e.trim() !== "" && n.indexOf(e) =
148
148
  d: "M7 3v4a1 1 0 0 0 1 1h7",
149
149
  key: "t51u73"
150
150
  }]
151
- ]), J = U("trash-2", [
151
+ ]), J = q("trash-2", [
152
152
  ["path", {
153
153
  d: "M10 11v6",
154
154
  key: "nco0om"
@@ -169,7 +169,7 @@ var B = (...e) => e.filter((e, t, n) => !!e && e.trim() !== "" && n.indexOf(e) =
169
169
  d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",
170
170
  key: "e791ji"
171
171
  }]
172
- ]), ue = U("video", [["path", {
172
+ ]), fe = q("video", [["path", {
173
173
  d: "m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5",
174
174
  key: "ftymec"
175
175
  }], ["rect", {
@@ -179,7 +179,7 @@ var B = (...e) => e.filter((e, t, n) => !!e && e.trim() !== "" && n.indexOf(e) =
179
179
  height: "12",
180
180
  rx: "2",
181
181
  key: "158x01"
182
- }]]), de = U("volume-2", [
182
+ }]]), pe = q("volume-2", [
183
183
  ["path", {
184
184
  d: "M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z",
185
185
  key: "uqj9uw"
@@ -192,120 +192,273 @@ var B = (...e) => e.filter((e, t, n) => !!e && e.trim() !== "" && n.indexOf(e) =
192
192
  d: "M19.364 18.364a9 9 0 0 0 0-12.728",
193
193
  key: "ijwkga"
194
194
  }]
195
- ]), Y = U("x", [["path", {
195
+ ]), Y = q("x", [["path", {
196
196
  d: "M18 6 6 18",
197
197
  key: "1bl5f8"
198
198
  }], ["path", {
199
199
  d: "m6 6 12 12",
200
200
  key: "d8bk6v"
201
- }]]);
201
+ }]]), { useT: X, Trans: me, translate: Z } = p("pipeline-orchestrator", {
202
+ en: {
203
+ quickStats: {
204
+ requiresDevice: "PipelineQuickStats requires a deviceId",
205
+ phase: "Phase",
206
+ detectionFps: "Detection FPS",
207
+ inference: "Inference",
208
+ activeTracks: "Active Tracks",
209
+ currentDb: "Current dB",
210
+ recording: "Recording",
211
+ target: "target: {{value}}",
212
+ queue: "queue: {{value}}",
213
+ dropped: "dropped: {{value}}",
214
+ rec: {
215
+ disabled: "Disabled",
216
+ idle: "Idle",
217
+ continuous: "Continuous",
218
+ onMotion: "On motion",
219
+ onAudio: "On audio"
220
+ }
221
+ },
222
+ zoneEditor: { requiresDevice: "ZoneEditor requires a deviceId" },
223
+ zonesTab: {
224
+ title: "Detection Zones",
225
+ hint: "Pick a shape below or use the <b>Zones</b> button on the live frame to draw new polygons or tripwires. Behaviour is set per-stage in the rules editors below — a zone with no rule is observed by analytics but never filters."
226
+ },
227
+ zone: { kind: {
228
+ polygon: "Polygon",
229
+ tripwire: "Tripwire"
230
+ } },
231
+ zoneList: {
232
+ cancelDrawing: "Cancel drawing",
233
+ cancel: "Cancel",
234
+ addPolygon: "Add polygon zone",
235
+ addTripwire: "Add tripwire",
236
+ empty: "No zones yet — pick \"Zone\" or \"Tripwire\" to draw.",
237
+ deleteZone: "Delete zone"
238
+ },
239
+ zoneForm: {
240
+ editing: "Editing",
241
+ namePlaceholder: "Zone name",
242
+ twoPoints: "2 points",
243
+ pointCount: "{{count}} pts",
244
+ dragHint: "· drag handles to reshape",
245
+ saveTitle: "Save changes (Enter)",
246
+ save: "Save",
247
+ discardTitle: "Discard changes (Esc)",
248
+ discard: "Discard",
249
+ delete: "Delete"
250
+ },
251
+ rules: {
252
+ title: {
253
+ motion: "Motion Rules",
254
+ detection: "Detection Rules",
255
+ package: "Package Rules"
256
+ },
257
+ add: "Add rule",
258
+ addShort: "Add",
259
+ save: "Save",
260
+ noZones: "Define a zone first — rules reference zone ids.",
261
+ empty: "No rules — every detection at this stage passes through.",
262
+ disable: "Disable rule",
263
+ enable: "Enable rule",
264
+ activeTitle: "Rule is active — click to disable",
265
+ disabledTitle: "Rule is disabled — click to enable",
266
+ namePlaceholder: "Rule name (optional)",
267
+ remove: "Remove rule",
268
+ mode: "Mode",
269
+ include: "Include",
270
+ exclude: "Exclude",
271
+ zones: "Zones",
272
+ noneZones: "none",
273
+ class: "Class",
274
+ anyClass: "any"
275
+ }
276
+ },
277
+ it: {
278
+ quickStats: {
279
+ requiresDevice: "PipelineQuickStats richiede un deviceId",
280
+ phase: "Fase",
281
+ detectionFps: "FPS di rilevamento",
282
+ inference: "Inferenza",
283
+ activeTracks: "Tracce attive",
284
+ currentDb: "dB attuali",
285
+ recording: "Registrazione",
286
+ target: "obiettivo: {{value}}",
287
+ queue: "coda: {{value}}",
288
+ dropped: "scartati: {{value}}",
289
+ rec: {
290
+ disabled: "Disattivata",
291
+ idle: "Inattiva",
292
+ continuous: "Continua",
293
+ onMotion: "Su movimento",
294
+ onAudio: "Su audio"
295
+ }
296
+ },
297
+ zoneEditor: { requiresDevice: "ZoneEditor richiede un deviceId" },
298
+ zonesTab: {
299
+ title: "Zone di rilevamento",
300
+ hint: "Scegli una forma qui sotto oppure usa il pulsante <b>Zone</b> sul fotogramma live per disegnare nuovi poligoni o linee di passaggio. Il comportamento si imposta per fase negli editor delle regole qui sotto: una zona senza regole viene osservata dalle analisi ma non filtra mai."
301
+ },
302
+ zone: { kind: {
303
+ polygon: "Poligono",
304
+ tripwire: "Linea di passaggio"
305
+ } },
306
+ zoneList: {
307
+ cancelDrawing: "Annulla disegno",
308
+ cancel: "Annulla",
309
+ addPolygon: "Aggiungi zona poligonale",
310
+ addTripwire: "Aggiungi linea di passaggio",
311
+ empty: "Nessuna zona ancora: scegli \"Zona\" o \"Linea di passaggio\" per disegnare.",
312
+ deleteZone: "Elimina zona"
313
+ },
314
+ zoneForm: {
315
+ editing: "Modifica",
316
+ namePlaceholder: "Nome zona",
317
+ twoPoints: "2 punti",
318
+ pointCount: "{{count}} punti",
319
+ dragHint: "· trascina i punti per rimodellare",
320
+ saveTitle: "Salva le modifiche (Invio)",
321
+ save: "Salva",
322
+ discardTitle: "Scarta le modifiche (Esc)",
323
+ discard: "Scarta",
324
+ delete: "Elimina"
325
+ },
326
+ rules: {
327
+ title: {
328
+ motion: "Regole di movimento",
329
+ detection: "Regole di rilevamento",
330
+ package: "Regole pacchi"
331
+ },
332
+ add: "Aggiungi regola",
333
+ addShort: "Aggiungi",
334
+ save: "Salva",
335
+ noZones: "Definisci prima una zona: le regole fanno riferimento agli id delle zone.",
336
+ empty: "Nessuna regola: ogni rilevamento di questa fase passa senza filtri.",
337
+ disable: "Disattiva regola",
338
+ enable: "Attiva regola",
339
+ activeTitle: "La regola è attiva: clicca per disattivarla",
340
+ disabledTitle: "La regola è disattivata: clicca per attivarla",
341
+ namePlaceholder: "Nome regola (facoltativo)",
342
+ remove: "Rimuovi regola",
343
+ mode: "Modalità",
344
+ include: "Includi",
345
+ exclude: "Escludi",
346
+ zones: "Zone",
347
+ noneZones: "nessuna",
348
+ class: "Classe",
349
+ anyClass: "qualsiasi"
350
+ }
351
+ }
352
+ });
202
353
  //#endregion
203
354
  //#region src/widgets/PipelineQuickStats.tsx
204
- function fe(e) {
205
- return e.deviceId === void 0 ? /* @__PURE__ */ L("div", {
355
+ function he(e) {
356
+ let { t } = X();
357
+ return e.deviceId === void 0 ? /* @__PURE__ */ B("div", {
206
358
  className: "rounded-lg border border-warning/30 bg-warning/10 px-3 py-2 text-xs text-warning",
207
- children: "PipelineQuickStats requires a deviceId"
208
- }) : /* @__PURE__ */ L(pe, { deviceId: e.deviceId });
359
+ children: t("quickStats.requiresDevice")
360
+ }) : /* @__PURE__ */ B(ge, { deviceId: e.deviceId });
209
361
  }
210
- function pe({ deviceId: n }) {
211
- A(["pipelineOrchestrator", "getPipelineAssignment"], [
362
+ function ge({ deviceId: n }) {
363
+ let { t: r } = X(), i = x();
364
+ M(["pipelineOrchestrator", "getPipelineAssignment"], [
212
365
  "pipeline.camera-assigned",
213
366
  "pipeline.camera-unassigned",
214
367
  "pipeline.camera-updated"
215
- ]), A(["pipelineRunner", "getCameraMetrics"], ["pipeline.camera-metrics-snapshot"]);
216
- let { data: r } = w({ deviceId: n }, { staleTime: 3e4 }), i = r?.agentNodeId ?? null, a = typeof i == "string" && i.length > 0 && !i.startsWith("addon:") ? i : void 0, { data: o } = _({
368
+ ]), M(["pipelineRunner", "getCameraMetrics"], ["pipeline.camera-metrics-snapshot"]);
369
+ let { data: a } = E({ deviceId: n }, { staleTime: 3e4 }), o = a?.agentNodeId ?? null, s = typeof o == "string" && o.length > 0 && !o.startsWith("addon:") ? o : void 0, { data: c } = y({
217
370
  deviceId: n,
218
- ...a ? { nodeId: a } : {}
371
+ ...s ? { nodeId: s } : {}
219
372
  }, {
220
373
  staleTime: 2e3,
221
374
  retry: !1
222
- }), s = j(), c = M(s.trpcClient, n), { data: l } = e({
375
+ }), l = N(), u = P(l.trpcClient, n), { data: d } = e({
223
376
  queryKey: [
224
377
  "device",
225
378
  n,
226
379
  "pipelineAnalytics.getActiveTracks"
227
380
  ],
228
- queryFn: () => c?.pipelineAnalytics?.getActiveTracks({}) ?? [],
229
- enabled: !!c,
381
+ queryFn: () => u?.pipelineAnalytics?.getActiveTracks({}) ?? [],
382
+ enabled: !!u,
230
383
  refetchInterval: 5e3,
231
384
  retry: !1
232
- }), { data: u } = e({
385
+ }), { data: f } = e({
233
386
  queryKey: [
234
387
  "device",
235
388
  n,
236
389
  "audioMetrics.getCurrentSnapshot"
237
390
  ],
238
- queryFn: () => c?.audioMetrics?.getCurrentSnapshot({}) ?? null,
239
- enabled: !!c,
391
+ queryFn: () => u?.audioMetrics?.getCurrentSnapshot({}) ?? null,
392
+ enabled: !!u,
240
393
  refetchInterval: 2e3,
241
394
  retry: !1
242
- }), { data: d } = e({
395
+ }), { data: p } = e({
243
396
  queryKey: ["recordingArchive.getStatus", n],
244
- queryFn: async () => await s.trpcClient.recordingArchive.getStatus.query({ deviceId: n }),
397
+ queryFn: async () => await l.trpcClient.recordingArchive.getStatus.query({ deviceId: n }),
245
398
  refetchInterval: 5e3,
246
399
  retry: !1
247
- }), { data: f } = e({
400
+ }), { data: m } = e({
248
401
  queryKey: ["recordingArchive.getDeviceConfig", n],
249
- queryFn: async () => await s.trpcClient.recordingArchive.getDeviceConfig.query({ deviceId: n }),
402
+ queryFn: async () => await l.trpcClient.recordingArchive.getDeviceConfig.query({ deviceId: n }),
250
403
  staleTime: 3e4,
251
404
  retry: !1
252
- }), { data: m } = e({
405
+ }), { data: g } = e({
253
406
  queryKey: [
254
407
  "device",
255
408
  n,
256
409
  "addonSettings.getDeviceSettings.detectionMode"
257
410
  ],
258
- queryFn: () => s.trpcClient.addonSettings.getDeviceSettings.query({
411
+ queryFn: () => l.trpcClient.addonSettings.getDeviceSettings.query({
259
412
  addonId: "pipeline-orchestrator",
260
413
  deviceId: n
261
414
  }),
262
415
  staleTime: 3e4,
263
416
  retry: !1
264
- }), h = t(m, "detectionMode"), g = typeof h == "string" ? h : null, y = o?.phase ?? null, b = g, x = Number(o?.actualFps ?? 0), S = Number(o?.configuredFps ?? 0), C = Number(o?.avgInferenceTimeMs ?? 0), T = Number(o?.queueDepth ?? 0), E = Number(o?.droppedFrames ?? 0), D = p(y ?? "watching"), O = l?.length ?? 0, k = u?.level.dbfs ?? null, N = typeof k == "number" ? `${k.toFixed(1)} dBFS` : "—", P = d?.enabled ?? !1, F = d?.activeMode ?? "off", I = P && F !== "off", z = P ? F === "off" ? "Idle" : F === "continuous" ? "Continuous" : F === "onMotion" ? "On motion" : F === "onAudioThreshold" ? "On audio" : F : "Disabled", B = (f?.profiles ?? []).join(", "), V = d ? `${me(d.storageBytes ?? 0)}${B ? ` · ${B}` : ""}` : void 0;
265
- return /* @__PURE__ */ R("div", {
266
- className: ee,
417
+ }), _ = t(g, "detectionMode"), v = typeof _ == "string" ? _ : null, b = c?.phase ?? null, C = v, w = Number(c?.actualFps ?? 0), T = Number(c?.configuredFps ?? 0), D = Number(c?.avgInferenceTimeMs ?? 0), O = Number(c?.queueDepth ?? 0), k = Number(c?.droppedFrames ?? 0), A = h(b ?? "watching", i), j = d?.length ?? 0, F = f?.level.dbfs ?? null, I = typeof F == "number" ? `${F.toFixed(1)} dBFS` : "—", L = p?.enabled ?? !1, z = p?.activeMode ?? "off", H = L && z !== "off", U = L ? z === "off" ? r("quickStats.rec.idle") : z === "continuous" ? r("quickStats.rec.continuous") : z === "onMotion" ? r("quickStats.rec.onMotion") : z === "onAudioThreshold" ? r("quickStats.rec.onAudio") : z : r("quickStats.rec.disabled"), W = (m?.profiles ?? []).join(", "), G = p ? `${_e(p.storageBytes ?? 0)}${W ? ` · ${W}` : ""}` : void 0;
418
+ return /* @__PURE__ */ V("div", {
419
+ className: S,
267
420
  children: [
268
- /* @__PURE__ */ L(v, {
269
- icon: ae,
270
- label: "Phase",
271
- value: D.label,
272
- valueClassName: D.textColor,
273
- subtext: b ?? void 0
421
+ /* @__PURE__ */ B(R, {
422
+ icon: re,
423
+ label: r("quickStats.phase"),
424
+ value: A.label,
425
+ valueClassName: A.textColor,
426
+ subtext: C ?? void 0
274
427
  }),
275
- /* @__PURE__ */ L(v, {
276
- icon: K,
277
- label: "Detection FPS",
278
- value: `${x.toFixed(1)}`,
279
- subtext: S > 0 ? `target: ${S}` : void 0
428
+ /* @__PURE__ */ B(R, {
429
+ icon: se,
430
+ label: r("quickStats.detectionFps"),
431
+ value: `${w.toFixed(1)}`,
432
+ subtext: T > 0 ? r("quickStats.target", { value: T }) : void 0
280
433
  }),
281
- /* @__PURE__ */ L(v, {
282
- icon: W,
283
- label: "Inference",
284
- value: `${C.toFixed(1)} ms`,
285
- subtext: T > 0 ? `queue: ${T}` : void 0
434
+ /* @__PURE__ */ B(R, {
435
+ icon: ae,
436
+ label: r("quickStats.inference"),
437
+ value: `${D.toFixed(1)} ms`,
438
+ subtext: O > 0 ? r("quickStats.queue", { value: O }) : void 0
286
439
  }),
287
- /* @__PURE__ */ L(v, {
288
- icon: G,
289
- label: "Active Tracks",
290
- value: String(O),
291
- subtext: E > 0 ? `dropped: ${E}` : void 0
440
+ /* @__PURE__ */ B(R, {
441
+ icon: oe,
442
+ label: r("quickStats.activeTracks"),
443
+ value: String(j),
444
+ subtext: k > 0 ? r("quickStats.dropped", { value: k }) : void 0
292
445
  }),
293
- /* @__PURE__ */ L(v, {
294
- icon: de,
295
- label: "Current dB",
296
- value: N
446
+ /* @__PURE__ */ B(R, {
447
+ icon: pe,
448
+ label: r("quickStats.currentDb"),
449
+ value: I
297
450
  }),
298
- /* @__PURE__ */ L(v, {
299
- icon: ue,
300
- label: "Recording",
301
- value: z,
302
- valueClassName: I ? "text-success" : void 0,
303
- subtext: V
451
+ /* @__PURE__ */ B(R, {
452
+ icon: fe,
453
+ label: r("quickStats.recording"),
454
+ value: U,
455
+ valueClassName: H ? "text-success" : void 0,
456
+ subtext: G
304
457
  })
305
458
  ]
306
459
  });
307
460
  }
308
- function me(e) {
461
+ function _e(e) {
309
462
  if (!Number.isFinite(e) || e <= 0) return "0 B";
310
463
  let t = [
311
464
  "B",
@@ -318,70 +471,68 @@ function me(e) {
318
471
  }
319
472
  //#endregion
320
473
  //#region src/widgets/zone-editor/ZoneList.tsx
321
- var he = {
322
- polygon: "Polygon",
323
- tripwire: "Tripwire"
324
- }, ge = {
474
+ var ve = {
325
475
  polygon: "bg-info/10 text-info",
326
476
  tripwire: "bg-warning/10 text-warning"
327
477
  };
328
- function X({ zones: e, selectedZoneId: t, drawingKind: n, onSelectZone: r, onDeleteZone: i, onStartDraw: a, onCancelDraw: o }) {
329
- return /* @__PURE__ */ R("div", {
478
+ function ye({ zones: e, selectedZoneId: t, drawingKind: n, onSelectZone: r, onDeleteZone: i, onStartDraw: a, onCancelDraw: o }) {
479
+ let { t: s } = X();
480
+ return /* @__PURE__ */ V("div", {
330
481
  className: "flex flex-col gap-2",
331
- children: [/* @__PURE__ */ L("div", {
482
+ children: [/* @__PURE__ */ B("div", {
332
483
  className: "flex items-center gap-1",
333
- children: n ? /* @__PURE__ */ R("button", {
484
+ children: n ? /* @__PURE__ */ V("button", {
334
485
  type: "button",
335
486
  onClick: o,
336
487
  className: "inline-flex items-center justify-center gap-1 rounded border border-amber-500/30 bg-amber-500/10 text-amber-400 hover:bg-amber-500/20 transition-colors h-8 px-3 text-[11px] font-medium",
337
- title: "Cancel drawing",
338
- children: [/* @__PURE__ */ L(Y, { className: "h-3.5 w-3.5" }), /* @__PURE__ */ L("span", { children: "Cancel" })]
339
- }) : /* @__PURE__ */ R(z, { children: [/* @__PURE__ */ L("button", {
488
+ title: s("zoneList.cancelDrawing"),
489
+ children: [/* @__PURE__ */ B(Y, { className: "h-3.5 w-3.5" }), /* @__PURE__ */ B("span", { children: s("zoneList.cancel") })]
490
+ }) : /* @__PURE__ */ V(H, { children: [/* @__PURE__ */ B("button", {
340
491
  type: "button",
341
492
  onClick: () => a("polygon"),
342
493
  className: "inline-flex items-center justify-center rounded border border-border bg-surface text-foreground-subtle hover:text-primary hover:border-primary/40 hover:bg-primary/5 transition-colors h-8 w-8",
343
- title: "Add polygon zone",
344
- "aria-label": "Add polygon zone",
345
- children: /* @__PURE__ */ L(q, { className: "h-4 w-4" })
346
- }), /* @__PURE__ */ L("button", {
494
+ title: s("zoneList.addPolygon"),
495
+ "aria-label": s("zoneList.addPolygon"),
496
+ children: /* @__PURE__ */ B(ce, { className: "h-4 w-4" })
497
+ }), /* @__PURE__ */ B("button", {
347
498
  type: "button",
348
499
  onClick: () => a("tripwire"),
349
500
  className: "inline-flex items-center justify-center rounded border border-border bg-surface text-foreground-subtle hover:text-primary hover:border-primary/40 hover:bg-primary/5 transition-colors h-8 w-8",
350
- title: "Add tripwire",
351
- "aria-label": "Add tripwire",
352
- children: /* @__PURE__ */ L(se, { className: "h-5 w-5 stroke-[2.5]" })
501
+ title: s("zoneList.addTripwire"),
502
+ "aria-label": s("zoneList.addTripwire"),
503
+ children: /* @__PURE__ */ B(le, { className: "h-5 w-5 stroke-[2.5]" })
353
504
  })] })
354
- }), e.length === 0 ? /* @__PURE__ */ L("p", {
505
+ }), e.length === 0 ? /* @__PURE__ */ B("p", {
355
506
  className: "text-center text-[10px] text-foreground-subtle py-3 leading-tight",
356
- children: "No zones yet — pick \"Zone\" or \"Tripwire\" to draw."
357
- }) : /* @__PURE__ */ L("div", {
507
+ children: s("zoneList.empty")
508
+ }) : /* @__PURE__ */ B("div", {
358
509
  className: "flex flex-col gap-1",
359
510
  children: e.map((e) => {
360
511
  let n = e.id === t;
361
- return /* @__PURE__ */ R("div", {
512
+ return /* @__PURE__ */ V("div", {
362
513
  className: ["flex items-center gap-1.5 rounded border px-1.5 py-1 cursor-pointer transition-colors", n ? "border-primary/50 bg-primary/10" : "border-border bg-surface hover:bg-surface-hover"].join(" "),
363
514
  onClick: () => r(n ? null : e.id),
364
515
  children: [
365
- /* @__PURE__ */ L("div", {
516
+ /* @__PURE__ */ B("div", {
366
517
  className: "h-2.5 w-2.5 rounded-sm flex-shrink-0",
367
518
  style: { backgroundColor: e.color }
368
519
  }),
369
- /* @__PURE__ */ L("span", {
520
+ /* @__PURE__ */ B("span", {
370
521
  className: "flex-1 truncate text-[11px] font-medium text-foreground",
371
522
  children: e.name
372
523
  }),
373
- /* @__PURE__ */ L("span", {
374
- className: `inline-flex items-center rounded-full px-1.5 py-0.5 text-[9px] font-medium flex-shrink-0 ${ge[e.kind]}`,
375
- children: he[e.kind]
524
+ /* @__PURE__ */ B("span", {
525
+ className: `inline-flex items-center rounded-full px-1.5 py-0.5 text-[9px] font-medium flex-shrink-0 ${ve[e.kind]}`,
526
+ children: e.kind === "tripwire" ? s("zone.kind.tripwire") : s("zone.kind.polygon")
376
527
  }),
377
- /* @__PURE__ */ L("button", {
528
+ /* @__PURE__ */ B("button", {
378
529
  type: "button",
379
- title: "Delete zone",
530
+ title: s("zoneList.deleteZone"),
380
531
  className: "p-0.5 text-foreground-subtle hover:text-danger rounded transition-colors flex-shrink-0",
381
532
  onClick: (t) => {
382
533
  t.stopPropagation(), i(e.id);
383
534
  },
384
- children: /* @__PURE__ */ L(J, { className: "h-3 w-3" })
535
+ children: /* @__PURE__ */ B(J, { className: "h-3 w-3" })
385
536
  })
386
537
  ]
387
538
  }, e.id);
@@ -391,7 +542,7 @@ function X({ zones: e, selectedZoneId: t, drawingKind: n, onSelectZone: r, onDel
391
542
  }
392
543
  //#endregion
393
544
  //#region src/widgets/zone-editor/ZoneForm.tsx
394
- var _e = [
545
+ var be = [
395
546
  "#3b82f6",
396
547
  "#10b981",
397
548
  "#f59e0b",
@@ -401,110 +552,109 @@ var _e = [
401
552
  "#f97316",
402
553
  "#ec4899"
403
554
  ];
404
- function ve({ zone: e, onSave: t, onDiscard: n, onDelete: r }) {
405
- let i = y(), a = (e) => i?.updateDraft({ name: e }), o = (e) => i?.updateDraft({ color: e });
406
- return /* @__PURE__ */ R("div", {
555
+ function xe({ zone: e, onSave: t, onDiscard: n, onDelete: r }) {
556
+ let { t: i } = X(), a = D(), o = (e) => a?.updateDraft({ name: e }), s = (e) => a?.updateDraft({ color: e });
557
+ return /* @__PURE__ */ V("div", {
407
558
  className: "flex flex-col gap-2",
408
559
  children: [
409
- /* @__PURE__ */ R("div", {
560
+ /* @__PURE__ */ V("div", {
410
561
  className: "flex items-center justify-between",
411
- children: [/* @__PURE__ */ L("h3", {
562
+ children: [/* @__PURE__ */ B("h3", {
412
563
  className: "text-[10px] font-semibold text-foreground-subtle uppercase tracking-wider",
413
- children: "Editing"
414
- }), /* @__PURE__ */ L("span", {
564
+ children: i("zoneForm.editing")
565
+ }), /* @__PURE__ */ B("span", {
415
566
  className: "inline-flex items-center rounded-full px-1.5 py-0.5 text-[9px] font-medium bg-surface border border-border text-foreground-subtle",
416
- children: e.kind === "tripwire" ? "Tripwire" : "Polygon"
567
+ children: e.kind === "tripwire" ? i("zone.kind.tripwire") : i("zone.kind.polygon")
417
568
  })]
418
569
  }),
419
- /* @__PURE__ */ L("input", {
570
+ /* @__PURE__ */ B("input", {
420
571
  type: "text",
421
- className: k,
572
+ className: O,
422
573
  value: e.name,
423
574
  autoFocus: !0,
424
- onChange: (e) => a(e.target.value),
575
+ onChange: (e) => o(e.target.value),
425
576
  onKeyDown: (e) => {
426
577
  e.key === "Enter" ? (e.preventDefault(), t()) : e.key === "Escape" && (e.preventDefault(), n());
427
578
  },
428
- placeholder: "Zone name"
579
+ placeholder: i("zoneForm.namePlaceholder")
429
580
  }),
430
- /* @__PURE__ */ L("div", {
581
+ /* @__PURE__ */ B("div", {
431
582
  className: "flex flex-wrap gap-1",
432
- children: _e.map((t) => /* @__PURE__ */ L("button", {
583
+ children: be.map((t) => /* @__PURE__ */ B("button", {
433
584
  type: "button",
434
585
  title: t,
435
586
  className: ["h-4 w-4 rounded border-2 cursor-pointer transition-transform hover:scale-110", e.color === t ? "border-white scale-110" : "border-transparent"].join(" "),
436
587
  style: { backgroundColor: t },
437
- onClick: () => o(t)
588
+ onClick: () => s(t)
438
589
  }, t))
439
590
  }),
440
- /* @__PURE__ */ R("div", {
591
+ /* @__PURE__ */ V("div", {
441
592
  className: "flex items-center gap-1.5",
442
- children: [/* @__PURE__ */ L("input", {
593
+ children: [/* @__PURE__ */ B("input", {
443
594
  type: "color",
444
595
  className: "h-6 w-6 rounded border border-border bg-background cursor-pointer p-0.5",
445
596
  value: e.color,
446
- onChange: (e) => o(e.target.value)
447
- }), /* @__PURE__ */ L("input", {
597
+ onChange: (e) => s(e.target.value)
598
+ }), /* @__PURE__ */ B("input", {
448
599
  type: "text",
449
- className: `${k} font-mono`,
600
+ className: `${O} font-mono`,
450
601
  value: e.color,
451
602
  placeholder: "#000000",
452
- onChange: (e) => o(e.target.value)
603
+ onChange: (e) => s(e.target.value)
453
604
  })]
454
605
  }),
455
- /* @__PURE__ */ R("p", {
606
+ /* @__PURE__ */ V("p", {
456
607
  className: "text-[9px] text-foreground-subtle leading-tight",
457
- children: [e.kind === "tripwire" ? "2 points" : `${e.points.length} pts`, e.points.length > 0 && " · drag handles to reshape"]
608
+ children: [e.kind === "tripwire" ? i("zoneForm.twoPoints") : i("zoneForm.pointCount", { count: e.points.length }), e.points.length > 0 && ` ${i("zoneForm.dragHint")}`]
458
609
  }),
459
- /* @__PURE__ */ R("div", {
610
+ /* @__PURE__ */ V("div", {
460
611
  className: "flex items-center gap-1.5",
461
- children: [/* @__PURE__ */ R("button", {
612
+ children: [/* @__PURE__ */ V("button", {
462
613
  type: "button",
463
614
  onClick: t,
464
615
  className: "inline-flex items-center justify-center gap-1 rounded border border-primary/40 bg-primary/10 text-primary hover:bg-primary/20 transition-colors h-7 flex-1 text-[11px] font-medium",
465
- title: "Save changes (Enter)",
466
- children: [/* @__PURE__ */ L(oe, { className: "h-3 w-3" }), "Save"]
467
- }), /* @__PURE__ */ R("button", {
616
+ title: i("zoneForm.saveTitle"),
617
+ children: [/* @__PURE__ */ B(ie, { className: "h-3 w-3" }), i("zoneForm.save")]
618
+ }), /* @__PURE__ */ V("button", {
468
619
  type: "button",
469
620
  onClick: n,
470
621
  className: "inline-flex items-center justify-center gap-1 rounded border border-border bg-surface text-foreground-subtle hover:text-foreground hover:bg-surface-hover transition-colors h-7 px-3 text-[11px] font-medium",
471
- title: "Discard changes (Esc)",
472
- children: [/* @__PURE__ */ L(Y, { className: "h-3 w-3" }), "Discard"]
622
+ title: i("zoneForm.discardTitle"),
623
+ children: [/* @__PURE__ */ B(Y, { className: "h-3 w-3" }), i("zoneForm.discard")]
473
624
  })]
474
625
  }),
475
- /* @__PURE__ */ R("button", {
626
+ /* @__PURE__ */ V("button", {
476
627
  type: "button",
477
628
  onClick: r,
478
- className: `${x} w-full`,
479
- children: [/* @__PURE__ */ L(J, { className: "h-3 w-3" }), "Delete"]
629
+ className: `${j} w-full`,
630
+ children: [/* @__PURE__ */ B(J, { className: "h-3 w-3" }), i("zoneForm.delete")]
480
631
  })
481
632
  ]
482
633
  });
483
634
  }
484
635
  //#endregion
485
636
  //#region src/widgets/zone-editor/zone-rule-stage.ts
486
- var ye = {
487
- motion: "Motion",
488
- detection: "Detection",
489
- package: "Package"
490
- };
491
- function be(e) {
492
- return `${ye[e]} Rules`;
637
+ function Se(e) {
638
+ switch (e) {
639
+ case "motion": return Z("rules.title.motion");
640
+ case "detection": return Z("rules.title.detection");
641
+ case "package": return Z("rules.title.package");
642
+ }
493
643
  }
494
644
  r.options;
495
645
  //#endregion
496
646
  //#region src/widgets/zone-editor/zone-rule-class-filter.ts
497
- var xe = new Set(["detection"]);
498
- function Se(e) {
499
- return xe.has(e);
647
+ var Ce = new Set(["detection"]);
648
+ function we(e) {
649
+ return Ce.has(e);
500
650
  }
501
651
  //#endregion
502
652
  //#region src/widgets/zone-editor/ZoneRulesEditor.tsx
503
- var Ce = n.map((e) => ({
653
+ var Te = n.map((e) => ({
504
654
  id: e.id,
505
655
  label: e.name
506
656
  }));
507
- function we(e) {
657
+ function Ee(e) {
508
658
  return {
509
659
  id: e.id,
510
660
  name: e.name ?? "",
@@ -514,7 +664,7 @@ function we(e) {
514
664
  enabled: e.enabled !== !1
515
665
  };
516
666
  }
517
- function Te(e) {
667
+ function De(e) {
518
668
  let t = {
519
669
  id: e.id,
520
670
  zoneIds: e.zoneIds,
@@ -523,199 +673,203 @@ function Te(e) {
523
673
  };
524
674
  return e.name && (t.name = e.name), e.classFilter.length > 0 && (t.classFilter = e.classFilter), t;
525
675
  }
526
- function Z({ dev: e, stage: t, zones: n, title: r }) {
527
- let a = D(e?.state.zoneRules), o = c(() => a?.[t] ?? [], [a, t]), [s, l] = u([]), [d, p] = u(!1), [g, _] = u(!1), [v, y] = u(null), x = Se(t);
676
+ function Q({ dev: e, stage: t, zones: n, title: r }) {
677
+ let { t: a } = X(), o = k(e?.state.zoneRules), s = c(() => o?.[t] ?? [], [o, t]), [l, d] = u([]), [p, h] = u(!1), [g, v] = u(!1), [y, b] = u(null), x = we(t);
528
678
  i(() => {
529
- d || l(o.map(we));
530
- }, [o, d]);
679
+ p || d(s.map(Ee));
680
+ }, [s, p]);
531
681
  let S = () => {
532
- l((e) => [...e, {
682
+ d((e) => [...e, {
533
683
  id: crypto.randomUUID(),
534
684
  name: "",
535
685
  zoneIds: n[0] ? [n[0].id] : [],
536
686
  mode: "exclude",
537
687
  classFilter: [],
538
688
  enabled: !0
539
- }]), p(!0);
540
- }, C = (e, t) => {
541
- l((n) => n.map((n, r) => {
689
+ }]), h(!0);
690
+ }, w = (e, t) => {
691
+ d((n) => n.map((n, r) => {
542
692
  if (r !== e) return n;
543
693
  let i = n.classFilter.includes(t) ? n.classFilter.filter((e) => e !== t) : [...n.classFilter, t];
544
694
  return {
545
695
  ...n,
546
696
  classFilter: i
547
697
  };
548
- })), p(!0);
549
- }, w = (e, t) => {
550
- l((n) => n.map((n, r) => {
698
+ })), h(!0);
699
+ }, E = (e, t) => {
700
+ d((n) => n.map((n, r) => {
551
701
  if (r !== e) return n;
552
702
  let i = n.zoneIds.includes(t) ? n.zoneIds.filter((e) => e !== t) : [...n.zoneIds, t];
553
703
  return {
554
704
  ...n,
555
705
  zoneIds: i
556
706
  };
557
- })), p(!0);
558
- }, T = (e) => {
559
- l((t) => t.filter((t, n) => n !== e)), p(!0);
560
- }, O = (e, t) => {
561
- l((n) => n.map((n, r) => r === e ? {
707
+ })), h(!0);
708
+ }, D = (e) => {
709
+ d((t) => t.filter((t, n) => n !== e)), h(!0);
710
+ }, A = (e, t) => {
711
+ d((n) => n.map((n, r) => r === e ? {
562
712
  ...n,
563
713
  ...t
564
- } : n)), p(!0);
565
- }, A = async () => {
714
+ } : n)), h(!0);
715
+ }, j = async () => {
566
716
  if (e?.zoneRules) {
567
- _(!0), y(null);
717
+ v(!0), b(null);
568
718
  try {
569
719
  await e.zoneRules.setRules({
570
720
  stage: t,
571
- rules: s.map(Te)
572
- }), p(!1);
721
+ rules: l.map(De)
722
+ }), h(!1);
573
723
  } catch (e) {
574
- y(e instanceof Error ? e.message : String(e));
724
+ b(e instanceof Error ? e.message : String(e));
575
725
  } finally {
576
- _(!1);
726
+ v(!1);
577
727
  }
578
728
  }
579
- }, j = d && !!e?.zoneRules && !g;
580
- return /* @__PURE__ */ R("div", {
729
+ }, M = p && !!e?.zoneRules && !g;
730
+ return /* @__PURE__ */ V("div", {
581
731
  className: "rounded-md border border-border bg-surface/40 p-2.5",
582
732
  children: [
583
- /* @__PURE__ */ R("div", {
733
+ /* @__PURE__ */ V("div", {
584
734
  className: "flex items-center justify-between mb-2 gap-2",
585
- children: [/* @__PURE__ */ L("h3", {
586
- className: `${m} truncate`,
587
- children: r ?? be(t)
588
- }), /* @__PURE__ */ R("div", {
735
+ children: [/* @__PURE__ */ B("h3", {
736
+ className: `${f} truncate`,
737
+ children: r ?? Se(t)
738
+ }), /* @__PURE__ */ V("div", {
589
739
  className: "flex items-center gap-1 flex-shrink-0",
590
- children: [/* @__PURE__ */ R("button", {
740
+ children: [/* @__PURE__ */ V("button", {
591
741
  type: "button",
592
742
  onClick: S,
593
743
  disabled: n.length === 0 || !e,
594
- className: b,
595
- title: "Add rule",
596
- children: [/* @__PURE__ */ L(ce, { className: "h-2.5 w-2.5" }), " Add"]
597
- }), /* @__PURE__ */ R("button", {
744
+ className: C,
745
+ title: a("rules.add"),
746
+ children: [
747
+ /* @__PURE__ */ B(ue, { className: "h-2.5 w-2.5" }),
748
+ " ",
749
+ a("rules.addShort")
750
+ ]
751
+ }), /* @__PURE__ */ V("button", {
598
752
  type: "button",
599
753
  onClick: () => {
600
- A();
754
+ j();
601
755
  },
602
- disabled: !j,
603
- className: E,
604
- children: [/* @__PURE__ */ L(le, { className: "h-2.5 w-2.5" }), g ? "…" : "Save"]
756
+ disabled: !M,
757
+ className: T,
758
+ children: [/* @__PURE__ */ B(de, { className: "h-2.5 w-2.5" }), g ? "…" : a("rules.save")]
605
759
  })]
606
760
  })]
607
761
  }),
608
- s.length === 0 ? /* @__PURE__ */ L("p", {
762
+ l.length === 0 ? /* @__PURE__ */ B("p", {
609
763
  className: "text-[10px] text-foreground-subtle italic px-1 py-1.5 leading-tight",
610
- children: n.length === 0 ? "Define a zone first — rules reference zone ids." : "No rules — every detection at this stage passes through."
611
- }) : /* @__PURE__ */ L("div", {
764
+ children: n.length === 0 ? a("rules.noZones") : a("rules.empty")
765
+ }) : /* @__PURE__ */ B("div", {
612
766
  className: "flex flex-col gap-1.5",
613
- children: s.map((e, t) => /* @__PURE__ */ R("div", {
767
+ children: l.map((e, t) => /* @__PURE__ */ V("div", {
614
768
  className: "rounded border border-border/50 bg-background/40 px-2 py-1.5 flex flex-col gap-1.5 overflow-hidden",
615
769
  children: [
616
- /* @__PURE__ */ R("div", {
770
+ /* @__PURE__ */ V("div", {
617
771
  className: "grid grid-cols-[auto_1fr_auto] items-center gap-1.5",
618
772
  children: [
619
- /* @__PURE__ */ L("button", {
773
+ /* @__PURE__ */ B("button", {
620
774
  type: "button",
621
775
  role: "switch",
622
776
  "aria-checked": e.enabled,
623
- "aria-label": e.enabled ? "Disable rule" : "Enable rule",
624
- title: e.enabled ? "Rule is active — click to disable" : "Rule is disabled — click to enable",
625
- onClick: () => O(t, { enabled: !e.enabled }),
777
+ "aria-label": e.enabled ? a("rules.disable") : a("rules.enable"),
778
+ title: e.enabled ? a("rules.activeTitle") : a("rules.disabledTitle"),
779
+ onClick: () => A(t, { enabled: !e.enabled }),
626
780
  className: ["inline-flex items-center justify-center h-5 w-9 rounded-full border transition-colors flex-shrink-0", e.enabled ? "border-primary/50 bg-primary/20 text-primary" : "border-border bg-surface text-foreground-subtle"].join(" "),
627
- children: /* @__PURE__ */ L("span", { className: ["h-3 w-3 rounded-full transition-transform", e.enabled ? "translate-x-1.5 bg-primary" : "-translate-x-1.5 bg-foreground-subtle"].join(" ") })
781
+ children: /* @__PURE__ */ B("span", { className: ["h-3 w-3 rounded-full transition-transform", e.enabled ? "translate-x-1.5 bg-primary" : "-translate-x-1.5 bg-foreground-subtle"].join(" ") })
628
782
  }),
629
- /* @__PURE__ */ L("input", {
630
- className: `${k} min-w-0`,
631
- placeholder: "Rule name (optional)",
783
+ /* @__PURE__ */ B("input", {
784
+ className: `${O} min-w-0`,
785
+ placeholder: a("rules.namePlaceholder"),
632
786
  value: e.name,
633
- onChange: (e) => O(t, { name: e.target.value })
787
+ onChange: (e) => A(t, { name: e.target.value })
634
788
  }),
635
- /* @__PURE__ */ L("button", {
789
+ /* @__PURE__ */ B("button", {
636
790
  type: "button",
637
- title: "Remove rule",
638
- "aria-label": "Remove rule",
639
- onClick: () => T(t),
791
+ title: a("rules.remove"),
792
+ "aria-label": a("rules.remove"),
793
+ onClick: () => D(t),
640
794
  className: "flex items-center justify-center h-6 w-6 rounded text-foreground-subtle hover:text-danger hover:bg-danger/10 transition-colors flex-shrink-0",
641
- children: /* @__PURE__ */ L(J, { className: "h-3.5 w-3.5" })
795
+ children: /* @__PURE__ */ B(J, { className: "h-3.5 w-3.5" })
642
796
  })
643
797
  ]
644
798
  }),
645
- /* @__PURE__ */ R("div", {
799
+ /* @__PURE__ */ V("div", {
646
800
  className: "flex items-center gap-1.5 flex-wrap",
647
801
  children: [
648
- /* @__PURE__ */ L("span", {
802
+ /* @__PURE__ */ B("span", {
649
803
  className: "text-[9px] uppercase tracking-wider text-foreground-subtle w-12 sm:w-14 flex-shrink-0",
650
- children: "Mode"
804
+ children: a("rules.mode")
651
805
  }),
652
- /* @__PURE__ */ L("button", {
806
+ /* @__PURE__ */ B("button", {
653
807
  type: "button",
654
- onClick: () => O(t, { mode: "include" }),
655
- className: `${P} ${e.mode === "include" ? f : h}`,
656
- children: "Include"
808
+ onClick: () => A(t, { mode: "include" }),
809
+ className: `${_} ${e.mode === "include" ? L : m}`,
810
+ children: a("rules.include")
657
811
  }),
658
- /* @__PURE__ */ L("button", {
812
+ /* @__PURE__ */ B("button", {
659
813
  type: "button",
660
- onClick: () => O(t, { mode: "exclude" }),
661
- className: `${P} ${e.mode === "exclude" ? f : h}`,
662
- children: "Exclude"
814
+ onClick: () => A(t, { mode: "exclude" }),
815
+ className: `${_} ${e.mode === "exclude" ? L : m}`,
816
+ children: a("rules.exclude")
663
817
  })
664
818
  ]
665
819
  }),
666
- /* @__PURE__ */ R("div", {
820
+ /* @__PURE__ */ V("div", {
667
821
  className: "flex items-center gap-1.5 flex-wrap",
668
- children: [/* @__PURE__ */ L("span", {
822
+ children: [/* @__PURE__ */ B("span", {
669
823
  className: "text-[9px] uppercase tracking-wider text-foreground-subtle w-12 sm:w-14 flex-shrink-0",
670
- children: "Zones"
671
- }), n.length === 0 ? /* @__PURE__ */ L("span", {
824
+ children: a("rules.zones")
825
+ }), n.length === 0 ? /* @__PURE__ */ B("span", {
672
826
  className: "text-[10px] text-foreground-subtle italic",
673
- children: "none"
674
- }) : n.map((n) => /* @__PURE__ */ R("button", {
827
+ children: a("rules.noneZones")
828
+ }) : n.map((n) => /* @__PURE__ */ V("button", {
675
829
  type: "button",
676
- onClick: () => w(t, n.id),
677
- className: `${P} ${e.zoneIds.includes(n.id) ? f : h}`,
678
- children: [/* @__PURE__ */ L("span", {
830
+ onClick: () => E(t, n.id),
831
+ className: `${_} ${e.zoneIds.includes(n.id) ? L : m}`,
832
+ children: [/* @__PURE__ */ B("span", {
679
833
  className: "h-2 w-2 rounded-sm",
680
834
  style: { backgroundColor: n.color || "#3b82f6" }
681
- }), /* @__PURE__ */ L("span", {
835
+ }), /* @__PURE__ */ B("span", {
682
836
  className: "truncate max-w-[7rem]",
683
837
  children: n.name || n.id
684
838
  })]
685
839
  }, n.id))]
686
840
  }),
687
- x && /* @__PURE__ */ R("div", {
841
+ x && /* @__PURE__ */ V("div", {
688
842
  className: "flex items-center gap-1.5 flex-wrap",
689
843
  children: [
690
- /* @__PURE__ */ L("span", {
844
+ /* @__PURE__ */ B("span", {
691
845
  className: "text-[9px] uppercase tracking-wider text-foreground-subtle w-12 sm:w-14 flex-shrink-0",
692
- children: "Class"
846
+ children: a("rules.class")
693
847
  }),
694
- Ce.map((n) => /* @__PURE__ */ L("button", {
848
+ Te.map((n) => /* @__PURE__ */ B("button", {
695
849
  type: "button",
696
- onClick: () => C(t, n.id),
697
- className: `${P} ${e.classFilter.includes(n.id) ? f : h}`,
850
+ onClick: () => w(t, n.id),
851
+ className: `${_} ${e.classFilter.includes(n.id) ? L : m}`,
698
852
  children: n.label
699
853
  }, n.id)),
700
- e.classFilter.length === 0 && /* @__PURE__ */ L("span", {
854
+ e.classFilter.length === 0 && /* @__PURE__ */ B("span", {
701
855
  className: "text-[10px] text-foreground-subtle italic ml-1",
702
- children: "any"
856
+ children: a("rules.anyClass")
703
857
  })
704
858
  ]
705
859
  })
706
860
  ]
707
861
  }, e.id))
708
862
  }),
709
- v && /* @__PURE__ */ L("p", {
863
+ y && /* @__PURE__ */ B("p", {
710
864
  className: "mt-2 text-[10px] text-danger",
711
- children: v
865
+ children: y
712
866
  })
713
867
  ]
714
868
  });
715
869
  }
716
870
  //#endregion
717
871
  //#region src/widgets/zone-editor/types.ts
718
- function Ee(e) {
872
+ function Oe(e) {
719
873
  let t = e.kind === "tripwire" ? {
720
874
  kind: "line",
721
875
  points: e.points
@@ -730,15 +884,15 @@ function Ee(e) {
730
884
  shape: t
731
885
  };
732
886
  }
733
- function De(e) {
887
+ function ke(e) {
734
888
  return e === "line" ? "tripwire" : "polygon";
735
889
  }
736
- function Oe(e) {
890
+ function Ae(e) {
737
891
  return e === "tripwire" ? "line" : "polygon";
738
892
  }
739
893
  //#endregion
740
894
  //#region src/widgets/zone-editor/ZoneOverlay.tsx
741
- var ke = "#3b82f6", Ae = ["polygon", "line"], Q = [
895
+ var je = "#3b82f6", Me = ["polygon", "line"], $ = [
742
896
  "#3b82f6",
743
897
  "#10b981",
744
898
  "#f59e0b",
@@ -750,21 +904,21 @@ var ke = "#3b82f6", Ae = ["polygon", "line"], Q = [
750
904
  "#8b5cf6",
751
905
  "#14b8a6"
752
906
  ];
753
- function je(e) {
907
+ function Ne(e) {
754
908
  let t = new Set(e.map((e) => (e.color ?? "").toLowerCase()));
755
- for (let e of Q) if (!t.has(e.toLowerCase())) return e;
756
- return Q[e.length % Q.length];
909
+ for (let e of $) if (!t.has(e.toLowerCase())) return e;
910
+ return $[e.length % $.length];
757
911
  }
758
- function Me(e) {
912
+ function Pe(e) {
759
913
  return {
760
914
  id: e.id,
761
915
  name: e.name,
762
916
  kind: e.kind,
763
- color: e.color || ke,
917
+ color: e.color || je,
764
918
  points: [...e.polygon]
765
919
  };
766
920
  }
767
- function $(e) {
921
+ function Fe(e) {
768
922
  return {
769
923
  id: e.id,
770
924
  name: e.name,
@@ -773,24 +927,24 @@ function $(e) {
773
927
  color: e.color
774
928
  };
775
929
  }
776
- function Ne({ deviceId: e }) {
777
- let t = M(j().trpcClient, e), n = D(t?.state.zones), r = c(() => n?.zones ?? [], [n]), i = y(), s = i?.drawingKind ?? null, l = i?.selectedZoneId ?? null, u = i?.editingDraft ?? null, d = c(() => r.map((e) => u && u.id === e.id ? {
930
+ function Ie({ deviceId: e }) {
931
+ let t = P(N().trpcClient, e), n = k(t?.state.zones), r = c(() => n?.zones ?? [], [n]), i = D(), s = i?.drawingKind ?? null, l = i?.selectedZoneId ?? null, u = i?.editingDraft ?? null, d = c(() => r.map((e) => u && u.id === e.id ? {
778
932
  id: u.id,
779
933
  name: u.name,
780
934
  kind: u.kind,
781
935
  color: u.color,
782
936
  points: [...u.points]
783
- } : Me(e)), [r, u]), f = c(() => d.map(Ee), [d]), p = o(async (e) => {
937
+ } : Pe(e)), [r, u]), f = c(() => d.map(Oe), [d]), p = o(async (e) => {
784
938
  if (e.kind !== "polygon" && e.kind !== "line") return;
785
939
  let n = {
786
940
  id: crypto.randomUUID(),
787
941
  name: "",
788
- kind: De(e.kind),
789
- color: je(d),
942
+ kind: ke(e.kind),
943
+ color: Ne(d),
790
944
  points: [...e.points]
791
945
  };
792
946
  try {
793
- await t?.zones?.addZone({ zone: $(n) });
947
+ await t?.zones?.addZone({ zone: Fe(n) });
794
948
  } catch (e) {
795
949
  console.error("zones.addZone failed", e);
796
950
  }
@@ -810,7 +964,7 @@ function Ne({ deviceId: e }) {
810
964
  }
811
965
  let a = d.find((t) => t.id === e);
812
966
  if (a) try {
813
- await t?.zones?.updateZone({ zone: $({
967
+ await t?.zones?.updateZone({ zone: Fe({
814
968
  ...a,
815
969
  points: r
816
970
  }) });
@@ -843,33 +997,33 @@ function Ne({ deviceId: e }) {
843
997
  points: t.points
844
998
  });
845
999
  }, [d, i]);
846
- return d.length === 0 && s === null ? null : /* @__PURE__ */ L(a, {
1000
+ return d.length === 0 && s === null ? null : /* @__PURE__ */ B(a, {
847
1001
  fallback: null,
848
- children: /* @__PURE__ */ L(C, {
1002
+ children: /* @__PURE__ */ B(w, {
849
1003
  transparent: !0,
850
1004
  items: f,
851
- supportedShapes: Ae,
1005
+ supportedShapes: Me,
852
1006
  selectedId: l,
853
1007
  onSelect: g,
854
1008
  onShapeChange: h,
855
1009
  onDrawComplete: (e) => {
856
1010
  p(e);
857
1011
  },
858
- drawingKind: s === null ? null : Oe(s)
1012
+ drawingKind: s === null ? null : Ae(s)
859
1013
  })
860
1014
  });
861
1015
  }
862
- var Pe = l(Ne), Fe = "#3b82f6";
863
- function Ie(e) {
1016
+ var Le = l(Ie), Re = "#3b82f6";
1017
+ function ze(e) {
864
1018
  return {
865
1019
  id: e.id,
866
1020
  name: e.name,
867
1021
  kind: e.kind,
868
- color: e.color || Fe,
1022
+ color: e.color || Re,
869
1023
  points: [...e.polygon]
870
1024
  };
871
1025
  }
872
- function Le(e) {
1026
+ function Be(e) {
873
1027
  return {
874
1028
  id: e.id,
875
1029
  name: e.name,
@@ -878,31 +1032,31 @@ function Le(e) {
878
1032
  color: e.color
879
1033
  };
880
1034
  }
881
- function Re({ deviceId: e }) {
882
- let t = M(j().trpcClient, e), n = D(t?.state.zones), r = c(() => n?.zones ?? [], [n]), i = c(() => r.map(Ie), [r]), a = y(), s = a?.drawingKind ?? null, l = a?.selectedZoneId ?? null, u = a?.editingDraft ?? null;
883
- O(c(() => ({
1035
+ function Ve({ deviceId: e }) {
1036
+ let { t } = X(), n = P(N().trpcClient, e), r = k(n?.state.zones), i = c(() => r?.zones ?? [], [r]), a = c(() => i.map(ze), [i]), s = D(), l = s?.drawingKind ?? null, u = s?.selectedZoneId ?? null, d = s?.editingDraft ?? null;
1037
+ A(c(() => ({
884
1038
  id: "zones",
885
1039
  order: 100,
886
- node: /* @__PURE__ */ L(Pe, { deviceId: e })
1040
+ node: /* @__PURE__ */ B(Le, { deviceId: e })
887
1041
  }), [e]));
888
- let d = o((e) => {
1042
+ let p = o((e) => {
889
1043
  if (e === null) {
890
- a?.discardDraft(), a?.setSelectedZoneId(null);
1044
+ s?.discardDraft(), s?.setSelectedZoneId(null);
891
1045
  return;
892
1046
  }
893
- let t = i.find((t) => t.id === e);
894
- t && a?.enterDraft({
1047
+ let t = a.find((t) => t.id === e);
1048
+ t && s?.enterDraft({
895
1049
  id: t.id,
896
1050
  kind: t.kind,
897
1051
  name: t.name,
898
1052
  color: t.color,
899
1053
  points: t.points
900
1054
  });
901
- }, [i, a]), f = o(async () => {
902
- let e = a?.editingDraft;
1055
+ }, [a, s]), m = o(async () => {
1056
+ let e = s?.editingDraft;
903
1057
  if (e) {
904
1058
  try {
905
- await t?.zones?.updateZone({ zone: Le({
1059
+ await n?.zones?.updateZone({ zone: Be({
906
1060
  id: e.id,
907
1061
  kind: e.kind,
908
1062
  name: e.name,
@@ -912,86 +1066,81 @@ function Re({ deviceId: e }) {
912
1066
  } catch (e) {
913
1067
  console.error("zones.updateZone failed", e);
914
1068
  }
915
- a?.discardDraft(), a?.setSelectedZoneId(null);
1069
+ s?.discardDraft(), s?.setSelectedZoneId(null);
916
1070
  }
917
- }, [t, a]), p = o(() => {
918
- a?.discardDraft(), a?.setSelectedZoneId(null);
919
- }, [a]), h = o(async (e) => {
1071
+ }, [n, s]), h = o(() => {
1072
+ s?.discardDraft(), s?.setSelectedZoneId(null);
1073
+ }, [s]), _ = o(async (e) => {
920
1074
  try {
921
- await t?.zones?.removeZone({ zoneId: e });
1075
+ await n?.zones?.removeZone({ zoneId: e });
922
1076
  } catch (e) {
923
1077
  console.error("zones.removeZone failed", e);
924
1078
  }
925
- a?.selectedZoneId === e && a.setSelectedZoneId(null), a?.editingDraft?.id === e && a.discardDraft();
926
- }, [t, a]), _ = o((e) => {
927
- a?.startDrawing(e);
928
- }, [a]), v = o(() => {
929
- a?.setDrawingKind(null);
930
- }, [a]), b = c(() => !u || !i.find((e) => e.id === u.id) ? null : {
931
- id: u.id,
932
- kind: u.kind,
933
- name: u.name,
934
- color: u.color,
935
- points: [...u.points]
936
- }, [i, u]);
937
- return /* @__PURE__ */ R("div", {
938
- className: I,
939
- children: [/* @__PURE__ */ R("div", {
940
- className: g,
941
- children: [/* @__PURE__ */ L("div", {
942
- className: N,
943
- children: /* @__PURE__ */ R("div", {
1079
+ s?.selectedZoneId === e && s.setSelectedZoneId(null), s?.editingDraft?.id === e && s.discardDraft();
1080
+ }, [n, s]), y = o((e) => {
1081
+ s?.startDrawing(e);
1082
+ }, [s]), x = o(() => {
1083
+ s?.setDrawingKind(null);
1084
+ }, [s]), S = c(() => !d || !a.find((e) => e.id === d.id) ? null : {
1085
+ id: d.id,
1086
+ kind: d.kind,
1087
+ name: d.name,
1088
+ color: d.color,
1089
+ points: [...d.points]
1090
+ }, [a, d]);
1091
+ return /* @__PURE__ */ V("div", {
1092
+ className: b,
1093
+ children: [/* @__PURE__ */ V("div", {
1094
+ className: I,
1095
+ children: [/* @__PURE__ */ B("div", {
1096
+ className: z,
1097
+ children: /* @__PURE__ */ V("div", {
944
1098
  className: "min-w-0",
945
- children: [/* @__PURE__ */ L("h2", {
946
- className: m,
947
- children: "Detection Zones"
948
- }), /* @__PURE__ */ R("p", {
949
- className: `${F} mt-0.5 leading-tight`,
950
- children: [
951
- "Pick a shape below or use the ",
952
- /* @__PURE__ */ L("strong", {
953
- className: "text-foreground",
954
- children: "Zones"
955
- }),
956
- " ",
957
- "button on the live frame to draw new polygons or tripwires. Behaviour is set per-stage in the rules editors below — a zone with no rule is observed by analytics but never filters."
958
- ]
1099
+ children: [/* @__PURE__ */ B("h2", {
1100
+ className: f,
1101
+ children: t("zonesTab.title")
1102
+ }), /* @__PURE__ */ B("p", {
1103
+ className: `${g} mt-0.5 leading-tight`,
1104
+ children: /* @__PURE__ */ B(me, {
1105
+ i18nKey: "zonesTab.hint",
1106
+ components: { b: /* @__PURE__ */ B("strong", { className: "text-foreground" }) }
1107
+ })
959
1108
  })]
960
1109
  })
961
- }), /* @__PURE__ */ R("div", {
962
- className: `${S} flex flex-col gap-3`,
963
- children: [/* @__PURE__ */ L(X, {
964
- zones: i,
965
- selectedZoneId: l,
966
- drawingKind: s,
967
- onSelectZone: d,
968
- onDeleteZone: h,
969
- onStartDraw: _,
970
- onCancelDraw: v
971
- }), b && /* @__PURE__ */ R(z, { children: [/* @__PURE__ */ L("div", { className: "border-t border-border" }), /* @__PURE__ */ L(ve, {
972
- zone: b,
973
- onSave: f,
974
- onDiscard: p,
975
- onDelete: () => h(b.id)
1110
+ }), /* @__PURE__ */ V("div", {
1111
+ className: `${v} flex flex-col gap-3`,
1112
+ children: [/* @__PURE__ */ B(ye, {
1113
+ zones: a,
1114
+ selectedZoneId: u,
1115
+ drawingKind: l,
1116
+ onSelectZone: p,
1117
+ onDeleteZone: _,
1118
+ onStartDraw: y,
1119
+ onCancelDraw: x
1120
+ }), S && /* @__PURE__ */ V(H, { children: [/* @__PURE__ */ B("div", { className: "border-t border-border" }), /* @__PURE__ */ B(xe, {
1121
+ zone: S,
1122
+ onSave: m,
1123
+ onDiscard: h,
1124
+ onDelete: () => _(S.id)
976
1125
  })] })]
977
1126
  })]
978
- }), /* @__PURE__ */ R("div", {
979
- className: `grid grid-cols-1 2xl:grid-cols-2 ${T}`,
1127
+ }), /* @__PURE__ */ V("div", {
1128
+ className: `grid grid-cols-1 2xl:grid-cols-2 ${F}`,
980
1129
  children: [
981
- /* @__PURE__ */ L(Z, {
982
- dev: t,
1130
+ /* @__PURE__ */ B(Q, {
1131
+ dev: n,
983
1132
  stage: "motion",
984
- zones: r
1133
+ zones: i
985
1134
  }),
986
- /* @__PURE__ */ L(Z, {
987
- dev: t,
1135
+ /* @__PURE__ */ B(Q, {
1136
+ dev: n,
988
1137
  stage: "detection",
989
- zones: r
1138
+ zones: i
990
1139
  }),
991
- /* @__PURE__ */ L(Z, {
992
- dev: t,
1140
+ /* @__PURE__ */ B(Q, {
1141
+ dev: n,
993
1142
  stage: "package",
994
- zones: r
1143
+ zones: i
995
1144
  })
996
1145
  ]
997
1146
  })]
@@ -999,17 +1148,18 @@ function Re({ deviceId: e }) {
999
1148
  }
1000
1149
  //#endregion
1001
1150
  //#region src/widgets/ZoneEditor.tsx
1002
- function ze(e) {
1003
- return e.deviceId === void 0 ? /* @__PURE__ */ L("div", {
1151
+ function He(e) {
1152
+ let { t } = X();
1153
+ return e.deviceId === void 0 ? /* @__PURE__ */ B("div", {
1004
1154
  className: "rounded-lg border border-warning/30 bg-warning/10 px-3 py-2 text-xs text-warning",
1005
- children: "ZoneEditor requires a deviceId"
1006
- }) : /* @__PURE__ */ L(Re, { deviceId: e.deviceId });
1155
+ children: t("zoneEditor.requiresDevice")
1156
+ }) : /* @__PURE__ */ B(Ve, { deviceId: e.deviceId });
1007
1157
  }
1008
1158
  //#endregion
1009
1159
  //#region src/widgets/index.tsx
1010
- var Be = {
1011
- "pipeline-quick-stats": fe,
1012
- "zone-editor": ze
1160
+ var Ue = {
1161
+ "pipeline-quick-stats": he,
1162
+ "zone-editor": He
1013
1163
  };
1014
1164
  //#endregion
1015
- export { Be as default };
1165
+ export { Ue as default };