diff --git a/custom_components/nestquest/www/nestquest-cards.js b/custom_components/nestquest/www/nestquest-cards.js index adf4f3e..7b4f8e7 100644 --- a/custom_components/nestquest/www/nestquest-cards.js +++ b/custom_components/nestquest/www/nestquest-cards.js @@ -1,7 +1,7 @@ -const O = globalThis, F = O.ShadowRoot && (O.ShadyCSS === void 0 || O.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, L = /* @__PURE__ */ Symbol(), K = /* @__PURE__ */ new WeakMap(); -let pt = class { +const z = globalThis, F = z.ShadowRoot && (z.ShadyCSS === void 0 || z.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, j = /* @__PURE__ */ Symbol(), K = /* @__PURE__ */ new WeakMap(); +let ct = class { constructor(t, e, n) { - if (this._$cssResult$ = !0, n !== L) throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead."); + if (this._$cssResult$ = !0, n !== j) throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead."); this.cssText = t, this.t = e; } get styleSheet() { @@ -17,28 +17,28 @@ let pt = class { return this.cssText; } }; -const $ = (s) => new pt(typeof s == "string" ? s : s + "", void 0, L), yt = (s, ...t) => { +const $ = (s) => new ct(typeof s == "string" ? s : s + "", void 0, j), _t = (s, ...t) => { const e = s.length === 1 ? s[0] : t.reduce((n, i, 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."); })(i) + s[r + 1], s[0]); - return new pt(e, s, L); -}, bt = (s, t) => { + return new ct(e, s, j); +}, $t = (s, t) => { if (F) s.adoptedStyleSheets = t.map((e) => e instanceof CSSStyleSheet ? e : e.styleSheet); else for (const e of t) { - const n = document.createElement("style"), i = O.litNonce; + const n = document.createElement("style"), i = z.litNonce; i !== void 0 && n.setAttribute("nonce", i), n.textContent = e.cssText, s.appendChild(n); } -}, Z = F ? (s) => s : (s) => s instanceof CSSStyleSheet ? ((t) => { +}, J = F ? (s) => s : (s) => s instanceof CSSStyleSheet ? ((t) => { let e = ""; for (const n of t.cssRules) e += n.cssText; return $(e); })(s) : s; -const { is: _t, defineProperty: $t, getOwnPropertyDescriptor: vt, getOwnPropertyNames: qt, getOwnPropertySymbols: xt, getPrototypeOf: wt } = Object, z = globalThis, G = z.trustedTypes, At = G ? G.emptyScript : "", kt = z.reactiveElementPolyfillSupport, E = (s, t) => s, I = { toAttribute(s, t) { +const { is: vt, defineProperty: qt, getOwnPropertyDescriptor: xt, getOwnPropertyNames: wt, getOwnPropertySymbols: At, getPrototypeOf: kt } = Object, T = globalThis, G = T.trustedTypes, Et = G ? G.emptyScript : "", Ct = T.reactiveElementPolyfillSupport, E = (s, t) => s, I = { toAttribute(s, t) { switch (t) { case Boolean: - s = s ? At : null; + s = s ? Et : null; break; case Object: case Array: @@ -63,8 +63,8 @@ const { is: _t, defineProperty: $t, getOwnPropertyDescriptor: vt, getOwnProperty } } return e; -} }, dt = (s, t) => !_t(s, t), J = { attribute: !0, type: String, converter: I, reflect: !1, useDefault: !1, hasChanged: dt }; -Symbol.metadata ??= /* @__PURE__ */ Symbol("metadata"), z.litPropertyMetadata ??= /* @__PURE__ */ new WeakMap(); +} }, ht = (s, t) => !vt(s, t), X = { attribute: !0, type: String, converter: I, reflect: !1, useDefault: !1, hasChanged: ht }; +Symbol.metadata ??= /* @__PURE__ */ Symbol("metadata"), T.litPropertyMetadata ??= /* @__PURE__ */ new WeakMap(); let x = class extends HTMLElement { static addInitializer(t) { this._$Ei(), (this.l ??= []).push(t); @@ -72,14 +72,14 @@ let x = class extends HTMLElement { static get observedAttributes() { return this.finalize(), this._$Eh && [...this._$Eh.keys()]; } - static createProperty(t, e = J) { + static createProperty(t, e = X) { if (e.state && (e.attribute = !1), this._$Ei(), this.prototype.hasOwnProperty(t) && ((e = Object.create(e)).wrapped = !0), this.elementProperties.set(t, e), !e.noAccessor) { const n = /* @__PURE__ */ Symbol(), i = this.getPropertyDescriptor(t, n, e); - i !== void 0 && $t(this.prototype, t, i); + i !== void 0 && qt(this.prototype, t, i); } } static getPropertyDescriptor(t, e, n) { - const { get: i, set: r } = vt(this.prototype, t) ?? { get() { + const { get: i, set: r } = xt(this.prototype, t) ?? { get() { return this[e]; }, set(a) { this[e] = a; @@ -90,17 +90,17 @@ let x = class extends HTMLElement { }, configurable: !0, enumerable: !0 }; } static getPropertyOptions(t) { - return this.elementProperties.get(t) ?? J; + return this.elementProperties.get(t) ?? X; } static _$Ei() { if (this.hasOwnProperty(E("elementProperties"))) return; - const t = wt(this); + const t = kt(this); t.finalize(), t.l !== void 0 && (this.l = [...t.l]), this.elementProperties = new Map(t.elementProperties); } static finalize() { if (this.hasOwnProperty(E("finalized"))) return; if (this.finalized = !0, this._$Ei(), this.hasOwnProperty(E("properties"))) { - const e = this.properties, n = [...qt(e), ...xt(e)]; + const e = this.properties, n = [...wt(e), ...At(e)]; for (const i of n) this.createProperty(i, e[i]); } const t = this[Symbol.metadata]; @@ -119,8 +119,8 @@ let x = class extends HTMLElement { const e = []; if (Array.isArray(t)) { const n = new Set(t.flat(1 / 0).reverse()); - for (const i of n) e.unshift(Z(i)); - } else t !== void 0 && e.push(Z(t)); + for (const i of n) e.unshift(J(i)); + } else t !== void 0 && e.push(J(t)); return e; } static _$Eu(t, e) { @@ -146,7 +146,7 @@ let x = class extends HTMLElement { } createRenderRoot() { const t = this.shadowRoot ?? this.attachShadow(this.constructor.shadowRootOptions); - return bt(t, this.constructor.elementStyles), t; + return $t(t, this.constructor.elementStyles), t; } connectedCallback() { this.renderRoot ??= this.createRenderRoot(), this.enableUpdating(!0), this._$EO?.forEach((t) => t.hostConnected?.()); @@ -178,7 +178,7 @@ let x = class extends HTMLElement { requestUpdate(t, e, n, i = !1, r) { if (t !== void 0) { const a = this.constructor; - if (i === !1 && (r = this[t]), n ??= a.getPropertyOptions(t), !((n.hasChanged ?? dt)(r, e) || n.useDefault && n.reflect && r === this._$Ej?.get(t) && !this.hasAttribute(a._$Eu(t, n)))) return; + if (i === !1 && (r = this[t]), n ??= a.getPropertyOptions(t), !((n.hasChanged ?? ht)(r, e) || n.useDefault && n.reflect && r === this._$Ej?.get(t) && !this.hasAttribute(a._$Eu(t, n)))) return; this.C(t, e, n); } this.isUpdatePending === !1 && (this._$ES = this._$EP()); @@ -246,51 +246,51 @@ let x = class extends HTMLElement { firstUpdated(t) { } }; -x.elementStyles = [], x.shadowRootOptions = { mode: "open" }, x[E("elementProperties")] = /* @__PURE__ */ new Map(), x[E("finalized")] = /* @__PURE__ */ new Map(), kt?.({ ReactiveElement: x }), (z.reactiveElementVersions ??= []).push("2.1.2"); -const j = globalThis, Y = (s) => s, T = j.trustedTypes, X = T ? T.createPolicy("lit-html", { createHTML: (s) => s }) : void 0, ct = "$lit$", y = `lit$${Math.random().toFixed(9).slice(2)}$`, ht = "?" + y, Et = `<${ht}>`, q = document, S = () => q.createComment(""), C = (s) => s === null || typeof s != "object" && typeof s != "function", B = Array.isArray, St = (s) => B(s) || typeof s?.[Symbol.iterator] == "function", H = `[ -\f\r]`, k = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g, tt = /-->/g, et = />/g, b = RegExp(`>|${H}(?:([^\\s"'>=/]+)(${H}*=${H}*(?:[^ -\f\r"'\`<>=]|("|')|))|$)`, "g"), nt = /'/g, st = /"/g, ut = /^(?:script|style|textarea|title)$/i, Ct = (s) => (t, ...e) => ({ _$litType$: s, strings: t, values: e }), g = Ct(1), w = /* @__PURE__ */ Symbol.for("lit-noChange"), c = /* @__PURE__ */ Symbol.for("lit-nothing"), it = /* @__PURE__ */ new WeakMap(), _ = q.createTreeWalker(q, 129); -function ft(s, t) { - if (!B(s) || !s.hasOwnProperty("raw")) throw Error("invalid template strings array"); - return X !== void 0 ? X.createHTML(t) : t; +x.elementStyles = [], x.shadowRootOptions = { mode: "open" }, x[E("elementProperties")] = /* @__PURE__ */ new Map(), x[E("finalized")] = /* @__PURE__ */ new Map(), Ct?.({ ReactiveElement: x }), (T.reactiveElementVersions ??= []).push("2.1.2"); +const B = globalThis, Y = (s) => s, O = B.trustedTypes, tt = O ? O.createPolicy("lit-html", { createHTML: (s) => s }) : void 0, ut = "$lit$", y = `lit$${Math.random().toFixed(9).slice(2)}$`, ft = "?" + y, St = `<${ft}>`, q = document, C = () => q.createComment(""), S = (s) => s === null || typeof s != "object" && typeof s != "function", Q = Array.isArray, Nt = (s) => Q(s) || typeof s?.[Symbol.iterator] == "function", H = `[ +\f\r]`, k = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g, et = /-->/g, nt = />/g, b = RegExp(`>|${H}(?:([^\\s"'>=/]+)(${H}*=${H}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`, "g"), st = /'/g, it = /"/g, gt = /^(?:script|style|textarea|title)$/i, Pt = (s) => (t, ...e) => ({ _$litType$: s, strings: t, values: e }), g = Pt(1), w = /* @__PURE__ */ Symbol.for("lit-noChange"), c = /* @__PURE__ */ Symbol.for("lit-nothing"), rt = /* @__PURE__ */ new WeakMap(), _ = q.createTreeWalker(q, 129); +function mt(s, t) { + if (!Q(s) || !s.hasOwnProperty("raw")) throw Error("invalid template strings array"); + return tt !== void 0 ? tt.createHTML(t) : t; } -const Nt = (s, t) => { +const zt = (s, t) => { const e = s.length - 1, n = []; let i, r = t === 2 ? "" : t === 3 ? "" : "", a = k; for (let p = 0; p < e; p++) { const o = s[p]; let d, h, l = -1, f = 0; - for (; f < o.length && (a.lastIndex = f, h = a.exec(o), h !== null); ) f = a.lastIndex, a === k ? h[1] === "!--" ? a = tt : h[1] !== void 0 ? a = et : h[2] !== void 0 ? (ut.test(h[2]) && (i = RegExp("" ? (a = i ?? k, l = -1) : h[1] === void 0 ? l = -2 : (l = a.lastIndex - h[2].length, d = h[1], a = h[3] === void 0 ? b : h[3] === '"' ? st : nt) : a === st || a === nt ? a = b : a === tt || a === et ? a = k : (a = b, i = void 0); + for (; f < o.length && (a.lastIndex = f, h = a.exec(o), h !== null); ) f = a.lastIndex, a === k ? h[1] === "!--" ? a = et : h[1] !== void 0 ? a = nt : h[2] !== void 0 ? (gt.test(h[2]) && (i = RegExp("" ? (a = i ?? k, l = -1) : h[1] === void 0 ? l = -2 : (l = a.lastIndex - h[2].length, d = h[1], a = h[3] === void 0 ? b : h[3] === '"' ? it : st) : a === it || a === st ? a = b : a === et || a === nt ? a = k : (a = b, i = void 0); const u = a === b && s[p + 1].startsWith("/>") ? " " : ""; - r += a === k ? o + Et : l >= 0 ? (n.push(d), o.slice(0, l) + ct + o.slice(l) + y + u) : o + y + (l === -2 ? p : u); + r += a === k ? o + St : l >= 0 ? (n.push(d), o.slice(0, l) + ut + o.slice(l) + y + u) : o + y + (l === -2 ? p : u); } - return [ft(s, r + (s[e] || "") + (t === 2 ? "" : t === 3 ? "" : "")), n]; + return [mt(s, r + (s[e] || "") + (t === 2 ? "" : t === 3 ? "" : "")), n]; }; class N { constructor({ strings: t, _$litType$: e }, n) { let i; this.parts = []; let r = 0, a = 0; - const p = t.length - 1, o = this.parts, [d, h] = Nt(t, e); + const p = t.length - 1, o = this.parts, [d, h] = zt(t, e); if (this.el = N.createElement(d, n), _.currentNode = this.el.content, e === 2 || e === 3) { const l = this.el.content.firstChild; l.replaceWith(...l.childNodes); } for (; (i = _.nextNode()) !== null && o.length < p; ) { if (i.nodeType === 1) { - if (i.hasAttributes()) for (const l of i.getAttributeNames()) if (l.endsWith(ct)) { + if (i.hasAttributes()) for (const l of i.getAttributeNames()) if (l.endsWith(ut)) { const f = h[a++], u = i.getAttribute(l).split(y), m = /([.?@])?(.*)/.exec(f); - o.push({ type: 1, index: r, name: m[2], strings: u, ctor: m[1] === "." ? Ot : m[1] === "?" ? Tt : m[1] === "@" ? zt : M }), i.removeAttribute(l); + o.push({ type: 1, index: r, name: m[2], strings: u, ctor: m[1] === "." ? Tt : m[1] === "?" ? Mt : m[1] === "@" ? Ut : M }), i.removeAttribute(l); } else l.startsWith(y) && (o.push({ type: 6, index: r }), i.removeAttribute(l)); - if (ut.test(i.tagName)) { + if (gt.test(i.tagName)) { const l = i.textContent.split(y), f = l.length - 1; if (f > 0) { - i.textContent = T ? T.emptyScript : ""; - for (let u = 0; u < f; u++) i.append(l[u], S()), _.nextNode(), o.push({ type: 2, index: ++r }); - i.append(l[f], S()); + i.textContent = O ? O.emptyScript : ""; + for (let u = 0; u < f; u++) i.append(l[u], C()), _.nextNode(), o.push({ type: 2, index: ++r }); + i.append(l[f], C()); } } - } else if (i.nodeType === 8) if (i.data === ht) o.push({ type: 2, index: r }); + } else if (i.nodeType === 8) if (i.data === ft) o.push({ type: 2, index: r }); else { let l = -1; for (; (l = i.data.indexOf(y, l + 1)) !== -1; ) o.push({ type: 7, index: r }), l += y.length - 1; @@ -306,10 +306,10 @@ class N { function A(s, t, e = s, n) { if (t === w) return t; let i = n !== void 0 ? e._$Co?.[n] : e._$Cl; - const r = C(t) ? void 0 : t._$litDirective$; + const r = S(t) ? void 0 : t._$litDirective$; return i?.constructor !== r && (i?._$AO?.(!1), r === void 0 ? i = void 0 : (i = new r(s), i._$AT(s, e, n)), n !== void 0 ? (e._$Co ??= [])[n] = i : e._$Cl = i), i !== void 0 && (t = A(s, i._$AS(s, t.values), i, n)), t; } -class Pt { +class Ot { constructor(t, e) { this._$AV = [], this._$AN = void 0, this._$AD = t, this._$AM = e; } @@ -326,7 +326,7 @@ class Pt { for (; o !== void 0; ) { if (a === o.index) { let d; - o.type === 2 ? d = new P(r, r.nextSibling, this, t) : o.type === 1 ? d = new o.ctor(r, o.name, o.strings, this, t) : o.type === 6 && (d = new Mt(r, this, t)), this._$AV.push(d), o = n[++p]; + o.type === 2 ? d = new P(r, r.nextSibling, this, t) : o.type === 1 ? d = new o.ctor(r, o.name, o.strings, this, t) : o.type === 6 && (d = new Ht(r, this, t)), this._$AV.push(d), o = n[++p]; } a !== o?.index && (r = _.nextNode(), a++); } @@ -356,7 +356,7 @@ class P { return this._$AB; } _$AI(t, e = this) { - t = A(this, t, e), C(t) ? t === c || t == null || t === "" ? (this._$AH !== c && this._$AR(), this._$AH = c) : t !== this._$AH && t !== w && this._(t) : t._$litType$ !== void 0 ? this.$(t) : t.nodeType !== void 0 ? this.T(t) : St(t) ? this.k(t) : this._(t); + t = A(this, t, e), S(t) ? t === c || t == null || t === "" ? (this._$AH !== c && this._$AR(), this._$AH = c) : t !== this._$AH && t !== w && this._(t) : t._$litType$ !== void 0 ? this.$(t) : t.nodeType !== void 0 ? this.T(t) : Nt(t) ? this.k(t) : this._(t); } O(t) { return this._$AA.parentNode.insertBefore(t, this._$AB); @@ -365,25 +365,25 @@ class P { this._$AH !== t && (this._$AR(), this._$AH = this.O(t)); } _(t) { - this._$AH !== c && C(this._$AH) ? this._$AA.nextSibling.data = t : this.T(q.createTextNode(t)), this._$AH = t; + this._$AH !== c && S(this._$AH) ? this._$AA.nextSibling.data = t : this.T(q.createTextNode(t)), this._$AH = t; } $(t) { - const { values: e, _$litType$: n } = t, i = typeof n == "number" ? this._$AC(t) : (n.el === void 0 && (n.el = N.createElement(ft(n.h, n.h[0]), this.options)), n); + const { values: e, _$litType$: n } = t, i = typeof n == "number" ? this._$AC(t) : (n.el === void 0 && (n.el = N.createElement(mt(n.h, n.h[0]), this.options)), n); if (this._$AH?._$AD === i) this._$AH.p(e); else { - const r = new Pt(i, this), a = r.u(this.options); + const r = new Ot(i, this), a = r.u(this.options); r.p(e), this.T(a), this._$AH = r; } } _$AC(t) { - let e = it.get(t.strings); - return e === void 0 && it.set(t.strings, e = new N(t)), e; + let e = rt.get(t.strings); + return e === void 0 && rt.set(t.strings, e = new N(t)), e; } k(t) { - B(this._$AH) || (this._$AH = [], this._$AR()); + Q(this._$AH) || (this._$AH = [], this._$AR()); const e = this._$AH; let n, i = 0; - for (const r of t) i === e.length ? e.push(n = new P(this.O(S()), this.O(S()), this, this.options)) : n = e[i], n._$AI(r), i++; + for (const r of t) i === e.length ? e.push(n = new P(this.O(C()), this.O(C()), this, this.options)) : n = e[i], n._$AI(r), i++; i < e.length && (this._$AR(n && n._$AB.nextSibling, i), e.length = i); } _$AR(t = this._$AA.nextSibling, e) { @@ -409,11 +409,11 @@ class M { _$AI(t, e = this, n, i) { const r = this.strings; let a = !1; - if (r === void 0) t = A(this, t, e, 0), a = !C(t) || t !== this._$AH && t !== w, a && (this._$AH = t); + if (r === void 0) t = A(this, t, e, 0), a = !S(t) || t !== this._$AH && t !== w, a && (this._$AH = t); else { const p = t; let o, d; - for (t = r[0], o = 0; o < r.length - 1; o++) d = A(this, p[n + o], e, o), d === w && (d = this._$AH[o]), a ||= !C(d) || d !== this._$AH[o], d === c ? t = c : t !== c && (t += (d ?? "") + r[o + 1]), this._$AH[o] = d; + for (t = r[0], o = 0; o < r.length - 1; o++) d = A(this, p[n + o], e, o), d === w && (d = this._$AH[o]), a ||= !S(d) || d !== this._$AH[o], d === c ? t = c : t !== c && (t += (d ?? "") + r[o + 1]), this._$AH[o] = d; } a && !i && this.j(t); } @@ -421,7 +421,7 @@ class M { t === c ? this.element.removeAttribute(this.name) : this.element.setAttribute(this.name, t ?? ""); } } -class Ot extends M { +class Tt extends M { constructor() { super(...arguments), this.type = 3; } @@ -429,7 +429,7 @@ class Ot extends M { this.element[this.name] = t === c ? void 0 : t; } } -class Tt extends M { +class Mt extends M { constructor() { super(...arguments), this.type = 4; } @@ -437,7 +437,7 @@ class Tt extends M { this.element.toggleAttribute(this.name, !!t && t !== c); } } -class zt extends M { +class Ut extends M { constructor(t, e, n, i, r) { super(t, e, n, i, r), this.type = 5; } @@ -450,7 +450,7 @@ class zt extends M { typeof this._$AH == "function" ? this._$AH.call(this.options?.host ?? this.element, t) : this._$AH.handleEvent(t); } } -class Mt { +class Ht { constructor(t, e, n) { this.element = t, this.type = 6, this._$AN = void 0, this._$AM = e, this.options = n; } @@ -461,18 +461,18 @@ class Mt { A(this, t); } } -const Ut = j.litHtmlPolyfillSupport; -Ut?.(N, P), (j.litHtmlVersions ??= []).push("3.3.3"); -const Ht = (s, t, e) => { +const Dt = B.litHtmlPolyfillSupport; +Dt?.(N, P), (B.litHtmlVersions ??= []).push("3.3.3"); +const Rt = (s, t, e) => { const n = e?.renderBefore ?? t; let i = n._$litPart$; if (i === void 0) { const r = e?.renderBefore ?? null; - n._$litPart$ = i = new P(t.insertBefore(S(), r), r, void 0, e ?? {}); + n._$litPart$ = i = new P(t.insertBefore(C(), r), r, void 0, e ?? {}); } return i._$AI(s), i; }; -const Q = globalThis; +const W = globalThis; class v extends x { constructor() { super(...arguments), this.renderOptions = { host: this }, this._$Do = void 0; @@ -483,7 +483,7 @@ class v extends x { } update(t) { const e = this.render(); - this.hasUpdated || (this.renderOptions.isConnected = this.isConnected), super.update(t), this._$Do = Ht(e, this.renderRoot, this.renderOptions); + this.hasUpdated || (this.renderOptions.isConnected = this.isConnected), super.update(t), this._$Do = Rt(e, this.renderRoot, this.renderOptions); } connectedCallback() { super.connectedCallback(), this._$Do?.setConnected(!0); @@ -495,22 +495,48 @@ class v extends x { return w; } } -v._$litElement$ = !0, v.finalized = !0, Q.litElementHydrateSupport?.({ LitElement: v }); -const Dt = Q.litElementPolyfillSupport; -Dt?.({ LitElement: v }); -(Q.litElementVersions ??= []).push("4.2.2"); -const gt = ':host{--nq-p-parchment-top: #f0e4c7;--nq-p-parchment-bottom: #ddcca3;--nq-p-parchment: radial-gradient(ellipse at 25% 10%, rgba(255,255,255,.5), transparent 55%), radial-gradient(ellipse at 85% 90%, rgba(120,86,44,.28), transparent 60%), repeating-linear-gradient(93deg, rgba(150,115,70,.05) 0 2px, transparent 2px 6px), repeating-linear-gradient(2deg, rgba(150,115,70,.04) 0 3px, transparent 3px 7px), linear-gradient(var(--nq-p-parchment-top), var(--nq-p-parchment-bottom));--nq-p-vignette: inset 0 0 200px rgba(80,52,20,.35);--nq-p-card-open: linear-gradient(#fdf7e6, #f2e7c9);--nq-p-card-done: linear-gradient(#f0e8d3, #e6dcc2);--nq-p-card-panel: linear-gradient(#fcf6e6, #f1e6ca);--nq-p-card-border: rgba(120,88,48,.38);--nq-p-panel-border: rgba(92,62,26,.4);--nq-p-card-shadow: 0 3px 0 rgba(120,88,48,.2), inset 0 1px 0 rgba(255,255,255,.7);--nq-p-panel-shadow: 0 6px 0 rgba(92,62,26,.18), inset 0 2px 0 rgba(255,255,255,.7);--nq-p-frame-outer: 2px solid rgba(92,62,26,.45);--nq-p-frame-inner: 1px solid rgba(92,62,26,.28);--nq-p-rule: 2px solid rgba(92,62,26,.35);--nq-p-ink: #2b1f14;--nq-p-ink-secondary: #5c452a;--nq-p-ink-muted: #6f6455;--nq-p-ink-away: #4d4433;--nq-p-ink-late: #8f1526;--nq-brand-blue: #3B3AB8;--nq-brand-purple: #A035CC;--nq-brand-gradient: linear-gradient(135deg, var(--nq-brand-blue), var(--nq-brand-purple));--nq-p-icon-tile: var(--nq-brand-gradient);--nq-p-icon-tile-done: rgba(92,62,26,.16);--nq-p-crest: var(--nq-brand-gradient);--nq-p-crest-away: linear-gradient(135deg, #6b6b7a, #7a7286);--nq-p-seal: radial-gradient(circle at 35% 30%, #a8283a, #6d1322);--nq-p-seal-shadow: 0 4px 10px rgba(60,10,20,.4), inset 0 0 0 4px rgba(255,255,255,.14);--nq-p-seal-size: 78px;--nq-p-dock-bg: rgba(43,31,20,.9);--nq-p-dock-ink: #f7efdb;--nq-p-dock-ink-secondary: #d8c9a6;--nq-p-dock-divider: rgba(233,220,189,.3);--nq-p-dock-height: 84px;--nq-p-font-display: "Cinzel Decorative", Cinzel, serif;--nq-p-font-heading: Cinzel, serif;--nq-p-font-body: Nunito, system-ui, sans-serif;--nq-p-size-hero: 86px;--nq-p-size-wordmark: 66px;--nq-p-size-title: 56px;--nq-p-size-name: 48px;--nq-p-size-section: 32px;--nq-p-size-quest: 30px;--nq-p-size-body: 22px;--nq-p-size-label: 21px;--nq-p-track-label: .14em;--nq-p-track-kicker: .3em;--nq-p-quest-min-height: 116px;--nq-p-quest-gap: 24px;--nq-p-button-height: 72px;--nq-p-button-min-width: 150px;--nq-p-confirm-button: 108px;--nq-p-radius-card: 12px;--nq-p-radius-panel: 20px;--nq-p-radius-pill: 9999px;--nq-p-page-inset: 62px;--nq-p-frame-inset: 26px;--nq-ease-out: cubic-bezier(0, 0, .2, 1);--nq-dur-micro: .12s;--nq-dur-base: .2s;--nq-dur-modal: .35s}'; -function W(s) { +v._$litElement$ = !0, v.finalized = !0, W.litElementHydrateSupport?.({ LitElement: v }); +const It = W.litElementPolyfillSupport; +It?.({ LitElement: v }); +(W.litElementVersions ??= []).push("4.2.2"); +const yt = ':host{--nq-p-parchment-top: #f0e4c7;--nq-p-parchment-bottom: #ddcca3;--nq-p-parchment: radial-gradient(ellipse at 25% 10%, rgba(255,255,255,.5), transparent 55%), radial-gradient(ellipse at 85% 90%, rgba(120,86,44,.28), transparent 60%), repeating-linear-gradient(93deg, rgba(150,115,70,.05) 0 2px, transparent 2px 6px), repeating-linear-gradient(2deg, rgba(150,115,70,.04) 0 3px, transparent 3px 7px), linear-gradient(var(--nq-p-parchment-top), var(--nq-p-parchment-bottom));--nq-p-vignette: inset 0 0 200px rgba(80,52,20,.35);--nq-p-card-open: linear-gradient(#fdf7e6, #f2e7c9);--nq-p-card-done: linear-gradient(#f0e8d3, #e6dcc2);--nq-p-card-panel: linear-gradient(#fcf6e6, #f1e6ca);--nq-p-card-border: rgba(120,88,48,.38);--nq-p-panel-border: rgba(92,62,26,.4);--nq-p-card-shadow: 0 3px 0 rgba(120,88,48,.2), inset 0 1px 0 rgba(255,255,255,.7);--nq-p-panel-shadow: 0 6px 0 rgba(92,62,26,.18), inset 0 2px 0 rgba(255,255,255,.7);--nq-p-frame-outer: 2px solid rgba(92,62,26,.45);--nq-p-frame-inner: 1px solid rgba(92,62,26,.28);--nq-p-rule: 2px solid rgba(92,62,26,.35);--nq-p-ink: #2b1f14;--nq-p-ink-secondary: #5c452a;--nq-p-ink-muted: #6f6455;--nq-p-ink-away: #4d4433;--nq-p-ink-late: #8f1526;--nq-brand-blue: #3B3AB8;--nq-brand-purple: #A035CC;--nq-brand-gradient: linear-gradient(135deg, var(--nq-brand-blue), var(--nq-brand-purple));--nq-p-icon-tile: var(--nq-brand-gradient);--nq-p-icon-tile-done: rgba(92,62,26,.16);--nq-p-crest: var(--nq-brand-gradient);--nq-p-crest-away: linear-gradient(135deg, #6b6b7a, #7a7286);--nq-p-seal: radial-gradient(circle at 35% 30%, #a8283a, #6d1322);--nq-p-seal-shadow: 0 4px 10px rgba(60,10,20,.4), inset 0 0 0 4px rgba(255,255,255,.14);--nq-p-seal-size: 78px;--nq-p-dock-bg: rgba(43,31,20,.9);--nq-p-dock-ink: #f7efdb;--nq-p-dock-ink-secondary: #d8c9a6;--nq-p-dock-divider: rgba(233,220,189,.3);--nq-p-dock-height: 84px;--nq-p-font-display: "Cinzel Decorative", Cinzel, serif;--nq-p-font-heading: Cinzel, serif;--nq-p-font-body: Nunito, system-ui, sans-serif;--nq-p-size-hero: 86px;--nq-p-size-wordmark: 66px;--nq-p-size-title: 56px;--nq-p-size-name: 48px;--nq-p-size-section: 32px;--nq-p-size-quest: 30px;--nq-p-size-body: 22px;--nq-p-size-label: 21px;--nq-p-track-label: .14em;--nq-p-track-kicker: .3em;--nq-p-quest-min-height: 116px;--nq-p-quest-gap: 24px;--nq-p-button-height: 72px;--nq-p-button-min-width: 150px;--nq-p-confirm-button: 108px;--nq-p-radius-card: 12px;--nq-p-radius-panel: 20px;--nq-p-radius-pill: 9999px;--nq-p-page-inset: 62px;--nq-p-frame-inset: 26px;--nq-ease-out: cubic-bezier(0, 0, .2, 1);--nq-dur-micro: .12s;--nq-dur-base: .2s;--nq-dur-modal: .35s}'; +function V(s) { window.customCards = window.customCards ?? [], window.customCards.push(s); } -const Rt = new Intl.DateTimeFormat("en-US", { weekday: "long" }), It = new Intl.DateTimeFormat("en-US", { - month: "short", - day: "numeric" -}), Ft = new Intl.DateTimeFormat("en-US", { - hour: "numeric", - minute: "2-digit", - hour12: !0 -}), Lt = { +const at = /* @__PURE__ */ new Map(); +function L(s, t) { + const e = `${s ?? "local"}|${JSON.stringify(t)}`, n = at.get(e); + if (n) + return n; + let i; + try { + i = new Intl.DateTimeFormat("en-US", { + ...t, + ...s ? { timeZone: s } : {} + }); + } catch { + i = new Intl.DateTimeFormat("en-US", t); + } + return at.set(e, i), i; +} +function Lt(s) { + const t = s?.config; + if (!t || typeof t != "object") + return; + const e = t.time_zone; + return typeof e == "string" && e.trim() ? e.trim() : void 0; +} +function D(s, t) { + return `${L(t, { weekday: "long" }).format(s)}, ${L(t, { month: "short", day: "numeric" }).format(s)}`; +} +function Ft(s, t) { + return L(t, { + hour: "numeric", + minute: "2-digit", + hour12: !0 + }).format(s); +} +const jt = { "clear-night": "Clear", cloudy: "Cloudy", exceptional: "Clear", @@ -526,7 +552,7 @@ const Rt = new Intl.DateTimeFormat("en-US", { weekday: "long" }), It = new Intl. sunny: "Sunny", windy: "Windy", "windy-variant": "Windy" -}, jt = { +}, Bt = { "clear-night": "Clear night skies", cloudy: "Grey skies today", exceptional: "A striking day", @@ -543,25 +569,25 @@ const Rt = new Intl.DateTimeFormat("en-US", { weekday: "long" }), It = new Intl. windy: "A blustery day", "windy-variant": "A blustery day" }; -function rt(s) { +function ot(s) { return typeof s == "string" ? s.trim() : ""; } -function D(s, t) { +function R(s, t) { if (s == null || s === "") return t; const e = typeof s == "number" ? s : Number(s); return Number.isFinite(e) ? e : t; } -function at(s) { +function lt(s) { return Number.isFinite(s) ? Math.min(100, Math.max(0, s)) : 0; } -function ot(s) { +function pt(s) { return Math.max(0, Math.round(s)); } -function Bt(s) { +function Qt(s) { return s.split(/[-_]+/).filter(Boolean).map((t) => t.charAt(0).toUpperCase() + t.slice(1)).join(" "); } -function Qt(s) { +function Wt(s) { if (typeof s != "string") return null; const t = s.split("-"); @@ -573,17 +599,14 @@ function Qt(s) { const r = new Date(e, n - 1, i); return Number.isNaN(r.getTime()) ? null : r; } -function R(s) { - return `${Rt.format(s)}, ${It.format(s)}`; -} -function Wt(s) { - const t = Lt[s]; +function Vt(s) { + const t = jt[s]; return t || s.charAt(0).toUpperCase() + s.slice(1); } -function Vt(s, t) { - return jt[s] ?? t; +function Zt(s, t) { + return Bt[s] ?? t; } -const lt = "polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%)", Kt = "polygon(50% 0, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%)", Zt = yt` +const dt = "polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%)", Kt = "polygon(50% 0, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%)", Jt = _t` * { box-sizing: border-box; } @@ -603,8 +626,8 @@ const lt = "polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%)", Kt = "polygon(50% display: flex; flex-direction: column; align-items: center; - height: 100vh; - padding: 48px 110px 150px; + height: 1080px; + padding: 0; overflow: hidden; background: var(--nq-p-parchment); box-shadow: var(--nq-p-vignette); @@ -629,6 +652,10 @@ const lt = "polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%)", Kt = "polygon(50% } .wordmark-row { + position: absolute; + top: 64px; + left: 0; + right: 0; display: flex; align-items: center; justify-content: center; @@ -676,7 +703,10 @@ const lt = "polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%)", Kt = "polygon(50% } .kicker { - margin: 14px 0 0; + position: absolute; + top: 154px; + left: 0; + right: 0; font-family: var(--nq-p-font-heading); font-size: 22px; font-weight: 600; @@ -688,8 +718,10 @@ const lt = "polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%)", Kt = "polygon(50% } .plates { - width: 100%; - margin-top: 42px; + position: absolute; + top: 262px; + left: 110px; + right: 110px; display: grid; grid-template-columns: repeat(3, 1fr); gap: 56px; @@ -701,9 +733,9 @@ const lt = "polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%)", Kt = "polygon(50% flex-direction: column; align-items: center; justify-content: center; - gap: 20px; - height: 560px; - padding: 36px 28px 32px; + gap: 26px; + height: 600px; + padding: 44px 28px 40px; border: 2px solid var(--nq-p-panel-border); border-radius: var(--nq-p-radius-panel); background: var(--nq-p-card-panel); @@ -737,10 +769,10 @@ const lt = "polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%)", Kt = "polygon(50% .crest { position: relative; flex: none; - width: 168px; - height: 192px; + width: 188px; + height: 214px; padding: 4px; - clip-path: ${$(lt)}; + clip-path: ${$(dt)}; background: rgba(255, 255, 255, 0.22); } @@ -751,7 +783,7 @@ const lt = "polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%)", Kt = "polygon(50% align-items: center; justify-content: center; padding-bottom: 70px; - clip-path: ${$(lt)}; + clip-path: ${$(dt)}; background: var(--nq-p-crest); } @@ -836,7 +868,10 @@ const lt = "polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%)", Kt = "polygon(50% } .hint { - margin: 26px 0 0; + position: absolute; + top: 930px; + left: 0; + right: 0; font-family: var(--nq-p-font-body); font-size: 24px; font-weight: 600; @@ -921,7 +956,7 @@ const lt = "polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%)", Kt = "polygon(50% > -`, Jt = g``, Xt = g` `; -class Xt extends v { +class te extends v { static properties = { hass: { attribute: !1 }, _config: { state: !0 }, _now: { state: !0 } }; - static styles = [$(gt), Zt]; + static styles = [$(yt), Jt]; hass; _config; _now = /* @__PURE__ */ new Date(); @@ -991,7 +1026,7 @@ class Xt extends v { -

The Party · ${R(this._now)}

+

The Party · ${D(this._now, this._timeZone())}

${this._plates().map((t) => this._renderPlate(t))}
@@ -1001,6 +1036,9 @@ class Xt extends v { `; } + _timeZone() { + return Lt(this.hass); + } _state(t) { const n = this.hass?.states?.[t]; return n && typeof n == "object" ? n : null; @@ -1023,7 +1061,7 @@ class Xt extends v { `sensor.nestquest_${t}_completion_pct_today` ), r = this._state( `binary_sensor.nestquest_${t}_present_today` - ), a = e?.attributes ?? {}, p = ot(D(e?.state, 0)), o = ot(D(n?.state, 0)), d = i ? D(i.state, Number.NaN) : Number.NaN, h = Number.isFinite(d) ? at(d) : p > 0 ? at(o / p * 100) : 0; + ), a = e?.attributes ?? {}, p = pt(R(e?.state, 0)), o = pt(R(n?.state, 0)), d = i ? R(i.state, Number.NaN) : Number.NaN, h = Number.isFinite(d) ? lt(d) : p > 0 ? lt(o / p * 100) : 0; let l = !0; const f = a.present; typeof f == "boolean" ? l = f : r && (l = String(r.state ?? "").trim().toLowerCase() === "on"); @@ -1035,8 +1073,8 @@ class Xt extends v { const U = r?.attributes?.child_name; typeof U == "string" && U.trim() && (u = U.trim()); } - u || (u = Bt(t)); - const V = Qt(r?.attributes?.next_present), mt = V ? `Returns ${R(V)}` : null; + u || (u = Qt(t)); + const Z = Wt(r?.attributes?.next_present), bt = Z ? `Returns ${D(Z, this._timeZone())}` : null; return { slug: t, name: u, @@ -1045,7 +1083,7 @@ class Xt extends v { completed: o, due: p, pct: h, - returns: mt + returns: bt }; } _renderPlate(t) { @@ -1082,7 +1120,7 @@ class Xt extends v { ${t.name} - ${Jt} + ${Xt} On travels ${t.returns ?? "Returns —"} @@ -1091,11 +1129,11 @@ class Xt extends v { `; } _openQuestLog(t) { - const e = rt(this._config?.quest_log_path); + const e = ot(this._config?.quest_log_path); e && (window.history.pushState(null, "", `${e.replace(/\/+$/, "")}/${t}`), window.dispatchEvent(new Event("location-changed"))); } _dockWeather() { - const t = rt(this._config?.weather_entity); + const t = ot(this._config?.weather_entity); if (!t) return null; const e = this._state(t); @@ -1132,12 +1170,12 @@ class Xt extends v { if (!t) return g`
- ${R(this._now)} + ${D(this._now, this._timeZone())} - ${Ft.format(this._now)} + ${Ft(this._now, this._timeZone())}
`; - const e = Wt(t.condition), n = Vt(t.condition, e), i = t.temperature === null ? c : g`${Math.round(t.temperature)}°`, r = t.high === null || t.low === null ? null : `${Math.round(t.high)}° / ${Math.round(t.low)}°`; + const e = Vt(t.condition), n = Zt(t.condition, e), i = t.temperature === null ? c : g`${Math.round(t.temperature)}°`, r = t.high === null || t.low === null ? null : `${Math.round(t.high)}° / ${Math.round(t.low)}°`; return g`
${Yt} @@ -1152,18 +1190,18 @@ class Xt extends v { `; } } -customElements.define("nestquest-party-board-card", Xt); -W({ +customElements.define("nestquest-party-board-card", te); +V({ type: "nestquest-party-board-card", name: "NestQuest Party Board", description: "Kids' attract screen for present and away adventurers." }); -class te extends v { +class ee extends v { static properties = { hass: { attribute: !1 }, _config: { state: !0 } }; - static styles = [$(gt)]; + static styles = [$(yt)]; hass; _config; setConfig(t) { @@ -1178,19 +1216,19 @@ class te extends v { return g`
NestQuest Quest Log
`; } } -customElements.define("nestquest-quest-log-card", te); -W({ +customElements.define("nestquest-quest-log-card", ee); +V({ type: "nestquest-quest-log-card", name: "NestQuest Quest Log", description: "One child's daily quests, grouped by window." }); -const ee = ":host{--nq-brand-blue: #3B3AB8;--nq-brand-purple: #A035CC;--nq-brand-gradient: linear-gradient(135deg, var(--nq-brand-blue), var(--nq-brand-purple));--nq-brand-gradient-h: linear-gradient(90deg, var(--nq-brand-blue), var(--nq-brand-purple));--nq-a-page: #F4F4F6;--nq-a-surface: #FFFFFF;--nq-a-surface-subtle: #FAFAFA;--nq-a-selected: #EBEBF8;--nq-a-border: #E4E4EA;--nq-a-divider: #EDEDF1;--nq-a-ink: #18181D;--nq-a-ink-secondary: #52525E;--nq-a-ink-tertiary: #70707E;--nq-a-success: #15803D;--nq-a-danger: #DC2626;--nq-a-danger-strong: #B91C1C;--nq-a-danger-bg: #FEE2E2;--nq-a-warning-ink: #92400E;--nq-a-warning-ink-2: #78350F;--nq-a-warning-bg: #FEF3C7;--nq-a-warning-border: rgba(217,119,6,.35);--nq-a-info-bg: #DBEAFE;--nq-a-info-ink: #1E3A8A;--nq-a-info-icon: #1D4ED8;--nq-a-reversal: #7D28A0;--nq-a-font: Nunito, system-ui, sans-serif;--nq-a-size-screen: 24px;--nq-a-size-hero: 32px;--nq-a-size-card-title: 19px;--nq-a-size-stat: 22px;--nq-a-size-row: 13.5px;--nq-a-size-meta: 11px;--nq-a-size-label: 10px;--nq-a-track-label: .1em;--nq-a-density-page-pad: 14px 16px 92px;--nq-a-density-gap: 10px;--nq-a-density-card-pad: 13px;--nq-a-density-row-pad: 11px 13px;--nq-a-density-radius: 8px;--nq-a-density-shadow: none;--nq-a-tabbar-height: 70px;--nq-a-tap-min: 44px;--nq-a-radius-pill: 9999px;--nq-a-radius-sheet: 20px 20px 0 0;--nq-a-sheet-shadow: 0 -8px 28px rgba(9,9,11,.18);--nq-a-progress-height: 6px;--nq-ease-out: cubic-bezier(0, 0, .2, 1);--nq-dur-micro: .12s;--nq-dur-base: .2s;--nq-dur-modal: .35s}"; -class ne extends v { +const ne = ":host{--nq-brand-blue: #3B3AB8;--nq-brand-purple: #A035CC;--nq-brand-gradient: linear-gradient(135deg, var(--nq-brand-blue), var(--nq-brand-purple));--nq-brand-gradient-h: linear-gradient(90deg, var(--nq-brand-blue), var(--nq-brand-purple));--nq-a-page: #F4F4F6;--nq-a-surface: #FFFFFF;--nq-a-surface-subtle: #FAFAFA;--nq-a-selected: #EBEBF8;--nq-a-border: #E4E4EA;--nq-a-divider: #EDEDF1;--nq-a-ink: #18181D;--nq-a-ink-secondary: #52525E;--nq-a-ink-tertiary: #70707E;--nq-a-success: #15803D;--nq-a-danger: #DC2626;--nq-a-danger-strong: #B91C1C;--nq-a-danger-bg: #FEE2E2;--nq-a-warning-ink: #92400E;--nq-a-warning-ink-2: #78350F;--nq-a-warning-bg: #FEF3C7;--nq-a-warning-border: rgba(217,119,6,.35);--nq-a-info-bg: #DBEAFE;--nq-a-info-ink: #1E3A8A;--nq-a-info-icon: #1D4ED8;--nq-a-reversal: #7D28A0;--nq-a-font: Nunito, system-ui, sans-serif;--nq-a-size-screen: 24px;--nq-a-size-hero: 32px;--nq-a-size-card-title: 19px;--nq-a-size-stat: 22px;--nq-a-size-row: 13.5px;--nq-a-size-meta: 11px;--nq-a-size-label: 10px;--nq-a-track-label: .1em;--nq-a-density-page-pad: 14px 16px 92px;--nq-a-density-gap: 10px;--nq-a-density-card-pad: 13px;--nq-a-density-row-pad: 11px 13px;--nq-a-density-radius: 8px;--nq-a-density-shadow: none;--nq-a-tabbar-height: 70px;--nq-a-tap-min: 44px;--nq-a-radius-pill: 9999px;--nq-a-radius-sheet: 20px 20px 0 0;--nq-a-sheet-shadow: 0 -8px 28px rgba(9,9,11,.18);--nq-a-progress-height: 6px;--nq-ease-out: cubic-bezier(0, 0, .2, 1);--nq-dur-micro: .12s;--nq-dur-base: .2s;--nq-dur-modal: .35s}"; +class se extends v { static properties = { hass: { attribute: !1 }, _config: { state: !0 } }; - static styles = [$(ee)]; + static styles = [$(ne)]; hass; _config; setConfig(t) { @@ -1205,14 +1243,14 @@ class ne extends v { return g`
NestQuest Admin
`; } } -customElements.define("nestquest-admin-card", ne); -W({ +customElements.define("nestquest-admin-card", se); +V({ type: "nestquest-admin-card", name: "NestQuest Admin", description: "Parent phone view for today, tasks, schedule, and history." }); -const se = "/nestquest-static/nestquest-fonts.css"; +const ie = "/nestquest-static/nestquest-fonts.css"; if (!document.querySelector('link[data-nq-fonts=""]')) { const s = document.createElement("link"); - s.rel = "stylesheet", s.href = se, s.dataset.nqFonts = "", document.head.appendChild(s); + s.rel = "stylesheet", s.href = ie, s.dataset.nqFonts = "", document.head.appendChild(s); } diff --git a/frontend/src/cards/party-board.ts b/frontend/src/cards/party-board.ts index 7fd428d..f9e7e5d 100644 --- a/frontend/src/cards/party-board.ts +++ b/frontend/src/cards/party-board.ts @@ -26,16 +26,52 @@ type DockWeather = { low: number | null; }; -const WEEKDAY_FORMAT = new Intl.DateTimeFormat("en-US", { weekday: "long" }); -const MONTH_DAY_FORMAT = new Intl.DateTimeFormat("en-US", { - month: "short", - day: "numeric", -}); -const TIME_FORMAT = new Intl.DateTimeFormat("en-US", { - hour: "numeric", - minute: "2-digit", - hour12: true, -}); +const FORMATTER_CACHE = new Map(); + +function zonedFormatter( + timeZone: string | undefined, + options: Intl.DateTimeFormatOptions +): Intl.DateTimeFormat { + const key = `${timeZone ?? "local"}|${JSON.stringify(options)}`; + const cached = FORMATTER_CACHE.get(key); + if (cached) { + return cached; + } + let formatter: Intl.DateTimeFormat; + try { + formatter = new Intl.DateTimeFormat("en-US", { + ...options, + ...(timeZone ? { timeZone } : {}), + }); + } catch { + formatter = new Intl.DateTimeFormat("en-US", options); + } + FORMATTER_CACHE.set(key, formatter); + return formatter; +} + +function hassTimeZone(hass: HassLike | undefined): string | undefined { + const config = hass?.config; + if (!config || typeof config !== "object") { + return undefined; + } + const timeZone = (config as Record).time_zone; + return typeof timeZone === "string" && timeZone.trim() + ? timeZone.trim() + : undefined; +} + +function formatDay(date: Date, timeZone: string | undefined): string { + return `${zonedFormatter(timeZone, { weekday: "long" }).format(date)}, ${zonedFormatter(timeZone, { month: "short", day: "numeric" }).format(date)}`; +} + +function formatTime(date: Date, timeZone: string | undefined): string { + return zonedFormatter(timeZone, { + hour: "numeric", + minute: "2-digit", + hour12: true, + }).format(date); +} const CONDITION_LABELS: Record = { "clear-night": "Clear", @@ -120,10 +156,6 @@ function parseIsoDate(value: unknown): Date | null { return Number.isNaN(date.getTime()) ? null : date; } -function formatDay(date: Date): string { - return `${WEEKDAY_FORMAT.format(date)}, ${MONTH_DAY_FORMAT.format(date)}`; -} - function conditionLabel(condition: string): string { const known = CONDITION_LABELS[condition]; if (known) { @@ -160,8 +192,8 @@ const boardStyles = css` display: flex; flex-direction: column; align-items: center; - height: 100vh; - padding: 48px 110px 150px; + height: 1080px; + padding: 0; overflow: hidden; background: var(--nq-p-parchment); box-shadow: var(--nq-p-vignette); @@ -186,6 +218,10 @@ const boardStyles = css` } .wordmark-row { + position: absolute; + top: 64px; + left: 0; + right: 0; display: flex; align-items: center; justify-content: center; @@ -233,7 +269,10 @@ const boardStyles = css` } .kicker { - margin: 14px 0 0; + position: absolute; + top: 154px; + left: 0; + right: 0; font-family: var(--nq-p-font-heading); font-size: 22px; font-weight: 600; @@ -245,8 +284,10 @@ const boardStyles = css` } .plates { - width: 100%; - margin-top: 42px; + position: absolute; + top: 262px; + left: 110px; + right: 110px; display: grid; grid-template-columns: repeat(3, 1fr); gap: 56px; @@ -258,9 +299,9 @@ const boardStyles = css` flex-direction: column; align-items: center; justify-content: center; - gap: 20px; - height: 560px; - padding: 36px 28px 32px; + gap: 26px; + height: 600px; + padding: 44px 28px 40px; border: 2px solid var(--nq-p-panel-border); border-radius: var(--nq-p-radius-panel); background: var(--nq-p-card-panel); @@ -294,8 +335,8 @@ const boardStyles = css` .crest { position: relative; flex: none; - width: 168px; - height: 192px; + width: 188px; + height: 214px; padding: 4px; clip-path: ${unsafeCSS(SHIELD_CLIP)}; background: rgba(255, 255, 255, 0.22); @@ -393,7 +434,10 @@ const boardStyles = css` } .hint { - margin: 26px 0 0; + position: absolute; + top: 930px; + left: 0; + right: 0; font-family: var(--nq-p-font-body); font-size: 24px; font-weight: 600; @@ -569,7 +613,7 @@ export class NestQuestPartyBoardCard extends LitElement {
-

The Party · ${formatDay(this._now)}

+

The Party · ${formatDay(this._now, this._timeZone())}

${this._plates().map((plate) => this._renderPlate(plate))}
@@ -580,6 +624,10 @@ export class NestQuestPartyBoardCard extends LitElement { `; } + private _timeZone(): string | undefined { + return hassTimeZone(this.hass); + } + private _state(entityId: string): StateObject | null { const states = this.hass?.states as | Record @@ -655,7 +703,9 @@ export class NestQuestPartyBoardCard extends LitElement { } const returnsDate = parseIsoDate(presence?.attributes?.next_present); - const returns = returnsDate ? `Returns ${formatDay(returnsDate)}` : null; + const returns = returnsDate + ? `Returns ${formatDay(returnsDate, this._timeZone())}` + : null; return { slug, @@ -770,9 +820,9 @@ export class NestQuestPartyBoardCard extends LitElement { if (!weather) { return html`
- ${formatDay(this._now)} + ${formatDay(this._now, this._timeZone())} - ${TIME_FORMAT.format(this._now)} + ${formatTime(this._now, this._timeZone())}
`; }