@camstack/addon-agent-ui 1.2.319 → 1.2.320

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/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.2.320] - 2026-11-03
11
+
12
+ ### Changed
13
+
14
+ - Rebuilt against updated dependencies.
15
+
10
16
  ## [1.2.319] - 2026-11-03
11
17
 
12
18
  ### Changed
@@ -368,13 +368,13 @@
368
368
  "server/server/packages/addon-agent-ui/src/updates-panel.tsx": "ec6b5b3701322bbb68dbe581703409cd373418cf9227ce829ea0945e70e60d54",
369
369
  "server/server/packages/addon-agent-ui/src/use-pairing-status.ts": "46193a1e83a64250dfff75d417448d74ba0159465e710861b996e903b03e427a",
370
370
  "server/server/packages/sdk/dist/index.js": "89409bfb9632c46ed63ee4882c70b7994c1ff90c67d27a5d4b86b0990c41a131",
371
- "server/server/packages/types/dist/client.mjs": "29aec32cd1776767c51423f18198dab43b87e4608e76b85ffd6c9f837b4fa705",
372
- "server/server/packages/types/dist/composition-CD1GdFuv.mjs": "968b9e385cf4b36059c28614f34427cecd938ace7fb34002926dce0c1bd5cad8",
371
+ "server/server/packages/types/dist/client.mjs": "45e65b47085b8da478fb007edb2d8a1b9dd63bd038fce1824703e9dc545be007",
372
+ "server/server/packages/types/dist/composition-BSGHhcr8.mjs": "1bc1b66a90549d3c75fc4aee59b262f5ca4fcb9b93c8c80ec0d69277f939e01f",
373
373
  "server/server/packages/types/dist/device-type-D1fH5kaC.mjs": "4002ae7c812789715d7a35271c91b8133511b710eaeb07fea061d285db3dc14a",
374
374
  "server/server/packages/types/dist/event-category-BmPm98n0.mjs": "3617fc3941b34f1dcc8ebd3c1360e8c302747400d43e3ab694388496c2bb568d",
375
- "server/server/packages/types/dist/index.mjs": "053943c477da6f25c94271757454c598144e107cc2c885226b2b996515448b44",
375
+ "server/server/packages/types/dist/index.mjs": "628b696efc84aef1919df0614cb68a4ea76837eb010c0311740a2b0f699cbd85",
376
376
  "server/server/packages/types/dist/node-color.mjs": "942e663074f0a4ed2f8643848ec139eba4b5fd16d0d01c8a23e08a4819a8843b",
377
- "server/server/packages/types/dist/sleep-DV4qxEyD.mjs": "39684da29db5b6f370d1e303bcb1e10fd0dc2f7b14f2d2dec006069eed98e3f7",
377
+ "server/server/packages/types/dist/sleep-hK-Dss3z.mjs": "bba7b7e6453539791a94cc8c40a873683e6c157af22317ffe3ed850870661d3c",
378
378
  "server/server/packages/ui-library/src/composites/adaptive-downlink.ts": "6e9698e97721461b72a3750f930dd7214d049be865491bfb5a64b87c032b0e22",
379
379
  "server/server/packages/ui-library/src/composites/camera-stream-player.tsx": "c04809229b4e091fed09e2bcfe643d6b36cb3a39e294229b49f8a095e4e6ebd3",
380
380
  "server/server/packages/ui-library/src/composites/cap-settings/CameraGridLayoutEditor.tsx": "bfe36ccad931ef0981fe24fa315f136d35dbc3164df221346806173447178324",
@@ -487,7 +487,7 @@
487
487
  "server/server/packages/ui-library/src/contexts/vod-playback.tsx": "c07bc3ecc786182ef2f27d13bec3f520288e0b47367edd7ddf1c2e7b4a2bbaee",
488
488
  "server/server/packages/ui-library/src/contexts/widget-registry.tsx": "71a2815c645abe96821a9e9e5668e321187527d569414f5ca197d615856cc17a",
489
489
  "server/server/packages/ui-library/src/contexts/zone-editing.tsx": "275f680304ab09dd747fd9e859dc5aa0535df33eb2d33eb669a6de66bdd848d0",
490
- "server/server/packages/ui-library/src/generated/system-hooks.ts": "50a1c989d50a9116a65e0e8349cb67f4a3e6d08e6478ed86ef47190fef191594",
490
+ "server/server/packages/ui-library/src/generated/system-hooks.ts": "c2bfff736e496d41ff925445be816e59803d5f11b45214f4a01547d2c9d5782e",
491
491
  "server/server/packages/ui-library/src/hooks/turn-server-cache.ts": "fab22f069c3761e0d268449e056d9c499b6ea63b879684a40023735b34bad061",
492
492
  "server/server/packages/ui-library/src/hooks/use-device-capability.ts": "b74ede60be8039c45f6c712a407ee838ab208610435870ddabaa8598cb7cd7f3",
493
493
  "server/server/packages/ui-library/src/hooks/use-device-detections.ts": "61cc54cf2e7f53864e5aa533c05679e4c204c4004a0296f63ab169b003888611",
