From dbf7685438ccd19af2034d1b0baf23e477f3bf6a Mon Sep 17 00:00:00 2001 From: Thomas Gregg <2538782+thomasgregg@users.noreply.github.com> Date: Sun, 13 Sep 2026 17:05:16 +0200 Subject: [PATCH] feat: redesign custom camera controls Introduce the consistent low-chrome control system for dashboard, portrait, landscape, and desktop layouts. Keep native media controls untouched, preserve dashboard enlarge, refine Talk and door interactions, and add compact, reduced-motion, forced-colour, and narrow-screen validation for v0.11.0-beta.1. --- CHANGELOG.md | 77 ++ README.md | 11 +- dist/ring-view.js | 1332 ++++++++++++++++--------- docs/configuration.md | 15 +- docs/door-access.md | 39 +- docs/privacy-and-accessibility.md | 2 +- package-lock.json | 4 +- package.json | 2 +- src/ring-view-dialog.ts | 27 +- src/styles.ts | 581 ++++++++--- src/translations/de.json | 2 +- src/translations/en.json | 2 +- tests/browser/card.spec.ts | 83 +- tests/browser/liquid-controls.spec.ts | 333 +++++++ tests/unit/door-control.test.ts | 21 +- 15 files changed, 1861 insertions(+), 670 deletions(-) create mode 100644 tests/browser/liquid-controls.spec.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index e51f5a9..67359e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,83 @@ All notable changes to this project are documented here. ## [Unreleased] +## [0.11.0-beta.1] - 2026-09-13 + +### Added + +- Add one coherent, low-chrome control language inspired by modern video-player + interfaces: borderless translucent rails, circular inset interaction states, + consistent icon weight, and restrained semantic colour. +- Keep an enlarge action at the top right of every interactive dashboard card. + It opens the complete Ring View viewer and remains distinct from any native + fullscreen action supplied by the browser or Home Assistant media player. +- Add explicit forced-colour treatment for utility rails, visitor actions, + selected modes, door progress, and keyboard focus. +- Add a compact-height rule that gives a central connection, Ding, or feedback + message temporary priority over visitor controls when both cannot fit without + colliding. + +### Changed + +- Standardize custom utility controls at 44 pixels on desktop and 48 pixels on + touch layouts. Talk and door access now use stable 48-pixel-high targets in + desktop, landscape, portrait, and dashboard modes. +- Group Recording and Live in one top rail. The selected mode, hover, pressed, + and keyboard-focus treatments stay four pixels inside the control instead of + changing its outer size. +- Use a plain 17-pixel red dot for Live without the unrelated white ring that + previously made the selected state look like a different control family. +- Keep portrait camera actions in a compact vertical rail at the right edge, + while mode switching stays near the top right and Close stays in the corner. + Desktop and landscape retain their established header arrangement. +- Combine Talk and door access in one borderless visitor rail without a divider, + nested outlines, shadow, or default blur. Talk turns red only while audio is + being transmitted. +- Extend the default door confirmation to 1.6 seconds. Its warm progress surface + fills from left to right with a rounded starting edge and a straight moving + edge, while the target and its contents remain stationary. +- Present loading as a lightweight spinner and label, actionable recovery as a + dark rounded action, and short snapshot, door, and session feedback as one + central dark message surface with left-aligned wrapping text. +- Preserve the browser and Home Assistant media player's own playback, volume, + timeline, captions, and fullscreen controls exactly as supplied. + +### Fixed + +- Prevent snapshot, mode, Talk, and door states from changing the size or outer + silhouette of their controls. +- Prevent the snapshot focus/selected treatment from overflowing the edge of a + narrow card or appearing visibly non-circular. +- Keep long status and translated text inside the camera surface without + overlapping header controls, visitor actions, or native playback controls. +- Restore visitor actions automatically when a short interactive card becomes + tall enough or its central message clears. +- Keep both Talk and door access at equal 48-by-48 icon targets on exceptionally + narrow screens where visible labels can no longer fit. +- Make keyboard focus visible in Windows forced-colour mode, where decorative + box shadows may be suppressed by the browser. +- Cancel a door hold at the final millisecond when the pointer is released or + cancelled, and prevent pointer-up after activation from sending a second + service call. + +### Documentation + +- Describe the new custom-control layout, dashboard enlarge action, Talk state, + visitor rail, compact-message priority, and 1.6-second door confirmation in + the README and configuration, door-access, privacy, and accessibility guides. +- Explicitly distinguish Ring View's custom controls from the native media + controls that the redesign intentionally does not modify. + +### Tests + +- Pass 195 unit and component tests and 134 browser scenarios across desktop and + phone projects. +- Cover stable rail geometry, dashboard enlarge behavior, selected, hover, + pressed, and keyboard states, active Talk colour, progressive door fill, + pointer cancellation, compact-height recovery, long feedback text, portrait, + landscape, 280-pixel width, reduced motion, forced colours, provider parity, + and preservation of native media controls. + ## [0.10.0] - 2026-09-13 ### Added diff --git a/README.md b/README.md index 417118a..20fe457 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,9 @@ event instead of changing that choice for you.
Tap the card to open the viewer. The history icon selects the latest recording; -the red dot selects Live. +the red dot selects Live. Ring View's own controls use consistent dark, +borderless rails over the camera image. The browser or Home Assistant player's +native playback, volume, timeline, and fullscreen controls remain unchanged. ### Choose how the dashboard card works @@ -150,7 +152,7 @@ In the visual editor, open **Dashboard card → Dashboard behavior**: | Choose | What it does | Good for | | --- | --- | --- | | **Open fullscreen viewer** (default) | Shows a still image. Tap it to open the complete viewer. | Phones and everyday dashboards. | -| **Control camera in card** | Adds Recording, Live and optional visitor controls directly to the card. | Wall tablets and hands-on dashboards. | +| **Control camera in card** | Adds Recording, Live and optional visitor controls directly to the card. The enlarge button remains at the top right and opens the complete Ring View viewer. | Wall tablets and hands-on dashboards. | For an interactive card, **No — wait for a tap** is the calmest startup choice. The selected Recording or Live view remains highlighted while its still image @@ -189,6 +191,11 @@ Enable **Fullscreen viewer → Two-way audio** to add **Hold to talk**. This req the official Ring Live view camera, an HTTPS Home Assistant connection and microphone permission. +Talk and door access share one calm, borderless action rail with 48-pixel touch +targets. Talk becomes red only while audio is being transmitted. A door action +fills from left to right during its 1.6-second confirmation and stays the same +size throughout the hold. + To add a door action, open **Door access** and select a lock. Choose **Unlock** or, for a compatible lock such as Nuki, **Open door** to release the latch. An optional contact sensor makes the icon show whether the physical door is open diff --git a/dist/ring-view.js b/dist/ring-view.js index 7ed7880..ed9bd38 100644 --- a/dist/ring-view.js +++ b/dist/ring-view.js @@ -1,5 +1,5 @@ var be = "M11,15H13V17H11V15M11,7H13V13H11V7M12,2C6.47,2 2,6.5 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20Z", yt = "M10,21H14A2,2 0 0,1 12,23A2,2 0 0,1 10,21M21,19V20H3V19L5,17V11C5,7.9 7.03,5.17 10,4.29C10,4.19 10,4.1 10,4A2,2 0 0,1 12,2A2,2 0 0,1 14,4C14,4.1 14,4.19 14,4.29C16.97,5.17 19,7.9 19,11V17L21,19M17,11A5,5 0 0,0 12,6A5,5 0 0,0 7,11V18H17V11M19.75,3.19L18.33,4.61C20.04,6.3 21,8.6 21,11H23C23,8.07 21.84,5.25 19.75,3.19M1,11H3C3,8.6 3.96,6.3 5.67,4.61L4.25,3.19C2.16,5.25 1,8.07 1,11Z", Zt = "M9,12C9,11.19 9.3,10.5 9.89,9.89C10.5,9.3 11.19,9 12,9C12.81,9 13.5,9.3 14.11,9.89C14.7,10.5 15,11.19 15,12C15,12.81 14.7,13.5 14.11,14.11C13.5,14.7 12.81,15 12,15C11.19,15 10.5,14.7 9.89,14.11C9.3,13.5 9,12.81 9,12M5.53,8.44L7.31,10.22L5.53,12L7.31,13.78L5.53,15.56L2,12L5.53,8.44M8.44,18.47L10.22,16.69L12,18.47L13.78,16.69L15.56,18.47L12,22L8.44,18.47M18.47,15.56L16.69,13.78L18.47,12L16.69,10.22L18.47,8.44L22,12L18.47,15.56M15.56,5.53L13.78,7.31L12,5.53L10.22,7.31L8.44,5.53L12,2L15.56,5.53Z", kt = "M20,4H16.83L15,2H9L7.17,4H4A2,2 0 0,0 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V6A2,2 0 0,0 20,4M20,18H4V6H8.05L9.88,4H14.12L15.95,6H20V18M12,7A5,5 0 0,0 7,12A5,5 0 0,0 12,17A5,5 0 0,0 17,12A5,5 0 0,0 12,7M12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15Z", Ze = "M12 2C6.5 2 2 6.5 2 12S6.5 22 12 22 22 17.5 22 12 17.5 2 12 2M12 20C7.59 20 4 16.41 4 12S7.59 4 12 4 20 7.59 20 12 16.41 20 12 20M16.59 7.58L10 14.17L7.41 11.59L6 13L10 17L18 9L16.59 7.58Z", Gt = "M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z", Qt = "M16,11H18V13H16V11M12,3H19C20.11,3 21,3.89 21,5V19H22V21H2V19H10V5C10,3.89 10.89,3 12,3M12,5V19H19V5H12Z", Ge = "M12,3C10.89,3 10,3.89 10,5H3V19H2V21H22V19H21V5C21,3.89 20.11,3 19,3H12M12,5H19V19H12V5M5,11H7V13H5V11Z", Yt = "M14 15C14 16.11 13.11 17 12 17S10 16.11 10 15 10.9 13 12 13 14 13.9 14 15M18 4V20C18 21.1 17.11 22 16 22H8C6.9 22 6 21.11 6 20V4C6 2.9 6.9 2 8 2H16C17.11 2 18 2.9 18 4M10.5 7C10.5 7.83 11.17 8.5 12 8.5S13.5 7.83 13.5 7 12.83 5.5 12 5.5 10.5 6.17 10.5 7M16 10H8V20H16V10Z", Jt = "M5,5H10V7H7V10H5V5M14,5H19V10H17V7H14V5M17,14H19V19H14V17H17V14M10,17V19H5V14H7V17H10Z", Xt = "M13.5,8H12V13L16.28,15.54L17,14.33L13.5,12.25V8M13,3A9,9 0 0,0 4,12H1L4.96,16.03L9,12H6A7,7 0 0,1 13,5A7,7 0 0,1 20,12A7,7 0 0,1 13,19C11.07,19 9.32,18.21 8.06,16.94L6.64,18.36C8.27,20 10.5,21 13,21A9,9 0 0,0 22,12A9,9 0 0,0 13,3", Qe = "M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z", At = "M10 13C11.1 13 12 13.89 12 15C12 16.11 11.11 17 10 17S8 16.11 8 15 8.9 13 10 13M18 1C15.24 1 13 3.24 13 6V8H4C2.9 8 2 8.9 2 10V20C2 21.1 2.9 22 4 22H16C17.1 22 18 21.1 18 20V10C18 8.9 17.1 8 16 8H15V6C15 4.34 16.34 3 18 3S21 4.34 21 6V8H23V6C23 3.24 20.76 1 18 1M16 10V20H4V10H16Z", St = "M12,2A3,3 0 0,1 15,5V11A3,3 0 0,1 12,14A3,3 0 0,1 9,11V5A3,3 0 0,1 12,2M19,11C19,14.53 16.39,17.44 13,17.93V21H11V17.93C7.61,17.44 5,14.53 5,11H7A5,5 0 0,0 12,16A5,5 0 0,0 17,11H19Z", Tt = "M19,11C19,12.19 18.66,13.3 18.1,14.28L16.87,13.05C17.14,12.43 17.3,11.74 17.3,11H19M15,11.16L9,5.18V5A3,3 0 0,1 12,2A3,3 0 0,1 15,5V11L15,11.16M4.27,3L21,19.73L19.73,21L15.54,16.81C14.77,17.27 13.91,17.58 13,17.72V21H11V17.72C7.72,17.23 5,14.41 5,11H6.7C6.7,14 9.24,16.1 12,16.1C12.81,16.1 13.6,15.91 14.31,15.58L12.65,13.92L12,14A3,3 0 0,1 9,11V10.28L3,4.27L4.27,3Z", ei = "M12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2C17.5,2 22,6 22,11A6,6 0 0,1 16,17H14.2C13.9,17 13.7,17.2 13.7,17.5C13.7,17.6 13.8,17.7 13.8,17.8C14.2,18.3 14.4,18.9 14.4,19.5C14.5,20.9 13.4,22 12,22M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C12.3,20 12.5,19.8 12.5,19.5C12.5,19.3 12.4,19.2 12.4,19.1C12,18.6 11.8,18.1 11.8,17.5C11.8,16.1 12.9,15 14.3,15H16A4,4 0 0,0 20,11C20,7.1 16.4,4 12,4M6.5,10C7.3,10 8,10.7 8,11.5C8,12.3 7.3,13 6.5,13C5.7,13 5,12.3 5,11.5C5,10.7 5.7,10 6.5,10M9.5,6C10.3,6 11,6.7 11,7.5C11,8.3 10.3,9 9.5,9C8.7,9 8,8.3 8,7.5C8,6.7 8.7,6 9.5,6M14.5,6C15.3,6 16,6.7 16,7.5C16,8.3 15.3,9 14.5,9C13.7,9 13,8.3 13,7.5C13,6.7 13.7,6 14.5,6M17.5,10C18.3,10 19,10.7 19,11.5C19,12.3 18.3,13 17.5,13C16.7,13 16,12.3 16,11.5C16,10.7 16.7,10 17.5,10Z", Rt = "M8,5.14V19.14L19,12.14L8,5.14Z", ti = "M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M10,16.5L16,12L10,7.5V16.5Z"; -const oe = globalThis, De = oe.ShadowRoot && (oe.ShadyCSS === void 0 || oe.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, Ie = /* @__PURE__ */ Symbol(), Ye = /* @__PURE__ */ new WeakMap(); +const re = globalThis, De = re.ShadowRoot && (re.ShadyCSS === void 0 || re.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, Ie = /* @__PURE__ */ Symbol(), Ye = /* @__PURE__ */ new WeakMap(); let $t = class { constructor(t, i, n) { if (this._$cssResult$ = !0, n !== Ie) throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead."); @@ -19,24 +19,24 @@ let $t = class { } }; const ii = (e) => new $t(typeof e == "string" ? e : e + "", void 0, Ie), K = (e, ...t) => { - const i = e.length === 1 ? e[0] : t.reduce((n, r, o) => n + ((a) => { + const i = e.length === 1 ? e[0] : t.reduce((n, o, r) => n + ((a) => { if (a._$cssResult$ === !0) return a.cssText; if (typeof a == "number") return a; throw Error("Value passed to 'css' function must be a 'css' function result: " + a + ". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security."); - })(r) + e[o + 1], e[0]); + })(o) + e[r + 1], e[0]); return new $t(i, e, Ie); }, ni = (e, t) => { if (De) e.adoptedStyleSheets = t.map((i) => i instanceof CSSStyleSheet ? i : i.styleSheet); else for (const i of t) { - const n = document.createElement("style"), r = oe.litNonce; - r !== void 0 && n.setAttribute("nonce", r), n.textContent = i.cssText, e.appendChild(n); + const n = document.createElement("style"), o = re.litNonce; + o !== void 0 && n.setAttribute("nonce", o), n.textContent = i.cssText, e.appendChild(n); } }, Je = De ? (e) => e : (e) => e instanceof CSSStyleSheet ? ((t) => { let i = ""; for (const n of t.cssRules) i += n.cssText; return ii(i); })(e) : e; -const { is: ri, defineProperty: oi, getOwnPropertyDescriptor: si, getOwnPropertyNames: ai, getOwnPropertySymbols: di, getPrototypeOf: ci } = Object, ge = globalThis, Xe = ge.trustedTypes, li = Xe ? Xe.emptyScript : "", hi = ge.reactiveElementPolyfillSupport, G = (e, t) => e, ce = { toAttribute(e, t) { +const { is: oi, defineProperty: ri, getOwnPropertyDescriptor: si, getOwnPropertyNames: ai, getOwnPropertySymbols: di, getPrototypeOf: ci } = Object, ge = globalThis, Xe = ge.trustedTypes, li = Xe ? Xe.emptyScript : "", hi = ge.reactiveElementPolyfillSupport, G = (e, t) => e, ce = { toAttribute(e, t) { switch (t) { case Boolean: e = e ? li : null; @@ -64,7 +64,7 @@ const { is: ri, defineProperty: oi, getOwnPropertyDescriptor: si, getOwnProperty } } return i; -} }, Ve = (e, t) => !ri(e, t), et = { attribute: !0, type: String, converter: ce, reflect: !1, useDefault: !1, hasChanged: Ve }; +} }, Ve = (e, t) => !oi(e, t), et = { attribute: !0, type: String, converter: ce, reflect: !1, useDefault: !1, hasChanged: Ve }; Symbol.metadata ??= /* @__PURE__ */ Symbol("metadata"), ge.litPropertyMetadata ??= /* @__PURE__ */ new WeakMap(); let B = class extends HTMLElement { static addInitializer(t) { @@ -75,19 +75,19 @@ let B = class extends HTMLElement { } static createProperty(t, i = et) { if (i.state && (i.attribute = !1), this._$Ei(), this.prototype.hasOwnProperty(t) && ((i = Object.create(i)).wrapped = !0), this.elementProperties.set(t, i), !i.noAccessor) { - const n = /* @__PURE__ */ Symbol(), r = this.getPropertyDescriptor(t, n, i); - r !== void 0 && oi(this.prototype, t, r); + const n = /* @__PURE__ */ Symbol(), o = this.getPropertyDescriptor(t, n, i); + o !== void 0 && ri(this.prototype, t, o); } } static getPropertyDescriptor(t, i, n) { - const { get: r, set: o } = si(this.prototype, t) ?? { get() { + const { get: o, set: r } = si(this.prototype, t) ?? { get() { return this[i]; }, set(a) { this[i] = a; } }; - return { get: r, set(a) { - const d = r?.call(this); - o?.call(this, a), this.requestUpdate(t, d, n); + return { get: o, set(a) { + const d = o?.call(this); + r?.call(this, a), this.requestUpdate(t, d, n); }, configurable: !0, enumerable: !0 }; } static getPropertyOptions(t) { @@ -102,17 +102,17 @@ let B = class extends HTMLElement { if (this.hasOwnProperty(G("finalized"))) return; if (this.finalized = !0, this._$Ei(), this.hasOwnProperty(G("properties"))) { const i = this.properties, n = [...ai(i), ...di(i)]; - for (const r of n) this.createProperty(r, i[r]); + for (const o of n) this.createProperty(o, i[o]); } const t = this[Symbol.metadata]; if (t !== null) { const i = litPropertyMetadata.get(t); - if (i !== void 0) for (const [n, r] of i) this.elementProperties.set(n, r); + if (i !== void 0) for (const [n, o] of i) this.elementProperties.set(n, o); } this._$Eh = /* @__PURE__ */ new Map(); for (const [i, n] of this.elementProperties) { - const r = this._$Eu(i, n); - r !== void 0 && this._$Eh.set(r, i); + const o = this._$Eu(i, n); + o !== void 0 && this._$Eh.set(o, i); } this.elementStyles = this.finalizeStyles(this.styles); } @@ -120,7 +120,7 @@ let B = class extends HTMLElement { const i = []; if (Array.isArray(t)) { const n = new Set(t.flat(1 / 0).reverse()); - for (const r of n) i.unshift(Je(r)); + for (const o of n) i.unshift(Je(o)); } else t !== void 0 && i.push(Je(t)); return i; } @@ -161,31 +161,31 @@ let B = class extends HTMLElement { this._$AK(t, n); } _$ET(t, i) { - const n = this.constructor.elementProperties.get(t), r = this.constructor._$Eu(t, n); - if (r !== void 0 && n.reflect === !0) { - const o = (n.converter?.toAttribute !== void 0 ? n.converter : ce).toAttribute(i, n.type); - this._$Em = t, o == null ? this.removeAttribute(r) : this.setAttribute(r, o), this._$Em = null; + const n = this.constructor.elementProperties.get(t), o = this.constructor._$Eu(t, n); + if (o !== void 0 && n.reflect === !0) { + const r = (n.converter?.toAttribute !== void 0 ? n.converter : ce).toAttribute(i, n.type); + this._$Em = t, r == null ? this.removeAttribute(o) : this.setAttribute(o, r), this._$Em = null; } } _$AK(t, i) { - const n = this.constructor, r = n._$Eh.get(t); - if (r !== void 0 && this._$Em !== r) { - const o = n.getPropertyOptions(r), a = typeof o.converter == "function" ? { fromAttribute: o.converter } : o.converter?.fromAttribute !== void 0 ? o.converter : ce; - this._$Em = r; - const d = a.fromAttribute(i, o.type); - this[r] = d ?? this._$Ej?.get(r) ?? d, this._$Em = null; + const n = this.constructor, o = n._$Eh.get(t); + if (o !== void 0 && this._$Em !== o) { + const r = n.getPropertyOptions(o), a = typeof r.converter == "function" ? { fromAttribute: r.converter } : r.converter?.fromAttribute !== void 0 ? r.converter : ce; + this._$Em = o; + const d = a.fromAttribute(i, r.type); + this[o] = d ?? this._$Ej?.get(o) ?? d, this._$Em = null; } } - requestUpdate(t, i, n, r = !1, o) { + requestUpdate(t, i, n, o = !1, r) { if (t !== void 0) { const a = this.constructor; - if (r === !1 && (o = this[t]), n ??= a.getPropertyOptions(t), !((n.hasChanged ?? Ve)(o, i) || n.useDefault && n.reflect && o === this._$Ej?.get(t) && !this.hasAttribute(a._$Eu(t, n)))) return; + if (o === !1 && (r = this[t]), n ??= a.getPropertyOptions(t), !((n.hasChanged ?? Ve)(r, i) || n.useDefault && n.reflect && r === this._$Ej?.get(t) && !this.hasAttribute(a._$Eu(t, n)))) return; this.C(t, i, n); } this.isUpdatePending === !1 && (this._$ES = this._$EP()); } - C(t, i, { useDefault: n, reflect: r, wrapped: o }, a) { - n && !(this._$Ej ??= /* @__PURE__ */ new Map()).has(t) && (this._$Ej.set(t, a ?? i ?? this[t]), o !== !0 || a !== void 0) || (this._$AL.has(t) || (this.hasUpdated || n || (i = void 0), this._$AL.set(t, i)), r === !0 && this._$Em !== t && (this._$Eq ??= /* @__PURE__ */ new Set()).add(t)); + C(t, i, { useDefault: n, reflect: o, wrapped: r }, a) { + n && !(this._$Ej ??= /* @__PURE__ */ new Map()).has(t) && (this._$Ej.set(t, a ?? i ?? this[t]), r !== !0 || a !== void 0) || (this._$AL.has(t) || (this.hasUpdated || n || (i = void 0), this._$AL.set(t, i)), o === !0 && this._$Em !== t && (this._$Eq ??= /* @__PURE__ */ new Set()).add(t)); } async _$EP() { this.isUpdatePending = !0; @@ -204,13 +204,13 @@ let B = class extends HTMLElement { if (!this.isUpdatePending) return; if (!this.hasUpdated) { if (this.renderRoot ??= this.createRenderRoot(), this._$Ep) { - for (const [r, o] of this._$Ep) this[r] = o; + for (const [o, r] of this._$Ep) this[o] = r; this._$Ep = void 0; } const n = this.constructor.elementProperties; - if (n.size > 0) for (const [r, o] of n) { - const { wrapped: a } = o, d = this[r]; - a !== !0 || this._$AL.has(r) || d === void 0 || this.C(r, void 0, o, d); + if (n.size > 0) for (const [o, r] of n) { + const { wrapped: a } = r, d = this[o]; + a !== !0 || this._$AL.has(o) || d === void 0 || this.C(o, void 0, r, d); } } let t = !1; @@ -248,55 +248,55 @@ let B = class extends HTMLElement { } }; B.elementStyles = [], B.shadowRootOptions = { mode: "open" }, B[G("elementProperties")] = /* @__PURE__ */ new Map(), B[G("finalized")] = /* @__PURE__ */ new Map(), hi?.({ ReactiveElement: B }), (ge.reactiveElementVersions ??= []).push("2.1.2"); -const Ue = globalThis, tt = (e) => e, le = Ue.trustedTypes, it = le ? le.createPolicy("lit-html", { createHTML: (e) => e }) : void 0, Ct = "$lit$", M = `lit$${Math.random().toFixed(9).slice(2)}$`, Et = "?" + M, ui = `<${Et}>`, z = document, Q = () => z.createComment(""), Y = (e) => e === null || typeof e != "object" && typeof e != "function", He = Array.isArray, pi = (e) => He(e) || typeof e?.[Symbol.iterator] == "function", we = `[ -\f\r]`, Z = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g, nt = /-->/g, rt = />/g, U = RegExp(`>|${we}(?:([^\\s"'>=/]+)(${we}*=${we}*(?:[^ -\f\r"'\`<>=]|("|')|))|$)`, "g"), ot = /'/g, st = /"/g, xt = /^(?:script|style|textarea|title)$/i, vi = (e) => (t, ...i) => ({ _$litType$: e, strings: t, values: i }), h = vi(1), D = /* @__PURE__ */ Symbol.for("lit-noChange"), l = /* @__PURE__ */ Symbol.for("lit-nothing"), at = /* @__PURE__ */ new WeakMap(), H = z.createTreeWalker(z, 129); +const Ue = globalThis, tt = (e) => e, le = Ue.trustedTypes, it = le ? le.createPolicy("lit-html", { createHTML: (e) => e }) : void 0, xt = "$lit$", M = `lit$${Math.random().toFixed(9).slice(2)}$`, Ct = "?" + M, ui = `<${Ct}>`, z = document, Q = () => z.createComment(""), Y = (e) => e === null || typeof e != "object" && typeof e != "function", He = Array.isArray, pi = (e) => He(e) || typeof e?.[Symbol.iterator] == "function", we = `[ +\f\r]`, Z = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g, nt = /-->/g, ot = />/g, U = RegExp(`>|${we}(?:([^\\s"'>=/]+)(${we}*=${we}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`, "g"), rt = /'/g, st = /"/g, Et = /^(?:script|style|textarea|title)$/i, vi = (e) => (t, ...i) => ({ _$litType$: e, strings: t, values: i }), h = vi(1), D = /* @__PURE__ */ Symbol.for("lit-noChange"), l = /* @__PURE__ */ Symbol.for("lit-nothing"), at = /* @__PURE__ */ new WeakMap(), H = z.createTreeWalker(z, 129); function Pt(e, t) { if (!He(e) || !e.hasOwnProperty("raw")) throw Error("invalid template strings array"); return it !== void 0 ? it.createHTML(t) : t; } const gi = (e, t) => { const i = e.length - 1, n = []; - let r, o = t === 2 ? "" : t === 3 ? "" : "")), n]; }; class J { constructor({ strings: t, _$litType$: i }, n) { - let r; + let o; this.parts = []; - let o = 0, a = 0; + let r = 0, a = 0; const d = t.length - 1, c = this.parts, [u, p] = gi(t, i); if (this.el = J.createElement(u, n), H.currentNode = this.el.content, i === 2 || i === 3) { const v = this.el.content.firstChild; v.replaceWith(...v.childNodes); } - for (; (r = H.nextNode()) !== null && c.length < d; ) { - if (r.nodeType === 1) { - if (r.hasAttributes()) for (const v of r.getAttributeNames()) if (v.endsWith(Ct)) { - const y = p[a++], A = r.getAttribute(v).split(M), q = /([.?@])?(.*)/.exec(y); - c.push({ type: 1, index: o, name: q[2], strings: A, ctor: q[1] === "." ? mi : q[1] === "?" ? _i : q[1] === "@" ? bi : fe }), r.removeAttribute(v); - } else v.startsWith(M) && (c.push({ type: 6, index: o }), r.removeAttribute(v)); - if (xt.test(r.tagName)) { - const v = r.textContent.split(M), y = v.length - 1; + for (; (o = H.nextNode()) !== null && c.length < d; ) { + if (o.nodeType === 1) { + if (o.hasAttributes()) for (const v of o.getAttributeNames()) if (v.endsWith(xt)) { + const y = p[a++], A = o.getAttribute(v).split(M), q = /([.?@])?(.*)/.exec(y); + c.push({ type: 1, index: r, name: q[2], strings: A, ctor: q[1] === "." ? mi : q[1] === "?" ? _i : q[1] === "@" ? bi : fe }), o.removeAttribute(v); + } else v.startsWith(M) && (c.push({ type: 6, index: r }), o.removeAttribute(v)); + if (Et.test(o.tagName)) { + const v = o.textContent.split(M), y = v.length - 1; if (y > 0) { - r.textContent = le ? le.emptyScript : ""; - for (let A = 0; A < y; A++) r.append(v[A], Q()), H.nextNode(), c.push({ type: 2, index: ++o }); - r.append(v[y], Q()); + o.textContent = le ? le.emptyScript : ""; + for (let A = 0; A < y; A++) o.append(v[A], Q()), H.nextNode(), c.push({ type: 2, index: ++r }); + o.append(v[y], Q()); } } - } else if (r.nodeType === 8) if (r.data === Et) c.push({ type: 2, index: o }); + } else if (o.nodeType === 8) if (o.data === Ct) c.push({ type: 2, index: r }); else { let v = -1; - for (; (v = r.data.indexOf(M, v + 1)) !== -1; ) c.push({ type: 7, index: o }), v += M.length - 1; + for (; (v = o.data.indexOf(M, v + 1)) !== -1; ) c.push({ type: 7, index: r }), v += M.length - 1; } - o++; + r++; } } static createElement(t, i) { @@ -306,9 +306,9 @@ class J { } function W(e, t, i = e, n) { if (t === D) return t; - let r = n !== void 0 ? i._$Co?.[n] : i._$Cl; - const o = Y(t) ? void 0 : t._$litDirective$; - return r?.constructor !== o && (r?._$AO?.(!1), o === void 0 ? r = void 0 : (r = new o(e), r._$AT(e, i, n)), n !== void 0 ? (i._$Co ??= [])[n] = r : i._$Cl = r), r !== void 0 && (t = W(e, r._$AS(e, t.values), r, n)), t; + let o = n !== void 0 ? i._$Co?.[n] : i._$Cl; + const r = Y(t) ? void 0 : t._$litDirective$; + return o?.constructor !== r && (o?._$AO?.(!1), r === void 0 ? o = void 0 : (o = new r(e), o._$AT(e, i, n)), n !== void 0 ? (i._$Co ??= [])[n] = o : i._$Cl = o), o !== void 0 && (t = W(e, o._$AS(e, t.values), o, n)), t; } class fi { constructor(t, i) { @@ -321,17 +321,17 @@ class fi { return this._$AM._$AU; } u(t) { - const { el: { content: i }, parts: n } = this._$AD, r = (t?.creationScope ?? z).importNode(i, !0); - H.currentNode = r; - let o = H.nextNode(), a = 0, d = 0, c = n[0]; + const { el: { content: i }, parts: n } = this._$AD, o = (t?.creationScope ?? z).importNode(i, !0); + H.currentNode = o; + let r = H.nextNode(), a = 0, d = 0, c = n[0]; for (; c !== void 0; ) { if (a === c.index) { let u; - c.type === 2 ? u = new te(o, o.nextSibling, this, t) : c.type === 1 ? u = new c.ctor(o, c.name, c.strings, this, t) : c.type === 6 && (u = new wi(o, this, t)), this._$AV.push(u), c = n[++d]; + c.type === 2 ? u = new te(r, r.nextSibling, this, t) : c.type === 1 ? u = new c.ctor(r, c.name, c.strings, this, t) : c.type === 6 && (u = new wi(r, this, t)), this._$AV.push(u), c = n[++d]; } - a !== c?.index && (o = H.nextNode(), a++); + a !== c?.index && (r = H.nextNode(), a++); } - return H.currentNode = z, r; + return H.currentNode = z, o; } p(t) { let i = 0; @@ -342,8 +342,8 @@ class te { get _$AU() { return this._$AM?._$AU ?? this._$Cv; } - constructor(t, i, n, r) { - this.type = 2, this._$AH = l, this._$AN = void 0, this._$AA = t, this._$AB = i, this._$AM = n, this.options = r, this._$Cv = r?.isConnected ?? !0; + constructor(t, i, n, o) { + this.type = 2, this._$AH = l, this._$AN = void 0, this._$AA = t, this._$AB = i, this._$AM = n, this.options = o, this._$Cv = o?.isConnected ?? !0; } get parentNode() { let t = this._$AA.parentNode; @@ -369,11 +369,11 @@ class te { this._$AH !== l && Y(this._$AH) ? this._$AA.nextSibling.data = t : this.T(z.createTextNode(t)), this._$AH = t; } $(t) { - const { values: i, _$litType$: n } = t, r = typeof n == "number" ? this._$AC(t) : (n.el === void 0 && (n.el = J.createElement(Pt(n.h, n.h[0]), this.options)), n); - if (this._$AH?._$AD === r) this._$AH.p(i); + const { values: i, _$litType$: n } = t, o = typeof n == "number" ? this._$AC(t) : (n.el === void 0 && (n.el = J.createElement(Pt(n.h, n.h[0]), this.options)), n); + if (this._$AH?._$AD === o) this._$AH.p(i); else { - const o = new fi(r, this), a = o.u(this.options); - o.p(i), this.T(a), this._$AH = o; + const r = new fi(o, this), a = r.u(this.options); + r.p(i), this.T(a), this._$AH = r; } } _$AC(t) { @@ -383,9 +383,9 @@ class te { k(t) { He(this._$AH) || (this._$AH = [], this._$AR()); const i = this._$AH; - let n, r = 0; - for (const o of t) r === i.length ? i.push(n = new te(this.O(Q()), this.O(Q()), this, this.options)) : n = i[r], n._$AI(o), r++; - r < i.length && (this._$AR(n && n._$AB.nextSibling, r), i.length = r); + let n, o = 0; + for (const r of t) o === i.length ? i.push(n = new te(this.O(Q()), this.O(Q()), this, this.options)) : n = i[o], n._$AI(r), o++; + o < i.length && (this._$AR(n && n._$AB.nextSibling, o), i.length = o); } _$AR(t = this._$AA.nextSibling, i) { for (this._$AP?.(!1, !0, i); t !== this._$AB; ) { @@ -404,19 +404,19 @@ class fe { get _$AU() { return this._$AM._$AU; } - constructor(t, i, n, r, o) { - this.type = 1, this._$AH = l, this._$AN = void 0, this.element = t, this.name = i, this._$AM = r, this.options = o, n.length > 2 || n[0] !== "" || n[1] !== "" ? (this._$AH = Array(n.length - 1).fill(new String()), this.strings = n) : this._$AH = l; + constructor(t, i, n, o, r) { + this.type = 1, this._$AH = l, this._$AN = void 0, this.element = t, this.name = i, this._$AM = o, this.options = r, n.length > 2 || n[0] !== "" || n[1] !== "" ? (this._$AH = Array(n.length - 1).fill(new String()), this.strings = n) : this._$AH = l; } - _$AI(t, i = this, n, r) { - const o = this.strings; + _$AI(t, i = this, n, o) { + const r = this.strings; let a = !1; - if (o === void 0) t = W(this, t, i, 0), a = !Y(t) || t !== this._$AH && t !== D, a && (this._$AH = t); + if (r === void 0) t = W(this, t, i, 0), a = !Y(t) || t !== this._$AH && t !== D, a && (this._$AH = t); else { const d = t; let c, u; - for (t = o[0], c = 0; c < o.length - 1; c++) u = W(this, d[n + c], i, c), u === D && (u = this._$AH[c]), a ||= !Y(u) || u !== this._$AH[c], u === l ? t = l : t !== l && (t += (u ?? "") + o[c + 1]), this._$AH[c] = u; + for (t = r[0], c = 0; c < r.length - 1; c++) u = W(this, d[n + c], i, c), u === D && (u = this._$AH[c]), a ||= !Y(u) || u !== this._$AH[c], u === l ? t = l : t !== l && (t += (u ?? "") + r[c + 1]), this._$AH[c] = u; } - a && !r && this.j(t); + a && !o && this.j(t); } j(t) { t === l ? this.element.removeAttribute(this.name) : this.element.setAttribute(this.name, t ?? ""); @@ -439,13 +439,13 @@ class _i extends fe { } } class bi extends fe { - constructor(t, i, n, r, o) { - super(t, i, n, r, o), this.type = 5; + constructor(t, i, n, o, r) { + super(t, i, n, o, r), this.type = 5; } _$AI(t, i = this) { if ((t = W(this, t, i, 0) ?? l) === D) return; - const n = this._$AH, r = t === l && n !== l || t.capture !== n.capture || t.once !== n.once || t.passive !== n.passive, o = t !== l && (n === l || r); - r && this.element.removeEventListener(this.name, this, n), o && this.element.addEventListener(this.name, this, t), this._$AH = t; + const n = this._$AH, o = t === l && n !== l || t.capture !== n.capture || t.once !== n.once || t.passive !== n.passive, r = t !== l && (n === l || o); + o && this.element.removeEventListener(this.name, this, n), r && this.element.addEventListener(this.name, this, t), this._$AH = t; } handleEvent(t) { typeof this._$AH == "function" ? this._$AH.call(this.options?.host ?? this.element, t) : this._$AH.handleEvent(t); @@ -466,15 +466,15 @@ const yi = Ue.litHtmlPolyfillSupport; yi?.(J, te), (Ue.litHtmlVersions ??= []).push("3.3.3"); const ki = (e, t, i) => { const n = i?.renderBefore ?? t; - let r = n._$litPart$; - if (r === void 0) { - const o = i?.renderBefore ?? null; - n._$litPart$ = r = new te(t.insertBefore(Q(), o), o, void 0, i ?? {}); + let o = n._$litPart$; + if (o === void 0) { + const r = i?.renderBefore ?? null; + n._$litPart$ = o = new te(t.insertBefore(Q(), r), r, void 0, i ?? {}); } - return r._$AI(e), r; + return o._$AI(e), o; }; const Oe = globalThis; -let C = class extends B { +let x = class extends B { constructor() { super(...arguments), this.renderOptions = { host: this }, this._$Do = void 0; } @@ -496,9 +496,9 @@ let C = class extends B { return D; } }; -C._$litElement$ = !0, C.finalized = !0, Oe.litElementHydrateSupport?.({ LitElement: C }); +x._$litElement$ = !0, x.finalized = !0, Oe.litElementHydrateSupport?.({ LitElement: x }); const Ai = Oe.litElementPolyfillSupport; -Ai?.({ LitElement: C }); +Ai?.({ LitElement: x }); (Oe.litElementVersions ??= []).push("4.2.2"); const Lt = { ATTRIBUTE: 1 }, ze = (e) => (...t) => ({ _$litDirective$: e, values: t }); let Fe = class { @@ -532,11 +532,11 @@ const Mt = "important", Si = " !" + Mt, Ae = ze(class extends Fe { if (this.ft === void 0) return this.ft = new Set(Object.keys(t)), this.render(t); for (const n of this.ft) t[n] == null && (this.ft.delete(n), n.includes("-") ? i.removeProperty(n) : i[n] = null); for (const n in t) { - const r = t[n]; - if (r != null) { + const o = t[n]; + if (o != null) { this.ft.add(n); - const o = typeof r == "string" && r.endsWith(Si); - n.includes("-") || o ? i.setProperty(n, o ? r.slice(0, -11) : r, o ? Mt : "") : i[n] = r; + const r = typeof o == "string" && o.endsWith(Si); + n.includes("-") || r ? i.setProperty(n, r ? o.slice(0, -11) : o, r ? Mt : "") : i[n] = o; } } return D; @@ -548,9 +548,9 @@ const j = (e) => (t, i) => { }) : customElements.define(e, t); }; const Ti = { attribute: !0, type: String, converter: ce, reflect: !1, hasChanged: Ve }, Ri = (e = Ti, t, i) => { - const { kind: n, metadata: r } = i; - let o = globalThis.litPropertyMetadata.get(r); - if (o === void 0 && globalThis.litPropertyMetadata.set(r, o = /* @__PURE__ */ new Map()), n === "setter" && ((e = Object.create(e)).wrapped = !0), o.set(i.name, e), n === "accessor") { + const { kind: n, metadata: o } = i; + let r = globalThis.litPropertyMetadata.get(o); + if (r === void 0 && globalThis.litPropertyMetadata.set(o, r = /* @__PURE__ */ new Map()), n === "setter" && ((e = Object.create(e)).wrapped = !0), r.set(i.name, e), n === "accessor") { const { name: a } = i; return { set(d) { const c = t.get.call(this); @@ -569,9 +569,9 @@ const Ti = { attribute: !0, type: String, converter: ce, reflect: !1, hasChanged throw Error("Unsupported decorator location: " + n); }; function _(e) { - return (t, i) => typeof i == "object" ? Ri(e, t, i) : ((n, r, o) => { - const a = r.hasOwnProperty(o); - return r.constructor.createProperty(o, n), a ? Object.getOwnPropertyDescriptor(r, o) : void 0; + return (t, i) => typeof i == "object" ? Ri(e, t, i) : ((n, o, r) => { + const a = o.hasOwnProperty(r); + return o.constructor.createProperty(r, n), a ? Object.getOwnPropertyDescriptor(o, r) : void 0; })(e, t, i); } function g(e) { @@ -608,7 +608,7 @@ const $i = `