Files
NestQuest/custom_components/nestquest/www/nestquest-cards.js
T

3387 lines
98 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const B = globalThis, nt = B.ShadowRoot && (B.ShadyCSS === void 0 || B.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, it = /* @__PURE__ */ Symbol(), lt = /* @__PURE__ */ new WeakMap();
let zt = class {
constructor(t, e, n) {
if (this._$cssResult$ = !0, n !== it) throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");
this.cssText = t, this.t = e;
}
get styleSheet() {
let t = this.o;
const e = this.t;
if (nt && t === void 0) {
const n = e !== void 0 && e.length === 1;
n && (t = lt.get(e)), t === void 0 && ((this.o = t = new CSSStyleSheet()).replaceSync(this.cssText), n && lt.set(e, t));
}
return t;
}
toString() {
return this.cssText;
}
};
const b = (i) => new zt(typeof i == "string" ? i : i + "", void 0, it), Pt = (i, ...t) => {
const e = i.length === 1 ? i[0] : t.reduce((n, s, r) => n + ((o) => {
if (o._$cssResult$ === !0) return o.cssText;
if (typeof o == "number") return o;
throw Error("Value passed to 'css' function must be a 'css' function result: " + o + ". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.");
})(s) + i[r + 1], i[0]);
return new zt(e, i, it);
}, Bt = (i, t) => {
if (nt) i.adoptedStyleSheets = t.map((e) => e instanceof CSSStyleSheet ? e : e.styleSheet);
else for (const e of t) {
const n = document.createElement("style"), s = B.litNonce;
s !== void 0 && n.setAttribute("nonce", s), n.textContent = e.cssText, i.appendChild(n);
}
}, dt = nt ? (i) => i : (i) => i instanceof CSSStyleSheet ? ((t) => {
let e = "";
for (const n of t.cssRules) e += n.cssText;
return b(e);
})(i) : i;
const { is: Ft, defineProperty: Qt, getOwnPropertyDescriptor: Wt, getOwnPropertyNames: Zt, getOwnPropertySymbols: Vt, getPrototypeOf: Gt } = Object, W = globalThis, ct = W.trustedTypes, Kt = ct ? ct.emptyScript : "", Jt = W.reactiveElementPolyfillSupport, I = (i, t) => i, et = { toAttribute(i, t) {
switch (t) {
case Boolean:
i = i ? Kt : null;
break;
case Object:
case Array:
i = i == null ? i : JSON.stringify(i);
}
return i;
}, fromAttribute(i, t) {
let e = i;
switch (t) {
case Boolean:
e = i !== null;
break;
case Number:
e = i === null ? null : Number(i);
break;
case Object:
case Array:
try {
e = JSON.parse(i);
} catch {
e = null;
}
}
return e;
} }, It = (i, t) => !Ft(i, t), pt = { attribute: !0, type: String, converter: et, reflect: !1, useDefault: !1, hasChanged: It };
Symbol.metadata ??= /* @__PURE__ */ Symbol("metadata"), W.litPropertyMetadata ??= /* @__PURE__ */ new WeakMap();
let T = class extends HTMLElement {
static addInitializer(t) {
this._$Ei(), (this.l ??= []).push(t);
}
static get observedAttributes() {
return this.finalize(), this._$Eh && [...this._$Eh.keys()];
}
static createProperty(t, e = pt) {
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(), s = this.getPropertyDescriptor(t, n, e);
s !== void 0 && Qt(this.prototype, t, s);
}
}
static getPropertyDescriptor(t, e, n) {
const { get: s, set: r } = Wt(this.prototype, t) ?? { get() {
return this[e];
}, set(o) {
this[e] = o;
} };
return { get: s, set(o) {
const l = s?.call(this);
r?.call(this, o), this.requestUpdate(t, l, n);
}, configurable: !0, enumerable: !0 };
}
static getPropertyOptions(t) {
return this.elementProperties.get(t) ?? pt;
}
static _$Ei() {
if (this.hasOwnProperty(I("elementProperties"))) return;
const t = Gt(this);
t.finalize(), t.l !== void 0 && (this.l = [...t.l]), this.elementProperties = new Map(t.elementProperties);
}
static finalize() {
if (this.hasOwnProperty(I("finalized"))) return;
if (this.finalized = !0, this._$Ei(), this.hasOwnProperty(I("properties"))) {
const e = this.properties, n = [...Zt(e), ...Vt(e)];
for (const s of n) this.createProperty(s, e[s]);
}
const t = this[Symbol.metadata];
if (t !== null) {
const e = litPropertyMetadata.get(t);
if (e !== void 0) for (const [n, s] of e) this.elementProperties.set(n, s);
}
this._$Eh = /* @__PURE__ */ new Map();
for (const [e, n] of this.elementProperties) {
const s = this._$Eu(e, n);
s !== void 0 && this._$Eh.set(s, e);
}
this.elementStyles = this.finalizeStyles(this.styles);
}
static finalizeStyles(t) {
const e = [];
if (Array.isArray(t)) {
const n = new Set(t.flat(1 / 0).reverse());
for (const s of n) e.unshift(dt(s));
} else t !== void 0 && e.push(dt(t));
return e;
}
static _$Eu(t, e) {
const n = e.attribute;
return n === !1 ? void 0 : typeof n == "string" ? n : typeof t == "string" ? t.toLowerCase() : void 0;
}
constructor() {
super(), this._$Ep = void 0, this.isUpdatePending = !1, this.hasUpdated = !1, this._$Em = null, this._$Ev();
}
_$Ev() {
this._$ES = new Promise((t) => this.enableUpdating = t), this._$AL = /* @__PURE__ */ new Map(), this._$E_(), this.requestUpdate(), this.constructor.l?.forEach((t) => t(this));
}
addController(t) {
(this._$EO ??= /* @__PURE__ */ new Set()).add(t), this.renderRoot !== void 0 && this.isConnected && t.hostConnected?.();
}
removeController(t) {
this._$EO?.delete(t);
}
_$E_() {
const t = /* @__PURE__ */ new Map(), e = this.constructor.elementProperties;
for (const n of e.keys()) this.hasOwnProperty(n) && (t.set(n, this[n]), delete this[n]);
t.size > 0 && (this._$Ep = t);
}
createRenderRoot() {
const t = this.shadowRoot ?? this.attachShadow(this.constructor.shadowRootOptions);
return Bt(t, this.constructor.elementStyles), t;
}
connectedCallback() {
this.renderRoot ??= this.createRenderRoot(), this.enableUpdating(!0), this._$EO?.forEach((t) => t.hostConnected?.());
}
enableUpdating(t) {
}
disconnectedCallback() {
this._$EO?.forEach((t) => t.hostDisconnected?.());
}
attributeChangedCallback(t, e, n) {
this._$AK(t, n);
}
_$ET(t, e) {
const n = this.constructor.elementProperties.get(t), s = this.constructor._$Eu(t, n);
if (s !== void 0 && n.reflect === !0) {
const r = (n.converter?.toAttribute !== void 0 ? n.converter : et).toAttribute(e, n.type);
this._$Em = t, r == null ? this.removeAttribute(s) : this.setAttribute(s, r), this._$Em = null;
}
}
_$AK(t, e) {
const n = this.constructor, s = n._$Eh.get(t);
if (s !== void 0 && this._$Em !== s) {
const r = n.getPropertyOptions(s), o = typeof r.converter == "function" ? { fromAttribute: r.converter } : r.converter?.fromAttribute !== void 0 ? r.converter : et;
this._$Em = s;
const l = o.fromAttribute(e, r.type);
this[s] = l ?? this._$Ej?.get(s) ?? l, this._$Em = null;
}
}
requestUpdate(t, e, n, s = !1, r) {
if (t !== void 0) {
const o = this.constructor;
if (s === !1 && (r = this[t]), n ??= o.getPropertyOptions(t), !((n.hasChanged ?? It)(r, e) || n.useDefault && n.reflect && r === this._$Ej?.get(t) && !this.hasAttribute(o._$Eu(t, n)))) return;
this.C(t, e, n);
}
this.isUpdatePending === !1 && (this._$ES = this._$EP());
}
C(t, e, { useDefault: n, reflect: s, wrapped: r }, o) {
n && !(this._$Ej ??= /* @__PURE__ */ new Map()).has(t) && (this._$Ej.set(t, o ?? e ?? this[t]), r !== !0 || o !== void 0) || (this._$AL.has(t) || (this.hasUpdated || n || (e = void 0), this._$AL.set(t, e)), s === !0 && this._$Em !== t && (this._$Eq ??= /* @__PURE__ */ new Set()).add(t));
}
async _$EP() {
this.isUpdatePending = !0;
try {
await this._$ES;
} catch (e) {
Promise.reject(e);
}
const t = this.scheduleUpdate();
return t != null && await t, !this.isUpdatePending;
}
scheduleUpdate() {
return this.performUpdate();
}
performUpdate() {
if (!this.isUpdatePending) return;
if (!this.hasUpdated) {
if (this.renderRoot ??= this.createRenderRoot(), this._$Ep) {
for (const [s, r] of this._$Ep) this[s] = r;
this._$Ep = void 0;
}
const n = this.constructor.elementProperties;
if (n.size > 0) for (const [s, r] of n) {
const { wrapped: o } = r, l = this[s];
o !== !0 || this._$AL.has(s) || l === void 0 || this.C(s, void 0, r, l);
}
}
let t = !1;
const e = this._$AL;
try {
t = this.shouldUpdate(e), t ? (this.willUpdate(e), this._$EO?.forEach((n) => n.hostUpdate?.()), this.update(e)) : this._$EM();
} catch (n) {
throw t = !1, this._$EM(), n;
}
t && this._$AE(e);
}
willUpdate(t) {
}
_$AE(t) {
this._$EO?.forEach((e) => e.hostUpdated?.()), this.hasUpdated || (this.hasUpdated = !0, this.firstUpdated(t)), this.updated(t);
}
_$EM() {
this._$AL = /* @__PURE__ */ new Map(), this.isUpdatePending = !1;
}
get updateComplete() {
return this.getUpdateComplete();
}
getUpdateComplete() {
return this._$ES;
}
shouldUpdate(t) {
return !0;
}
update(t) {
this._$Eq &&= this._$Eq.forEach((e) => this._$ET(e, this[e])), this._$EM();
}
updated(t) {
}
firstUpdated(t) {
}
};
T.elementStyles = [], T.shadowRootOptions = { mode: "open" }, T[I("elementProperties")] = /* @__PURE__ */ new Map(), T[I("finalized")] = /* @__PURE__ */ new Map(), Jt?.({ ReactiveElement: T }), (W.reactiveElementVersions ??= []).push("2.1.2");
const st = globalThis, ht = (i) => i, F = st.trustedTypes, ut = F ? F.createPolicy("lit-html", { createHTML: (i) => i }) : void 0, Dt = "$lit$", w = `lit$${Math.random().toFixed(9).slice(2)}$`, Ut = "?" + w, Xt = `<${Ut}>`, S = document, D = () => S.createComment(""), U = (i) => i === null || typeof i != "object" && typeof i != "function", rt = Array.isArray, Yt = (i) => rt(i) || typeof i?.[Symbol.iterator] == "function", G = `[
\f\r]`, z = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g, ft = /-->/g, mt = />/g, q = RegExp(`>|${G}(?:([^\\s"'>=/]+)(${G}*=${G}*(?:[^
\f\r"'\`<>=]|("|')|))|$)`, "g"), gt = /'/g, _t = /"/g, jt = /^(?:script|style|textarea|title)$/i, te = (i) => (t, ...e) => ({ _$litType$: i, strings: t, values: e }), c = te(1), N = /* @__PURE__ */ Symbol.for("lit-noChange"), g = /* @__PURE__ */ Symbol.for("lit-nothing"), yt = /* @__PURE__ */ new WeakMap(), C = S.createTreeWalker(S, 129);
function Ht(i, t) {
if (!rt(i) || !i.hasOwnProperty("raw")) throw Error("invalid template strings array");
return ut !== void 0 ? ut.createHTML(t) : t;
}
const ee = (i, t) => {
const e = i.length - 1, n = [];
let s, r = t === 2 ? "<svg>" : t === 3 ? "<math>" : "", o = z;
for (let l = 0; l < e; l++) {
const a = i[l];
let p, u, d = -1, f = 0;
for (; f < a.length && (o.lastIndex = f, u = o.exec(a), u !== null); ) f = o.lastIndex, o === z ? u[1] === "!--" ? o = ft : u[1] !== void 0 ? o = mt : u[2] !== void 0 ? (jt.test(u[2]) && (s = RegExp("</" + u[2], "g")), o = q) : u[3] !== void 0 && (o = q) : o === q ? u[0] === ">" ? (o = s ?? z, d = -1) : u[1] === void 0 ? d = -2 : (d = o.lastIndex - u[2].length, p = u[1], o = u[3] === void 0 ? q : u[3] === '"' ? _t : gt) : o === _t || o === gt ? o = q : o === ft || o === mt ? o = z : (o = q, s = void 0);
const h = o === q && i[l + 1].startsWith("/>") ? " " : "";
r += o === z ? a + Xt : d >= 0 ? (n.push(p), a.slice(0, d) + Dt + a.slice(d) + w + h) : a + w + (d === -2 ? l : h);
}
return [Ht(i, r + (i[e] || "<?>") + (t === 2 ? "</svg>" : t === 3 ? "</math>" : "")), n];
};
class j {
constructor({ strings: t, _$litType$: e }, n) {
let s;
this.parts = [];
let r = 0, o = 0;
const l = t.length - 1, a = this.parts, [p, u] = ee(t, e);
if (this.el = j.createElement(p, n), C.currentNode = this.el.content, e === 2 || e === 3) {
const d = this.el.content.firstChild;
d.replaceWith(...d.childNodes);
}
for (; (s = C.nextNode()) !== null && a.length < l; ) {
if (s.nodeType === 1) {
if (s.hasAttributes()) for (const d of s.getAttributeNames()) if (d.endsWith(Dt)) {
const f = u[o++], h = s.getAttribute(d).split(w), m = /([.?@])?(.*)/.exec(f);
a.push({ type: 1, index: r, name: m[2], strings: h, ctor: m[1] === "." ? ie : m[1] === "?" ? se : m[1] === "@" ? re : Z }), s.removeAttribute(d);
} else d.startsWith(w) && (a.push({ type: 6, index: r }), s.removeAttribute(d));
if (jt.test(s.tagName)) {
const d = s.textContent.split(w), f = d.length - 1;
if (f > 0) {
s.textContent = F ? F.emptyScript : "";
for (let h = 0; h < f; h++) s.append(d[h], D()), C.nextNode(), a.push({ type: 2, index: ++r });
s.append(d[f], D());
}
}
} else if (s.nodeType === 8) if (s.data === Ut) a.push({ type: 2, index: r });
else {
let d = -1;
for (; (d = s.data.indexOf(w, d + 1)) !== -1; ) a.push({ type: 7, index: r }), d += w.length - 1;
}
r++;
}
}
static createElement(t, e) {
const n = S.createElement("template");
return n.innerHTML = t, n;
}
}
function E(i, t, e = i, n) {
if (t === N) return t;
let s = n !== void 0 ? e._$Co?.[n] : e._$Cl;
const r = U(t) ? void 0 : t._$litDirective$;
return s?.constructor !== r && (s?._$AO?.(!1), r === void 0 ? s = void 0 : (s = new r(i), s._$AT(i, e, n)), n !== void 0 ? (e._$Co ??= [])[n] = s : e._$Cl = s), s !== void 0 && (t = E(i, s._$AS(i, t.values), s, n)), t;
}
class ne {
constructor(t, e) {
this._$AV = [], this._$AN = void 0, this._$AD = t, this._$AM = e;
}
get parentNode() {
return this._$AM.parentNode;
}
get _$AU() {
return this._$AM._$AU;
}
u(t) {
const { el: { content: e }, parts: n } = this._$AD, s = (t?.creationScope ?? S).importNode(e, !0);
C.currentNode = s;
let r = C.nextNode(), o = 0, l = 0, a = n[0];
for (; a !== void 0; ) {
if (o === a.index) {
let p;
a.type === 2 ? p = new O(r, r.nextSibling, this, t) : a.type === 1 ? p = new a.ctor(r, a.name, a.strings, this, t) : a.type === 6 && (p = new oe(r, this, t)), this._$AV.push(p), a = n[++l];
}
o !== a?.index && (r = C.nextNode(), o++);
}
return C.currentNode = S, s;
}
p(t) {
let e = 0;
for (const n of this._$AV) n !== void 0 && (n.strings !== void 0 ? (n._$AI(t, n, e), e += n.strings.length - 2) : n._$AI(t[e])), e++;
}
}
class O {
get _$AU() {
return this._$AM?._$AU ?? this._$Cv;
}
constructor(t, e, n, s) {
this.type = 2, this._$AH = g, this._$AN = void 0, this._$AA = t, this._$AB = e, this._$AM = n, this.options = s, this._$Cv = s?.isConnected ?? !0;
}
get parentNode() {
let t = this._$AA.parentNode;
const e = this._$AM;
return e !== void 0 && t?.nodeType === 11 && (t = e.parentNode), t;
}
get startNode() {
return this._$AA;
}
get endNode() {
return this._$AB;
}
_$AI(t, e = this) {
t = E(this, t, e), U(t) ? t === g || t == null || t === "" ? (this._$AH !== g && this._$AR(), this._$AH = g) : t !== this._$AH && t !== N && this._(t) : t._$litType$ !== void 0 ? this.$(t) : t.nodeType !== void 0 ? this.T(t) : Yt(t) ? this.k(t) : this._(t);
}
O(t) {
return this._$AA.parentNode.insertBefore(t, this._$AB);
}
T(t) {
this._$AH !== t && (this._$AR(), this._$AH = this.O(t));
}
_(t) {
this._$AH !== g && U(this._$AH) ? this._$AA.nextSibling.data = t : this.T(S.createTextNode(t)), this._$AH = t;
}
$(t) {
const { values: e, _$litType$: n } = t, s = typeof n == "number" ? this._$AC(t) : (n.el === void 0 && (n.el = j.createElement(Ht(n.h, n.h[0]), this.options)), n);
if (this._$AH?._$AD === s) this._$AH.p(e);
else {
const r = new ne(s, this), o = r.u(this.options);
r.p(e), this.T(o), this._$AH = r;
}
}
_$AC(t) {
let e = yt.get(t.strings);
return e === void 0 && yt.set(t.strings, e = new j(t)), e;
}
k(t) {
rt(this._$AH) || (this._$AH = [], this._$AR());
const e = this._$AH;
let n, s = 0;
for (const r of t) s === e.length ? e.push(n = new O(this.O(D()), this.O(D()), this, this.options)) : n = e[s], n._$AI(r), s++;
s < e.length && (this._$AR(n && n._$AB.nextSibling, s), e.length = s);
}
_$AR(t = this._$AA.nextSibling, e) {
for (this._$AP?.(!1, !0, e); t !== this._$AB; ) {
const n = ht(t).nextSibling;
ht(t).remove(), t = n;
}
}
setConnected(t) {
this._$AM === void 0 && (this._$Cv = t, this._$AP?.(t));
}
}
class Z {
get tagName() {
return this.element.tagName;
}
get _$AU() {
return this._$AM._$AU;
}
constructor(t, e, n, s, r) {
this.type = 1, this._$AH = g, this._$AN = void 0, this.element = t, this.name = e, this._$AM = s, this.options = r, n.length > 2 || n[0] !== "" || n[1] !== "" ? (this._$AH = Array(n.length - 1).fill(new String()), this.strings = n) : this._$AH = g;
}
_$AI(t, e = this, n, s) {
const r = this.strings;
let o = !1;
if (r === void 0) t = E(this, t, e, 0), o = !U(t) || t !== this._$AH && t !== N, o && (this._$AH = t);
else {
const l = t;
let a, p;
for (t = r[0], a = 0; a < r.length - 1; a++) p = E(this, l[n + a], e, a), p === N && (p = this._$AH[a]), o ||= !U(p) || p !== this._$AH[a], p === g ? t = g : t !== g && (t += (p ?? "") + r[a + 1]), this._$AH[a] = p;
}
o && !s && this.j(t);
}
j(t) {
t === g ? this.element.removeAttribute(this.name) : this.element.setAttribute(this.name, t ?? "");
}
}
class ie extends Z {
constructor() {
super(...arguments), this.type = 3;
}
j(t) {
this.element[this.name] = t === g ? void 0 : t;
}
}
class se extends Z {
constructor() {
super(...arguments), this.type = 4;
}
j(t) {
this.element.toggleAttribute(this.name, !!t && t !== g);
}
}
class re extends Z {
constructor(t, e, n, s, r) {
super(t, e, n, s, r), this.type = 5;
}
_$AI(t, e = this) {
if ((t = E(this, t, e, 0) ?? g) === N) return;
const n = this._$AH, s = t === g && n !== g || t.capture !== n.capture || t.once !== n.once || t.passive !== n.passive, r = t !== g && (n === g || s);
s && 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);
}
}
class oe {
constructor(t, e, n) {
this.element = t, this.type = 6, this._$AN = void 0, this._$AM = e, this.options = n;
}
get _$AU() {
return this._$AM._$AU;
}
_$AI(t) {
E(this, t);
}
}
const ae = { I: O }, le = st.litHtmlPolyfillSupport;
le?.(j, O), (st.litHtmlVersions ??= []).push("3.3.3");
const de = (i, t, e) => {
const n = e?.renderBefore ?? t;
let s = n._$litPart$;
if (s === void 0) {
const r = e?.renderBefore ?? null;
n._$litPart$ = s = new O(t.insertBefore(D(), r), r, void 0, e ?? {});
}
return s._$AI(i), s;
};
const ot = globalThis;
let A = class extends T {
constructor() {
super(...arguments), this.renderOptions = { host: this }, this._$Do = void 0;
}
createRenderRoot() {
const t = super.createRenderRoot();
return this.renderOptions.renderBefore ??= t.firstChild, t;
}
update(t) {
const e = this.render();
this.hasUpdated || (this.renderOptions.isConnected = this.isConnected), super.update(t), this._$Do = de(e, this.renderRoot, this.renderOptions);
}
connectedCallback() {
super.connectedCallback(), this._$Do?.setConnected(!0);
}
disconnectedCallback() {
super.disconnectedCallback(), this._$Do?.setConnected(!1);
}
render() {
return N;
}
};
A._$litElement$ = !0, A.finalized = !0, ot.litElementHydrateSupport?.({ LitElement: A });
const ce = ot.litElementPolyfillSupport;
ce?.({ LitElement: A });
(ot.litElementVersions ??= []).push("4.2.2");
const Lt = ':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-quest-pad: 22px;--nq-p-tile-size: 64px;--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 at(i) {
window.customCards = window.customCards ?? [], window.customCards.push(i);
}
const vt = /* @__PURE__ */ new Map();
function Q(i, t) {
const e = `${i ?? "local"}|${JSON.stringify(t)}`, n = vt.get(e);
if (n)
return n;
let s;
try {
s = new Intl.DateTimeFormat("en-US", {
...t,
...i ? { timeZone: i } : {}
});
} catch {
s = new Intl.DateTimeFormat("en-US", t);
}
return vt.set(e, s), s;
}
function pe(i) {
const t = i?.config;
if (!t || typeof t != "object")
return;
const e = t.time_zone;
return typeof e == "string" && e.trim() ? e.trim() : void 0;
}
function K(i, t) {
return `${Q(t, { weekday: "long" }).format(i)}, ${Q(t, { month: "short", day: "numeric" }).format(i)}`;
}
function he(i, t) {
return Q(t, {
hour: "numeric",
minute: "2-digit",
hour12: !0
}).format(i);
}
const ue = {
"clear-night": "Clear",
cloudy: "Cloudy",
exceptional: "Clear",
fog: "Foggy",
hail: "Hail",
lightning: "Storms",
"lightning-rimmed": "Storms",
partlycloudy: "Partly cloudy",
pouring: "Heavy rain",
rainy: "Rain",
snowy: "Snow",
"snowy-rainy": "Sleet",
sunny: "Sunny",
windy: "Windy",
"windy-variant": "Windy"
}, fe = {
"clear-night": "Clear night skies",
cloudy: "Grey skies today",
exceptional: "A striking day",
fog: "Mist on the road",
hail: "Ice from the sky",
lightning: "Storms may roll in",
"lightning-rimmed": "Storms may roll in",
partlycloudy: "Sun between clouds",
pouring: "Heavy rain outside",
rainy: "Rain on the walls",
snowy: "Snow on the peaks",
"snowy-rainy": "Sleet may fall",
sunny: "Clear skies today",
windy: "A blustery day",
"windy-variant": "A blustery day"
};
function bt(i) {
return typeof i == "string" ? i.trim() : "";
}
function J(i, t) {
if (i == null || i === "")
return t;
const e = typeof i == "number" ? i : Number(i);
return Number.isFinite(e) ? e : t;
}
function xt(i) {
return Number.isFinite(i) ? Math.min(100, Math.max(0, i)) : 0;
}
function $t(i) {
return Math.max(0, Math.round(i));
}
function me(i) {
return i.split(/[-_]+/).filter(Boolean).map((t) => t.charAt(0).toUpperCase() + t.slice(1)).join(" ");
}
function ge(i, t) {
const e = Q(t, {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hourCycle: "h23"
}).formatToParts(new Date(i)), n = (r) => {
const o = e.find((l) => l.type === r);
return o ? Number(o.value) : Number.NaN;
}, s = Date.UTC(
n("year"),
n("month") - 1,
n("day"),
n("hour"),
n("minute"),
n("second")
);
return Number.isFinite(s) ? s - i : 0;
}
function _e(i, t) {
if (typeof i != "string")
return null;
const e = i.split("-");
if (e.length !== 3)
return null;
const [n, s, r] = e.map((a) => Number(a));
if (!n || !s || !r)
return null;
if (!t) {
const a = new Date(n, s - 1, r);
return Number.isNaN(a.getTime()) ? null : a;
}
const o = Date.UTC(n, s - 1, r), l = new Date(o - ge(o, t));
return Number.isNaN(l.getTime()) ? null : l;
}
function ye(i) {
const t = ue[i];
return t || i.charAt(0).toUpperCase() + i.slice(1);
}
function ve(i, t) {
return fe[i] ?? t;
}
const wt = "polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%)", be = "polygon(50% 0, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%)", xe = Pt`
* {
box-sizing: border-box;
}
:host {
display: block;
height: 100%;
--nq-brand-gradient-h: linear-gradient(
90deg,
var(--nq-brand-blue),
var(--nq-brand-purple)
);
}
.board {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
height: 1080px;
padding: 0;
overflow: hidden;
background: var(--nq-p-parchment);
box-shadow: var(--nq-p-vignette);
font-family: var(--nq-p-font-body);
}
.frame {
position: absolute;
pointer-events: none;
}
.frame-outer {
inset: 26px;
border: var(--nq-p-frame-outer);
border-radius: 14px;
}
.frame-inner {
inset: 36px;
border: var(--nq-p-frame-inner);
border-radius: 8px;
}
.wordmark-row {
position: absolute;
top: 64px;
left: 0;
right: 0;
display: flex;
align-items: center;
justify-content: center;
gap: 26px;
width: 100%;
}
.rule {
flex: 1 1 0;
max-width: 340px;
height: 2px;
border-radius: 1px;
background: rgba(92, 62, 26, 0.35);
}
.d20 {
flex: none;
display: inline-flex;
align-items: center;
justify-content: center;
width: 34px;
height: 34px;
clip-path: ${b(be)};
background: var(--nq-brand-gradient);
}
.d20-numeral {
font-family: var(--nq-p-font-heading);
font-size: 15px;
font-weight: 700;
line-height: 1;
color: #ffffff;
}
.wordmark {
font-family: var(--nq-p-font-display);
font-size: var(--nq-p-size-wordmark);
font-weight: 900;
line-height: 1.15;
background: var(--nq-brand-gradient);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
-webkit-text-fill-color: transparent;
}
.kicker {
position: absolute;
top: 154px;
left: 0;
right: 0;
font-family: var(--nq-p-font-heading);
font-size: 22px;
font-weight: 600;
letter-spacing: var(--nq-p-track-kicker);
text-transform: uppercase;
line-height: 1.2;
text-align: center;
color: var(--nq-p-ink-secondary);
}
.plates {
position: absolute;
top: 262px;
left: 110px;
right: 110px;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 56px;
}
.plate {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
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);
box-shadow: var(--nq-p-panel-shadow);
font: inherit;
}
button.plate {
width: 100%;
color: inherit;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
transition: transform var(--nq-dur-micro) var(--nq-ease-out);
}
button.plate:active {
transform: scale(0.98);
}
button.plate:focus-visible {
outline: 3px solid var(--nq-brand-purple);
outline-offset: 4px;
}
.plate.away {
border: 2px dashed var(--nq-p-panel-border);
background: linear-gradient(#f2ecdd, #e6dcc6);
box-shadow: none;
}
.crest {
position: relative;
flex: none;
width: 188px;
height: 214px;
padding: 4px;
clip-path: ${b(wt)};
background: rgba(255, 255, 255, 0.22);
}
.crest-face {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 70px;
clip-path: ${b(wt)};
background: var(--nq-p-crest);
}
.crest.away .crest-face {
background: var(--nq-p-crest-away);
}
.crest .initial {
font-family: var(--nq-p-font-display);
font-size: 82px;
font-weight: 700;
line-height: 1;
color: #ffffff;
}
.name {
font-family: var(--nq-p-font-heading);
font-size: var(--nq-p-size-name);
font-weight: 700;
letter-spacing: 0.02em;
line-height: 1.1;
color: var(--nq-p-ink);
}
.plate.away .name {
color: var(--nq-p-ink-away);
}
.pill {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 8px 20px;
border-radius: var(--nq-p-radius-pill);
background: rgba(22, 120, 60, 0.35);
font-family: var(--nq-p-font-heading);
font-size: 22px;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
line-height: 1;
color: #146b36;
}
.pill svg {
width: 22px;
height: 22px;
color: #157a3c;
}
.pill.away {
background: rgba(92, 62, 26, 0.16);
color: var(--nq-p-ink-away);
}
.pill.away svg {
color: var(--nq-p-ink-away);
}
.progress-line {
font-family: var(--nq-p-font-heading);
font-size: 24px;
font-weight: 700;
line-height: 1.2;
color: var(--nq-p-ink-secondary);
}
.bar {
width: 100%;
height: 16px;
border: 1px solid rgba(92, 62, 26, 0.35);
border-radius: var(--nq-p-radius-pill);
background: rgba(92, 62, 26, 0.18);
overflow: hidden;
}
.bar .fill {
display: block;
height: 100%;
border-radius: inherit;
background: var(--nq-brand-gradient-h);
}
.hint {
position: absolute;
top: 930px;
left: 0;
right: 0;
font-family: var(--nq-p-font-body);
font-size: 24px;
font-weight: 600;
line-height: 1.4;
text-align: center;
color: var(--nq-p-ink-secondary);
}
.plate.unknown {
border: 2px dashed var(--nq-p-panel-border);
background: linear-gradient(#f2ecdd, #e6dcc6);
box-shadow: none;
}
.notice-wrap {
position: absolute;
top: 262px;
left: 110px;
right: 110px;
bottom: 190px;
display: flex;
align-items: center;
justify-content: center;
}
.notice {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
width: 100%;
max-width: 980px;
padding: 56px 64px;
border: 2px solid var(--nq-p-panel-border);
border-radius: var(--nq-p-radius-panel);
background: var(--nq-p-card-panel);
box-shadow: var(--nq-p-panel-shadow);
text-align: center;
}
.notice svg {
width: 46px;
height: 46px;
color: var(--nq-p-ink-secondary);
}
.notice-headline {
font-family: var(--nq-p-font-heading);
font-size: 40px;
font-weight: 700;
line-height: 1.15;
color: var(--nq-p-ink);
}
.notice-body {
margin: 0;
font-family: var(--nq-p-font-body);
font-size: 24px;
font-weight: 600;
line-height: 1.4;
color: var(--nq-p-ink-secondary);
}
.spacer {
flex: 1 1 0;
}
.dock {
position: absolute;
left: 46px;
right: 46px;
bottom: 46px;
height: var(--nq-p-dock-height);
display: flex;
align-items: center;
justify-content: center;
gap: 24px;
padding: 0 40px;
border-radius: 14px;
background: var(--nq-p-dock-bg);
pointer-events: none;
user-select: none;
color: var(--nq-p-dock-ink);
}
.dock svg {
flex: none;
width: 40px;
height: 40px;
color: var(--nq-p-dock-ink);
}
.dock .divider {
flex: none;
width: 1px;
height: 40px;
background: var(--nq-p-dock-divider);
}
.dock .temp {
font-family: var(--nq-p-font-body);
font-size: 38px;
font-weight: 800;
line-height: 1;
}
.dock .condition {
font-family: var(--nq-p-font-body);
font-size: 26px;
font-weight: 600;
line-height: 1.2;
color: var(--nq-p-dock-ink-secondary);
}
.dock .date {
font-family: var(--nq-p-font-body);
font-size: 26px;
font-weight: 600;
line-height: 1.2;
color: var(--nq-p-dock-ink-secondary);
}
.dock .clock {
font-family: var(--nq-p-font-body);
font-size: 38px;
font-weight: 800;
line-height: 1;
}
`, $e = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<circle cx="12" cy="12" r="10"></circle>
<path d="m9 12 2 2 4-4"></path>
</svg>`, we = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M3.5 21 12 3.5 20.5 21"></path>
<path d="M9 21l3-8 3 8"></path>
</svg>`, qe = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<circle cx="12" cy="12" r="10"></circle>
<polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon>
</svg>`, ke = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"></path>
<path d="M12 9v4"></path>
<path d="M12 17h.01"></path>
</svg>`, Ce = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z"></path>
<path d="m2 2 20 20"></path>
</svg>`, qt = {
icon: ke,
headline: "NestQuest is not set up yet",
body: "A parent needs to finish setting up NestQuest before the party can gather."
}, Ae = {
icon: Ce,
headline: "The records cannot be reached",
body: "The party's records are quiet right now. NestQuest will return shortly."
}, Se = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M12 2v2"></path>
<path d="m4.93 4.93 1.41 1.41"></path>
<path d="M20 12h2"></path>
<path d="m19.07 4.93-1.41 1.41"></path>
<path d="M15.947 12.65a4 4 0 0 0-5.925-4.128"></path>
<path d="M13 22H7a5 5 0 1 1 4.9-6H13a3 3 0 0 1 0 6Z"></path>
</svg>`;
class Ne extends A {
static properties = {
hass: { attribute: !1 },
_config: { state: !0 },
_now: { state: !0 }
};
static styles = [b(Lt), xe];
hass;
_config;
_now = /* @__PURE__ */ new Date();
_clockTimer;
setConfig(t) {
if (!t || typeof t != "object")
throw new Error("Invalid configuration");
this._config = t;
}
getCardSize() {
return 22;
}
connectedCallback() {
super.connectedCallback(), this._now = /* @__PURE__ */ new Date(), this._clockTimer = window.setInterval(() => {
this._now = /* @__PURE__ */ new Date();
}, 1e3);
}
disconnectedCallback() {
this._clockTimer !== void 0 && (window.clearInterval(this._clockTimer), this._clockTimer = void 0), super.disconnectedCallback();
}
render() {
const t = this._boardNotice();
return c`
<div class="board">
<div class="frame frame-outer"></div>
<div class="frame frame-inner"></div>
<div class="wordmark-row">
<span class="rule"></span>
<span class="d20" aria-hidden="true">
<span class="d20-numeral">20</span>
</span>
<span class="wordmark">NestQuest</span>
<span class="d20" aria-hidden="true">
<span class="d20-numeral">20</span>
</span>
<span class="rule"></span>
</div>
<p class="kicker">The Party · ${K(this._now, this._timeZone())}</p>
${t ? this._renderNotice(t) : c`
<div class="plates">
${this._plates().map((e) => this._renderPlate(e))}
</div>
<p class="hint">Tap your crest to open your Quest Log</p>
`}
<div class="spacer"></div>
${this._renderDock()}
</div>
`;
}
_timeZone() {
return pe(this.hass);
}
_state(t) {
const n = this.hass?.states?.[t];
return n && typeof n == "object" ? n : null;
}
_childSlugs() {
if (!this._config)
return [];
const t = this._config.child_order;
return Array.isArray(t) ? t.filter(
(e) => typeof e == "string" && e.trim().length > 0
).map((e) => e.trim()) : [];
}
_plates() {
return this._childSlugs().map((t) => this._plate(t));
}
_plate(t) {
const e = this._dueSensorResolution(t), n = this._state(`sensor.nestquest_${t}_quests_due_today`), s = this._state(
`sensor.nestquest_${t}_quests_completed_today`
), r = this._state(
`sensor.nestquest_${t}_completion_pct_today`
), o = this._state(
`binary_sensor.nestquest_${t}_present_today`
), l = n?.attributes ?? {}, a = $t(J(n?.state, 0)), p = $t(J(s?.state, 0)), u = r ? J(r.state, Number.NaN) : Number.NaN, d = Number.isFinite(u) ? xt(u) : a > 0 ? xt(p / a * 100) : 0;
let f = !0;
const h = l.present;
typeof h == "boolean" ? f = h : o && (f = String(o.state ?? "").trim().toLowerCase() === "on");
let m = "";
const _ = l.child_name;
if (typeof _ == "string" && _.trim())
m = _.trim();
else {
const V = o?.attributes?.child_name;
typeof V == "string" && V.trim() && (m = V.trim());
}
m || (m = me(t));
const x = _e(
o?.attributes?.next_present,
this._timeZone()
), H = x ? `Returns ${K(x, this._timeZone())}` : null;
return {
slug: t,
name: m,
initial: (m.charAt(0) || "?").toUpperCase(),
present: e === null ? f : !1,
completed: p,
due: a,
pct: d,
returns: H,
unresolved: e
};
}
/** "missing" when the due sensor does not exist (integration not
* configured), "stale" when it answers unavailable/unknown (backend
* unreachable or the child is absent from the snapshot), null when
* it resolves. */
_dueSensorResolution(t) {
const e = this._state(`sensor.nestquest_${t}_quests_due_today`);
if (!e)
return "missing";
const n = String(e.state ?? "").trim().toLowerCase();
return !n || n === "unavailable" || n === "unknown" ? "stale" : null;
}
_boardNotice() {
const t = this._plates();
return t.length === 0 ? qt : t.every((e) => e.unresolved !== null) ? t.some((e) => e.unresolved === "stale") ? Ae : qt : null;
}
_renderNotice(t) {
return c`
<div class="notice-wrap">
<div class="notice" role="status">
${t.icon}
<span class="notice-headline">${t.headline}</span>
<p class="notice-body">${t.body}</p>
</div>
</div>
`;
}
_renderPlate(t) {
return t.unresolved ? c`
<div class="plate unknown">
<span class="crest away">
<span class="crest-face">
<span class="initial">${t.initial}</span>
</span>
</span>
<span class="name">${t.name}</span>
<span class="pill away">
${qe}
<span>Unknown</span>
</span>
<span class="progress-line">&mdash;</span>
<span class="bar"></span>
</div>
` : t.present ? c`
<button
class="plate"
type="button"
aria-label="Open ${t.name}'s Quest Log"
@click=${() => this._openQuestLog(t.slug)}
>
<span class="crest">
<span class="crest-face">
<span class="initial">${t.initial}</span>
</span>
</span>
<span class="name">${t.name}</span>
<span class="pill">
${$e}
<span>Home today</span>
</span>
<span class="progress-line">
${t.completed} of ${t.due} quests claimed
</span>
<span class="bar">
<span class="fill" style="width: ${t.pct}%"></span>
</span>
</button>
` : c`
<div class="plate away">
<span class="crest away">
<span class="crest-face">
<span class="initial">${t.initial}</span>
</span>
</span>
<span class="name">${t.name}</span>
<span class="pill away">
${we}
<span>On travels</span>
</span>
<span class="progress-line">${t.returns ?? "Returns —"}</span>
<span class="bar"></span>
</div>
`;
}
_openQuestLog(t) {
const e = bt(this._config?.quest_log_path);
e && (window.history.pushState(null, "", `${e.replace(/\/+$/, "")}/${t}`), window.dispatchEvent(new Event("location-changed")));
}
_dockWeather() {
const t = bt(this._config?.weather_entity);
if (!t)
return null;
const e = this._state(t);
if (!e)
return null;
const n = String(e.state ?? "").trim();
if (!n || n === "unavailable" || n === "unknown")
return null;
const s = e.attributes ?? {};
let r = null, o = null;
const l = s.forecast;
if (Array.isArray(l) && l.length > 0) {
const a = l[0];
if (a && typeof a == "object") {
const p = a;
r = this._optionalNumber(p.temperature), o = this._optionalNumber(p.templow);
}
}
return {
condition: n,
temperature: this._optionalNumber(s.temperature),
high: r,
low: o
};
}
_optionalNumber(t) {
if (t == null || t === "")
return null;
const e = typeof t == "number" ? t : Number(t);
return Number.isFinite(e) ? e : null;
}
_renderDock() {
const t = this._dockWeather();
if (!t)
return c`
<div class="dock">
<span class="date">${K(this._now, this._timeZone())}</span>
<span class="divider"></span>
<span class="clock">${he(this._now, this._timeZone())}</span>
</div>
`;
const e = ye(t.condition), n = ve(t.condition, e), s = t.temperature === null ? g : c`<span class="temp">${Math.round(t.temperature)}°</span>`, r = t.high === null || t.low === null ? null : `${Math.round(t.high)}° / ${Math.round(t.low)}°`;
return c`
<div class="dock">
${Se}
${s}
<span class="divider"></span>
<span class="condition">
${r === null ? e : `${e} · ${r}`}
</span>
<span class="divider"></span>
<span class="condition">${n}</span>
</div>
`;
}
}
customElements.define("nestquest-party-board-card", Ne);
at({
type: "nestquest-party-board-card",
name: "NestQuest Party Board",
description: "Kids' attract screen for present and away adventurers."
});
const Te = { CHILD: 2 }, Ee = (i) => (...t) => ({ _$litDirective$: i, values: t });
let Me = class {
constructor(t) {
}
get _$AU() {
return this._$AM._$AU;
}
_$AT(t, e, n) {
this._$Ct = t, this._$AM = e, this._$Ci = n;
}
_$AS(t, e) {
return this.update(t, e);
}
update(t, e) {
return this.render(...e);
}
};
const { I: Oe } = ae, kt = (i) => i, Ct = () => document.createComment(""), P = (i, t, e) => {
const n = i._$AA.parentNode, s = t === void 0 ? i._$AB : t._$AA;
if (e === void 0) {
const r = n.insertBefore(Ct(), s), o = n.insertBefore(Ct(), s);
e = new Oe(r, o, i, i.options);
} else {
const r = e._$AB.nextSibling, o = e._$AM, l = o !== i;
if (l) {
let a;
e._$AQ?.(i), e._$AM = i, e._$AP !== void 0 && (a = i._$AU) !== o._$AU && e._$AP(a);
}
if (r !== s || l) {
let a = e._$AA;
for (; a !== r; ) {
const p = kt(a).nextSibling;
kt(n).insertBefore(a, s), a = p;
}
}
}
return e;
}, k = (i, t, e = i) => (i._$AI(t, e), i), ze = {}, Pe = (i, t = ze) => i._$AH = t, Ie = (i) => i._$AH, X = (i) => {
i._$AR(), i._$AA.remove();
};
const At = (i, t, e) => {
const n = /* @__PURE__ */ new Map();
for (let s = t; s <= e; s++) n.set(i[s], s);
return n;
}, St = Ee(class extends Me {
constructor(i) {
if (super(i), i.type !== Te.CHILD) throw Error("repeat() can only be used in text expressions");
}
dt(i, t, e) {
let n;
e === void 0 ? e = t : t !== void 0 && (n = t);
const s = [], r = [];
let o = 0;
for (const l of i) s[o] = n ? n(l, o) : o, r[o] = e(l, o), o++;
return { values: r, keys: s };
}
render(i, t, e) {
return this.dt(i, t, e).values;
}
update(i, [t, e, n]) {
const s = Ie(i), { values: r, keys: o } = this.dt(t, e, n);
if (!Array.isArray(s)) return this.ut = o, r;
const l = this.ut ??= [], a = [];
let p, u, d = 0, f = s.length - 1, h = 0, m = r.length - 1;
for (; d <= f && h <= m; ) if (s[d] === null) d++;
else if (s[f] === null) f--;
else if (l[d] === o[h]) a[h] = k(s[d], r[h]), d++, h++;
else if (l[f] === o[m]) a[m] = k(s[f], r[m]), f--, m--;
else if (l[d] === o[m]) a[m] = k(s[d], r[m]), P(i, a[m + 1], s[d]), d++, m--;
else if (l[f] === o[h]) a[h] = k(s[f], r[h]), P(i, s[d], s[f]), f--, h++;
else if (p === void 0 && (p = At(o, h, m), u = At(l, d, f)), p.has(l[d])) if (p.has(l[f])) {
const _ = u.get(o[h]), x = _ !== void 0 ? s[_] : null;
if (x === null) {
const H = P(i, s[d]);
k(H, r[h]), a[h] = H;
} else a[h] = k(x, r[h]), P(i, s[d], x), s[_] = null;
h++;
} else X(s[f]), f--;
else X(s[d]), d++;
for (; h <= m; ) {
const _ = P(i, a[m + 1]);
k(_, r[h]), a[h++] = _;
}
for (; d <= f; ) {
const _ = s[d++];
_ !== null && X(_);
}
return this.ut = o, Pe(i, a), N;
}
}), Nt = /* @__PURE__ */ new Map();
function M(i, t) {
const e = `${i ?? "local"}|${JSON.stringify(t)}`, n = Nt.get(e);
if (n)
return n;
let s;
try {
s = new Intl.DateTimeFormat("en-US", {
...t,
...i ? { timeZone: i } : {}
});
} catch {
s = new Intl.DateTimeFormat("en-US", t);
}
return Nt.set(e, s), s;
}
function De(i) {
const t = i?.config;
if (!t || typeof t != "object")
return;
const e = t.time_zone;
return typeof e == "string" && e.trim() ? e.trim() : void 0;
}
function L(i, t) {
return `${M(t, { weekday: "long" }).format(i)}, ${M(t, { month: "short", day: "numeric" }).format(i)}`;
}
function Ue(i, t) {
return M(t, { weekday: "long" }).format(i);
}
function je(i, t) {
const e = new Date(i);
return Number.isNaN(e.getTime()) ? "" : M(t, {
hour: "numeric",
minute: "2-digit",
hour12: !0
}).format(e);
}
function He(i, t) {
return M(t, {
hour: "numeric",
minute: "2-digit",
hour12: !0
}).format(i);
}
function Le(i) {
if (!i)
return null;
const t = /^(\d{1,2}):(\d{2})$/.exec(i.trim());
if (!t)
return null;
const e = Number(t[1]);
if (e > 23)
return null;
const n = e >= 12 ? "PM" : "AM";
return `${e % 12 === 0 ? 12 : e % 12}:${t[2]} ${n}`;
}
function $(i) {
return typeof i == "string" ? i.trim() : "";
}
function y(i, t) {
if (i == null || i === "")
return t;
const e = typeof i == "number" ? i : Number(i);
return Number.isFinite(e) ? e : t;
}
function v(i) {
return Math.max(0, Math.round(i));
}
function Y(i) {
return i.split(/[-_]+/).filter(Boolean).map((t) => t.charAt(0).toUpperCase() + t.slice(1)).join(" ");
}
function Re(i, t) {
const e = M(t, {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hourCycle: "h23"
}).formatToParts(new Date(i)), n = (r) => {
const o = e.find((l) => l.type === r);
return o ? Number(o.value) : Number.NaN;
}, s = Date.UTC(
n("year"),
n("month") - 1,
n("day"),
n("hour"),
n("minute"),
n("second")
);
return Number.isFinite(s) ? s - i : 0;
}
function Tt(i, t) {
if (typeof i != "string")
return null;
const e = i.split("-");
if (e.length !== 3)
return null;
const [n, s, r] = e.map((a) => Number(a));
if (!n || !s || !r)
return null;
if (!t) {
const a = new Date(n, s - 1, r);
return Number.isNaN(a.getTime()) ? null : a;
}
const o = Date.UTC(n, s - 1, r), l = new Date(o - Re(o, t));
return Number.isNaN(l.getTime()) ? null : l;
}
function Be() {
const i = window.location.pathname.split("/").filter(Boolean);
return i.length > 0 ? i[i.length - 1].toLowerCase() : "";
}
function Fe(i) {
if (!i || typeof i != "object")
return null;
const t = i, e = $(t.state).toLowerCase();
if (e !== "open" && e !== "completed")
return null;
const n = y(t.id, 0);
if (!n)
return null;
const s = y(t.child_id, 0);
return {
id: n,
child_id: s > 0 ? s : null,
title: $(t.title) || "Quest",
icon: $(t.icon) || null,
window: $(t.window).toLowerCase(),
due_time: $(t.due_time) || null,
state: e,
overdue: t.overdue === !0,
completed_at: $(t.completed_at) || null,
on_time: t.on_time === !0 ? !0 : t.on_time === !1 ? !1 : null
};
}
const Qe = {
"clear-night": "Clear",
cloudy: "Cloudy",
exceptional: "Clear",
fog: "Foggy",
hail: "Hail",
lightning: "Storms",
"lightning-rainy": "Storms",
partlycloudy: "Partly cloudy",
pouring: "Heavy rain",
rainy: "Rain",
snowy: "Snow",
"snowy-rainy": "Sleet",
sunny: "Sunny",
windy: "Windy",
"windy-variant": "Windy"
}, We = {
"clear-night": "Clear night skies",
cloudy: "Grey skies today",
exceptional: "A striking day",
fog: "Mist on the road",
hail: "Ice from the sky",
lightning: "Storms may roll in",
"lightning-rainy": "Storms may roll in",
partlycloudy: "Sun between clouds",
pouring: "Heavy rain outside",
rainy: "Rain on the walls",
snowy: "Snow on the peaks",
"snowy-rainy": "Sleet may fall",
sunny: "Clear skies today",
windy: "A blustery day",
"windy-variant": "A blustery day"
};
function Ze(i) {
const t = Qe[i];
return t || i.charAt(0).toUpperCase() + i.slice(1);
}
function Ve(i, t) {
return We[i] ?? t;
}
const Et = "polygon(0 0, 100% 0, 100% 62%, 50% 100%, 0 62%)", Ge = "polygon(50% 0, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%)", Mt = [-9, 6, -4], Ke = Pt`
* {
box-sizing: border-box;
}
:host {
display: block;
height: 100%;
--nq-brand-gradient-h: linear-gradient(
90deg,
var(--nq-brand-blue),
var(--nq-brand-purple)
);
}
.board {
position: relative;
display: flex;
flex-direction: column;
height: 1080px;
padding: 0;
overflow: hidden;
background: var(--nq-p-parchment);
box-shadow: var(--nq-p-vignette);
font-family: var(--nq-p-font-body);
}
.frame {
position: absolute;
pointer-events: none;
}
.frame-outer {
inset: 26px;
border: var(--nq-p-frame-outer);
border-radius: 14px;
}
.frame-inner {
inset: 36px;
border: var(--nq-p-frame-inner);
border-radius: 8px;
}
.header {
position: absolute;
top: 56px;
left: var(--nq-p-page-inset);
right: var(--nq-p-page-inset);
display: flex;
align-items: center;
gap: 28px;
}
.crest {
position: relative;
flex: none;
width: 96px;
height: 110px;
padding: 3px;
clip-path: ${b(Et)};
background: rgba(255, 255, 255, 0.22);
}
.crest-face {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 32px;
clip-path: ${b(Et)};
background: var(--nq-p-crest);
}
.crest .initial {
font-family: var(--nq-p-font-display);
font-size: 44px;
font-weight: 700;
line-height: 1;
color: #ffffff;
}
.crest.away .crest-face {
background: var(--nq-p-crest-away);
}
.titles {
display: flex;
flex-direction: column;
gap: 8px;
min-width: 0;
}
.title {
margin: 0;
font-family: var(--nq-p-font-display);
font-size: var(--nq-p-size-title);
font-weight: 700;
line-height: 1.1;
color: var(--nq-p-ink);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.sub {
margin: 0;
font-family: var(--nq-p-font-heading);
font-size: 20px;
font-weight: 600;
letter-spacing: 0.24em;
text-transform: uppercase;
line-height: 1.2;
color: var(--nq-p-ink-secondary);
}
.remaining {
margin-left: auto;
flex: none;
display: flex;
align-items: center;
gap: 20px;
padding: 18px 28px;
border: 2px solid var(--nq-p-panel-border);
border-radius: 14px;
background: var(--nq-p-card-panel);
box-shadow: var(--nq-p-panel-shadow);
}
.d20 {
flex: none;
display: inline-flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
clip-path: ${b(Ge)};
background: var(--nq-brand-gradient);
}
.d20-numeral {
font-family: var(--nq-p-font-heading);
font-size: 19px;
font-weight: 700;
line-height: 1;
color: #ffffff;
}
.remaining-text {
display: flex;
flex-direction: column;
gap: 4px;
}
.remaining-count {
font-family: var(--nq-p-font-heading);
font-size: 28px;
font-weight: 700;
line-height: 1.1;
color: var(--nq-p-ink);
}
.party-line {
font-family: var(--nq-p-font-body);
font-size: var(--nq-p-size-body);
font-weight: 600;
line-height: 1.2;
color: var(--nq-p-ink-secondary);
}
.columns {
position: absolute;
top: 236px;
left: var(--nq-p-page-inset);
right: var(--nq-p-page-inset);
bottom: 130px;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 34px;
}
.column {
display: flex;
flex-direction: column;
min-height: 0;
}
.column-head {
flex: none;
display: flex;
align-items: center;
gap: 14px;
padding-bottom: 14px;
border-bottom: var(--nq-p-rule);
}
.column-head svg {
flex: none;
width: 34px;
height: 34px;
color: var(--nq-p-ink-secondary);
}
.column-names {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
}
.column-name {
font-family: var(--nq-p-font-heading);
font-size: var(--nq-p-size-section);
font-weight: 700;
line-height: 1.1;
color: var(--nq-p-ink);
}
.column-range {
font-family: var(--nq-p-font-heading);
font-size: var(--nq-p-size-label);
font-weight: 600;
letter-spacing: var(--nq-p-track-label);
text-transform: uppercase;
line-height: 1.2;
color: var(--nq-p-ink-secondary);
}
.column-count {
margin-left: auto;
flex: none;
font-family: var(--nq-p-font-heading);
font-size: 26px;
font-weight: 700;
line-height: 1.1;
color: var(--nq-p-ink-secondary);
}
.stack {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
overflow: visible;
gap: var(--nq-p-quest-gap);
margin-top: 22px;
}
.quest {
position: relative;
display: flex;
align-items: center;
gap: 22px;
min-height: var(--nq-p-quest-min-height);
padding: var(--nq-p-quest-pad);
border: 1px solid var(--nq-p-card-border);
border-radius: var(--nq-p-radius-card);
background: var(--nq-p-card-open);
box-shadow: var(--nq-p-card-shadow);
-webkit-tap-highlight-color: transparent;
transition: transform var(--nq-dur-micro) var(--nq-ease-out);
}
.quest.tappable {
cursor: pointer;
}
.quest.tappable:active {
transform: scale(0.98);
}
.quest.sealed {
background: var(--nq-p-card-done);
opacity: 0.82;
box-shadow: none;
}
.tile {
flex: none;
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--nq-p-tile-size);
height: var(--nq-p-tile-size);
border-radius: 10px;
background: var(--nq-p-icon-tile);
color: #ffffff;
}
.tile svg {
width: 34px;
height: 34px;
}
.sealed .tile {
background: var(--nq-p-icon-tile-done);
color: var(--nq-p-ink-secondary);
}
.quest .body {
flex: 1 1 0;
min-width: 0;
display: flex;
flex-direction: column;
gap: 6px;
}
.quest .quest-title {
font-family: var(--nq-p-font-heading);
font-size: var(--nq-p-size-quest);
font-weight: 700;
line-height: 1.15;
color: var(--nq-p-ink);
overflow-wrap: anywhere;
}
.sealed .quest-title {
color: var(--nq-p-ink-muted);
text-decoration: line-through;
}
.quest .meta {
font-family: var(--nq-p-font-body);
font-size: var(--nq-p-size-body);
font-weight: 600;
line-height: 1.2;
color: var(--nq-p-ink-secondary);
}
.quest .meta.late {
font-family: var(--nq-p-font-heading);
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--nq-p-ink-late);
}
button.complete {
flex: none;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10px;
height: var(--nq-p-button-height);
min-width: var(--nq-p-button-min-width);
border: none;
border-radius: 10px;
background: var(--nq-brand-gradient);
color: #ffffff;
font-family: var(--nq-p-font-heading);
font-size: 24px;
font-weight: 700;
line-height: 1;
box-shadow: 0 3px 0 rgba(40, 20, 60, 0.35);
cursor: pointer;
-webkit-tap-highlight-color: transparent;
transition: transform var(--nq-dur-micro) var(--nq-ease-out);
}
button.complete:active {
transform: scale(0.98);
}
button.complete svg {
width: 30px;
height: 30px;
}
button:focus-visible {
outline: 3px solid var(--nq-brand-purple);
outline-offset: 4px;
}
.seal {
position: absolute;
right: 16px;
top: -12px;
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--nq-p-seal-size);
height: var(--nq-p-seal-size);
border-radius: var(--nq-p-radius-pill);
background: var(--nq-p-seal);
box-shadow: var(--nq-p-seal-shadow);
color: rgba(255, 235, 235, 0.95);
transform: rotate(var(--seal-rot, 0deg));
animation: seal-in var(--nq-dur-base) var(--nq-ease-out);
pointer-events: none;
}
.seal svg {
width: 38px;
height: 38px;
}
@keyframes seal-in {
from {
opacity: 0;
transform: scale(1.15) rotate(var(--seal-rot, 0deg));
}
to {
opacity: 1;
transform: scale(1) rotate(var(--seal-rot, 0deg));
}
}
.rollup {
border: 2px dashed var(--nq-p-panel-border);
border-radius: 14px;
padding: 16px 22px;
}
.rollup-label {
display: block;
font-family: var(--nq-p-font-heading);
font-size: 18px;
font-weight: 600;
letter-spacing: var(--nq-p-track-label);
text-transform: uppercase;
line-height: 1.2;
color: var(--nq-p-ink-secondary);
}
.rollup-body {
margin: 6px 0 0;
font-family: var(--nq-p-font-body);
font-size: 23px;
font-weight: 600;
line-height: 1.35;
color: var(--nq-p-ink-secondary);
}
.notice-wrap {
position: absolute;
top: 236px;
left: var(--nq-p-page-inset);
right: var(--nq-p-page-inset);
bottom: 130px;
display: flex;
align-items: center;
justify-content: center;
}
.notice {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
width: 100%;
max-width: 980px;
padding: 56px 64px;
border: 2px solid var(--nq-p-panel-border);
border-radius: 20px;
background: var(--nq-p-card-panel);
box-shadow: var(--nq-p-panel-shadow);
text-align: center;
}
.notice svg {
width: 46px;
height: 46px;
color: var(--nq-p-ink-secondary);
}
.notice.away {
background: linear-gradient(#f2ecdd, #e6dcc6);
border-style: dashed;
box-shadow: none;
}
.notice-headline {
font-family: var(--nq-p-font-heading);
font-size: 40px;
font-weight: 700;
line-height: 1.15;
color: var(--nq-p-ink);
}
.notice-body {
margin: 0;
font-family: var(--nq-p-font-body);
font-size: 24px;
font-weight: 600;
line-height: 1.4;
color: var(--nq-p-ink-secondary);
}
.notice-seal {
display: inline-flex;
align-items: center;
justify-content: center;
width: 78px;
height: 78px;
border-radius: var(--nq-p-radius-pill);
background: var(--nq-p-seal);
box-shadow: var(--nq-p-seal-shadow);
color: rgba(255, 235, 235, 0.95);
transform: rotate(-6deg);
}
.notice-seal svg {
width: 38px;
height: 38px;
color: rgba(255, 235, 235, 0.95);
}
.scrim {
position: absolute;
inset: 0;
z-index: 10;
background: rgba(30, 20, 10, 0.62);
}
.dialog {
position: absolute;
top: 196px;
left: 460px;
width: 1000px;
border: 3px solid rgba(92, 62, 26, 0.5);
border-radius: 20px;
background: var(--nq-p-card-panel);
box-shadow: 0 26px 60px rgba(20, 10, 0, 0.5);
padding: 64px 68px 56px;
animation: dialog-in var(--nq-dur-modal) var(--nq-ease-out);
}
@keyframes dialog-in {
from {
opacity: 0;
transform: scale(0.96);
}
to {
opacity: 1;
transform: scale(1);
}
}
.dialog-header {
display: flex;
align-items: center;
gap: 24px;
}
.dialog-tile {
flex: none;
display: inline-flex;
align-items: center;
justify-content: center;
width: 104px;
height: 104px;
border-radius: 12px;
background: var(--nq-p-icon-tile);
color: #ffffff;
}
.dialog-tile svg {
width: 56px;
height: 56px;
}
.dialog-titles {
display: flex;
flex-direction: column;
gap: 8px;
min-width: 0;
}
.dialog-kicker {
font-family: var(--nq-p-font-heading);
font-size: var(--nq-p-size-label);
font-weight: 600;
letter-spacing: 0.24em;
text-transform: uppercase;
line-height: 1.2;
color: var(--nq-p-ink-secondary);
}
.dialog-quest-title {
font-family: var(--nq-p-font-display);
font-size: 58px;
font-weight: 700;
line-height: 1.1;
color: var(--nq-p-ink);
overflow-wrap: anywhere;
}
.dialog-body {
margin: 28px 0 0;
font-family: var(--nq-p-font-body);
font-size: 32px;
font-weight: 600;
line-height: 1.35;
color: #3f2f1c;
}
.dialog-buttons {
display: flex;
gap: 24px;
margin-top: 40px;
}
button.confirm-button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 12px;
height: var(--nq-p-confirm-button);
border-radius: 10px;
font-family: var(--nq-p-font-heading);
font-weight: 700;
line-height: 1;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
transition: transform var(--nq-dur-micro) var(--nq-ease-out);
}
button.confirm-button:active {
transform: scale(0.98);
}
button.not-yet {
flex: 1;
border: 2px solid rgba(92, 62, 26, 0.5);
background: rgba(92, 62, 26, 0.1);
color: var(--nq-p-ink);
font-size: 30px;
}
button.not-yet svg {
width: 34px;
height: 34px;
}
button.do-complete {
flex: 2;
border: none;
background: var(--nq-brand-gradient);
color: #ffffff;
font-size: 32px;
}
button.do-complete svg {
width: 38px;
height: 38px;
}
.toast {
position: absolute;
left: 50%;
bottom: 158px;
z-index: 20;
transform: translateX(-50%);
max-width: calc(100% - 124px);
padding: 20px 32px;
border: 2px solid var(--nq-p-panel-border);
border-radius: 14px;
background: var(--nq-p-card-panel);
box-shadow: var(--nq-p-panel-shadow);
font-family: var(--nq-p-font-body);
font-size: 23px;
font-weight: 600;
line-height: 1.2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: var(--nq-p-ink-secondary);
animation: toast-in var(--nq-dur-base) var(--nq-ease-out);
pointer-events: none;
}
@keyframes toast-in {
from {
opacity: 0;
transform: translate(-50%, 12px);
}
to {
opacity: 1;
transform: translate(-50%, 0);
}
}
.complete-screen {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
padding: 96px 62px 130px;
text-align: center;
}
.complete-seal {
display: inline-flex;
align-items: center;
justify-content: center;
width: 168px;
height: 168px;
border-radius: var(--nq-p-radius-pill);
background: var(--nq-p-seal);
box-shadow: 0 10px 24px rgba(60, 10, 20, 0.42);
color: rgba(255, 235, 235, 0.95);
transform: rotate(-6deg);
}
.complete-seal svg {
width: 86px;
height: 86px;
}
.complete-title {
margin: 28px 0 0;
font-family: var(--nq-p-font-display);
font-size: 86px;
font-weight: 900;
line-height: 1.1;
background: var(--nq-brand-gradient-h);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
-webkit-text-fill-color: transparent;
}
.complete-sub {
margin: 16px 0 0;
max-width: 1000px;
font-family: var(--nq-p-font-body);
font-size: 34px;
font-weight: 600;
line-height: 1.35;
color: var(--nq-p-ink-secondary);
text-wrap: pretty;
}
.complete-countdown {
margin: 12px 0 0;
font-family: var(--nq-p-font-body);
font-size: 25px;
font-weight: 600;
line-height: 1.2;
color: var(--nq-p-ink-secondary);
}
.complete-stats {
position: absolute;
top: 540px;
left: 200px;
right: 200px;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 26px;
}
.complete-stat {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
padding: 28px 16px;
border: 2px solid var(--nq-p-panel-border);
border-radius: 16px;
background: var(--nq-p-card-panel);
box-shadow: var(--nq-p-panel-shadow);
}
.complete-stat .numeral {
font-family: var(--nq-p-font-heading);
font-size: 58px;
font-weight: 900;
line-height: 1;
color: var(--nq-p-ink);
}
.complete-stat .label {
font-family: var(--nq-p-font-heading);
font-size: 17px;
font-weight: 600;
letter-spacing: 0.14em;
text-transform: uppercase;
line-height: 1.2;
color: var(--nq-p-ink-secondary);
}
.dock {
position: absolute;
left: 46px;
right: 46px;
bottom: 46px;
height: var(--nq-p-dock-height);
display: flex;
align-items: center;
justify-content: center;
gap: 24px;
padding: 0 40px;
border-radius: 14px;
background: var(--nq-p-dock-bg);
pointer-events: none;
user-select: none;
color: var(--nq-p-dock-ink);
}
.dock svg {
flex: none;
width: 40px;
height: 40px;
color: var(--nq-p-dock-ink);
}
.dock .divider {
flex: none;
width: 1px;
height: 40px;
background: var(--nq-p-dock-divider);
}
.dock .temp {
font-family: var(--nq-p-font-body);
font-size: 38px;
font-weight: 800;
line-height: 1;
}
.dock .condition {
font-family: var(--nq-p-font-body);
font-size: 26px;
font-weight: 600;
line-height: 1.2;
color: var(--nq-p-dock-ink-secondary);
}
.dock .date {
font-family: var(--nq-p-font-body);
font-size: 26px;
font-weight: 600;
line-height: 1.2;
color: var(--nq-p-dock-ink-secondary);
}
.dock .clock {
font-family: var(--nq-p-font-body);
font-size: 38px;
font-weight: 800;
line-height: 1;
}
`, R = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M20 6 9 17l-5-5"></path>
</svg>`, Je = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M18 6 6 18"></path>
<path d="m6 6 12 12"></path>
</svg>`, tt = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<polygon
points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26"
></polygon>
</svg>`, Xe = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M12 2v8"></path>
<path d="m4.93 10.93 1.41 1.41"></path>
<path d="M2 18h2"></path>
<path d="M20 18h2"></path>
<path d="m19.07 10.93-1.41 1.41"></path>
<path d="M22 22H2"></path>
<path d="m8 6 4-4 4 4"></path>
<path d="M16 18a4 4 0 0 0-8 0"></path>
</svg>`, Rt = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<circle cx="12" cy="12" r="4"></circle>
<path d="M12 2v2"></path>
<path d="M12 20v2"></path>
<path d="m4.93 4.93 1.41 1.41"></path>
<path d="m17.66 17.66 1.41 1.41"></path>
<path d="M2 12h2"></path>
<path d="M20 12h2"></path>
<path d="m6.34 17.66-1.41 1.41"></path>
<path d="m19.07 4.93-1.41 1.41"></path>
</svg>`, Ye = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path>
</svg>`, tn = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M3.5 21 12 3.5 20.5 21"></path>
<path d="M9 21l3-8 3 8"></path>
</svg>`, en = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<circle cx="12" cy="12" r="10"></circle>
<polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon>
</svg>`, nn = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"></path>
<path d="M12 9v4"></path>
<path d="M12 17h.01"></path>
</svg>`, sn = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z"></path>
<path d="m2 2 20 20"></path>
</svg>`, rn = c`<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M13 16a3 3 0 1 1 0 6H7a5 5 0 1 1 4.9-6Z"></path>
<path d="M10.1 9A6 6 0 0 1 16 4a4.5 4.5 0 0 0 8.9 4.2v.1a6 6 0 0 1-5.2 5.7"></path>
</svg>`, on = {
icon: en,
headline: "No adventurer chosen",
body: "Open The Party and tap your crest to open your quest log."
}, an = {
icon: nn,
headline: "NestQuest is not set up yet",
body: "A parent needs to finish setting up NestQuest."
}, ln = {
icon: sn,
headline: "The records cannot be reached",
body: "The party's records are quiet right now. NestQuest will return shortly."
}, Ot = [
{ key: "morning", name: "Morning", range: "Until 11:59 AM", icon: Xe },
{ key: "afternoon", name: "Afternoon", range: "12:00–5:00 PM", icon: Rt },
{ key: "evening", name: "Evening", range: "5:00–9:00 PM", icon: Ye }
];
class dn extends A {
static properties = {
hass: { attribute: !1 },
_config: { state: !0 },
_now: { state: !0 },
_confirm: { state: !0 },
_optimistic: { state: !0 },
_toast: { state: !0 },
_countdown: { state: !0 }
};
static styles = [b(Lt), Ke];
hass;
_config;
_now = /* @__PURE__ */ new Date();
_confirm = null;
/** instanceId → the completion instant stamped before the service has
* answered; the seal shows immediately and reverts on failure. */
_optimistic = /* @__PURE__ */ new Map();
_toast = null;
_countdown = null;
_clockTimer;
_idleTimer;
_confirmTimer;
_toastTimer;
_completeTimer;
setConfig(t) {
if (!t || typeof t != "object")
throw new Error("Invalid configuration");
this._config = t;
}
getCardSize() {
return 22;
}
connectedCallback() {
super.connectedCallback(), this._now = /* @__PURE__ */ new Date(), this._clockTimer = window.setInterval(() => {
this._now = /* @__PURE__ */ new Date();
}, 6e4), window.addEventListener("pointerdown", this._onActivity, !0), window.addEventListener("touchstart", this._onActivity, !0), window.addEventListener("keydown", this._onActivity, !0), this._armIdle(), this._logView().kind === "complete-day" && this._armCompleteTimer();
}
updated() {
this._logView().kind === "complete-day" ? (this._clearIdle(), this._armCompleteTimer()) : (this._clearCompleteTimer(), this._countdown = null, this._idleTimer === void 0 && this._armIdle());
}
disconnectedCallback() {
this._stopClock(), this._clearIdle(), this._clearConfirmTimer(), this._clearToastTimer(), this._clearCompleteTimer(), window.removeEventListener("pointerdown", this._onActivity, !0), window.removeEventListener("touchstart", this._onActivity, !0), window.removeEventListener("keydown", this._onActivity, !0), super.disconnectedCallback();
}
render() {
const t = this._logView();
return c`
<div class="board">
<div class="frame frame-outer"></div>
<div class="frame frame-inner"></div>
${this._renderMain(t)}
${this._renderConfirm()}
${this._renderToast()}
</div>
`;
}
_renderToast() {
return this._toast === null ? g : c`
<div class="toast" role="status">${this._toast}</div>
`;
}
_renderMain(t) {
switch (t.kind) {
case "no-adventurer":
return this._renderNotice(on);
case "not-set-up":
return this._renderNotice(an);
case "unreachable":
return this._renderNotice(ln);
case "away": {
const e = ["The quest log unlocks when they return."];
return t.returns && e.push(`Returns ${t.returns}`), c`
${this._renderHeader(t)}
<div class="notice-wrap">
<div class="notice away" role="status">
${tn}
<span class="notice-headline">${t.name} is on travels</span>
${e.map((n) => c`<p class="notice-body">${n}</p>`)}
</div>
</div>
`;
}
case "empty-day":
return c`
${this._renderHeader(t)}
<div class="notice-wrap">
<div class="notice" role="status">
${Rt}
<span class="notice-headline">No quests today</span>
<p class="notice-body">
${`Nothing was posted for today, ${t.name}. Enjoy the day's rest!`}
</p>
</div>
</div>
`;
case "complete-day": {
const e = this._completeStats(), n = this._countdown ?? this._completeSeconds();
return c`
<div class="complete-screen" role="status">
<span class="complete-seal" aria-hidden="true">${R}</span>
<h1 class="complete-title">Quest complete</h1>
<p class="complete-sub">${this._completeSub(t.name)}</p>
<p class="complete-countdown">
Returning to The Party in ${n} seconds
</p>
<div class="complete-stats">
<div class="complete-stat">
<span class="numeral">${e.claimed}</span>
<span class="label">Quests claimed</span>
</div>
<div class="complete-stat">
<span class="numeral">${e.onTime}</span>
<span class="label">On time</span>
</div>
<div class="complete-stat">
<span class="numeral">${e.late}</span>
<span class="label">Late</span>
</div>
<div class="complete-stat">
<span class="numeral">${e.party}</span>
<span class="label">The Party</span>
</div>
</div>
</div>
${this._renderDock()}
`;
}
default:
return c`
${this._renderHeader(t)}
<div class="columns">
${Ot.map((e) => this._renderColumn(e))}
</div>
`;
}
}
/** Resolve the log's single render state; the priority order is
* design/PANEL-EMPTY-STATES.md §2. */
_logView() {
const t = this._childSlug();
if (!t)
return { kind: "no-adventurer" };
const e = this._dueSensorResolution(t);
if (e === "missing")
return { kind: "not-set-up" };
if (e === "stale")
return { kind: "unreachable" };
const n = this._childName(t) ?? Y(t);
if (!this._childPresent(t))
return { kind: "away", name: n, returns: this._awayReturns(t) };
const s = this._state(`binary_sensor.nestquest_${t}_all_done`);
if (s && String(s.state ?? "").trim().toLowerCase() === "on")
return { kind: "complete-day", name: n };
const r = this._state(`sensor.nestquest_${t}_quests_due_today`);
return v(y(r?.state, 0)) === 0 ? { kind: "empty-day", name: n } : this._remaining() === 0 ? { kind: "complete-day", name: n } : { kind: "normal" };
}
/** "missing" when the due sensor does not exist (integration not
* configured), "stale" when it answers unavailable/unknown (backend
* unreachable or the child is absent from the snapshot), null when
* it resolves. */
_dueSensorResolution(t) {
const e = this._state(`sensor.nestquest_${t}_quests_due_today`);
if (!e)
return "missing";
const n = String(e.state ?? "").trim().toLowerCase();
return !n || n === "unavailable" || n === "unknown" ? "stale" : null;
}
_childPresent(t) {
const n = this._state(`sensor.nestquest_${t}_quests_due_today`)?.attributes?.present;
if (typeof n == "boolean")
return n;
const s = this._state(
`binary_sensor.nestquest_${t}_present_today`
);
return s ? String(s.state ?? "").trim().toLowerCase() === "on" : !0;
}
_awayReturns(t) {
const e = this._timeZone(), n = this._state(
`binary_sensor.nestquest_${t}_present_today`
), s = Tt(
n?.attributes?.next_present,
e
);
return s ? L(s, e) : null;
}
_renderNotice(t) {
return c`
<div class="notice-wrap">
<div class="notice" role="status">
${t.icon}
<span class="notice-headline">${t.headline}</span>
<p class="notice-body">${t.body}</p>
</div>
</div>
`;
}
_timeZone() {
return De(this.hass);
}
_state(t) {
const n = this.hass?.states?.[t];
return n && typeof n == "object" ? n : null;
}
_childSlug() {
return Be();
}
_boardPath() {
return $(this._config?.board_path).replace(/\/+$/, "");
}
_idleSeconds() {
const t = y(this._config?.idle_return_seconds, 40);
return t > 0 ? t : 40;
}
_confirmSeconds() {
const t = y(this._config?.confirm_timeout_seconds, 15);
return t > 0 ? t : 15;
}
_completeSeconds() {
const t = y(this._config?.complete_screen_seconds, 12);
return t > 0 ? t : 12;
}
_childName(t) {
if (!t)
return null;
const e = this._state(`sensor.nestquest_${t}_quests_due_today`)?.attributes?.child_name;
return typeof e == "string" && e.trim() ? e.trim() : null;
}
_childCounts(t) {
const e = this._state(`sensor.nestquest_${t}_quests_due_today`), n = this._state(
`sensor.nestquest_${t}_quests_completed_today`
), s = v(y(e?.state, 0)), r = v(y(n?.state, 0));
return { due: s, completed: r };
}
_instances() {
const t = this._childSlug();
if (!t)
return [];
const e = this._state(`sensor.nestquest_${t}_quests_due_today`)?.attributes?.instances;
if (!Array.isArray(e))
return [];
const n = e.map(Fe).filter((r) => r !== null);
if (this._optimistic.size === 0)
return n;
let s = !1;
for (const r of n) {
const o = this._optimistic.get(r.id);
if (o !== void 0) {
if (r.state === "completed") {
this._optimistic.delete(r.id), s = !0;
continue;
}
r.state = "completed", r.overdue = !1, r.completed_at = o;
}
}
return s && (this._optimistic = new Map(this._optimistic)), n;
}
_remaining() {
const t = this._childSlug();
if (!t)
return 0;
const e = this._state(
`sensor.nestquest_${t}_quests_remaining_today`
);
if (e)
return v(y(e.state, 0));
const { due: n, completed: s } = this._childCounts(t);
return v(n - s);
}
_partyCounts() {
const t = this._state(
"sensor.nestquest_household_quests_due_today"
), e = this._state(
"sensor.nestquest_household_quests_completed_today"
);
if (t || e)
return {
completed: v(y(e?.state, 0)),
due: v(y(t?.state, 0))
};
const n = this._childSlug(), { due: s, completed: r } = this._childCounts(n);
return { completed: r, due: s };
}
_completeStats() {
const t = this._childCounts(this._childSlug()).completed, e = this._instances().filter(
(r) => r.on_time === !0
).length, n = v(t - e), s = this._partyCounts();
return {
claimed: t,
onTime: e,
late: n,
party: `${s.completed}/${s.due}`
};
}
_completeSub(t) {
const e = L(this._now, this._timeZone()), { claimed: n, onTime: s, late: r } = this._completeStats();
return n === 0 ? `${t} sealed the day on ${e}.` : r === 0 ? `${t} claimed every quest on ${e} — all on time.` : s === 0 ? `${t} claimed every quest on ${e} — all late.` : `${t} claimed every quest on ${e} — ${s} on time, ${r} late.`;
}
_otherChildren() {
const t = this.hass?.states;
if (!t)
return [];
const e = this._childSlug(), n = this._timeZone(), s = [];
for (const r of Object.keys(t)) {
const o = /^sensor\.nestquest_(.+)_quests_due_today$/.exec(r);
if (!o || o[1] === "household" || o[1] === e)
continue;
const l = o[1], a = t[r]?.attributes ?? {};
let p = "";
typeof a.child_name == "string" && a.child_name.trim() ? p = a.child_name.trim() : p = Y(l);
let u = !0;
if (typeof a.present == "boolean")
u = a.present;
else {
const _ = this._state(
`binary_sensor.nestquest_${l}_present_today`
);
u = _ === null || String(_.state ?? "").trim().toLowerCase() !== "off";
}
const d = this._state(
`sensor.nestquest_${l}_quests_completed_today`
), f = v(y(t[r]?.state, 0)), h = v(y(d?.state, 0));
let m = null;
if (!u) {
const _ = this._state(
`binary_sensor.nestquest_${l}_present_today`
), x = Tt(
_?.attributes?.next_present,
n
);
m = x ? Ue(x, n) : null;
}
s.push({
slug: l,
name: p,
present: u,
due: f,
completed: h,
returnsWeekday: m
});
}
return s.sort((r, o) => r.name.localeCompare(o.name));
}
_renderHeader(t) {
const e = this._childSlug(), n = t.kind === "away", s = n ? t.name : this._childName(e) ?? (e ? Y(e) : null), { due: r, completed: o } = this._childCounts(e), l = this._remaining(), a = this._partyCounts(), p = s ? `${s}'s Quest Log` : "Quest Log", u = (s?.charAt(0) || "?").toUpperCase();
return c`
<header class="header">
<span class="crest${n ? " away" : g}" aria-hidden="true">
<span class="crest-face">
<span class="initial">${u}</span>
</span>
</span>
<div class="titles">
<h1 class="title">${p}</h1>
<p class="sub">
${L(this._now, this._timeZone())} ·
${n ? "On travels" : `${o} of ${r} claimed`}
</p>
</div>
${n ? g : c`
<div class="remaining">
<span class="d20" aria-hidden="true">
<span class="d20-numeral">${l}</span>
</span>
<div class="remaining-text">
<span class="remaining-count">
${l} quest${l === 1 ? "" : "s"} left
</span>
<span class="party-line">
Party progress · ${a.completed} of ${a.due} today
</span>
</div>
</div>
`}
</header>
`;
}
_renderColumn(t) {
const e = this._instances().filter(
(l) => l.window === t.key
), n = e.filter((l) => l.state === "open").sort(
(l, a) => (l.due_time ?? "99:99").localeCompare(a.due_time ?? "99:99")
), s = e.filter(
(l) => l.state === "completed"
), r = e.length;
let o = g;
if (r > 0) {
const a = 644 - (t.key === "afternoon" && this._otherChildren().length > 0 ? 160 : 0), p = Math.min(
24,
Math.max(8, Math.floor((a - r * 74) / Math.max(r - 1, 1)))
), u = Math.min(
116,
Math.max(58, Math.floor((a - (r - 1) * p) / r))
), d = Math.max(56, Math.min(72, u - 2));
let f = `--nq-p-quest-gap: ${p}px; --nq-p-quest-min-height: ${u}px; --nq-p-button-height: ${d}px`;
if (r >= 5) {
const h = Math.max(0, Math.min(22, Math.floor((u - d - 2) / 2))), m = Math.max(44, Math.min(64, u - 2 * h - 2));
f += `; --nq-p-quest-pad: ${h}px; --nq-p-tile-size: ${m}px`;
}
o = f;
}
return c`
<section
class="column"
aria-label="${t.name} quests"
style=${o}
>
<div class="column-head">
${t.icon}
<div class="column-names">
<span class="column-name">${t.name}</span>
<span class="column-range">${t.range}</span>
</div>
<span class="column-count">${s.length}/${e.length}</span>
</div>
<div class="stack">
${St(
n,
(l) => l.id,
(l) => this._renderQuest(l, !1, 0)
)}
${St(
s,
(l) => l.id,
(l, a) => this._renderQuest(l, !0, a)
)}
${t.key === "afternoon" ? this._renderRollup() : g}
</div>
</section>
`;
}
_renderQuest(t, e, n) {
if (e) {
const o = Mt[n % Mt.length], l = t.completed_at ? je(t.completed_at, this._timeZone()) : "";
return c`
<div class="quest sealed" data-instance-id=${t.id}>
<span class="tile" aria-hidden="true">${tt}</span>
<div class="body">
<span class="quest-title">${t.title}</span>
<span class="meta">
${l ? `Claimed ${l}` : "Claimed"}
</span>
</div>
<span
class="seal"
aria-hidden="true"
style="--seal-rot: ${o}deg"
>
${R}
</span>
</div>
`;
}
const s = Le(t.due_time), r = t.overdue ? {
text: s ? `Overdue · due ${s}` : "Overdue",
late: !0
} : {
text: s ? `Due by ${s}` : "Due today",
late: !1
};
return c`
<div
class="quest tappable"
role="button"
tabindex="0"
data-instance-id=${t.id}
aria-label="Complete ${t.title}"
@click=${() => this._openConfirm(t.id)}
@keydown=${(o) => {
(o.key === "Enter" || o.key === " ") && (o.preventDefault(), this._openConfirm(t.id));
}}
>
<span class="tile" aria-hidden="true">${tt}</span>
<div class="body">
<span class="quest-title">${t.title}</span>
<span class="meta ${r.late ? "late" : g}">${r.text}</span>
</div>
<button
class="complete"
type="button"
@click=${() => this._openConfirm(t.id)}
>
${R}
<span>Complete</span>
</button>
</div>
`;
}
_renderRollup() {
const t = this._otherChildren();
if (t.length === 0)
return g;
const e = t.slice(0, 2).map((n) => n.present ? n.due > 0 && n.completed >= n.due ? `${n.name} has finished their log.` : n.due > n.completed ? `${n.name} has ${n.due - n.completed} quests left.` : `${n.name} has no quests today.` : n.returnsWeekday ? `${n.name} is on travels until ${n.returnsWeekday}.` : `${n.name} is on travels.`);
return c`
<div class="rollup">
<span class="rollup-label">Party roll-up</span>
<p class="rollup-body">${e.join(" ")}</p>
</div>
`;
}
_renderConfirm() {
if (this._confirm === null)
return g;
const t = this._instances().find(
(n) => n.id === this._confirm
);
if (!t)
return g;
const e = Ot.find((n) => n.key === t.window);
return c`
<div class="scrim" @click=${() => this._closeConfirm()}>
<div
class="dialog"
role="dialog"
aria-modal="true"
@click=${(n) => n.stopPropagation()}
>
<div class="dialog-header">
<span class="dialog-tile" aria-hidden="true">${tt}</span>
<div class="dialog-titles">
<span class="dialog-kicker">
${e?.name ?? "Quest"}
</span>
<span class="dialog-quest-title">${t.title}</span>
</div>
</div>
<p class="dialog-body">
Mark this quest complete? Once the seal is set, only a parent can
undo it.
</p>
<div class="dialog-buttons">
<button
class="confirm-button not-yet"
type="button"
@click=${() => this._closeConfirm()}
>
${Je}
<span>Not yet</span>
</button>
<button
class="confirm-button do-complete"
type="button"
@click=${() => this._confirmComplete()}
>
${R}
<span>Complete</span>
</button>
</div>
</div>
</div>
`;
}
_openConfirm(t) {
this._confirm = t, this._armConfirmTimer();
}
_closeConfirm() {
this._clearConfirmTimer(), this._confirm = null;
}
_confirmComplete() {
const t = this._confirm === null ? void 0 : this._instances().find(
(e) => e.id === this._confirm
);
this._closeConfirm(), !(!t || t.state !== "open") && this._completeQuest(t);
}
/** Seal first, ask the service second: the seal is stamped and the column
* re-sorted immediately (PANEL-SPEC §4); a service failure reverts the
* card and raises the parchment toast. */
async _completeQuest(t) {
if (this._optimistic.has(t.id))
return;
const e = (/* @__PURE__ */ new Date()).toISOString();
this._optimistic = new Map(this._optimistic).set(t.id, e);
try {
await this._callCompleteQuest(t);
} catch {
const n = new Map(this._optimistic);
n.delete(t.id), this._optimistic = n, this._showToast(
"NestQuest could not set the seal just now. Please try again."
);
}
}
async _callCompleteQuest(t) {
const e = this.hass;
if (typeof e?.callService != "function")
throw new Error("Home Assistant is not connected");
const n = this._actorChildId(t);
if (n === null)
throw new Error("The tapped adventurer is unknown");
await e.callService.call(e, "nestquest", "complete_quest", {
instance_id: t.id,
actor: "panel",
actor_child_id: n
});
}
/** The tapped profile (decision 9): the instance's own child_id, falling
* back to the due sensor's child-level attribute. */
_actorChildId(t) {
if (t.child_id !== null)
return t.child_id;
const e = this._childSlug(), n = y(
this._state(`sensor.nestquest_${e}_quests_due_today`)?.attributes?.child_id,
0
);
return n > 0 ? n : null;
}
_showToast(t) {
this._toast = t, this._clearToastTimer(), this._toastTimer = window.setTimeout(() => {
this._toastTimer = void 0, this._toast = null;
}, 6e3);
}
_clearToastTimer() {
this._toastTimer !== void 0 && (window.clearTimeout(this._toastTimer), this._toastTimer = void 0);
}
_armConfirmTimer() {
this._clearConfirmTimer(), this._confirmTimer = window.setTimeout(() => {
this._confirmTimer = void 0, this._closeConfirm();
}, this._confirmSeconds() * 1e3);
}
_clearConfirmTimer() {
this._confirmTimer !== void 0 && (window.clearTimeout(this._confirmTimer), this._confirmTimer = void 0);
}
_armIdle() {
this._clearIdle(), this._idleTimer = window.setTimeout(() => {
this._idleTimer = void 0, this._returnToBoard();
}, this._idleSeconds() * 1e3);
}
_clearIdle() {
this._idleTimer !== void 0 && (window.clearTimeout(this._idleTimer), this._idleTimer = void 0);
}
_armCompleteTimer() {
if (this._completeTimer === void 0) {
if (this._countdown === null && (this._countdown = this._completeSeconds()), this._countdown <= 0) {
this._returnToBoard();
return;
}
this._completeTimer = window.setInterval(() => {
const t = (this._countdown ?? 1) - 1;
if (t <= 0) {
this._countdown = 0, this._clearCompleteTimer(), this._returnToBoard();
return;
}
this._countdown = t;
}, 1e3);
}
}
_clearCompleteTimer() {
this._completeTimer !== void 0 && (window.clearInterval(this._completeTimer), this._completeTimer = void 0);
}
_optionalNumber(t) {
if (t == null || t === "")
return null;
const e = typeof t == "number" ? t : Number(t);
return Number.isFinite(e) ? e : null;
}
_dockWeather() {
const t = $(this._config?.weather_entity);
if (!t)
return null;
const e = this._state(t);
if (!e)
return null;
const n = String(e.state ?? "").trim();
if (!n || n === "unavailable" || n === "unknown")
return null;
const s = e.attributes ?? {};
let r = null, o = null;
const l = s.forecast;
if (Array.isArray(l) && l.length > 0) {
const a = l[0];
if (a && typeof a == "object") {
const p = a;
r = this._optionalNumber(p.temperature), o = this._optionalNumber(p.templow);
}
}
return {
condition: n,
temperature: this._optionalNumber(s.temperature),
high: r,
low: o
};
}
_renderDock() {
const t = this._dockWeather();
if (!t)
return c`
<div class="dock">
<span class="date">${L(this._now, this._timeZone())}</span>
<span class="divider"></span>
<span class="clock">${He(this._now, this._timeZone())}</span>
</div>
`;
const e = Ze(t.condition), n = Ve(t.condition, e), s = t.temperature === null ? g : c`<span class="temp">${Math.round(t.temperature)}°</span>`, r = t.high === null || t.low === null ? null : `${Math.round(t.high)}° / ${Math.round(t.low)}°`;
return c`
<div class="dock">
${rn}
${s}
<span class="divider"></span>
<span class="condition">
${r === null ? e : `${e} · ${r}`}
</span>
<span class="divider"></span>
<span class="condition">${n}</span>
</div>
`;
}
_returnToBoard() {
const t = this._boardPath();
t && (window.history.pushState(null, "", t), window.dispatchEvent(new Event("location-changed")));
}
_stopClock() {
this._clockTimer !== void 0 && (window.clearInterval(this._clockTimer), this._clockTimer = void 0);
}
_onActivity = () => {
this._logView().kind !== "complete-day" && this._armIdle(), this._confirm !== null && this._armConfirmTimer();
};
}
customElements.define("nestquest-quest-log-card", dn);
at({
type: "nestquest-quest-log-card",
name: "NestQuest Quest Log",
description: "One child's daily quests, grouped by window."
});
const cn = ":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 pn extends A {
static properties = {
hass: { attribute: !1 },
_config: { state: !0 }
};
static styles = [b(cn)];
hass;
_config;
setConfig(t) {
if (!t || typeof t != "object")
throw new Error("Invalid configuration");
this._config = t;
}
getCardSize() {
return 8;
}
render() {
return c`<div>NestQuest Admin</div>`;
}
}
customElements.define("nestquest-admin-card", pn);
at({
type: "nestquest-admin-card",
name: "NestQuest Admin",
description: "Parent phone view for today, tasks, schedule, and history."
});
const hn = "/nestquest-static/nestquest-fonts.css";
if (!document.querySelector('link[data-nq-fonts=""]')) {
const i = document.createElement("link");
i.rel = "stylesheet", i.href = hn, i.dataset.nqFonts = "", document.head.appendChild(i);
}