package/dist/addon.js CHANGED
@@ -5849,7 +5849,7 @@ var ZodIssueCode = {
5849
5849
  var ZodFirstPartyTypeKind;
5850
5850
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5851
5851
  //#endregion
5852
- //#region ../types/dist/sleep-DV4qxEyD.mjs
5852
+ //#region ../types/dist/sleep-hK-Dss3z.mjs
5853
5853
  /**
5854
5854
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5855
5855
  * window to float samples (D455).
@@ -11309,7 +11309,7 @@ object({
11309
11309
  params: record(string(), union([string(), number()])).optional()
11310
11310
  });
11311
11311
  //#endregion
11312
- //#region ../types/dist/composition-CD1GdFuv.mjs
11312
+ //#region ../types/dist/composition-BSGHhcr8.mjs
11313
11313
  /**
11314
11314
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11315
11315
  *
@@ -12072,38 +12072,6 @@ function validateExpressionSource(src) {
12072
12072
  return null;
12073
12073
  }
12074
12074
  /**
12075
- * One power reading becomes a washer, dryer or dishwasher (D944 helper).
12076
- *
12077
- * Standby of the control board is under 5 W; a motor or a heater is far
12078
- * above that, so 5 W is the line. A door lock or a short pump spike is not
12079
- * a cycle, so running starts only after 30 s above the line.
12080
- *
12081
- * A wash, dry or dishwasher pause (soak, heat-wait, heater off between
12082
- * bursts) lasts several minutes. Ending the cycle on the first dip would
12083
- * report "finished" in the middle of the program, so the stop dwell is
12084
- * 10 minutes on every type. The number is stored per type so one machine
12085
- * can move later without moving the others.
12086
- */
12087
- var APPLIANCE_TEMPLATE_ID = "appliance";
12088
- var APPLIANCE_KINDS = [
12089
- "washer",
12090
- "dryer",
12091
- "dishwasher"
12092
- ];
12093
- DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
12094
- /**
12095
- * A dumb plug-in stove or heater, built from a smart plug (a `Heater`).
12096
- *
12097
- * The plug is the actuator: `switch.setState` is a FORWARD to the plug's own
12098
- * (D967), so the heater switches exactly as the plug does. Running is the
12099
- * plug being on; when a power reading is also named, running is the draw
12100
- * staying above the line instead (a plug left on with the stove's own
12101
- * thermostat open is not heating). 50 W is far above any standby and below
12102
- * the smallest radiator.
12103
- */
12104
- var HEATER_TEMPLATE_ID = "heater";
12105
- DeviceType.Heater;
12106
- /**
12107
12075
  * Statistics and integrals over a history read (D944).
12108
12076
  *
12109
12077
  * Pure: the same points always give the same answer, so a restart recomputes
@@ -12247,34 +12215,6 @@ var HelperSpecSchema = discriminatedUnion("kind", [
12247
12215
  /** Actuator groups only. Readings leave this unset. */
12248
12216
  active: ActiveCriterionSchema.optional()
12249
12217
  }),
12250
- object({
12251
- kind: literal("appliance"),
12252
- templateId: literal(APPLIANCE_TEMPLATE_ID),
12253
- appliance: _enum(APPLIANCE_KINDS),
12254
- thresholdWatts: number().positive(),
12255
- startDwellMs: number().int().nonnegative(),
12256
- stopDwellMs: number().int().nonnegative(),
12257
- member: HelperMemberSchema,
12258
- /**
12259
- * The plug that powers the appliance (`switch.on`). Optional: when set, the
12260
- * composition carries a `power-source` feature, so the hub can cut and restore
12261
- * that plug (power-source link of a NEW-target composition).
12262
- */
12263
- powerSwitch: HelperMemberSchema.optional(),
12264
- /** How long a forced restart keeps the plug off; the cap's default when unset. */
12265
- offSeconds: number().optional()
12266
- }),
12267
- object({
12268
- kind: literal("heater"),
12269
- templateId: literal(HEATER_TEMPLATE_ID),
12270
- /** The plug, read and commanded at `switch.on`. */
12271
- plug: HelperMemberSchema,
12272
- /** The plug's power reading, when it has one: running is then the draw, not the switch. */
12273
- meter: HelperMemberSchema.optional(),
12274
- thresholdWatts: number().positive(),
12275
- startDwellMs: number().int().nonnegative(),
12276
- stopDwellMs: number().int().nonnegative()
12277
- }),
12278
12218
  object({
12279
12219
  kind: literal("statistic"),
12280
12220
  stat: _enum(STATISTIC_STATS),
@@ -48081,49 +48021,6 @@ function actuator(input) {
48081
48021
  };
48082
48022
  }
48083
48023
  new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
48084
- var PowerSourceRefusalReasonSchema = _enum([
48085
- "no-power-source",
48086
- "switch-missing",
48087
- "switch-offline",
48088
- "switch-unknown",
48089
- "restart-in-progress",
48090
- "switch-call-failed"
48091
- ]);
48092
- var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
48093
- accepted: literal(false),
48094
- reason: PowerSourceRefusalReasonSchema,
48095
- /** One line for the operator, naming the switch. */
48096
- message: string()
48097
- })]);
48098
- var PowerSourceInfoSchema = object({
48099
- /** False when the device has no power source linked; the rest is then empty. */
48100
- linked: boolean(),
48101
- switchDeviceId: number().nullable(),
48102
- switchName: string().nullable(),
48103
- /** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
48104
- switchOn: boolean().nullable(),
48105
- switchOnline: boolean().nullable(),
48106
- restartInProgress: boolean(),
48107
- offSeconds: number().int()
48108
- });
48109
- object({
48110
- /** Null while the switch has not reported: unknown is never `false` (D393). */
48111
- switchOn: boolean().nullable(),
48112
- offSeconds: number().int(),
48113
- lastFetchedAt: number()
48114
- });
48115
- DeviceType.Gateway, DeviceType.Hub, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater;
48116
- var deviceInput$2 = object({ deviceId: number().int().nonnegative() });
48117
- systemMethod(deviceInput$2, PowerSourceInfoSchema, { auth: "admin" }), systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
48118
- kind: "mutation",
48119
- auth: "admin"
48120
- }), systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
48121
- kind: "mutation",
48122
- auth: "admin"
48123
- }), systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
48124
- kind: "mutation",
48125
- auth: "admin"
48126
- });
48127
48024
  DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
48128
48025
  DeviceType.Camera;
48129
48026
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
@@ -48151,6 +48048,165 @@ var ComposableCapabilitiesSchema = object({ caps: array(object({
48151
48048
  }).optional()
48152
48049
  })) });
48153
48050
  /**
48051
+ * A saved Customize assignment (D936). The schema is the type: callers use
48052
+ * `z.infer`, they do not restate the shape.
48053
+ */
48054
+ var OverrideSourceSchema = object({
48055
+ addonId: string().min(1),
48056
+ stableId: string().min(1),
48057
+ cap: string().min(1),
48058
+ fieldPath: string().min(1)
48059
+ });
48060
+ var IdentityTransformDraftSchema = object({ kind: literal("identity") });
48061
+ var InvertTransformDraftSchema = object({ kind: literal("invert") });
48062
+ var ThresholdTransformDraftSchema = object({
48063
+ kind: literal("threshold"),
48064
+ comparison: _enum(["gte", "lte"]),
48065
+ threshold: number(),
48066
+ startDwellMs: number().nonnegative(),
48067
+ stopDwellMs: number().nonnegative()
48068
+ });
48069
+ var CustomizeTransformDraftSchema = discriminatedUnion("kind", [
48070
+ IdentityTransformDraftSchema,
48071
+ InvertTransformDraftSchema,
48072
+ ThresholdTransformDraftSchema,
48073
+ object({
48074
+ kind: literal("linear"),
48075
+ scale: number(),
48076
+ offset: number(),
48077
+ clampMin: number().optional(),
48078
+ clampMax: number().optional()
48079
+ }),
48080
+ object({
48081
+ kind: literal("enum-map"),
48082
+ mapping: record(string(), string().nullable())
48083
+ }),
48084
+ object({ kind: literal("number") })
48085
+ ]);
48086
+ var ScalarOverrideDraftSchema = object({
48087
+ kind: literal("scalar"),
48088
+ cap: string().min(1),
48089
+ targetId: string().min(1),
48090
+ source: OverrideSourceSchema,
48091
+ transform: CustomizeTransformDraftSchema
48092
+ });
48093
+ var KeyedArrayOverrideDraftSchema = object({
48094
+ kind: literal("keyedArray"),
48095
+ cap: string().min(1),
48096
+ targetId: string().min(1),
48097
+ key: string().min(1),
48098
+ label: string().min(1),
48099
+ fieldPath: string().min(1),
48100
+ source: OverrideSourceSchema,
48101
+ transform: CustomizeTransformDraftSchema
48102
+ });
48103
+ var PulseOverrideDraftSchema = object({
48104
+ kind: literal("pulse"),
48105
+ cap: string().min(1),
48106
+ targetId: string().min(1),
48107
+ enabled: boolean(),
48108
+ debounceMs: number().nonnegative(),
48109
+ edges: array(_enum([
48110
+ "rising",
48111
+ "button",
48112
+ "native-event"
48113
+ ])).min(1).optional(),
48114
+ sources: array(OverrideSourceSchema).min(1)
48115
+ });
48116
+ var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([
48117
+ "in-addition",
48118
+ "confirm",
48119
+ "replace"
48120
+ ]).optional() });
48121
+ discriminatedUnion("kind", [
48122
+ ScalarOverrideDraftSchema,
48123
+ KeyedArrayOverrideDraftSchema,
48124
+ PulseOverrideDraftSchema,
48125
+ object({
48126
+ kind: literal("span"),
48127
+ cap: string().min(1),
48128
+ targetId: string().min(1),
48129
+ timeoutMs: number().nonnegative(),
48130
+ cooldownMs: number().nonnegative().optional(),
48131
+ sources: array(SpanSourceDraftSchema)
48132
+ })
48133
+ ]);
48134
+ object({
48135
+ targetId: string().min(1),
48136
+ configured: boolean(),
48137
+ source: _enum(["native", "assigned"]),
48138
+ state: _enum([
48139
+ "active",
48140
+ "stale",
48141
+ "released",
48142
+ "unavailable"
48143
+ ]),
48144
+ reason: string().nullable()
48145
+ });
48146
+ /**
48147
+ * What a running-threshold proposal says (planning#405). The schema is the
48148
+ * type: the algorithm, the capability method and the UI all read `z.infer`.
48149
+ *
48150
+ * A proposal is read-only advice: it carries the numbers it was derived from
48151
+ * (`evidence`) so the operator can judge it, and an `insufficient` answer names
48152
+ * WHY with a typed reason and the catalog defaults, never an invented number.
48153
+ */
48154
+ var RunningProposalTierSchema = _enum(["raw", "5m"]);
48155
+ var RunningProposalConfidenceSchema = _enum([
48156
+ "high",
48157
+ "medium",
48158
+ "low"
48159
+ ]);
48160
+ var RunningProposalInsufficientReasonSchema = _enum([
48161
+ "no-history",
48162
+ "span-too-short",
48163
+ "truncated",
48164
+ "no-idle-floor",
48165
+ "no-runs",
48166
+ "no-separation"
48167
+ ]);
48168
+ var RunningProposalReplaySchema = object({
48169
+ /** Intervals the real dwell expression held true over the window. */
48170
+ intervals: number().int().nonnegative(),
48171
+ /** Intervals outside every run, or a second interval inside one. */
48172
+ extra: number().int().nonnegative(),
48173
+ /** Runs without an interval of their own (none, or merged into a neighbour). */
48174
+ missed: number().int().nonnegative()
48175
+ });
48176
+ var RunningProposalEvidenceSchema = object({
48177
+ tier: RunningProposalTierSchema,
48178
+ spanMs: number().nonnegative(),
48179
+ from: number(),
48180
+ to: number(),
48181
+ points: number().int().nonnegative(),
48182
+ runsFound: number().int().nonnegative(),
48183
+ pausesFound: number().int().nonnegative(),
48184
+ longestPauseMs: number().nonnegative(),
48185
+ idleFloorW: number(),
48186
+ noiseCeilingW: number(),
48187
+ plateauW: number(),
48188
+ replay: RunningProposalReplaySchema
48189
+ });
48190
+ var RunningProposalInsufficientEvidenceSchema = object({
48191
+ spanMs: number().nonnegative(),
48192
+ points: number().int().nonnegative(),
48193
+ runsFound: number().int().nonnegative()
48194
+ });
48195
+ var RunningThresholdProposalSchema = discriminatedUnion("state", [object({
48196
+ state: literal("proposal"),
48197
+ transform: ThresholdTransformDraftSchema,
48198
+ evidence: RunningProposalEvidenceSchema,
48199
+ confidence: RunningProposalConfidenceSchema,
48200
+ /** Why the confidence is not higher; empty when nothing held it back. */
48201
+ notes: array(string())
48202
+ }), object({
48203
+ state: literal("insufficient"),
48204
+ reason: RunningProposalInsufficientReasonSchema,
48205
+ evidence: RunningProposalInsufficientEvidenceSchema,
48206
+ /** The catalog defaults, offered as what stands when nothing better is known. */
48207
+ fallback: ThresholdTransformDraftSchema
48208
+ })]);
48209
+ /**
48154
48210
  * The report of a one-shot "move this owner's plaintext secrets into the
48155
48211
  * keychain" run (D878). COUNTS only - never a value, never a ref.
48156
48212
  *
@@ -48440,6 +48496,14 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
48440
48496
  kind: "mutation",
48441
48497
  auth: "admin",
48442
48498
  access: "view"
48499
+ }), method(object({
48500
+ deviceId: number().int(),
48501
+ cap: string().min(1),
48502
+ fieldPath: string().min(1),
48503
+ deviceType: _enum(DeviceType).optional()
48504
+ }), RunningThresholdProposalSchema, {
48505
+ auth: "admin",
48506
+ access: "view"
48443
48507
  }), method(object({
48444
48508
  blockId: string().min(1),
48445
48509
  trace: AutomationRunTraceSchema.optional(),
@@ -52540,15 +52604,15 @@ var FanPresetCaptureResultSchema = object({
52540
52604
  items: array(FanCaptureItemSchema),
52541
52605
  omitted: array(FanCaptureOmissionSchema)
52542
52606
  });
52543
- var deviceInput$1 = object({ deviceId: number().int().nonnegative() });
52607
+ var deviceInput$2 = object({ deviceId: number().int().nonnegative() });
52544
52608
  DeviceType.Fan;
52545
- method(deviceInput$1, array(FanPresetSchema), { auth: "protected" }), method(deviceInput$1.extend({ presets: array(FanPresetSchema).max(12) }), FanPresetsSetResultSchema, {
52609
+ method(deviceInput$2, array(FanPresetSchema), { auth: "protected" }), method(deviceInput$2.extend({ presets: array(FanPresetSchema).max(12) }), FanPresetsSetResultSchema, {
52546
52610
  kind: "mutation",
52547
52611
  auth: "admin"
52548
- }), method(deviceInput$1.extend({ presetId: string().min(1) }), FanPresetApplyResultSchema, {
52612
+ }), method(deviceInput$2.extend({ presetId: string().min(1) }), FanPresetApplyResultSchema, {
52549
52613
  kind: "mutation",
52550
52614
  auth: "protected"
52551
- }), method(deviceInput$1.extend({
52615
+ }), method(deviceInput$2.extend({
52552
52616
  fields: array(FanCaptureFieldSchema).optional(),
52553
52617
  steps: array(ActionStepSchema).max(20).default([])
52554
52618
  }), FanPresetCaptureResultSchema, { auth: "protected" });
@@ -66584,6 +66648,49 @@ object({
66584
66648
  DeviceType.PetFeeder,
66585
66649
  DeviceType.Heater
66586
66650
  ].filter((type) => type !== DeviceType.Sensor);
66651
+ var PowerSourceRefusalReasonSchema = _enum([
66652
+ "no-power-source",
66653
+ "switch-missing",
66654
+ "switch-offline",
66655
+ "switch-unknown",
66656
+ "restart-in-progress",
66657
+ "switch-call-failed"
66658
+ ]);
66659
+ var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
66660
+ accepted: literal(false),
66661
+ reason: PowerSourceRefusalReasonSchema,
66662
+ /** One line for the operator, naming the switch. */
66663
+ message: string()
66664
+ })]);
66665
+ var PowerSourceInfoSchema = object({
66666
+ /** False when the device has no power source linked; the rest is then empty. */
66667
+ linked: boolean(),
66668
+ switchDeviceId: number().nullable(),
66669
+ switchName: string().nullable(),
66670
+ /** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
66671
+ switchOn: boolean().nullable(),
66672
+ switchOnline: boolean().nullable(),
66673
+ restartInProgress: boolean(),
66674
+ offSeconds: number().int()
66675
+ });
66676
+ object({
66677
+ /** Null while the switch has not reported: unknown is never `false` (D393). */
66678
+ switchOn: boolean().nullable(),
66679
+ offSeconds: number().int(),
66680
+ lastFetchedAt: number()
66681
+ });
66682
+ DeviceType.Gateway, DeviceType.Hub, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater;
66683
+ var deviceInput$1 = object({ deviceId: number().int().nonnegative() });
66684
+ systemMethod(deviceInput$1, PowerSourceInfoSchema, { auth: "admin" }), systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
66685
+ kind: "mutation",
66686
+ auth: "admin"
66687
+ }), systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
66688
+ kind: "mutation",
66689
+ auth: "admin"
66690
+ }), systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
66691
+ kind: "mutation",
66692
+ auth: "admin"
66693
+ });
66587
66694
  /**
66588
66695
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
66589
66696
  * on `DeviceType.Presence`. Read-only — no setters: presence is
@@ -74764,6 +74871,34 @@ discriminatedUnion("kind", [object({
74764
74871
  kind: literal("pronto"),
74765
74872
  code: string()
74766
74873
  })]);
74874
+ /**
74875
+ * Propose a running threshold from a device's own power history (planning#405).
74876
+ *
74877
+ * Pure and deterministic: the same series gives the same answer. The series is
74878
+ * a HELD-VALUE step series — a point's value stands until the next point — which
74879
+ * is how device-history stores a gauge, so "time at a level" is exact, not a
74880
+ * count of samples.
74881
+ *
74882
+ * 1. idle floor F = time-weighted median; noise ceiling N = highest value seen
74883
+ * near F. A device that is not near one level most of the time has no floor.
74884
+ * 2. coarse activity = above max(2N, N + 5 W); consecutive stretches are episodes.
74885
+ * 3. gaps between episodes split in two by the largest log-scale jump (>= 3x):
74886
+ * the short ones are pauses INSIDE a run (a soak), the long ones separate runs.
74887
+ * 4. T = geometric mean of N and the plateau's 10th percentile, snapped to a
74888
+ * grid a person would type; stop dwell covers the longest pause; start dwell
74889
+ * covers the longest standby blip above T.
74890
+ * 5. the numbers are proved by REPLAY through the real `dwell` evaluation: each
74891
+ * run must hold exactly one true interval and nothing may flap outside.
74892
+ */
74893
+ var SECOND_MS = 1e3;
74894
+ var MINUTE_MS$1 = 60 * SECOND_MS;
74895
+ 24 * (60 * MINUTE_MS$1);
74896
+ 10 * MINUTE_MS$1;
74897
+ 30 * SECOND_MS;
74898
+ 60 * SECOND_MS;
74899
+ 20 * MINUTE_MS$1;
74900
+ 10 * SECOND_MS;
74901
+ 10 * SECOND_MS;
74767
74902
  DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
74768
74903
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
74769
74904
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
@@ -76305,6 +76440,13 @@ Object.freeze({
76305
76440
  access: "create",
76306
76441
  role: "admin"
76307
76442
  },
76443
+ "coreBlocks.proposeRunningThreshold": {
76444
+ capName: "core-blocks",
76445
+ capScope: "system",
76446
+ addonId: null,
76447
+ access: "view",
76448
+ role: "admin"
76449
+ },
76308
76450
  "coreBlocks.reportAutomationRun": {
76309
76451
  capName: "core-blocks",
76310
76452
  capScope: "system",
@@ -85763,6 +85905,11 @@ Object.freeze({
85763
85905
  form: "single",
85764
85906
  optional: false
85765
85907
  }],
85908
+ "coreBlocks.proposeRunningThreshold": [{
85909
+ name: "deviceId",
85910
+ form: "single",
85911
+ optional: false
85912
+ }],
85766
85913
  "cover.close": [{
85767
85914
  name: "deviceId",
85768
85915
  form: "single",
@@ -88426,99 +88573,6 @@ Object.freeze({
88426
88573
  "network-access": "ingress",
88427
88574
  "smtp-provider": "email"
88428
88575
  });
88429
- /**
88430
- * A saved Customize assignment (D936). The schema is the type: callers use
88431
- * `z.infer`, they do not restate the shape.
88432
- */
88433
- var OverrideSourceSchema = object({
88434
- addonId: string().min(1),
88435
- stableId: string().min(1),
88436
- cap: string().min(1),
88437
- fieldPath: string().min(1)
88438
- });
88439
- var CustomizeTransformDraftSchema = discriminatedUnion("kind", [
88440
- object({ kind: literal("identity") }),
88441
- object({ kind: literal("invert") }),
88442
- object({
88443
- kind: literal("threshold"),
88444
- comparison: _enum(["gte", "lte"]),
88445
- threshold: number(),
88446
- startDwellMs: number().nonnegative(),
88447
- stopDwellMs: number().nonnegative()
88448
- }),
88449
- object({
88450
- kind: literal("linear"),
88451
- scale: number(),
88452
- offset: number(),
88453
- clampMin: number().optional(),
88454
- clampMax: number().optional()
88455
- }),
88456
- object({
88457
- kind: literal("enum-map"),
88458
- mapping: record(string(), string().nullable())
88459
- }),
88460
- object({ kind: literal("number") })
88461
- ]);
88462
- var ScalarOverrideDraftSchema = object({
88463
- kind: literal("scalar"),
88464
- cap: string().min(1),
88465
- targetId: string().min(1),
88466
- source: OverrideSourceSchema,
88467
- transform: CustomizeTransformDraftSchema
88468
- });
88469
- var KeyedArrayOverrideDraftSchema = object({
88470
- kind: literal("keyedArray"),
88471
- cap: string().min(1),
88472
- targetId: string().min(1),
88473
- key: string().min(1),
88474
- label: string().min(1),
88475
- fieldPath: string().min(1),
88476
- source: OverrideSourceSchema,
88477
- transform: CustomizeTransformDraftSchema
88478
- });
88479
- var PulseOverrideDraftSchema = object({
88480
- kind: literal("pulse"),
88481
- cap: string().min(1),
88482
- targetId: string().min(1),
88483
- enabled: boolean(),
88484
- debounceMs: number().nonnegative(),
88485
- edges: array(_enum([
88486
- "rising",
88487
- "button",
88488
- "native-event"
88489
- ])).min(1).optional(),
88490
- sources: array(OverrideSourceSchema).min(1)
88491
- });
88492
- var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([
88493
- "in-addition",
88494
- "confirm",
88495
- "replace"
88496
- ]).optional() });
88497
- discriminatedUnion("kind", [
88498
- ScalarOverrideDraftSchema,
88499
- KeyedArrayOverrideDraftSchema,
88500
- PulseOverrideDraftSchema,
88501
- object({
88502
- kind: literal("span"),
88503
- cap: string().min(1),
88504
- targetId: string().min(1),
88505
- timeoutMs: number().nonnegative(),
88506
- cooldownMs: number().nonnegative().optional(),
88507
- sources: array(SpanSourceDraftSchema)
88508
- })
88509
- ]);
88510
- object({
88511
- targetId: string().min(1),
88512
- configured: boolean(),
88513
- source: _enum(["native", "assigned"]),
88514
- state: _enum([
88515
- "active",
88516
- "stale",
88517
- "released",
88518
- "unavailable"
88519
- ]),
88520
- reason: string().nullable()
88521
- });
88522
88576
  var G711_SCALE_CORRECTION_DB = {
88523
88577
  PCMU: 20 * Math.log10(4),
88524
88578
  PCMA: 20 * Math.log10(8)
@@ -89870,7 +89924,7 @@ var AgentUIAddon = class extends BaseAddon {
89870
89924
  capability: adminUiCapability,
89871
89925
  provider: {
89872
89926
  getStaticDir: async () => ({ staticDir: path.resolve(__dirname) }),
89873
- getVersion: async () => ({ version: "1.2.319" })
89927
+ getVersion: async () => ({ version: "1.2.320" })
89874
89928
  }
89875
89929
  }];
89876
89930
  }
@@ -1 +1 @@
1
- import{a as e,i as t,l as n,n as r,o as i}from"./scheduler-0XZJE03d.js";import{c as a,d as o,g as s,l as c,p as l,t as u,w as d}from"./index-D38ZZu5a.js";import{MaskShapeCanvas as f}from"./MaskShapeCanvas-BTvl6Rin.js";var p=d(`grid-2x2`,[[`path`,{d:`M12 3v18`,key:`108xh3`}],[`path`,{d:`M3 12h18`,key:`1i2n21`}],[`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`,key:`h1oib`}]]),m=n(i(),1),h=n(e(),1),g=110,_=`motion-zones`,v=0,y=[1,2,3],b=1,x=`rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-medium text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,S=`rounded-md border border-primary/50 bg-primary/15 px-2.5 py-1 text-[11px] font-medium text-primary hover:bg-primary/25 disabled:opacity-40 transition-colors`;function C(e,t,n){let r=t*n,i=Array.from({length:r});for(let t=0;t<r;t+=1)i[t]=e[t]===!0;return i}function w(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1)if(e[n]!==t[n])return!1;return!0}function T(e,t){return Math.ceil(e/t)}function E(e,t,n){return Math.min(n-1,Math.floor((e+.5)/t*n))}function D(e,t,n,r){let i=T(t,r),a=T(n,r),o=Array.from({length:i*a},()=>!1);for(let r=0;r<n;r+=1){let s=E(r,n,a);for(let n=0;n<t;n+=1)e[r*t+n]===!0&&(o[s*i+E(n,t,i)]=!0)}return o}function O(e,t,n,r){let i=T(t,r),a=T(n,r),o=Array.from({length:t*n},()=>!1);for(let r=0;r<n;r+=1){let s=E(r,n,a);for(let n=0;n<t;n+=1)o[r*t+n]=e[s*i+E(n,t,i)]===!0}return o}function k({deviceId:e}){let n=t(),i=o(s().trpcClient,e),[d,E]=(0,m.useState)(null),[k,A]=(0,m.useState)(!1),[j,M]=(0,m.useState)(null),[N,P]=(0,m.useState)(null),[F,I]=(0,m.useState)(b),[L,R]=(0,m.useState)(!1),[z,B]=(0,m.useState)(!1),V=(0,m.useRef)(!1);(0,m.useEffect)(()=>{if(!i)return;let e=!1;return V.current=!1,E(null),A(!1),M(null),P(null),I(b),B(!1),(async()=>{try{let t=await i.motionZones?.getOptions({});if(e)return;if(!t)throw Error(`device proxy not ready`);if(E(t),V.current)return;let n=await i.motionZones?.getStatus({});if(e)return;if(!n)throw Error(`device proxy not ready`);V.current=!0;let r=n.regions.find(e=>e.shape.kind===`grid`),a=C(r?r.shape.cells:[],t.grid.width,t.grid.height);P(a),M(D(a,t.grid.width,t.grid.height,b))}catch(t){if(e)return;c(t)?A(!0):console.error(`Motion Zones load failed`,t)}})(),()=>{e=!0}},[i]);let H=d?d.grid.width:0,U=d?d.grid.height:0,W=T(H,F),G=T(U,F),K=(0,m.useMemo)(()=>j&&d?O(j,H,U,F):null,[j,d,H,U,F]),q=(0,m.useMemo)(()=>K!==null&&N!==null&&!w(K,N),[K,N]),J=(0,m.useMemo)(()=>K?K.reduce((e,t)=>t?e+1:e,0):0,[K]),Y=H*U,X=W*G,Z=(0,m.useCallback)(e=>{X>0&&M(Array.from({length:X},()=>e))},[X]),ee=(0,m.useCallback)(()=>{M(e=>e&&e.map(e=>!e))},[]),te=(0,m.useCallback)(()=>{N&&d&&M(D(N,H,U,F))},[N,d,H,U,F]),ne=(0,m.useCallback)(e=>{e===F||!d||M(t=>{if(!t)return I(e),t;let n=D(O(t,H,U,F),H,U,e);return I(e),n})},[F,d,H,U]),Q=(0,m.useMemo)(()=>d&&j?[{id:v,shape:{kind:`grid`,gridWidth:W,gridHeight:G,cells:[...j]}}]:[],[d,j,W,G]),$=(0,m.useCallback)((e,t)=>{t.kind===`grid`&&M(t.cells)},[]),re=(0,m.useCallback)(async()=>{if(!(!i||!j||!d)){R(!0);try{let e=O(j,d.grid.width,d.grid.height,F),t={kind:`grid`,gridWidth:d.grid.width,gridHeight:d.grid.height,cells:e};await i.motionZones?.setZone({patch:{regions:[{id:v,enabled:!0,shape:t}]}});let n=await i.motionZones?.getStatus({});if(n){let e=n.regions.find(e=>e.shape.kind===`grid`),t=C(e?e.shape.cells:[],d.grid.width,d.grid.height);P(t),M(D(t,d.grid.width,d.grid.height,F))}}catch(e){console.error(`Motion Zones save failed`,e)}finally{R(!1)}}},[i,j,d,F]);u((0,m.useMemo)(()=>z&&!k&&d&&j?{id:_,order:g,node:(0,h.jsx)(f,{transparent:!0,items:Q,supportedShapes:[`grid`],grid:{width:W,height:G},selectedId:v,onSelect:()=>{},onShapeChange:$,onDrawComplete:()=>{},drawingKind:null})}:null,[z,k,d,j,Q,$,W,G]));let ie=!k&&d!==null&&j!==null;return i?(0,h.jsx)(l,{title:n(`uiLibrary.motionZones.title`,`Motion Zones`),icon:(0,h.jsx)(p,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,h.jsx)(`div`,{className:`flex flex-col gap-3`,children:k?(0,h.jsx)(`p`,{className:`${a} leading-relaxed`,children:n(`uiLibrary.motionZones.unsupported`,`This camera doesn't expose an on-board motion zones grid.`)}):ie?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`p`,{className:`${a} leading-relaxed`,children:(0,h.jsx)(r,{uiKey:`uiLibrary.motionZones.intro`,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).`,components:[(0,h.jsx)(`strong`,{className:`text-foreground`},`e`),(0,h.jsx)(`strong`,{className:`text-foreground`},`s`),(0,h.jsx)(`strong`,{className:`text-foreground`},`c`)],values:{width:d.grid.width,height:d.grid.height}})}),(0,h.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,h.jsx)(`button`,{type:`button`,onClick:()=>B(e=>!e),disabled:L,"aria-pressed":z,className:z?S:x,children:z?n(`uiLibrary.motionZones.doneEditing`,`Done editing`):n(`uiLibrary.motionZones.editGrid`,`Edit grid`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>Z(!0),disabled:L,className:x,children:n(`uiLibrary.motionZones.allOn`,`All on`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>Z(!1),disabled:L,className:x,children:n(`uiLibrary.motionZones.allOff`,`All off`)}),(0,h.jsx)(`button`,{type:`button`,onClick:ee,disabled:L,className:x,children:n(`uiLibrary.motionZones.invert`,`Invert`)}),(0,h.jsxs)(`div`,{className:`flex items-center gap-1 ml-1`,role:`group`,"aria-label":n(`uiLibrary.motionZones.cellSize`,`Cell size`),children:[(0,h.jsx)(`span`,{className:`${a} mr-0.5`,children:n(`uiLibrary.motionZones.cellSize`,`Cell size`)}),y.map(e=>(0,h.jsxs)(`button`,{type:`button`,onClick:()=>ne(e),disabled:L,"aria-pressed":F===e,title:e===1?n(`uiLibrary.motionZones.cameraGrid`,`Camera grid {{width}}×{{height}} (finest)`,{width:H,height:U}):n(`uiLibrary.motionZones.paintingGrid`,`{{width}}×{{height}} painting grid · cells ×{{factor}} bigger`,{width:T(H,e),height:T(U,e),factor:e}),className:F===e?S:x,children:[`×`,e]},e))]}),(0,h.jsxs)(`span`,{className:`${a} ml-1 tabular-nums`,children:[n(`uiLibrary.motionZones.cells`,`{{active}} / {{total}} cells · {{width}}×{{height}}`,{active:J,total:Y,width:d.grid.width,height:d.grid.height}),F===1?``:n(`uiLibrary.motionZones.paintSuffix`,` · paint {{width}}×{{height}}`,{width:W,height:G})]}),(0,h.jsx)(`span`,{className:`flex-1`}),(0,h.jsx)(`button`,{type:`button`,onClick:te,disabled:L||!q,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`,children:n(`uiLibrary.motionZones.revert`,`Revert`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>void re(),disabled:L||!q,className:S,children:L?n(`uiLibrary.motionZones.saving`,`Saving…`):n(`uiLibrary.motionZones.save`,`Save`)})]})]}):(0,h.jsx)(`p`,{className:`${a} leading-relaxed`,children:n(`uiLibrary.motionZones.loading`,`Loading the camera's grid…`)})})}):null}export{k as MotionZonesSettings};
1
+ import{a as e,i as t,l as n,n as r,o as i}from"./scheduler-0XZJE03d.js";import{c as a,d as o,g as s,l as c,p as l,t as u,w as d}from"./index-BXAblT4K.js";import{MaskShapeCanvas as f}from"./MaskShapeCanvas-BTvl6Rin.js";var p=d(`grid-2x2`,[[`path`,{d:`M12 3v18`,key:`108xh3`}],[`path`,{d:`M3 12h18`,key:`1i2n21`}],[`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`,key:`h1oib`}]]),m=n(i(),1),h=n(e(),1),g=110,_=`motion-zones`,v=0,y=[1,2,3],b=1,x=`rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-medium text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,S=`rounded-md border border-primary/50 bg-primary/15 px-2.5 py-1 text-[11px] font-medium text-primary hover:bg-primary/25 disabled:opacity-40 transition-colors`;function C(e,t,n){let r=t*n,i=Array.from({length:r});for(let t=0;t<r;t+=1)i[t]=e[t]===!0;return i}function w(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1)if(e[n]!==t[n])return!1;return!0}function T(e,t){return Math.ceil(e/t)}function E(e,t,n){return Math.min(n-1,Math.floor((e+.5)/t*n))}function D(e,t,n,r){let i=T(t,r),a=T(n,r),o=Array.from({length:i*a},()=>!1);for(let r=0;r<n;r+=1){let s=E(r,n,a);for(let n=0;n<t;n+=1)e[r*t+n]===!0&&(o[s*i+E(n,t,i)]=!0)}return o}function O(e,t,n,r){let i=T(t,r),a=T(n,r),o=Array.from({length:t*n},()=>!1);for(let r=0;r<n;r+=1){let s=E(r,n,a);for(let n=0;n<t;n+=1)o[r*t+n]=e[s*i+E(n,t,i)]===!0}return o}function k({deviceId:e}){let n=t(),i=o(s().trpcClient,e),[d,E]=(0,m.useState)(null),[k,A]=(0,m.useState)(!1),[j,M]=(0,m.useState)(null),[N,P]=(0,m.useState)(null),[F,I]=(0,m.useState)(b),[L,R]=(0,m.useState)(!1),[z,B]=(0,m.useState)(!1),V=(0,m.useRef)(!1);(0,m.useEffect)(()=>{if(!i)return;let e=!1;return V.current=!1,E(null),A(!1),M(null),P(null),I(b),B(!1),(async()=>{try{let t=await i.motionZones?.getOptions({});if(e)return;if(!t)throw Error(`device proxy not ready`);if(E(t),V.current)return;let n=await i.motionZones?.getStatus({});if(e)return;if(!n)throw Error(`device proxy not ready`);V.current=!0;let r=n.regions.find(e=>e.shape.kind===`grid`),a=C(r?r.shape.cells:[],t.grid.width,t.grid.height);P(a),M(D(a,t.grid.width,t.grid.height,b))}catch(t){if(e)return;c(t)?A(!0):console.error(`Motion Zones load failed`,t)}})(),()=>{e=!0}},[i]);let H=d?d.grid.width:0,U=d?d.grid.height:0,W=T(H,F),G=T(U,F),K=(0,m.useMemo)(()=>j&&d?O(j,H,U,F):null,[j,d,H,U,F]),q=(0,m.useMemo)(()=>K!==null&&N!==null&&!w(K,N),[K,N]),J=(0,m.useMemo)(()=>K?K.reduce((e,t)=>t?e+1:e,0):0,[K]),Y=H*U,X=W*G,Z=(0,m.useCallback)(e=>{X>0&&M(Array.from({length:X},()=>e))},[X]),ee=(0,m.useCallback)(()=>{M(e=>e&&e.map(e=>!e))},[]),te=(0,m.useCallback)(()=>{N&&d&&M(D(N,H,U,F))},[N,d,H,U,F]),ne=(0,m.useCallback)(e=>{e===F||!d||M(t=>{if(!t)return I(e),t;let n=D(O(t,H,U,F),H,U,e);return I(e),n})},[F,d,H,U]),Q=(0,m.useMemo)(()=>d&&j?[{id:v,shape:{kind:`grid`,gridWidth:W,gridHeight:G,cells:[...j]}}]:[],[d,j,W,G]),$=(0,m.useCallback)((e,t)=>{t.kind===`grid`&&M(t.cells)},[]),re=(0,m.useCallback)(async()=>{if(!(!i||!j||!d)){R(!0);try{let e=O(j,d.grid.width,d.grid.height,F),t={kind:`grid`,gridWidth:d.grid.width,gridHeight:d.grid.height,cells:e};await i.motionZones?.setZone({patch:{regions:[{id:v,enabled:!0,shape:t}]}});let n=await i.motionZones?.getStatus({});if(n){let e=n.regions.find(e=>e.shape.kind===`grid`),t=C(e?e.shape.cells:[],d.grid.width,d.grid.height);P(t),M(D(t,d.grid.width,d.grid.height,F))}}catch(e){console.error(`Motion Zones save failed`,e)}finally{R(!1)}}},[i,j,d,F]);u((0,m.useMemo)(()=>z&&!k&&d&&j?{id:_,order:g,node:(0,h.jsx)(f,{transparent:!0,items:Q,supportedShapes:[`grid`],grid:{width:W,height:G},selectedId:v,onSelect:()=>{},onShapeChange:$,onDrawComplete:()=>{},drawingKind:null})}:null,[z,k,d,j,Q,$,W,G]));let ie=!k&&d!==null&&j!==null;return i?(0,h.jsx)(l,{title:n(`uiLibrary.motionZones.title`,`Motion Zones`),icon:(0,h.jsx)(p,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,h.jsx)(`div`,{className:`flex flex-col gap-3`,children:k?(0,h.jsx)(`p`,{className:`${a} leading-relaxed`,children:n(`uiLibrary.motionZones.unsupported`,`This camera doesn't expose an on-board motion zones grid.`)}):ie?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`p`,{className:`${a} leading-relaxed`,children:(0,h.jsx)(r,{uiKey:`uiLibrary.motionZones.intro`,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).`,components:[(0,h.jsx)(`strong`,{className:`text-foreground`},`e`),(0,h.jsx)(`strong`,{className:`text-foreground`},`s`),(0,h.jsx)(`strong`,{className:`text-foreground`},`c`)],values:{width:d.grid.width,height:d.grid.height}})}),(0,h.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,h.jsx)(`button`,{type:`button`,onClick:()=>B(e=>!e),disabled:L,"aria-pressed":z,className:z?S:x,children:z?n(`uiLibrary.motionZones.doneEditing`,`Done editing`):n(`uiLibrary.motionZones.editGrid`,`Edit grid`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>Z(!0),disabled:L,className:x,children:n(`uiLibrary.motionZones.allOn`,`All on`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>Z(!1),disabled:L,className:x,children:n(`uiLibrary.motionZones.allOff`,`All off`)}),(0,h.jsx)(`button`,{type:`button`,onClick:ee,disabled:L,className:x,children:n(`uiLibrary.motionZones.invert`,`Invert`)}),(0,h.jsxs)(`div`,{className:`flex items-center gap-1 ml-1`,role:`group`,"aria-label":n(`uiLibrary.motionZones.cellSize`,`Cell size`),children:[(0,h.jsx)(`span`,{className:`${a} mr-0.5`,children:n(`uiLibrary.motionZones.cellSize`,`Cell size`)}),y.map(e=>(0,h.jsxs)(`button`,{type:`button`,onClick:()=>ne(e),disabled:L,"aria-pressed":F===e,title:e===1?n(`uiLibrary.motionZones.cameraGrid`,`Camera grid {{width}}×{{height}} (finest)`,{width:H,height:U}):n(`uiLibrary.motionZones.paintingGrid`,`{{width}}×{{height}} painting grid · cells ×{{factor}} bigger`,{width:T(H,e),height:T(U,e),factor:e}),className:F===e?S:x,children:[`×`,e]},e))]}),(0,h.jsxs)(`span`,{className:`${a} ml-1 tabular-nums`,children:[n(`uiLibrary.motionZones.cells`,`{{active}} / {{total}} cells · {{width}}×{{height}}`,{active:J,total:Y,width:d.grid.width,height:d.grid.height}),F===1?``:n(`uiLibrary.motionZones.paintSuffix`,` · paint {{width}}×{{height}}`,{width:W,height:G})]}),(0,h.jsx)(`span`,{className:`flex-1`}),(0,h.jsx)(`button`,{type:`button`,onClick:te,disabled:L||!q,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`,children:n(`uiLibrary.motionZones.revert`,`Revert`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>void re(),disabled:L||!q,className:S,children:L?n(`uiLibrary.motionZones.saving`,`Saving…`):n(`uiLibrary.motionZones.save`,`Save`)})]})]}):(0,h.jsx)(`p`,{className:`${a} leading-relaxed`,children:n(`uiLibrary.motionZones.loading`,`Loading the camera's grid…`)})})}):null}export{k as MotionZonesSettings};