diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 00000000..6f4a5816 --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,9 @@ +name: "CodeQL config" + +# Exclude the Vite-built JS bundle from analysis. It is generated output — +# the authoritative source is the TypeScript under src/, which CodeQL analyses +# directly. Scanning the bundle produces false positives (e.g. lit-html's +# internal /-->/ template-parser regex flagged as a bad HTML sanitiser) with +# no actionable remediation path. +paths-ignore: + - "custom_components/hass_datapoints/hass-datapoints-cards.js" diff --git a/.github/commitlint.config.js b/.github/commitlint.config.mjs similarity index 83% rename from .github/commitlint.config.js rename to .github/commitlint.config.mjs index 5ea5fd56..4be77761 100644 --- a/.github/commitlint.config.js +++ b/.github/commitlint.config.mjs @@ -1,5 +1,5 @@ /** @type {import('@commitlint/types').UserConfig} */ -module.exports = { +export default { extends: ["@commitlint/config-conventional"], rules: { // Allow both `doc` (historically used here) and `docs` (conventional spec) @@ -22,5 +22,7 @@ module.exports = { ], // Allow any subject casing — sentence case reads better for this project "subject-case": [0], + // Allow long body lines for detailed commit descriptions + "body-max-line-length": [0], }, }; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5feace8e..08e9232c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,12 @@ on: branches: [main] workflow_dispatch: +# Default to read-only for all jobs. Jobs that need additional permissions +# (e.g. build provenance attestation) declare them explicitly at the job level, +# which fully overrides this block for that job. +permissions: + contents: read + jobs: # ══════════════════════════════════════════════════════════════════════════════ # Stage 1 — Lint & structural checks (all run in parallel) @@ -101,8 +107,8 @@ jobs: with: python-version: "3.12" - - name: Install pytest - run: pip install pytest pytest-asyncio --quiet + - name: Install Python dependencies + run: pip install -r requirements_dev.txt --quiet - name: Run Python tests run: pytest tests/ -v @@ -197,6 +203,7 @@ jobs: needs: [test-python, test-unit, test-storybook, test-chromatic, hacs] runs-on: ubuntu-latest permissions: + contents: read id-token: write attestations: write steps: diff --git a/.github/workflows/commit-lint.yml b/.github/workflows/commit-lint.yml index a801d443..24d498ba 100644 --- a/.github/workflows/commit-lint.yml +++ b/.github/workflows/commit-lint.yml @@ -9,6 +9,9 @@ jobs: commitlint: name: Validate commit messages runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read steps: - uses: actions/checkout@v4 with: @@ -17,6 +20,6 @@ jobs: - name: Lint commit messages uses: wagoid/commitlint-github-action@v6 with: - configFile: .github/commitlint.config.js + configFile: .github/commitlint.config.mjs failOnWarnings: false helpURL: https://www.conventionalcommits.org/en/v1.0.0/ diff --git a/.npmpackagejsonlintrc.json b/.npmpackagejsonlintrc.json new file mode 100644 index 00000000..5fc6b120 --- /dev/null +++ b/.npmpackagejsonlintrc.json @@ -0,0 +1,6 @@ +{ + "rules": { + "prefer-absolute-version-dependencies": "error", + "prefer-absolute-version-devDependencies": "error" + } +} diff --git a/README.md b/README.md index 68b62afa..96973d88 100644 --- a/README.md +++ b/README.md @@ -1000,5 +1000,5 @@ pnpm dev:watch - CI checks build correctness and integration metadata. - The built frontend bundle is committed as `custom_components/hass_datapoints/hass-datapoints-cards.js`. -- Pre-commit hooks format staged files, validate frontend types, and rebuild the frontend when needed. -- Pre-push hooks run tests, lint checks, and frontend type validation before pushing. +- Pre-commit hooks format staged files, lint package.json versions, validate frontend types, and rebuild the frontend when needed. +- Pre-push hooks run tests, lint checks, package.json version linting, and frontend type validation before pushing. diff --git a/custom_components/hass_datapoints/__init__.py b/custom_components/hass_datapoints/__init__.py index b7df8b29..cd225c17 100644 --- a/custom_components/hass_datapoints/__init__.py +++ b/custom_components/hass_datapoints/__init__.py @@ -70,15 +70,27 @@ def _find_automation_id(hass: HomeAssistant, context) -> str | None: SERVICE_RECORD_SCHEMA = vol.Schema( { - vol.Required(ATTR_MESSAGE): cv.string, - vol.Optional(ATTR_ANNOTATION): cv.string, + vol.Required(ATTR_MESSAGE): vol.All( + cv.string, vol.Length(max=ws_api._MAX_LEN_MESSAGE) + ), + vol.Optional(ATTR_ANNOTATION): vol.All( + cv.string, vol.Length(max=ws_api._MAX_LEN_MESSAGE) + ), vol.Optional(ATTR_ENTITY_IDS): vol.All(cv.ensure_list, [cv.entity_id]), vol.Optional(ATTR_DEVICE_IDS): vol.All(cv.ensure_list, [cv.string]), vol.Optional(ATTR_AREA_IDS): vol.All(cv.ensure_list, [cv.string]), vol.Optional(ATTR_LABEL_IDS): vol.All(cv.ensure_list, [cv.string]), - vol.Optional(ATTR_ICON): cv.string, - vol.Optional(ATTR_COLOR): vol.Any( + vol.Optional(ATTR_ICON): vol.All( cv.string, + vol.Length(max=ws_api._MAX_LEN_ICON), + vol.Match(ws_api._RE_MDI_ICON), + ), + vol.Optional(ATTR_COLOR): vol.Any( + vol.All( + cv.string, + vol.Length(max=ws_api._MAX_LEN_COLOR), + vol.Match(ws_api._RE_HEX_COLOR), + ), vol.All( [vol.Coerce(int)], vol.Length(min=3, max=3), @@ -142,10 +154,21 @@ async def handle_record(call: ServiceCall) -> None: automation_id = _find_automation_id(hass, call.context) + # For user-initiated calls, filter entity_ids to those the caller is + # permitted to read. Automation / system calls (no user_id) bypass + # this check so they can tag events with any entity. + entity_ids = call.data.get(ATTR_ENTITY_IDS) + if call.context.user_id: + user = await hass.auth.async_get_user(call.context.user_id) + if user is not None and not user.is_admin and entity_ids: + entity_ids = [ + eid for eid in entity_ids if ws_api._can_read_entity(user, eid) + ] + event_data = await store.async_record( message=call.data[ATTR_MESSAGE], annotation=call.data.get(ATTR_ANNOTATION), - entity_ids=call.data.get(ATTR_ENTITY_IDS), + entity_ids=entity_ids, device_ids=call.data.get(ATTR_DEVICE_IDS), area_ids=call.data.get(ATTR_AREA_IDS), label_ids=call.data.get(ATTR_LABEL_IDS), diff --git a/custom_components/hass_datapoints/hass-datapoints-cards.js b/custom_components/hass_datapoints/hass-datapoints-cards.js index b05dce2c..c8d7ffd2 100644 --- a/custom_components/hass_datapoints/hass-datapoints-cards.js +++ b/custom_components/hass_datapoints/hass-datapoints-cards.js @@ -1,506 +1,595 @@ (function() { - "use strict"; - const t$4 = globalThis, e$4 = t$4.ShadowRoot && (void 0 === t$4.ShadyCSS || t$4.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, s$3 = /* @__PURE__ */ Symbol(), o$4 = /* @__PURE__ */ new WeakMap(); - let n$3 = class n { - constructor(t2, e2, o2) { - if (this._$cssResult$ = true, o2 !== s$3) throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead."); - this.cssText = t2, this.t = e2; - } - get styleSheet() { - let t2 = this.o; - const s2 = this.t; - if (e$4 && void 0 === t2) { - const e2 = void 0 !== s2 && 1 === s2.length; - e2 && (t2 = o$4.get(s2)), void 0 === t2 && ((this.o = t2 = new CSSStyleSheet()).replaceSync(this.cssText), e2 && o$4.set(s2, t2)); - } - return t2; - } - toString() { - return this.cssText; - } - }; - const r$4 = (t2) => new n$3("string" == typeof t2 ? t2 : t2 + "", void 0, s$3), i$5 = (t2, ...e2) => { - const o2 = 1 === t2.length ? t2[0] : e2.reduce((e3, s2, o3) => e3 + ((t3) => { - if (true === t3._$cssResult$) return t3.cssText; - if ("number" == typeof t3) return t3; - throw Error("Value passed to 'css' function must be a 'css' function result: " + t3 + ". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security."); - })(s2) + t2[o3 + 1], t2[0]); - return new n$3(o2, t2, s$3); - }, S$1 = (s2, o2) => { - if (e$4) s2.adoptedStyleSheets = o2.map((t2) => t2 instanceof CSSStyleSheet ? t2 : t2.styleSheet); - else for (const e2 of o2) { - const o3 = document.createElement("style"), n2 = t$4.litNonce; - void 0 !== n2 && o3.setAttribute("nonce", n2), o3.textContent = e2.cssText, s2.appendChild(o3); - } - }, c$3 = e$4 ? (t2) => t2 : (t2) => t2 instanceof CSSStyleSheet ? ((t3) => { - let e2 = ""; - for (const s2 of t3.cssRules) e2 += s2.cssText; - return r$4(e2); - })(t2) : t2; - const { is: i$4, defineProperty: e$3, getOwnPropertyDescriptor: h$2, getOwnPropertyNames: r$3, getOwnPropertySymbols: o$3, getPrototypeOf: n$2 } = Object, a$1 = globalThis, c$2 = a$1.trustedTypes, l$1 = c$2 ? c$2.emptyScript : "", p$2 = a$1.reactiveElementPolyfillSupport, d$1 = (t2, s2) => t2, u$3 = { toAttribute(t2, s2) { - switch (s2) { - case Boolean: - t2 = t2 ? l$1 : null; - break; - case Object: - case Array: - t2 = null == t2 ? t2 : JSON.stringify(t2); - } - return t2; - }, fromAttribute(t2, s2) { - let i2 = t2; - switch (s2) { - case Boolean: - i2 = null !== t2; - break; - case Number: - i2 = null === t2 ? null : Number(t2); - break; - case Object: - case Array: - try { - i2 = JSON.parse(t2); - } catch (t3) { - i2 = null; - } - } - return i2; - } }, f$1 = (t2, s2) => !i$4(t2, s2), b$1 = { attribute: true, type: String, converter: u$3, reflect: false, useDefault: false, hasChanged: f$1 }; - Symbol.metadata ??= /* @__PURE__ */ Symbol("metadata"), a$1.litPropertyMetadata ??= /* @__PURE__ */ new WeakMap(); - let y$1 = class y extends HTMLElement { - static addInitializer(t2) { - this._$Ei(), (this.l ??= []).push(t2); - } - static get observedAttributes() { - return this.finalize(), this._$Eh && [...this._$Eh.keys()]; - } - static createProperty(t2, s2 = b$1) { - if (s2.state && (s2.attribute = false), this._$Ei(), this.prototype.hasOwnProperty(t2) && ((s2 = Object.create(s2)).wrapped = true), this.elementProperties.set(t2, s2), !s2.noAccessor) { - const i2 = /* @__PURE__ */ Symbol(), h2 = this.getPropertyDescriptor(t2, i2, s2); - void 0 !== h2 && e$3(this.prototype, t2, h2); - } - } - static getPropertyDescriptor(t2, s2, i2) { - const { get: e2, set: r2 } = h$2(this.prototype, t2) ?? { get() { - return this[s2]; - }, set(t3) { - this[s2] = t3; - } }; - return { get: e2, set(s3) { - const h2 = e2?.call(this); - r2?.call(this, s3), this.requestUpdate(t2, h2, i2); - }, configurable: true, enumerable: true }; - } - static getPropertyOptions(t2) { - return this.elementProperties.get(t2) ?? b$1; - } - static _$Ei() { - if (this.hasOwnProperty(d$1("elementProperties"))) return; - const t2 = n$2(this); - t2.finalize(), void 0 !== t2.l && (this.l = [...t2.l]), this.elementProperties = new Map(t2.elementProperties); - } - static finalize() { - if (this.hasOwnProperty(d$1("finalized"))) return; - if (this.finalized = true, this._$Ei(), this.hasOwnProperty(d$1("properties"))) { - const t3 = this.properties, s2 = [...r$3(t3), ...o$3(t3)]; - for (const i2 of s2) this.createProperty(i2, t3[i2]); - } - const t2 = this[Symbol.metadata]; - if (null !== t2) { - const s2 = litPropertyMetadata.get(t2); - if (void 0 !== s2) for (const [t3, i2] of s2) this.elementProperties.set(t3, i2); - } - this._$Eh = /* @__PURE__ */ new Map(); - for (const [t3, s2] of this.elementProperties) { - const i2 = this._$Eu(t3, s2); - void 0 !== i2 && this._$Eh.set(i2, t3); - } - this.elementStyles = this.finalizeStyles(this.styles); - } - static finalizeStyles(s2) { - const i2 = []; - if (Array.isArray(s2)) { - const e2 = new Set(s2.flat(1 / 0).reverse()); - for (const s3 of e2) i2.unshift(c$3(s3)); - } else void 0 !== s2 && i2.push(c$3(s2)); - return i2; - } - static _$Eu(t2, s2) { - const i2 = s2.attribute; - return false === i2 ? void 0 : "string" == typeof i2 ? i2 : "string" == typeof t2 ? t2.toLowerCase() : void 0; - } - constructor() { - super(), this._$Ep = void 0, this.isUpdatePending = false, this.hasUpdated = false, this._$Em = null, this._$Ev(); - } - _$Ev() { - this._$ES = new Promise((t2) => this.enableUpdating = t2), this._$AL = /* @__PURE__ */ new Map(), this._$E_(), this.requestUpdate(), this.constructor.l?.forEach((t2) => t2(this)); - } - addController(t2) { - (this._$EO ??= /* @__PURE__ */ new Set()).add(t2), void 0 !== this.renderRoot && this.isConnected && t2.hostConnected?.(); - } - removeController(t2) { - this._$EO?.delete(t2); - } - _$E_() { - const t2 = /* @__PURE__ */ new Map(), s2 = this.constructor.elementProperties; - for (const i2 of s2.keys()) this.hasOwnProperty(i2) && (t2.set(i2, this[i2]), delete this[i2]); - t2.size > 0 && (this._$Ep = t2); - } - createRenderRoot() { - const t2 = this.shadowRoot ?? this.attachShadow(this.constructor.shadowRootOptions); - return S$1(t2, this.constructor.elementStyles), t2; - } - connectedCallback() { - this.renderRoot ??= this.createRenderRoot(), this.enableUpdating(true), this._$EO?.forEach((t2) => t2.hostConnected?.()); - } - enableUpdating(t2) { - } - disconnectedCallback() { - this._$EO?.forEach((t2) => t2.hostDisconnected?.()); - } - attributeChangedCallback(t2, s2, i2) { - this._$AK(t2, i2); - } - _$ET(t2, s2) { - const i2 = this.constructor.elementProperties.get(t2), e2 = this.constructor._$Eu(t2, i2); - if (void 0 !== e2 && true === i2.reflect) { - const h2 = (void 0 !== i2.converter?.toAttribute ? i2.converter : u$3).toAttribute(s2, i2.type); - this._$Em = t2, null == h2 ? this.removeAttribute(e2) : this.setAttribute(e2, h2), this._$Em = null; - } - } - _$AK(t2, s2) { - const i2 = this.constructor, e2 = i2._$Eh.get(t2); - if (void 0 !== e2 && this._$Em !== e2) { - const t3 = i2.getPropertyOptions(e2), h2 = "function" == typeof t3.converter ? { fromAttribute: t3.converter } : void 0 !== t3.converter?.fromAttribute ? t3.converter : u$3; - this._$Em = e2; - const r2 = h2.fromAttribute(s2, t3.type); - this[e2] = r2 ?? this._$Ej?.get(e2) ?? r2, this._$Em = null; - } - } - requestUpdate(t2, s2, i2, e2 = false, h2) { - if (void 0 !== t2) { - const r2 = this.constructor; - if (false === e2 && (h2 = this[t2]), i2 ??= r2.getPropertyOptions(t2), !((i2.hasChanged ?? f$1)(h2, s2) || i2.useDefault && i2.reflect && h2 === this._$Ej?.get(t2) && !this.hasAttribute(r2._$Eu(t2, i2)))) return; - this.C(t2, s2, i2); - } - false === this.isUpdatePending && (this._$ES = this._$EP()); - } - C(t2, s2, { useDefault: i2, reflect: e2, wrapped: h2 }, r2) { - i2 && !(this._$Ej ??= /* @__PURE__ */ new Map()).has(t2) && (this._$Ej.set(t2, r2 ?? s2 ?? this[t2]), true !== h2 || void 0 !== r2) || (this._$AL.has(t2) || (this.hasUpdated || i2 || (s2 = void 0), this._$AL.set(t2, s2)), true === e2 && this._$Em !== t2 && (this._$Eq ??= /* @__PURE__ */ new Set()).add(t2)); - } - async _$EP() { - this.isUpdatePending = true; - try { - await this._$ES; - } catch (t3) { - Promise.reject(t3); - } - const t2 = this.scheduleUpdate(); - return null != t2 && await t2, !this.isUpdatePending; - } - scheduleUpdate() { - return this.performUpdate(); - } - performUpdate() { - if (!this.isUpdatePending) return; - if (!this.hasUpdated) { - if (this.renderRoot ??= this.createRenderRoot(), this._$Ep) { - for (const [t4, s3] of this._$Ep) this[t4] = s3; - this._$Ep = void 0; - } - const t3 = this.constructor.elementProperties; - if (t3.size > 0) for (const [s3, i2] of t3) { - const { wrapped: t4 } = i2, e2 = this[s3]; - true !== t4 || this._$AL.has(s3) || void 0 === e2 || this.C(s3, void 0, i2, e2); - } - } - let t2 = false; - const s2 = this._$AL; - try { - t2 = this.shouldUpdate(s2), t2 ? (this.willUpdate(s2), this._$EO?.forEach((t3) => t3.hostUpdate?.()), this.update(s2)) : this._$EM(); - } catch (s3) { - throw t2 = false, this._$EM(), s3; - } - t2 && this._$AE(s2); - } - willUpdate(t2) { - } - _$AE(t2) { - this._$EO?.forEach((t3) => t3.hostUpdated?.()), this.hasUpdated || (this.hasUpdated = true, this.firstUpdated(t2)), this.updated(t2); - } - _$EM() { - this._$AL = /* @__PURE__ */ new Map(), this.isUpdatePending = false; - } - get updateComplete() { - return this.getUpdateComplete(); - } - getUpdateComplete() { - return this._$ES; - } - shouldUpdate(t2) { - return true; - } - update(t2) { - this._$Eq &&= this._$Eq.forEach((t3) => this._$ET(t3, this[t3])), this._$EM(); - } - updated(t2) { - } - firstUpdated(t2) { - } - }; - y$1.elementStyles = [], y$1.shadowRootOptions = { mode: "open" }, y$1[d$1("elementProperties")] = /* @__PURE__ */ new Map(), y$1[d$1("finalized")] = /* @__PURE__ */ new Map(), p$2?.({ ReactiveElement: y$1 }), (a$1.reactiveElementVersions ??= []).push("2.1.2"); - const t$3 = globalThis, i$3 = (t2) => t2, s$2 = t$3.trustedTypes, e$2 = s$2 ? s$2.createPolicy("lit-html", { createHTML: (t2) => t2 }) : void 0, h$1 = "$lit$", o$2 = `lit$${Math.random().toFixed(9).slice(2)}$`, n$1 = "?" + o$2, r$2 = `<${n$1}>`, l = document, c$1 = () => l.createComment(""), a = (t2) => null === t2 || "object" != typeof t2 && "function" != typeof t2, u$2 = Array.isArray, d = (t2) => u$2(t2) || "function" == typeof t2?.[Symbol.iterator], f = "[ \n\f\r]", v$1 = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g, _ = /-->/g, m$1 = />/g, p$1 = RegExp(`>|${f}(?:([^\\s"'>=/]+)(${f}*=${f}*(?:[^ -\f\r"'\`<>=]|("|')|))|$)`, "g"), g = /'/g, $ = /"/g, y = /^(?:script|style|textarea|title)$/i, x = (t2) => (i2, ...s2) => ({ _$litType$: t2, strings: i2, values: s2 }), b = x(1), E = /* @__PURE__ */ Symbol.for("lit-noChange"), A = /* @__PURE__ */ Symbol.for("lit-nothing"), C = /* @__PURE__ */ new WeakMap(), P = l.createTreeWalker(l, 129); - function V(t2, i2) { - if (!u$2(t2) || !t2.hasOwnProperty("raw")) throw Error("invalid template strings array"); - return void 0 !== e$2 ? e$2.createHTML(i2) : i2; - } - const N = (t2, i2) => { - const s2 = t2.length - 1, e2 = []; - let n2, l2 = 2 === i2 ? "" : 3 === i2 ? "" : "", c2 = v$1; - for (let i3 = 0; i3 < s2; i3++) { - const s3 = t2[i3]; - let a2, u2, d2 = -1, f2 = 0; - for (; f2 < s3.length && (c2.lastIndex = f2, u2 = c2.exec(s3), null !== u2); ) f2 = c2.lastIndex, c2 === v$1 ? "!--" === u2[1] ? c2 = _ : void 0 !== u2[1] ? c2 = m$1 : void 0 !== u2[2] ? (y.test(u2[2]) && (n2 = RegExp("" === u2[0] ? (c2 = n2 ?? v$1, d2 = -1) : void 0 === u2[1] ? d2 = -2 : (d2 = c2.lastIndex - u2[2].length, a2 = u2[1], c2 = void 0 === u2[3] ? p$1 : '"' === u2[3] ? $ : g) : c2 === $ || c2 === g ? c2 = p$1 : c2 === _ || c2 === m$1 ? c2 = v$1 : (c2 = p$1, n2 = void 0); - const x2 = c2 === p$1 && t2[i3 + 1].startsWith("/>") ? " " : ""; - l2 += c2 === v$1 ? s3 + r$2 : d2 >= 0 ? (e2.push(a2), s3.slice(0, d2) + h$1 + s3.slice(d2) + o$2 + x2) : s3 + o$2 + (-2 === d2 ? i3 : x2); - } - return [V(t2, l2 + (t2[s2] || "") + (2 === i2 ? "" : 3 === i2 ? "" : "")), e2]; - }; - class S { - constructor({ strings: t2, _$litType$: i2 }, e2) { - let r2; - this.parts = []; - let l2 = 0, a2 = 0; - const u2 = t2.length - 1, d2 = this.parts, [f2, v2] = N(t2, i2); - if (this.el = S.createElement(f2, e2), P.currentNode = this.el.content, 2 === i2 || 3 === i2) { - const t3 = this.el.content.firstChild; - t3.replaceWith(...t3.childNodes); - } - for (; null !== (r2 = P.nextNode()) && d2.length < u2; ) { - if (1 === r2.nodeType) { - if (r2.hasAttributes()) for (const t3 of r2.getAttributeNames()) if (t3.endsWith(h$1)) { - const i3 = v2[a2++], s2 = r2.getAttribute(t3).split(o$2), e3 = /([.?@])?(.*)/.exec(i3); - d2.push({ type: 1, index: l2, name: e3[2], strings: s2, ctor: "." === e3[1] ? I : "?" === e3[1] ? L : "@" === e3[1] ? z : H }), r2.removeAttribute(t3); - } else t3.startsWith(o$2) && (d2.push({ type: 6, index: l2 }), r2.removeAttribute(t3)); - if (y.test(r2.tagName)) { - const t3 = r2.textContent.split(o$2), i3 = t3.length - 1; - if (i3 > 0) { - r2.textContent = s$2 ? s$2.emptyScript : ""; - for (let s2 = 0; s2 < i3; s2++) r2.append(t3[s2], c$1()), P.nextNode(), d2.push({ type: 2, index: ++l2 }); - r2.append(t3[i3], c$1()); - } - } - } else if (8 === r2.nodeType) if (r2.data === n$1) d2.push({ type: 2, index: l2 }); - else { - let t3 = -1; - for (; -1 !== (t3 = r2.data.indexOf(o$2, t3 + 1)); ) d2.push({ type: 7, index: l2 }), t3 += o$2.length - 1; - } - l2++; - } - } - static createElement(t2, i2) { - const s2 = l.createElement("template"); - return s2.innerHTML = t2, s2; - } - } - function M$1(t2, i2, s2 = t2, e2) { - if (i2 === E) return i2; - let h2 = void 0 !== e2 ? s2._$Co?.[e2] : s2._$Cl; - const o2 = a(i2) ? void 0 : i2._$litDirective$; - return h2?.constructor !== o2 && (h2?._$AO?.(false), void 0 === o2 ? h2 = void 0 : (h2 = new o2(t2), h2._$AT(t2, s2, e2)), void 0 !== e2 ? (s2._$Co ??= [])[e2] = h2 : s2._$Cl = h2), void 0 !== h2 && (i2 = M$1(t2, h2._$AS(t2, i2.values), h2, e2)), i2; - } - class R { - constructor(t2, i2) { - this._$AV = [], this._$AN = void 0, this._$AD = t2, this._$AM = i2; - } - get parentNode() { - return this._$AM.parentNode; - } - get _$AU() { - return this._$AM._$AU; - } - u(t2) { - const { el: { content: i2 }, parts: s2 } = this._$AD, e2 = (t2?.creationScope ?? l).importNode(i2, true); - P.currentNode = e2; - let h2 = P.nextNode(), o2 = 0, n2 = 0, r2 = s2[0]; - for (; void 0 !== r2; ) { - if (o2 === r2.index) { - let i3; - 2 === r2.type ? i3 = new k(h2, h2.nextSibling, this, t2) : 1 === r2.type ? i3 = new r2.ctor(h2, r2.name, r2.strings, this, t2) : 6 === r2.type && (i3 = new Z(h2, this, t2)), this._$AV.push(i3), r2 = s2[++n2]; - } - o2 !== r2?.index && (h2 = P.nextNode(), o2++); - } - return P.currentNode = l, e2; - } - p(t2) { - let i2 = 0; - for (const s2 of this._$AV) void 0 !== s2 && (void 0 !== s2.strings ? (s2._$AI(t2, s2, i2), i2 += s2.strings.length - 2) : s2._$AI(t2[i2])), i2++; - } - } - class k { - get _$AU() { - return this._$AM?._$AU ?? this._$Cv; - } - constructor(t2, i2, s2, e2) { - this.type = 2, this._$AH = A, this._$AN = void 0, this._$AA = t2, this._$AB = i2, this._$AM = s2, this.options = e2, this._$Cv = e2?.isConnected ?? true; - } - get parentNode() { - let t2 = this._$AA.parentNode; - const i2 = this._$AM; - return void 0 !== i2 && 11 === t2?.nodeType && (t2 = i2.parentNode), t2; - } - get startNode() { - return this._$AA; - } - get endNode() { - return this._$AB; - } - _$AI(t2, i2 = this) { - t2 = M$1(this, t2, i2), a(t2) ? t2 === A || null == t2 || "" === t2 ? (this._$AH !== A && this._$AR(), this._$AH = A) : t2 !== this._$AH && t2 !== E && this._(t2) : void 0 !== t2._$litType$ ? this.$(t2) : void 0 !== t2.nodeType ? this.T(t2) : d(t2) ? this.k(t2) : this._(t2); - } - O(t2) { - return this._$AA.parentNode.insertBefore(t2, this._$AB); - } - T(t2) { - this._$AH !== t2 && (this._$AR(), this._$AH = this.O(t2)); - } - _(t2) { - this._$AH !== A && a(this._$AH) ? this._$AA.nextSibling.data = t2 : this.T(l.createTextNode(t2)), this._$AH = t2; - } - $(t2) { - const { values: i2, _$litType$: s2 } = t2, e2 = "number" == typeof s2 ? this._$AC(t2) : (void 0 === s2.el && (s2.el = S.createElement(V(s2.h, s2.h[0]), this.options)), s2); - if (this._$AH?._$AD === e2) this._$AH.p(i2); - else { - const t3 = new R(e2, this), s3 = t3.u(this.options); - t3.p(i2), this.T(s3), this._$AH = t3; - } - } - _$AC(t2) { - let i2 = C.get(t2.strings); - return void 0 === i2 && C.set(t2.strings, i2 = new S(t2)), i2; - } - k(t2) { - u$2(this._$AH) || (this._$AH = [], this._$AR()); - const i2 = this._$AH; - let s2, e2 = 0; - for (const h2 of t2) e2 === i2.length ? i2.push(s2 = new k(this.O(c$1()), this.O(c$1()), this, this.options)) : s2 = i2[e2], s2._$AI(h2), e2++; - e2 < i2.length && (this._$AR(s2 && s2._$AB.nextSibling, e2), i2.length = e2); - } - _$AR(t2 = this._$AA.nextSibling, s2) { - for (this._$AP?.(false, true, s2); t2 !== this._$AB; ) { - const s3 = i$3(t2).nextSibling; - i$3(t2).remove(), t2 = s3; - } - } - setConnected(t2) { - void 0 === this._$AM && (this._$Cv = t2, this._$AP?.(t2)); - } - } - class H { - get tagName() { - return this.element.tagName; - } - get _$AU() { - return this._$AM._$AU; - } - constructor(t2, i2, s2, e2, h2) { - this.type = 1, this._$AH = A, this._$AN = void 0, this.element = t2, this.name = i2, this._$AM = e2, this.options = h2, s2.length > 2 || "" !== s2[0] || "" !== s2[1] ? (this._$AH = Array(s2.length - 1).fill(new String()), this.strings = s2) : this._$AH = A; - } - _$AI(t2, i2 = this, s2, e2) { - const h2 = this.strings; - let o2 = false; - if (void 0 === h2) t2 = M$1(this, t2, i2, 0), o2 = !a(t2) || t2 !== this._$AH && t2 !== E, o2 && (this._$AH = t2); - else { - const e3 = t2; - let n2, r2; - for (t2 = h2[0], n2 = 0; n2 < h2.length - 1; n2++) r2 = M$1(this, e3[s2 + n2], i2, n2), r2 === E && (r2 = this._$AH[n2]), o2 ||= !a(r2) || r2 !== this._$AH[n2], r2 === A ? t2 = A : t2 !== A && (t2 += (r2 ?? "") + h2[n2 + 1]), this._$AH[n2] = r2; - } - o2 && !e2 && this.j(t2); - } - j(t2) { - t2 === A ? this.element.removeAttribute(this.name) : this.element.setAttribute(this.name, t2 ?? ""); - } - } - class I extends H { - constructor() { - super(...arguments), this.type = 3; - } - j(t2) { - this.element[this.name] = t2 === A ? void 0 : t2; - } - } - class L extends H { - constructor() { - super(...arguments), this.type = 4; - } - j(t2) { - this.element.toggleAttribute(this.name, !!t2 && t2 !== A); - } - } - class z extends H { - constructor(t2, i2, s2, e2, h2) { - super(t2, i2, s2, e2, h2), this.type = 5; - } - _$AI(t2, i2 = this) { - if ((t2 = M$1(this, t2, i2, 0) ?? A) === E) return; - const s2 = this._$AH, e2 = t2 === A && s2 !== A || t2.capture !== s2.capture || t2.once !== s2.once || t2.passive !== s2.passive, h2 = t2 !== A && (s2 === A || e2); - e2 && this.element.removeEventListener(this.name, this, s2), h2 && this.element.addEventListener(this.name, this, t2), this._$AH = t2; - } - handleEvent(t2) { - "function" == typeof this._$AH ? this._$AH.call(this.options?.host ?? this.element, t2) : this._$AH.handleEvent(t2); - } - } - class Z { - constructor(t2, i2, s2) { - this.element = t2, this.type = 6, this._$AN = void 0, this._$AM = i2, this.options = s2; - } - get _$AU() { - return this._$AM._$AU; - } - _$AI(t2) { - M$1(this, t2); - } - } - const j = { I: k }, B = t$3.litHtmlPolyfillSupport; - B?.(S, k), (t$3.litHtmlVersions ??= []).push("3.3.2"); - const D = (t2, i2, s2) => { - const e2 = s2?.renderBefore ?? i2; - let h2 = e2._$litPart$; - if (void 0 === h2) { - const t3 = s2?.renderBefore ?? null; - e2._$litPart$ = h2 = new k(i2.insertBefore(c$1(), t3), t3, void 0, s2 ?? {}); - } - return h2._$AI(t2), h2; - }; - const s$1 = globalThis; - let i$2 = class i extends y$1 { - constructor() { - super(...arguments), this.renderOptions = { host: this }, this._$Do = void 0; - } - createRenderRoot() { - const t2 = super.createRenderRoot(); - return this.renderOptions.renderBefore ??= t2.firstChild, t2; - } - update(t2) { - const r2 = this.render(); - this.hasUpdated || (this.renderOptions.isConnected = this.isConnected), super.update(t2), this._$Do = D(r2, this.renderRoot, this.renderOptions); - } - connectedCallback() { - super.connectedCallback(), this._$Do?.setConnected(true); - } - disconnectedCallback() { - super.disconnectedCallback(), this._$Do?.setConnected(false); - } - render() { - return E; - } - }; - i$2._$litElement$ = true, i$2["finalized"] = true, s$1.litElementHydrateSupport?.({ LitElement: i$2 }); - const o$1 = s$1.litElementPolyfillSupport; - o$1?.({ LitElement: i$2 }); - (s$1.litElementVersions ??= []).push("4.2.2"); - const styles$16 = i$5` + //#region \0rolldown/runtime.js + var __defProp = Object.defineProperty; + var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res); + var __exportAll = (all, no_symbols) => { + let target = {}; + for (var name in all) __defProp(target, name, { + get: all[name], + enumerable: true + }); + if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" }); + return target; + }; + //#endregion + //#region node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/css-tag.js + /** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ + var t$4 = globalThis, e$5 = t$4.ShadowRoot && (void 0 === t$4.ShadyCSS || t$4.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, s$3 = Symbol(), o$4 = /* @__PURE__ */ new WeakMap(); + var n$4 = class { + constructor(t, e, o) { + if (this._$cssResult$ = !0, o !== s$3) throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead."); + this.cssText = t, this.t = e; + } + get styleSheet() { + let t = this.o; + const s = this.t; + if (e$5 && void 0 === t) { + const e = void 0 !== s && 1 === s.length; + e && (t = o$4.get(s)), void 0 === t && ((this.o = t = new CSSStyleSheet()).replaceSync(this.cssText), e && o$4.set(s, t)); + } + return t; + } + toString() { + return this.cssText; + } + }; + var r$5 = (t) => new n$4("string" == typeof t ? t : t + "", void 0, s$3), i$5 = (t, ...e) => { + return new n$4(1 === t.length ? t[0] : e.reduce((e, s, o) => e + ((t) => { + if (!0 === t._$cssResult$) return t.cssText; + if ("number" == typeof t) return t; + throw Error("Value passed to 'css' function must be a 'css' function result: " + t + ". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security."); + })(s) + t[o + 1], t[0]), t, s$3); + }, S$1 = (s, o) => { + if (e$5) s.adoptedStyleSheets = o.map((t) => t instanceof CSSStyleSheet ? t : t.styleSheet); + else for (const e of o) { + const o = document.createElement("style"), n = t$4.litNonce; + void 0 !== n && o.setAttribute("nonce", n), o.textContent = e.cssText, s.appendChild(o); + } + }, c$4 = e$5 ? (t) => t : (t) => t instanceof CSSStyleSheet ? ((t) => { + let e = ""; + for (const s of t.cssRules) e += s.cssText; + return r$5(e); + })(t) : t; + //#endregion + //#region node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/reactive-element.js + /** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ var { is: i$4, defineProperty: e$4, getOwnPropertyDescriptor: h$2, getOwnPropertyNames: r$4, getOwnPropertySymbols: o$3, getPrototypeOf: n$3 } = Object, a$1 = globalThis, c$3 = a$1.trustedTypes, l$2 = c$3 ? c$3.emptyScript : "", p$2 = a$1.reactiveElementPolyfillSupport, d$2 = (t, s) => t, u$3 = { + toAttribute(t, s) { + switch (s) { + case Boolean: + t = t ? l$2 : null; + break; + case Object: + case Array: t = null == t ? t : JSON.stringify(t); + } + return t; + }, + fromAttribute(t, s) { + let i = t; + switch (s) { + case Boolean: + i = null !== t; + break; + case Number: + i = null === t ? null : Number(t); + break; + case Object: + case Array: try { + i = JSON.parse(t); + } catch (t) { + i = null; + } + } + return i; + } + }, f$2 = (t, s) => !i$4(t, s), b$1 = { + attribute: !0, + type: String, + converter: u$3, + reflect: !1, + useDefault: !1, + hasChanged: f$2 + }; + Symbol.metadata ??= Symbol("metadata"), a$1.litPropertyMetadata ??= /* @__PURE__ */ new WeakMap(); + var y$1 = 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, s = b$1) { + if (s.state && (s.attribute = !1), this._$Ei(), this.prototype.hasOwnProperty(t) && ((s = Object.create(s)).wrapped = !0), this.elementProperties.set(t, s), !s.noAccessor) { + const i = Symbol(), h = this.getPropertyDescriptor(t, i, s); + void 0 !== h && e$4(this.prototype, t, h); + } + } + static getPropertyDescriptor(t, s, i) { + const { get: e, set: r } = h$2(this.prototype, t) ?? { + get() { + return this[s]; + }, + set(t) { + this[s] = t; + } + }; + return { + get: e, + set(s) { + const h = e?.call(this); + r?.call(this, s), this.requestUpdate(t, h, i); + }, + configurable: !0, + enumerable: !0 + }; + } + static getPropertyOptions(t) { + return this.elementProperties.get(t) ?? b$1; + } + static _$Ei() { + if (this.hasOwnProperty(d$2("elementProperties"))) return; + const t = n$3(this); + t.finalize(), void 0 !== t.l && (this.l = [...t.l]), this.elementProperties = new Map(t.elementProperties); + } + static finalize() { + if (this.hasOwnProperty(d$2("finalized"))) return; + if (this.finalized = !0, this._$Ei(), this.hasOwnProperty(d$2("properties"))) { + const t = this.properties, s = [...r$4(t), ...o$3(t)]; + for (const i of s) this.createProperty(i, t[i]); + } + const t = this[Symbol.metadata]; + if (null !== t) { + const s = litPropertyMetadata.get(t); + if (void 0 !== s) for (const [t, i] of s) this.elementProperties.set(t, i); + } + this._$Eh = /* @__PURE__ */ new Map(); + for (const [t, s] of this.elementProperties) { + const i = this._$Eu(t, s); + void 0 !== i && this._$Eh.set(i, t); + } + this.elementStyles = this.finalizeStyles(this.styles); + } + static finalizeStyles(s) { + const i = []; + if (Array.isArray(s)) { + const e = new Set(s.flat(Infinity).reverse()); + for (const s of e) i.unshift(c$4(s)); + } else void 0 !== s && i.push(c$4(s)); + return i; + } + static _$Eu(t, s) { + const i = s.attribute; + return !1 === i ? void 0 : "string" == typeof i ? i : "string" == typeof t ? 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), void 0 !== this.renderRoot && this.isConnected && t.hostConnected?.(); + } + removeController(t) { + this._$EO?.delete(t); + } + _$E_() { + const t = /* @__PURE__ */ new Map(), s = this.constructor.elementProperties; + for (const i of s.keys()) this.hasOwnProperty(i) && (t.set(i, this[i]), delete this[i]); + t.size > 0 && (this._$Ep = t); + } + createRenderRoot() { + const t = this.shadowRoot ?? this.attachShadow(this.constructor.shadowRootOptions); + return S$1(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, s, i) { + this._$AK(t, i); + } + _$ET(t, s) { + const i = this.constructor.elementProperties.get(t), e = this.constructor._$Eu(t, i); + if (void 0 !== e && !0 === i.reflect) { + const h = (void 0 !== i.converter?.toAttribute ? i.converter : u$3).toAttribute(s, i.type); + this._$Em = t, null == h ? this.removeAttribute(e) : this.setAttribute(e, h), this._$Em = null; + } + } + _$AK(t, s) { + const i = this.constructor, e = i._$Eh.get(t); + if (void 0 !== e && this._$Em !== e) { + const t = i.getPropertyOptions(e), h = "function" == typeof t.converter ? { fromAttribute: t.converter } : void 0 !== t.converter?.fromAttribute ? t.converter : u$3; + this._$Em = e; + const r = h.fromAttribute(s, t.type); + this[e] = r ?? this._$Ej?.get(e) ?? r, this._$Em = null; + } + } + requestUpdate(t, s, i, e = !1, h) { + if (void 0 !== t) { + const r = this.constructor; + if (!1 === e && (h = this[t]), i ??= r.getPropertyOptions(t), !((i.hasChanged ?? f$2)(h, s) || i.useDefault && i.reflect && h === this._$Ej?.get(t) && !this.hasAttribute(r._$Eu(t, i)))) return; + this.C(t, s, i); + } + !1 === this.isUpdatePending && (this._$ES = this._$EP()); + } + C(t, s, { useDefault: i, reflect: e, wrapped: h }, r) { + i && !(this._$Ej ??= /* @__PURE__ */ new Map()).has(t) && (this._$Ej.set(t, r ?? s ?? this[t]), !0 !== h || void 0 !== r) || (this._$AL.has(t) || (this.hasUpdated || i || (s = void 0), this._$AL.set(t, s)), !0 === e && this._$Em !== t && (this._$Eq ??= /* @__PURE__ */ new Set()).add(t)); + } + async _$EP() { + this.isUpdatePending = !0; + try { + await this._$ES; + } catch (t) { + Promise.reject(t); + } + const t = this.scheduleUpdate(); + return null != t && 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 [t, s] of this._$Ep) this[t] = s; + this._$Ep = void 0; + } + const t = this.constructor.elementProperties; + if (t.size > 0) for (const [s, i] of t) { + const { wrapped: t } = i, e = this[s]; + !0 !== t || this._$AL.has(s) || void 0 === e || this.C(s, void 0, i, e); + } + } + let t = !1; + const s = this._$AL; + try { + t = this.shouldUpdate(s), t ? (this.willUpdate(s), this._$EO?.forEach((t) => t.hostUpdate?.()), this.update(s)) : this._$EM(); + } catch (s) { + throw t = !1, this._$EM(), s; + } + t && this._$AE(s); + } + willUpdate(t) {} + _$AE(t) { + this._$EO?.forEach((t) => t.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((t) => this._$ET(t, this[t])), this._$EM(); + } + updated(t) {} + firstUpdated(t) {} + }; + y$1.elementStyles = [], y$1.shadowRootOptions = { mode: "open" }, y$1[d$2("elementProperties")] = /* @__PURE__ */ new Map(), y$1[d$2("finalized")] = /* @__PURE__ */ new Map(), p$2?.({ ReactiveElement: y$1 }), (a$1.reactiveElementVersions ??= []).push("2.1.2"); + //#endregion + //#region node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/lit-html.js + /** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ + var t$3 = globalThis, i$3 = (t) => t, s$2 = t$3.trustedTypes, e$3 = s$2 ? s$2.createPolicy("lit-html", { createHTML: (t) => t }) : void 0, h$1 = "$lit$", o$2 = `lit$${Math.random().toFixed(9).slice(2)}$`, n$2 = "?" + o$2, r$3 = `<${n$2}>`, l$1 = document, c$2 = () => l$1.createComment(""), a = (t) => null === t || "object" != typeof t && "function" != typeof t, u$2 = Array.isArray, d$1 = (t) => u$2(t) || "function" == typeof t?.[Symbol.iterator], f$1 = "[ \n\f\r]", v$1 = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g, _ = /-->/g, m$1 = />/g, p$1 = RegExp(`>|${f$1}(?:([^\\s"'>=/]+)(${f$1}*=${f$1}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`, "g"), g = /'/g, $ = /"/g, y = /^(?:script|style|textarea|title)$/i, x = (t) => (i, ...s) => ({ + _$litType$: t, + strings: i, + values: s + }), b = x(1); + x(2); + x(3); + var E = Symbol.for("lit-noChange"), A = Symbol.for("lit-nothing"), C = /* @__PURE__ */ new WeakMap(), P = l$1.createTreeWalker(l$1, 129); + function V(t, i) { + if (!u$2(t) || !t.hasOwnProperty("raw")) throw Error("invalid template strings array"); + return void 0 !== e$3 ? e$3.createHTML(i) : i; + } + var N = (t, i) => { + const s = t.length - 1, e = []; + let n, l = 2 === i ? "" : 3 === i ? "" : "", c = v$1; + for (let i = 0; i < s; i++) { + const s = t[i]; + let a, u, d = -1, f = 0; + for (; f < s.length && (c.lastIndex = f, u = c.exec(s), null !== u);) f = c.lastIndex, c === v$1 ? "!--" === u[1] ? c = _ : void 0 !== u[1] ? c = m$1 : void 0 !== u[2] ? (y.test(u[2]) && (n = RegExp("" === u[0] ? (c = n ?? v$1, d = -1) : void 0 === u[1] ? d = -2 : (d = c.lastIndex - u[2].length, a = u[1], c = void 0 === u[3] ? p$1 : "\"" === u[3] ? $ : g) : c === $ || c === g ? c = p$1 : c === _ || c === m$1 ? c = v$1 : (c = p$1, n = void 0); + const x = c === p$1 && t[i + 1].startsWith("/>") ? " " : ""; + l += c === v$1 ? s + r$3 : d >= 0 ? (e.push(a), s.slice(0, d) + h$1 + s.slice(d) + o$2 + x) : s + o$2 + (-2 === d ? i : x); + } + return [V(t, l + (t[s] || "") + (2 === i ? "" : 3 === i ? "" : "")), e]; + }; + var S = class S { + constructor({ strings: t, _$litType$: i }, e) { + let r; + this.parts = []; + let l = 0, a = 0; + const u = t.length - 1, d = this.parts, [f, v] = N(t, i); + if (this.el = S.createElement(f, e), P.currentNode = this.el.content, 2 === i || 3 === i) { + const t = this.el.content.firstChild; + t.replaceWith(...t.childNodes); + } + for (; null !== (r = P.nextNode()) && d.length < u;) { + if (1 === r.nodeType) { + if (r.hasAttributes()) for (const t of r.getAttributeNames()) if (t.endsWith(h$1)) { + const i = v[a++], s = r.getAttribute(t).split(o$2), e = /([.?@])?(.*)/.exec(i); + d.push({ + type: 1, + index: l, + name: e[2], + strings: s, + ctor: "." === e[1] ? I : "?" === e[1] ? L : "@" === e[1] ? z : H + }), r.removeAttribute(t); + } else t.startsWith(o$2) && (d.push({ + type: 6, + index: l + }), r.removeAttribute(t)); + if (y.test(r.tagName)) { + const t = r.textContent.split(o$2), i = t.length - 1; + if (i > 0) { + r.textContent = s$2 ? s$2.emptyScript : ""; + for (let s = 0; s < i; s++) r.append(t[s], c$2()), P.nextNode(), d.push({ + type: 2, + index: ++l + }); + r.append(t[i], c$2()); + } + } + } else if (8 === r.nodeType) if (r.data === n$2) d.push({ + type: 2, + index: l + }); + else { + let t = -1; + for (; -1 !== (t = r.data.indexOf(o$2, t + 1));) d.push({ + type: 7, + index: l + }), t += o$2.length - 1; + } + l++; + } + } + static createElement(t, i) { + const s = l$1.createElement("template"); + return s.innerHTML = t, s; + } + }; + function M$1(t, i, s = t, e) { + if (i === E) return i; + let h = void 0 !== e ? s._$Co?.[e] : s._$Cl; + const o = a(i) ? void 0 : i._$litDirective$; + return h?.constructor !== o && (h?._$AO?.(!1), void 0 === o ? h = void 0 : (h = new o(t), h._$AT(t, s, e)), void 0 !== e ? (s._$Co ??= [])[e] = h : s._$Cl = h), void 0 !== h && (i = M$1(t, h._$AS(t, i.values), h, e)), i; + } + var R = class { + constructor(t, i) { + this._$AV = [], this._$AN = void 0, this._$AD = t, this._$AM = i; + } + get parentNode() { + return this._$AM.parentNode; + } + get _$AU() { + return this._$AM._$AU; + } + u(t) { + const { el: { content: i }, parts: s } = this._$AD, e = (t?.creationScope ?? l$1).importNode(i, !0); + P.currentNode = e; + let h = P.nextNode(), o = 0, n = 0, r = s[0]; + for (; void 0 !== r;) { + if (o === r.index) { + let i; + 2 === r.type ? i = new k(h, h.nextSibling, this, t) : 1 === r.type ? i = new r.ctor(h, r.name, r.strings, this, t) : 6 === r.type && (i = new Z(h, this, t)), this._$AV.push(i), r = s[++n]; + } + o !== r?.index && (h = P.nextNode(), o++); + } + return P.currentNode = l$1, e; + } + p(t) { + let i = 0; + for (const s of this._$AV) void 0 !== s && (void 0 !== s.strings ? (s._$AI(t, s, i), i += s.strings.length - 2) : s._$AI(t[i])), i++; + } + }; + var k = class k { + get _$AU() { + return this._$AM?._$AU ?? this._$Cv; + } + constructor(t, i, s, e) { + this.type = 2, this._$AH = A, this._$AN = void 0, this._$AA = t, this._$AB = i, this._$AM = s, this.options = e, this._$Cv = e?.isConnected ?? !0; + } + get parentNode() { + let t = this._$AA.parentNode; + const i = this._$AM; + return void 0 !== i && 11 === t?.nodeType && (t = i.parentNode), t; + } + get startNode() { + return this._$AA; + } + get endNode() { + return this._$AB; + } + _$AI(t, i = this) { + t = M$1(this, t, i), a(t) ? t === A || null == t || "" === t ? (this._$AH !== A && this._$AR(), this._$AH = A) : t !== this._$AH && t !== E && this._(t) : void 0 !== t._$litType$ ? this.$(t) : void 0 !== t.nodeType ? this.T(t) : d$1(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 !== A && a(this._$AH) ? this._$AA.nextSibling.data = t : this.T(l$1.createTextNode(t)), this._$AH = t; + } + $(t) { + const { values: i, _$litType$: s } = t, e = "number" == typeof s ? this._$AC(t) : (void 0 === s.el && (s.el = S.createElement(V(s.h, s.h[0]), this.options)), s); + if (this._$AH?._$AD === e) this._$AH.p(i); + else { + const t = new R(e, this), s = t.u(this.options); + t.p(i), this.T(s), this._$AH = t; + } + } + _$AC(t) { + let i = C.get(t.strings); + return void 0 === i && C.set(t.strings, i = new S(t)), i; + } + k(t) { + u$2(this._$AH) || (this._$AH = [], this._$AR()); + const i = this._$AH; + let s, e = 0; + for (const h of t) e === i.length ? i.push(s = new k(this.O(c$2()), this.O(c$2()), this, this.options)) : s = i[e], s._$AI(h), e++; + e < i.length && (this._$AR(s && s._$AB.nextSibling, e), i.length = e); + } + _$AR(t = this._$AA.nextSibling, s) { + for (this._$AP?.(!1, !0, s); t !== this._$AB;) { + const s = i$3(t).nextSibling; + i$3(t).remove(), t = s; + } + } + setConnected(t) { + void 0 === this._$AM && (this._$Cv = t, this._$AP?.(t)); + } + }; + var H = class { + get tagName() { + return this.element.tagName; + } + get _$AU() { + return this._$AM._$AU; + } + constructor(t, i, s, e, h) { + this.type = 1, this._$AH = A, this._$AN = void 0, this.element = t, this.name = i, this._$AM = e, this.options = h, s.length > 2 || "" !== s[0] || "" !== s[1] ? (this._$AH = Array(s.length - 1).fill(/* @__PURE__ */ new String()), this.strings = s) : this._$AH = A; + } + _$AI(t, i = this, s, e) { + const h = this.strings; + let o = !1; + if (void 0 === h) t = M$1(this, t, i, 0), o = !a(t) || t !== this._$AH && t !== E, o && (this._$AH = t); + else { + const e = t; + let n, r; + for (t = h[0], n = 0; n < h.length - 1; n++) r = M$1(this, e[s + n], i, n), r === E && (r = this._$AH[n]), o ||= !a(r) || r !== this._$AH[n], r === A ? t = A : t !== A && (t += (r ?? "") + h[n + 1]), this._$AH[n] = r; + } + o && !e && this.j(t); + } + j(t) { + t === A ? this.element.removeAttribute(this.name) : this.element.setAttribute(this.name, t ?? ""); + } + }; + var I = class extends H { + constructor() { + super(...arguments), this.type = 3; + } + j(t) { + this.element[this.name] = t === A ? void 0 : t; + } + }; + var L = class extends H { + constructor() { + super(...arguments), this.type = 4; + } + j(t) { + this.element.toggleAttribute(this.name, !!t && t !== A); + } + }; + var z = class extends H { + constructor(t, i, s, e, h) { + super(t, i, s, e, h), this.type = 5; + } + _$AI(t, i = this) { + if ((t = M$1(this, t, i, 0) ?? A) === E) return; + const s = this._$AH, e = t === A && s !== A || t.capture !== s.capture || t.once !== s.once || t.passive !== s.passive, h = t !== A && (s === A || e); + e && this.element.removeEventListener(this.name, this, s), h && this.element.addEventListener(this.name, this, t), this._$AH = t; + } + handleEvent(t) { + "function" == typeof this._$AH ? this._$AH.call(this.options?.host ?? this.element, t) : this._$AH.handleEvent(t); + } + }; + var Z = class { + constructor(t, i, s) { + this.element = t, this.type = 6, this._$AN = void 0, this._$AM = i, this.options = s; + } + get _$AU() { + return this._$AM._$AU; + } + _$AI(t) { + M$1(this, t); + } + }; + var j$1 = { + M: h$1, + P: o$2, + A: n$2, + C: 1, + L: N, + R, + D: d$1, + V: M$1, + I: k, + H, + N: L, + U: z, + B: I, + F: Z + }, B = t$3.litHtmlPolyfillSupport; + B?.(S, k), (t$3.litHtmlVersions ??= []).push("3.3.2"); + var D = (t, i, s) => { + const e = s?.renderBefore ?? i; + let h = e._$litPart$; + if (void 0 === h) { + const t = s?.renderBefore ?? null; + e._$litPart$ = h = new k(i.insertBefore(c$2(), t), t, void 0, s ?? {}); + } + return h._$AI(t), h; + }; + //#endregion + //#region node_modules/.pnpm/lit-element@4.2.2/node_modules/lit-element/lit-element.js + /** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ var s$1 = globalThis; + var i$2 = class extends y$1 { + 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 r = this.render(); + this.hasUpdated || (this.renderOptions.isConnected = this.isConnected), super.update(t), this._$Do = D(r, this.renderRoot, this.renderOptions); + } + connectedCallback() { + super.connectedCallback(), this._$Do?.setConnected(!0); + } + disconnectedCallback() { + super.disconnectedCallback(), this._$Do?.setConnected(!1); + } + render() { + return E; + } + }; + i$2._$litElement$ = !0, i$2["finalized"] = !0, s$1.litElementHydrateSupport?.({ LitElement: i$2 }); + var o$1 = s$1.litElementPolyfillSupport; + o$1?.({ LitElement: i$2 }); + (s$1.litElementVersions ??= []).push("4.2.2"); + //#endregion + //#region custom_components/hass_datapoints/src/cards/action/action.styles.ts + var styles$70 = i$5` :host { display: block; } @@ -592,48 +681,79 @@ font-size: 0.875rem; } `; - const DOMAIN = "hass_datapoints"; - const PANEL_URL_PATH = "hass-datapoints-history"; - const COLORS = [ - "#3b82f6", - "#ef4444", - "#10b981", - "#f59e0b", - "#8b5cf6", - "#ec4899" - ]; - const AMBER = "#ff9800"; - const o = { attribute: true, type: String, converter: u$3, reflect: false, hasChanged: f$1 }, r$1 = (t2 = o, e2, r2) => { - const { kind: n2, metadata: i2 } = r2; - let s2 = globalThis.litPropertyMetadata.get(i2); - if (void 0 === s2 && globalThis.litPropertyMetadata.set(i2, s2 = /* @__PURE__ */ new Map()), "setter" === n2 && ((t2 = Object.create(t2)).wrapped = true), s2.set(r2.name, t2), "accessor" === n2) { - const { name: o2 } = r2; - return { set(r3) { - const n3 = e2.get.call(this); - e2.set.call(this, r3), this.requestUpdate(o2, n3, t2, true, r3); - }, init(e3) { - return void 0 !== e3 && this.C(o2, void 0, t2, e3), e3; - } }; - } - if ("setter" === n2) { - const { name: o2 } = r2; - return function(r3) { - const n3 = this[o2]; - e2.call(this, r3), this.requestUpdate(o2, n3, t2, true, r3); - }; - } - throw Error("Unsupported decorator location: " + n2); - }; - function n(t2) { - return (e2, o2) => "object" == typeof o2 ? r$1(t2, e2, o2) : ((t3, e3, o3) => { - const r2 = e3.hasOwnProperty(o3); - return e3.constructor.createProperty(o3, t3), r2 ? Object.getOwnPropertyDescriptor(e3, o3) : void 0; - })(t2, e2, o2); - } - function r(r2) { - return n({ ...r2, state: true, attribute: false }); - } - const styles$15 = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/constants.ts + /** + * Shared constants used by all Hass Records cards. + */ + var DOMAIN = "hass_datapoints"; + var PANEL_URL_PATH = "hass-datapoints-history"; + var COLORS = [ + "#3b82f6", + "#ef4444", + "#10b981", + "#f59e0b", + "#8b5cf6", + "#ec4899" + ]; + //#endregion + //#region node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/decorators/property.js + /** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ var o = { + attribute: !0, + type: String, + converter: u$3, + reflect: !1, + hasChanged: f$2 + }, r$2 = (t = o, e, r) => { + const { kind: n, metadata: i } = r; + let s = globalThis.litPropertyMetadata.get(i); + if (void 0 === s && globalThis.litPropertyMetadata.set(i, s = /* @__PURE__ */ new Map()), "setter" === n && ((t = Object.create(t)).wrapped = !0), s.set(r.name, t), "accessor" === n) { + const { name: o } = r; + return { + set(r) { + const n = e.get.call(this); + e.set.call(this, r), this.requestUpdate(o, n, t, !0, r); + }, + init(e) { + return void 0 !== e && this.C(o, void 0, t, e), e; + } + }; + } + if ("setter" === n) { + const { name: o } = r; + return function(r) { + const n = this[o]; + e.call(this, r), this.requestUpdate(o, n, t, !0, r); + }; + } + throw Error("Unsupported decorator location: " + n); + }; + function n$1(t) { + return (e, o) => "object" == typeof o ? r$2(t, e, o) : ((t, e, o) => { + const r = e.hasOwnProperty(o); + return e.constructor.createProperty(o, t), r ? Object.getOwnPropertyDescriptor(e, o) : void 0; + })(t, e, o); + } + //#endregion + //#region node_modules/.pnpm/@lit+reactive-element@2.1.2/node_modules/@lit/reactive-element/decorators/state.js + /** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ function r$1(r) { + return n$1({ + ...r, + state: !0, + attribute: !1 + }); + } + //#endregion + //#region custom_components/hass_datapoints/src/atoms/display/color-swatch/color-swatch.styles.ts + var styles$69 = i$5` :host { display: block; } @@ -678,70 +798,110 @@ pointer-events: none; } `; - var __create$V = Object.create; - var __defProp$1b = Object.defineProperty; - var __getOwnPropDesc$V = Object.getOwnPropertyDescriptor; - var __knownSymbol$V = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$V = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$1b = (obj, key, value) => key in obj ? __defProp$1b(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$V = (base) => [, , , __create$V(base?.[__knownSymbol$V("metadata")] ?? null)]; - var __decoratorStrings$V = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$V = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$V("Function expected") : fn; - var __decoratorContext$V = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$V[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$V("Already initialized") : fns.push(__expectFn$V(fn || null)) }); - var __decoratorMetadata$V = (array, target) => __defNormalProp$1b(target, __knownSymbol$V("metadata"), array[3]); - var __runInitializers$V = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$V = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$V[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$V({ get [name]() { - return __privateGet$U(this, extra); - }, set [name](x2) { - return __privateSet$U(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$V(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$V(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$V("Object expected"); - else __expectFn$V(fn = it.get) && (desc.get = fn), __expectFn$V(fn = it.set) && (desc.set = fn), __expectFn$V(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$1b(target, name, desc), target; - }; - var __publicField$1b = (obj, key, value) => __defNormalProp$1b(obj, key + "", value); - var __accessCheck$U = (obj, member, msg2) => member.has(obj) || __typeError$V("Cannot " + msg2); - var __privateGet$U = (obj, member, getter) => (__accessCheck$U(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$U = (obj, member, value) => member.has(obj) ? __typeError$V("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$U = (obj, member, value, setter) => (__accessCheck$U(obj, member, "write to private field"), member.set(obj, value), value); - var _label_dec$e, _color_dec$2, _a$V, _init$V, _color$2, _label$e; - class ColorSwatch extends (_a$V = i$2, _color_dec$2 = [n({ type: String })], _label_dec$e = [n({ type: String })], _a$V) { - constructor() { - super(...arguments); - __privateAdd$U(this, _color$2, __runInitializers$V(_init$V, 8, this, "#ff9800")), __runInitializers$V(_init$V, 11, this); - __privateAdd$U(this, _label$e, __runInitializers$V(_init$V, 12, this, "")), __runInitializers$V(_init$V, 15, this); - } - _onInput(e2) { - const newColor = e2.target.value; - this.dispatchEvent( - new CustomEvent("dp-color-change", { - detail: { color: newColor }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b` + //#endregion + //#region \0@oxc-project+runtime@0.122.0/helpers/decorate.js + function __decorate(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + } + //#endregion + //#region \0@oxc-project+runtime@0.122.0/helpers/checkPrivateRedeclaration.js + function _checkPrivateRedeclaration(e, t) { + if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); + } + //#endregion + //#region \0@oxc-project+runtime@0.122.0/helpers/classPrivateFieldInitSpec.js + function _classPrivateFieldInitSpec(e, t, a) { + _checkPrivateRedeclaration(e, t), t.set(e, a); + } + //#endregion + //#region \0@oxc-project+runtime@0.122.0/helpers/assertClassBrand.js + function _assertClassBrand(e, t, n) { + if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; + throw new TypeError("Private element is not present on this object"); + } + //#endregion + //#region \0@oxc-project+runtime@0.122.0/helpers/classPrivateFieldGet2.js + function _classPrivateFieldGet2(s, a) { + return s.get(_assertClassBrand(s, a)); + } + //#endregion + //#region \0@oxc-project+runtime@0.122.0/helpers/classPrivateFieldSet2.js + function _classPrivateFieldSet2(s, a, r) { + return s.set(_assertClassBrand(s, a), r), r; + } + //#endregion + //#region \0@oxc-project+runtime@0.122.0/helpers/typeof.js + function _typeof(o) { + "@babel/helpers - typeof"; + return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) { + return typeof o; + } : function(o) { + return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; + }, _typeof(o); + } + //#endregion + //#region \0@oxc-project+runtime@0.122.0/helpers/toPrimitive.js + function toPrimitive(t, r) { + if ("object" != _typeof(t) || !t) return t; + var e = t[Symbol.toPrimitive]; + if (void 0 !== e) { + var i = e.call(t, r || "default"); + if ("object" != _typeof(i)) return i; + throw new TypeError("@@toPrimitive must return a primitive value."); + } + return ("string" === r ? String : Number)(t); + } + //#endregion + //#region \0@oxc-project+runtime@0.122.0/helpers/toPropertyKey.js + function toPropertyKey(t) { + var i = toPrimitive(t, "string"); + return "symbol" == _typeof(i) ? i : i + ""; + } + //#endregion + //#region \0@oxc-project+runtime@0.122.0/helpers/defineProperty.js + function _defineProperty(e, r, t) { + return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, { + value: t, + enumerable: !0, + configurable: !0, + writable: !0 + }) : e[r] = t, e; + } + //#endregion + //#region custom_components/hass_datapoints/src/atoms/display/color-swatch/color-swatch.ts + var _color_accessor_storage$2 = /* @__PURE__ */ new WeakMap(); + var _label_accessor_storage$14 = /* @__PURE__ */ new WeakMap(); + var ColorSwatch = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _color_accessor_storage$2, "#ff9800"); + _classPrivateFieldInitSpec(this, _label_accessor_storage$14, ""); + } + get color() { + return _classPrivateFieldGet2(_color_accessor_storage$2, this); + } + set color(value) { + _classPrivateFieldSet2(_color_accessor_storage$2, this, value); + } + get label() { + return _classPrivateFieldGet2(_label_accessor_storage$14, this); + } + set label(value) { + _classPrivateFieldSet2(_label_accessor_storage$14, this, value); + } + _onInput(e) { + const newColor = e.target.value; + this.dispatchEvent(new CustomEvent("dp-color-change", { + detail: { color: newColor }, + bubbles: true, + composed: true + })); + } + render() { + return b`
${this.label ? b`${this.label}` : ""}
`; - } - } - _init$V = __decoratorStart$V(_a$V); - _color$2 = /* @__PURE__ */ new WeakMap(); - _label$e = /* @__PURE__ */ new WeakMap(); - __decorateElement$V(_init$V, 4, "color", _color_dec$2, ColorSwatch, _color$2); - __decorateElement$V(_init$V, 4, "label", _label_dec$e, ColorSwatch, _label$e); - __decoratorMetadata$V(_init$V, ColorSwatch); - __publicField$1b(ColorSwatch, "styles", styles$15); - customElements.define("color-swatch", ColorSwatch); - const styles$14 = i$5` + } + }; + _defineProperty(ColorSwatch, "styles", styles$69); + __decorate([n$1({ type: String })], ColorSwatch.prototype, "color", null); + __decorate([n$1({ type: String })], ColorSwatch.prototype, "label", null); + customElements.define("color-swatch", ColorSwatch); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/display/feedback-banner/feedback-banner.styles.ts + var styles$68 = i$5` :host { display: block; } @@ -790,65 +948,47 @@ color: var(--error-color, #f44336); } `; - var __create$U = Object.create; - var __defProp$1a = Object.defineProperty; - var __getOwnPropDesc$U = Object.getOwnPropertyDescriptor; - var __knownSymbol$U = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$U = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$1a = (obj, key, value) => key in obj ? __defProp$1a(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$U = (base) => [, , , __create$U(base?.[__knownSymbol$U("metadata")] ?? null)]; - var __decoratorStrings$U = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$U = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$U("Function expected") : fn; - var __decoratorContext$U = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$U[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$U("Already initialized") : fns.push(__expectFn$U(fn || null)) }); - var __decoratorMetadata$U = (array, target) => __defNormalProp$1a(target, __knownSymbol$U("metadata"), array[3]); - var __runInitializers$U = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$U = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$U[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$U({ get [name]() { - return __privateGet$T(this, extra); - }, set [name](x2) { - return __privateSet$T(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$U(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$U(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$U("Object expected"); - else __expectFn$U(fn = it.get) && (desc.get = fn), __expectFn$U(fn = it.set) && (desc.set = fn), __expectFn$U(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$1a(target, name, desc), target; - }; - var __publicField$1a = (obj, key, value) => __defNormalProp$1a(obj, key + "", value); - var __accessCheck$T = (obj, member, msg2) => member.has(obj) || __typeError$U("Cannot " + msg2); - var __privateGet$T = (obj, member, getter) => (__accessCheck$T(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$T = (obj, member, value) => member.has(obj) ? __typeError$U("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$T = (obj, member, value, setter) => (__accessCheck$T(obj, member, "write to private field"), member.set(obj, value), value); - var _variant_dec, _visible_dec$1, _text_dec$1, _kind_dec, _a$U, _init$U, _kind, _text$1, _visible$1, _variant; - class FeedbackBanner extends (_a$U = i$2, _kind_dec = [n({ type: String })], _text_dec$1 = [n({ type: String })], _visible_dec$1 = [n({ type: Boolean })], _variant_dec = [n({ type: String })], _a$U) { - constructor() { - super(...arguments); - __privateAdd$T(this, _kind, __runInitializers$U(_init$U, 8, this, "")), __runInitializers$U(_init$U, 11, this); - __privateAdd$T(this, _text$1, __runInitializers$U(_init$U, 12, this, "")), __runInitializers$U(_init$U, 15, this); - __privateAdd$T(this, _visible$1, __runInitializers$U(_init$U, 16, this, false)), __runInitializers$U(_init$U, 19, this); - __privateAdd$T(this, _variant, __runInitializers$U(_init$U, 20, this, "default")), __runInitializers$U(_init$U, 23, this); - } - render() { - if (!this.text) { - return A; - } - return b` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/display/feedback-banner/feedback-banner.ts + var _kind_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _text_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _visible_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _variant_accessor_storage = /* @__PURE__ */ new WeakMap(); + var FeedbackBanner = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _kind_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _text_accessor_storage$1, ""); + _classPrivateFieldInitSpec(this, _visible_accessor_storage$1, false); + _classPrivateFieldInitSpec(this, _variant_accessor_storage, "default"); + } + get kind() { + return _classPrivateFieldGet2(_kind_accessor_storage, this); + } + set kind(value) { + _classPrivateFieldSet2(_kind_accessor_storage, this, value); + } + get text() { + return _classPrivateFieldGet2(_text_accessor_storage$1, this); + } + set text(value) { + _classPrivateFieldSet2(_text_accessor_storage$1, this, value); + } + get visible() { + return _classPrivateFieldGet2(_visible_accessor_storage$1, this); + } + set visible(value) { + _classPrivateFieldSet2(_visible_accessor_storage$1, this, value); + } + get variant() { + return _classPrivateFieldGet2(_variant_accessor_storage, this); + } + set variant(value) { + _classPrivateFieldSet2(_variant_accessor_storage, this, value); + } + render() { + if (!this.text) return A; + return b`
`; - } - } - _init$U = __decoratorStart$U(_a$U); - _kind = /* @__PURE__ */ new WeakMap(); - _text$1 = /* @__PURE__ */ new WeakMap(); - _visible$1 = /* @__PURE__ */ new WeakMap(); - _variant = /* @__PURE__ */ new WeakMap(); - __decorateElement$U(_init$U, 4, "kind", _kind_dec, FeedbackBanner, _kind); - __decorateElement$U(_init$U, 4, "text", _text_dec$1, FeedbackBanner, _text$1); - __decorateElement$U(_init$U, 4, "visible", _visible_dec$1, FeedbackBanner, _visible$1); - __decorateElement$U(_init$U, 4, "variant", _variant_dec, FeedbackBanner, _variant); - __decoratorMetadata$U(_init$U, FeedbackBanner); - __publicField$1a(FeedbackBanner, "styles", styles$14); - customElements.define("feedback-banner", FeedbackBanner); - const styles$13 = i$5` + } + }; + _defineProperty(FeedbackBanner, "styles", styles$68); + __decorate([n$1({ type: String })], FeedbackBanner.prototype, "kind", null); + __decorate([n$1({ type: String })], FeedbackBanner.prototype, "text", null); + __decorate([n$1({ type: Boolean })], FeedbackBanner.prototype, "visible", null); + __decorate([n$1({ type: String })], FeedbackBanner.prototype, "variant", null); + customElements.define("feedback-banner", FeedbackBanner); + //#endregion + //#region custom_components/hass_datapoints/src/cards/action/action-targets/action-targets.styles.ts + var styles$67 = i$5` :host { display: block; } @@ -880,7 +1016,9 @@ width: 100%; } `; - const styles$12 = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/molecules/chip-group/chip-group.styles.ts + var styles$66 = i$5` :host { display: block; } @@ -895,7 +1033,9 @@ margin-bottom: 4px; } `; - const styles$11 = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/form/entity-chip/entity-chip.styles.ts + var styles$65 = i$5` :host { display: inline-flex; } @@ -923,92 +1063,65 @@ color: var(--error-color, #f44336); } `; - var __create$T = Object.create; - var __defProp$19 = Object.defineProperty; - var __getOwnPropDesc$T = Object.getOwnPropertyDescriptor; - var __knownSymbol$T = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$T = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$19 = (obj, key, value) => key in obj ? __defProp$19(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$T = (base) => [, , , __create$T(base?.[__knownSymbol$T("metadata")] ?? null)]; - var __decoratorStrings$T = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$T = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$T("Function expected") : fn; - var __decoratorContext$T = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$T[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$T("Already initialized") : fns.push(__expectFn$T(fn || null)) }); - var __decoratorMetadata$T = (array, target) => __defNormalProp$19(target, __knownSymbol$T("metadata"), array[3]); - var __runInitializers$T = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$T = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$T[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$T({ get [name]() { - return __privateGet$S(this, extra); - }, set [name](x2) { - return __privateSet$S(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$T(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$T(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$T("Object expected"); - else __expectFn$T(fn = it.get) && (desc.get = fn), __expectFn$T(fn = it.set) && (desc.set = fn), __expectFn$T(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$19(target, name, desc), target; - }; - var __publicField$19 = (obj, key, value) => __defNormalProp$19(obj, key + "", value); - var __accessCheck$S = (obj, member, msg2) => member.has(obj) || __typeError$T("Cannot " + msg2); - var __privateGet$S = (obj, member, getter) => (__accessCheck$S(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$S = (obj, member, value) => member.has(obj) ? __typeError$T("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$S = (obj, member, value, setter) => (__accessCheck$S(obj, member, "write to private field"), member.set(obj, value), value); - var _removable_dec$1, _hass_dec$e, _itemId_dec$1, _type_dec$2, _a$T, _init$T, _type$2, _itemId$1, _hass$e, _removable$1; - class EntityChip extends (_a$T = i$2, _type_dec$2 = [n({ type: String })], _itemId_dec$1 = [n({ type: String, attribute: "item-id" })], _hass_dec$e = [n({ type: Object })], _removable_dec$1 = [n({ type: Boolean })], _a$T) { - constructor() { - super(...arguments); - __privateAdd$S(this, _type$2, __runInitializers$T(_init$T, 8, this, "entity")), __runInitializers$T(_init$T, 11, this); - __privateAdd$S(this, _itemId$1, __runInitializers$T(_init$T, 12, this, "")), __runInitializers$T(_init$T, 15, this); - __privateAdd$S(this, _hass$e, __runInitializers$T(_init$T, 16, this, null)), __runInitializers$T(_init$T, 19, this); - __privateAdd$S(this, _removable$1, __runInitializers$T(_init$T, 20, this, false)), __runInitializers$T(_init$T, 23, this); - } - _getName() { - if (!this.hass || !this.itemId) { - return this.itemId || ""; - } - switch (this.type) { - case "entity": { - const state = this.hass.states?.[this.itemId]; - return state?.attributes?.friendly_name ?? this.itemId; - } - case "device": { - const device = this.hass.devices?.[this.itemId]; - return device?.name ?? this.itemId; - } - case "area": { - const area = this.hass.areas?.[this.itemId]; - return area?.name ?? this.itemId; - } - default: - return this.itemId; - } - } - _onRemove() { - this.dispatchEvent( - new CustomEvent("dp-chip-remove", { - detail: { type: this.type, itemId: this.itemId }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/form/entity-chip/entity-chip.ts + var _type_accessor_storage$2 = /* @__PURE__ */ new WeakMap(); + var _itemId_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _hass_accessor_storage$14 = /* @__PURE__ */ new WeakMap(); + var _removable_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var EntityChip = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _type_accessor_storage$2, "entity"); + _classPrivateFieldInitSpec(this, _itemId_accessor_storage$1, ""); + _classPrivateFieldInitSpec(this, _hass_accessor_storage$14, null); + _classPrivateFieldInitSpec(this, _removable_accessor_storage$1, false); + } + get type() { + return _classPrivateFieldGet2(_type_accessor_storage$2, this); + } + set type(value) { + _classPrivateFieldSet2(_type_accessor_storage$2, this, value); + } + get itemId() { + return _classPrivateFieldGet2(_itemId_accessor_storage$1, this); + } + set itemId(value) { + _classPrivateFieldSet2(_itemId_accessor_storage$1, this, value); + } + get hass() { + return _classPrivateFieldGet2(_hass_accessor_storage$14, this); + } + set hass(value) { + _classPrivateFieldSet2(_hass_accessor_storage$14, this, value); + } + get removable() { + return _classPrivateFieldGet2(_removable_accessor_storage$1, this); + } + set removable(value) { + _classPrivateFieldSet2(_removable_accessor_storage$1, this, value); + } + _getName() { + if (!this.hass || !this.itemId) return this.itemId || ""; + switch (this.type) { + case "entity": return (this.hass.states?.[this.itemId])?.attributes?.friendly_name ?? this.itemId; + case "device": return (this.hass.devices?.[this.itemId])?.name ?? this.itemId; + case "area": return (this.hass.areas?.[this.itemId])?.name ?? this.itemId; + default: return this.itemId; + } + } + _onRemove() { + this.dispatchEvent(new CustomEvent("dp-chip-remove", { + detail: { + type: this.type, + itemId: this.itemId + }, + bubbles: true, + composed: true + })); + } + render() { + return b` ${this._getName()} ${this.removable ? b`` : ""} `; - } - } - _init$T = __decoratorStart$T(_a$T); - _type$2 = /* @__PURE__ */ new WeakMap(); - _itemId$1 = /* @__PURE__ */ new WeakMap(); - _hass$e = /* @__PURE__ */ new WeakMap(); - _removable$1 = /* @__PURE__ */ new WeakMap(); - __decorateElement$T(_init$T, 4, "type", _type_dec$2, EntityChip, _type$2); - __decorateElement$T(_init$T, 4, "itemId", _itemId_dec$1, EntityChip, _itemId$1); - __decorateElement$T(_init$T, 4, "hass", _hass_dec$e, EntityChip, _hass$e); - __decorateElement$T(_init$T, 4, "removable", _removable_dec$1, EntityChip, _removable$1); - __decoratorMetadata$T(_init$T, EntityChip); - __publicField$19(EntityChip, "styles", styles$11); - customElements.define("entity-chip", EntityChip); - var __create$S = Object.create; - var __defProp$18 = Object.defineProperty; - var __getOwnPropDesc$S = Object.getOwnPropertyDescriptor; - var __knownSymbol$S = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$S = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$18 = (obj, key, value) => key in obj ? __defProp$18(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$S = (base) => [, , , __create$S(base?.[__knownSymbol$S("metadata")] ?? null)]; - var __decoratorStrings$S = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$S = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$S("Function expected") : fn; - var __decoratorContext$S = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$S[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$S("Already initialized") : fns.push(__expectFn$S(fn || null)) }); - var __decoratorMetadata$S = (array, target) => __defNormalProp$18(target, __knownSymbol$S("metadata"), array[3]); - var __runInitializers$S = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$S = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$S[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$S({ get [name]() { - return __privateGet$R(this, extra); - }, set [name](x2) { - return __privateSet$R(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$S(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$S(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$S("Object expected"); - else __expectFn$S(fn = it.get) && (desc.get = fn), __expectFn$S(fn = it.set) && (desc.set = fn), __expectFn$S(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$18(target, name, desc), target; - }; - var __publicField$18 = (obj, key, value) => __defNormalProp$18(obj, key + "", value); - var __accessCheck$R = (obj, member, msg2) => member.has(obj) || __typeError$S("Cannot " + msg2); - var __privateGet$R = (obj, member, getter) => (__accessCheck$R(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$R = (obj, member, value) => member.has(obj) ? __typeError$S("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$R = (obj, member, value, setter) => (__accessCheck$R(obj, member, "write to private field"), member.set(obj, value), value); - var _label_dec$d, _removable_dec, _hass_dec$d, _items_dec$1, _a$S, _init$S, _items$1, _hass$d, _removable, _label$d; - class ChipGroup extends (_a$S = i$2, _items_dec$1 = [n({ type: Array })], _hass_dec$d = [n({ type: Object })], _removable_dec = [n({ type: Boolean })], _label_dec$d = [n({ type: String })], _a$S) { - constructor() { - super(...arguments); - __privateAdd$R(this, _items$1, __runInitializers$S(_init$S, 8, this, [])), __runInitializers$S(_init$S, 11, this); - __privateAdd$R(this, _hass$d, __runInitializers$S(_init$S, 12, this, null)), __runInitializers$S(_init$S, 15, this); - __privateAdd$R(this, _removable, __runInitializers$S(_init$S, 16, this, false)), __runInitializers$S(_init$S, 19, this); - __privateAdd$R(this, _label$d, __runInitializers$S(_init$S, 20, this, "")), __runInitializers$S(_init$S, 23, this); - } - _onRemove(e2) { - const { type, itemId } = e2.detail; - const next = this.items.filter( - (item) => !(item.type === type && item.id === itemId) - ); - this.dispatchEvent( - new CustomEvent("dp-chips-change", { - detail: { items: next }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b` + } + }; + _defineProperty(EntityChip, "styles", styles$65); + __decorate([n$1({ type: String })], EntityChip.prototype, "type", null); + __decorate([n$1({ + type: String, + attribute: "item-id" + })], EntityChip.prototype, "itemId", null); + __decorate([n$1({ type: Object })], EntityChip.prototype, "hass", null); + __decorate([n$1({ type: Boolean })], EntityChip.prototype, "removable", null); + customElements.define("entity-chip", EntityChip); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/chip-group/chip-group.ts + var _items_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _hass_accessor_storage$13 = /* @__PURE__ */ new WeakMap(); + var _removable_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _label_accessor_storage$13 = /* @__PURE__ */ new WeakMap(); + var ChipGroup = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _items_accessor_storage$1, []); + _classPrivateFieldInitSpec(this, _hass_accessor_storage$13, null); + _classPrivateFieldInitSpec(this, _removable_accessor_storage, false); + _classPrivateFieldInitSpec(this, _label_accessor_storage$13, ""); + } + get items() { + return _classPrivateFieldGet2(_items_accessor_storage$1, this); + } + set items(value) { + _classPrivateFieldSet2(_items_accessor_storage$1, this, value); + } + get hass() { + return _classPrivateFieldGet2(_hass_accessor_storage$13, this); + } + set hass(value) { + _classPrivateFieldSet2(_hass_accessor_storage$13, this, value); + } + get removable() { + return _classPrivateFieldGet2(_removable_accessor_storage, this); + } + set removable(value) { + _classPrivateFieldSet2(_removable_accessor_storage, this, value); + } + get label() { + return _classPrivateFieldGet2(_label_accessor_storage$13, this); + } + set label(value) { + _classPrivateFieldSet2(_label_accessor_storage$13, this, value); + } + _onRemove(e) { + const { type, itemId } = e.detail; + const next = this.items.filter((item) => !(item.type === type && item.id === itemId)); + this.dispatchEvent(new CustomEvent("dp-chips-change", { + detail: { items: next }, + bubbles: true, + composed: true + })); + } + render() { + return b` ${this.label ? b`
${this.label}
` : ""}
- ${this.items.map( - (item) => b` + ${this.items.map((item) => b` - ` - )} + `)}
`; - } - } - _init$S = __decoratorStart$S(_a$S); - _items$1 = /* @__PURE__ */ new WeakMap(); - _hass$d = /* @__PURE__ */ new WeakMap(); - _removable = /* @__PURE__ */ new WeakMap(); - _label$d = /* @__PURE__ */ new WeakMap(); - __decorateElement$S(_init$S, 4, "items", _items_dec$1, ChipGroup, _items$1); - __decorateElement$S(_init$S, 4, "hass", _hass_dec$d, ChipGroup, _hass$d); - __decorateElement$S(_init$S, 4, "removable", _removable_dec, ChipGroup, _removable); - __decorateElement$S(_init$S, 4, "label", _label_dec$d, ChipGroup, _label$d); - __decoratorMetadata$S(_init$S, ChipGroup); - __publicField$18(ChipGroup, "styles", styles$12); - customElements.define("chip-group", ChipGroup); - var __create$R = Object.create; - var __defProp$17 = Object.defineProperty; - var __getOwnPropDesc$R = Object.getOwnPropertyDescriptor; - var __knownSymbol$R = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$R = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$17 = (obj, key, value) => key in obj ? __defProp$17(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$R = (base) => [, , , __create$R(base?.[__knownSymbol$R("metadata")] ?? null)]; - var __decoratorStrings$R = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$R = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$R("Function expected") : fn; - var __decoratorContext$R = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$R[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$R("Already initialized") : fns.push(__expectFn$R(fn || null)) }); - var __decoratorMetadata$R = (array, target) => __defNormalProp$17(target, __knownSymbol$R("metadata"), array[3]); - var __runInitializers$R = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$R = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$R[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$R({ get [name]() { - return __privateGet$Q(this, extra); - }, set [name](x2) { - return __privateSet$Q(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$R(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$R(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$R("Object expected"); - else __expectFn$R(fn = it.get) && (desc.get = fn), __expectFn$R(fn = it.set) && (desc.set = fn), __expectFn$R(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$17(target, name, desc), target; - }; - var __publicField$17 = (obj, key, value) => __defNormalProp$17(obj, key + "", value); - var __accessCheck$Q = (obj, member, msg2) => member.has(obj) || __typeError$R("Cannot " + msg2); - var __privateGet$Q = (obj, member, getter) => (__accessCheck$Q(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$Q = (obj, member, value) => member.has(obj) ? __typeError$R("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$Q = (obj, member, value, setter) => (__accessCheck$Q(obj, member, "write to private field"), member.set(obj, value), value); - var __targetValue_dec, _configChips_dec, _showTargetPicker_dec, _showConfigTargets_dec, _hass_dec$c, _a$R, _init$R, _hass$c, _showConfigTargets, _showTargetPicker, _configChips, __targetValue; - class CardActionTargets extends (_a$R = i$2, _hass_dec$c = [n({ attribute: false })], _showConfigTargets_dec = [n({ type: Boolean, attribute: "show-config-targets" })], _showTargetPicker_dec = [n({ type: Boolean, attribute: "show-target-picker" })], _configChips_dec = [n({ attribute: false })], __targetValue_dec = [r()], _a$R) { - constructor() { - super(...arguments); - __privateAdd$Q(this, _hass$c, __runInitializers$R(_init$R, 8, this, null)), __runInitializers$R(_init$R, 11, this); - __privateAdd$Q(this, _showConfigTargets, __runInitializers$R(_init$R, 12, this, true)), __runInitializers$R(_init$R, 15, this); - __privateAdd$Q(this, _showTargetPicker, __runInitializers$R(_init$R, 16, this, true)), __runInitializers$R(_init$R, 19, this); - __privateAdd$Q(this, _configChips, __runInitializers$R(_init$R, 20, this, [])), __runInitializers$R(_init$R, 23, this); - __privateAdd$Q(this, __targetValue, __runInitializers$R(_init$R, 24, this, {})), __runInitializers$R(_init$R, 27, this); - } - resetSelection() { - this._targetValue = {}; - } - _onTargetChanged(e2) { - this._targetValue = e2.detail.value || {}; - this.dispatchEvent( - new CustomEvent("dp-target-change", { - detail: { value: this._targetValue }, - bubbles: true, - composed: true - }) - ); - } - render() { - const hasChips = this.configChips.length > 0; - return b` + } + }; + _defineProperty(ChipGroup, "styles", styles$66); + __decorate([n$1({ type: Array })], ChipGroup.prototype, "items", null); + __decorate([n$1({ type: Object })], ChipGroup.prototype, "hass", null); + __decorate([n$1({ type: Boolean })], ChipGroup.prototype, "removable", null); + __decorate([n$1({ type: String })], ChipGroup.prototype, "label", null); + customElements.define("chip-group", ChipGroup); + //#endregion + //#region custom_components/hass_datapoints/src/cards/action/action-targets/action-targets.ts + var _hass_accessor_storage$12 = /* @__PURE__ */ new WeakMap(); + var _showConfigTargets_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _showTargetPicker_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _configChips_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _targetValue_accessor_storage = /* @__PURE__ */ new WeakMap(); + var CardActionTargets = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _hass_accessor_storage$12, null); + _classPrivateFieldInitSpec(this, _showConfigTargets_accessor_storage, true); + _classPrivateFieldInitSpec(this, _showTargetPicker_accessor_storage, true); + _classPrivateFieldInitSpec(this, _configChips_accessor_storage, []); + _classPrivateFieldInitSpec(this, _targetValue_accessor_storage, {}); + } + get hass() { + return _classPrivateFieldGet2(_hass_accessor_storage$12, this); + } + set hass(value) { + _classPrivateFieldSet2(_hass_accessor_storage$12, this, value); + } + get showConfigTargets() { + return _classPrivateFieldGet2(_showConfigTargets_accessor_storage, this); + } + set showConfigTargets(value) { + _classPrivateFieldSet2(_showConfigTargets_accessor_storage, this, value); + } + get showTargetPicker() { + return _classPrivateFieldGet2(_showTargetPicker_accessor_storage, this); + } + set showTargetPicker(value) { + _classPrivateFieldSet2(_showTargetPicker_accessor_storage, this, value); + } + get configChips() { + return _classPrivateFieldGet2(_configChips_accessor_storage, this); + } + set configChips(value) { + _classPrivateFieldSet2(_configChips_accessor_storage, this, value); + } + get _targetValue() { + return _classPrivateFieldGet2(_targetValue_accessor_storage, this); + } + set _targetValue(value) { + _classPrivateFieldSet2(_targetValue_accessor_storage, this, value); + } + resetSelection() { + this._targetValue = {}; + } + _onTargetChanged(e) { + this._targetValue = e.detail.value || {}; + this.dispatchEvent(new CustomEvent("dp-target-change", { + detail: { value: this._targetValue }, + bubbles: true, + composed: true + })); + } + render() { + const hasChips = this.configChips.length > 0; + return b` ${this.showConfigTargets && hasChips ? b` ` : A} `; - } - } - _init$R = __decoratorStart$R(_a$R); - _hass$c = /* @__PURE__ */ new WeakMap(); - _showConfigTargets = /* @__PURE__ */ new WeakMap(); - _showTargetPicker = /* @__PURE__ */ new WeakMap(); - _configChips = /* @__PURE__ */ new WeakMap(); - __targetValue = /* @__PURE__ */ new WeakMap(); - __decorateElement$R(_init$R, 4, "hass", _hass_dec$c, CardActionTargets, _hass$c); - __decorateElement$R(_init$R, 4, "showConfigTargets", _showConfigTargets_dec, CardActionTargets, _showConfigTargets); - __decorateElement$R(_init$R, 4, "showTargetPicker", _showTargetPicker_dec, CardActionTargets, _showTargetPicker); - __decorateElement$R(_init$R, 4, "configChips", _configChips_dec, CardActionTargets, _configChips); - __decorateElement$R(_init$R, 4, "_targetValue", __targetValue_dec, CardActionTargets, __targetValue); - __decoratorMetadata$R(_init$R, CardActionTargets); - __publicField$17(CardActionTargets, "styles", styles$13); - customElements.define("action-targets", CardActionTargets); - const isDev = () => typeof window !== "undefined" && !!window.__HASS_DATAPOINTS_DEV__; - const logger$1 = { - log: (...args) => { - if (isDev()) { - console.log(...args); - } - }, - debug: (...args) => { - if (isDev()) { - console.debug(...args); - } - }, - info: (...args) => { - if (isDev()) { - console.info(...args); - } - }, - warn: (...args) => console.warn(...args), - error: (...args) => console.error(...args) - }; - var __defProp$16 = Object.defineProperty; - var __defNormalProp$16 = (obj, key, value) => key in obj ? __defProp$16(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$16 = (obj, key, value) => __defNormalProp$16(obj, typeof key !== "symbol" ? key + "" : key, value); - class HassRecordsActionCard extends i$2 { - constructor() { - super(); - __publicField$16(this, "_userTarget", {}); - this._config = {}; - this._hass = null; - this._color = "#03a9f4"; - this._feedbackClass = ""; - this._feedbackText = ""; - this._feedbackVisible = false; - } - setConfig(config) { - this._config = config || {}; - this._color = config?.default_color || "#03a9f4"; - } - set hass(hass) { - this._hass = hass; - } - get hass() { - return this._hass; - } - _nowStr() { - const d2 = /* @__PURE__ */ new Date(); - const yyyy = d2.getFullYear(); - const mm = String(d2.getMonth() + 1).padStart(2, "0"); - const dd = String(d2.getDate()).padStart(2, "0"); - const hh = String(d2.getHours()).padStart(2, "0"); - const min = String(d2.getMinutes()).padStart(2, "0"); - return `${yyyy}-${mm}-${dd}T${hh}:${min}`; - } - _configTarget() { - const cfg = this._config; - const norm = (v2) => { - if (!v2) { - return []; - } - if (Array.isArray(v2)) { - return v2; - } - return [v2]; - }; - let raw; - if (cfg.target) raw = cfg.target; - else if (cfg.entity) raw = { entity_id: [cfg.entity] }; - else if (cfg.entities?.length) - raw = { entity_id: cfg.entities }; - else return { entity_id: [], device_id: [], area_id: [], label_id: [] }; - return { - entity_id: norm(raw.entity_id), - device_id: norm(raw.device_id), - area_id: norm(raw.area_id), - label_id: norm(raw.label_id) - }; - } - _configChipItems() { - const t2 = this._configTarget(); - const items = []; - t2.entity_id.forEach((id) => items.push({ type: "entity", id })); - t2.device_id.forEach((id) => items.push({ type: "device", id })); - t2.area_id.forEach((id) => items.push({ type: "area", id })); - t2.label_id.forEach((id) => items.push({ type: "label", id })); - return items; - } - _mergeTargets(a2, b2) { - const norm = (v2) => { - if (!v2) { - return []; - } - if (Array.isArray(v2)) { - return v2; - } - return [v2]; - }; - const merge = (x2, y2) => [ - .../* @__PURE__ */ new Set([...norm(x2), ...norm(y2)]) - ]; - return { - entity_id: merge(a2.entity_id, b2.entity_id), - device_id: merge(a2.device_id, b2.device_id), - area_id: merge(a2.area_id, b2.area_id), - label_id: merge(a2.label_id, b2.label_id) - }; - } - async _record() { - const msgEl = this.shadowRoot.querySelector("#msg"); - const message = (msgEl?.value || "").trim(); - if (!message) { - msgEl?.focus(); - return; - } - const btn = this.shadowRoot.querySelector("#btn"); - if (btn) btn.disabled = true; - const data = { message }; - const annEl = this.shadowRoot.querySelector("#ann"); - const ann = (annEl?.value || "").trim(); - if (ann) data.annotation = ann; - const iconPicker = this.shadowRoot.querySelector("#icon-picker"); - const icon = iconPicker?.value; - if (icon) data.icon = icon; - data.color = this._color; - const dateEl = this.shadowRoot.querySelector("#date"); - const dateVal = (dateEl?.value || "").trim(); - if (dateVal) data.date = dateVal; - const merged2 = this._mergeTargets(this._configTarget(), this._userTarget); - if (merged2.entity_id.length) data.entity_ids = merged2.entity_id; - if (merged2.device_id.length) data.device_ids = merged2.device_id; - if (merged2.area_id.length) data.area_ids = merged2.area_id; - if (merged2.label_id.length) data.label_ids = merged2.label_id; - try { - await this._hass.callService(DOMAIN, "record", data); - window.dispatchEvent(new CustomEvent("hass-datapoints-event-recorded")); - this.dispatchEvent( - new CustomEvent("hass-datapoints-action-recorded", { - bubbles: true, - composed: true, - detail: { ...data } - }) - ); - if (msgEl) msgEl.value = ""; - if (annEl) annEl.value = ""; - if (dateEl) - dateEl.value = this._config.default_date || this._nowStr(); - this._userTarget = {}; - const targets = this.shadowRoot.querySelector("action-targets"); - if (targets?.resetSelection) { - targets.resetSelection(); - } - this._feedbackClass = "ok"; - this._feedbackText = "Event recorded!"; - this._feedbackVisible = true; - setTimeout(() => { - this._feedbackVisible = false; - }, 3e3); - } catch (e2) { - const err = e2; - this._feedbackClass = "err"; - this._feedbackText = `Error: ${err.message || "unknown error"}`; - this._feedbackVisible = true; - logger$1.error("[hass-datapoints action-card]", e2); - } - if (btn) btn.disabled = false; - } - _onColorChange(e2) { - this._color = e2.detail.color; - } - _onTargetChanged(e2) { - this._userTarget = e2.detail.value || {}; - } - _onAnnKeydown(e2) { - if (e2.key === "Enter" && (e2.ctrlKey || e2.metaKey)) { - e2.preventDefault(); - this._record(); - } - } - get _isAdmin() { - return this._hass?.user?.is_admin === true; - } - render() { - const cfg = this._config; - const hasTitle = !!cfg.title; - if (!this._isAdmin) { - return b` + } + }; + _defineProperty(CardActionTargets, "styles", styles$67); + __decorate([n$1({ attribute: false })], CardActionTargets.prototype, "hass", null); + __decorate([n$1({ + type: Boolean, + attribute: "show-config-targets" + })], CardActionTargets.prototype, "showConfigTargets", null); + __decorate([n$1({ + type: Boolean, + attribute: "show-target-picker" + })], CardActionTargets.prototype, "showTargetPicker", null); + __decorate([n$1({ attribute: false })], CardActionTargets.prototype, "configChips", null); + __decorate([r$1()], CardActionTargets.prototype, "_targetValue", null); + customElements.define("action-targets", CardActionTargets); + //#endregion + //#region custom_components/hass_datapoints/src/lib/logger.ts + /** + * Centralised logger for hass-datapoints. + * + * - `logger.warn(...)` and `logger.error(...)` always emit to the console. + * - `logger.log(...)`, `logger.debug(...)`, and `logger.info(...)` are silenced + * unless the global dev flag `window.__HASS_DATAPOINTS_DEV__` is truthy. + * + * To enable verbose output open the browser console and run: + * window.__HASS_DATAPOINTS_DEV__ = true + */ + var isDev = () => typeof window !== "undefined" && !!window.__HASS_DATAPOINTS_DEV__; + var logger$1 = { + log: (...args) => { + if (isDev()) console.log(...args); + }, + debug: (...args) => { + if (isDev()) console.debug(...args); + }, + info: (...args) => { + if (isDev()) console.info(...args); + }, + warn: (...args) => console.warn(...args), + error: (...args) => console.error(...args) + }; + //#endregion + //#region custom_components/hass_datapoints/src/cards/action/action.ts + /** + * hass-datapoints-action-card – Full form to record a custom event. + * + * Related items UI: + * - Config-set targets rendered as non-removable chip-group chips. + * - A ha-selector (target schema) lets the user add extra items per recording. + * - On submit both are merged; the selector resets to empty afterwards. + */ + var HassRecordsActionCard = class extends i$2 { + constructor() { + super(); + _defineProperty(this, "_userTarget", {}); + this._config = {}; + this._hass = null; + this._color = "#03a9f4"; + this._feedbackClass = ""; + this._feedbackText = ""; + this._feedbackVisible = false; + } + setConfig(config) { + this._config = config || {}; + this._color = config?.default_color || "#03a9f4"; + } + set hass(hass) { + this._hass = hass; + } + get hass() { + return this._hass; + } + _nowStr() { + const d = /* @__PURE__ */ new Date(); + return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}T${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`; + } + _configTarget() { + const cfg = this._config; + const norm = (v) => { + if (!v) return []; + if (Array.isArray(v)) return v; + return [v]; + }; + let raw; + if (cfg.target) raw = cfg.target; + else if (cfg.entity) raw = { entity_id: [cfg.entity] }; + else if (cfg.entities?.length) raw = { entity_id: cfg.entities }; + else return { + entity_id: [], + device_id: [], + area_id: [], + label_id: [] + }; + return { + entity_id: norm(raw.entity_id), + device_id: norm(raw.device_id), + area_id: norm(raw.area_id), + label_id: norm(raw.label_id) + }; + } + _configChipItems() { + const t = this._configTarget(); + const items = []; + t.entity_id.forEach((id) => items.push({ + type: "entity", + id + })); + t.device_id.forEach((id) => items.push({ + type: "device", + id + })); + t.area_id.forEach((id) => items.push({ + type: "area", + id + })); + t.label_id.forEach((id) => items.push({ + type: "label", + id + })); + return items; + } + _mergeTargets(a, b) { + const norm = (v) => { + if (!v) return []; + if (Array.isArray(v)) return v; + return [v]; + }; + const merge = (x, y) => [...new Set([...norm(x), ...norm(y)])]; + return { + entity_id: merge(a.entity_id, b.entity_id), + device_id: merge(a.device_id, b.device_id), + area_id: merge(a.area_id, b.area_id), + label_id: merge(a.label_id, b.label_id) + }; + } + async _record() { + const msgEl = this.shadowRoot.querySelector("#msg"); + const message = (msgEl?.value || "").trim(); + if (!message) { + msgEl?.focus(); + return; + } + const btn = this.shadowRoot.querySelector("#btn"); + if (btn) btn.disabled = true; + const data = { message }; + const annEl = this.shadowRoot.querySelector("#ann"); + const ann = (annEl?.value || "").trim(); + if (ann) data.annotation = ann; + const icon = this.shadowRoot.querySelector("#icon-picker")?.value; + if (icon) data.icon = icon; + data.color = this._color; + const dateEl = this.shadowRoot.querySelector("#date"); + const dateVal = (dateEl?.value || "").trim(); + if (dateVal) data.date = dateVal; + const merged = this._mergeTargets(this._configTarget(), this._userTarget); + if (merged.entity_id.length) data.entity_ids = merged.entity_id; + if (merged.device_id.length) data.device_ids = merged.device_id; + if (merged.area_id.length) data.area_ids = merged.area_id; + if (merged.label_id.length) data.label_ids = merged.label_id; + try { + await this._hass.callService(DOMAIN, "record", data); + window.dispatchEvent(new CustomEvent("hass-datapoints-event-recorded")); + this.dispatchEvent(new CustomEvent("hass-datapoints-action-recorded", { + bubbles: true, + composed: true, + detail: { ...data } + })); + if (msgEl) msgEl.value = ""; + if (annEl) annEl.value = ""; + if (dateEl) dateEl.value = this._config.default_date || this._nowStr(); + this._userTarget = {}; + const targets = this.shadowRoot.querySelector("action-targets"); + if (targets?.resetSelection) targets.resetSelection(); + this._feedbackClass = "ok"; + this._feedbackText = "Event recorded!"; + this._feedbackVisible = true; + setTimeout(() => { + this._feedbackVisible = false; + }, 3e3); + } catch (e) { + const err = e; + this._feedbackClass = "err"; + this._feedbackText = `Error: ${err.message || "unknown error"}`; + this._feedbackVisible = true; + logger$1.error("[hass-datapoints action-card]", e); + } + if (btn) btn.disabled = false; + } + _onColorChange(e) { + this._color = e.detail.color; + } + _onTargetChanged(e) { + this._userTarget = e.detail.value || {}; + } + _onAnnKeydown(e) { + if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { + e.preventDefault(); + this._record(); + } + } + get _isAdmin() { + return this._hass?.user?.is_admin === true; + } + render() { + const cfg = this._config; + const hasTitle = !!cfg.title; + if (!this._isAdmin) return b` ${hasTitle ? b`
${cfg.title}
` : ""}
@@ -1429,13 +1506,12 @@
`; - } - const showDate = cfg.show_date !== false; - const showAnnotation = cfg.show_annotation !== false; - const showConfigTargets = cfg.show_config_targets !== false; - const showTargetPicker = cfg.show_target_picker !== false; - const configChips = this._configChipItems(); - return b` + const showDate = cfg.show_date !== false; + const showAnnotation = cfg.show_annotation !== false; + const showConfigTargets = cfg.show_config_targets !== false; + const showTargetPicker = cfg.show_target_picker !== false; + const configChips = this._configChipItems(); + return b` ${hasTitle ? b`
${cfg.title}
` : ""} @@ -1510,309 +1586,3597 @@ >
`; - } - static getConfigElement() { - return document.createElement("hass-datapoints-action-card-editor"); - } - static getStubConfig() { - return { title: "Record Event" }; - } - getGridOptions() { - const hasAnnotation = this._config?.show_annotation !== false; - return { - rows: hasAnnotation ? 10 : 7, - min_rows: hasAnnotation ? 10 : 7, - max_rows: hasAnnotation ? 10 : 7 - }; - } - getCardSize() { - return this._config?.show_annotation !== false ? 10 : 7; - } - } - __publicField$16(HassRecordsActionCard, "properties", { - _config: { state: true }, - _hass: { state: true }, - _color: { state: true }, - _feedbackClass: { state: true }, - _feedbackText: { state: true }, - _feedbackVisible: { state: true } - }); - __publicField$16(HassRecordsActionCard, "styles", styles$16); - const LOCALE_STATUS_EVENT = "lit-localize-status"; - const isStrTagged = (val) => typeof val !== "string" && "strTag" in val; - const joinStringsAndValues = (strings, values, valueOrder) => { - let concat = strings[0]; - for (let i2 = 1; i2 < strings.length; i2++) { - concat += values[valueOrder ? valueOrder[i2 - 1] : i2 - 1]; - concat += strings[i2]; - } - return concat; - }; - const defaultMsg = ((template) => isStrTagged(template) ? joinStringsAndValues(template.strings, template.values) : template); - let msg$1 = defaultMsg; - let installed = false; - function _installMsgImplementation(impl) { - if (installed) { - throw new Error("lit-localize can only be configured once"); - } - msg$1 = impl; - installed = true; - } - class LocalizeController { - constructor(host) { - this.__litLocalizeEventHandler = (event) => { - if (event.detail.status === "ready") { - this.host.requestUpdate(); - } - }; - this.host = host; - } - hostConnected() { - window.addEventListener(LOCALE_STATUS_EVENT, this.__litLocalizeEventHandler); - } - hostDisconnected() { - window.removeEventListener(LOCALE_STATUS_EVENT, this.__litLocalizeEventHandler); - } - } - const _updateWhenLocaleChanges = (host) => host.addController(new LocalizeController(host)); - const updateWhenLocaleChanges = _updateWhenLocaleChanges; - const localized = () => (clazz, _context2) => { - clazz.addInitializer(updateWhenLocaleChanges); - return clazz; - }; - class Deferred { - constructor() { - this.settled = false; - this.promise = new Promise((resolve, reject) => { - this._resolve = resolve; - this._reject = reject; - }); - } - resolve(value) { - this.settled = true; - this._resolve(value); - } - reject(error) { - this.settled = true; - this._reject(error); - } - } - const hl = []; - for (let i2 = 0; i2 < 256; i2++) { - hl[i2] = (i2 >> 4 & 15).toString(16) + (i2 & 15).toString(16); - } - function fnv1a64(str) { - let t0 = 0, v0 = 8997, t1 = 0, v1 = 33826, t2 = 0, v2 = 40164, t3 = 0, v3 = 52210; - for (let i2 = 0; i2 < str.length; i2++) { - v0 ^= str.charCodeAt(i2); - t0 = v0 * 435; - t1 = v1 * 435; - t2 = v2 * 435; - t3 = v3 * 435; - t2 += v0 << 8; - t3 += v1 << 8; - t1 += t0 >>> 16; - v0 = t0 & 65535; - t2 += t1 >>> 16; - v1 = t1 & 65535; - v3 = t3 + (t2 >>> 16) & 65535; - v2 = t2 & 65535; - } - return hl[v3 >> 8] + hl[v3 & 255] + hl[v2 >> 8] + hl[v2 & 255] + hl[v1 >> 8] + hl[v1 & 255] + hl[v0 >> 8] + hl[v0 & 255]; - } - const HASH_DELIMITER = ""; - const HTML_PREFIX = "h"; - const STRING_PREFIX = "s"; - function generateMsgId(strings, isHtmlTagged) { - return (isHtmlTagged ? HTML_PREFIX : STRING_PREFIX) + fnv1a64(typeof strings === "string" ? strings : strings.join(HASH_DELIMITER)); - } - const expressionOrders = /* @__PURE__ */ new WeakMap(); - const hashCache = /* @__PURE__ */ new Map(); - function runtimeMsg(templates2, template, options) { - if (templates2) { - const id = options?.id ?? generateId(template); - const localized2 = templates2[id]; - if (localized2) { - if (typeof localized2 === "string") { - return localized2; - } else if ("strTag" in localized2) { - return joinStringsAndValues( - localized2.strings, - // Cast `template` because its type wasn't automatically narrowed (but - // we know it must be the same type as `localized`). - template.values, - localized2.values - ); - } else { - let order = expressionOrders.get(localized2); - if (order === void 0) { - order = localized2.values; - expressionOrders.set(localized2, order); - } - return { - ...localized2, - values: order.map((i2) => template.values[i2]) - }; - } - } - } - return defaultMsg(template); - } - function generateId(template) { - const strings = typeof template === "string" ? template : template.strings; - let id = hashCache.get(strings); - if (id === void 0) { - id = generateMsgId(strings, typeof template !== "string" && !("strTag" in template)); - hashCache.set(strings, id); - } - return id; - } - function dispatchStatusEvent(detail) { - window.dispatchEvent(new CustomEvent(LOCALE_STATUS_EVENT, { detail })); - } - let activeLocale = ""; - let loadingLocale; - let sourceLocale; - let validLocales; - let loadLocale; - let templates$6; - let loading = new Deferred(); - loading.resolve(); - let requestId$2 = 0; - const configureLocalization = (config) => { - _installMsgImplementation(((template, options) => runtimeMsg(templates$6, template, options))); - activeLocale = sourceLocale = config.sourceLocale; - validLocales = new Set(config.targetLocales); - validLocales.add(config.sourceLocale); - loadLocale = config.loadLocale; - return { getLocale: getLocale$1, setLocale: setLocale$1 }; - }; - const getLocale$1 = () => { - return activeLocale; - }; - const setLocale$1 = (newLocale) => { - if (newLocale === (loadingLocale ?? activeLocale)) { - return loading.promise; - } - if (!validLocales || !loadLocale) { - throw new Error("Internal error"); - } - if (!validLocales.has(newLocale)) { - throw new Error("Invalid locale code"); - } - requestId$2++; - const thisRequestId = requestId$2; - loadingLocale = newLocale; - if (loading.settled) { - loading = new Deferred(); - } - dispatchStatusEvent({ status: "loading", loadingLocale: newLocale }); - const localePromise = newLocale === sourceLocale ? ( - // We could switch to the source locale synchronously, but we prefer to - // queue it on a microtask so that switching locales is consistently - // asynchronous. - Promise.resolve({ templates: void 0 }) - ) : loadLocale(newLocale); - localePromise.then((mod) => { - if (requestId$2 === thisRequestId) { - activeLocale = newLocale; - loadingLocale = void 0; - templates$6 = mod.templates; - dispatchStatusEvent({ status: "ready", readyLocale: newLocale }); - loading.resolve(); - } - }, (err) => { - if (requestId$2 === thisRequestId) { - dispatchStatusEvent({ - status: "error", - errorLocale: newLocale, - errorMessage: err.toString() - }); - loading.reject(err); - } - }); - return loading.promise; - }; - const supportedLocalesJson = [ - "de", - "es", - "fi", - "fr", - "pt", - "zh-Hans" - ]; - const TARGET_LOCALES = supportedLocalesJson; - const targetLocales = TARGET_LOCALES; - const { getLocale, setLocale } = configureLocalization({ - sourceLocale: "en", - targetLocales, - loadLocale: (locale) => { - if (locale === "fi") { - return Promise.resolve().then(() => fi); - } - if (locale === "fr") { - return Promise.resolve().then(() => fr); - } - if (locale === "de") { - return Promise.resolve().then(() => de); - } - if (locale === "es") { - return Promise.resolve().then(() => es); - } - if (locale === "pt") { - return Promise.resolve().then(() => pt); - } - if (locale === "zh-Hans") { - return Promise.resolve().then(() => zhHans); - } - throw new Error(`Unsupported locale "${locale}"`); - } - }); - function normalizeLocale(locale) { - const normalizedLocale = (locale ?? "").trim().replaceAll("_", "-").toLowerCase(); - if (!normalizedLocale) { - return "en"; - } - if (normalizedLocale === "fi" || normalizedLocale.startsWith("fi-")) { - return "fi"; - } - if (normalizedLocale === "fr" || normalizedLocale.startsWith("fr-")) { - return "fr"; - } - if (normalizedLocale === "de" || normalizedLocale.startsWith("de-")) { - return "de"; - } - if (normalizedLocale === "es" || normalizedLocale.startsWith("es-")) { - return "es"; - } - if (normalizedLocale === "pt" || normalizedLocale.startsWith("pt-")) { - return "pt"; - } - if (normalizedLocale === "zh" || normalizedLocale.startsWith("zh-") || normalizedLocale.startsWith("cmn-")) { - return "zh-Hans"; - } - return "en"; - } - async function setFrontendLocale(locale) { - const nextLocale = normalizeLocale(locale); - if (getLocale() !== nextLocale) { - await setLocale(nextLocale); - } - return nextLocale; - } - async function syncFrontendLocale(hass) { - return setFrontendLocale(hass?.locale?.language ?? hass?.language); - } - function msg(str, opts) { - return msg$1(str, { ...opts, id: opts?.id ?? str }); - } - const styles$10 = i$5` + } + static getConfigElement() { + return document.createElement("hass-datapoints-action-card-editor"); + } + static getStubConfig() { + return { title: "Record Event" }; + } + getGridOptions() { + const hasAnnotation = this._config?.show_annotation !== false; + return { + rows: hasAnnotation ? 10 : 7, + min_rows: hasAnnotation ? 10 : 7, + max_rows: hasAnnotation ? 10 : 7 + }; + } + getCardSize() { + return this._config?.show_annotation !== false ? 10 : 7; + } + }; + _defineProperty(HassRecordsActionCard, "properties", { + _config: { state: true }, + _hass: { state: true }, + _color: { state: true }, + _feedbackClass: { state: true }, + _feedbackText: { state: true }, + _feedbackVisible: { state: true } + }); + _defineProperty(HassRecordsActionCard, "styles", styles$70); + //#endregion + //#region node_modules/.pnpm/@lit+localize@0.12.2/node_modules/@lit/localize/internal/locale-status-event.js + /** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ + /** + * Name of the event dispatched to `window` whenever a locale change starts, + * finishes successfully, or fails. Only relevant to runtime mode. + * + * The `detail` of this event is an object with a `status` string that can be: + * "loading", "ready", or "error", along with the relevant locale code, and + * error message if applicable. + * + * You can listen for this event to know when your application should be + * re-rendered following a locale change. See also the Localized mixin, which + * automatically re-renders LitElement classes using this event. + */ + var LOCALE_STATUS_EVENT = "lit-localize-status"; + //#endregion + //#region node_modules/.pnpm/@lit+localize@0.12.2/node_modules/@lit/localize/internal/str-tag.js + var isStrTagged = (val) => typeof val !== "string" && "strTag" in val; + /** + * Render the result of a `str` tagged template to a string. Note we don't need + * to do this for Lit templates, since Lit itself handles rendering. + */ + var joinStringsAndValues = (strings, values, valueOrder) => { + let concat = strings[0]; + for (let i = 1; i < strings.length; i++) { + concat += values[valueOrder ? valueOrder[i - 1] : i - 1]; + concat += strings[i]; + } + return concat; + }; + //#endregion + //#region node_modules/.pnpm/@lit+localize@0.12.2/node_modules/@lit/localize/internal/default-msg.js + /** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ + /** + * Default identity msg implementation. Simply returns the input template with + * no awareness of translations. If the template is str-tagged, returns it in + * string form. + */ + var defaultMsg = ((template) => isStrTagged(template) ? joinStringsAndValues(template.strings, template.values) : template); + //#endregion + //#region node_modules/.pnpm/@lit+localize@0.12.2/node_modules/@lit/localize/init/install.js + /** + * Make a string or lit-html template localizable. + * + * @param template A string, a lit-html template, or a function that returns + * either a string or lit-html template. + * @param options Optional configuration object with the following properties: + * - id: Optional project-wide unique identifier for this template. If + * omitted, an id will be automatically generated from the template strings. + * - desc: Optional description + */ + var msg$1 = defaultMsg; + var installed = false; + /** + * Internal only. Do not use this function. + * + * Installs an implementation of the msg function to replace the default + * identity function. Throws if called more than once. + * + * @internal + */ + function _installMsgImplementation(impl) { + if (installed) throw new Error("lit-localize can only be configured once"); + msg$1 = impl; + installed = true; + } + //#endregion + //#region node_modules/.pnpm/@lit+localize@0.12.2/node_modules/@lit/localize/internal/localized-controller.js + /** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ + var LocalizeController = class { + constructor(host) { + this.__litLocalizeEventHandler = (event) => { + if (event.detail.status === "ready") this.host.requestUpdate(); + }; + this.host = host; + } + hostConnected() { + window.addEventListener(LOCALE_STATUS_EVENT, this.__litLocalizeEventHandler); + } + hostDisconnected() { + window.removeEventListener(LOCALE_STATUS_EVENT, this.__litLocalizeEventHandler); + } + }; + /** + * Re-render the given LitElement whenever a new active locale has loaded. + * + * See also {@link localized} for the same functionality as a decorator. + * + * When using lit-localize in transform mode, calls to this function are + * replaced with undefined. + * + * Usage: + * + * import {LitElement, html} from 'lit'; + * import {msg, updateWhenLocaleChanges} from '@lit/localize'; + * + * class MyElement extends LitElement { + * constructor() { + * super(); + * updateWhenLocaleChanges(this); + * } + * + * render() { + * return html`${msg('Hello World')}`; + * } + * } + */ + var _updateWhenLocaleChanges = (host) => host.addController(new LocalizeController(host)); + var updateWhenLocaleChanges = _updateWhenLocaleChanges; + //#endregion + //#region node_modules/.pnpm/@lit+localize@0.12.2/node_modules/@lit/localize/internal/localized-decorator.js + /** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ + /** + * Class decorator to enable re-rendering the given LitElement whenever a new + * active locale has loaded. + * + * See also {@link updateWhenLocaleChanges} for the same functionality without + * the use of decorators. + * + * When using lit-localize in transform mode, applications of this decorator are + * removed. + * + * Usage: + * + * import {LitElement, html} from 'lit'; + * import {customElement} from 'lit/decorators.js'; + * import {msg, localized} from '@lit/localize'; + * + * @localized() + * @customElement('my-element') + * class MyElement extends LitElement { + * render() { + * return html`${msg('Hello World')}`; + * } + * } + */ + var localized = () => (clazz, _context) => { + clazz.addInitializer(updateWhenLocaleChanges); + return clazz; + }; + //#endregion + //#region node_modules/.pnpm/@lit+localize@0.12.2/node_modules/@lit/localize/internal/deferred.js + /** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ + var Deferred = class { + constructor() { + this.settled = false; + this.promise = new Promise((resolve, reject) => { + this._resolve = resolve; + this._reject = reject; + }); + } + resolve(value) { + this.settled = true; + this._resolve(value); + } + reject(error) { + this.settled = true; + this._reject(error); + } + }; + //#endregion + //#region node_modules/.pnpm/@lit+localize@0.12.2/node_modules/@lit/localize/internal/fnv1a64.js + /** + * @license + * Copyright 2014 Travis Webb + * SPDX-License-Identifier: MIT + */ + var hl = []; + for (let i = 0; i < 256; i++) hl[i] = (i >> 4 & 15).toString(16) + (i & 15).toString(16); + /** + * Perform a FNV-1A 64-bit hash of the given string (as UTF-16 code units), and + * return a hexadecimal digest (left zero padded to 16 characters). + * + * @see {@link http://tools.ietf.org/html/draft-eastlake-fnv-06} + */ + function fnv1a64(str) { + let t0 = 0, v0 = 8997, t1 = 0, v1 = 33826, t2 = 0, v2 = 40164, t3 = 0, v3 = 52210; + for (let i = 0; i < str.length; i++) { + v0 ^= str.charCodeAt(i); + t0 = v0 * 435; + t1 = v1 * 435; + t2 = v2 * 435; + t3 = v3 * 435; + t2 += v0 << 8; + t3 += v1 << 8; + t1 += t0 >>> 16; + v0 = t0 & 65535; + t2 += t1 >>> 16; + v1 = t1 & 65535; + v3 = t3 + (t2 >>> 16) & 65535; + v2 = t2 & 65535; + } + return hl[v3 >> 8] + hl[v3 & 255] + hl[v2 >> 8] + hl[v2 & 255] + hl[v1 >> 8] + hl[v1 & 255] + hl[v0 >> 8] + hl[v0 & 255]; + } + /** + * Id prefix on html-tagged templates to distinguish e.g. `x` from + * html`x`. + */ + var HTML_PREFIX = "h"; + /** + * Id prefix on plain string templates to distinguish e.g. `x` from + * html`x`. + */ + var STRING_PREFIX = "s"; + /** + * Generate a unique ID for a lit-localize message. + * + * Example: + * Template: html`Hello ${who}!` + * Params: ["Hello ", "!"], true + * Output: h82ccc38d4d46eaa9 + * + * The ID is constructed as: + * + * [0] Kind of template: [h]tml or [s]tring. + * [1,16] 64-bit FNV-1a hash hex digest of the template strings, as UTF-16 + * code points, delineated by an ASCII "record separator" character. + * + * We choose FNV-1a because: + * + * 1. It's pretty fast (e.g. much faster than SHA-1). + * 2. It's pretty small (0.25 KiB minified + brotli). + * 3. We don't require cryptographic security, and 64 bits should give + * sufficient collision resistance for any one application. Worst + * case, we will always detect collisions during analysis. + * 4. We can't use Web Crypto API (e.g. SHA-1), because it's asynchronous. + * 5. It's a well known non-cryptographic hash with implementations in many + * languages. + * 6. There was an existing JavaScript implementation that doesn't require + * BigInt, for IE11 compatibility. + */ + function generateMsgId(strings, isHtmlTagged) { + return (isHtmlTagged ? HTML_PREFIX : STRING_PREFIX) + fnv1a64(typeof strings === "string" ? strings : strings.join("")); + } + //#endregion + //#region node_modules/.pnpm/@lit+localize@0.12.2/node_modules/@lit/localize/internal/runtime-msg.js + /** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ + var expressionOrders = /* @__PURE__ */ new WeakMap(); + var hashCache = /* @__PURE__ */ new Map(); + function runtimeMsg(templates, template, options) { + if (templates) { + const localized = templates[options?.id ?? generateId(template)]; + if (localized) if (typeof localized === "string") return localized; + else if ("strTag" in localized) return joinStringsAndValues(localized.strings, template.values, localized.values); + else { + let order = expressionOrders.get(localized); + if (order === void 0) { + order = localized.values; + expressionOrders.set(localized, order); + } + return { + ...localized, + values: order.map((i) => template.values[i]) + }; + } + } + return defaultMsg(template); + } + function generateId(template) { + const strings = typeof template === "string" ? template : template.strings; + let id = hashCache.get(strings); + if (id === void 0) { + id = generateMsgId(strings, typeof template !== "string" && !("strTag" in template)); + hashCache.set(strings, id); + } + return id; + } + //#endregion + //#region node_modules/.pnpm/@lit+localize@0.12.2/node_modules/@lit/localize/init/runtime.js + /** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ + /** + * Dispatch a "lit-localize-status" event to `window` with the given detail. + */ + function dispatchStatusEvent(detail) { + window.dispatchEvent(new CustomEvent(LOCALE_STATUS_EVENT, { detail })); + } + var activeLocale = ""; + var loadingLocale; + var sourceLocale; + var validLocales; + var loadLocale; + var templates$6; + var loading = new Deferred(); + loading.resolve(); + var requestId$2 = 0; + /** + * Set configuration parameters for lit-localize when in runtime mode. Returns + * an object with functions: + * + * - `getLocale`: Return the active locale code. + * - `setLocale`: Set the active locale code. + * + * Throws if called more than once. + */ + var configureLocalization = (config) => { + _installMsgImplementation(((template, options) => runtimeMsg(templates$6, template, options))); + activeLocale = sourceLocale = config.sourceLocale; + validLocales = new Set(config.targetLocales); + validLocales.add(config.sourceLocale); + loadLocale = config.loadLocale; + return { + getLocale: getLocale$1, + setLocale: setLocale$1 + }; + }; + /** + * Return the active locale code. + */ + var getLocale$1 = () => { + return activeLocale; + }; + /** + * Set the active locale code, and begin loading templates for that locale using + * the `loadLocale` function that was passed to `configureLocalization`. Returns + * a promise that resolves when the next locale is ready to be rendered. + * + * Note that if a second call to `setLocale` is made while the first requested + * locale is still loading, then the second call takes precedence, and the + * promise returned from the first call will resolve when second locale is + * ready. If you need to know whether a particular locale was loaded, check + * `getLocale` after the promise resolves. + * + * Throws if the given locale is not contained by the configured `sourceLocale` + * or `targetLocales`. + */ + var setLocale$1 = (newLocale) => { + if (newLocale === (loadingLocale ?? activeLocale)) return loading.promise; + if (!validLocales || !loadLocale) throw new Error("Internal error"); + if (!validLocales.has(newLocale)) throw new Error("Invalid locale code"); + requestId$2++; + const thisRequestId = requestId$2; + loadingLocale = newLocale; + if (loading.settled) loading = new Deferred(); + dispatchStatusEvent({ + status: "loading", + loadingLocale: newLocale + }); + (newLocale === sourceLocale ? Promise.resolve({ templates: void 0 }) : loadLocale(newLocale)).then((mod) => { + if (requestId$2 === thisRequestId) { + activeLocale = newLocale; + loadingLocale = void 0; + templates$6 = mod.templates; + dispatchStatusEvent({ + status: "ready", + readyLocale: newLocale + }); + loading.resolve(); + } + }, (err) => { + if (requestId$2 === thisRequestId) { + dispatchStatusEvent({ + status: "error", + errorLocale: newLocale, + errorMessage: err.toString() + }); + loading.reject(err); + } + }); + return loading.promise; + }; + //#endregion + //#region custom_components/hass_datapoints/src/lib/i18n/supported-locales.json + var supported_locales_default = [ + "de", + "es", + "fi", + "fr", + "pt", + "zh-Hans" + ]; + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/range-timeline/i18n/fi.ts + var fi_exports$25 = /* @__PURE__ */ __exportAll({ translations: () => translations$149 }); + var translations$149; + var init_fi$25 = __esmMin((() => { + translations$149 = { + "Updates with new data": "Päivittyy uusilla tiedoilla", + "Scroll to selected range": "Siirry valittuun alueeseen", + "Start date and time": "Alkamispäivä ja -aika", + "End date and time": "Päättymispäivä ja -aika", + Select: "Valitse" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/action/i18n/fi.ts + var fi_exports$24 = /* @__PURE__ */ __exportAll({ translations: () => translations$148 }); + var translations$148; + var init_fi$24 = __esmMin((() => { + translations$148 = { + General: "Yleiset", + "Related items": "Liittyvät kohteet", + "Datapoint Appearance": "Datapisteen ulkoasu", + "Form fields": "Lomakekentät", + "Card title (optional)": "Kortin otsikko (valinnainen)", + "Pre-fill entities, devices, areas or labels that are always linked to recordings from this card.": "Esitäytä entiteetit, laitteet, alueet tai merkinnät, jotka yhdistetään aina tämän kortin tallenteisiin.", + "Show always included targets on card": "Näytä aina mukana olevat kohteet kortilla", + "Allow user to add more related items": "Salli käyttäjän lisätä lisää liittyviä kohteita", + "Default icon": "Oletuskuvake", + "Default colour": "Oletusväri", + "Show date & time field": "Näytä päivämäärä- ja aikakenttä", + "Show annotation field": "Näytä huomautuskenttä" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/history-chart/i18n/fi.ts + var fi_exports$23 = /* @__PURE__ */ __exportAll({ translations: () => translations$147 }); + var translations$147; + var init_fi$23 = __esmMin((() => { + translations$147 = { + "Date window:": "Aikaikkuna:", + "Actual:": "Todellinen:" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/i18n/fi.ts + var fi_exports$22 = /* @__PURE__ */ __exportAll({ translations: () => translations$146 }); + var translations$146; + var init_fi$22 = __esmMin((() => { + translations$146 = { + General: "Yleiset", + Entity: "Entiteetti", + "Multiple entities": "Useita entiteettejä", + Display: "Näyttö", + "Card title (optional)": "Kortin otsikko (valinnainen)", + "Hours to show": "Näytettävät tunnit", + "Single entity": "Yksittäinen entiteetti", + "Show data gaps": "Näytä tietoaukot", + "Highlight missing data ranges with dashed lines and boundary markers": "Korosta puuttuvat tietoalueet katkoviivoilla ja rajamerkeillä" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/list/i18n/fi.ts + var fi_exports$21 = /* @__PURE__ */ __exportAll({ translations: () => translations$145 }); + var translations$145; + var init_fi$21 = __esmMin((() => { + translations$145 = { + "Search datapoints…": "Hae datapisteitä…", + "Delete record": "Poista tietue", + Delete: "Poista", + "Show annotation": "Näytä huomautus", + "Open related data point history": "Avaa liittyvän datapisteen historia", + "Edit record": "Muokkaa tietuetta", + "Show chart marker": "Näytä kaavion merkki", + "Hide chart marker": "Piilota kaavion merkki", + "Choose colour": "Valitse väri", + Save: "Tallenna", + Cancel: "Peruuta", + Message: "Viesti", + "Annotation / full message": "Huomautus / koko viesti" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/quick/i18n/fi.ts + var fi_exports$20 = /* @__PURE__ */ __exportAll({ translations: () => translations$144 }); + var translations$144; + var init_fi$20 = __esmMin((() => { + translations$144 = { + General: "Yleiset", + "Icon & colour": "Kuvake ja väri", + "Related items": "Liittyvät kohteet", + "Multiple entities": "Useita entiteettejä", + "Form fields": "Lomakekentät", + "Card title (optional)": "Kortin otsikko (valinnainen)", + "Input placeholder text": "Syötteen ohjeteksti", + Icon: "Kuvake", + Colour: "Väri", + "These items will be linked to every record made with this card.": "Nämä kohteet yhdistetään kaikkiin tällä kortilla tehtyihin tallenteisiin.", + "Single entity (optional)": "Yksittäinen entiteetti (valinnainen)", + "Add related items": "Lisää liittyvät kohteet", + "Show annotation field": "Näytä huomautuskenttä" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/i18n/fi.ts + var fi_exports$19 = /* @__PURE__ */ __exportAll({ translations: () => translations$143 }); + var translations$143; + var init_fi$19 = __esmMin((() => { + translations$143 = { + Entity: "Entiteetti", + Display: "Näyttö", + "Records list": "Tietueiden luettelo", + "Sensor entity *": "Anturientiteetti *", + "Override display name (optional)": "Korvaa näyttönimi (valinnainen)", + "Hours to show": "Näytettävät tunnit", + "Graph colour": "Kaavion väri", + "Annotation style": "Huomautustyyli", + "Circle on line": "Ympyrä viivalla", + "Dotted vertical line": "Pisteytetty pystylinja", + "Show records list below graph": "Näytä tietueiden luettelo kaavion alla", + "Records per page (blank = show all)": "Tietueita per sivu (tyhjä = kaikki)", + "Max records to show (blank = all)": "Enimmäismäärä näytettäviä tietueita (tyhjä = kaikki)", + "Show full message": "Näytä koko viesti", + "User will be able to expand the row if hidden": "Käyttäjä voi laajentaa rivin, jos se on piilotettu" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/chart/i18n/fi.ts + var fi_exports$18 = /* @__PURE__ */ __exportAll({ translations: () => translations$142 }); + var translations$142; + var init_fi$18 = __esmMin((() => { + translations$142 = { + "⚠️ Anomaly Insight": "⚠️ Poikkeavuushavainto", + "⚠️ Multi-method Anomaly": "⚠️ Monimenetelmäinen poikkeavuus", + "Click the highlighted circle to add an annotation.": "Klikkaa korostettua ympyrää lisätäksesi huomautuksen.", + "Alert:": "Hälytys:", + "Confirmed by": "Vahvistettu", + "methods:": "menetelmällä:", + "Trend deviation": "Trendipoikkeama", + "Sudden change": "Äkillinen muutos", + "Statistical outlier (IQR)": "Tilastollinen poikkeama (IQR)", + "Rolling Z-score": "Liukuva Z-arvo", + "Flat-line / stuck": "Tasainen / jumittunut", + "Comparison window": "Vertailuikkuna", + "{0} deviates from its expected trend between {1} and {2}.": "{0} poikkeaa odotetusta trendistä välillä {1} – {2}.", + "{0} shows an unusual rate of change between {1} and {2}.": "{0} näyttää epätavallista muutosnopeutta välillä {1} – {2}.", + "{0} contains statistical outliers between {1} and {2}.": "{0} sisältää tilastollisia poikkeamia välillä {1} – {2}.", + "{0} shows statistically unusual values between {1} and {2}.": "{0} näyttää tilastollisesti epätavallisia arvoja välillä {1} – {2}.", + "{0} appears stuck or flat between {1} and {2}{3}.": "{0} vaikuttaa jumittuneelta tai tasaiselta välillä {1} – {2}{3}.", + "{0} deviates significantly from the comparison window between {1} and {2}.": "{0} poikkeaa merkittävästi vertailuikkunasta välillä {1} – {2}.", + "Peak deviation: {0} from a baseline of {1} at {2}.": "Huippupoikkeama: {0} perusarvosta {1} ajankohtana {2}.", + "Peak rate deviation: {0} from a typical rate of {1} at {2}.": "Huippumuutosnopeus: {0} tyypillisestä nopeudesta {1} ajankohtana {2}.", + "Peak value: {0}, deviating {1} from the median at {2}.": "Huippuarvo: {0}, poikkeaa {1} mediaanista ajankohtana {2}.", + "Peak deviation: {0} from a rolling mean of {1} at {2}.": "Huippupoikkeama: {0} liukuvasta keskiarvosta {1} ajankohtana {2}.", + "Value remained near {0} for an unusually long period.": "Arvo pysyi lähellä arvoa {0} epätavallisen pitkään.", + "Peak deviation from comparison: {0} at {1}.": "Huippupoikkeama vertailusta: {0} ajankohtana {1}.", + " (range: {0})": " (vaihteluväli: {0})", + "Date window": "Aikaikkuna", + Trend: "Trendi", + Rate: "Muutosnopeus", + Delta: "Delta", + Threshold: "Kynnysarvo" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/ha/i18n/fi.ts + var fi_exports$17 = /* @__PURE__ */ __exportAll({ translations: () => translations$141 }); + var translations$141; + var init_fi$17 = __esmMin((() => { + translations$141 = { + "Confirm delete": "Vahvista poisto", + "Are you sure you want to delete this item?": "Oletko varma, että haluat poistaa tämän kohteen?", + Cancel: "Peruuta", + Delete: "Poista", + "Delete date window": "Poista aikaikkuna", + "this date window": "tämä aikaikkuna", + "Edit date window": "Muokkaa aikaikkunaa", + "Add date window": "Lisää aikaikkuna", + "Save date window": "Tallenna aikaikkuna", + "Create date window": "Luo aikaikkuna" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/timeline/i18n/fi.ts + var fi_exports$16 = /* @__PURE__ */ __exportAll({ translations: () => translations$140 }); + var translations$140; + var init_fi$16 = __esmMin((() => { + translations$140 = { + Wk: "Vk", + "Week of": "Viikko" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-anomaly-group/i18n/fi.ts + var fi_exports$15 = /* @__PURE__ */ __exportAll({ translations: () => translations$139 }); + var translations$139; + var init_fi$15 = __esmMin((() => { + translations$139 = { + "Show anomalies": "Näytä poikkeamat", + Sensitivity: "Herkkyys", + "Use downsampled data for detection": "Käytä alasnäytteistettyä dataa havaitsemiseen", + "Rate window": "Muutosikkuna", + "Rolling window": "Liukuva ikkuna", + "Min flat duration": "Pienin tasainen kesto", + "Compare to window": "Vertaa ikkunaan", + "— select window —": "— valitse ikkuna —", + "When methods overlap": "Kun menetelmät menevät päällekkäin", + Low: "Matala", + Medium: "Keskitaso", + High: "Korkea", + "Trend deviation": "Trendipoikkeama", + "Sudden change": "Äkillinen muutos", + "Statistical outlier (IQR)": "Tilastollinen poikkeama (IQR)", + "Rolling Z-score": "Liukuva Z-arvo", + "Flat-line / stuck value": "Tasainen / jumittunut arvo", + "Comparison window deviation": "Vertailuikkunan poikkeama", + "Flags points that deviate significantly from a fitted trend line. Good for catching gradual drift or sudden jumps away from a steady baseline.": "Merkitsee pisteet, jotka poikkeavat selvästi sovitetusta trendiviivasta. Sopii asteittaisen ajautumisen tai äkillisten hyppyjen löytämiseen.", + "Flags unusually fast rises or drops compared to the typical rate of change. Best for detecting spikes, crashes, or rapid transitions.": "Merkitsee poikkeuksellisen nopeat nousut ja laskut verrattuna tyypilliseen muutosnopeuteen. Paras piikkien ja romahdusten havaitsemiseen.", + "Uses the interquartile range to flag values far outside the normal spread of data. Robust against outliers that skew averages.": "Käyttää interkvartiiliväliä arvojen merkitsemiseen, kun ne ovat kaukana normaalista vaihtelusta. Kestävä poikkeaville arvoille, jotka vääristävät keskiarvoja.", + "Compares each value to a rolling mean and standard deviation. Catches unusual readings relative to recent context rather than the whole series.": "Vertaa jokaista arvoa liukuvaan keskiarvoon ja keskihajontaan. Löytää epätavalliset lukemat suhteessa tuoreeseen kontekstiin.", + "Flags when a sensor reports nearly the same value for an unusually long time. Useful for detecting stuck sensors or frozen readings.": "Merkitsee tilanteet, joissa sensori raportoi lähes samaa arvoa poikkeuksellisen pitkään. Hyödyllinen jumittuneiden antureiden löytämiseen.", + "Compares the current period to a reference date window. Highlights differences from an expected historical pattern, such as last week or the same day last year.": "Vertaa nykyistä jaksoa viiteajanjaksoon. Korostaa erot odotetusta historiallisesta mallista, kuten viime viikkoon tai samaan päivään viime vuonna.", + "Show all anomalies": "Näytä kaikki poikkeamat", + "Overlaps only": "Vain päällekkäisyydet", + "Computing…": "Lasketaan…", + "1 hour": "1 tunti", + "3 hours": "3 tuntia", + "6 hours": "6 tuntia", + "12 hours": "12 tuntia", + "24 hours": "24 tuntia", + "7 days": "7 päivää", + "30 minutes": "30 minuuttia" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-delta-group/i18n/fi.ts + var fi_exports$14 = /* @__PURE__ */ __exportAll({ translations: () => translations$138 }); + var translations$138; + var init_fi$14 = __esmMin((() => { + translations$138 = { + "Show delta vs selected date window": "Näytä delta vs. valittu aikaikkuna", + "Select a date window tab to enable delta analysis.": "Valitse aikaikkuna-välilehti ottaaksesi delta-analyysin käyttöön.", + "Show delta in tooltip": "Näytä delta työkaluvihjeessä", + "Show delta lines": "Näytä deltaviivat" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-rate-group/i18n/fi.ts + var fi_exports$13 = /* @__PURE__ */ __exportAll({ translations: () => translations$137 }); + var translations$137; + var init_fi$13 = __esmMin((() => { + translations$137 = { + "Show rate of change": "Näytä muutosnopeus", + "Show rate of change crosshairs": "Näytä muutosnopeuden tähtäin", + "Rate window": "Muutosikkuna", + "Point to point": "Piste pisteeseen", + "1 hour": "1 tunti", + "6 hours": "6 tuntia", + "24 hours": "24 tuntia" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-sample-group/i18n/fi.ts + var fi_exports$12 = /* @__PURE__ */ __exportAll({ translations: () => translations$136 }); + var translations$136; + var init_fi$12 = __esmMin((() => { + translations$136 = { + Downsampling: "Alasnäytteistys", + Interval: "Väli", + Aggregate: "Kooste", + "Raw (no sampling)": "Raaka (ei näytteistystä)", + "5 seconds": "5 sekuntia", + "10 seconds": "10 sekuntia", + "15 seconds": "15 sekuntia", + "30 seconds": "30 sekuntia", + "1 minute": "1 minuutti", + "2 minutes": "2 minuuttia", + "5 minutes": "5 minuuttia", + "10 minutes": "10 minuuttia", + "15 minutes": "15 minuuttia", + "30 minutes": "30 minuuttia", + "1 hour": "1 tunti", + "2 hours": "2 tuntia", + "3 hours": "3 tuntia", + "4 hours": "4 tuntia", + "6 hours": "6 tuntia", + "12 hours": "12 tuntia", + "24 hours": "24 tuntia", + "Mean (average)": "Keskiarvo", + Min: "Min", + Max: "Max", + Median: "Mediaani", + First: "Ensimmäinen", + Last: "Viimeinen" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-summary-group/i18n/fi.ts + var fi_exports$11 = /* @__PURE__ */ __exportAll({ translations: () => translations$135 }); + var translations$135; + var init_fi$11 = __esmMin((() => { + translations$135 = { + "Show min / max / mean": "Näytä min / max / keskiarvo", + "Show range shading": "Näytä aluevarjostus" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-threshold-group/i18n/fi.ts + var fi_exports$10 = /* @__PURE__ */ __exportAll({ translations: () => translations$134 }); + var translations$134; + var init_fi$10 = __esmMin((() => { + translations$134 = { + "Show threshold analysis": "Näytä kynnysanalyysi", + "Shade threshold area": "Varjosta kynnysalue", + Threshold: "Kynnys", + "Shade area": "Varjostusalue", + "Shade above": "Varjosta yläpuolelle", + "Shade below": "Varjosta alapuolelle" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-trend-group/i18n/fi.ts + var fi_exports$9 = /* @__PURE__ */ __exportAll({ translations: () => translations$133 }); + var translations$133; + var init_fi$9 = __esmMin((() => { + translations$133 = { + "Show trend lines": "Näytä trendiviivat", + "Show trend crosshairs": "Näytä trenditähtäin", + "Trend method": "Trendimenetelmä", + "Trend window": "Trendiikkuna", + "Rolling average": "Liukuva keskiarvo", + "Linear trend": "Lineaarinen trendi", + "1 hour": "1 tunti", + "6 hours": "6 tuntia", + "24 hours": "24 tuntia", + "7 days": "7 päivää", + "14 days": "14 päivää", + "21 days": "21 päivää", + "28 days": "28 päivää" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/comparison-tab-rail/i18n/fi.ts + var fi_exports$8 = /* @__PURE__ */ __exportAll({ translations: () => translations$132 }); + var translations$132; + var init_fi$8 = __esmMin((() => { + translations$132 = { "Add date window": "Lisää aikaikkuna" }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/date-window-dialog/i18n/fi.ts + var fi_exports$7 = /* @__PURE__ */ __exportAll({ translations: () => translations$131 }); + var translations$131; + var init_fi$7 = __esmMin((() => { + translations$131 = { + "A date window saves a named date range as a tab, so you can quickly preview it against the selected range or jump the chart back to it later.": "Aikaikkuna tallentaa nimetyn päivävälin välilehteksi, jotta voit nopeasti esikatsella sitä suhteessa valittuun alueeseen tai palata kaavion kyseiseen ajanjaksoon.", + Name: "Nimi", + "e.g. Heating season start": "esim. Lämmityskausi alkaa", + "Date range": "Päiväväli", + Start: "Alku", + End: "Loppu", + "Use previous range": "Käytä edellistä aluetta", + "Use next range": "Käytä seuraavaa aluetta", + "Delete date window": "Poista aikaikkuna", + Cancel: "Peruuta" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/sidebar-options/sections/i18n/fi.ts + var fi_exports$6 = /* @__PURE__ */ __exportAll({ translations: () => translations$130 }); + var translations$130; + var init_fi$6 = __esmMin((() => { + translations$130 = { + Datapoints: "Datapisteet", + "Choose which annotation datapoints appear on the chart.": "Valitse, mitkä huomautusten datapisteet näkyvät kaaviossa.", + "Linked to selected targets": "Linkitetty valittuihin kohteisiin", + "All datapoints": "Kaikki datapisteet", + "Hide datapoints": "Piilota datapisteet" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/target-row/i18n/fi.ts + var fi_exports$5 = /* @__PURE__ */ __exportAll({ translations: () => translations$129 }); + var translations$129; + var init_fi$5 = __esmMin((() => { + translations$129 = { + "Analysis configured": "Analyysi määritetty", + "Configure analysis": "Määritä analyysi", + "Stepped series": "Porrastettu sarja", + "Hide source series": "Piilota lähdesarja", + "All targets already have the same settings": "Kaikilla kohteilla on jo samat asetukset", + "Copy these analysis settings to all targets": "Kopioi nämä analyysiasetus kaikille kohteille", + "Copy to all targets": "Kopioi kaikille kohteille" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/target-row-list/i18n/fi.ts + var fi_exports$4 = /* @__PURE__ */ __exportAll({ translations: () => translations$128 }); + var translations$128; + var init_fi$4 = __esmMin((() => { + translations$128 = {}; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/history-targets/i18n/fi.ts + var fi_exports$3 = /* @__PURE__ */ __exportAll({ translations: () => translations$127 }); + var translations$127; + var init_fi$3 = __esmMin((() => { + translations$127 = { + Targets: "Kohteet", + "Each row controls one chart series.": "Jokainen rivi ohjaa yhtä kaaviosarjaa.", + "Add target": "Lisää kohde", + "Chart preferences": "Kaavioasetukset" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/panel-shell/i18n/fi.ts + var fi_exports$2 = /* @__PURE__ */ __exportAll({ translations: () => translations$126 }); + var translations$126; + var init_fi$2 = __esmMin((() => { + translations$126 = { + "Loading Datapoints…": "Ladataan Datapoints…", + Datapoints: "Datapoints", + "Page options": "Sivun asetukset", + "Download spreadsheet": "Lataa taulukko", + "Save page state": "Tallenna sivun tila", + "Restore saved page": "Palauta tallennettu sivu", + "Clear saved page": "Tyhjennä tallennettu sivu", + "Expand targets sidebar": "Laajenna kohteiden sivupalkki", + "Collapse targets sidebar": "Kutista kohteiden sivupalkki" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/range-toolbar/i18n/fi.ts + var fi_exports$1 = /* @__PURE__ */ __exportAll({ translations: () => translations$125 }); + var translations$125; + var init_fi$1 = __esmMin((() => { + translations$125 = { + "Toggle sidebar": "Vaihda sivupalkki", + Start: "Alku", + End: "Loppu", + "Select date range": "Valitse aikaväli", + "Timeline options": "Aikajanan asetukset", + "Zoom level": "Zoomaustaso", + "Date snapping": "Päivän kohdistus", + Auto: "Automaattinen", + Hour: "Tunti", + Day: "Päivä", + Week: "Viikko", + Month: "Kuukausi", + Minute: "Minuutti", + Second: "Sekunti", + Quarterly: "Neljännesvuosi", + "Month Compressed": "Kuukausi (tiivis)", + "Month Short": "Kuukausi (lyhyt)", + "Month Expanded": "Kuukausi (laaja)", + "Week Compressed": "Viikko (tiivis)", + "Week Expanded": "Viikko (laaja)" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/i18n/locales/fi.ts + var fi_exports = /* @__PURE__ */ __exportAll({ templates: () => templates$5 }); + var modules$5, merged$5, templates$5; + var init_fi = __esmMin((() => { + init_fi$25(); + init_fi$24(); + init_fi$23(); + init_fi$22(); + init_fi$21(); + init_fi$20(); + init_fi$19(); + init_fi$18(); + init_fi$17(); + init_fi$16(); + init_fi$15(); + init_fi$14(); + init_fi$13(); + init_fi$12(); + init_fi$11(); + init_fi$10(); + init_fi$9(); + init_fi$8(); + init_fi$7(); + init_fi$6(); + init_fi$5(); + init_fi$4(); + init_fi$3(); + init_fi$2(); + init_fi$1(); + modules$5 = /* @__PURE__ */ Object.assign({ + "../../../atoms/interactive/range-timeline/i18n/fi.ts": fi_exports$25, + "../../../cards/action/i18n/fi.ts": fi_exports$24, + "../../../cards/history/history-chart/i18n/fi.ts": fi_exports$23, + "../../../cards/history/i18n/fi.ts": fi_exports$22, + "../../../cards/list/i18n/fi.ts": fi_exports$21, + "../../../cards/quick/i18n/fi.ts": fi_exports$20, + "../../../cards/sensor/i18n/fi.ts": fi_exports$19, + "../../chart/i18n/fi.ts": fi_exports$18, + "../../ha/i18n/fi.ts": fi_exports$17, + "../../timeline/i18n/fi.ts": fi_exports$16, + "../../../molecules/analysis-anomaly-group/i18n/fi.ts": fi_exports$15, + "../../../molecules/analysis-delta-group/i18n/fi.ts": fi_exports$14, + "../../../molecules/analysis-rate-group/i18n/fi.ts": fi_exports$13, + "../../../molecules/analysis-sample-group/i18n/fi.ts": fi_exports$12, + "../../../molecules/analysis-summary-group/i18n/fi.ts": fi_exports$11, + "../../../molecules/analysis-threshold-group/i18n/fi.ts": fi_exports$10, + "../../../molecules/analysis-trend-group/i18n/fi.ts": fi_exports$9, + "../../../molecules/comparison-tab-rail/i18n/fi.ts": fi_exports$8, + "../../../molecules/date-window-dialog/i18n/fi.ts": fi_exports$7, + "../../../molecules/sidebar-options/sections/i18n/fi.ts": fi_exports$6, + "../../../molecules/target-row/i18n/fi.ts": fi_exports$5, + "../../../molecules/target-row-list/i18n/fi.ts": fi_exports$4, + "../../../panels/datapoints/components/history-targets/i18n/fi.ts": fi_exports$3, + "../../../panels/datapoints/components/panel-shell/i18n/fi.ts": fi_exports$2, + "../../../panels/datapoints/components/range-toolbar/i18n/fi.ts": fi_exports$1 + }); + merged$5 = {}; + for (const mod of Object.values(modules$5)) Object.assign(merged$5, mod.translations); + templates$5 = merged$5; + })); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/range-timeline/i18n/fr.ts + var fr_exports$25 = /* @__PURE__ */ __exportAll({ translations: () => translations$124 }); + var translations$124; + var init_fr$25 = __esmMin((() => { + translations$124 = { + "Updates with new data": "Se met à jour avec de nouvelles données", + "Scroll to selected range": "Faire défiler jusqu’à la plage sélectionnée", + "Start date and time": "Date et heure de début", + "End date and time": "Date et heure de fin", + Select: "Sélectionner" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/action/i18n/fr.ts + var fr_exports$24 = /* @__PURE__ */ __exportAll({ translations: () => translations$123 }); + var translations$123; + var init_fr$24 = __esmMin((() => { + translations$123 = { + General: "Général", + "Related items": "Éléments liés", + "Datapoint Appearance": "Apparence du point de données", + "Form fields": "Champs du formulaire", + "Card title (optional)": "Titre de la carte (facultatif)", + "Pre-fill entities, devices, areas or labels that are always linked to recordings from this card.": "Préremplissez les entités, appareils, zones ou étiquettes toujours liés aux enregistrements créés avec cette carte.", + "Show always included targets on card": "Afficher sur la carte les cibles toujours incluses", + "Allow user to add more related items": "Autoriser l’utilisateur à ajouter d’autres éléments liés", + "Default icon": "Icône par défaut", + "Default colour": "Couleur par défaut", + "Show date & time field": "Afficher le champ date et heure", + "Show annotation field": "Afficher le champ d’annotation" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/history-chart/i18n/fr.ts + var fr_exports$23 = /* @__PURE__ */ __exportAll({ translations: () => translations$122 }); + var translations$122; + var init_fr$23 = __esmMin((() => { + translations$122 = { + "Date window:": "Fenêtre de dates :", + "Actual:": "Réel :" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/i18n/fr.ts + var fr_exports$22 = /* @__PURE__ */ __exportAll({ translations: () => translations$121 }); + var translations$121; + var init_fr$22 = __esmMin((() => { + translations$121 = { + General: "Général", + Entity: "Entité", + "Multiple entities": "Plusieurs entités", + Display: "Affichage", + "Card title (optional)": "Titre de la carte (facultatif)", + "Hours to show": "Heures à afficher", + "Single entity": "Entité unique", + "Show data gaps": "Afficher les lacunes de données", + "Highlight missing data ranges with dashed lines and boundary markers": "Mettre en évidence les plages de données manquantes avec des lignes en pointillés et des marqueurs de bord" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/list/i18n/fr.ts + var fr_exports$21 = /* @__PURE__ */ __exportAll({ translations: () => translations$120 }); + var translations$120; + var init_fr$21 = __esmMin((() => { + translations$120 = { + "Search datapoints…": "Rechercher des points de données…", + "Delete record": "Supprimer l’enregistrement", + Delete: "Supprimer", + "Show annotation": "Afficher l’annotation", + "Open related data point history": "Ouvrir l’historique du point de données lié", + "Edit record": "Modifier l’enregistrement", + "Show chart marker": "Afficher le marqueur du graphique", + "Hide chart marker": "Masquer le marqueur du graphique", + "Choose colour": "Choisir une couleur", + Save: "Enregistrer", + Cancel: "Annuler", + Message: "Message", + "Annotation / full message": "Annotation / message complet" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/quick/i18n/fr.ts + var fr_exports$20 = /* @__PURE__ */ __exportAll({ translations: () => translations$119 }); + var translations$119; + var init_fr$20 = __esmMin((() => { + translations$119 = { + General: "Général", + "Icon & colour": "Icône et couleur", + "Related items": "Éléments liés", + "Multiple entities": "Plusieurs entités", + "Form fields": "Champs du formulaire", + "Card title (optional)": "Titre de la carte (facultatif)", + "Input placeholder text": "Texte indicatif du champ", + Icon: "Icône", + Colour: "Couleur", + "These items will be linked to every record made with this card.": "Ces éléments seront liés à chaque enregistrement créé avec cette carte.", + "Single entity (optional)": "Entité unique (facultatif)", + "Add related items": "Ajouter des éléments liés", + "Show annotation field": "Afficher le champ d’annotation" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/i18n/fr.ts + var fr_exports$19 = /* @__PURE__ */ __exportAll({ translations: () => translations$118 }); + var translations$118; + var init_fr$19 = __esmMin((() => { + translations$118 = { + Entity: "Entité", + Display: "Affichage", + "Records list": "Liste des enregistrements", + "Sensor entity *": "Entité capteur *", + "Override display name (optional)": "Remplacer le nom affiché (facultatif)", + "Hours to show": "Heures à afficher", + "Graph colour": "Couleur du graphique", + "Annotation style": "Style d’annotation", + "Circle on line": "Cercle sur la ligne", + "Dotted vertical line": "Ligne verticale pointillée", + "Show records list below graph": "Afficher la liste des enregistrements sous le graphique", + "Records per page (blank = show all)": "Enregistrements par page (vide = tout afficher)", + "Max records to show (blank = all)": "Nombre maximal d’enregistrements à afficher (vide = tous)", + "Show full message": "Afficher le message complet", + "User will be able to expand the row if hidden": "L’utilisateur pourra développer la ligne si elle est masquée" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/chart/i18n/fr.ts + var fr_exports$18 = /* @__PURE__ */ __exportAll({ translations: () => translations$117 }); + var translations$117; + var init_fr$18 = __esmMin((() => { + translations$117 = { + "⚠️ Anomaly Insight": "⚠️ Analyse d’anomalie", + "⚠️ Multi-method Anomaly": "⚠️ Anomalie multi-méthodes", + "Click the highlighted circle to add an annotation.": "Cliquez sur le cercle en surbrillance pour ajouter une annotation.", + "Alert:": "Alerte :", + "Confirmed by": "Confirmé par", + "methods:": "méthodes :", + "Trend deviation": "Écart de tendance", + "Sudden change": "Changement brusque", + "Statistical outlier (IQR)": "Valeur aberrante statistique (IQR)", + "Rolling Z-score": "Z-score glissant", + "Flat-line / stuck": "Stable / bloqué", + "Comparison window": "Fenêtre de comparaison", + "{0} deviates from its expected trend between {1} and {2}.": "{0} s’écarte de sa tendance attendue entre {1} et {2}.", + "{0} shows an unusual rate of change between {1} and {2}.": "{0} présente un taux de variation inhabituel entre {1} et {2}.", + "{0} contains statistical outliers between {1} and {2}.": "{0} contient des valeurs aberrantes statistiques entre {1} et {2}.", + "{0} shows statistically unusual values between {1} and {2}.": "{0} présente des valeurs statistiquement inhabituelles entre {1} et {2}.", + "{0} appears stuck or flat between {1} and {2}{3}.": "{0} semble bloqué ou stable entre {1} et {2}{3}.", + "{0} deviates significantly from the comparison window between {1} and {2}.": "{0} s’écarte significativement de la fenêtre de comparaison entre {1} et {2}.", + "Peak deviation: {0} from a baseline of {1} at {2}.": "Écart maximal : {0} par rapport à une base de {1} à {2}.", + "Peak rate deviation: {0} from a typical rate of {1} at {2}.": "Écart maximal de vitesse : {0} par rapport à une vitesse typique de {1} à {2}.", + "Peak value: {0}, deviating {1} from the median at {2}.": "Valeur maximale : {0}, s’écarte de {1} par rapport à la médiane à {2}.", + "Peak deviation: {0} from a rolling mean of {1} at {2}.": "Écart maximal : {0} par rapport à une moyenne mobile de {1} à {2}.", + "Value remained near {0} for an unusually long period.": "La valeur est restée proche de {0} pendant une durée inhabituellement longue.", + "Peak deviation from comparison: {0} at {1}.": "Écart maximal par rapport à la comparaison : {0} à {1}.", + " (range: {0})": " (plage : {0})", + "Date window": "Fenêtre de dates", + Trend: "Tendance", + Rate: "Vitesse", + Delta: "Delta", + Threshold: "Seuil" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/ha/i18n/fr.ts + var fr_exports$17 = /* @__PURE__ */ __exportAll({ translations: () => translations$116 }); + var translations$116; + var init_fr$17 = __esmMin((() => { + translations$116 = { + "Confirm delete": "Confirmer la suppression", + "Are you sure you want to delete this item?": "Voulez-vous vraiment supprimer cet élément ?", + Cancel: "Annuler", + Delete: "Supprimer", + "Delete date window": "Supprimer la fenêtre de dates", + "this date window": "cette fenêtre de dates", + "Edit date window": "Modifier la fenêtre de dates", + "Add date window": "Ajouter une fenêtre de dates", + "Save date window": "Enregistrer la fenêtre de dates", + "Create date window": "Créer une fenêtre de dates" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/timeline/i18n/fr.ts + var fr_exports$16 = /* @__PURE__ */ __exportAll({ translations: () => translations$115 }); + var translations$115; + var init_fr$16 = __esmMin((() => { + translations$115 = { + Wk: "Sem.", + "Week of": "Semaine du" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-anomaly-group/i18n/fr.ts + var fr_exports$15 = /* @__PURE__ */ __exportAll({ translations: () => translations$114 }); + var translations$114; + var init_fr$15 = __esmMin((() => { + translations$114 = { + "Show anomalies": "Afficher les anomalies", + Sensitivity: "Sensibilité", + "Use downsampled data for detection": "Utiliser les données rééchantillonnées pour la détection", + "Rate window": "Fenêtre de variation", + "Rolling window": "Fenêtre glissante", + "Min flat duration": "Durée minimale de stabilité", + "Compare to window": "Comparer à la fenêtre", + "— select window —": "— sélectionner une fenêtre —", + "When methods overlap": "Lorsque les méthodes se chevauchent", + Low: "Faible", + Medium: "Moyenne", + High: "Élevée", + "Trend deviation": "Écart de tendance", + "Sudden change": "Changement brusque", + "Statistical outlier (IQR)": "Valeur aberrante statistique (IQR)", + "Rolling Z-score": "Z-score glissant", + "Flat-line / stuck value": "Valeur stable / bloquée", + "Comparison window deviation": "Écart par rapport à la fenêtre de comparaison", + "Flags points that deviate significantly from a fitted trend line. Good for catching gradual drift or sudden jumps away from a steady baseline.": "Signale les points qui s’écartent fortement d’une ligne de tendance ajustée. Idéal pour repérer une dérive progressive ou des sauts soudains loin d’une base stable.", + "Flags unusually fast rises or drops compared to the typical rate of change. Best for detecting spikes, crashes, or rapid transitions.": "Signale les hausses ou baisses inhabituellement rapides par rapport au taux de variation habituel. Idéal pour détecter des pics, des chutes ou des transitions rapides.", + "Uses the interquartile range to flag values far outside the normal spread of data. Robust against outliers that skew averages.": "Utilise l’intervalle interquartile pour signaler les valeurs très en dehors de la dispersion normale des données. Robuste face aux valeurs extrêmes qui faussent les moyennes.", + "Compares each value to a rolling mean and standard deviation. Catches unusual readings relative to recent context rather than the whole series.": "Compare chaque valeur à une moyenne mobile et à un écart-type. Détecte les lectures inhabituelles par rapport au contexte récent plutôt qu’à l’ensemble de la série.", + "Flags when a sensor reports nearly the same value for an unusually long time. Useful for detecting stuck sensors or frozen readings.": "Signale lorsqu’un capteur rapporte presque la même valeur pendant une durée inhabituellement longue. Utile pour détecter des capteurs bloqués ou des lectures figées.", + "Compares the current period to a reference date window. Highlights differences from an expected historical pattern, such as last week or the same day last year.": "Compare la période actuelle à une fenêtre de dates de référence. Met en évidence les différences par rapport à un modèle historique attendu, comme la semaine dernière ou le même jour l’année précédente.", + "Show all anomalies": "Afficher toutes les anomalies", + "Overlaps only": "Chevauchements uniquement", + "Computing…": "Calcul…", + "1 hour": "1 heure", + "3 hours": "3 heures", + "6 hours": "6 heures", + "12 hours": "12 heures", + "24 hours": "24 heures", + "7 days": "7 jours", + "30 minutes": "30 minutes" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-delta-group/i18n/fr.ts + var fr_exports$14 = /* @__PURE__ */ __exportAll({ translations: () => translations$113 }); + var translations$113; + var init_fr$14 = __esmMin((() => { + translations$113 = { + "Show delta vs selected date window": "Afficher le delta par rapport à la fenêtre de dates sélectionnée", + "Select a date window tab to enable delta analysis.": "Sélectionnez un onglet de fenêtre de dates pour activer l’analyse delta.", + "Show delta in tooltip": "Afficher le delta dans l’infobulle", + "Show delta lines": "Afficher les lignes delta" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-rate-group/i18n/fr.ts + var fr_exports$13 = /* @__PURE__ */ __exportAll({ translations: () => translations$112 }); + var translations$112; + var init_fr$13 = __esmMin((() => { + translations$112 = { + "Show rate of change": "Afficher le taux de variation", + "Show rate of change crosshairs": "Afficher les repères du taux de variation", + "Rate window": "Fenêtre de variation", + "Point to point": "Point à point", + "1 hour": "1 heure", + "6 hours": "6 heures", + "24 hours": "24 heures" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-sample-group/i18n/fr.ts + var fr_exports$12 = /* @__PURE__ */ __exportAll({ translations: () => translations$111 }); + var translations$111; + var init_fr$12 = __esmMin((() => { + translations$111 = { + Downsampling: "Rééchantillonnage", + Interval: "Intervalle", + Aggregate: "Agrégat", + "Raw (no sampling)": "Brut (sans échantillonnage)", + "5 seconds": "5 secondes", + "10 seconds": "10 secondes", + "15 seconds": "15 secondes", + "30 seconds": "30 secondes", + "1 minute": "1 minute", + "2 minutes": "2 minutes", + "5 minutes": "5 minutes", + "10 minutes": "10 minutes", + "15 minutes": "15 minutes", + "30 minutes": "30 minutes", + "1 hour": "1 heure", + "2 hours": "2 heures", + "3 hours": "3 heures", + "4 hours": "4 heures", + "6 hours": "6 heures", + "12 hours": "12 heures", + "24 hours": "24 heures", + "Mean (average)": "Moyenne", + Min: "Min", + Max: "Max", + Median: "Médiane", + First: "Premier", + Last: "Dernier" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-summary-group/i18n/fr.ts + var fr_exports$11 = /* @__PURE__ */ __exportAll({ translations: () => translations$110 }); + var translations$110; + var init_fr$11 = __esmMin((() => { + translations$110 = { + "Show min / max / mean": "Afficher min / max / moyenne", + "Show range shading": "Afficher l’ombrage de la plage" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-threshold-group/i18n/fr.ts + var fr_exports$10 = /* @__PURE__ */ __exportAll({ translations: () => translations$109 }); + var translations$109; + var init_fr$10 = __esmMin((() => { + translations$109 = { + "Show threshold analysis": "Afficher l’analyse de seuil", + "Shade threshold area": "Ombrer la zone du seuil", + Threshold: "Seuil", + "Shade area": "Ombrer la zone", + "Shade above": "Ombrer au-dessus", + "Shade below": "Ombrer en dessous" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-trend-group/i18n/fr.ts + var fr_exports$9 = /* @__PURE__ */ __exportAll({ translations: () => translations$108 }); + var translations$108; + var init_fr$9 = __esmMin((() => { + translations$108 = { + "Show trend lines": "Afficher les lignes de tendance", + "Show trend crosshairs": "Afficher les repères de tendance", + "Trend method": "Méthode de tendance", + "Trend window": "Fenêtre de tendance", + "Rolling average": "Moyenne mobile", + "Linear trend": "Tendance linéaire", + "1 hour": "1 heure", + "6 hours": "6 heures", + "24 hours": "24 heures", + "7 days": "7 jours", + "14 days": "14 jours", + "21 days": "21 jours", + "28 days": "28 jours" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/comparison-tab-rail/i18n/fr.ts + var fr_exports$8 = /* @__PURE__ */ __exportAll({ translations: () => translations$107 }); + var translations$107; + var init_fr$8 = __esmMin((() => { + translations$107 = { "Add date window": "Ajouter une fenêtre de dates" }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/date-window-dialog/i18n/fr.ts + var fr_exports$7 = /* @__PURE__ */ __exportAll({ translations: () => translations$106 }); + var translations$106; + var init_fr$7 = __esmMin((() => { + translations$106 = { + "A date window saves a named date range as a tab, so you can quickly preview it against the selected range or jump the chart back to it later.": "Une fenêtre de dates enregistre une plage de dates nommée comme onglet afin que vous puissiez la prévisualiser rapidement par rapport à la plage sélectionnée ou y revenir plus tard dans le graphique.", + Name: "Nom", + "e.g. Heating season start": "ex. Début de la saison de chauffe", + "Date range": "Plage de dates", + Start: "Début", + End: "Fin", + "Use previous range": "Utiliser la plage précédente", + "Use next range": "Utiliser la plage suivante", + "Delete date window": "Supprimer la fenêtre de dates", + Cancel: "Annuler" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/sidebar-options/sections/i18n/fr.ts + var fr_exports$6 = /* @__PURE__ */ __exportAll({ translations: () => translations$105 }); + var translations$105; + var init_fr$6 = __esmMin((() => { + translations$105 = { + Datapoints: "Points de données", + "Choose which annotation datapoints appear on the chart.": "Choisissez quels points de données d’annotation apparaissent sur le graphique.", + "Linked to selected targets": "Liés aux cibles sélectionnées", + "All datapoints": "Tous les points de données", + "Hide datapoints": "Masquer les points de données" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/target-row/i18n/fr.ts + var fr_exports$5 = /* @__PURE__ */ __exportAll({ translations: () => translations$104 }); + var translations$104; + var init_fr$5 = __esmMin((() => { + translations$104 = { + "Analysis configured": "Analyse configurée", + "Configure analysis": "Configurer l’analyse", + "Stepped series": "Série en escalier", + "Hide source series": "Masquer la série source", + "All targets already have the same settings": "Toutes les cibles ont déjà les mêmes paramètres", + "Copy these analysis settings to all targets": "Copier ces paramètres d’analyse vers toutes les cibles", + "Copy to all targets": "Copier vers toutes les cibles" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/target-row-list/i18n/fr.ts + var fr_exports$4 = /* @__PURE__ */ __exportAll({ translations: () => translations$103 }); + var translations$103; + var init_fr$4 = __esmMin((() => { + translations$103 = {}; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/history-targets/i18n/fr.ts + var fr_exports$3 = /* @__PURE__ */ __exportAll({ translations: () => translations$102 }); + var translations$102; + var init_fr$3 = __esmMin((() => { + translations$102 = { + Targets: "Cibles", + "Each row controls one chart series.": "Chaque ligne contrôle une série du graphique.", + "Add target": "Ajouter une cible", + "Chart preferences": "Préférences du graphique" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/panel-shell/i18n/fr.ts + var fr_exports$2 = /* @__PURE__ */ __exportAll({ translations: () => translations$101 }); + var translations$101; + var init_fr$2 = __esmMin((() => { + translations$101 = { + "Loading Datapoints…": "Chargement des points de données…", + Datapoints: "Points de données", + "Page options": "Options de la page", + "Download spreadsheet": "Télécharger la feuille de calcul", + "Save page state": "Enregistrer l’état de la page", + "Restore saved page": "Restaurer la page enregistrée", + "Clear saved page": "Effacer la page enregistrée", + "Expand targets sidebar": "Développer la barre latérale des cibles", + "Collapse targets sidebar": "Réduire la barre latérale des cibles" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/range-toolbar/i18n/fr.ts + var fr_exports$1 = /* @__PURE__ */ __exportAll({ translations: () => translations$100 }); + var translations$100; + var init_fr$1 = __esmMin((() => { + translations$100 = { + "Toggle sidebar": "Basculer la barre latérale", + Start: "Début", + End: "Fin", + "Select date range": "Sélectionner une plage de dates", + "Timeline options": "Options de la chronologie", + "Zoom level": "Niveau de zoom", + "Date snapping": "Ajustement des dates", + Auto: "Auto", + Hour: "Heure", + Day: "Jour", + Week: "Semaine", + Month: "Mois", + Minute: "Minute", + Second: "Seconde", + Quarterly: "Trimestriel", + "Month Compressed": "Mois compact", + "Month Short": "Mois abrégé", + "Month Expanded": "Mois développé", + "Week Compressed": "Semaine compacte", + "Week Expanded": "Semaine développée" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/i18n/locales/fr.ts + var fr_exports = /* @__PURE__ */ __exportAll({ templates: () => templates$4 }); + var modules$4, merged$4, templates$4; + var init_fr = __esmMin((() => { + init_fr$25(); + init_fr$24(); + init_fr$23(); + init_fr$22(); + init_fr$21(); + init_fr$20(); + init_fr$19(); + init_fr$18(); + init_fr$17(); + init_fr$16(); + init_fr$15(); + init_fr$14(); + init_fr$13(); + init_fr$12(); + init_fr$11(); + init_fr$10(); + init_fr$9(); + init_fr$8(); + init_fr$7(); + init_fr$6(); + init_fr$5(); + init_fr$4(); + init_fr$3(); + init_fr$2(); + init_fr$1(); + modules$4 = /* @__PURE__ */ Object.assign({ + "../../../atoms/interactive/range-timeline/i18n/fr.ts": fr_exports$25, + "../../../cards/action/i18n/fr.ts": fr_exports$24, + "../../../cards/history/history-chart/i18n/fr.ts": fr_exports$23, + "../../../cards/history/i18n/fr.ts": fr_exports$22, + "../../../cards/list/i18n/fr.ts": fr_exports$21, + "../../../cards/quick/i18n/fr.ts": fr_exports$20, + "../../../cards/sensor/i18n/fr.ts": fr_exports$19, + "../../chart/i18n/fr.ts": fr_exports$18, + "../../ha/i18n/fr.ts": fr_exports$17, + "../../timeline/i18n/fr.ts": fr_exports$16, + "../../../molecules/analysis-anomaly-group/i18n/fr.ts": fr_exports$15, + "../../../molecules/analysis-delta-group/i18n/fr.ts": fr_exports$14, + "../../../molecules/analysis-rate-group/i18n/fr.ts": fr_exports$13, + "../../../molecules/analysis-sample-group/i18n/fr.ts": fr_exports$12, + "../../../molecules/analysis-summary-group/i18n/fr.ts": fr_exports$11, + "../../../molecules/analysis-threshold-group/i18n/fr.ts": fr_exports$10, + "../../../molecules/analysis-trend-group/i18n/fr.ts": fr_exports$9, + "../../../molecules/comparison-tab-rail/i18n/fr.ts": fr_exports$8, + "../../../molecules/date-window-dialog/i18n/fr.ts": fr_exports$7, + "../../../molecules/sidebar-options/sections/i18n/fr.ts": fr_exports$6, + "../../../molecules/target-row/i18n/fr.ts": fr_exports$5, + "../../../molecules/target-row-list/i18n/fr.ts": fr_exports$4, + "../../../panels/datapoints/components/history-targets/i18n/fr.ts": fr_exports$3, + "../../../panels/datapoints/components/panel-shell/i18n/fr.ts": fr_exports$2, + "../../../panels/datapoints/components/range-toolbar/i18n/fr.ts": fr_exports$1 + }); + merged$4 = {}; + for (const mod of Object.values(modules$4)) Object.assign(merged$4, mod.translations); + templates$4 = merged$4; + })); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/range-timeline/i18n/de.ts + var de_exports$25 = /* @__PURE__ */ __exportAll({ translations: () => translations$99 }); + var translations$99; + var init_de$25 = __esmMin((() => { + translations$99 = { + "Updates with new data": "Aktualisiert sich mit neuen Daten", + "Scroll to selected range": "Zum ausgewählten Bereich scrollen", + "Start date and time": "Startdatum und -zeit", + "End date and time": "Enddatum und -zeit", + Select: "Auswählen" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/action/i18n/de.ts + var de_exports$24 = /* @__PURE__ */ __exportAll({ translations: () => translations$98 }); + var translations$98; + var init_de$24 = __esmMin((() => { + translations$98 = { + General: "Allgemein", + "Related items": "Verknüpfte Elemente", + "Datapoint Appearance": "Darstellung des Datenpunkts", + "Form fields": "Formularfelder", + "Card title (optional)": "Kartentitel (optional)", + "Pre-fill entities, devices, areas or labels that are always linked to recordings from this card.": "Entitäten, Geräte, Bereiche oder Labels vorab ausfüllen, die immer mit Aufzeichnungen dieser Karte verknüpft sind.", + "Show always included targets on card": "Immer enthaltene Ziele auf der Karte anzeigen", + "Allow user to add more related items": "Benutzer dürfen weitere verknüpfte Elemente hinzufügen", + "Default icon": "Standardsymbol", + "Default colour": "Standardfarbe", + "Show date & time field": "Datums- und Uhrzeitfeld anzeigen", + "Show annotation field": "Anmerkungsfeld anzeigen" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/history-chart/i18n/de.ts + var de_exports$23 = /* @__PURE__ */ __exportAll({ translations: () => translations$97 }); + var translations$97; + var init_de$23 = __esmMin((() => { + translations$97 = { + "Date window:": "Datumsfenster:", + "Actual:": "Tatsächlich:" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/i18n/de.ts + var de_exports$22 = /* @__PURE__ */ __exportAll({ translations: () => translations$96 }); + var translations$96; + var init_de$22 = __esmMin((() => { + translations$96 = { + General: "Allgemein", + Entity: "Entität", + "Multiple entities": "Mehrere Entitäten", + Display: "Anzeige", + "Card title (optional)": "Kartentitel (optional)", + "Hours to show": "Anzuzeigende Stunden", + "Single entity": "Einzelne Entität", + "Show data gaps": "Datenlücken anzeigen", + "Highlight missing data ranges with dashed lines and boundary markers": "Fehlende Datenbereiche mit gestrichelten Linien und Randmarkierungen hervorheben" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/list/i18n/de.ts + var de_exports$21 = /* @__PURE__ */ __exportAll({ translations: () => translations$95 }); + var translations$95; + var init_de$21 = __esmMin((() => { + translations$95 = { + "Search datapoints…": "Datenpunkte suchen…", + "Delete record": "Eintrag löschen", + Delete: "Löschen", + "Show annotation": "Anmerkung anzeigen", + "Open related data point history": "Verlauf des verknüpften Datenpunkts öffnen", + "Edit record": "Eintrag bearbeiten", + "Show chart marker": "Diagrammmarkierung anzeigen", + "Hide chart marker": "Diagrammmarkierung ausblenden", + "Choose colour": "Farbe auswählen", + Save: "Speichern", + Cancel: "Abbrechen", + Message: "Nachricht", + "Annotation / full message": "Anmerkung / vollständige Nachricht" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/quick/i18n/de.ts + var de_exports$20 = /* @__PURE__ */ __exportAll({ translations: () => translations$94 }); + var translations$94; + var init_de$20 = __esmMin((() => { + translations$94 = { + General: "Allgemein", + "Icon & colour": "Symbol und Farbe", + "Related items": "Verknüpfte Elemente", + "Multiple entities": "Mehrere Entitäten", + "Form fields": "Formularfelder", + "Card title (optional)": "Kartentitel (optional)", + "Input placeholder text": "Platzhaltertext", + Icon: "Symbol", + Colour: "Farbe", + "These items will be linked to every record made with this card.": "Diese Elemente werden mit jedem Eintrag verknüpft, der mit dieser Karte erstellt wird.", + "Single entity (optional)": "Einzelne Entität (optional)", + "Add related items": "Verknüpfte Elemente hinzufügen", + "Show annotation field": "Anmerkungsfeld anzeigen" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/i18n/de.ts + var de_exports$19 = /* @__PURE__ */ __exportAll({ translations: () => translations$93 }); + var translations$93; + var init_de$19 = __esmMin((() => { + translations$93 = { + Entity: "Entität", + Display: "Anzeige", + "Records list": "Eintragsliste", + "Sensor entity *": "Sensor-Entität *", + "Override display name (optional)": "Anzeigenamen überschreiben (optional)", + "Hours to show": "Anzuzeigende Stunden", + "Graph colour": "Diagrammfarbe", + "Annotation style": "Anmerkungsstil", + "Circle on line": "Kreis auf der Linie", + "Dotted vertical line": "Gepunktete vertikale Linie", + "Show records list below graph": "Eintragsliste unter dem Diagramm anzeigen", + "Records per page (blank = show all)": "Einträge pro Seite (leer = alle anzeigen)", + "Max records to show (blank = all)": "Maximal anzuzeigende Einträge (leer = alle)", + "Show full message": "Vollständige Nachricht anzeigen", + "User will be able to expand the row if hidden": "Benutzer können die Zeile erweitern, wenn sie ausgeblendet ist" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/chart/i18n/de.ts + var de_exports$18 = /* @__PURE__ */ __exportAll({ translations: () => translations$92 }); + var translations$92; + var init_de$18 = __esmMin((() => { + translations$92 = { + "⚠️ Anomaly Insight": "⚠️ Anomalie-Einblick", + "⚠️ Multi-method Anomaly": "⚠️ Anomalie mit mehreren Methoden", + "Click the highlighted circle to add an annotation.": "Klicken Sie auf den hervorgehobenen Kreis, um eine Anmerkung hinzuzufügen.", + "Alert:": "Warnung:", + "Confirmed by": "Bestätigt durch", + "methods:": "Methoden:", + "Trend deviation": "Trendabweichung", + "Sudden change": "Plötzliche Änderung", + "Statistical outlier (IQR)": "Statistischer Ausreißer (IQR)", + "Rolling Z-score": "Gleitender Z-Score", + "Flat-line / stuck": "Flach / festhängend", + "Comparison window": "Vergleichsfenster", + "{0} deviates from its expected trend between {1} and {2}.": "{0} weicht zwischen {1} und {2} von seinem erwarteten Trend ab.", + "{0} shows an unusual rate of change between {1} and {2}.": "{0} zeigt zwischen {1} und {2} eine ungewöhnliche Änderungsrate.", + "{0} contains statistical outliers between {1} and {2}.": "{0} enthält statistische Ausreißer zwischen {1} und {2}.", + "{0} shows statistically unusual values between {1} and {2}.": "{0} zeigt zwischen {1} und {2} statistisch ungewöhnliche Werte.", + "{0} appears stuck or flat between {1} and {2}{3}.": "{0} scheint zwischen {1} und {2}{3} festzuhängen oder flach zu sein.", + "{0} deviates significantly from the comparison window between {1} and {2}.": "{0} weicht zwischen {1} und {2} deutlich vom Vergleichsfenster ab.", + "Peak deviation: {0} from a baseline of {1} at {2}.": "Maximale Abweichung: {0} von einer Basis von {1} um {2}.", + "Peak rate deviation: {0} from a typical rate of {1} at {2}.": "Maximale Ratenabweichung: {0} von einer typischen Rate von {1} um {2}.", + "Peak value: {0}, deviating {1} from the median at {2}.": "Spitzenwert: {0}, weicht um {1} vom Median bei {2} ab.", + "Peak deviation: {0} from a rolling mean of {1} at {2}.": "Maximale Abweichung: {0} von einem gleitenden Mittelwert von {1} um {2}.", + "Value remained near {0} for an unusually long period.": "Der Wert blieb ungewöhnlich lange nahe bei {0}.", + "Peak deviation from comparison: {0} at {1}.": "Maximale Abweichung vom Vergleich: {0} um {1}.", + " (range: {0})": " (Bereich: {0})", + "Date window": "Datumsfenster", + Trend: "Trend", + Rate: "Rate", + Delta: "Delta", + Threshold: "Schwellenwert" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/ha/i18n/de.ts + var de_exports$17 = /* @__PURE__ */ __exportAll({ translations: () => translations$91 }); + var translations$91; + var init_de$17 = __esmMin((() => { + translations$91 = { + "Confirm delete": "Löschen bestätigen", + "Are you sure you want to delete this item?": "Möchten Sie dieses Element wirklich löschen?", + Cancel: "Abbrechen", + Delete: "Löschen", + "Delete date window": "Datumsfenster löschen", + "this date window": "dieses Datumsfenster", + "Edit date window": "Datumsfenster bearbeiten", + "Add date window": "Datumsfenster hinzufügen", + "Save date window": "Datumsfenster speichern", + "Create date window": "Datumsfenster erstellen" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/timeline/i18n/de.ts + var de_exports$16 = /* @__PURE__ */ __exportAll({ translations: () => translations$90 }); + var translations$90; + var init_de$16 = __esmMin((() => { + translations$90 = { + Wk: "KW", + "Week of": "Woche von" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-anomaly-group/i18n/de.ts + var de_exports$15 = /* @__PURE__ */ __exportAll({ translations: () => translations$89 }); + var translations$89; + var init_de$15 = __esmMin((() => { + translations$89 = { + "Show anomalies": "Anomalien anzeigen", + Sensitivity: "Empfindlichkeit", + "Use downsampled data for detection": "Heruntergesampelte Daten für die Erkennung verwenden", + "Rate window": "Ratenfenster", + "Rolling window": "Gleitendes Fenster", + "Min flat duration": "Minimale Flachdauer", + "Compare to window": "Mit Fenster vergleichen", + "— select window —": "— Fenster auswählen —", + "When methods overlap": "Wenn sich Methoden überschneiden", + Low: "Niedrig", + Medium: "Mittel", + High: "Hoch", + "Trend deviation": "Trendabweichung", + "Sudden change": "Plötzliche Änderung", + "Statistical outlier (IQR)": "Statistischer Ausreißer (IQR)", + "Rolling Z-score": "Gleitender Z-Score", + "Flat-line / stuck value": "Flacher / festhängender Wert", + "Comparison window deviation": "Abweichung vom Vergleichsfenster", + "Flags points that deviate significantly from a fitted trend line. Good for catching gradual drift or sudden jumps away from a steady baseline.": "Markiert Punkte, die deutlich von einer angepassten Trendlinie abweichen. Gut geeignet, um schleichende Drift oder plötzliche Sprünge von einer stabilen Basis zu erkennen.", + "Flags unusually fast rises or drops compared to the typical rate of change. Best for detecting spikes, crashes, or rapid transitions.": "Markiert ungewöhnlich schnelle Anstiege oder Abfälle im Vergleich zur typischen Änderungsrate. Ideal zum Erkennen von Spitzen, Einbrüchen oder schnellen Übergängen.", + "Uses the interquartile range to flag values far outside the normal spread of data. Robust against outliers that skew averages.": "Verwendet den Interquartilsabstand, um Werte zu markieren, die weit außerhalb der normalen Datenstreuung liegen. Robust gegenüber Ausreißern, die Mittelwerte verzerren.", + "Compares each value to a rolling mean and standard deviation. Catches unusual readings relative to recent context rather than the whole series.": "Vergleicht jeden Wert mit einem gleitenden Mittelwert und einer Standardabweichung. Erkennt ungewöhnliche Werte relativ zum jüngsten Kontext statt zur gesamten Serie.", + "Flags when a sensor reports nearly the same value for an unusually long time. Useful for detecting stuck sensors or frozen readings.": "Markiert, wenn ein Sensor ungewöhnlich lange nahezu denselben Wert meldet. Nützlich zum Erkennen festhängender Sensoren oder eingefrorener Messwerte.", + "Compares the current period to a reference date window. Highlights differences from an expected historical pattern, such as last week or the same day last year.": "Vergleicht den aktuellen Zeitraum mit einem Referenz-Datumsfenster. Hebt Unterschiede zu einem erwarteten historischen Muster hervor, etwa zur letzten Woche oder zum gleichen Tag im Vorjahr.", + "Show all anomalies": "Alle Anomalien anzeigen", + "Overlaps only": "Nur Überlappungen", + "Computing…": "Wird berechnet…", + "1 hour": "1 Stunde", + "3 hours": "3 Stunden", + "6 hours": "6 Stunden", + "12 hours": "12 Stunden", + "24 hours": "24 Stunden", + "7 days": "7 Tage", + "30 minutes": "30 Minuten" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-delta-group/i18n/de.ts + var de_exports$14 = /* @__PURE__ */ __exportAll({ translations: () => translations$88 }); + var translations$88; + var init_de$14 = __esmMin((() => { + translations$88 = { + "Show delta vs selected date window": "Delta gegenüber dem ausgewählten Datumsfenster anzeigen", + "Select a date window tab to enable delta analysis.": "Wählen Sie einen Tab für ein Datumsfenster, um die Delta-Analyse zu aktivieren.", + "Show delta in tooltip": "Delta im Tooltip anzeigen", + "Show delta lines": "Delta-Linien anzeigen" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-rate-group/i18n/de.ts + var de_exports$13 = /* @__PURE__ */ __exportAll({ translations: () => translations$87 }); + var translations$87; + var init_de$13 = __esmMin((() => { + translations$87 = { + "Show rate of change": "Änderungsrate anzeigen", + "Show rate of change crosshairs": "Fadenkreuz für Änderungsrate anzeigen", + "Rate window": "Ratenfenster", + "Point to point": "Punkt zu Punkt", + "1 hour": "1 Stunde", + "6 hours": "6 Stunden", + "24 hours": "24 Stunden" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-sample-group/i18n/de.ts + var de_exports$12 = /* @__PURE__ */ __exportAll({ translations: () => translations$86 }); + var translations$86; + var init_de$12 = __esmMin((() => { + translations$86 = { + Downsampling: "Downsampling", + Interval: "Intervall", + Aggregate: "Aggregation", + "Raw (no sampling)": "Roh (ohne Sampling)", + "5 seconds": "5 Sekunden", + "10 seconds": "10 Sekunden", + "15 seconds": "15 Sekunden", + "30 seconds": "30 Sekunden", + "1 minute": "1 Minute", + "2 minutes": "2 Minuten", + "5 minutes": "5 Minuten", + "10 minutes": "10 Minuten", + "15 minutes": "15 Minuten", + "30 minutes": "30 Minuten", + "1 hour": "1 Stunde", + "2 hours": "2 Stunden", + "3 hours": "3 Stunden", + "4 hours": "4 Stunden", + "6 hours": "6 Stunden", + "12 hours": "12 Stunden", + "24 hours": "24 Stunden", + "Mean (average)": "Mittelwert", + Min: "Min.", + Max: "Max.", + Median: "Median", + First: "Erster", + Last: "Letzter" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-summary-group/i18n/de.ts + var de_exports$11 = /* @__PURE__ */ __exportAll({ translations: () => translations$85 }); + var translations$85; + var init_de$11 = __esmMin((() => { + translations$85 = { + "Show min / max / mean": "Min / Max / Mittelwert anzeigen", + "Show range shading": "Bereichsschattierung anzeigen" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-threshold-group/i18n/de.ts + var de_exports$10 = /* @__PURE__ */ __exportAll({ translations: () => translations$84 }); + var translations$84; + var init_de$10 = __esmMin((() => { + translations$84 = { + "Show threshold analysis": "Schwellwertanalyse anzeigen", + "Shade threshold area": "Schwellwertbereich schattieren", + Threshold: "Schwellenwert", + "Shade area": "Bereich schattieren", + "Shade above": "Oberhalb schattieren", + "Shade below": "Unterhalb schattieren" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-trend-group/i18n/de.ts + var de_exports$9 = /* @__PURE__ */ __exportAll({ translations: () => translations$83 }); + var translations$83; + var init_de$9 = __esmMin((() => { + translations$83 = { + "Show trend lines": "Trendlinien anzeigen", + "Show trend crosshairs": "Fadenkreuz für Trend anzeigen", + "Trend method": "Trendmethode", + "Trend window": "Trendfenster", + "Rolling average": "Gleitender Durchschnitt", + "Linear trend": "Linearer Trend", + "1 hour": "1 Stunde", + "6 hours": "6 Stunden", + "24 hours": "24 Stunden", + "7 days": "7 Tage", + "14 days": "14 Tage", + "21 days": "21 Tage", + "28 days": "28 Tage" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/comparison-tab-rail/i18n/de.ts + var de_exports$8 = /* @__PURE__ */ __exportAll({ translations: () => translations$82 }); + var translations$82; + var init_de$8 = __esmMin((() => { + translations$82 = { "Add date window": "Datumsfenster hinzufügen" }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/date-window-dialog/i18n/de.ts + var de_exports$7 = /* @__PURE__ */ __exportAll({ translations: () => translations$81 }); + var translations$81; + var init_de$7 = __esmMin((() => { + translations$81 = { + "A date window saves a named date range as a tab, so you can quickly preview it against the selected range or jump the chart back to it later.": "Ein Datumsfenster speichert einen benannten Datumsbereich als Registerkarte, damit Sie ihn schnell mit dem ausgewählten Bereich vergleichen oder später im Diagramm wieder dorthin springen können.", + Name: "Name", + "e.g. Heating season start": "z. B. Beginn der Heizsaison", + "Date range": "Datumsbereich", + Start: "Start", + End: "Ende", + "Use previous range": "Vorherigen Bereich verwenden", + "Use next range": "Nächsten Bereich verwenden", + "Delete date window": "Datumsfenster löschen", + Cancel: "Abbrechen" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/sidebar-options/sections/i18n/de.ts + var de_exports$6 = /* @__PURE__ */ __exportAll({ translations: () => translations$80 }); + var translations$80; + var init_de$6 = __esmMin((() => { + translations$80 = { + Datapoints: "Datenpunkte", + "Choose which annotation datapoints appear on the chart.": "Wählen Sie aus, welche Anmerkungs-Datenpunkte im Diagramm angezeigt werden.", + "Linked to selected targets": "Mit ausgewählten Zielen verknüpft", + "All datapoints": "Alle Datenpunkte", + "Hide datapoints": "Datenpunkte ausblenden" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/target-row/i18n/de.ts + var de_exports$5 = /* @__PURE__ */ __exportAll({ translations: () => translations$79 }); + var translations$79; + var init_de$5 = __esmMin((() => { + translations$79 = { + "Analysis configured": "Analyse konfiguriert", + "Configure analysis": "Analyse konfigurieren", + "Stepped series": "Stufenserie", + "Hide source series": "Quellserie ausblenden", + "All targets already have the same settings": "Alle Ziele haben bereits dieselben Einstellungen", + "Copy these analysis settings to all targets": "Diese Analyseeinstellungen auf alle Ziele kopieren", + "Copy to all targets": "Auf alle Ziele kopieren" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/target-row-list/i18n/de.ts + var de_exports$4 = /* @__PURE__ */ __exportAll({ translations: () => translations$78 }); + var translations$78; + var init_de$4 = __esmMin((() => { + translations$78 = {}; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/history-targets/i18n/de.ts + var de_exports$3 = /* @__PURE__ */ __exportAll({ translations: () => translations$77 }); + var translations$77; + var init_de$3 = __esmMin((() => { + translations$77 = { + Targets: "Ziele", + "Each row controls one chart series.": "Jede Zeile steuert eine Diagrammserie.", + "Add target": "Ziel hinzufügen", + "Chart preferences": "Diagrammeinstellungen" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/panel-shell/i18n/de.ts + var de_exports$2 = /* @__PURE__ */ __exportAll({ translations: () => translations$76 }); + var translations$76; + var init_de$2 = __esmMin((() => { + translations$76 = { + "Loading Datapoints…": "Datenpunkte werden geladen…", + Datapoints: "Datenpunkte", + "Page options": "Seitenoptionen", + "Download spreadsheet": "Tabellenblatt herunterladen", + "Save page state": "Seitenstatus speichern", + "Restore saved page": "Gespeicherte Seite wiederherstellen", + "Clear saved page": "Gespeicherte Seite löschen", + "Expand targets sidebar": "Ziel-Seitenleiste ausklappen", + "Collapse targets sidebar": "Ziel-Seitenleiste einklappen" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/range-toolbar/i18n/de.ts + var de_exports$1 = /* @__PURE__ */ __exportAll({ translations: () => translations$75 }); + var translations$75; + var init_de$1 = __esmMin((() => { + translations$75 = { + "Toggle sidebar": "Seitenleiste umschalten", + Start: "Start", + End: "Ende", + "Select date range": "Datumsbereich auswählen", + "Timeline options": "Zeitachsenoptionen", + "Zoom level": "Zoomstufe", + "Date snapping": "Datumsraster", + Auto: "Auto", + Hour: "Stunde", + Day: "Tag", + Week: "Woche", + Month: "Monat", + Minute: "Minute", + Second: "Sekunde", + Quarterly: "Quartalsweise", + "Month Compressed": "Monat kompakt", + "Month Short": "Monat kurz", + "Month Expanded": "Monat erweitert", + "Week Compressed": "Woche kompakt", + "Week Expanded": "Woche erweitert" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/i18n/locales/de.ts + var de_exports = /* @__PURE__ */ __exportAll({ templates: () => templates$3 }); + var modules$3, merged$3, templates$3; + var init_de = __esmMin((() => { + init_de$25(); + init_de$24(); + init_de$23(); + init_de$22(); + init_de$21(); + init_de$20(); + init_de$19(); + init_de$18(); + init_de$17(); + init_de$16(); + init_de$15(); + init_de$14(); + init_de$13(); + init_de$12(); + init_de$11(); + init_de$10(); + init_de$9(); + init_de$8(); + init_de$7(); + init_de$6(); + init_de$5(); + init_de$4(); + init_de$3(); + init_de$2(); + init_de$1(); + modules$3 = /* @__PURE__ */ Object.assign({ + "../../../atoms/interactive/range-timeline/i18n/de.ts": de_exports$25, + "../../../cards/action/i18n/de.ts": de_exports$24, + "../../../cards/history/history-chart/i18n/de.ts": de_exports$23, + "../../../cards/history/i18n/de.ts": de_exports$22, + "../../../cards/list/i18n/de.ts": de_exports$21, + "../../../cards/quick/i18n/de.ts": de_exports$20, + "../../../cards/sensor/i18n/de.ts": de_exports$19, + "../../chart/i18n/de.ts": de_exports$18, + "../../ha/i18n/de.ts": de_exports$17, + "../../timeline/i18n/de.ts": de_exports$16, + "../../../molecules/analysis-anomaly-group/i18n/de.ts": de_exports$15, + "../../../molecules/analysis-delta-group/i18n/de.ts": de_exports$14, + "../../../molecules/analysis-rate-group/i18n/de.ts": de_exports$13, + "../../../molecules/analysis-sample-group/i18n/de.ts": de_exports$12, + "../../../molecules/analysis-summary-group/i18n/de.ts": de_exports$11, + "../../../molecules/analysis-threshold-group/i18n/de.ts": de_exports$10, + "../../../molecules/analysis-trend-group/i18n/de.ts": de_exports$9, + "../../../molecules/comparison-tab-rail/i18n/de.ts": de_exports$8, + "../../../molecules/date-window-dialog/i18n/de.ts": de_exports$7, + "../../../molecules/sidebar-options/sections/i18n/de.ts": de_exports$6, + "../../../molecules/target-row/i18n/de.ts": de_exports$5, + "../../../molecules/target-row-list/i18n/de.ts": de_exports$4, + "../../../panels/datapoints/components/history-targets/i18n/de.ts": de_exports$3, + "../../../panels/datapoints/components/panel-shell/i18n/de.ts": de_exports$2, + "../../../panels/datapoints/components/range-toolbar/i18n/de.ts": de_exports$1 + }); + merged$3 = {}; + for (const mod of Object.values(modules$3)) Object.assign(merged$3, mod.translations); + templates$3 = merged$3; + })); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/range-timeline/i18n/es.ts + var es_exports$25 = /* @__PURE__ */ __exportAll({ translations: () => translations$74 }); + var translations$74; + var init_es$25 = __esmMin((() => { + translations$74 = { + "Updates with new data": "Se actualiza con datos nuevos", + "Scroll to selected range": "Desplazarse al rango seleccionado", + "Start date and time": "Fecha y hora de inicio", + "End date and time": "Fecha y hora de fin", + Select: "Seleccionar" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/action/i18n/es.ts + var es_exports$24 = /* @__PURE__ */ __exportAll({ translations: () => translations$73 }); + var translations$73; + var init_es$24 = __esmMin((() => { + translations$73 = { + General: "General", + "Related items": "Elementos relacionados", + "Datapoint Appearance": "Apariencia del punto de datos", + "Form fields": "Campos del formulario", + "Card title (optional)": "Título de la tarjeta (opcional)", + "Pre-fill entities, devices, areas or labels that are always linked to recordings from this card.": "Rellena previamente entidades, dispositivos, áreas o etiquetas que siempre estarán vinculados a los registros creados con esta tarjeta.", + "Show always included targets on card": "Mostrar en la tarjeta los objetivos siempre incluidos", + "Allow user to add more related items": "Permitir que el usuario añada más elementos relacionados", + "Default icon": "Icono predeterminado", + "Default colour": "Color predeterminado", + "Show date & time field": "Mostrar campo de fecha y hora", + "Show annotation field": "Mostrar campo de anotación" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/history-chart/i18n/es.ts + var es_exports$23 = /* @__PURE__ */ __exportAll({ translations: () => translations$72 }); + var translations$72; + var init_es$23 = __esmMin((() => { + translations$72 = { + "Date window:": "Ventana de fechas:", + "Actual:": "Real:" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/i18n/es.ts + var es_exports$22 = /* @__PURE__ */ __exportAll({ translations: () => translations$71 }); + var translations$71; + var init_es$22 = __esmMin((() => { + translations$71 = { + General: "General", + Entity: "Entidad", + "Multiple entities": "Varias entidades", + Display: "Visualización", + "Card title (optional)": "Título de la tarjeta (opcional)", + "Hours to show": "Horas a mostrar", + "Single entity": "Entidad única", + "Show data gaps": "Mostrar huecos de datos", + "Highlight missing data ranges with dashed lines and boundary markers": "Resaltar los rangos de datos faltantes con líneas discontinuas y marcadores de límite" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/list/i18n/es.ts + var es_exports$21 = /* @__PURE__ */ __exportAll({ translations: () => translations$70 }); + var translations$70; + var init_es$21 = __esmMin((() => { + translations$70 = { + "Search datapoints…": "Buscar puntos de datos…", + "Delete record": "Eliminar registro", + Delete: "Eliminar", + "Show annotation": "Mostrar anotación", + "Open related data point history": "Abrir el historial del punto de datos relacionado", + "Edit record": "Editar registro", + "Show chart marker": "Mostrar marcador del gráfico", + "Hide chart marker": "Ocultar marcador del gráfico", + "Choose colour": "Elegir color", + Save: "Guardar", + Cancel: "Cancelar", + Message: "Mensaje", + "Annotation / full message": "Anotación / mensaje completo" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/quick/i18n/es.ts + var es_exports$20 = /* @__PURE__ */ __exportAll({ translations: () => translations$69 }); + var translations$69; + var init_es$20 = __esmMin((() => { + translations$69 = { + General: "General", + "Icon & colour": "Icono y color", + "Related items": "Elementos relacionados", + "Multiple entities": "Varias entidades", + "Form fields": "Campos del formulario", + "Card title (optional)": "Título de la tarjeta (opcional)", + "Input placeholder text": "Texto de marcador de posición", + Icon: "Icono", + Colour: "Color", + "These items will be linked to every record made with this card.": "Estos elementos se vincularán a cada registro creado con esta tarjeta.", + "Single entity (optional)": "Entidad única (opcional)", + "Add related items": "Añadir elementos relacionados", + "Show annotation field": "Mostrar campo de anotación" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/i18n/es.ts + var es_exports$19 = /* @__PURE__ */ __exportAll({ translations: () => translations$68 }); + var translations$68; + var init_es$19 = __esmMin((() => { + translations$68 = { + Entity: "Entidad", + Display: "Visualización", + "Records list": "Lista de registros", + "Sensor entity *": "Entidad de sensor *", + "Override display name (optional)": "Sobrescribir nombre visible (opcional)", + "Hours to show": "Horas a mostrar", + "Graph colour": "Color del gráfico", + "Annotation style": "Estilo de anotación", + "Circle on line": "Círculo sobre la línea", + "Dotted vertical line": "Línea vertical punteada", + "Show records list below graph": "Mostrar lista de registros debajo del gráfico", + "Records per page (blank = show all)": "Registros por página (en blanco = mostrar todos)", + "Max records to show (blank = all)": "Máximo de registros a mostrar (en blanco = todos)", + "Show full message": "Mostrar mensaje completo", + "User will be able to expand the row if hidden": "El usuario podrá ampliar la fila si está oculta" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/chart/i18n/es.ts + var es_exports$18 = /* @__PURE__ */ __exportAll({ translations: () => translations$67 }); + var translations$67; + var init_es$18 = __esmMin((() => { + translations$67 = { + "⚠️ Anomaly Insight": "⚠️ Información de anomalía", + "⚠️ Multi-method Anomaly": "⚠️ Anomalía multimétodo", + "Click the highlighted circle to add an annotation.": "Haz clic en el círculo resaltado para añadir una anotación.", + "Alert:": "Alerta:", + "Confirmed by": "Confirmado por", + "methods:": "métodos:", + "Trend deviation": "Desviación de tendencia", + "Sudden change": "Cambio repentino", + "Statistical outlier (IQR)": "Valor atípico estadístico (IQR)", + "Rolling Z-score": "Puntuación Z móvil", + "Flat-line / stuck": "Plano / atascado", + "Comparison window": "Ventana de comparación", + "{0} deviates from its expected trend between {1} and {2}.": "{0} se desvía de su tendencia esperada entre {1} y {2}.", + "{0} shows an unusual rate of change between {1} and {2}.": "{0} muestra una tasa de cambio inusual entre {1} y {2}.", + "{0} contains statistical outliers between {1} and {2}.": "{0} contiene valores atípicos estadísticos entre {1} y {2}.", + "{0} shows statistically unusual values between {1} and {2}.": "{0} muestra valores estadísticamente inusuales entre {1} y {2}.", + "{0} appears stuck or flat between {1} and {2}{3}.": "{0} parece atascado o plano entre {1} y {2}{3}.", + "{0} deviates significantly from the comparison window between {1} and {2}.": "{0} se desvía significativamente de la ventana de comparación entre {1} y {2}.", + "Peak deviation: {0} from a baseline of {1} at {2}.": "Desviación máxima: {0} respecto a una base de {1} en {2}.", + "Peak rate deviation: {0} from a typical rate of {1} at {2}.": "Desviación máxima de la tasa: {0} respecto a una tasa típica de {1} en {2}.", + "Peak value: {0}, deviating {1} from the median at {2}.": "Valor máximo: {0}, con una desviación de {1} respecto a la mediana en {2}.", + "Peak deviation: {0} from a rolling mean of {1} at {2}.": "Desviación máxima: {0} respecto a una media móvil de {1} en {2}.", + "Value remained near {0} for an unusually long period.": "El valor permaneció cerca de {0} durante un período inusualmente largo.", + "Peak deviation from comparison: {0} at {1}.": "Desviación máxima respecto a la comparación: {0} en {1}.", + " (range: {0})": " (rango: {0})", + "Date window": "Ventana de fechas", + Trend: "Tendencia", + Rate: "Tasa", + Delta: "Delta", + Threshold: "Umbral" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/ha/i18n/es.ts + var es_exports$17 = /* @__PURE__ */ __exportAll({ translations: () => translations$66 }); + var translations$66; + var init_es$17 = __esmMin((() => { + translations$66 = { + "Confirm delete": "Confirmar eliminación", + "Are you sure you want to delete this item?": "¿Seguro que quieres eliminar este elemento?", + Cancel: "Cancelar", + Delete: "Eliminar", + "Delete date window": "Eliminar ventana de fechas", + "this date window": "esta ventana de fechas", + "Edit date window": "Editar ventana de fechas", + "Add date window": "Añadir ventana de fechas", + "Save date window": "Guardar ventana de fechas", + "Create date window": "Crear ventana de fechas" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/timeline/i18n/es.ts + var es_exports$16 = /* @__PURE__ */ __exportAll({ translations: () => translations$65 }); + var translations$65; + var init_es$16 = __esmMin((() => { + translations$65 = { + Wk: "Sem.", + "Week of": "Semana del" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-anomaly-group/i18n/es.ts + var es_exports$15 = /* @__PURE__ */ __exportAll({ translations: () => translations$64 }); + var translations$64; + var init_es$15 = __esmMin((() => { + translations$64 = { + "Show anomalies": "Mostrar anomalías", + Sensitivity: "Sensibilidad", + "Use downsampled data for detection": "Usar datos submuestreados para la detección", + "Rate window": "Ventana de tasa", + "Rolling window": "Ventana móvil", + "Min flat duration": "Duración mínima plana", + "Compare to window": "Comparar con la ventana", + "— select window —": "— seleccionar ventana —", + "When methods overlap": "Cuando los métodos se superponen", + Low: "Baja", + Medium: "Media", + High: "Alta", + "Trend deviation": "Desviación de tendencia", + "Sudden change": "Cambio repentino", + "Statistical outlier (IQR)": "Valor atípico estadístico (IQR)", + "Rolling Z-score": "Puntuación Z móvil", + "Flat-line / stuck value": "Valor plano / atascado", + "Comparison window deviation": "Desviación respecto a la ventana de comparación", + "Flags points that deviate significantly from a fitted trend line. Good for catching gradual drift or sudden jumps away from a steady baseline.": "Marca puntos que se desvían significativamente de una línea de tendencia ajustada. Es útil para detectar una deriva gradual o saltos bruscos desde una base estable.", + "Flags unusually fast rises or drops compared to the typical rate of change. Best for detecting spikes, crashes, or rapid transitions.": "Marca subidas o bajadas inusualmente rápidas en comparación con la tasa de cambio típica. Es ideal para detectar picos, caídas o transiciones rápidas.", + "Uses the interquartile range to flag values far outside the normal spread of data. Robust against outliers that skew averages.": "Usa el rango intercuartílico para marcar valores muy alejados de la dispersión normal de los datos. Es robusto frente a valores atípicos que sesgan las medias.", + "Compares each value to a rolling mean and standard deviation. Catches unusual readings relative to recent context rather than the whole series.": "Compara cada valor con una media móvil y una desviación estándar. Detecta lecturas inusuales en relación con el contexto reciente, no con toda la serie.", + "Flags when a sensor reports nearly the same value for an unusually long time. Useful for detecting stuck sensors or frozen readings.": "Marca cuando un sensor informa casi el mismo valor durante un tiempo inusualmente largo. Es útil para detectar sensores atascados o lecturas congeladas.", + "Compares the current period to a reference date window. Highlights differences from an expected historical pattern, such as last week or the same day last year.": "Compara el periodo actual con una ventana de fechas de referencia. Destaca las diferencias respecto a un patrón histórico esperado, como la semana pasada o el mismo día del año anterior.", + "Show all anomalies": "Mostrar todas las anomalías", + "Overlaps only": "Solo solapamientos", + "Computing…": "Calculando…", + "1 hour": "1 hora", + "3 hours": "3 horas", + "6 hours": "6 horas", + "12 hours": "12 horas", + "24 hours": "24 horas", + "7 days": "7 días", + "30 minutes": "30 minutos" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-delta-group/i18n/es.ts + var es_exports$14 = /* @__PURE__ */ __exportAll({ translations: () => translations$63 }); + var translations$63; + var init_es$14 = __esmMin((() => { + translations$63 = { + "Show delta vs selected date window": "Mostrar delta frente a la ventana de fechas seleccionada", + "Select a date window tab to enable delta analysis.": "Selecciona una pestaña de ventana de fechas para habilitar el análisis delta.", + "Show delta in tooltip": "Mostrar delta en la información sobre herramientas", + "Show delta lines": "Mostrar líneas delta" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-rate-group/i18n/es.ts + var es_exports$13 = /* @__PURE__ */ __exportAll({ translations: () => translations$62 }); + var translations$62; + var init_es$13 = __esmMin((() => { + translations$62 = { + "Show rate of change": "Mostrar tasa de cambio", + "Show rate of change crosshairs": "Mostrar guías de la tasa de cambio", + "Rate window": "Ventana de tasa", + "Point to point": "Punto a punto", + "1 hour": "1 hora", + "6 hours": "6 horas", + "24 hours": "24 horas" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-sample-group/i18n/es.ts + var es_exports$12 = /* @__PURE__ */ __exportAll({ translations: () => translations$61 }); + var translations$61; + var init_es$12 = __esmMin((() => { + translations$61 = { + Downsampling: "Submuestreo", + Interval: "Intervalo", + Aggregate: "Agregado", + "Raw (no sampling)": "Sin procesar (sin muestreo)", + "5 seconds": "5 segundos", + "10 seconds": "10 segundos", + "15 seconds": "15 segundos", + "30 seconds": "30 segundos", + "1 minute": "1 minuto", + "2 minutes": "2 minutos", + "5 minutes": "5 minutos", + "10 minutes": "10 minutos", + "15 minutes": "15 minutos", + "30 minutes": "30 minutos", + "1 hour": "1 hora", + "2 hours": "2 horas", + "3 hours": "3 horas", + "4 hours": "4 horas", + "6 hours": "6 horas", + "12 hours": "12 horas", + "24 hours": "24 horas", + "Mean (average)": "Media", + Min: "Mín.", + Max: "Máx.", + Median: "Mediana", + First: "Primero", + Last: "Último" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-summary-group/i18n/es.ts + var es_exports$11 = /* @__PURE__ */ __exportAll({ translations: () => translations$60 }); + var translations$60; + var init_es$11 = __esmMin((() => { + translations$60 = { + "Show min / max / mean": "Mostrar mín. / máx. / media", + "Show range shading": "Mostrar sombreado del rango" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-threshold-group/i18n/es.ts + var es_exports$10 = /* @__PURE__ */ __exportAll({ translations: () => translations$59 }); + var translations$59; + var init_es$10 = __esmMin((() => { + translations$59 = { + "Show threshold analysis": "Mostrar análisis de umbral", + "Shade threshold area": "Sombrear área del umbral", + Threshold: "Umbral", + "Shade area": "Sombrear área", + "Shade above": "Sombrear arriba", + "Shade below": "Sombrear abajo" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-trend-group/i18n/es.ts + var es_exports$9 = /* @__PURE__ */ __exportAll({ translations: () => translations$58 }); + var translations$58; + var init_es$9 = __esmMin((() => { + translations$58 = { + "Show trend lines": "Mostrar líneas de tendencia", + "Show trend crosshairs": "Mostrar guías de tendencia", + "Trend method": "Método de tendencia", + "Trend window": "Ventana de tendencia", + "Rolling average": "Media móvil", + "Linear trend": "Tendencia lineal", + "1 hour": "1 hora", + "6 hours": "6 horas", + "24 hours": "24 horas", + "7 days": "7 días", + "14 days": "14 días", + "21 days": "21 días", + "28 days": "28 días" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/comparison-tab-rail/i18n/es.ts + var es_exports$8 = /* @__PURE__ */ __exportAll({ translations: () => translations$57 }); + var translations$57; + var init_es$8 = __esmMin((() => { + translations$57 = { "Add date window": "Añadir ventana de fechas" }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/date-window-dialog/i18n/es.ts + var es_exports$7 = /* @__PURE__ */ __exportAll({ translations: () => translations$56 }); + var translations$56; + var init_es$7 = __esmMin((() => { + translations$56 = { + "A date window saves a named date range as a tab, so you can quickly preview it against the selected range or jump the chart back to it later.": "Una ventana de fechas guarda un rango de fechas con nombre como pestaña, para que puedas previsualizarlo rápidamente frente al rango seleccionado o volver más tarde a él en el gráfico.", + Name: "Nombre", + "e.g. Heating season start": "p. ej., inicio de la temporada de calefacción", + "Date range": "Rango de fechas", + Start: "Inicio", + End: "Fin", + "Use previous range": "Usar rango anterior", + "Use next range": "Usar rango siguiente", + "Delete date window": "Eliminar ventana de fechas", + Cancel: "Cancelar" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/sidebar-options/sections/i18n/es.ts + var es_exports$6 = /* @__PURE__ */ __exportAll({ translations: () => translations$55 }); + var translations$55; + var init_es$6 = __esmMin((() => { + translations$55 = { + Datapoints: "Puntos de datos", + "Choose which annotation datapoints appear on the chart.": "Elige qué puntos de datos de anotación aparecen en el gráfico.", + "Linked to selected targets": "Vinculados a los objetivos seleccionados", + "All datapoints": "Todos los puntos de datos", + "Hide datapoints": "Ocultar puntos de datos" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/target-row/i18n/es.ts + var es_exports$5 = /* @__PURE__ */ __exportAll({ translations: () => translations$54 }); + var translations$54; + var init_es$5 = __esmMin((() => { + translations$54 = { + "Analysis configured": "Análisis configurado", + "Configure analysis": "Configurar análisis", + "Stepped series": "Serie escalonada", + "Hide source series": "Ocultar serie de origen", + "All targets already have the same settings": "Todos los objetivos ya tienen la misma configuración", + "Copy these analysis settings to all targets": "Copiar estos ajustes de análisis a todos los objetivos", + "Copy to all targets": "Copiar a todos los objetivos" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/target-row-list/i18n/es.ts + var es_exports$4 = /* @__PURE__ */ __exportAll({ translations: () => translations$53 }); + var translations$53; + var init_es$4 = __esmMin((() => { + translations$53 = {}; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/history-targets/i18n/es.ts + var es_exports$3 = /* @__PURE__ */ __exportAll({ translations: () => translations$52 }); + var translations$52; + var init_es$3 = __esmMin((() => { + translations$52 = { + Targets: "Objetivos", + "Each row controls one chart series.": "Cada fila controla una serie del gráfico.", + "Add target": "Añadir objetivo", + "Chart preferences": "Preferencias del gráfico" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/panel-shell/i18n/es.ts + var es_exports$2 = /* @__PURE__ */ __exportAll({ translations: () => translations$51 }); + var translations$51; + var init_es$2 = __esmMin((() => { + translations$51 = { + "Loading Datapoints…": "Cargando puntos de datos…", + Datapoints: "Puntos de datos", + "Page options": "Opciones de la página", + "Download spreadsheet": "Descargar hoja de cálculo", + "Save page state": "Guardar estado de la página", + "Restore saved page": "Restaurar página guardada", + "Clear saved page": "Borrar página guardada", + "Expand targets sidebar": "Expandir la barra lateral de objetivos", + "Collapse targets sidebar": "Contraer la barra lateral de objetivos" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/range-toolbar/i18n/es.ts + var es_exports$1 = /* @__PURE__ */ __exportAll({ translations: () => translations$50 }); + var translations$50; + var init_es$1 = __esmMin((() => { + translations$50 = { + "Toggle sidebar": "Alternar barra lateral", + Start: "Inicio", + End: "Fin", + "Select date range": "Seleccionar rango de fechas", + "Timeline options": "Opciones de la línea temporal", + "Zoom level": "Nivel de zoom", + "Date snapping": "Ajuste de fechas", + Auto: "Auto", + Hour: "Hora", + Day: "Día", + Week: "Semana", + Month: "Mes", + Minute: "Minuto", + Second: "Segundo", + Quarterly: "Trimestral", + "Month Compressed": "Mes comprimido", + "Month Short": "Mes corto", + "Month Expanded": "Mes expandido", + "Week Compressed": "Semana comprimida", + "Week Expanded": "Semana expandida" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/i18n/locales/es.ts + var es_exports = /* @__PURE__ */ __exportAll({ templates: () => templates$2 }); + var modules$2, merged$2, templates$2; + var init_es = __esmMin((() => { + init_es$25(); + init_es$24(); + init_es$23(); + init_es$22(); + init_es$21(); + init_es$20(); + init_es$19(); + init_es$18(); + init_es$17(); + init_es$16(); + init_es$15(); + init_es$14(); + init_es$13(); + init_es$12(); + init_es$11(); + init_es$10(); + init_es$9(); + init_es$8(); + init_es$7(); + init_es$6(); + init_es$5(); + init_es$4(); + init_es$3(); + init_es$2(); + init_es$1(); + modules$2 = /* @__PURE__ */ Object.assign({ + "../../../atoms/interactive/range-timeline/i18n/es.ts": es_exports$25, + "../../../cards/action/i18n/es.ts": es_exports$24, + "../../../cards/history/history-chart/i18n/es.ts": es_exports$23, + "../../../cards/history/i18n/es.ts": es_exports$22, + "../../../cards/list/i18n/es.ts": es_exports$21, + "../../../cards/quick/i18n/es.ts": es_exports$20, + "../../../cards/sensor/i18n/es.ts": es_exports$19, + "../../chart/i18n/es.ts": es_exports$18, + "../../ha/i18n/es.ts": es_exports$17, + "../../timeline/i18n/es.ts": es_exports$16, + "../../../molecules/analysis-anomaly-group/i18n/es.ts": es_exports$15, + "../../../molecules/analysis-delta-group/i18n/es.ts": es_exports$14, + "../../../molecules/analysis-rate-group/i18n/es.ts": es_exports$13, + "../../../molecules/analysis-sample-group/i18n/es.ts": es_exports$12, + "../../../molecules/analysis-summary-group/i18n/es.ts": es_exports$11, + "../../../molecules/analysis-threshold-group/i18n/es.ts": es_exports$10, + "../../../molecules/analysis-trend-group/i18n/es.ts": es_exports$9, + "../../../molecules/comparison-tab-rail/i18n/es.ts": es_exports$8, + "../../../molecules/date-window-dialog/i18n/es.ts": es_exports$7, + "../../../molecules/sidebar-options/sections/i18n/es.ts": es_exports$6, + "../../../molecules/target-row/i18n/es.ts": es_exports$5, + "../../../molecules/target-row-list/i18n/es.ts": es_exports$4, + "../../../panels/datapoints/components/history-targets/i18n/es.ts": es_exports$3, + "../../../panels/datapoints/components/panel-shell/i18n/es.ts": es_exports$2, + "../../../panels/datapoints/components/range-toolbar/i18n/es.ts": es_exports$1 + }); + merged$2 = {}; + for (const mod of Object.values(modules$2)) Object.assign(merged$2, mod.translations); + templates$2 = merged$2; + })); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/range-timeline/i18n/pt.ts + var pt_exports$25 = /* @__PURE__ */ __exportAll({ translations: () => translations$49 }); + var translations$49; + var init_pt$25 = __esmMin((() => { + translations$49 = { + "Updates with new data": "Atualiza com novos dados", + "Scroll to selected range": "Deslocar para o intervalo selecionado", + "Start date and time": "Data e hora de início", + "End date and time": "Data e hora de fim", + Select: "Selecionar" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/action/i18n/pt.ts + var pt_exports$24 = /* @__PURE__ */ __exportAll({ translations: () => translations$48 }); + var translations$48; + var init_pt$24 = __esmMin((() => { + translations$48 = { + General: "Geral", + "Related items": "Itens relacionados", + "Datapoint Appearance": "Aspeto do ponto de dados", + "Form fields": "Campos do formulário", + "Card title (optional)": "Título do cartão (opcional)", + "Pre-fill entities, devices, areas or labels that are always linked to recordings from this card.": "Pré-preencha entidades, dispositivos, áreas ou etiquetas que estejam sempre ligados aos registos feitos com este cartão.", + "Show always included targets on card": "Mostrar no cartão os alvos sempre incluídos", + "Allow user to add more related items": "Permitir que o utilizador adicione mais itens relacionados", + "Default icon": "Ícone predefinido", + "Default colour": "Cor predefinida", + "Show date & time field": "Mostrar campo de data e hora", + "Show annotation field": "Mostrar campo de anotação" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/history-chart/i18n/pt.ts + var pt_exports$23 = /* @__PURE__ */ __exportAll({ translations: () => translations$47 }); + var translations$47; + var init_pt$23 = __esmMin((() => { + translations$47 = { + "Date window:": "Janela de datas:", + "Actual:": "Real:" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/i18n/pt.ts + var pt_exports$22 = /* @__PURE__ */ __exportAll({ translations: () => translations$46 }); + var translations$46; + var init_pt$22 = __esmMin((() => { + translations$46 = { + General: "Geral", + Entity: "Entidade", + "Multiple entities": "Múltiplas entidades", + Display: "Visualização", + "Card title (optional)": "Título do cartão (opcional)", + "Hours to show": "Horas a mostrar", + "Single entity": "Entidade única", + "Show data gaps": "Mostrar lacunas de dados", + "Highlight missing data ranges with dashed lines and boundary markers": "Realçar intervalos de dados em falta com linhas tracejadas e marcadores de limite" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/list/i18n/pt.ts + var pt_exports$21 = /* @__PURE__ */ __exportAll({ translations: () => translations$45 }); + var translations$45; + var init_pt$21 = __esmMin((() => { + translations$45 = { + "Search datapoints…": "Pesquisar pontos de dados…", + "Delete record": "Eliminar registo", + Delete: "Eliminar", + "Show annotation": "Mostrar anotação", + "Open related data point history": "Abrir histórico do ponto de dados relacionado", + "Edit record": "Editar registo", + "Show chart marker": "Mostrar marcador do gráfico", + "Hide chart marker": "Ocultar marcador do gráfico", + "Choose colour": "Escolher cor", + Save: "Guardar", + Cancel: "Cancelar", + Message: "Mensagem", + "Annotation / full message": "Anotação / mensagem completa" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/quick/i18n/pt.ts + var pt_exports$20 = /* @__PURE__ */ __exportAll({ translations: () => translations$44 }); + var translations$44; + var init_pt$20 = __esmMin((() => { + translations$44 = { + General: "Geral", + "Icon & colour": "Ícone e cor", + "Related items": "Itens relacionados", + "Multiple entities": "Múltiplas entidades", + "Form fields": "Campos do formulário", + "Card title (optional)": "Título do cartão (opcional)", + "Input placeholder text": "Texto do marcador de posição", + Icon: "Ícone", + Colour: "Cor", + "These items will be linked to every record made with this card.": "Estes itens serão ligados a cada registo feito com este cartão.", + "Single entity (optional)": "Entidade única (opcional)", + "Add related items": "Adicionar itens relacionados", + "Show annotation field": "Mostrar campo de anotação" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/i18n/pt.ts + var pt_exports$19 = /* @__PURE__ */ __exportAll({ translations: () => translations$43 }); + var translations$43; + var init_pt$19 = __esmMin((() => { + translations$43 = { + Entity: "Entidade", + Display: "Visualização", + "Records list": "Lista de registos", + "Sensor entity *": "Entidade do sensor *", + "Override display name (optional)": "Substituir nome apresentado (opcional)", + "Hours to show": "Horas a mostrar", + "Graph colour": "Cor do gráfico", + "Annotation style": "Estilo da anotação", + "Circle on line": "Círculo na linha", + "Dotted vertical line": "Linha vertical pontilhada", + "Show records list below graph": "Mostrar lista de registos abaixo do gráfico", + "Records per page (blank = show all)": "Registos por página (em branco = mostrar todos)", + "Max records to show (blank = all)": "Máximo de registos a mostrar (em branco = todos)", + "Show full message": "Mostrar mensagem completa", + "User will be able to expand the row if hidden": "O utilizador poderá expandir a linha se estiver oculta" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/chart/i18n/pt.ts + var pt_exports$18 = /* @__PURE__ */ __exportAll({ translations: () => translations$42 }); + var translations$42; + var init_pt$18 = __esmMin((() => { + translations$42 = { + "⚠️ Anomaly Insight": "⚠️ Informação de anomalia", + "⚠️ Multi-method Anomaly": "⚠️ Anomalia multimétodo", + "Click the highlighted circle to add an annotation.": "Clique no círculo destacado para adicionar uma anotação.", + "Alert:": "Alerta:", + "Confirmed by": "Confirmado por", + "methods:": "métodos:", + "Trend deviation": "Desvio de tendência", + "Sudden change": "Mudança súbita", + "Statistical outlier (IQR)": "Valor atípico estatístico (IQR)", + "Rolling Z-score": "Z-score móvel", + "Flat-line / stuck": "Plano / bloqueado", + "Comparison window": "Janela de comparação", + "{0} deviates from its expected trend between {1} and {2}.": "{0} desvia-se da tendência esperada entre {1} e {2}.", + "{0} shows an unusual rate of change between {1} and {2}.": "{0} mostra uma taxa de variação invulgar entre {1} e {2}.", + "{0} contains statistical outliers between {1} and {2}.": "{0} contém valores atípicos estatísticos entre {1} e {2}.", + "{0} shows statistically unusual values between {1} and {2}.": "{0} mostra valores estatisticamente invulgares entre {1} e {2}.", + "{0} appears stuck or flat between {1} and {2}{3}.": "{0} parece bloqueado ou plano entre {1} e {2}{3}.", + "{0} deviates significantly from the comparison window between {1} and {2}.": "{0} desvia-se significativamente da janela de comparação entre {1} e {2}.", + "Peak deviation: {0} from a baseline of {1} at {2}.": "Desvio máximo: {0} em relação a uma base de {1} em {2}.", + "Peak rate deviation: {0} from a typical rate of {1} at {2}.": "Desvio máximo da taxa: {0} em relação a uma taxa típica de {1} em {2}.", + "Peak value: {0}, deviating {1} from the median at {2}.": "Valor máximo: {0}, desviando-se {1} da mediana em {2}.", + "Peak deviation: {0} from a rolling mean of {1} at {2}.": "Desvio máximo: {0} em relação a uma média móvel de {1} em {2}.", + "Value remained near {0} for an unusually long period.": "O valor manteve-se perto de {0} durante um período invulgarmente longo.", + "Peak deviation from comparison: {0} at {1}.": "Desvio máximo da comparação: {0} em {1}.", + " (range: {0})": " (intervalo: {0})", + "Date window": "Janela de datas", + Trend: "Tendência", + Rate: "Taxa", + Delta: "Delta", + Threshold: "Limiar" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/ha/i18n/pt.ts + var pt_exports$17 = /* @__PURE__ */ __exportAll({ translations: () => translations$41 }); + var translations$41; + var init_pt$17 = __esmMin((() => { + translations$41 = { + "Confirm delete": "Confirmar eliminação", + "Are you sure you want to delete this item?": "Tem a certeza de que pretende eliminar este item?", + Cancel: "Cancelar", + Delete: "Eliminar", + "Delete date window": "Eliminar janela de datas", + "this date window": "esta janela de datas", + "Edit date window": "Editar janela de datas", + "Add date window": "Adicionar janela de datas", + "Save date window": "Guardar janela de datas", + "Create date window": "Criar janela de datas" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/timeline/i18n/pt.ts + var pt_exports$16 = /* @__PURE__ */ __exportAll({ translations: () => translations$40 }); + var translations$40; + var init_pt$16 = __esmMin((() => { + translations$40 = { + Wk: "Sem.", + "Week of": "Semana de" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-anomaly-group/i18n/pt.ts + var pt_exports$15 = /* @__PURE__ */ __exportAll({ translations: () => translations$39 }); + var translations$39; + var init_pt$15 = __esmMin((() => { + translations$39 = { + "Show anomalies": "Mostrar anomalias", + Sensitivity: "Sensibilidade", + "Use downsampled data for detection": "Usar dados reamostrados para deteção", + "Rate window": "Janela da taxa", + "Rolling window": "Janela móvel", + "Min flat duration": "Duração mínima plana", + "Compare to window": "Comparar com a janela", + "— select window —": "— selecionar janela —", + "When methods overlap": "Quando os métodos se sobrepõem", + Low: "Baixa", + Medium: "Média", + High: "Alta", + "Trend deviation": "Desvio de tendência", + "Sudden change": "Mudança súbita", + "Statistical outlier (IQR)": "Valor atípico estatístico (IQR)", + "Rolling Z-score": "Z-score móvel", + "Flat-line / stuck value": "Valor plano / bloqueado", + "Comparison window deviation": "Desvio da janela de comparação", + "Flags points that deviate significantly from a fitted trend line. Good for catching gradual drift or sudden jumps away from a steady baseline.": "Assinala pontos que se desviam significativamente de uma linha de tendência ajustada. É útil para detetar deriva gradual ou saltos súbitos a partir de uma base estável.", + "Flags unusually fast rises or drops compared to the typical rate of change. Best for detecting spikes, crashes, or rapid transitions.": "Assinala subidas ou descidas invulgarmente rápidas em comparação com a taxa de variação típica. É ideal para detetar picos, quedas ou transições rápidas.", + "Uses the interquartile range to flag values far outside the normal spread of data. Robust against outliers that skew averages.": "Usa o intervalo interquartil para assinalar valores muito fora da dispersão normal dos dados. É robusto contra valores atípicos que distorcem as médias.", + "Compares each value to a rolling mean and standard deviation. Catches unusual readings relative to recent context rather than the whole series.": "Compara cada valor com uma média móvel e um desvio padrão. Deteta leituras invulgares em relação ao contexto recente, e não à série completa.", + "Flags when a sensor reports nearly the same value for an unusually long time. Useful for detecting stuck sensors or frozen readings.": "Assinala quando um sensor reporta quase o mesmo valor durante um período invulgarmente longo. É útil para detetar sensores bloqueados ou leituras congeladas.", + "Compares the current period to a reference date window. Highlights differences from an expected historical pattern, such as last week or the same day last year.": "Compara o período atual com uma janela de datas de referência. Destaca diferenças face a um padrão histórico esperado, como a semana passada ou o mesmo dia do ano anterior.", + "Show all anomalies": "Mostrar todas as anomalias", + "Overlaps only": "Apenas sobreposições", + "Computing…": "A calcular…", + "1 hour": "1 hora", + "3 hours": "3 horas", + "6 hours": "6 horas", + "12 hours": "12 horas", + "24 hours": "24 horas", + "7 days": "7 dias", + "30 minutes": "30 minutos" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-delta-group/i18n/pt.ts + var pt_exports$14 = /* @__PURE__ */ __exportAll({ translations: () => translations$38 }); + var translations$38; + var init_pt$14 = __esmMin((() => { + translations$38 = { + "Show delta vs selected date window": "Mostrar delta face à janela de datas selecionada", + "Select a date window tab to enable delta analysis.": "Selecione um separador de janela de datas para ativar a análise delta.", + "Show delta in tooltip": "Mostrar delta na dica", + "Show delta lines": "Mostrar linhas delta" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-rate-group/i18n/pt.ts + var pt_exports$13 = /* @__PURE__ */ __exportAll({ translations: () => translations$37 }); + var translations$37; + var init_pt$13 = __esmMin((() => { + translations$37 = { + "Show rate of change": "Mostrar taxa de variação", + "Show rate of change crosshairs": "Mostrar guias da taxa de variação", + "Rate window": "Janela da taxa", + "Point to point": "Ponto a ponto", + "1 hour": "1 hora", + "6 hours": "6 horas", + "24 hours": "24 horas" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-sample-group/i18n/pt.ts + var pt_exports$12 = /* @__PURE__ */ __exportAll({ translations: () => translations$36 }); + var translations$36; + var init_pt$12 = __esmMin((() => { + translations$36 = { + Downsampling: "Reamostragem", + Interval: "Intervalo", + Aggregate: "Agregado", + "Raw (no sampling)": "Em bruto (sem amostragem)", + "5 seconds": "5 segundos", + "10 seconds": "10 segundos", + "15 seconds": "15 segundos", + "30 seconds": "30 segundos", + "1 minute": "1 minuto", + "2 minutes": "2 minutos", + "5 minutes": "5 minutos", + "10 minutes": "10 minutos", + "15 minutes": "15 minutos", + "30 minutes": "30 minutos", + "1 hour": "1 hora", + "2 hours": "2 horas", + "3 hours": "3 horas", + "4 hours": "4 horas", + "6 hours": "6 horas", + "12 hours": "12 horas", + "24 hours": "24 horas", + "Mean (average)": "Média", + Min: "Mín.", + Max: "Máx.", + Median: "Mediana", + First: "Primeiro", + Last: "Último" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-summary-group/i18n/pt.ts + var pt_exports$11 = /* @__PURE__ */ __exportAll({ translations: () => translations$35 }); + var translations$35; + var init_pt$11 = __esmMin((() => { + translations$35 = { + "Show min / max / mean": "Mostrar mín. / máx. / média", + "Show range shading": "Mostrar sombreamento do intervalo" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-threshold-group/i18n/pt.ts + var pt_exports$10 = /* @__PURE__ */ __exportAll({ translations: () => translations$34 }); + var translations$34; + var init_pt$10 = __esmMin((() => { + translations$34 = { + "Show threshold analysis": "Mostrar análise de limiar", + "Shade threshold area": "Sombrear área do limiar", + Threshold: "Limiar", + "Shade area": "Sombrear área", + "Shade above": "Sombrear acima", + "Shade below": "Sombrear abaixo" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-trend-group/i18n/pt.ts + var pt_exports$9 = /* @__PURE__ */ __exportAll({ translations: () => translations$33 }); + var translations$33; + var init_pt$9 = __esmMin((() => { + translations$33 = { + "Show trend lines": "Mostrar linhas de tendência", + "Show trend crosshairs": "Mostrar guias da tendência", + "Trend method": "Método de tendência", + "Trend window": "Janela de tendência", + "Rolling average": "Média móvel", + "Linear trend": "Tendência linear", + "1 hour": "1 hora", + "6 hours": "6 horas", + "24 hours": "24 horas", + "7 days": "7 dias", + "14 days": "14 dias", + "21 days": "21 dias", + "28 days": "28 dias" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/comparison-tab-rail/i18n/pt.ts + var pt_exports$8 = /* @__PURE__ */ __exportAll({ translations: () => translations$32 }); + var translations$32; + var init_pt$8 = __esmMin((() => { + translations$32 = { "Add date window": "Adicionar janela de datas" }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/date-window-dialog/i18n/pt.ts + var pt_exports$7 = /* @__PURE__ */ __exportAll({ translations: () => translations$31 }); + var translations$31; + var init_pt$7 = __esmMin((() => { + translations$31 = { + "A date window saves a named date range as a tab, so you can quickly preview it against the selected range or jump the chart back to it later.": "Uma janela de datas guarda um intervalo de datas com nome como separador, para que possa pré-visualizá-lo rapidamente face ao intervalo selecionado ou regressar mais tarde a ele no gráfico.", + Name: "Nome", + "e.g. Heating season start": "ex.: início da época de aquecimento", + "Date range": "Intervalo de datas", + Start: "Início", + End: "Fim", + "Use previous range": "Usar intervalo anterior", + "Use next range": "Usar intervalo seguinte", + "Delete date window": "Eliminar janela de datas", + Cancel: "Cancelar" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/sidebar-options/sections/i18n/pt.ts + var pt_exports$6 = /* @__PURE__ */ __exportAll({ translations: () => translations$30 }); + var translations$30; + var init_pt$6 = __esmMin((() => { + translations$30 = { + Datapoints: "Pontos de dados", + "Choose which annotation datapoints appear on the chart.": "Escolha quais os pontos de dados de anotação que aparecem no gráfico.", + "Linked to selected targets": "Ligados aos alvos selecionados", + "All datapoints": "Todos os pontos de dados", + "Hide datapoints": "Ocultar pontos de dados" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/target-row/i18n/pt.ts + var pt_exports$5 = /* @__PURE__ */ __exportAll({ translations: () => translations$29 }); + var translations$29; + var init_pt$5 = __esmMin((() => { + translations$29 = { + "Analysis configured": "Análise configurada", + "Configure analysis": "Configurar análise", + "Stepped series": "Série em degraus", + "Hide source series": "Ocultar série de origem", + "All targets already have the same settings": "Todos os alvos já têm as mesmas definições", + "Copy these analysis settings to all targets": "Copiar estas definições de análise para todos os alvos", + "Copy to all targets": "Copiar para todos os alvos" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/target-row-list/i18n/pt.ts + var pt_exports$4 = /* @__PURE__ */ __exportAll({ translations: () => translations$28 }); + var translations$28; + var init_pt$4 = __esmMin((() => { + translations$28 = {}; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/history-targets/i18n/pt.ts + var pt_exports$3 = /* @__PURE__ */ __exportAll({ translations: () => translations$27 }); + var translations$27; + var init_pt$3 = __esmMin((() => { + translations$27 = { + Targets: "Alvos", + "Each row controls one chart series.": "Cada linha controla uma série do gráfico.", + "Add target": "Adicionar alvo", + "Chart preferences": "Preferências do gráfico" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/panel-shell/i18n/pt.ts + var pt_exports$2 = /* @__PURE__ */ __exportAll({ translations: () => translations$26 }); + var translations$26; + var init_pt$2 = __esmMin((() => { + translations$26 = { + "Loading Datapoints…": "A carregar pontos de dados…", + Datapoints: "Pontos de dados", + "Page options": "Opções da página", + "Download spreadsheet": "Transferir folha de cálculo", + "Save page state": "Guardar estado da página", + "Restore saved page": "Restaurar página guardada", + "Clear saved page": "Limpar página guardada", + "Expand targets sidebar": "Expandir barra lateral dos alvos", + "Collapse targets sidebar": "Recolher barra lateral dos alvos" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/range-toolbar/i18n/pt.ts + var pt_exports$1 = /* @__PURE__ */ __exportAll({ translations: () => translations$25 }); + var translations$25; + var init_pt$1 = __esmMin((() => { + translations$25 = { + "Toggle sidebar": "Alternar barra lateral", + Start: "Início", + End: "Fim", + "Select date range": "Selecionar intervalo de datas", + "Timeline options": "Opções da linha temporal", + "Zoom level": "Nível de zoom", + "Date snapping": "Ajuste de datas", + Auto: "Auto", + Hour: "Hora", + Day: "Dia", + Week: "Semana", + Month: "Mês", + Minute: "Minuto", + Second: "Segundo", + Quarterly: "Trimestral", + "Month Compressed": "Mês comprimido", + "Month Short": "Mês curto", + "Month Expanded": "Mês expandido", + "Week Compressed": "Semana comprimida", + "Week Expanded": "Semana expandida" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/i18n/locales/pt.ts + var pt_exports = /* @__PURE__ */ __exportAll({ templates: () => templates$1 }); + var modules$1, merged$1, templates$1; + var init_pt = __esmMin((() => { + init_pt$25(); + init_pt$24(); + init_pt$23(); + init_pt$22(); + init_pt$21(); + init_pt$20(); + init_pt$19(); + init_pt$18(); + init_pt$17(); + init_pt$16(); + init_pt$15(); + init_pt$14(); + init_pt$13(); + init_pt$12(); + init_pt$11(); + init_pt$10(); + init_pt$9(); + init_pt$8(); + init_pt$7(); + init_pt$6(); + init_pt$5(); + init_pt$4(); + init_pt$3(); + init_pt$2(); + init_pt$1(); + modules$1 = /* @__PURE__ */ Object.assign({ + "../../../atoms/interactive/range-timeline/i18n/pt.ts": pt_exports$25, + "../../../cards/action/i18n/pt.ts": pt_exports$24, + "../../../cards/history/history-chart/i18n/pt.ts": pt_exports$23, + "../../../cards/history/i18n/pt.ts": pt_exports$22, + "../../../cards/list/i18n/pt.ts": pt_exports$21, + "../../../cards/quick/i18n/pt.ts": pt_exports$20, + "../../../cards/sensor/i18n/pt.ts": pt_exports$19, + "../../chart/i18n/pt.ts": pt_exports$18, + "../../ha/i18n/pt.ts": pt_exports$17, + "../../timeline/i18n/pt.ts": pt_exports$16, + "../../../molecules/analysis-anomaly-group/i18n/pt.ts": pt_exports$15, + "../../../molecules/analysis-delta-group/i18n/pt.ts": pt_exports$14, + "../../../molecules/analysis-rate-group/i18n/pt.ts": pt_exports$13, + "../../../molecules/analysis-sample-group/i18n/pt.ts": pt_exports$12, + "../../../molecules/analysis-summary-group/i18n/pt.ts": pt_exports$11, + "../../../molecules/analysis-threshold-group/i18n/pt.ts": pt_exports$10, + "../../../molecules/analysis-trend-group/i18n/pt.ts": pt_exports$9, + "../../../molecules/comparison-tab-rail/i18n/pt.ts": pt_exports$8, + "../../../molecules/date-window-dialog/i18n/pt.ts": pt_exports$7, + "../../../molecules/sidebar-options/sections/i18n/pt.ts": pt_exports$6, + "../../../molecules/target-row/i18n/pt.ts": pt_exports$5, + "../../../molecules/target-row-list/i18n/pt.ts": pt_exports$4, + "../../../panels/datapoints/components/history-targets/i18n/pt.ts": pt_exports$3, + "../../../panels/datapoints/components/panel-shell/i18n/pt.ts": pt_exports$2, + "../../../panels/datapoints/components/range-toolbar/i18n/pt.ts": pt_exports$1 + }); + merged$1 = {}; + for (const mod of Object.values(modules$1)) Object.assign(merged$1, mod.translations); + templates$1 = merged$1; + })); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/range-timeline/i18n/zh-hans.ts + var zh_hans_exports$25 = /* @__PURE__ */ __exportAll({ translations: () => translations$24 }); + var translations$24; + var init_zh_hans$25 = __esmMin((() => { + translations$24 = { + "Updates with new data": "使用新数据更新", + "Scroll to selected range": "滚动到选定范围", + "Start date and time": "开始日期和时间", + "End date and time": "结束日期和时间", + Select: "选择" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/action/i18n/zh-hans.ts + var zh_hans_exports$24 = /* @__PURE__ */ __exportAll({ translations: () => translations$23 }); + var translations$23; + var init_zh_hans$24 = __esmMin((() => { + translations$23 = { + General: "常规", + "Related items": "关联项", + "Datapoint Appearance": "数据点外观", + "Form fields": "表单字段", + "Card title (optional)": "卡片标题(可选)", + "Pre-fill entities, devices, areas or labels that are always linked to recordings from this card.": "预先填写始终与此卡片创建的记录关联的实体、设备、区域或标签。", + "Show always included targets on card": "在卡片上显示始终包含的目标", + "Allow user to add more related items": "允许用户添加更多关联项", + "Default icon": "默认图标", + "Default colour": "默认颜色", + "Show date & time field": "显示日期和时间字段", + "Show annotation field": "显示注释字段" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/history-chart/i18n/zh-hans.ts + var zh_hans_exports$23 = /* @__PURE__ */ __exportAll({ translations: () => translations$22 }); + var translations$22; + var init_zh_hans$23 = __esmMin((() => { + translations$22 = { + "Date window:": "日期窗口:", + "Actual:": "实际:" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/i18n/zh-hans.ts + var zh_hans_exports$22 = /* @__PURE__ */ __exportAll({ translations: () => translations$21 }); + var translations$21; + var init_zh_hans$22 = __esmMin((() => { + translations$21 = { + General: "常规", + Entity: "实体", + "Multiple entities": "多个实体", + Display: "显示", + "Card title (optional)": "卡片标题(可选)", + "Hours to show": "显示小时数", + "Single entity": "单个实体", + "Show data gaps": "显示数据缺口", + "Highlight missing data ranges with dashed lines and boundary markers": "用虚线和边界标记突出显示缺失数据范围" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/list/i18n/zh-hans.ts + var zh_hans_exports$21 = /* @__PURE__ */ __exportAll({ translations: () => translations$20 }); + var translations$20; + var init_zh_hans$21 = __esmMin((() => { + translations$20 = { + "Search datapoints…": "搜索数据点…", + "Delete record": "删除记录", + Delete: "删除", + "Show annotation": "显示注释", + "Open related data point history": "打开相关数据点历史", + "Edit record": "编辑记录", + "Show chart marker": "显示图表标记", + "Hide chart marker": "隐藏图表标记", + "Choose colour": "选择颜色", + Save: "保存", + Cancel: "取消", + Message: "消息", + "Annotation / full message": "注释 / 完整消息" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/quick/i18n/zh-hans.ts + var zh_hans_exports$20 = /* @__PURE__ */ __exportAll({ translations: () => translations$19 }); + var translations$19; + var init_zh_hans$20 = __esmMin((() => { + translations$19 = { + General: "常规", + "Icon & colour": "图标和颜色", + "Related items": "关联项", + "Multiple entities": "多个实体", + "Form fields": "表单字段", + "Card title (optional)": "卡片标题(可选)", + "Input placeholder text": "输入占位文本", + Icon: "图标", + Colour: "颜色", + "These items will be linked to every record made with this card.": "这些项目将关联到使用此卡片创建的每一条记录。", + "Single entity (optional)": "单个实体(可选)", + "Add related items": "添加关联项", + "Show annotation field": "显示注释字段" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/i18n/zh-hans.ts + var zh_hans_exports$19 = /* @__PURE__ */ __exportAll({ translations: () => translations$18 }); + var translations$18; + var init_zh_hans$19 = __esmMin((() => { + translations$18 = { + Entity: "实体", + Display: "显示", + "Records list": "记录列表", + "Sensor entity *": "传感器实体 *", + "Override display name (optional)": "覆盖显示名称(可选)", + "Hours to show": "显示小时数", + "Graph colour": "图表颜色", + "Annotation style": "注释样式", + "Circle on line": "线上的圆点", + "Dotted vertical line": "点状垂直线", + "Show records list below graph": "在图表下方显示记录列表", + "Records per page (blank = show all)": "每页记录数(留空 = 显示全部)", + "Max records to show (blank = all)": "显示的最大记录数(留空 = 全部)", + "Show full message": "显示完整消息", + "User will be able to expand the row if hidden": "如果该行被隐藏,用户仍可展开它" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/chart/i18n/zh-hans.ts + var zh_hans_exports$18 = /* @__PURE__ */ __exportAll({ translations: () => translations$17 }); + var translations$17; + var init_zh_hans$18 = __esmMin((() => { + translations$17 = { + "⚠️ Anomaly Insight": "⚠️ 异常洞察", + "⚠️ Multi-method Anomaly": "⚠️ 多方法异常", + "Click the highlighted circle to add an annotation.": "点击高亮圆圈以添加注释。", + "Alert:": "警报:", + "Confirmed by": "确认方式", + "methods:": "方法:", + "Trend deviation": "趋势偏差", + "Sudden change": "突变", + "Statistical outlier (IQR)": "统计离群值(IQR)", + "Rolling Z-score": "滚动 Z 分数", + "Flat-line / stuck": "平直 / 卡住", + "Comparison window": "比较窗口", + "{0} deviates from its expected trend between {1} and {2}.": "{0} 在 {1} 到 {2} 之间偏离了预期趋势。", + "{0} shows an unusual rate of change between {1} and {2}.": "{0} 在 {1} 到 {2} 之间表现出异常的变化率。", + "{0} contains statistical outliers between {1} and {2}.": "{0} 在 {1} 到 {2} 之间包含统计离群值。", + "{0} shows statistically unusual values between {1} and {2}.": "{0} 在 {1} 到 {2} 之间显示出统计上异常的值。", + "{0} appears stuck or flat between {1} and {2}{3}.": "{0} 在 {1} 到 {2}{3} 之间似乎卡住或保持平直。", + "{0} deviates significantly from the comparison window between {1} and {2}.": "{0} 在 {1} 到 {2} 之间显著偏离比较窗口。", + "Peak deviation: {0} from a baseline of {1} at {2}.": "峰值偏差:{2} 时相对基线 {1} 偏差 {0}。", + "Peak rate deviation: {0} from a typical rate of {1} at {2}.": "峰值速率偏差:{2} 时相对典型速率 {1} 偏差 {0}。", + "Peak value: {0}, deviating {1} from the median at {2}.": "峰值:{0},在 {2} 时偏离中位数 {1}。", + "Peak deviation: {0} from a rolling mean of {1} at {2}.": "峰值偏差:{2} 时相对滚动平均值 {1} 偏差 {0}。", + "Value remained near {0} for an unusually long period.": "该值在异常长的时间内一直接近 {0}。", + "Peak deviation from comparison: {0} at {1}.": "与比较值的峰值偏差:{1} 时为 {0}。", + " (range: {0})": "(范围:{0})", + "Date window": "日期窗口", + Trend: "趋势", + Rate: "变化率", + Delta: "差值", + Threshold: "阈值" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/ha/i18n/zh-hans.ts + var zh_hans_exports$17 = /* @__PURE__ */ __exportAll({ translations: () => translations$16 }); + var translations$16; + var init_zh_hans$17 = __esmMin((() => { + translations$16 = { + "Confirm delete": "确认删除", + "Are you sure you want to delete this item?": "确定要删除此项目吗?", + Cancel: "取消", + Delete: "删除", + "Delete date window": "删除日期窗口", + "this date window": "此日期窗口", + "Edit date window": "编辑日期窗口", + "Add date window": "添加日期窗口", + "Save date window": "保存日期窗口", + "Create date window": "创建日期窗口" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/timeline/i18n/zh-hans.ts + var zh_hans_exports$16 = /* @__PURE__ */ __exportAll({ translations: () => translations$15 }); + var translations$15; + var init_zh_hans$16 = __esmMin((() => { + translations$15 = { + Wk: "周", + "Week of": "所在周" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-anomaly-group/i18n/zh-hans.ts + var zh_hans_exports$15 = /* @__PURE__ */ __exportAll({ translations: () => translations$14 }); + var translations$14; + var init_zh_hans$15 = __esmMin((() => { + translations$14 = { + "Show anomalies": "显示异常", + Sensitivity: "灵敏度", + "Use downsampled data for detection": "检测时使用降采样数据", + "Rate window": "变化率窗口", + "Rolling window": "滚动窗口", + "Min flat duration": "最小平稳持续时间", + "Compare to window": "与窗口比较", + "— select window —": "— 选择窗口 —", + "When methods overlap": "当方法重叠时", + Low: "低", + Medium: "中", + High: "高", + "Trend deviation": "趋势偏差", + "Sudden change": "突变", + "Statistical outlier (IQR)": "统计离群值(IQR)", + "Rolling Z-score": "滚动 Z 分数", + "Flat-line / stuck value": "平直 / 卡住值", + "Comparison window deviation": "比较窗口偏差", + "Flags points that deviate significantly from a fitted trend line. Good for catching gradual drift or sudden jumps away from a steady baseline.": "标记显著偏离拟合趋势线的点。适合捕捉从稳定基线逐渐漂移或突然跳变的情况。", + "Flags unusually fast rises or drops compared to the typical rate of change. Best for detecting spikes, crashes, or rapid transitions.": "标记相对于典型变化率异常快速的上升或下降。最适合检测尖峰、崩跌或快速变化。", + "Uses the interquartile range to flag values far outside the normal spread of data. Robust against outliers that skew averages.": "使用四分位距来标记远超正常数据分布范围的值。对会扭曲平均值的离群值具有较强鲁棒性。", + "Compares each value to a rolling mean and standard deviation. Catches unusual readings relative to recent context rather than the whole series.": "将每个值与滚动平均值和标准差进行比较。它根据近期上下文而不是整条序列来捕捉异常读数。", + "Flags when a sensor reports nearly the same value for an unusually long time. Useful for detecting stuck sensors or frozen readings.": "当传感器在异常长时间内报告几乎相同的值时进行标记。适用于检测卡住的传感器或冻结的读数。", + "Compares the current period to a reference date window. Highlights differences from an expected historical pattern, such as last week or the same day last year.": "将当前时间段与参考日期窗口进行比较。突出显示与预期历史模式的差异,例如上周或去年的同一天。", + "Show all anomalies": "显示所有异常", + "Overlaps only": "仅重叠项", + "Computing…": "计算中…", + "1 hour": "1小时", + "3 hours": "3小时", + "6 hours": "6小时", + "12 hours": "12小时", + "24 hours": "24小时", + "7 days": "7天", + "30 minutes": "30分钟" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-delta-group/i18n/zh-hans.ts + var zh_hans_exports$14 = /* @__PURE__ */ __exportAll({ translations: () => translations$13 }); + var translations$13; + var init_zh_hans$14 = __esmMin((() => { + translations$13 = { + "Show delta vs selected date window": "显示相对于所选日期窗口的差值", + "Select a date window tab to enable delta analysis.": "选择一个日期窗口标签以启用差值分析。", + "Show delta in tooltip": "在提示中显示差值", + "Show delta lines": "显示差值线" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-rate-group/i18n/zh-hans.ts + var zh_hans_exports$13 = /* @__PURE__ */ __exportAll({ translations: () => translations$12 }); + var translations$12; + var init_zh_hans$13 = __esmMin((() => { + translations$12 = { + "Show rate of change": "显示变化率", + "Show rate of change crosshairs": "显示变化率准星", + "Rate window": "变化率窗口", + "Point to point": "点对点", + "1 hour": "1小时", + "6 hours": "6小时", + "24 hours": "24小时" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-sample-group/i18n/zh-hans.ts + var zh_hans_exports$12 = /* @__PURE__ */ __exportAll({ translations: () => translations$11 }); + var translations$11; + var init_zh_hans$12 = __esmMin((() => { + translations$11 = { + Downsampling: "降采样", + Interval: "间隔", + Aggregate: "聚合", + "Raw (no sampling)": "原始(无采样)", + "5 seconds": "5秒", + "10 seconds": "10秒", + "15 seconds": "15秒", + "30 seconds": "30秒", + "1 minute": "1分钟", + "2 minutes": "2分钟", + "5 minutes": "5分钟", + "10 minutes": "10分钟", + "15 minutes": "15分钟", + "30 minutes": "30分钟", + "1 hour": "1小时", + "2 hours": "2小时", + "3 hours": "3小时", + "4 hours": "4小时", + "6 hours": "6小时", + "12 hours": "12小时", + "24 hours": "24小时", + "Mean (average)": "平均值", + Min: "最小", + Max: "最大", + Median: "中位数", + First: "首个", + Last: "最后一个" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-summary-group/i18n/zh-hans.ts + var zh_hans_exports$11 = /* @__PURE__ */ __exportAll({ translations: () => translations$10 }); + var translations$10; + var init_zh_hans$11 = __esmMin((() => { + translations$10 = { + "Show min / max / mean": "显示最小 / 最大 / 平均值", + "Show range shading": "显示范围阴影" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-threshold-group/i18n/zh-hans.ts + var zh_hans_exports$10 = /* @__PURE__ */ __exportAll({ translations: () => translations$9 }); + var translations$9; + var init_zh_hans$10 = __esmMin((() => { + translations$9 = { + "Show threshold analysis": "显示阈值分析", + "Shade threshold area": "为阈值区域着色", + Threshold: "阈值", + "Shade area": "着色区域", + "Shade above": "对上方区域着色", + "Shade below": "对下方区域着色" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-trend-group/i18n/zh-hans.ts + var zh_hans_exports$9 = /* @__PURE__ */ __exportAll({ translations: () => translations$8 }); + var translations$8; + var init_zh_hans$9 = __esmMin((() => { + translations$8 = { + "Show trend lines": "显示趋势线", + "Show trend crosshairs": "显示趋势准星", + "Trend method": "趋势方法", + "Trend window": "趋势窗口", + "Rolling average": "滚动平均", + "Linear trend": "线性趋势", + "1 hour": "1小时", + "6 hours": "6小时", + "24 hours": "24小时", + "7 days": "7天", + "14 days": "14天", + "21 days": "21天", + "28 days": "28天" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/comparison-tab-rail/i18n/zh-hans.ts + var zh_hans_exports$8 = /* @__PURE__ */ __exportAll({ translations: () => translations$7 }); + var translations$7; + var init_zh_hans$8 = __esmMin((() => { + translations$7 = { "Add date window": "添加日期窗口" }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/date-window-dialog/i18n/zh-hans.ts + var zh_hans_exports$7 = /* @__PURE__ */ __exportAll({ translations: () => translations$6 }); + var translations$6; + var init_zh_hans$7 = __esmMin((() => { + translations$6 = { + "A date window saves a named date range as a tab, so you can quickly preview it against the selected range or jump the chart back to it later.": "日期窗口会将一个命名的日期范围保存为标签页,这样你就可以快速将其与所选范围进行预览对比,或稍后在图表中跳回该范围。", + Name: "名称", + "e.g. Heating season start": "例如:供暖季开始", + "Date range": "日期范围", + Start: "开始", + End: "结束", + "Use previous range": "使用上一个范围", + "Use next range": "使用下一个范围", + "Delete date window": "删除日期窗口", + Cancel: "取消" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/sidebar-options/sections/i18n/zh-hans.ts + var zh_hans_exports$6 = /* @__PURE__ */ __exportAll({ translations: () => translations$5 }); + var translations$5; + var init_zh_hans$6 = __esmMin((() => { + translations$5 = { + Datapoints: "数据点", + "Choose which annotation datapoints appear on the chart.": "选择哪些注释数据点显示在图表上。", + "Linked to selected targets": "关联到所选目标", + "All datapoints": "所有数据点", + "Hide datapoints": "隐藏数据点" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/target-row/i18n/zh-hans.ts + var zh_hans_exports$5 = /* @__PURE__ */ __exportAll({ translations: () => translations$4 }); + var translations$4; + var init_zh_hans$5 = __esmMin((() => { + translations$4 = { + "Analysis configured": "分析已配置", + "Configure analysis": "配置分析", + "Stepped series": "阶梯序列", + "Hide source series": "隐藏源序列", + "All targets already have the same settings": "所有目标已经具有相同设置", + "Copy these analysis settings to all targets": "将这些分析设置复制到所有目标", + "Copy to all targets": "复制到所有目标" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/target-row-list/i18n/zh-hans.ts + var zh_hans_exports$4 = /* @__PURE__ */ __exportAll({ translations: () => translations$3 }); + var translations$3; + var init_zh_hans$4 = __esmMin((() => { + translations$3 = {}; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/history-targets/i18n/zh-hans.ts + var zh_hans_exports$3 = /* @__PURE__ */ __exportAll({ translations: () => translations$2 }); + var translations$2; + var init_zh_hans$3 = __esmMin((() => { + translations$2 = { + Targets: "目标", + "Each row controls one chart series.": "每一行控制一条图表序列。", + "Add target": "添加目标", + "Chart preferences": "图表偏好设置" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/panel-shell/i18n/zh-hans.ts + var zh_hans_exports$2 = /* @__PURE__ */ __exportAll({ translations: () => translations$1 }); + var translations$1; + var init_zh_hans$2 = __esmMin((() => { + translations$1 = { + "Loading Datapoints…": "正在加载数据点…", + Datapoints: "数据点", + "Page options": "页面选项", + "Download spreadsheet": "下载电子表格", + "Save page state": "保存页面状态", + "Restore saved page": "恢复已保存页面", + "Clear saved page": "清除已保存页面", + "Expand targets sidebar": "展开目标侧边栏", + "Collapse targets sidebar": "折叠目标侧边栏" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/range-toolbar/i18n/zh-hans.ts + var zh_hans_exports$1 = /* @__PURE__ */ __exportAll({ translations: () => translations }); + var translations; + var init_zh_hans$1 = __esmMin((() => { + translations = { + "Toggle sidebar": "切换侧边栏", + Start: "开始", + End: "结束", + "Select date range": "选择日期范围", + "Timeline options": "时间线选项", + "Zoom level": "缩放级别", + "Date snapping": "日期对齐", + Auto: "自动", + Hour: "小时", + Day: "日", + Week: "周", + Month: "月", + Minute: "分钟", + Second: "秒", + Quarterly: "按季度", + "Month Compressed": "月份紧凑", + "Month Short": "月份简写", + "Month Expanded": "月份展开", + "Week Compressed": "周紧凑", + "Week Expanded": "周展开" + }; + })); + //#endregion + //#region custom_components/hass_datapoints/src/lib/i18n/locales/zh-hans.ts + var zh_hans_exports = /* @__PURE__ */ __exportAll({ templates: () => templates }); + var modules, merged, templates; + var init_zh_hans = __esmMin((() => { + init_zh_hans$25(); + init_zh_hans$24(); + init_zh_hans$23(); + init_zh_hans$22(); + init_zh_hans$21(); + init_zh_hans$20(); + init_zh_hans$19(); + init_zh_hans$18(); + init_zh_hans$17(); + init_zh_hans$16(); + init_zh_hans$15(); + init_zh_hans$14(); + init_zh_hans$13(); + init_zh_hans$12(); + init_zh_hans$11(); + init_zh_hans$10(); + init_zh_hans$9(); + init_zh_hans$8(); + init_zh_hans$7(); + init_zh_hans$6(); + init_zh_hans$5(); + init_zh_hans$4(); + init_zh_hans$3(); + init_zh_hans$2(); + init_zh_hans$1(); + modules = /* @__PURE__ */ Object.assign({ + "../../../atoms/interactive/range-timeline/i18n/zh-hans.ts": zh_hans_exports$25, + "../../../cards/action/i18n/zh-hans.ts": zh_hans_exports$24, + "../../../cards/history/history-chart/i18n/zh-hans.ts": zh_hans_exports$23, + "../../../cards/history/i18n/zh-hans.ts": zh_hans_exports$22, + "../../../cards/list/i18n/zh-hans.ts": zh_hans_exports$21, + "../../../cards/quick/i18n/zh-hans.ts": zh_hans_exports$20, + "../../../cards/sensor/i18n/zh-hans.ts": zh_hans_exports$19, + "../../chart/i18n/zh-hans.ts": zh_hans_exports$18, + "../../ha/i18n/zh-hans.ts": zh_hans_exports$17, + "../../timeline/i18n/zh-hans.ts": zh_hans_exports$16, + "../../../molecules/analysis-anomaly-group/i18n/zh-hans.ts": zh_hans_exports$15, + "../../../molecules/analysis-delta-group/i18n/zh-hans.ts": zh_hans_exports$14, + "../../../molecules/analysis-rate-group/i18n/zh-hans.ts": zh_hans_exports$13, + "../../../molecules/analysis-sample-group/i18n/zh-hans.ts": zh_hans_exports$12, + "../../../molecules/analysis-summary-group/i18n/zh-hans.ts": zh_hans_exports$11, + "../../../molecules/analysis-threshold-group/i18n/zh-hans.ts": zh_hans_exports$10, + "../../../molecules/analysis-trend-group/i18n/zh-hans.ts": zh_hans_exports$9, + "../../../molecules/comparison-tab-rail/i18n/zh-hans.ts": zh_hans_exports$8, + "../../../molecules/date-window-dialog/i18n/zh-hans.ts": zh_hans_exports$7, + "../../../molecules/sidebar-options/sections/i18n/zh-hans.ts": zh_hans_exports$6, + "../../../molecules/target-row/i18n/zh-hans.ts": zh_hans_exports$5, + "../../../molecules/target-row-list/i18n/zh-hans.ts": zh_hans_exports$4, + "../../../panels/datapoints/components/history-targets/i18n/zh-hans.ts": zh_hans_exports$3, + "../../../panels/datapoints/components/panel-shell/i18n/zh-hans.ts": zh_hans_exports$2, + "../../../panels/datapoints/components/range-toolbar/i18n/zh-hans.ts": zh_hans_exports$1 + }); + merged = {}; + for (const mod of Object.values(modules)) Object.assign(merged, mod.translations); + templates = merged; + })); + var { getLocale, setLocale } = configureLocalization({ + sourceLocale: "en", + targetLocales: supported_locales_default, + loadLocale: (locale) => { + if (locale === "fi") return Promise.resolve().then(() => (init_fi(), fi_exports)); + if (locale === "fr") return Promise.resolve().then(() => (init_fr(), fr_exports)); + if (locale === "de") return Promise.resolve().then(() => (init_de(), de_exports)); + if (locale === "es") return Promise.resolve().then(() => (init_es(), es_exports)); + if (locale === "pt") return Promise.resolve().then(() => (init_pt(), pt_exports)); + if (locale === "zh-Hans") return Promise.resolve().then(() => (init_zh_hans(), zh_hans_exports)); + throw new Error(`Unsupported locale "${locale}"`); + } + }); + function normalizeLocale(locale) { + const normalizedLocale = (locale ?? "").trim().replaceAll("_", "-").toLowerCase(); + if (!normalizedLocale) return "en"; + if (normalizedLocale === "fi" || normalizedLocale.startsWith("fi-")) return "fi"; + if (normalizedLocale === "fr" || normalizedLocale.startsWith("fr-")) return "fr"; + if (normalizedLocale === "de" || normalizedLocale.startsWith("de-")) return "de"; + if (normalizedLocale === "es" || normalizedLocale.startsWith("es-")) return "es"; + if (normalizedLocale === "pt" || normalizedLocale.startsWith("pt-")) return "pt"; + if (normalizedLocale === "zh" || normalizedLocale.startsWith("zh-") || normalizedLocale.startsWith("cmn-")) return "zh-Hans"; + return "en"; + } + async function setFrontendLocale(locale) { + const nextLocale = normalizeLocale(locale); + if (getLocale() !== nextLocale) await setLocale(nextLocale); + return nextLocale; + } + async function syncFrontendLocale(hass) { + return setFrontendLocale(hass?.locale?.language ?? hass?.language); + } + /** + * Wrapper around `@lit/localize`'s `msg` that automatically uses the source + * string as the translation id, so call sites don't need to repeat it. + * + * Instead of: msg("Hello", { id: "Hello" }) + * Write: msg("Hello") + */ + function msg(str, opts) { + return msg$1(str, { + ...opts, + id: opts?.id ?? str + }); + } + //#endregion + //#region custom_components/hass_datapoints/src/cards/action/editor.styles.ts + var styles$64 = i$5` .note { font-size: 0.78rem; color: var(--secondary-text-color); } `; - const styles$$ = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/molecules/editor-base/editor-base.styles.ts + var styles$63 = i$5` :host { display: block; } @@ -1823,193 +5187,120 @@ padding: 4px 0 8px; } `; - var __create$Q = Object.create; - var __defProp$15 = Object.defineProperty; - var __getOwnPropDesc$Q = Object.getOwnPropertyDescriptor; - var __knownSymbol$Q = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$Q = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$15 = (obj, key, value) => key in obj ? __defProp$15(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __name$k = (target, value) => __defProp$15(target, "name", { value, configurable: true }); - var __decoratorStart$Q = (base) => [, , , __create$Q(base?.[__knownSymbol$Q("metadata")] ?? null)]; - var __decoratorStrings$Q = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$Q = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$Q("Function expected") : fn; - var __decoratorContext$Q = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$Q[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$Q("Already initialized") : fns.push(__expectFn$Q(fn || null)) }); - var __decoratorMetadata$Q = (array, target) => __defNormalProp$15(target, __knownSymbol$Q("metadata"), array[3]); - var __runInitializers$Q = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$Q = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = !!(flags & 8), p2 = !!(flags & 16); - var j2 = k2 > 3 ? array.length + 1 : k2 ? s2 ? 1 : 2 : 0, key = __decoratorStrings$Q[k2 + 5]; - var initializers = k2 > 3 && (array[j2 - 1] = []), extraInitializers = array[j2] || (array[j2] = []); - var desc = k2 && (!p2 && !s2 && (target = target.prototype), k2 < 5 && (k2 > 3 || !p2) && __getOwnPropDesc$Q(k2 < 4 ? target : { get [name]() { - return __privateGet$P(this, extra); - }, set [name](x2) { - return __privateSet$P(this, extra, x2); - } }, name)); - k2 ? p2 && k2 < 4 && __name$k(extra, (k2 > 2 ? "set " : k2 > 1 ? "get " : "") + name) : __name$k(target, name); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$Q(k2, name, done = {}, array[3], extraInitializers); - if (k2) { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: p2 ? (x2) => __privateIn$j(target, x2) : (x2) => name in x2 }; - if (k2 ^ 3) access.get = p2 ? (x2) => (k2 ^ 1 ? __privateGet$P : __privateMethod$j)(x2, target, k2 ^ 4 ? extra : desc.get) : (x2) => x2[name]; - if (k2 > 2) access.set = p2 ? (x2, y2) => __privateSet$P(x2, target, y2, k2 ^ 4 ? extra : desc.set) : (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])(k2 ? k2 < 4 ? p2 ? extra : desc[key] : k2 > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; - if (k2 ^ 4 || it === void 0) __expectFn$Q(it) && (k2 > 4 ? initializers.unshift(it) : k2 ? p2 ? extra = it : desc[key] = it : target = it); - else if (typeof it !== "object" || it === null) __typeError$Q("Object expected"); - else __expectFn$Q(fn = it.get) && (desc.get = fn), __expectFn$Q(fn = it.set) && (desc.set = fn), __expectFn$Q(fn = it.init) && initializers.unshift(fn); - } - return k2 || __decoratorMetadata$Q(array, target), desc && __defProp$15(target, name, desc), p2 ? k2 ^ 4 ? extra : desc : target; - }; - var __publicField$15 = (obj, key, value) => __defNormalProp$15(obj, key + "", value); - var __accessCheck$P = (obj, member, msg2) => member.has(obj) || __typeError$Q("Cannot " + msg2); - var __privateIn$j = (member, obj) => Object(obj) !== obj ? __typeError$Q('Cannot use the "in" operator on this value') : member.has(obj); - var __privateGet$P = (obj, member, getter) => (__accessCheck$P(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); - var __privateAdd$P = (obj, member, value) => member.has(obj) ? __typeError$Q("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$P = (obj, member, value, setter) => (__accessCheck$P(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); - var __privateMethod$j = (obj, member, method) => (__accessCheck$P(obj, member, "access private method"), method); - var _hass_dec$b, __config_dec, _a$Q, _EditorBase_decorators, _init$Q, __config, _hass$b; - _EditorBase_decorators = [localized()]; - class EditorBase extends (_a$Q = i$2, __config_dec = [r()], _hass_dec$b = [n({ type: Object })], _a$Q) { - constructor() { - super(...arguments); - __privateAdd$P(this, __config, __runInitializers$Q(_init$Q, 8, this, {})), __runInitializers$Q(_init$Q, 11, this); - __privateAdd$P(this, _hass$b, __runInitializers$Q(_init$Q, 12, this, null)), __runInitializers$Q(_init$Q, 15, this); - } - setConfig(config) { - this._config = { ...config }; - } - _fire(cfg) { - this.dispatchEvent( - new CustomEvent("config-changed", { - detail: { config: { ...cfg } }, - bubbles: true, - composed: true - }) - ); - } - _set(key, value) { - const cfg = { ...this._config }; - if (value === "" || value === null || value === void 0) { - delete cfg[key]; - } else { - cfg[key] = value; - } - this._config = cfg; - this._fire(cfg); - } - } - _init$Q = __decoratorStart$Q(_a$Q); - __config = /* @__PURE__ */ new WeakMap(); - _hass$b = /* @__PURE__ */ new WeakMap(); - __decorateElement$Q(_init$Q, 4, "_config", __config_dec, EditorBase, __config); - __decorateElement$Q(_init$Q, 4, "hass", _hass_dec$b, EditorBase, _hass$b); - EditorBase = __decorateElement$Q(_init$Q, 0, "EditorBase", _EditorBase_decorators, EditorBase); - __publicField$15(EditorBase, "styles", styles$$); - __runInitializers$Q(_init$Q, 1, EditorBase); - customElements.define("editor-base", EditorBase); - function normalizeEntityIds(value) { - if (!value) { - return []; - } - return (Array.isArray(value) ? value : [value]).map((item) => typeof item === "string" ? item.trim() : "").filter(Boolean); - } - function normalizeTargetValue(targetValue) { - if (!targetValue) { - return {}; - } - if (Array.isArray(targetValue)) { - return { entity_id: normalizeEntityIds(targetValue) }; - } - if (typeof targetValue === "string") { - return targetValue ? { entity_id: [targetValue] } : {}; - } - const normalized = { - entity_id: [ - ...normalizeEntityIds(targetValue.entity_id), - ...normalizeEntityIds(targetValue.entity_ids), - ...normalizeEntityIds(targetValue.entity), - ...normalizeEntityIds(targetValue.entities) - ], - device_id: normalizeEntityIds(targetValue.device_id), - area_id: normalizeEntityIds(targetValue.area_id), - label_id: normalizeEntityIds(targetValue.label_id) - }; - return Object.fromEntries( - Object.entries(normalized).filter(([, entries]) => entries.length) - ); - } - function normalizeTargetSelection(targetValue) { - const normalized = normalizeTargetValue(targetValue); - return { - entity_id: [...new Set(normalized.entity_id || [])], - device_id: [...new Set(normalized.device_id || [])], - area_id: [...new Set(normalized.area_id || [])], - label_id: [...new Set(normalized.label_id || [])] - }; - } - function mergeTargetSelections(...targets) { - const merged2 = { - entity_id: [], - device_id: [], - area_id: [], - label_id: [] - }; - for (const target of targets) { - const normalized = normalizeTargetSelection(target); - for (const key of Object.keys( - merged2 - )) { - merged2[key].push(...normalized[key]); - } - } - for (const key of Object.keys( - merged2 - )) { - merged2[key] = [...new Set(merged2[key])]; - } - return merged2; - } - function resolveEntityIdsFromTarget(hass, targetValue) { - const target = normalizeTargetSelection(targetValue); - const resolved = new Set(normalizeEntityIds(target.entity_id)); - const entityRegistry = hass?.entities || {}; - const selectedDevices = new Set(normalizeEntityIds(target.device_id)); - const selectedAreas = new Set(normalizeEntityIds(target.area_id)); - const selectedLabels = new Set(normalizeEntityIds(target.label_id)); - Object.entries(entityRegistry).forEach(([entityId, entry]) => { - const entityEntry = entry; - if (!entityEntry || typeof entityEntry !== "object") { - return; - } - const deviceId = entityEntry.device_id || entityEntry.deviceId || null; - const areaId = entityEntry.area_id || entityEntry.areaId || null; - const labels = [ - ...Array.isArray(entityEntry.labels) ? entityEntry.labels : [], - ...Array.isArray(entityEntry.label_ids) ? entityEntry.label_ids : [] - ]; - if (deviceId && selectedDevices.has(deviceId) || areaId && selectedAreas.has(areaId) || labels.some((labelId) => selectedLabels.has(labelId))) { - resolved.add(entityId); - } - }); - return [...resolved]; - } - function panelConfigTarget(panelCfg) { - if (!panelCfg) { - return {}; - } - if (panelCfg.target) { - return normalizeTargetValue(panelCfg.target); - } - return normalizeTargetValue({ - entity_id: panelCfg.entities?.length ? panelCfg.entities : panelCfg.entity - }); - } - const styles$_ = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/molecules/editor-base/editor-base.ts + var _EditorBase, _config_accessor_storage, _hass_accessor_storage$11; + var EditorBase = (_config_accessor_storage = /* @__PURE__ */ new WeakMap(), _hass_accessor_storage$11 = /* @__PURE__ */ new WeakMap(), _EditorBase = class EditorBase extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _config_accessor_storage, {}); + _classPrivateFieldInitSpec(this, _hass_accessor_storage$11, null); + } + get _config() { + return _classPrivateFieldGet2(_config_accessor_storage, this); + } + set _config(value) { + _classPrivateFieldSet2(_config_accessor_storage, this, value); + } + get hass() { + return _classPrivateFieldGet2(_hass_accessor_storage$11, this); + } + set hass(value) { + _classPrivateFieldSet2(_hass_accessor_storage$11, this, value); + } + setConfig(config) { + this._config = { ...config }; + } + _fire(cfg) { + this.dispatchEvent(new CustomEvent("config-changed", { + detail: { config: { ...cfg } }, + bubbles: true, + composed: true + })); + } + _set(key, value) { + const cfg = { ...this._config }; + if (value === "" || value === null || value === void 0) delete cfg[key]; + else cfg[key] = value; + this._config = cfg; + this._fire(cfg); + } + }, _defineProperty(_EditorBase, "styles", styles$63), _EditorBase); + __decorate([r$1()], EditorBase.prototype, "_config", null); + __decorate([n$1({ type: Object })], EditorBase.prototype, "hass", null); + EditorBase = __decorate([localized()], EditorBase); + customElements.define("editor-base", EditorBase); + //#endregion + //#region custom_components/hass_datapoints/src/lib/domain/target-selection.ts + function normalizeEntityIds(value) { + if (!value) return []; + return (Array.isArray(value) ? value : [value]).map((item) => typeof item === "string" ? item.trim() : "").filter(Boolean); + } + function normalizeTargetValue(targetValue) { + if (!targetValue) return {}; + if (Array.isArray(targetValue)) return { entity_id: normalizeEntityIds(targetValue) }; + if (typeof targetValue === "string") return targetValue ? { entity_id: [targetValue] } : {}; + const normalized = { + entity_id: [ + ...normalizeEntityIds(targetValue.entity_id), + ...normalizeEntityIds(targetValue.entity_ids), + ...normalizeEntityIds(targetValue.entity), + ...normalizeEntityIds(targetValue.entities) + ], + device_id: normalizeEntityIds(targetValue.device_id), + area_id: normalizeEntityIds(targetValue.area_id), + label_id: normalizeEntityIds(targetValue.label_id) + }; + return Object.fromEntries(Object.entries(normalized).filter(([, entries]) => entries.length)); + } + function normalizeTargetSelection(targetValue) { + const normalized = normalizeTargetValue(targetValue); + return { + entity_id: [...new Set(normalized.entity_id || [])], + device_id: [...new Set(normalized.device_id || [])], + area_id: [...new Set(normalized.area_id || [])], + label_id: [...new Set(normalized.label_id || [])] + }; + } + function mergeTargetSelections(...targets) { + const merged = { + entity_id: [], + device_id: [], + area_id: [], + label_id: [] + }; + for (const target of targets) { + const normalized = normalizeTargetSelection(target); + for (const key of Object.keys(merged)) merged[key].push(...normalized[key]); + } + for (const key of Object.keys(merged)) merged[key] = [...new Set(merged[key])]; + return merged; + } + function resolveEntityIdsFromTarget(hass, targetValue) { + const target = normalizeTargetSelection(targetValue); + const resolved = new Set(normalizeEntityIds(target.entity_id)); + const entityRegistry = hass?.entities || {}; + const selectedDevices = new Set(normalizeEntityIds(target.device_id)); + const selectedAreas = new Set(normalizeEntityIds(target.area_id)); + const selectedLabels = new Set(normalizeEntityIds(target.label_id)); + Object.entries(entityRegistry).forEach(([entityId, entry]) => { + const entityEntry = entry; + if (!entityEntry || typeof entityEntry !== "object") return; + const deviceId = entityEntry.device_id || entityEntry.deviceId || null; + const areaId = entityEntry.area_id || entityEntry.areaId || null; + const labels = [...Array.isArray(entityEntry.labels) ? entityEntry.labels : [], ...Array.isArray(entityEntry.label_ids) ? entityEntry.label_ids : []]; + if (deviceId && selectedDevices.has(deviceId) || areaId && selectedAreas.has(areaId) || labels.some((labelId) => selectedLabels.has(labelId))) resolved.add(entityId); + }); + return [...resolved]; + } + function panelConfigTarget(panelCfg) { + if (!panelCfg) return {}; + if (panelCfg.target) return normalizeTargetValue(panelCfg.target); + return normalizeTargetValue({ entity_id: panelCfg.entities?.length ? panelCfg.entities : panelCfg.entity }); + } + //#endregion + //#region custom_components/hass_datapoints/src/atoms/display/section-heading/section-heading.styles.ts + var styles$62 = i$5` :host { display: block; } @@ -2021,68 +5312,30 @@ color: var(--secondary-text-color); } `; - var __create$P = Object.create; - var __defProp$14 = Object.defineProperty; - var __getOwnPropDesc$P = Object.getOwnPropertyDescriptor; - var __knownSymbol$P = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$P = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$14 = (obj, key, value) => key in obj ? __defProp$14(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$P = (base) => [, , , __create$P(base?.[__knownSymbol$P("metadata")] ?? null)]; - var __decoratorStrings$P = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$P = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$P("Function expected") : fn; - var __decoratorContext$P = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$P[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$P("Already initialized") : fns.push(__expectFn$P(fn || null)) }); - var __decoratorMetadata$P = (array, target) => __defNormalProp$14(target, __knownSymbol$P("metadata"), array[3]); - var __runInitializers$P = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$P = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$P[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$P({ get [name]() { - return __privateGet$O(this, extra); - }, set [name](x2) { - return __privateSet$O(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$P(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$P(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$P("Object expected"); - else __expectFn$P(fn = it.get) && (desc.get = fn), __expectFn$P(fn = it.set) && (desc.set = fn), __expectFn$P(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$14(target, name, desc), target; - }; - var __publicField$14 = (obj, key, value) => __defNormalProp$14(obj, key + "", value); - var __accessCheck$O = (obj, member, msg2) => member.has(obj) || __typeError$P("Cannot " + msg2); - var __privateGet$O = (obj, member, getter) => (__accessCheck$O(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$O = (obj, member, value) => member.has(obj) ? __typeError$P("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$O = (obj, member, value, setter) => (__accessCheck$O(obj, member, "write to private field"), member.set(obj, value), value); - var _text_dec, _a$P, _init$P, _text; - class SectionHeading extends (_a$P = i$2, _text_dec = [n({ type: String })], _a$P) { - constructor() { - super(...arguments); - __privateAdd$O(this, _text, __runInitializers$P(_init$P, 8, this, "")), __runInitializers$P(_init$P, 11, this); - } - render() { - return b`
${this.text}
`; - } - } - _init$P = __decoratorStart$P(_a$P); - _text = /* @__PURE__ */ new WeakMap(); - __decorateElement$P(_init$P, 4, "text", _text_dec, SectionHeading, _text); - __decoratorMetadata$P(_init$P, SectionHeading); - __publicField$14(SectionHeading, "styles", styles$_); - customElements.define("section-heading", SectionHeading); - const styles$Z = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/display/section-heading/section-heading.ts + var _text_accessor_storage = /* @__PURE__ */ new WeakMap(); + var SectionHeading = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _text_accessor_storage, ""); + } + get text() { + return _classPrivateFieldGet2(_text_accessor_storage, this); + } + set text(value) { + _classPrivateFieldSet2(_text_accessor_storage, this, value); + } + render() { + return b`
${this.text}
`; + } + }; + _defineProperty(SectionHeading, "styles", styles$62); + __decorate([n$1({ type: String })], SectionHeading.prototype, "text", null); + customElements.define("section-heading", SectionHeading); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/form/editor-text-field/editor-text-field.styles.ts + var styles$61 = i$5` :host { display: block; } @@ -2091,125 +5344,91 @@ width: 100%; } `; - var __create$O = Object.create; - var __defProp$13 = Object.defineProperty; - var __getOwnPropDesc$O = Object.getOwnPropertyDescriptor; - var __knownSymbol$O = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$O = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$13 = (obj, key, value) => key in obj ? __defProp$13(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$O = (base) => [, , , __create$O(base?.[__knownSymbol$O("metadata")] ?? null)]; - var __decoratorStrings$O = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$O = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$O("Function expected") : fn; - var __decoratorContext$O = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$O[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$O("Already initialized") : fns.push(__expectFn$O(fn || null)) }); - var __decoratorMetadata$O = (array, target) => __defNormalProp$13(target, __knownSymbol$O("metadata"), array[3]); - var __runInitializers$O = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$O = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$O[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$O({ get [name]() { - return __privateGet$N(this, extra); - }, set [name](x2) { - return __privateSet$N(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$O(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$O(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$O("Object expected"); - else __expectFn$O(fn = it.get) && (desc.get = fn), __expectFn$O(fn = it.set) && (desc.set = fn), __expectFn$O(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$13(target, name, desc), target; - }; - var __publicField$13 = (obj, key, value) => __defNormalProp$13(obj, key + "", value); - var __accessCheck$N = (obj, member, msg2) => member.has(obj) || __typeError$O("Cannot " + msg2); - var __privateGet$N = (obj, member, getter) => (__accessCheck$N(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$N = (obj, member, value) => member.has(obj) ? __typeError$O("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$N = (obj, member, value, setter) => (__accessCheck$N(obj, member, "write to private field"), member.set(obj, value), value); - var _suffix_dec, _placeholder_dec$2, _type_dec$1, _value_dec$7, _label_dec$c, _a$O, _init$O, _label$c, _value$7, _type$1, _placeholder$2, _suffix; - class EditorTextField extends (_a$O = i$2, _label_dec$c = [n({ type: String })], _value_dec$7 = [n({ type: String })], _type_dec$1 = [n({ type: String })], _placeholder_dec$2 = [n({ type: String })], _suffix_dec = [n({ type: String })], _a$O) { - constructor() { - super(...arguments); - __privateAdd$N(this, _label$c, __runInitializers$O(_init$O, 8, this, "")), __runInitializers$O(_init$O, 11, this); - __privateAdd$N(this, _value$7, __runInitializers$O(_init$O, 12, this, "")), __runInitializers$O(_init$O, 15, this); - __privateAdd$N(this, _type$1, __runInitializers$O(_init$O, 16, this, "text")), __runInitializers$O(_init$O, 19, this); - __privateAdd$N(this, _placeholder$2, __runInitializers$O(_init$O, 20, this, "")), __runInitializers$O(_init$O, 23, this); - __privateAdd$N(this, _suffix, __runInitializers$O(_init$O, 24, this, "")), __runInitializers$O(_init$O, 27, this); - } - firstUpdated() { - const field = this.shadowRoot.querySelector( - "ha-textfield" - ); - if (field) { - field.label = this.label; - field.value = this.value; - if (this.type) { - field.type = this.type; - } - if (this.placeholder) { - field.placeholder = this.placeholder; - } - if (this.suffix) { - field.suffix = this.suffix; - } - } - } - updated(changedProps) { - const field = this.shadowRoot.querySelector( - "ha-textfield" - ); - if (!field) { - return; - } - if (changedProps.has("value")) { - field.value = this.value; - } - if (changedProps.has("label")) { - field.label = this.label; - } - } - _onInput(e2) { - const rawValue = e2.target.value; - const value = this.type === "number" ? parseFloat(rawValue) : rawValue; - this.dispatchEvent( - new CustomEvent("dp-field-change", { - detail: { - value: this.type === "number" && Number.isNaN(value) ? void 0 : value - }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b``; - } - } - _init$O = __decoratorStart$O(_a$O); - _label$c = /* @__PURE__ */ new WeakMap(); - _value$7 = /* @__PURE__ */ new WeakMap(); - _type$1 = /* @__PURE__ */ new WeakMap(); - _placeholder$2 = /* @__PURE__ */ new WeakMap(); - _suffix = /* @__PURE__ */ new WeakMap(); - __decorateElement$O(_init$O, 4, "label", _label_dec$c, EditorTextField, _label$c); - __decorateElement$O(_init$O, 4, "value", _value_dec$7, EditorTextField, _value$7); - __decorateElement$O(_init$O, 4, "type", _type_dec$1, EditorTextField, _type$1); - __decorateElement$O(_init$O, 4, "placeholder", _placeholder_dec$2, EditorTextField, _placeholder$2); - __decorateElement$O(_init$O, 4, "suffix", _suffix_dec, EditorTextField, _suffix); - __decoratorMetadata$O(_init$O, EditorTextField); - __publicField$13(EditorTextField, "styles", styles$Z); - customElements.define("editor-text-field", EditorTextField); - const styles$Y = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/form/editor-text-field/editor-text-field.ts + var _label_accessor_storage$12 = /* @__PURE__ */ new WeakMap(); + var _value_accessor_storage$7 = /* @__PURE__ */ new WeakMap(); + var _type_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _placeholder_accessor_storage$2 = /* @__PURE__ */ new WeakMap(); + var _suffix_accessor_storage = /* @__PURE__ */ new WeakMap(); + var EditorTextField = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _label_accessor_storage$12, ""); + _classPrivateFieldInitSpec(this, _value_accessor_storage$7, ""); + _classPrivateFieldInitSpec(this, _type_accessor_storage$1, "text"); + _classPrivateFieldInitSpec(this, _placeholder_accessor_storage$2, ""); + _classPrivateFieldInitSpec(this, _suffix_accessor_storage, ""); + } + get label() { + return _classPrivateFieldGet2(_label_accessor_storage$12, this); + } + set label(value) { + _classPrivateFieldSet2(_label_accessor_storage$12, this, value); + } + get value() { + return _classPrivateFieldGet2(_value_accessor_storage$7, this); + } + set value(value) { + _classPrivateFieldSet2(_value_accessor_storage$7, this, value); + } + get type() { + return _classPrivateFieldGet2(_type_accessor_storage$1, this); + } + set type(value) { + _classPrivateFieldSet2(_type_accessor_storage$1, this, value); + } + get placeholder() { + return _classPrivateFieldGet2(_placeholder_accessor_storage$2, this); + } + set placeholder(value) { + _classPrivateFieldSet2(_placeholder_accessor_storage$2, this, value); + } + get suffix() { + return _classPrivateFieldGet2(_suffix_accessor_storage, this); + } + set suffix(value) { + _classPrivateFieldSet2(_suffix_accessor_storage, this, value); + } + firstUpdated() { + const field = this.shadowRoot.querySelector("ha-textfield"); + if (field) { + field.label = this.label; + field.value = this.value; + if (this.type) field.type = this.type; + if (this.placeholder) field.placeholder = this.placeholder; + if (this.suffix) field.suffix = this.suffix; + } + } + updated(changedProps) { + const field = this.shadowRoot.querySelector("ha-textfield"); + if (!field) return; + if (changedProps.has("value")) field.value = this.value; + if (changedProps.has("label")) field.label = this.label; + } + _onInput(e) { + const rawValue = e.target.value; + const value = this.type === "number" ? parseFloat(rawValue) : rawValue; + this.dispatchEvent(new CustomEvent("dp-field-change", { + detail: { value: this.type === "number" && Number.isNaN(value) ? void 0 : value }, + bubbles: true, + composed: true + })); + } + render() { + return b``; + } + }; + _defineProperty(EditorTextField, "styles", styles$61); + __decorate([n$1({ type: String })], EditorTextField.prototype, "label", null); + __decorate([n$1({ type: String })], EditorTextField.prototype, "value", null); + __decorate([n$1({ type: String })], EditorTextField.prototype, "type", null); + __decorate([n$1({ type: String })], EditorTextField.prototype, "placeholder", null); + __decorate([n$1({ type: String })], EditorTextField.prototype, "suffix", null); + customElements.define("editor-text-field", EditorTextField); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/form/editor-switch/editor-switch.styles.ts + var styles$60 = i$5` :host { display: block; } @@ -2248,102 +5467,61 @@ pointer-events: none; } `; - var __create$N = Object.create; - var __defProp$12 = Object.defineProperty; - var __getOwnPropDesc$N = Object.getOwnPropertyDescriptor; - var __knownSymbol$N = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$N = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$12 = (obj, key, value) => key in obj ? __defProp$12(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$N = (base) => [, , , __create$N(base?.[__knownSymbol$N("metadata")] ?? null)]; - var __decoratorStrings$N = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$N = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$N("Function expected") : fn; - var __decoratorContext$N = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$N[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$N("Already initialized") : fns.push(__expectFn$N(fn || null)) }); - var __decoratorMetadata$N = (array, target) => __defNormalProp$12(target, __knownSymbol$N("metadata"), array[3]); - var __runInitializers$N = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$N = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$N[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$N({ get [name]() { - return __privateGet$M(this, extra); - }, set [name](x2) { - return __privateSet$M(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$N(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$N(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$N("Object expected"); - else __expectFn$N(fn = it.get) && (desc.get = fn), __expectFn$N(fn = it.set) && (desc.set = fn), __expectFn$N(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$12(target, name, desc), target; - }; - var __publicField$12 = (obj, key, value) => __defNormalProp$12(obj, key + "", value); - var __accessCheck$M = (obj, member, msg2) => member.has(obj) || __typeError$N("Cannot " + msg2); - var __privateGet$M = (obj, member, getter) => (__accessCheck$M(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$M = (obj, member, value) => member.has(obj) ? __typeError$N("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$M = (obj, member, value, setter) => (__accessCheck$M(obj, member, "write to private field"), member.set(obj, value), value); - var _tooltip_dec, _checked_dec$1, _label_dec$b, _a$N, _init$N, _label$b, _checked$1, _tooltip; - class EditorSwitch extends (_a$N = i$2, _label_dec$b = [n({ type: String })], _checked_dec$1 = [n({ type: Boolean })], _tooltip_dec = [n({ type: String })], _a$N) { - constructor() { - super(...arguments); - __privateAdd$M(this, _label$b, __runInitializers$N(_init$N, 8, this, "")), __runInitializers$N(_init$N, 11, this); - __privateAdd$M(this, _checked$1, __runInitializers$N(_init$N, 12, this, false)), __runInitializers$N(_init$N, 15, this); - __privateAdd$M(this, _tooltip, __runInitializers$N(_init$N, 16, this, "")), __runInitializers$N(_init$N, 19, this); - } - firstUpdated() { - const ff = this.shadowRoot.querySelector( - "ha-formfield" - ); - if (ff) { - ff.label = this.label; - } - const sw = this.shadowRoot.querySelector( - "ha-switch" - ); - if (sw) { - sw.checked = this.checked; - } - } - updated(changedProps) { - if (changedProps.has("checked")) { - const sw = this.shadowRoot.querySelector( - "ha-switch" - ); - if (sw) { - sw.checked = this.checked; - } - } - if (changedProps.has("label")) { - const ff = this.shadowRoot.querySelector( - "ha-formfield" - ); - if (ff) { - ff.label = this.label; - } - } - } - _onChange(e2) { - this.dispatchEvent( - new CustomEvent("dp-switch-change", { - detail: { checked: e2.target.checked }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/form/editor-switch/editor-switch.ts + var _label_accessor_storage$11 = /* @__PURE__ */ new WeakMap(); + var _checked_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _tooltip_accessor_storage = /* @__PURE__ */ new WeakMap(); + var EditorSwitch = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _label_accessor_storage$11, ""); + _classPrivateFieldInitSpec(this, _checked_accessor_storage$1, false); + _classPrivateFieldInitSpec(this, _tooltip_accessor_storage, ""); + } + get label() { + return _classPrivateFieldGet2(_label_accessor_storage$11, this); + } + set label(value) { + _classPrivateFieldSet2(_label_accessor_storage$11, this, value); + } + get checked() { + return _classPrivateFieldGet2(_checked_accessor_storage$1, this); + } + set checked(value) { + _classPrivateFieldSet2(_checked_accessor_storage$1, this, value); + } + get tooltip() { + return _classPrivateFieldGet2(_tooltip_accessor_storage, this); + } + set tooltip(value) { + _classPrivateFieldSet2(_tooltip_accessor_storage, this, value); + } + firstUpdated() { + const ff = this.shadowRoot.querySelector("ha-formfield"); + if (ff) ff.label = this.label; + const sw = this.shadowRoot.querySelector("ha-switch"); + if (sw) sw.checked = this.checked; + } + updated(changedProps) { + if (changedProps.has("checked")) { + const sw = this.shadowRoot.querySelector("ha-switch"); + if (sw) sw.checked = this.checked; + } + if (changedProps.has("label")) { + const ff = this.shadowRoot.querySelector("ha-formfield"); + if (ff) ff.label = this.label; + } + } + _onChange(e) { + this.dispatchEvent(new CustomEvent("dp-switch-change", { + detail: { checked: e.target.checked }, + bubbles: true, + composed: true + })); + } + render() { + return b`
@@ -2356,19 +5534,16 @@ ` : ""}
`; - } - } - _init$N = __decoratorStart$N(_a$N); - _label$b = /* @__PURE__ */ new WeakMap(); - _checked$1 = /* @__PURE__ */ new WeakMap(); - _tooltip = /* @__PURE__ */ new WeakMap(); - __decorateElement$N(_init$N, 4, "label", _label_dec$b, EditorSwitch, _label$b); - __decorateElement$N(_init$N, 4, "checked", _checked_dec$1, EditorSwitch, _checked$1); - __decorateElement$N(_init$N, 4, "tooltip", _tooltip_dec, EditorSwitch, _tooltip); - __decoratorMetadata$N(_init$N, EditorSwitch); - __publicField$12(EditorSwitch, "styles", styles$Y); - customElements.define("editor-switch", EditorSwitch); - const styles$X = i$5` + } + }; + _defineProperty(EditorSwitch, "styles", styles$60); + __decorate([n$1({ type: String })], EditorSwitch.prototype, "label", null); + __decorate([n$1({ type: Boolean })], EditorSwitch.prototype, "checked", null); + __decorate([n$1({ type: String })], EditorSwitch.prototype, "tooltip", null); + customElements.define("editor-switch", EditorSwitch); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/form/editor-icon-picker/editor-icon-picker.styles.ts + var styles$59 = i$5` :host { display: block; } @@ -2377,180 +5552,115 @@ width: 100%; } `; - var __create$M = Object.create; - var __defProp$11 = Object.defineProperty; - var __getOwnPropDesc$M = Object.getOwnPropertyDescriptor; - var __knownSymbol$M = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$M = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$11 = (obj, key, value) => key in obj ? __defProp$11(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$M = (base) => [, , , __create$M(base?.[__knownSymbol$M("metadata")] ?? null)]; - var __decoratorStrings$M = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$M = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$M("Function expected") : fn; - var __decoratorContext$M = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$M[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$M("Already initialized") : fns.push(__expectFn$M(fn || null)) }); - var __decoratorMetadata$M = (array, target) => __defNormalProp$11(target, __knownSymbol$M("metadata"), array[3]); - var __runInitializers$M = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$M = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$M[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$M({ get [name]() { - return __privateGet$L(this, extra); - }, set [name](x2) { - return __privateSet$L(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$M(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$M(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$M("Object expected"); - else __expectFn$M(fn = it.get) && (desc.get = fn), __expectFn$M(fn = it.set) && (desc.set = fn), __expectFn$M(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$11(target, name, desc), target; - }; - var __publicField$11 = (obj, key, value) => __defNormalProp$11(obj, key + "", value); - var __accessCheck$L = (obj, member, msg2) => member.has(obj) || __typeError$M("Cannot " + msg2); - var __privateGet$L = (obj, member, getter) => (__accessCheck$L(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$L = (obj, member, value) => member.has(obj) ? __typeError$M("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$L = (obj, member, value, setter) => (__accessCheck$L(obj, member, "write to private field"), member.set(obj, value), value); - var _hass_dec$a, _value_dec$6, _label_dec$a, _a$M, _init$M, _label$a, _value$6, _hass$a; - class EditorIconPicker extends (_a$M = i$2, _label_dec$a = [n({ type: String })], _value_dec$6 = [n({ type: String })], _hass_dec$a = [n({ type: Object })], _a$M) { - constructor() { - super(...arguments); - __privateAdd$L(this, _label$a, __runInitializers$M(_init$M, 8, this, "")), __runInitializers$M(_init$M, 11, this); - __privateAdd$L(this, _value$6, __runInitializers$M(_init$M, 12, this, "mdi:bookmark")), __runInitializers$M(_init$M, 15, this); - __privateAdd$L(this, _hass$a, __runInitializers$M(_init$M, 16, this, null)), __runInitializers$M(_init$M, 19, this); - } - firstUpdated() { - const el = this.shadowRoot.querySelector( - "ha-icon-picker" - ); - if (el) { - el.label = this.label; - if (this.hass) { - el.hass = this.hass; - } - el.value = this.value; - } - } - updated(changedProps) { - const el = this.shadowRoot.querySelector( - "ha-icon-picker" - ); - if (!el) { - return; - } - if (changedProps.has("value")) { - el.value = this.value; - } - if (changedProps.has("hass") && this.hass) { - el.hass = this.hass; - } - } - _onValueChanged(e2) { - this.dispatchEvent( - new CustomEvent("dp-icon-change", { - detail: { value: e2.detail.value }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b``; - } - } - _init$M = __decoratorStart$M(_a$M); - _label$a = /* @__PURE__ */ new WeakMap(); - _value$6 = /* @__PURE__ */ new WeakMap(); - _hass$a = /* @__PURE__ */ new WeakMap(); - __decorateElement$M(_init$M, 4, "label", _label_dec$a, EditorIconPicker, _label$a); - __decorateElement$M(_init$M, 4, "value", _value_dec$6, EditorIconPicker, _value$6); - __decorateElement$M(_init$M, 4, "hass", _hass_dec$a, EditorIconPicker, _hass$a); - __decoratorMetadata$M(_init$M, EditorIconPicker); - __publicField$11(EditorIconPicker, "styles", styles$X); - customElements.define("editor-icon-picker", EditorIconPicker); - var __defProp$10 = Object.defineProperty; - var __defNormalProp$10 = (obj, key, value) => key in obj ? __defProp$10(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$10 = (obj, key, value) => __defNormalProp$10(obj, key + "", value); - class HassRecordsActionCardEditor extends EditorBase { - _configTarget() { - return normalizeTargetValue( - this._config.target ?? { - entity_id: Array.isArray(this._config.entities) && this._config.entities.length ? this._config.entities : this._config.entity - } - ) ?? {}; - } - _syncTargetPicker() { - const tp = this.shadowRoot?.querySelector( - "#target-picker" - ); - if (!tp) { - return; - } - if (this.hass) { - tp.hass = this.hass; - } - tp.value = this._configTarget(); - } - _setTargetConfig(target) { - const cfg = { ...this._config }; - delete cfg.entity; - delete cfg.entities; - if (!target || Object.values(target).every((value) => !value?.length)) { - delete cfg.target; - } else { - cfg.target = target; - } - this._config = cfg; - this._fire(cfg); - } - _onTargetChanged(e2) { - const val = e2.detail.value; - const isEmpty = !val || Object.values(val).every((value) => !value?.length); - this._setTargetConfig(isEmpty ? void 0 : val); - } - updated(changedProps) { - if (changedProps.has("hass") || changedProps.has("_config")) { - this._syncTargetPicker(); - } - if (changedProps.has("hass") && this.hass) { - this.shadowRoot?.querySelectorAll("ha-selector").forEach((el) => { - const selectorEl = el; - selectorEl.hass = this.hass; - }); - } - } - render() { - const c2 = this._config; - return b` + } + }; + _defineProperty(EditorIconPicker, "styles", styles$59); + __decorate([n$1({ type: String })], EditorIconPicker.prototype, "label", null); + __decorate([n$1({ type: String })], EditorIconPicker.prototype, "value", null); + __decorate([n$1({ type: Object })], EditorIconPicker.prototype, "hass", null); + customElements.define("editor-icon-picker", EditorIconPicker); + //#endregion + //#region custom_components/hass_datapoints/src/cards/action/editor.ts + var HassRecordsActionCardEditor = class extends EditorBase { + _configTarget() { + return normalizeTargetValue(this._config.target ?? { entity_id: Array.isArray(this._config.entities) && this._config.entities.length ? this._config.entities : this._config.entity }) ?? {}; + } + _syncTargetPicker() { + const tp = this.shadowRoot?.querySelector("#target-picker"); + if (!tp) return; + if (this.hass) tp.hass = this.hass; + tp.value = this._configTarget(); + } + _setTargetConfig(target) { + const cfg = { ...this._config }; + delete cfg.entity; + delete cfg.entities; + if (!target || Object.values(target).every((value) => !value?.length)) delete cfg.target; + else cfg.target = target; + this._config = cfg; + this._fire(cfg); + } + _onTargetChanged(e) { + const val = e.detail.value; + const isEmpty = !val || Object.values(val).every((value) => !value?.length); + this._setTargetConfig(isEmpty ? void 0 : val); + } + updated(changedProps) { + if (changedProps.has("hass") || changedProps.has("_config")) this._syncTargetPicker(); + if (changedProps.has("hass") && this.hass) this.shadowRoot?.querySelectorAll("ha-selector").forEach((el) => { + const selectorEl = el; + selectorEl.hass = this.hass; + }); + } + render() { + const c = this._config; + return b`
this._set("title", e2.detail.value)} + .value=${c.title || ""} + @dp-field-change=${(e) => this._set("title", e.detail.value)} >
- ${msg( - "Pre-fill entities, devices, areas or labels that are always linked to recordings from this card.", - { - id: "Pre-fill entities, devices, areas or labels that are always linked to recordings from this card." - } - )} + ${msg("Pre-fill entities, devices, areas or labels that are always linked to recordings from this card.", { id: "Pre-fill entities, devices, areas or labels that are always linked to recordings from this card." })}
this._set( - "show_config_targets", - e2.detail.checked ? void 0 : false - )} + .checked=${c.show_config_targets !== false} + @dp-switch-change=${(e) => this._set("show_config_targets", e.detail.checked ? void 0 : false)} > this._set( - "show_target_picker", - e2.detail.checked ? void 0 : false - )} + .checked=${c.show_target_picker !== false} + @dp-switch-change=${(e) => this._set("show_target_picker", e.detail.checked ? void 0 : false)} > this._set("default_icon", e2.detail.value)} + @dp-icon-change=${(e) => this._set("default_icon", e.detail.value)} > this._set("default_color", e2.detail.color)} + .color=${c.default_color || "#03a9f4"} + @dp-color-change=${(e) => this._set("default_color", e.detail.color)} > this._set("show_date", e2.detail.checked ? void 0 : false)} + .checked=${c.show_date !== false} + @dp-switch-change=${(e) => this._set("show_date", e.detail.checked ? void 0 : false)} > this._set("show_annotation", e2.detail.checked ? void 0 : false)} + .checked=${c.show_annotation !== false} + @dp-switch-change=${(e) => this._set("show_annotation", e.detail.checked ? void 0 : false)} >
`; - } - } - __publicField$10(HassRecordsActionCardEditor, "styles", [EditorBase.styles, styles$10]); - const DEFAULT_HA_COMPONENTS = [ - "ha-form", - "ha-icon", - "ha-icon-button", - "ha-selector", - "ha-textfield", - "ha-icon-picker", - "ha-icon-button", - "ha-entity-picker", - "ha-select", - "ha-dialog", - "ha-sortable", - "ha-svg-icon", - "ha-alert", - "ha-button", - "ha-color-picker", - "ha-badge", - "ha-sankey-chart", - "mwc-button" - ]; - const loadHaComponents = async (components) => { - const componentsToLoad = components || DEFAULT_HA_COMPONENTS; - try { - if (componentsToLoad.every((component) => customElements.get(component))) { - return; - } - await Promise.race([ - customElements.whenDefined("partial-panel-resolver"), - new Promise((_2, reject) => setTimeout(() => reject(new Error("Timeout waiting for partial-panel-resolver")), 1e4)) - ]); - const ppr = document.createElement("partial-panel-resolver"); - if (!ppr) { - throw new Error("Failed to create partial-panel-resolver element"); - } - ppr.hass = { - panels: [ - { - url_path: "tmp", - component_name: "config" - } - ] - }; - if (typeof ppr._updateRoutes !== "function") { - throw new Error("partial-panel-resolver does not have _updateRoutes method"); - } - ppr._updateRoutes(); - if (!ppr.routerOptions?.routes?.tmp?.load) { - throw new Error("Failed to create tmp route in partial-panel-resolver"); - } - await Promise.race([ - ppr.routerOptions.routes.tmp.load(), - new Promise((_2, reject) => setTimeout(() => reject(new Error("Timeout loading tmp route")), 1e4)) - ]); - await Promise.race([ - customElements.whenDefined("ha-panel-config"), - new Promise((_2, reject) => setTimeout(() => reject(new Error("Timeout waiting for ha-panel-config")), 1e4)) - ]); - const cpr = document.createElement("ha-panel-config"); - if (!cpr) { - throw new Error("Failed to create ha-panel-config element"); - } - if (!cpr.routerOptions?.routes?.automation?.load) { - throw new Error("ha-panel-config does not have automation route"); - } - await Promise.race([ - cpr.routerOptions.routes.automation.load(), - new Promise((_2, reject) => setTimeout(() => reject(new Error("Timeout loading automation components")), 1e4)) - ]); - const missingComponents = componentsToLoad.filter((component) => !customElements.get(component)); - if (missingComponents.length > 0) { - throw new Error(`Failed to load components: ${missingComponents.join(", ")}`); - } - } catch (error) { - console.error("Error loading Home Assistant form components:", error); - try { - if (window.customElements && window.customElements.get("home-assistant")) { - console.log("Attempting fallback loading method for HA components"); - const event = new CustomEvent("ha-request-load-components", { - detail: { - components: componentsToLoad - }, - bubbles: true, - composed: true - }); - document.dispatchEvent(event); - } - } catch (fallbackError) { - console.error("Fallback loading method failed:", fallbackError); - } - } - }; - function fmtTime(iso) { - return new Date(iso).toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit" - }); - } - function fmtDateTime(iso) { - return new Date(iso).toLocaleString([], { - month: "short", - day: "numeric", - hour: "2-digit", - minute: "2-digit" - }); - } - function fmtRelativeTime(iso) { - const now = Date.now(); - const t2 = new Date(iso).getTime(); - const diff = now - t2; - const mins = Math.floor(diff / 6e4); - if (mins < 1) { - return "Just now"; - } - if (mins < 60) { - return `${mins}m ago`; - } - const hours = Math.floor(mins / 60); - if (hours < 24) { - return `${hours}h ago`; - } - const days = Math.floor(hours / 24); - if (days < 7) { - return `${days}d ago`; - } - return fmtDateTime(iso); - } - function esc(str) { - return String(str).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); - } - const HA_COMPONENT_LOAD_TIMEOUT_MS = 6e3; - const HA_COMPONENT_LOADER_SUPPORTED_TAGS = /* @__PURE__ */ new Set([ - "ha-form", - "ha-icon", - "ha-icon-button", - "ha-selector", - "ha-textfield", - "ha-icon-picker", - "ha-entity-picker", - "ha-select", - "ha-dialog", - "ha-sortable", - "ha-svg-icon", - "ha-alert", - "ha-button", - "ha-color-picker", - "ha-badge", - "ha-sankey-chart", - "mwc-button" - ]); - const HA_HISTORY_ROUTE_COMPONENT_TAGS = /* @__PURE__ */ new Set([ - "ha-target-picker", - "ha-date-range-picker" - ]); - async function preloadHistoryRouteComponents(tags = []) { - const historyTags = tags.filter( - (tag) => HA_HISTORY_ROUTE_COMPONENT_TAGS.has(tag) && !customElements.get(tag) - ); - if (!historyTags.length) { - return; - } - try { - const app = document.querySelector("home-assistant"); - const panels = app?.hass?.panels; - if (!panels?.history) { - logger.warn( - "[hass-datapoints ha] history panel not available for preload" - ); - return; - } - const resolver = document.createElement( - "partial-panel-resolver" - ); - if (typeof resolver._updateRoutes !== "function") { - logger.warn( - "[hass-datapoints ha] partial-panel-resolver missing _updateRoutes" - ); - return; - } - resolver.hass = { panels }; - resolver._updateRoutes(); - const load = resolver.routerOptions?.routes?.history?.load; - if (typeof load !== "function") { - logger.warn("[hass-datapoints ha] history route loader missing"); - return; - } - await load(); - } catch (error) { - logger.warn("[hass-datapoints ha] history route preload failed", { - historyTags, - message: error instanceof Error ? error.message : String(error) - }); - } - } - function waitForHaComponent(tag, timeoutMs = HA_COMPONENT_LOAD_TIMEOUT_MS) { - if (!tag) { - return Promise.resolve(false); - } - if (customElements.get(tag)) { - return Promise.resolve(true); - } - return Promise.race([ - customElements.whenDefined(tag).then(() => true), - new Promise((resolve) => { - window.setTimeout(() => { - logger.warn("[hass-datapoints ha] component wait timed out", { - tag, - timeoutMs - }); - resolve(false); - }, timeoutMs); - }) - ]); - } - function ensureHaComponents(tags = []) { - const componentTags = [...new Set((tags || []).filter(Boolean))]; - const loaderTags = componentTags.filter( - (tag) => HA_COMPONENT_LOADER_SUPPORTED_TAGS.has(tag) - ); - const loadPromise = Promise.resolve().then( - () => typeof loadHaComponents === "function" && loaderTags.length ? Promise.resolve(loadHaComponents(loaderTags)).catch((error) => { - logger.warn("[hass-datapoints ha] loader failed", { - loaderTags, - message: error instanceof Error ? error.message : String(error) - }); - return void 0; - }) : void 0 - ).then(() => preloadHistoryRouteComponents(componentTags)); - return loadPromise.then( - () => Promise.all(componentTags.map((tag) => waitForHaComponent(tag))) - ).then( - (results) => componentTags.map((tag, index) => ({ - tag, - ready: !!results[index], - defined: !!customElements.get(tag) - })) - ); - } - function confirmDestructiveAction(host, options = {}) { - return ensureHaComponents(["ha-dialog"]).then( - () => new Promise((resolve) => { - const root = host?.shadowRoot || host; - if (!root || !("appendChild" in root)) { - const confirmation = window.confirm( - options.message || options.title || "Are you sure?" - ); - resolve(confirmation); - return; - } - const dialog = document.createElement("ha-dialog"); - dialog.setAttribute("hideActions", ""); - dialog.scrimClickAction = true; - dialog.escapeKeyAction = true; - dialog.open = false; - dialog.headerTitle = options.title || msg("Confirm delete"); - if (host?._hass) { - dialog.hass = host._hass; - } - dialog.innerHTML = ` - -
-
${esc(options.message || msg("Are you sure you want to delete this item?"))}
-
- - -
-
- `; - let settled = false; - const finish = (value) => { - if (settled) { - return; - } - settled = true; - dialog.open = false; - resolve(value); - }; - const cancelButton = dialog.querySelector( - ".confirm-dialog-button.cancel" - ); - const confirmButton = dialog.querySelector( - ".confirm-dialog-button.confirm" - ); - cancelButton?.addEventListener("click", () => { - finish(false); - }); - confirmButton?.addEventListener("click", () => { - finish(true); - }); - dialog.addEventListener("keydown", (event) => { - if (event.key !== "Enter" || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) { - return; - } - event.preventDefault(); - finish(true); - }); - dialog.addEventListener( - "closed", - () => { - dialog.remove(); - if (!settled) { - resolve(false); - } - }, - { once: true } - ); - root.appendChild(dialog); - dialog.open = true; - window.requestAnimationFrame(() => { - confirmButton?.focus(); - }); - }) - ); - } - const styles$W = ` + } + }; + _defineProperty(HassRecordsActionCardEditor, "styles", [EditorBase.styles, styles$64]); + //#endregion + //#region node_modules/.pnpm/@kipk+load-ha-components@1.0.3/node_modules/@kipk/load-ha-components/dist/load-ha-components.js + /** + * Utility function to asynchronously load Home Assistant form components + * if they are not already registered in the custom elements registry. + * + * @param components - Optional array of component names to load. If not provided, defaults to a predefined list. + * @returns Promise that resolves when all required components are loaded + */ + var DEFAULT_HA_COMPONENTS = [ + "ha-form", + "ha-icon", + "ha-icon-button", + "ha-selector", + "ha-textfield", + "ha-icon-picker", + "ha-icon-button", + "ha-entity-picker", + "ha-select", + "ha-dialog", + "ha-sortable", + "ha-svg-icon", + "ha-alert", + "ha-button", + "ha-color-picker", + "ha-badge", + "ha-sankey-chart", + "mwc-button" + ]; + var loadHaComponents = async (components) => { + const componentsToLoad = components || DEFAULT_HA_COMPONENTS; + try { + if (componentsToLoad.every((component) => customElements.get(component))) return; + await Promise.race([customElements.whenDefined("partial-panel-resolver"), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error("Timeout waiting for partial-panel-resolver")), 1e4))]); + const ppr = document.createElement("partial-panel-resolver"); + if (!ppr) throw new Error("Failed to create partial-panel-resolver element"); + ppr.hass = { panels: [{ + url_path: "tmp", + component_name: "config" + }] }; + if (typeof ppr._updateRoutes !== "function") throw new Error("partial-panel-resolver does not have _updateRoutes method"); + ppr._updateRoutes(); + if (!ppr.routerOptions?.routes?.tmp?.load) throw new Error("Failed to create tmp route in partial-panel-resolver"); + await Promise.race([ppr.routerOptions.routes.tmp.load(), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error("Timeout loading tmp route")), 1e4))]); + await Promise.race([customElements.whenDefined("ha-panel-config"), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error("Timeout waiting for ha-panel-config")), 1e4))]); + const cpr = document.createElement("ha-panel-config"); + if (!cpr) throw new Error("Failed to create ha-panel-config element"); + if (!cpr.routerOptions?.routes?.automation?.load) throw new Error("ha-panel-config does not have automation route"); + await Promise.race([cpr.routerOptions.routes.automation.load(), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error("Timeout loading automation components")), 1e4))]); + const missingComponents = componentsToLoad.filter((component) => !customElements.get(component)); + if (missingComponents.length > 0) throw new Error(`Failed to load components: ${missingComponents.join(", ")}`); + } catch (error) { + console.error("Error loading Home Assistant form components:", error); + try { + if (window.customElements && window.customElements.get("home-assistant")) { + console.log("Attempting fallback loading method for HA components"); + const event = new CustomEvent("ha-request-load-components", { + detail: { components: componentsToLoad }, + bubbles: true, + composed: true + }); + document.dispatchEvent(event); + } + } catch (fallbackError) { + console.error("Fallback loading method failed:", fallbackError); + } + } + }; + //#endregion + //#region custom_components/hass_datapoints/src/lib/ha/ha-components.ts + var HA_COMPONENT_LOAD_TIMEOUT_MS = 6e3; + var HA_COMPONENT_LOADER_SUPPORTED_TAGS = new Set([ + "ha-form", + "ha-icon", + "ha-icon-button", + "ha-selector", + "ha-textfield", + "ha-icon-picker", + "ha-entity-picker", + "ha-select", + "ha-dialog", + "ha-sortable", + "ha-svg-icon", + "ha-alert", + "ha-button", + "ha-color-picker", + "ha-badge", + "ha-sankey-chart", + "mwc-button" + ]); + var HA_HISTORY_ROUTE_COMPONENT_TAGS = new Set(["ha-target-picker", "ha-date-range-picker"]); + async function preloadHistoryRouteComponents(tags = []) { + const historyTags = tags.filter((tag) => HA_HISTORY_ROUTE_COMPONENT_TAGS.has(tag) && !customElements.get(tag)); + if (!historyTags.length) return; + try { + const panels = document.querySelector("home-assistant")?.hass?.panels; + if (!panels?.history) { + logger.warn("[hass-datapoints ha] history panel not available for preload"); + return; + } + const resolver = document.createElement("partial-panel-resolver"); + if (typeof resolver._updateRoutes !== "function") { + logger.warn("[hass-datapoints ha] partial-panel-resolver missing _updateRoutes"); + return; + } + resolver.hass = { panels }; + resolver._updateRoutes(); + const load = resolver.routerOptions?.routes?.history?.load; + if (typeof load !== "function") { + logger.warn("[hass-datapoints ha] history route loader missing"); + return; + } + await load(); + } catch (error) { + logger.warn("[hass-datapoints ha] history route preload failed", { + historyTags, + message: error instanceof Error ? error.message : String(error) + }); + } + } + function waitForHaComponent(tag, timeoutMs = HA_COMPONENT_LOAD_TIMEOUT_MS) { + if (!tag) return Promise.resolve(false); + if (customElements.get(tag)) return Promise.resolve(true); + return Promise.race([customElements.whenDefined(tag).then(() => true), new Promise((resolve) => { + window.setTimeout(() => { + logger.warn("[hass-datapoints ha] component wait timed out", { + tag, + timeoutMs + }); + resolve(false); + }, timeoutMs); + })]); + } + function ensureHaComponents(tags = []) { + const componentTags = [...new Set((tags || []).filter(Boolean))]; + const loaderTags = componentTags.filter((tag) => HA_COMPONENT_LOADER_SUPPORTED_TAGS.has(tag)); + return Promise.resolve().then(() => typeof loadHaComponents === "function" && loaderTags.length ? Promise.resolve(loadHaComponents(loaderTags)).catch((error) => { + logger.warn("[hass-datapoints ha] loader failed", { + loaderTags, + message: error instanceof Error ? error.message : String(error) + }); + }) : void 0).then(() => preloadHistoryRouteComponents(componentTags)).then(() => Promise.all(componentTags.map((tag) => waitForHaComponent(tag)))).then((results) => componentTags.map((tag, index) => ({ + tag, + ready: !!results[index], + defined: !!customElements.get(tag) + }))); + } + function confirmDestructiveAction(host, options = {}) { + return ensureHaComponents(["ha-dialog"]).then(() => new Promise((resolve) => { + const root = host?.shadowRoot || host; + if (!root || !("appendChild" in root)) { + resolve(window.confirm(options.message || options.title || "Are you sure?")); + return; + } + const dialog = document.createElement("ha-dialog"); + dialog.setAttribute("hideActions", ""); + dialog.scrimClickAction = true; + dialog.escapeKeyAction = true; + dialog.open = false; + dialog.headerTitle = options.title || msg("Confirm delete"); + if (host?._hass) dialog.hass = host._hass; + let settled = false; + const finish = (value) => { + if (settled) return; + settled = true; + dialog.open = false; + resolve(value); + }; + D(b` + +
+
+ ${options.message || msg("Are you sure you want to delete this item?")} +
+
+ + +
+
+ `, dialog); + dialog.addEventListener("keydown", (event) => { + if (event.key !== "Enter" || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + event.preventDefault(); + finish(true); + }); + dialog.addEventListener("closed", () => { + dialog.remove(); + if (!settled) resolve(false); + }, { once: true }); + root.appendChild(dialog); + dialog.open = true; + window.requestAnimationFrame(() => { + dialog.querySelector(".confirm-dialog-button.confirm")?.focus(); + }); + })); + } + //#endregion + //#region custom_components/hass_datapoints/src/cards/dev-tool/dev-tool.styles.ts + var styles$58 = ` :host { display: block; } ha-card { padding: 16px; } .card-header { @@ -3005,27 +6006,23 @@ --mdc-theme-primary: var(--error-color, #f44336); } `; - var __defProp$$ = Object.defineProperty; - var __defNormalProp$$ = (obj, key, value) => key in obj ? __defProp$$(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$$ = (obj, key, value) => __defNormalProp$$(obj, key + "", value); - class HassRecordsDevToolCardEditor extends EditorBase { - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/cards/dev-tool/editor.ts + var HassRecordsDevToolCardEditor = class extends EditorBase { + render() { + return b`

- ${msg( - "This card does not currently have configurable editor options.", - { - id: "This card does not currently have configurable editor options." - } - )} + ${msg("This card does not currently have configurable editor options.", { id: "This card does not currently have configurable editor options." })}

`; - } - } - __publicField$$(HassRecordsDevToolCardEditor, "styles", [EditorBase.styles]); - const styles$V = i$5` + } + }; + _defineProperty(HassRecordsDevToolCardEditor, "styles", [EditorBase.styles]); + //#endregion + //#region custom_components/hass_datapoints/src/cards/dev-tool/dev-tool-results/dev-tool-results.styles.ts + var styles$57 = i$5` :host { display: block; margin-top: 18px; @@ -3151,133 +6148,148 @@ color: var(--secondary-text-color); } `; - var __create$L = Object.create; - var __defProp$_ = Object.defineProperty; - var __getOwnPropDesc$L = Object.getOwnPropertyDescriptor; - var __knownSymbol$L = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$L = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$_ = (obj, key, value) => key in obj ? __defProp$_(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$L = (base) => [, , , __create$L(base?.[__knownSymbol$L("metadata")] ?? null)]; - var __decoratorStrings$L = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$L = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$L("Function expected") : fn; - var __decoratorContext$L = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$L[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$L("Already initialized") : fns.push(__expectFn$L(fn || null)) }); - var __decoratorMetadata$L = (array, target) => __defNormalProp$_(target, __knownSymbol$L("metadata"), array[3]); - var __runInitializers$L = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$L = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$L[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$L({ get [name]() { - return __privateGet$K(this, extra); - }, set [name](x2) { - return __privateSet$K(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$L(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$L(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$L("Object expected"); - else __expectFn$L(fn = it.get) && (desc.get = fn), __expectFn$L(fn = it.set) && (desc.set = fn), __expectFn$L(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$_(target, name, desc), target; - }; - var __publicField$_ = (obj, key, value) => __defNormalProp$_(obj, key + "", value); - var __accessCheck$K = (obj, member, msg2) => member.has(obj) || __typeError$L("Cannot " + msg2); - var __privateGet$K = (obj, member, getter) => (__accessCheck$K(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$K = (obj, member, value) => member.has(obj) ? __typeError$L("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$K = (obj, member, value, setter) => (__accessCheck$K(obj, member, "write to private field"), member.set(obj, value), value); - var __collapsedWindowIds_dec, _statusVisible_dec, _statusText_dec, _statusKind_dec, _isAdmin_dec, _results_dec, _a$L, _init$L, _results, _isAdmin, _statusKind, _statusText, _statusVisible, __collapsedWindowIds; - class CardDevToolResults extends (_a$L = i$2, _results_dec = [n({ attribute: false })], _isAdmin_dec = [n({ type: Boolean })], _statusKind_dec = [n({ type: String })], _statusText_dec = [n({ type: String })], _statusVisible_dec = [n({ type: Boolean })], __collapsedWindowIds_dec = [r()], _a$L) { - constructor() { - super(...arguments); - __privateAdd$K(this, _results, __runInitializers$L(_init$L, 8, this, [])), __runInitializers$L(_init$L, 11, this); - __privateAdd$K(this, _isAdmin, __runInitializers$L(_init$L, 12, this, false)), __runInitializers$L(_init$L, 15, this); - __privateAdd$K(this, _statusKind, __runInitializers$L(_init$L, 16, this, "")), __runInitializers$L(_init$L, 19, this); - __privateAdd$K(this, _statusText, __runInitializers$L(_init$L, 20, this, "")), __runInitializers$L(_init$L, 23, this); - __privateAdd$K(this, _statusVisible, __runInitializers$L(_init$L, 24, this, false)), __runInitializers$L(_init$L, 27, this); - __privateAdd$K(this, __collapsedWindowIds, __runInitializers$L(_init$L, 28, this, [])), __runInitializers$L(_init$L, 31, this); - } - _emitSelection() { - this.dispatchEvent( - new CustomEvent("dp-results-selection-change", { - detail: { - results: this.results.map((result) => ({ - ...result, - selected: [...result.selected] - })) - }, - bubbles: true, - composed: true - }) - ); - } - _updateSelected(windowId, selected) { - this.results = this.results.map((result) => { - if (result.id !== windowId) { - return result; - } - return { - ...result, - selected - }; - }); - this._emitSelection(); - } - _toggleCollapsed(windowId) { - if (this._collapsedWindowIds.includes(windowId)) { - this._collapsedWindowIds = this._collapsedWindowIds.filter( - (id) => id !== windowId - ); - return; - } - this._collapsedWindowIds = [...this._collapsedWindowIds, windowId]; - } - _emitRecordRequest() { - const selectedItems = []; - this.results.forEach((result) => { - [...result.selected].sort((a2, b2) => a2 - b2).forEach((index) => { - selectedItems.push(result.changes[index]); - }); - }); - this.dispatchEvent( - new CustomEvent("dp-record-selected-request", { - detail: { - items: selectedItems - }, - bubbles: true, - composed: true - }) - ); - } - _summaryParts() { - let selected = 0; - let total = 0; - this.results.forEach((result) => { - selected += result.selected.length; - total += result.changes.length; - }); - return { - selected, - total, - windows: this.results.length - }; - } - render() { - if (this.results.length === 0) { - return b``; - } - const summary = this._summaryParts(); - return b` + //#endregion + //#region custom_components/hass_datapoints/src/lib/util/format.ts + function fmtTime(iso) { + return new Date(iso).toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit" + }); + } + function fmtDateTime(iso) { + return new Date(iso).toLocaleString([], { + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit" + }); + } + function fmtRelativeTime(iso) { + const diff = Date.now() - new Date(iso).getTime(); + const mins = Math.floor(diff / 6e4); + if (mins < 1) return "Just now"; + if (mins < 60) return `${mins}m ago`; + const hours = Math.floor(mins / 60); + if (hours < 24) return `${hours}h ago`; + const days = Math.floor(hours / 24); + if (days < 7) return `${days}d ago`; + return fmtDateTime(iso); + } + /** Escape HTML for safe inline insertion */ + function esc(str) { + return String(str).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); + } + //#endregion + //#region custom_components/hass_datapoints/src/cards/dev-tool/dev-tool-results/dev-tool-results.ts + var _results_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _isAdmin_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _statusKind_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _statusText_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _statusVisible_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _collapsedWindowIds_accessor_storage = /* @__PURE__ */ new WeakMap(); + var CardDevToolResults = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _results_accessor_storage, []); + _classPrivateFieldInitSpec(this, _isAdmin_accessor_storage, false); + _classPrivateFieldInitSpec(this, _statusKind_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _statusText_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _statusVisible_accessor_storage, false); + _classPrivateFieldInitSpec(this, _collapsedWindowIds_accessor_storage, []); + } + get results() { + return _classPrivateFieldGet2(_results_accessor_storage, this); + } + set results(value) { + _classPrivateFieldSet2(_results_accessor_storage, this, value); + } + get isAdmin() { + return _classPrivateFieldGet2(_isAdmin_accessor_storage, this); + } + set isAdmin(value) { + _classPrivateFieldSet2(_isAdmin_accessor_storage, this, value); + } + get statusKind() { + return _classPrivateFieldGet2(_statusKind_accessor_storage, this); + } + set statusKind(value) { + _classPrivateFieldSet2(_statusKind_accessor_storage, this, value); + } + get statusText() { + return _classPrivateFieldGet2(_statusText_accessor_storage, this); + } + set statusText(value) { + _classPrivateFieldSet2(_statusText_accessor_storage, this, value); + } + get statusVisible() { + return _classPrivateFieldGet2(_statusVisible_accessor_storage, this); + } + set statusVisible(value) { + _classPrivateFieldSet2(_statusVisible_accessor_storage, this, value); + } + get _collapsedWindowIds() { + return _classPrivateFieldGet2(_collapsedWindowIds_accessor_storage, this); + } + set _collapsedWindowIds(value) { + _classPrivateFieldSet2(_collapsedWindowIds_accessor_storage, this, value); + } + _emitSelection() { + this.dispatchEvent(new CustomEvent("dp-results-selection-change", { + detail: { results: this.results.map((result) => ({ + ...result, + selected: [...result.selected] + })) }, + bubbles: true, + composed: true + })); + } + _updateSelected(windowId, selected) { + this.results = this.results.map((result) => { + if (result.id !== windowId) return result; + return { + ...result, + selected + }; + }); + this._emitSelection(); + } + _toggleCollapsed(windowId) { + if (this._collapsedWindowIds.includes(windowId)) { + this._collapsedWindowIds = this._collapsedWindowIds.filter((id) => id !== windowId); + return; + } + this._collapsedWindowIds = [...this._collapsedWindowIds, windowId]; + } + _emitRecordRequest() { + const selectedItems = []; + this.results.forEach((result) => { + [...result.selected].sort((a, b) => a - b).forEach((index) => { + selectedItems.push(result.changes[index]); + }); + }); + this.dispatchEvent(new CustomEvent("dp-record-selected-request", { + detail: { items: selectedItems }, + bubbles: true, + composed: true + })); + } + _summaryParts() { + let selected = 0; + let total = 0; + this.results.forEach((result) => { + selected += result.selected.length; + total += result.changes.length; + }); + return { + selected, + total, + windows: this.results.length + }; + } + render() { + if (this.results.length === 0) return b``; + const summary = this._summaryParts(); + return b`
@@ -3293,18 +6305,17 @@
${this.results.map((result) => { - const startLabel = result.startDt ? new Date(result.startDt).toLocaleString([], { - dateStyle: "short", - timeStyle: "short" - }) : "unknown start"; - const rangeLabel = result.endDt ? `${startLabel} → ${new Date(result.endDt).toLocaleString([], { - dateStyle: "short", - timeStyle: "short" - })}` : `${startLabel} → now`; - const collapsed = this._collapsedWindowIds.includes(result.id); - return b` + const startLabel = result.startDt ? new Date(result.startDt).toLocaleString([], { + dateStyle: "short", + timeStyle: "short" + }) : "unknown start"; + const rangeLabel = result.endDt ? `${startLabel} → ${new Date(result.endDt).toLocaleString([], { + dateStyle: "short", + timeStyle: "short" + })}` : `${startLabel} → now`; + return b`
- ` - )} + `)}
`; - })} + })}
`; - } - } - _init$L = __decoratorStart$L(_a$L); - _results = /* @__PURE__ */ new WeakMap(); - _isAdmin = /* @__PURE__ */ new WeakMap(); - _statusKind = /* @__PURE__ */ new WeakMap(); - _statusText = /* @__PURE__ */ new WeakMap(); - _statusVisible = /* @__PURE__ */ new WeakMap(); - __collapsedWindowIds = /* @__PURE__ */ new WeakMap(); - __decorateElement$L(_init$L, 4, "results", _results_dec, CardDevToolResults, _results); - __decorateElement$L(_init$L, 4, "isAdmin", _isAdmin_dec, CardDevToolResults, _isAdmin); - __decorateElement$L(_init$L, 4, "statusKind", _statusKind_dec, CardDevToolResults, _statusKind); - __decorateElement$L(_init$L, 4, "statusText", _statusText_dec, CardDevToolResults, _statusText); - __decorateElement$L(_init$L, 4, "statusVisible", _statusVisible_dec, CardDevToolResults, _statusVisible); - __decorateElement$L(_init$L, 4, "_collapsedWindowIds", __collapsedWindowIds_dec, CardDevToolResults, __collapsedWindowIds); - __decoratorMetadata$L(_init$L, CardDevToolResults); - __publicField$_(CardDevToolResults, "styles", styles$V); - customElements.define("dev-tool-results", CardDevToolResults); - const styles$U = i$5` + } + }; + _defineProperty(CardDevToolResults, "styles", styles$57); + __decorate([n$1({ attribute: false })], CardDevToolResults.prototype, "results", null); + __decorate([n$1({ type: Boolean })], CardDevToolResults.prototype, "isAdmin", null); + __decorate([n$1({ type: String })], CardDevToolResults.prototype, "statusKind", null); + __decorate([n$1({ type: String })], CardDevToolResults.prototype, "statusText", null); + __decorate([n$1({ type: Boolean })], CardDevToolResults.prototype, "statusVisible", null); + __decorate([r$1()], CardDevToolResults.prototype, "_collapsedWindowIds", null); + customElements.define("dev-tool-results", CardDevToolResults); + //#endregion + //#region custom_components/hass_datapoints/src/cards/dev-tool/dev-tool-windows/dev-tool-windows.styles.ts + var styles$56 = i$5` :host { display: block; } @@ -3520,117 +6515,74 @@ --mdc-icon-size: 18px; } `; - var __create$K = Object.create; - var __defProp$Z = Object.defineProperty; - var __getOwnPropDesc$K = Object.getOwnPropertyDescriptor; - var __knownSymbol$K = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$K = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$Z = (obj, key, value) => key in obj ? __defProp$Z(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$K = (base) => [, , , __create$K(base?.[__knownSymbol$K("metadata")] ?? null)]; - var __decoratorStrings$K = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$K = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$K("Function expected") : fn; - var __decoratorContext$K = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$K[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$K("Already initialized") : fns.push(__expectFn$K(fn || null)) }); - var __decoratorMetadata$K = (array, target) => __defNormalProp$Z(target, __knownSymbol$K("metadata"), array[3]); - var __runInitializers$K = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$K = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$K[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$K({ get [name]() { - return __privateGet$J(this, extra); - }, set [name](x2) { - return __privateSet$J(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$K(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$K(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$K("Object expected"); - else __expectFn$K(fn = it.get) && (desc.get = fn), __expectFn$K(fn = it.set) && (desc.set = fn), __expectFn$K(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$Z(target, name, desc), target; - }; - var __publicField$Z = (obj, key, value) => __defNormalProp$Z(obj, key + "", value); - var __accessCheck$J = (obj, member, msg2) => member.has(obj) || __typeError$K("Cannot " + msg2); - var __privateGet$J = (obj, member, getter) => (__accessCheck$J(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$J = (obj, member, value) => member.has(obj) ? __typeError$K("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$J = (obj, member, value, setter) => (__accessCheck$J(obj, member, "write to private field"), member.set(obj, value), value); - var __nextWindowId_dec, _windows_dec, _a$K, _init$K, _windows, __nextWindowId; - class CardDevToolWindows extends (_a$K = i$2, _windows_dec = [n({ attribute: false })], __nextWindowId_dec = [r()], _a$K) { - constructor() { - super(...arguments); - __privateAdd$J(this, _windows, __runInitializers$K(_init$K, 8, this, [])), __runInitializers$K(_init$K, 11, this); - __privateAdd$J(this, __nextWindowId, __runInitializers$K(_init$K, 12, this, 1)), __runInitializers$K(_init$K, 15, this); - } - connectedCallback() { - super.connectedCallback(); - if (this.windows.length === 0) { - this.windows = [this._createWindow()]; - } else { - this._nextWindowId = Math.max(...this.windows.map((windowConfig) => windowConfig.id), 0) + 1; - } - } - getWindowConfigs() { - return this.windows.map((windowConfig) => ({ ...windowConfig })); - } - _createWindow() { - const nextId = this._nextWindowId; - this._nextWindowId += 1; - return { - id: nextId, - label: "", - startDt: "", - endDt: "" - }; - } - _emitChange() { - this.dispatchEvent( - new CustomEvent("dp-window-configs-change", { - detail: { - windows: this.getWindowConfigs() - }, - bubbles: true, - composed: true - }) - ); - } - _addWindow() { - this.windows = [...this.windows, this._createWindow()]; - this._emitChange(); - } - _removeWindow(id) { - if (this.windows.length <= 1) { - return; - } - this.windows = this.windows.filter( - (windowConfig) => windowConfig.id !== id - ); - this._emitChange(); - } - _updateWindow(id, patch) { - this.windows = this.windows.map((windowConfig) => { - if (windowConfig.id !== id) { - return windowConfig; - } - return { - ...windowConfig, - ...patch - }; - }); - this._emitChange(); - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/cards/dev-tool/dev-tool-windows/dev-tool-windows.ts + var _windows_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _nextWindowId_accessor_storage = /* @__PURE__ */ new WeakMap(); + var CardDevToolWindows = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _windows_accessor_storage, []); + _classPrivateFieldInitSpec(this, _nextWindowId_accessor_storage, 1); + } + get windows() { + return _classPrivateFieldGet2(_windows_accessor_storage, this); + } + set windows(value) { + _classPrivateFieldSet2(_windows_accessor_storage, this, value); + } + get _nextWindowId() { + return _classPrivateFieldGet2(_nextWindowId_accessor_storage, this); + } + set _nextWindowId(value) { + _classPrivateFieldSet2(_nextWindowId_accessor_storage, this, value); + } + connectedCallback() { + super.connectedCallback(); + if (this.windows.length === 0) this.windows = [this._createWindow()]; + else this._nextWindowId = Math.max(...this.windows.map((windowConfig) => windowConfig.id), 0) + 1; + } + getWindowConfigs() { + return this.windows.map((windowConfig) => ({ ...windowConfig })); + } + _createWindow() { + const nextId = this._nextWindowId; + this._nextWindowId += 1; + return { + id: nextId, + label: "", + startDt: "", + endDt: "" + }; + } + _emitChange() { + this.dispatchEvent(new CustomEvent("dp-window-configs-change", { + detail: { windows: this.getWindowConfigs() }, + bubbles: true, + composed: true + })); + } + _addWindow() { + this.windows = [...this.windows, this._createWindow()]; + this._emitChange(); + } + _removeWindow(id) { + if (this.windows.length <= 1) return; + this.windows = this.windows.filter((windowConfig) => windowConfig.id !== id); + this._emitChange(); + } + _updateWindow(id, patch) { + this.windows = this.windows.map((windowConfig) => { + if (windowConfig.id !== id) return windowConfig; + return { + ...windowConfig, + ...patch + }; + }); + this._emitChange(); + } + render() { + return b`
Comparison windows
- ${this.windows.map( - (windowConfig, index) => b` + ${this.windows.map((windowConfig, index) => b`
@@ -3654,9 +6605,7 @@ type="text" .value=${windowConfig.label} placeholder=${`Window ${index + 1}`} - @input=${(event) => this._updateWindow(windowConfig.id, { - label: event.currentTarget.value - })} + @input=${(event) => this._updateWindow(windowConfig.id, { label: event.currentTarget.value })} />
@@ -3665,9 +6614,7 @@ class="w-start" type="datetime-local" .value=${windowConfig.startDt} - @input=${(event) => this._updateWindow(windowConfig.id, { - startDt: event.currentTarget.value - })} + @input=${(event) => this._updateWindow(windowConfig.id, { startDt: event.currentTarget.value })} />
@@ -3676,9 +6623,7 @@ class="w-end" type="datetime-local" .value=${windowConfig.endDt} - @input=${(event) => this._updateWindow(windowConfig.id, { - endDt: event.currentTarget.value - })} + @input=${(event) => this._updateWindow(windowConfig.id, { endDt: event.currentTarget.value })} />
@@ -3692,652 +6637,539 @@
- ` - )} + `)}
`; - } - } - _init$K = __decoratorStart$K(_a$K); - _windows = /* @__PURE__ */ new WeakMap(); - __nextWindowId = /* @__PURE__ */ new WeakMap(); - __decorateElement$K(_init$K, 4, "windows", _windows_dec, CardDevToolWindows, _windows); - __decorateElement$K(_init$K, 4, "_nextWindowId", __nextWindowId_dec, CardDevToolWindows, __nextWindowId); - __decoratorMetadata$K(_init$K, CardDevToolWindows); - __publicField$Z(CardDevToolWindows, "styles", styles$U); - customElements.define("dev-tool-windows", CardDevToolWindows); - var __defProp$Y = Object.defineProperty; - var __defNormalProp$Y = (obj, key, value) => key in obj ? __defProp$Y(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$Y = (obj, key, value) => __defNormalProp$Y(obj, typeof key !== "symbol" ? key + "" : key, value); - class HassRecordsDevToolCard extends HTMLElement { - constructor() { - super(); - __publicField$Y(this, "_config", {}); - __publicField$Y(this, "_hass", null); - __publicField$Y(this, "_rendered", false); - __publicField$Y(this, "_entities", []); - __publicField$Y(this, "_suppressEntityChange", false); - __publicField$Y(this, "_results", []); - this.attachShadow({ mode: "open" }); - } - setConfig(config) { - this._config = config || {}; - } - set hass(hass) { - this._hass = hass; - if (!this._rendered) { - this._render(); - this._refreshDevCount(); - } - this._updateHassOnChildren(); - } - _updateHassOnChildren() { - if (!this.shadowRoot || !this._hass) { - return; - } - const entityPicker = this.shadowRoot.getElementById( - "entity-picker" - ); - if (!entityPicker) { - return; - } - this._suppressEntityChange = true; - entityPicker.hass = this._hass; - entityPicker.value = this._entities; - setTimeout(() => { - this._suppressEntityChange = false; - }, 100); - const resultsEl = this.shadowRoot.getElementById( - "results-container" - ); - if (resultsEl) { - resultsEl.isAdmin = this._hass.user?.is_admin === true; - } - } - _render() { - this._rendered = true; - const cfg = this._config; - this.shadowRoot.innerHTML = ` - - - ${cfg.title ? `
${esc(cfg.title)}
` : ""} - -
Analyze HA History
- -
- -
- - - -
- Analyze all windows -
- - - - - -
- -
-
Dev Datapoints
-
- Currently recorded:  dev data points + } + }; + _defineProperty(CardDevToolWindows, "styles", styles$56); + __decorate([n$1({ attribute: false })], CardDevToolWindows.prototype, "windows", null); + __decorate([r$1()], CardDevToolWindows.prototype, "_nextWindowId", null); + customElements.define("dev-tool-windows", CardDevToolWindows); + //#endregion + //#region custom_components/hass_datapoints/src/cards/dev-tool/dev-tool.ts + var HassRecordsDevToolCard = class extends HTMLElement { + constructor() { + super(); + _defineProperty(this, "_config", {}); + _defineProperty(this, "_hass", null); + _defineProperty(this, "_rendered", false); + _defineProperty(this, "_entities", []); + _defineProperty(this, "_suppressEntityChange", false); + _defineProperty(this, "_results", []); + this.attachShadow({ mode: "open" }); + } + setConfig(config) { + this._config = config || {}; + } + set hass(hass) { + this._hass = hass; + if (!this._rendered) { + this._render(); + this._refreshDevCount(); + } + this._updateHassOnChildren(); + } + _updateHassOnChildren() { + if (!this.shadowRoot || !this._hass) return; + const entityPicker = this.shadowRoot.getElementById("entity-picker"); + if (!entityPicker) return; + this._suppressEntityChange = true; + entityPicker.hass = this._hass; + entityPicker.value = this._entities; + setTimeout(() => { + this._suppressEntityChange = false; + }, 100); + const resultsEl = this.shadowRoot.getElementById("results-container"); + if (resultsEl) resultsEl.isAdmin = this._hass.user?.is_admin === true; + } + _render() { + this._rendered = true; + const cfg = this._config; + if (!this.shadowRoot.adoptedStyleSheets.length) { + const sheet = new CSSStyleSheet(); + sheet.replaceSync(styles$58); + this.shadowRoot.adoptedStyleSheets = [sheet]; + } + D(b` + + ${cfg.title ? b`
${cfg.title}
` : ""} +
Analyze HA History
+
+
- Delete all dev datapoints - -
- - `; - const entityPicker = this.shadowRoot.getElementById( - "entity-picker" - ); - if (entityPicker) { - entityPicker.selector = { entity: { multiple: true } }; - entityPicker.value = []; - this._entities = []; - this._suppressEntityChange = false; - entityPicker.addEventListener("value-changed", (event) => { - if (this._suppressEntityChange) { - return; - } - const value = event.detail.value; - if (Array.isArray(value)) { - this._entities = value; - } else if (value) { - this._entities = [value]; - } else { - this._entities = []; - } - }); - } - this.shadowRoot.getElementById("analyze-btn").addEventListener( - "click", - () => { - this._analyzeHistory(); - } - ); - this.shadowRoot.getElementById("delete-dev-btn").addEventListener( - "click", - () => { - this._deleteAllDev(); - } - ); - this.shadowRoot.getElementById("results-container").addEventListener( - "dp-record-selected-request", - (event) => { - const detail = event.detail; - this._recordSelected(detail.items); - } - ); - } - _readWindowConfigs() { - const windowsEditor = this.shadowRoot.getElementById( - "windows-editor" - ); - return windowsEditor.getWindowConfigs().map((windowConfig, index) => ({ - ...windowConfig, - label: windowConfig.label.trim() || `Window ${index + 1}` - })); - } - async _analyzeHistory() { - if (!this._entities.length) { - this._showFeedback( - "analyze-status", - "err", - "Please select at least one entity." - ); - return; - } - const windowConfigs = this._readWindowConfigs(); - const button = this.shadowRoot.getElementById( - "analyze-btn" - ); - button.disabled = true; - this._results = []; - this._showFeedback( - "analyze-status", - "ok", - `Fetching history for ${windowConfigs.length} window${windowConfigs.length === 1 ? "" : "s"}…` - ); - try { - const now = /* @__PURE__ */ new Date(); - this._results = await Promise.all( - windowConfigs.map(async (windowConfig) => { - const start = windowConfig.startDt ? new Date(windowConfig.startDt) : now; - const end = windowConfig.endDt ? new Date(windowConfig.endDt) : now; - const raw = await this._hass.connection.sendMessagePromise({ - type: "history/history_during_period", - start_time: start.toISOString(), - end_time: end.toISOString(), - entity_ids: this._entities, - include_start_time_state: false, - significant_changes_only: false, - no_attributes: false - }); - const changes = this._detectChanges( - raw || {} - ); - return { - id: windowConfig.id, - label: windowConfig.label, - startDt: windowConfig.startDt, - endDt: windowConfig.endDt, - changes, - selected: changes.map((_2, index) => index) - }; - }) - ); - this._renderResults(); - this._hideFeedback("analyze-status"); - } catch (err) { - this._showFeedback( - "analyze-status", - "err", - `Error: ${err.message || "Failed to fetch history"}` - ); - logger$1.error("[hass-datapoints dev-tool]", err); - } - button.disabled = false; - } - _detectChanges(histResult) { - const changes = []; - for (const [entityId, statesRaw] of Object.entries(histResult)) { - const states = statesRaw; - if (!states?.length) { - continue; - } - const domain = entityId.split(".")[0]; - const entityState = this._hass?.states?.[entityId]; - const deviceClass = entityState?.attributes?.device_class || ""; - const friendlyName = entityState?.attributes?.friendly_name || entityId; - const unit = entityState?.attributes?.unit_of_measurement || ""; - for (let i2 = 0; i2 < states.length; i2 += 1) { - const state = states[i2]; - const previous = i2 > 0 ? states[i2 - 1] : null; - const currentValue = state.s; - const previousValue = previous?.s ?? null; - if (currentValue === "unavailable" || currentValue === "unknown") { - continue; - } - if (previous && previousValue === currentValue) { - if (domain !== "climate") { - continue; - } - } - const timestampRaw = state.lc ?? state.lu; - const timestamp = timestampRaw != null ? new Date(timestampRaw * 1e3).toISOString() : (/* @__PURE__ */ new Date()).toISOString(); - let message = null; - let icon = "mdi:bookmark"; - let color = "#03a9f4"; - if (domain === "binary_sensor" || domain === "input_boolean") { - message = `${friendlyName}: ${this._binaryLabel(deviceClass, currentValue)}`; - icon = currentValue === "on" ? "mdi:toggle-switch" : "mdi:toggle-switch-off"; - color = currentValue === "on" ? "#4caf50" : "#9e9e9e"; - } else if (domain === "switch") { - message = `${friendlyName}: turned ${currentValue === "on" ? "on" : "off"}`; - icon = currentValue === "on" ? "mdi:power-plug" : "mdi:power-plug-off"; - color = currentValue === "on" ? "#ff9800" : "#9e9e9e"; - } else if (domain === "light") { - message = `${friendlyName}: ${currentValue === "on" ? "on" : "off"}`; - icon = currentValue === "on" ? "mdi:lightbulb" : "mdi:lightbulb-off"; - color = currentValue === "on" ? "#ffee58" : "#9e9e9e"; - } else if (domain === "cover") { - const labels = { - open: "opened", - closed: "closed", - opening: "opening", - closing: "closing" - }; - if (!labels[currentValue]) { - continue; - } - message = `${friendlyName}: ${labels[currentValue]}`; - icon = currentValue === "open" || currentValue === "opening" ? "mdi:garage-open" : "mdi:garage"; - color = currentValue === "open" ? "#4caf50" : "#795548"; - } else if (domain === "climate") { - const stateAttributes = state.a; - const previousAttributes = previous?.a; - const currentTemperature = stateAttributes?.temperature; - const previousTemperature = previousAttributes?.temperature; - if (currentTemperature != null && currentTemperature !== previousTemperature) { - const temperatureUnit = stateAttributes?.temperature_unit || unit || "°"; - message = `${friendlyName}: setpoint → ${currentTemperature}${temperatureUnit}`; - icon = "mdi:thermostat"; - color = "#ff5722"; - } else if (!previous || previousValue !== currentValue) { - const modes = { - heat: "heating", - cool: "cooling", - auto: "auto", - off: "off", - heat_cool: "heat/cool", - fan_only: "fan only", - dry: "dry" - }; - message = `${friendlyName}: mode → ${modes[currentValue] || currentValue}`; - icon = "mdi:thermostat"; - color = "#ff5722"; - } else { - continue; - } - } else if (domain === "sensor") { - const currentNumber = parseFloat(currentValue); - const previousNumber = previousValue != null ? parseFloat(previousValue) : Number.NaN; - if (Number.isNaN(currentNumber)) { - continue; - } - if (!Number.isNaN(previousNumber) && Math.abs(currentNumber - previousNumber) < 0.5) { - continue; - } - message = `${friendlyName}: ${currentValue}${unit}`; - icon = "mdi:gauge"; - color = "#2196f3"; - } else if (domain === "input_number" || domain === "number") { - const currentNumber = parseFloat(currentValue); - const previousNumber = previousValue != null ? parseFloat(previousValue) : Number.NaN; - if (Number.isNaN(currentNumber)) { - continue; - } - if (!Number.isNaN(previousNumber) && currentNumber === previousNumber) { - continue; - } - message = `${friendlyName}: → ${currentValue}${unit}`; - icon = "mdi:numeric"; - color = "#9c27b0"; - } else if (domain === "input_select" || domain === "select") { - if (!previous || previousValue === currentValue) { - continue; - } - message = `${friendlyName}: → ${currentValue}`; - icon = "mdi:form-select"; - color = "#009688"; - } else { - if (!previous || previousValue === currentValue) { - continue; - } - message = `${friendlyName}: ${previousValue} → ${currentValue}`; - icon = "mdi:swap-horizontal"; - color = "#607d8b"; - } - if (!message) { - continue; - } - changes.push({ - timestamp, - message, - entity_id: entityId, - icon, - color - }); - } - } - changes.sort((a2, b2) => a2.timestamp < b2.timestamp ? -1 : 1); - return changes; - } - _binaryLabel(deviceClass, state) { - const on = state === "on"; - const map = { - door: ["opened", "closed"], - window: ["opened", "closed"], - garage_door: ["opened", "closed"], - opening: ["opened", "closed"], - lock: ["locked", "unlocked"], - motion: ["motion detected", "motion cleared"], - occupancy: ["occupied", "vacant"], - presence: ["home", "away"], - vibration: ["vibrating", "still"], - plug: ["plugged in", "unplugged"], - outlet: ["on", "off"], - smoke: ["smoke detected", "smoke cleared"], - moisture: ["wet", "dry"], - running: ["running", "stopped"], - connectivity: ["connected", "disconnected"], - power: ["on", "off"], - battery_charging: ["charging", "not charging"], - battery: ["low battery", "battery normal"], - cold: ["cold", "temperature normal"], - heat: ["heat", "temperature normal"], - light: ["light detected", "dark"], - sound: ["sound detected", "quiet"] - }; - const pair = map[deviceClass]; - if (pair) { - return on ? pair[0] : pair[1]; - } - return on ? "on" : "off"; - } - _renderResults() { - const resultsContainer = this.shadowRoot.getElementById( - "results-container" - ); - resultsContainer.results = [...this._results]; - resultsContainer.statusKind = ""; - resultsContainer.statusText = ""; - resultsContainer.statusVisible = false; - } - async _recordSelected(items) { - if (!items.length) { - this._showResultsStatus("err", "No items selected."); - return; - } - this._showResultsStatus( - "ok", - `Recording ${items.length} data point${items.length === 1 ? "" : "s"}…` - ); - const results = await Promise.allSettled( - items.map( - (item) => this._hass.callService(DOMAIN, "record", { - message: item.message, - entity_ids: [item.entity_id], - icon: item.icon, - color: item.color, - date: item.timestamp, - dev: true - }) - ) - ); - const ok = results.filter((result) => result.status === "fulfilled").length; - const fail = results.filter( - (result) => result.status === "rejected" - ).length; - if (fail) { - this._showResultsStatus("err", `Recorded ${ok}, failed ${fail}.`); - } else { - this._showResultsStatus( - "ok", - `Recorded ${ok} dev data point${ok === 1 ? "" : "s"}!` - ); - } - await this._refreshDevCount(); - window.dispatchEvent(new CustomEvent("hass-datapoints-event-recorded")); - } - async _deleteAllDev() { - const devCountEl = this.shadowRoot.getElementById("dev-count"); - const count = parseInt(devCountEl?.textContent ?? "0", 10) || 0; - if (count === 0) { - this._showFeedback( - "delete-status", - "err", - "No dev datapoints to delete." - ); - return; - } - const confirmed = await confirmDestructiveAction(this, { - title: "Delete dev datapoints", - message: `Delete all ${count} dev data point${count === 1 ? "" : "s"}?`, - confirmLabel: "Delete all" - }); - if (!confirmed) { - return; - } - const button = this.shadowRoot.getElementById( - "delete-dev-btn" - ); - button.disabled = true; - try { - const result = await this._hass.connection.sendMessagePromise({ - type: `${DOMAIN}/events/delete_dev` - }); - const deleted = result.deleted; - this._showFeedback( - "delete-status", - "ok", - `Deleted ${deleted} dev data point${deleted === 1 ? "" : "s"}.` - ); - await this._refreshDevCount(); - window.dispatchEvent(new CustomEvent("hass-datapoints-event-recorded")); - } catch (err) { - this._showFeedback( - "delete-status", - "err", - `Error: ${err.message || "failed"}` - ); - } - button.disabled = false; - } - async _refreshDevCount() { - try { - const result = await this._hass.connection.sendMessagePromise({ - type: `${DOMAIN}/events` - }); - const events = result.events || []; - const count = events.filter((event) => event.dev).length; - const countEl = this.shadowRoot.getElementById("dev-count"); - const pluralEl = this.shadowRoot.getElementById("dev-count-plural"); - if (countEl) { - countEl.textContent = String(count); - } - if (pluralEl) { - pluralEl.textContent = count === 1 ? "" : "s"; - } - } catch (error) { - logger$1.warn("[hass-datapoints dev-tool] refresh dev count failed", error); - } - } - _showFeedback(id, kind, text) { - const el = this.shadowRoot.getElementById(id); - if (!el) { - return; - } - el.kind = kind; - el.text = text; - el.visible = true; - } - _hideFeedback(id) { - const el = this.shadowRoot.getElementById(id); - if (!el) { - return; - } - el.visible = false; - } - _showResultsStatus(kind, text) { - const el = this.shadowRoot.getElementById( - "results-container" - ); - el.statusKind = kind; - el.statusText = text; - el.statusVisible = true; - } - static getStubConfig() { - return { title: "Dev Tool" }; - } - static getConfigElement() { - return document.createElement("hass-datapoints-dev-tool-card-editor"); - } - } - var __defProp$X = Object.defineProperty; - var __defNormalProp$X = (obj, key, value) => key in obj ? __defProp$X(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$X = (obj, key, value) => __defNormalProp$X(obj, typeof key !== "symbol" ? key + "" : key, value); - class ChartCardBase extends i$2 { - constructor() { - super(...arguments); - __publicField$X(this, "_hass"); - __publicField$X(this, "_config", {}); - __publicField$X(this, "_loadRequestId", 0); - __publicField$X(this, "_lastDrawArgs", []); - __publicField$X(this, "_previousSeriesEndpoints", /* @__PURE__ */ new Map()); - __publicField$X(this, "_unsubscribe", null); - __publicField$X(this, "_resizeObserver", null); - __publicField$X(this, "_loadRaf", null); - __publicField$X(this, "_loadInFlight", false); - __publicField$X(this, "_hasStartedInitialLoad", false); - __publicField$X(this, "_windowListener", null); - __publicField$X(this, "_initialized", false); - } - // ── Lovelace API ────────────────────────────────────────────────────────── - setConfig(config) { - this._config = config ?? {}; - this.requestUpdate(); - } - set hass(hass) { - this._hass = hass; - this.requestUpdate(); - if (this._hasStartedInitialLoad) { - this._scheduleLoad(); - } - } - get hass() { - return this._hass; - } - // ── Lifecycle ───────────────────────────────────────────────────────────── - connectedCallback() { - super.connectedCallback(); - if (this._initialized) { - if (!this._unsubscribe || !this._windowListener) { - this._setupAutoRefresh(); - } - this._setupResizeObserver(); - } - if (this._hass) { - this._scheduleLoad(); - } - } - updated() { - if (!this._initialized && this._hass) { - this._initialized = true; - this._setupAutoRefresh(); - this._setupResizeObserver(); - this._scheduleLoad(); - } - } - disconnectedCallback() { - super.disconnectedCallback(); - this._cleanup(); - } - // ── Scheduling ──────────────────────────────────────────────────────────── - _scheduleLoad() { - if (!this._hass || this._loadRaf !== null || this._loadInFlight) return; - this._loadRaf = window.requestAnimationFrame(() => { - this._loadRaf = null; - if (!this._hass || !this.isConnected || this._loadInFlight) return; - this._hasStartedInitialLoad = true; - this._loadInFlight = true; - Promise.resolve(this._load()).catch((err) => { - logger$1.error("[hass-datapoints chart-base] load failed", err); - }).finally(() => { - this._loadInFlight = false; - }); - }); - } - // ── Setup helpers ───────────────────────────────────────────────────────── - _setupAutoRefresh() { - if (!this._hass) return; - if (this._unsubscribe || this._windowListener) return; - this._hass.connection.subscribeEvents(() => { - this._scheduleLoad(); - }, `${DOMAIN}_event_recorded`).then((unsub) => { - this._unsubscribe = unsub; - }).catch(() => { - }); - this._windowListener = () => { - this._scheduleLoad(); - }; - window.addEventListener( - "hass-datapoints-event-recorded", - this._windowListener - ); - } - _setupResizeObserver() { - if (this._resizeObserver) return; - const wrap = this.shadowRoot?.querySelector(".chart-wrap") ?? this.shadowRoot?.querySelector("hass-datapoints-history-chart"); - if (!wrap || !window.ResizeObserver) return; - this._resizeObserver = new ResizeObserver(() => { - if (this._lastDrawArgs.length) { - this._drawChart(...this._lastDrawArgs); - } - }); - this._resizeObserver.observe(wrap); - } - // ── Cleanup ─────────────────────────────────────────────────────────────── - _cleanup() { - if (this._loadRaf !== null) { - window.cancelAnimationFrame(this._loadRaf); - this._loadRaf = null; - } - if (this._unsubscribe) { - this._unsubscribe(); - this._unsubscribe = null; - } - if (this._windowListener) { - window.removeEventListener( - "hass-datapoints-event-recorded", - this._windowListener - ); - this._windowListener = null; - } - if (this._resizeObserver) { - this._resizeObserver.disconnect(); - this._resizeObserver = null; - } - } - // ── Static API ──────────────────────────────────────────────────────────── - static getStubConfig() { - return { title: "" }; - } - } - const styles$T = i$5` + +
+ Analyze all windows +
+ + +
+
+
Dev Datapoints
+
+ Currently recorded:  dev data points +
+ Delete all dev datapoints + +
+ + `, this.shadowRoot); + const entityPicker = this.shadowRoot.getElementById("entity-picker"); + if (entityPicker) { + entityPicker.selector = { entity: { multiple: true } }; + entityPicker.value = []; + this._entities = []; + this._suppressEntityChange = false; + entityPicker.addEventListener("value-changed", (event) => { + if (this._suppressEntityChange) return; + const value = event.detail.value; + if (Array.isArray(value)) this._entities = value; + else if (value) this._entities = [value]; + else this._entities = []; + }); + } + this.shadowRoot.getElementById("analyze-btn").addEventListener("click", () => { + this._analyzeHistory(); + }); + this.shadowRoot.getElementById("delete-dev-btn").addEventListener("click", () => { + this._deleteAllDev(); + }); + this.shadowRoot.getElementById("results-container").addEventListener("dp-record-selected-request", (event) => { + const detail = event.detail; + this._recordSelected(detail.items); + }); + } + _readWindowConfigs() { + return this.shadowRoot.getElementById("windows-editor").getWindowConfigs().map((windowConfig, index) => ({ + ...windowConfig, + label: windowConfig.label.trim() || `Window ${index + 1}` + })); + } + async _analyzeHistory() { + if (!this._entities.length) { + this._showFeedback("analyze-status", "err", "Please select at least one entity."); + return; + } + const windowConfigs = this._readWindowConfigs(); + const button = this.shadowRoot.getElementById("analyze-btn"); + button.disabled = true; + this._results = []; + this._showFeedback("analyze-status", "ok", `Fetching history for ${windowConfigs.length} window${windowConfigs.length === 1 ? "" : "s"}…`); + try { + const now = /* @__PURE__ */ new Date(); + this._results = await Promise.all(windowConfigs.map(async (windowConfig) => { + const start = windowConfig.startDt ? new Date(windowConfig.startDt) : now; + const end = windowConfig.endDt ? new Date(windowConfig.endDt) : now; + const raw = await this._hass.connection.sendMessagePromise({ + type: "history/history_during_period", + start_time: start.toISOString(), + end_time: end.toISOString(), + entity_ids: this._entities, + include_start_time_state: false, + significant_changes_only: false, + no_attributes: false + }); + const changes = this._detectChanges(raw || {}); + return { + id: windowConfig.id, + label: windowConfig.label, + startDt: windowConfig.startDt, + endDt: windowConfig.endDt, + changes, + selected: changes.map((_, index) => index) + }; + })); + this._renderResults(); + this._hideFeedback("analyze-status"); + } catch (err) { + this._showFeedback("analyze-status", "err", `Error: ${err.message || "Failed to fetch history"}`); + logger$1.error("[hass-datapoints dev-tool]", err); + } + button.disabled = false; + } + _detectChanges(histResult) { + const changes = []; + for (const [entityId, statesRaw] of Object.entries(histResult)) { + const states = statesRaw; + if (!states?.length) continue; + const domain = entityId.split(".")[0]; + const entityState = this._hass?.states?.[entityId]; + const deviceClass = entityState?.attributes?.device_class || ""; + const friendlyName = entityState?.attributes?.friendly_name || entityId; + const unit = entityState?.attributes?.unit_of_measurement || ""; + for (let i = 0; i < states.length; i += 1) { + const state = states[i]; + const previous = i > 0 ? states[i - 1] : null; + const currentValue = state.s; + const previousValue = previous?.s ?? null; + if (currentValue === "unavailable" || currentValue === "unknown") continue; + if (previous && previousValue === currentValue) { + if (domain !== "climate") continue; + } + const timestampRaw = state.lc ?? state.lu; + const timestamp = timestampRaw != null ? (/* @__PURE__ */ new Date(timestampRaw * 1e3)).toISOString() : (/* @__PURE__ */ new Date()).toISOString(); + let message; + let icon; + let color; + if (domain === "binary_sensor" || domain === "input_boolean") { + message = `${friendlyName}: ${this._binaryLabel(deviceClass, currentValue)}`; + icon = currentValue === "on" ? "mdi:toggle-switch" : "mdi:toggle-switch-off"; + color = currentValue === "on" ? "#4caf50" : "#9e9e9e"; + } else if (domain === "switch") { + message = `${friendlyName}: turned ${currentValue === "on" ? "on" : "off"}`; + icon = currentValue === "on" ? "mdi:power-plug" : "mdi:power-plug-off"; + color = currentValue === "on" ? "#ff9800" : "#9e9e9e"; + } else if (domain === "light") { + message = `${friendlyName}: ${currentValue === "on" ? "on" : "off"}`; + icon = currentValue === "on" ? "mdi:lightbulb" : "mdi:lightbulb-off"; + color = currentValue === "on" ? "#ffee58" : "#9e9e9e"; + } else if (domain === "cover") { + const labels = { + open: "opened", + closed: "closed", + opening: "opening", + closing: "closing" + }; + if (!labels[currentValue]) continue; + message = `${friendlyName}: ${labels[currentValue]}`; + icon = currentValue === "open" || currentValue === "opening" ? "mdi:garage-open" : "mdi:garage"; + color = currentValue === "open" ? "#4caf50" : "#795548"; + } else if (domain === "climate") { + const stateAttributes = state.a; + const previousAttributes = previous?.a; + const currentTemperature = stateAttributes?.temperature; + const previousTemperature = previousAttributes?.temperature; + if (currentTemperature != null && currentTemperature !== previousTemperature) { + message = `${friendlyName}: setpoint → ${currentTemperature}${stateAttributes?.temperature_unit || unit || "°"}`; + icon = "mdi:thermostat"; + color = "#ff5722"; + } else if (!previous || previousValue !== currentValue) { + message = `${friendlyName}: mode → ${{ + heat: "heating", + cool: "cooling", + auto: "auto", + off: "off", + heat_cool: "heat/cool", + fan_only: "fan only", + dry: "dry" + }[currentValue] || currentValue}`; + icon = "mdi:thermostat"; + color = "#ff5722"; + } else continue; + } else if (domain === "sensor") { + const currentNumber = parseFloat(currentValue); + const previousNumber = previousValue != null ? parseFloat(previousValue) : NaN; + if (Number.isNaN(currentNumber)) continue; + if (!Number.isNaN(previousNumber) && Math.abs(currentNumber - previousNumber) < .5) continue; + message = `${friendlyName}: ${currentValue}${unit}`; + icon = "mdi:gauge"; + color = "#2196f3"; + } else if (domain === "input_number" || domain === "number") { + const currentNumber = parseFloat(currentValue); + const previousNumber = previousValue != null ? parseFloat(previousValue) : NaN; + if (Number.isNaN(currentNumber)) continue; + if (!Number.isNaN(previousNumber) && currentNumber === previousNumber) continue; + message = `${friendlyName}: → ${currentValue}${unit}`; + icon = "mdi:numeric"; + color = "#9c27b0"; + } else if (domain === "input_select" || domain === "select") { + if (!previous || previousValue === currentValue) continue; + message = `${friendlyName}: → ${currentValue}`; + icon = "mdi:form-select"; + color = "#009688"; + } else { + if (!previous || previousValue === currentValue) continue; + message = `${friendlyName}: ${previousValue} → ${currentValue}`; + icon = "mdi:swap-horizontal"; + color = "#607d8b"; + } + if (!message) continue; + changes.push({ + timestamp, + message, + entity_id: entityId, + icon, + color + }); + } + } + changes.sort((a, b) => a.timestamp < b.timestamp ? -1 : 1); + return changes; + } + _binaryLabel(deviceClass, state) { + const on = state === "on"; + const pair = { + door: ["opened", "closed"], + window: ["opened", "closed"], + garage_door: ["opened", "closed"], + opening: ["opened", "closed"], + lock: ["locked", "unlocked"], + motion: ["motion detected", "motion cleared"], + occupancy: ["occupied", "vacant"], + presence: ["home", "away"], + vibration: ["vibrating", "still"], + plug: ["plugged in", "unplugged"], + outlet: ["on", "off"], + smoke: ["smoke detected", "smoke cleared"], + moisture: ["wet", "dry"], + running: ["running", "stopped"], + connectivity: ["connected", "disconnected"], + power: ["on", "off"], + battery_charging: ["charging", "not charging"], + battery: ["low battery", "battery normal"], + cold: ["cold", "temperature normal"], + heat: ["heat", "temperature normal"], + light: ["light detected", "dark"], + sound: ["sound detected", "quiet"] + }[deviceClass]; + if (pair) return on ? pair[0] : pair[1]; + return on ? "on" : "off"; + } + _renderResults() { + const resultsContainer = this.shadowRoot.getElementById("results-container"); + resultsContainer.results = [...this._results]; + resultsContainer.statusKind = ""; + resultsContainer.statusText = ""; + resultsContainer.statusVisible = false; + } + async _recordSelected(items) { + if (!items.length) { + this._showResultsStatus("err", "No items selected."); + return; + } + this._showResultsStatus("ok", `Recording ${items.length} data point${items.length === 1 ? "" : "s"}…`); + const results = await Promise.allSettled(items.map((item) => this._hass.callService(DOMAIN, "record", { + message: item.message, + entity_ids: [item.entity_id], + icon: item.icon, + color: item.color, + date: item.timestamp, + dev: true + }))); + const ok = results.filter((result) => result.status === "fulfilled").length; + const fail = results.filter((result) => result.status === "rejected").length; + if (fail) this._showResultsStatus("err", `Recorded ${ok}, failed ${fail}.`); + else this._showResultsStatus("ok", `Recorded ${ok} dev data point${ok === 1 ? "" : "s"}!`); + await this._refreshDevCount(); + window.dispatchEvent(new CustomEvent("hass-datapoints-event-recorded")); + } + async _deleteAllDev() { + const devCountEl = this.shadowRoot.getElementById("dev-count"); + const count = parseInt(devCountEl?.textContent ?? "0", 10) || 0; + if (count === 0) { + this._showFeedback("delete-status", "err", "No dev datapoints to delete."); + return; + } + if (!await confirmDestructiveAction(this, { + title: "Delete dev datapoints", + message: `Delete all ${count} dev data point${count === 1 ? "" : "s"}?`, + confirmLabel: "Delete all" + })) return; + const button = this.shadowRoot.getElementById("delete-dev-btn"); + button.disabled = true; + try { + const deleted = (await this._hass.connection.sendMessagePromise({ type: `${DOMAIN}/events/delete_dev` })).deleted; + this._showFeedback("delete-status", "ok", `Deleted ${deleted} dev data point${deleted === 1 ? "" : "s"}.`); + await this._refreshDevCount(); + window.dispatchEvent(new CustomEvent("hass-datapoints-event-recorded")); + } catch (err) { + this._showFeedback("delete-status", "err", `Error: ${err.message || "failed"}`); + } + button.disabled = false; + } + async _refreshDevCount() { + try { + const count = ((await this._hass.connection.sendMessagePromise({ type: `hass_datapoints/events` })).events || []).filter((event) => event.dev).length; + const countEl = this.shadowRoot.getElementById("dev-count"); + const pluralEl = this.shadowRoot.getElementById("dev-count-plural"); + if (countEl) countEl.textContent = String(count); + if (pluralEl) pluralEl.textContent = count === 1 ? "" : "s"; + } catch (error) { + logger$1.warn("[hass-datapoints dev-tool] refresh dev count failed", error); + } + } + _showFeedback(id, kind, text) { + const el = this.shadowRoot.getElementById(id); + if (!el) return; + el.kind = kind; + el.text = text; + el.visible = true; + } + _hideFeedback(id) { + const el = this.shadowRoot.getElementById(id); + if (!el) return; + el.visible = false; + } + _showResultsStatus(kind, text) { + const el = this.shadowRoot.getElementById("results-container"); + el.statusKind = kind; + el.statusText = text; + el.statusVisible = true; + } + static getStubConfig() { + return { title: "Dev Tool" }; + } + static getConfigElement() { + return document.createElement("hass-datapoints-dev-tool-card-editor"); + } + }; + //#endregion + //#region custom_components/hass_datapoints/src/charts/base/chart-card-base.ts + /** + * ChartCardBase – shared LitElement base class for history and statistics + * chart cards. + * + * Handles: + * • hass setter + requestUpdate plumbing + * • Auto-refresh via HA domain event subscription + window custom event + * • ResizeObserver to redraw the chart when the container resizes + * • _scheduleLoad() — rAF-deferred load with in-flight guard + * + * Subclasses must implement: + * • render() — Lit template (must include a `.chart-wrap` element) + * • _load() — async data fetch + draw + * • _drawChart() — (re-)draw the canvas chart; called by ResizeObserver + */ + var ChartCardBase = class extends i$2 { + constructor(..._args) { + super(..._args); + _defineProperty(this, "_hass", void 0); + _defineProperty(this, "_config", {}); + _defineProperty(this, "_loadRequestId", 0); + _defineProperty( + this, + /** Subclasses store their last draw call arguments here so the + * ResizeObserver can re-invoke _drawChart with the same data. */ + "_lastDrawArgs", + [] + ); + _defineProperty( + this, + /** Tracks the last data point per series to detect new points for blip animations. + * Map of entityId → { t: timestamp, v: value } */ + "_previousSeriesEndpoints", + /* @__PURE__ */ new Map() + ); + _defineProperty(this, "_unsubscribe", null); + _defineProperty(this, "_resizeObserver", null); + _defineProperty(this, "_loadRaf", null); + _defineProperty(this, "_loadInFlight", false); + _defineProperty(this, "_hasStartedInitialLoad", false); + _defineProperty(this, "_windowListener", null); + _defineProperty( + this, + /** True once _setupAutoRefresh and _setupResizeObserver have been called. */ + "_initialized", + false + ); + } + setConfig(config) { + this._config = config ?? {}; + this.requestUpdate(); + } + set hass(hass) { + this._hass = hass; + this.requestUpdate(); + if (this._hasStartedInitialLoad) this._scheduleLoad(); + } + get hass() { + return this._hass; + } + connectedCallback() { + super.connectedCallback(); + if (this._initialized) { + if (!this._unsubscribe || !this._windowListener) this._setupAutoRefresh(); + this._setupResizeObserver(); + } + if (this._hass) this._scheduleLoad(); + } + updated() { + if (!this._initialized && this._hass) { + this._initialized = true; + this._setupAutoRefresh(); + this._setupResizeObserver(); + this._scheduleLoad(); + } + } + disconnectedCallback() { + super.disconnectedCallback(); + this._cleanup(); + } + _scheduleLoad() { + if (!this._hass || this._loadRaf !== null || this._loadInFlight) return; + this._loadRaf = window.requestAnimationFrame(() => { + this._loadRaf = null; + if (!this._hass || !this.isConnected || this._loadInFlight) return; + this._hasStartedInitialLoad = true; + this._loadInFlight = true; + Promise.resolve(this._load()).catch((err) => { + logger$1.error("[hass-datapoints chart-base] load failed", err); + }).finally(() => { + this._loadInFlight = false; + }); + }); + } + _setupAutoRefresh() { + if (!this._hass) return; + if (this._unsubscribe || this._windowListener) return; + this._hass.connection.subscribeEvents(() => { + this._scheduleLoad(); + }, `${DOMAIN}_event_recorded`).then((unsub) => { + this._unsubscribe = unsub; + }).catch(() => {}); + this._windowListener = () => { + this._scheduleLoad(); + }; + window.addEventListener("hass-datapoints-event-recorded", this._windowListener); + } + _setupResizeObserver() { + if (this._resizeObserver) return; + const wrap = this.shadowRoot?.querySelector(".chart-wrap") ?? this.shadowRoot?.querySelector("hass-datapoints-history-chart"); + if (!wrap || !window.ResizeObserver) return; + this._resizeObserver = new ResizeObserver(() => { + if (this._lastDrawArgs.length) this._drawChart(...this._lastDrawArgs); + }); + this._resizeObserver.observe(wrap); + } + _cleanup() { + if (this._loadRaf !== null) { + window.cancelAnimationFrame(this._loadRaf); + this._loadRaf = null; + } + if (this._unsubscribe) { + this._unsubscribe(); + this._unsubscribe = null; + } + if (this._windowListener) { + window.removeEventListener("hass-datapoints-event-recorded", this._windowListener); + this._windowListener = null; + } + if (this._resizeObserver) { + this._resizeObserver.disconnect(); + this._resizeObserver = null; + } + } + static getStubConfig() { + return { title: "" }; + } + }; + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/history.styles.ts + var styles$55 = i$5` :host { display: block; height: 100%; @@ -4383,4435 +7215,3665 @@ min-height: 0; } `; - const SAMPLE_INTERVAL_MS = { - "1s": 1e3, - "5s": 5e3, - "10s": 1e4, - "15s": 15e3, - "30s": 3e4, - "1m": 6e4, - "2m": 2 * 6e4, - "5m": 5 * 6e4, - "10m": 10 * 6e4, - "15m": 15 * 6e4, - "30m": 30 * 6e4, - "1h": 60 * 6e4, - "2h": 2 * 60 * 6e4, - "3h": 3 * 60 * 6e4, - "4h": 4 * 60 * 6e4, - "6h": 6 * 60 * 6e4, - "12h": 12 * 60 * 6e4, - "24h": 24 * 60 * 6e4 - }; - function binaryOnLabel(deviceClass) { - const labels = { - battery: "low", - battery_charging: "charging", - carbon_monoxide: "detected", - cold: "cold", - connectivity: "connected", - door: "open", - garage_door: "open", - gas: "detected", - heat: "hot", - lock: "unlocked", - moisture: "wet", - motion: "motion", - moving: "moving", - occupancy: "occupied", - opening: "open", - plug: "plugged in", - power: "power", - presence: "present", - problem: "problem", - running: "running", - safety: "unsafe", - smoke: "smoke", - sound: "sound", - tamper: "tampered", - update: "update available", - vibration: "vibration", - window: "open" - }; - return labels[deviceClass] || "on"; - } - function binaryOffLabel(deviceClass) { - const labels = { - battery: "normal", - battery_charging: "not charging", - carbon_monoxide: "clear", - cold: "normal", - connectivity: "disconnected", - door: "closed", - garage_door: "closed", - gas: "clear", - heat: "normal", - lock: "locked", - moisture: "dry", - motion: "clear", - moving: "still", - occupancy: "clear", - opening: "closed", - plug: "unplugged", - power: "off", - presence: "away", - problem: "ok", - running: "idle", - safety: "safe", - smoke: "clear", - sound: "quiet", - tamper: "clear", - update: "up to date", - vibration: "still", - window: "closed" - }; - return labels[deviceClass] || "off"; - } - function normalizeNumericHistory(_entityId2, histStates) { - return (Array.isArray(histStates) ? histStates : []).map((state) => { - const value = parseFloat(state?.s ?? ""); - if (Number.isNaN(value)) { - return null; - } - const rawTimestamp = state?.lu ?? state?.lc ?? state?.last_changed ?? state?.last_updated; - const timeSec = typeof rawTimestamp === "number" ? rawTimestamp : new Date(rawTimestamp || 0).getTime() / 1e3; - if (!Number.isFinite(timeSec)) { - return null; - } - return { - lu: Math.round(timeSec * 1e3) / 1e3, - s: String(value) - }; - }).filter((entry) => entry !== null); - } - function getHistoryStatesForEntity$1(histResult, entityId, entityIds = []) { - if (!histResult) { - return []; - } - const result = histResult; - if (Array.isArray(result[entityId])) { - return result[entityId]; - } - if (Array.isArray(histResult)) { - const entries = histResult; - const entityIndex = entityIds.indexOf(entityId); - if (entityIndex >= 0 && Array.isArray(entries[entityIndex])) { - return entries[entityIndex]; - } - if (entries.every( - (entry) => entry && typeof entry === "object" && !Array.isArray(entry) - )) { - return entries.filter( - (entry) => entry.entity_id === entityId - ); - } - } - if (histResult && typeof histResult === "object") { - const wrapped = histResult; - if (Array.isArray(wrapped.result?.[entityId])) { - return wrapped.result[entityId]; - } - if (Array.isArray(wrapped.result)) { - if (wrapped.result.every( - (entry) => entry && typeof entry === "object" && !Array.isArray(entry) - )) { - return wrapped.result.filter( - (entry) => entry.entity_id === entityId - ); - } - const entityIndex = entityIds.indexOf(entityId); - if (entityIndex >= 0 && Array.isArray(wrapped.result[entityIndex])) { - return wrapped.result[entityIndex]; - } - } - } - return []; - } - function normalizeStatisticsHistory(entityId, statsData) { - const statEntries = statsData && typeof statsData === "object" ? statsData[entityId] ?? [] : []; - return (Array.isArray(statEntries) ? statEntries : []).map((entry) => { - const value = Number(entry?.mean); - if (!Number.isFinite(value)) { - return null; - } - const rawTimestamp = entry?.start; - let timestamp; - if (typeof rawTimestamp === "number") { - if (rawTimestamp > 1e11) { - timestamp = rawTimestamp; - } else { - timestamp = rawTimestamp * 1e3; - } - } else { - timestamp = new Date(rawTimestamp).getTime(); - } - if (!Number.isFinite(timestamp)) { - return null; - } - return { - lu: Math.round(timestamp) / 1e3, - s: String(value) - }; - }).filter((entry) => entry !== null).sort((a2, b2) => a2.lu - b2.lu); - } - function mergeNumericHistoryWithStatistics(histPts, statsPts) { - const raw = Array.isArray(histPts) ? histPts : []; - const stats = Array.isArray(statsPts) ? statsPts : []; - if (!raw.length) { - return [...stats]; - } - if (!stats.length) { - return [...raw]; - } - const firstRawMs = raw[0].lu * 1e3; - const lastRawMs = raw[raw.length - 1].lu * 1e3; - const merged2 = [ - ...stats.filter((entry) => { - const timeMs = entry.lu * 1e3; - return timeMs < firstRawMs || timeMs > lastRawMs; - }), - ...raw - ]; - merged2.sort((a2, b2) => a2.lu - b2.lu); - return merged2; - } - function getAxisValueExtent(allValues) { - let min = Infinity; - let max = -Infinity; - for (const value of allValues) { - const numeric = Number(value); - if (!Number.isFinite(numeric)) { - continue; - } - if (numeric < min) { - min = numeric; - } - if (numeric > max) { - max = numeric; - } - } - if (!Number.isFinite(min) || !Number.isFinite(max)) { - return null; - } - return { min, max }; - } - function hexToRgba(hex, alpha) { - const h2 = hex.replace("#", ""); - const r2 = Number.parseInt(h2.substring(0, 2), 16); - const g2 = Number.parseInt(h2.substring(2, 4), 16); - const b2 = Number.parseInt(h2.substring(4, 6), 16); - return `rgba(${r2},${g2},${b2},${alpha})`; - } - function contrastColor(hex) { - if (!hex || typeof hex !== "string") { - return "#fff"; - } - const h2 = hex.replace("#", ""); - if (h2.length !== 6) { - return "#fff"; - } - const r2 = Number.parseInt(h2.substring(0, 2), 16) / 255; - const g2 = Number.parseInt(h2.substring(2, 4), 16) / 255; - const b2 = Number.parseInt(h2.substring(4, 6), 16) / 255; - const lin = (c2) => c2 <= 0.04045 ? c2 / 12.92 : ((c2 + 0.055) / 1.055) ** 2.4; - const luminance = 0.2126 * lin(r2) + 0.7152 * lin(g2) + 0.0722 * lin(b2); - return luminance > 0.179 ? "#000" : "#fff"; - } - var __defProp$W = Object.defineProperty; - var __defNormalProp$W = (obj, key, value) => key in obj ? __defProp$W(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$W = (obj, key, value) => __defNormalProp$W(obj, typeof key !== "symbol" ? key + "" : key, value); - function createFallbackCanvasContext() { - return { - beginPath() { - }, - moveTo() { - }, - lineTo() { - }, - stroke() { - }, - fill() { - }, - fillRect() { - }, - clearRect() { - }, - rect() { - }, - clip() { - }, - save() { - }, - restore() { - }, - scale() { - }, - setLineDash() { - }, - fillText() { - }, - closePath() { - }, - bezierCurveTo() { - }, - arc() { - }, - measureText() { - return { width: 0 }; - } - }; - } - class ChartRenderer { - constructor(canvas, cssWidth, cssHeight) { - __publicField$W(this, "canvas"); - __publicField$W(this, "ctx"); - __publicField$W(this, "cssW"); - __publicField$W(this, "cssH"); - __publicField$W(this, "basePad"); - __publicField$W(this, "pad"); - __publicField$W(this, "labelColor"); - __publicField$W(this, "_activeAxes", []); - this.canvas = canvas; - this.ctx = canvas.getContext("2d") || createFallbackCanvasContext(); - this.cssW = cssWidth; - this.cssH = cssHeight; - this.basePad = { top: 24, right: 12, bottom: 48, left: 12 }; - this.pad = { ...this.basePad }; - this.labelColor = "rgba(214,218,224,0.92)"; - } - static get AXIS_SLOT_WIDTH() { - return 30; - } - get cw() { - return this.cssW - this.pad.left - this.pad.right; - } - get ch() { - return this.cssH - this.pad.top - this.pad.bottom; - } - xOf(t2, t0, t1) { - return this.pad.left + (t2 - t0) / (t1 - t0) * this.cw; - } - yOf(v2, vMin, vMax) { - return this.pad.top + this.ch - (v2 - vMin) / (vMax - vMin) * this.ch; - } - clear() { - this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); - } - _normalizeAxes(vMinOrAxes, vMax) { - const axisColumnWidth = ChartRenderer.AXIS_SLOT_WIDTH; - const inputAxes = Array.isArray(vMinOrAxes) ? vMinOrAxes : [ - { - key: "default", - min: vMinOrAxes, - max: vMax ?? vMinOrAxes, - side: "left", - unit: "", - color: null - } - ]; - const leftAxes = []; - const rightAxes = []; - const axes = inputAxes.map((axis, index) => { - const normalized = { - key: axis.key || `axis-${index}`, - min: axis.min, - max: axis.max, - side: axis.side === "right" ? "right" : "left", - unit: axis.unit || "", - color: axis.color || "rgba(128,128,128,0.85)" - }; - const bucket = normalized.side === "right" ? rightAxes : leftAxes; - normalized.slot = bucket.length; - bucket.push(normalized); - return normalized; - }); - this.pad = { - top: this.basePad.top, - bottom: this.basePad.bottom, - left: this.basePad.left + Math.max(1, leftAxes.length) * axisColumnWidth, - right: this.basePad.right + rightAxes.length * axisColumnWidth - }; - this._activeAxes = axes; - return axes; - } - _formatAxisTick(v2, _unit2) { - const numeric = Math.abs(v2) >= 1e3 ? `${(v2 / 1e3).toFixed(1).replace(/\.0$/, "")}k` : v2.toFixed(v2 % 1 !== 0 ? 1 : 0); - return numeric; - } - _axisLabelX(axis) { - const columnWidth = ChartRenderer.AXIS_SLOT_WIDTH; - const leftAxisX = this.pad.left; - const rightAxisX = this.pad.left + this.cw; - if (axis.side === "right") { - return rightAxisX + 10 + (axis.slot ?? 0) * columnWidth; - } - return leftAxisX - 10 - (axis.slot ?? 0) * columnWidth; - } - _formatTimeTick(t2, t0, t1, tickSpanMs = null) { - const value = new Date(t2); - const spanMs = Math.max(0, t1 - t0); - const detailSpanMs = Number.isFinite(tickSpanMs) && (tickSpanMs ?? 0) > 0 ? tickSpanMs : spanMs; - const start = new Date(t0); - const end = new Date(t1); - const sameDay = start.getFullYear() === end.getFullYear() && start.getMonth() === end.getMonth() && start.getDate() === end.getDate(); - const sameMonth = start.getFullYear() === end.getFullYear() && start.getMonth() === end.getMonth(); - if (detailSpanMs <= 2 * 60 * 60 * 1e3) { - return value.toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit" - }); - } - if (detailSpanMs <= 12 * 60 * 60 * 1e3) { - return value.toLocaleString([], { - month: "short", - day: "numeric", - hour: "2-digit", - minute: "2-digit" - }); - } - if (detailSpanMs <= 2 * 24 * 60 * 60 * 1e3) { - return value.toLocaleString([], { - month: "short", - day: "numeric", - hour: "2-digit" - }); - } - if (sameDay) { - return value.toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit" - }); - } - if (detailSpanMs <= 6 * 60 * 60 * 1e3) { - return value.toLocaleString([], { - month: "short", - day: "numeric", - hour: "2-digit", - minute: "2-digit" - }); - } - if (detailSpanMs <= 24 * 60 * 60 * 1e3) { - return value.toLocaleString([], { - month: "short", - day: "numeric", - hour: "2-digit" - }); - } - if (sameMonth && spanMs <= 14 * 24 * 60 * 60 * 1e3) { - return value.toLocaleDateString([], { day: "numeric" }); - } - if (spanMs >= 2 * 24 * 60 * 60 * 1e3) { - return value.toLocaleDateString([], { month: "short", day: "numeric" }); - } - if (spanMs >= 24 * 60 * 60 * 1e3) { - return value.toLocaleString([], { - month: "short", - day: "numeric", - hour: "2-digit", - minute: "2-digit" - }); - } - return fmtTime(value.toISOString()); - } - _niceNumber(value, round) { - if (!Number.isFinite(value) || value <= 0) { - return 1; - } - const exponent = Math.floor(Math.log10(value)); - const fraction = value / 10 ** exponent; - let niceFraction; - if (round) { - if (fraction < 1.5) { - niceFraction = 1; - } else if (fraction < 3) { - niceFraction = 2; - } else if (fraction < 7) { - niceFraction = 5; - } else { - niceFraction = 10; - } - } else if (fraction <= 1) { - niceFraction = 1; - } else if (fraction <= 2) { - niceFraction = 2; - } else if (fraction <= 5) { - niceFraction = 5; - } else { - niceFraction = 10; - } - return niceFraction * 10 ** exponent; - } - _buildNiceAxisScale(axis, tickCount) { - const rawMin = Number.isFinite(axis.min) ? axis.min : 0; - const rawMax = Number.isFinite(axis.max) ? axis.max : 1; - if (rawMin === rawMax) { - const pad = Math.abs(rawMin || 1); - const step2 = this._niceNumber(pad * 2 / Math.max(1, tickCount), true); - const niceMin2 = Math.floor((rawMin - pad) / step2) * step2; - const niceMax2 = Math.ceil((rawMax + pad) / step2) * step2; - const ticks2 = []; - for (let value = niceMin2; value <= niceMax2 + step2 * 0.5; value += step2) { - ticks2.push(Number(value.toFixed(10))); - } - return { min: niceMin2, max: niceMax2, step: step2, ticks: ticks2 }; - } - const range = this._niceNumber(rawMax - rawMin, false); - const step = this._niceNumber(range / Math.max(1, tickCount), true); - const niceMin = Math.floor(rawMin / step) * step; - const niceMax = Math.ceil(rawMax / step) * step; - const ticks = []; - for (let value = niceMin; value <= niceMax + step * 0.5; value += step) { - ticks.push(Number(value.toFixed(10))); - } - return { min: niceMin, max: niceMax, step, ticks }; - } - _alignTimeTick(timestamp, stepMs) { - const date = new Date(timestamp); - if (stepMs < 60 * 1e3) { - return Math.floor(timestamp / stepMs) * stepMs; - } - if (stepMs < 60 * 60 * 1e3) { - const minutes = Math.max(1, Math.round(stepMs / (60 * 1e3))); - date.setSeconds(0, 0); - date.setMinutes(Math.floor(date.getMinutes() / minutes) * minutes); - return date.getTime(); - } - if (stepMs < 24 * 60 * 60 * 1e3) { - const hours = Math.max(1, Math.round(stepMs / (60 * 60 * 1e3))); - date.setMinutes(0, 0, 0); - date.setHours(Math.floor(date.getHours() / hours) * hours); - return date.getTime(); - } - if (stepMs < 7 * 24 * 60 * 60 * 1e3) { - const days = Math.max(1, Math.round(stepMs / (24 * 60 * 60 * 1e3))); - date.setHours(0, 0, 0, 0); - const dayOfMonth = date.getDate(); - date.setDate(dayOfMonth - (dayOfMonth - 1) % days); - return date.getTime(); - } - if (stepMs < 30 * 24 * 60 * 60 * 1e3) { - date.setHours(0, 0, 0, 0); - const day = date.getDay(); - const offset = (day + 6) % 7; - date.setDate(date.getDate() - offset); - return date.getTime(); - } - date.setHours(0, 0, 0, 0); - date.setDate(1); - return date.getTime(); - } - _getTimeTickStep(targetStepMs) { - const candidates = [ - 5 * 60 * 1e3, - 10 * 60 * 1e3, - 15 * 60 * 1e3, - 30 * 60 * 1e3, - 60 * 60 * 1e3, - 2 * 60 * 60 * 1e3, - 3 * 60 * 60 * 1e3, - 6 * 60 * 60 * 1e3, - 12 * 60 * 60 * 1e3, - 24 * 60 * 60 * 1e3, - 2 * 24 * 60 * 60 * 1e3, - 7 * 24 * 60 * 60 * 1e3, - 14 * 24 * 60 * 60 * 1e3, - 30 * 24 * 60 * 60 * 1e3 - ]; - return candidates.find((step) => step >= targetStepMs) || candidates[candidates.length - 1]; - } - _buildTimeTicks(t0, t1) { - const approxTickCount = Math.max( - 2, - Math.min(96, Math.floor(this.cw / 120)) - ); - const stepMs = this._getTimeTickStep( - (t1 - t0) / Math.max(1, approxTickCount) - ); - const ticks = []; - let tick = this._alignTimeTick(t0, stepMs); - if (tick < t0) { - tick += stepMs; - } - while (tick <= t1) { - ticks.push(tick); - tick += stepMs; - } - if (!ticks.length) { - ticks.push(t0, t1); - } - return { ticks, stepMs }; - } - drawGrid(t0, t1, vMin, vMax, yTicks = 5, options = {}) { - const { ctx, pad } = this; - const gridColor = "rgba(128,128,128,0.15)"; - const labelColor = this.labelColor; - const fixedAxisOverlay = !!options.fixedAxisOverlay; - const hideTimeLabels = !!options.hideTimeLabels; - const axes = this._normalizeAxes(vMin, vMax); - const unitCounts = axes.reduce((counts, axis) => { - if (!axis.unit) { - return counts; - } - counts.set(axis.unit, (counts.get(axis.unit) || 0) + 1); - return counts; - }, /* @__PURE__ */ new Map()); - const axisLabelColor = (axis) => { - const duplicateUnit = !!axis?.unit && (unitCounts.get(axis.unit) || 0) > 1; - if (!duplicateUnit || !axis?.color) { - return labelColor; - } - return axis.color; - }; - axes.forEach((axis) => { - const scale = this._buildNiceAxisScale(axis, yTicks); - axis.min = scale.min; - axis.max = scale.max; - axis.ticks = scale.ticks; - }); - const primaryAxis = axes[0]; - ctx.font = "12px sans-serif"; - for (const v2 of primaryAxis.ticks || []) { - const y2 = this.yOf(v2, primaryAxis.min, primaryAxis.max); - ctx.strokeStyle = gridColor; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(pad.left, y2); - ctx.lineTo(pad.left + this.cw, y2); - ctx.stroke(); - if (!fixedAxisOverlay) { - ctx.fillStyle = axisLabelColor(primaryAxis); - ctx.textAlign = "right"; - ctx.textBaseline = "middle"; - ctx.fillText( - this._formatAxisTick(v2, primaryAxis.unit), - this._axisLabelX(primaryAxis), - y2 - ); - } - } - if (!fixedAxisOverlay) { - for (const axis of axes.slice(1)) { - for (const v2 of axis.ticks || []) { - const y2 = this.yOf(v2, axis.min, axis.max); - ctx.fillStyle = axisLabelColor(axis); - ctx.textAlign = axis.side === "right" ? "left" : "right"; - ctx.textBaseline = "middle"; - ctx.fillText( - this._formatAxisTick(v2, axis.unit), - this._axisLabelX(axis), - y2 - ); - } - } - } - if (!fixedAxisOverlay) { - for (const axis of axes) { - if (!axis.unit) { - continue; - } - ctx.fillStyle = axisLabelColor(axis); - ctx.textAlign = axis.side === "right" ? "left" : "right"; - ctx.textBaseline = "bottom"; - ctx.fillText(axis.unit, this._axisLabelX(axis), pad.top - 6); - } - } - const { ticks: timeTicks, stepMs: tickSpanMs } = this._buildTimeTicks( - t0, - t1 - ); - for (const t2 of timeTicks) { - const x2 = this.xOf(t2, t0, t1); - const label = this._formatTimeTick(t2, t0, t1, tickSpanMs); - ctx.strokeStyle = "rgba(128,128,128,0.08)"; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(x2, pad.top); - ctx.lineTo(x2, pad.top + this.ch); - ctx.stroke(); - if (!hideTimeLabels) { - ctx.fillStyle = labelColor; - ctx.textAlign = "center"; - ctx.textBaseline = "top"; - const labelWidth = ctx.measureText(label).width; - const labelX = Math.min( - pad.left + this.cw - labelWidth / 2, - Math.max(pad.left + labelWidth / 2, x2) - ); - ctx.fillText(label, labelX, pad.top + this.ch + 6); - } - } - ctx.strokeStyle = "rgba(128,128,128,0.35)"; - ctx.lineWidth = 1; - ctx.beginPath(); - if (!fixedAxisOverlay) { - ctx.moveTo(pad.left, pad.top); - ctx.lineTo(pad.left, pad.top + this.ch); - } - ctx.moveTo(pad.left, pad.top + this.ch); - ctx.lineTo(pad.left + this.cw, pad.top + this.ch); - if (axes.some((axis) => axis.side === "right") && !fixedAxisOverlay) { - ctx.moveTo(pad.left + this.cw, pad.top); - ctx.lineTo(pad.left + this.cw, pad.top + this.ch); - } - ctx.stroke(); - } - drawRowLabel(text, color = "rgba(214,218,224,0.85)") { - if (!text) { - return; - } - const { ctx, pad } = this; - ctx.save(); - ctx.font = "bold 11px sans-serif"; - ctx.fillStyle = color; - ctx.textAlign = "left"; - ctx.textBaseline = "top"; - ctx.fillText(text, pad.left + 6, pad.top + 5); - ctx.restore(); - } - /** - * Append Catmull-Rom bezier segments to the current path, starting from pts[0] - * (caller must have already called moveTo(pts[0])). Each segment passes exactly - * through the data points with tangents derived from neighbouring points. - */ - static _catmullRomPath(ctx, pts) { - for (let i2 = 1; i2 < pts.length; i2++) { - const pm1 = pts[Math.max(0, i2 - 2)]; - const p0 = pts[i2 - 1]; - const p1 = pts[i2]; - const p2 = pts[Math.min(pts.length - 1, i2 + 1)]; - const cp1x = p0[0] + (p1[0] - pm1[0]) / 6; - const cp1y = p0[1] + (p1[1] - pm1[1]) / 6; - const cp2x = p1[0] - (p2[0] - p0[0]) / 6; - const cp2y = p1[1] - (p2[1] - p0[1]) / 6; - ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p1[0], p1[1]); - } - } - static _steppedPath(ctx, pts) { - for (let i2 = 1; i2 < pts.length; i2++) { - const previous = pts[i2 - 1]; - const current = pts[i2]; - ctx.lineTo(current[0], previous[1]); - ctx.lineTo(current[0], current[1]); - } - } - drawLine(points, color, t0, t1, vMin, vMax, options = {}) { - if (!points.length) { - return; - } - const { ctx, pad } = this; - const fillAlpha = Number.isFinite(options.fillAlpha) ? options.fillAlpha : 0; - const smooth = !!options.smooth; - const stepped = options.stepped === true; - const dashed = !!options.dashed; - const dotted = !!options.dotted; - const dashPattern = Array.isArray(options.dashPattern) ? options.dashPattern.filter( - (entry) => Number.isFinite(entry) && entry > 0 - ) : null; - const lineOpacity = Number.isFinite(options.lineOpacity) ? options.lineOpacity : 1; - const lineWidth = Number.isFinite(options.lineWidth) ? options.lineWidth : 1.75; - const px = points.map(([t2, v2]) => [ - this.xOf(t2, t0, t1), - this.yOf(v2, vMin, vMax) - ]); - const bottom = pad.top + this.ch; - ctx.save(); - ctx.beginPath(); - ctx.rect(pad.left, pad.top, this.cw, this.ch); - ctx.clip(); - if (dashPattern && dashPattern.length) { - ctx.setLineDash(dashPattern); - } else if (dotted) { - ctx.setLineDash([1, 3]); - ctx.lineCap = "round"; - } else if (dashed) { - ctx.setLineDash([6, 4]); - } - if (lineOpacity < 1) { - ctx.globalAlpha = lineOpacity; - } - if (fillAlpha > 0) { - ctx.beginPath(); - ctx.moveTo(px[0][0], bottom); - ctx.lineTo(px[0][0], px[0][1]); - if (stepped) { - ChartRenderer._steppedPath(ctx, px); - } else if (smooth) { - ChartRenderer._catmullRomPath(ctx, px); - } else { - for (let i2 = 1; i2 < px.length; i2++) { - ctx.lineTo(px[i2][0], px[i2][1]); - } - } - ctx.lineTo(px[px.length - 1][0], bottom); - ctx.closePath(); - ctx.fillStyle = hexToRgba(color, fillAlpha); - ctx.fill(); - } - ctx.beginPath(); - ctx.moveTo(px[0][0], px[0][1]); - if (stepped) { - ChartRenderer._steppedPath(ctx, px); - } else if (smooth) { - ChartRenderer._catmullRomPath(ctx, px); - } else { - for (let i2 = 1; i2 < px.length; i2++) { - ctx.lineTo(px[i2][0], px[i2][1]); - } - } - ctx.strokeStyle = color; - ctx.lineWidth = lineWidth; - if (stepped) { - ctx.lineJoin = "miter"; - ctx.lineCap = "butt"; - } else { - ctx.lineJoin = "round"; - ctx.lineCap = "round"; - } - ctx.stroke(); - ctx.restore(); - } - drawBars(points, color, t0, t1, vMin, vMax, options = {}) { - if (!points.length) { - return; - } - const { ctx } = this; - const fillAlpha = Number.isFinite(options.fillAlpha) ? options.fillAlpha : 0.78; - const widthFactor = Number.isFinite(options.widthFactor) ? options.widthFactor : 0.72; - const baselineY = this.yOf(Math.max(vMin, 0), vMin, vMax); - const xs = points.map(([t2]) => this.xOf(t2, t0, t1)); - let minGap = this.cw / Math.max(points.length, 1); - for (let i2 = 1; i2 < xs.length; i2++) { - minGap = Math.min(minGap, xs[i2] - xs[i2 - 1]); - } - const barWidth = Math.max(3, Math.min(28, minGap * widthFactor)); - ctx.save(); - ctx.fillStyle = hexToRgba(color, fillAlpha); - ctx.strokeStyle = color; - ctx.lineWidth = 1; - for (let i2 = 0; i2 < points.length; i2++) { - const [, v2] = points[i2]; - const x2 = xs[i2]; - const y2 = this.yOf(v2, vMin, vMax); - const top = Math.min(y2, baselineY); - const height = Math.max(1, Math.abs(baselineY - y2)); - const left = x2 - barWidth / 2; - ctx.fillRect(left, top, barWidth, height); - } - ctx.restore(); - } - drawStateBands(spans, t0, t1, color = "#03a9f4", alpha = 0.12) { - if (!spans?.length) { - return; - } - const { ctx, pad } = this; - ctx.save(); - ctx.fillStyle = hexToRgba(color, alpha); - for (const span of spans) { - const start = Math.max(t0, span.start); - const end = Math.min(t1, span.end); - if (!(start < end)) { - continue; - } - const x0 = this.xOf(start, t0, t1); - const x1 = this.xOf(end, t0, t1); - ctx.fillRect(x0, pad.top, Math.max(1, x1 - x0), this.ch); - } - ctx.restore(); - } - drawAnnotations(events, t0, t1, options = {}) { - const { ctx, pad } = this; - const hits = []; - const showLines = options.showLines !== false; - const showMarkers = options.showMarkers !== false; - for (const event of events) { - const t2 = new Date(event.timestamp).getTime(); - if (t2 < t0 || t2 > t1) { - continue; - } - const x2 = this.xOf(t2, t0, t1); - const color = event.color || "#03a9f4"; - if (showLines) { - ctx.save(); - ctx.setLineDash([4, 3]); - ctx.strokeStyle = color; - ctx.lineWidth = 1.5; - ctx.globalAlpha = 0.75; - ctx.beginPath(); - ctx.moveTo(x2, pad.top + 8); - ctx.lineTo(x2, pad.top + this.ch); - ctx.stroke(); - ctx.restore(); - } - if (showMarkers) { - const d2 = 5; - ctx.save(); - ctx.fillStyle = color; - ctx.strokeStyle = "rgba(255,255,255,0.8)"; - ctx.lineWidth = 1.5; - ctx.beginPath(); - ctx.moveTo(x2, pad.top - d2); - ctx.lineTo(x2 + d2, pad.top); - ctx.lineTo(x2, pad.top + d2); - ctx.lineTo(x2 - d2, pad.top); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); - ctx.restore(); - } - hits.push({ event, x: x2, y: pad.top }); - } - return hits; - } - /** - * Draw sensor-style vertical annotation lines that terminate on the data line - * with a small circle marker. - */ - drawAnnotationLinesOnLine(events, allSeries, t0, t1, vMin, vMax) { - const { ctx, pad } = this; - const firstPts = allSeries.length ? allSeries[0].pts : []; - const hits = []; - for (const event of events) { - const t2 = new Date(event.timestamp).getTime(); - if (t2 < t0 || t2 > t1) { - continue; - } - const x2 = this.xOf(t2, t0, t1); - const value = this._interpolateValue(firstPts, t2); - if (value === null) { - continue; - } - const y2 = this.yOf(value, vMin, vMax); - const color = event.color || "#03a9f4"; - ctx.save(); - ctx.setLineDash([4, 3]); - ctx.strokeStyle = color; - ctx.lineWidth = 1.5; - ctx.globalAlpha = 0.75; - ctx.beginPath(); - ctx.moveTo(x2, pad.top + this.ch); - ctx.lineTo(x2, y2); - ctx.stroke(); - ctx.restore(); - ctx.save(); - ctx.beginPath(); - ctx.arc(x2, y2, 4, 0, Math.PI * 2); - ctx.fillStyle = color; - ctx.fill(); - ctx.strokeStyle = "rgba(255,255,255,0.9)"; - ctx.lineWidth = 1.5; - ctx.stroke(); - ctx.restore(); - hits.push({ event, x: x2, y: y2, value }); - } - return hits; - } - /** - * Interpolate the Y pixel position on a data series at a given timestamp. - * Uses linear interpolation between surrounding data points. - */ - _interpolateY(seriesPoints, t2, _t0, _t1, vMin, vMax) { - if (!seriesPoints.length) { - return null; - } - if (t2 <= seriesPoints[0][0]) { - return this.yOf(seriesPoints[0][1], vMin, vMax); - } - if (t2 >= seriesPoints[seriesPoints.length - 1][0]) { - return this.yOf(seriesPoints[seriesPoints.length - 1][1], vMin, vMax); - } - for (let i2 = 0; i2 < seriesPoints.length - 1; i2++) { - const [t1p, v1p] = seriesPoints[i2]; - const [t2p, v2p] = seriesPoints[i2 + 1]; - if (t2 >= t1p && t2 <= t2p) { - const frac = (t2 - t1p) / (t2p - t1p); - const v2 = v1p + frac * (v2p - v1p); - return this.yOf(v2, vMin, vMax); - } - } - return null; - } - _interpolateValue(seriesPoints, t2) { - const len = seriesPoints.length; - if (!len) { - return null; - } - if (t2 < seriesPoints[0][0]) { - return null; - } - if (t2 > seriesPoints[len - 1][0]) { - return null; - } - let lo = 0; - let hi = len - 1; - while (lo + 1 < hi) { - const mid = Math.floor((lo + hi) / 2); - if (seriesPoints[mid][0] <= t2) { - lo = mid; - } else { - hi = mid; - } - } - const [t0, v0] = seriesPoints[lo]; - const [t1, v1] = seriesPoints[hi]; - if (t0 === t1) return v0; - return v0 + (v1 - v0) * ((t2 - t0) / (t1 - t0)); - } - /** - * Draw annotation markers directly on a sensor data line. - * No vertical dotted line — only a coloured circle on the line. - * - * @param {Array} events Recorded events array - * @param {Array} allSeries Array of {pts} objects — first series used for Y - * @param {number} t0 Start time ms - * @param {number} t1 End time ms - * @param {number} vMin Y axis min - * @param {number} vMax Y axis max - * @returns {Array} Array of {event, x, y} for hit-testing - */ - drawAnnotationsOnLine(events, allSeries, t0, t1, vMin, vMax) { - const { ctx } = this; - const firstPts = allSeries.length ? allSeries[0].pts : []; - const hits = []; - for (const event of events) { - const t2 = new Date(event.timestamp).getTime(); - if (t2 < t0 || t2 > t1) { - continue; - } - const x2 = this.xOf(t2, t0, t1); - const value = this._interpolateValue(firstPts, t2); - if (value === null) { - continue; - } - const y2 = this.yOf(value, vMin, vMax); - const color = event.color || "#03a9f4"; - const r2 = 10; - ctx.save(); - ctx.beginPath(); - ctx.arc(x2, y2, r2 + 1.5, 0, Math.PI * 2); - ctx.fillStyle = "rgba(255,255,255,0.9)"; - ctx.fill(); - ctx.restore(); - ctx.save(); - ctx.beginPath(); - ctx.arc(x2, y2, r2, 0, Math.PI * 2); - ctx.fillStyle = color; - ctx.fill(); - ctx.restore(); - hits.push({ event, x: x2, y: y2, value }); - } - return hits; - } - /** - * Draw a gradient-filled band between two data values, fading from the edge - * value toward the midpoint value. Used for min/max shading that fades toward - * the mean line. - * - * @param {number} valueEdge Data value at the opaque edge (the min or max line) - * @param {number} valueMid Data value at the transparent end (the mean line) - * @param {string} color Hex color string (e.g. "#03a9f4") - * @param {number} t0 Render start time ms - * @param {number} t1 Render end time ms - * @param {number} vMin Y-axis minimum data value - * @param {number} vMax Y-axis maximum data value - * @param {object} options { fillAlpha } - */ - drawGradientBand(valueEdge, valueMid, color, _t0, _t1, vMin, vMax, options = {}) { - const fillAlpha = Number.isFinite(options.fillAlpha) ? options.fillAlpha : 0.08; - if (fillAlpha <= 0) { - return; - } - const hexMatch = String(color || "").match( - /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i - ); - if (!hexMatch) { - return; - } - const r2 = parseInt(hexMatch[1], 16); - const g2 = parseInt(hexMatch[2], 16); - const b2 = parseInt(hexMatch[3], 16); - const yEdge = this.yOf(valueEdge, vMin, vMax); - const yMid = this.yOf(valueMid, vMin, vMax); - if (Math.abs(yMid - yEdge) < 1) { - return; - } - const { ctx, pad } = this; - const grad = ctx.createLinearGradient(0, yEdge, 0, yMid); - grad.addColorStop(0, `rgba(${r2}, ${g2}, ${b2}, ${fillAlpha})`); - grad.addColorStop(1, `rgba(${r2}, ${g2}, ${b2}, 0)`); - ctx.save(); - ctx.beginPath(); - ctx.rect(pad.left, pad.top, this.cw, this.ch); - ctx.clip(); - ctx.fillStyle = grad; - ctx.fillRect( - pad.left, - Math.min(yEdge, yMid), - this.cw, - Math.abs(yMid - yEdge) - ); - ctx.restore(); - } - drawThresholdArea(points, thresholdValue, color, t0, t1, vMin, vMax, options = {}) { - if (!Array.isArray(points) || points.length < 2) { - return; - } - if (!Number.isFinite(thresholdValue)) { - return; - } - const mode = options.mode === "below" ? "below" : "above"; - const fillAlpha = Number.isFinite(options.fillAlpha) ? options.fillAlpha : 0.12; - if (fillAlpha <= 0) { - return; - } - const segments = []; - let currentSegment = []; - const isInside = (value) => { - if (mode === "below") { - return value <= thresholdValue; - } - return value >= thresholdValue; - }; - const flushSegment = () => { - if (currentSegment.length >= 2) { - segments.push(currentSegment); - } - currentSegment = []; - }; - for (let index = 0; index < points.length - 1; index += 1) { - const startPoint = points[index]; - const endPoint = points[index + 1]; - const startInside = isInside(startPoint[1]); - const endInside = isInside(endPoint[1]); - if (startInside && currentSegment.length === 0) { - currentSegment.push(startPoint); - } - if (startInside && endInside) { - currentSegment.push(endPoint); - continue; - } - if (startInside !== endInside) { - const deltaValue = endPoint[1] - startPoint[1]; - if (deltaValue === 0) { - flushSegment(); - continue; - } - const fraction = (thresholdValue - startPoint[1]) / deltaValue; - const crossingTime = startPoint[0] + (endPoint[0] - startPoint[0]) * fraction; - const crossingPoint = [crossingTime, thresholdValue]; - if (startInside) { - currentSegment.push(crossingPoint); - flushSegment(); - } else { - currentSegment.push(crossingPoint); - currentSegment.push(endPoint); - } - continue; - } - if (!startInside && !endInside) { - flushSegment(); - } - } - flushSegment(); - if (!segments.length) { - return; - } - const { ctx, pad } = this; - const thresholdY = this.yOf(thresholdValue, vMin, vMax); - ctx.save(); - ctx.beginPath(); - ctx.rect(pad.left, pad.top, this.cw, this.ch); - ctx.clip(); - ctx.fillStyle = hexToRgba(color, fillAlpha); - segments.forEach((segment) => { - if (!Array.isArray(segment) || segment.length < 2) { - return; - } - ctx.beginPath(); - const firstPoint = segment[0]; - ctx.moveTo(this.xOf(firstPoint[0], t0, t1), thresholdY); - segment.forEach((point) => { - ctx.lineTo(this.xOf(point[0], t0, t1), this.yOf(point[1], vMin, vMax)); - }); - const lastPoint = segment[segment.length - 1]; - ctx.lineTo(this.xOf(lastPoint[0], t0, t1), thresholdY); - ctx.closePath(); - ctx.fill(); - }); - ctx.restore(); - } - /** - * Draw diagonal hash marks at gap boundary points to indicate the start/end - * of contiguous data ranges. - * - * @param {Array} boundaryPoints Array of [timeMs, value] pairs at gap edges - * @param {string} color Stroke colour - * @param {number} t0 Start time ms - * @param {number} t1 End time ms - * @param {number} vMin Y axis min - * @param {number} vMax Y axis max - */ - drawGapMarkers(boundaryPoints, color, t0, t1, vMin, vMax) { - if (!boundaryPoints.length) { - return; - } - const { ctx, pad } = this; - const h2 = 7; - const w = 3; - const gap = 2; - ctx.save(); - ctx.beginPath(); - ctx.rect(pad.left, pad.top, this.cw, this.ch); - ctx.clip(); - ctx.strokeStyle = color; - ctx.lineWidth = 1.5; - ctx.globalAlpha = 0.55; - for (let i2 = 0; i2 < boundaryPoints.length; i2++) { - const [t2, v2] = boundaryPoints[i2]; - const x2 = this.xOf(t2, t0, t1); - const y2 = this.yOf(v2, vMin, vMax); - const dir = i2 % 2 === 0 ? 1 : -1; - for (let d2 = -gap; d2 <= gap; d2 += gap * 2) { - ctx.beginPath(); - ctx.moveTo(x2 + d2 - w * dir, y2 - h2); - ctx.lineTo(x2 + d2 + w * dir, y2 + h2); - ctx.stroke(); - } - } - ctx.restore(); - } - drawAnomalyClusters(clusters, color, t0, t1, vMin, vMax, options = {}) { - if (!Array.isArray(clusters) || clusters.length === 0) { - return; - } - const strokeAlpha = Number.isFinite(options.strokeAlpha) ? options.strokeAlpha : 0.92; - const lineWidth = Number.isFinite(options.lineWidth) ? options.lineWidth : 2; - const haloWidth = Number.isFinite(options.haloWidth) ? options.haloWidth : Math.max(2.5, lineWidth + 1.5); - const haloColor = typeof options.haloColor === "string" && options.haloColor ? options.haloColor : "rgba(255,255,255,0.9)"; - const haloAlpha = Number.isFinite(options.haloAlpha) ? options.haloAlpha : 0.9; - const fillColor = typeof options.fillColor === "string" && options.fillColor ? options.fillColor : null; - const fillAlpha = Number.isFinite(options.fillAlpha) ? options.fillAlpha : 0; - const pointPadding = Number.isFinite(options.pointPadding) ? options.pointPadding : 10; - const minRadiusX = Number.isFinite(options.minRadiusX) ? options.minRadiusX : 10; - const minRadiusY = Number.isFinite(options.minRadiusY) ? options.minRadiusY : 10; - const clusterRegions = this.getAnomalyClusterRegions( - clusters, - t0, - t1, - vMin, - vMax, - { - pointPadding, - minRadiusX, - minRadiusY - } - ); - const { ctx, pad } = this; - ctx.save(); - ctx.beginPath(); - ctx.rect(pad.left, pad.top, this.cw, this.ch); - ctx.clip(); - clusterRegions.forEach((region) => { - ctx.save(); - ctx.setLineDash([]); - if (fillColor && fillAlpha > 0) { - ctx.globalAlpha = fillAlpha; - ctx.fillStyle = fillColor; - ctx.beginPath(); - ctx.ellipse( - region.centerX, - region.centerY, - region.radiusX, - region.radiusY, - 0, - 0, - Math.PI * 2 - ); - ctx.fill(); - } - ctx.globalAlpha = haloAlpha; - ctx.strokeStyle = haloColor; - ctx.lineWidth = haloWidth; - ctx.beginPath(); - ctx.ellipse( - region.centerX, - region.centerY, - region.radiusX, - region.radiusY, - 0, - 0, - Math.PI * 2 - ); - ctx.stroke(); - ctx.globalAlpha = strokeAlpha; - ctx.strokeStyle = color; - ctx.lineWidth = lineWidth; - ctx.beginPath(); - ctx.ellipse( - region.centerX, - region.centerY, - region.radiusX, - region.radiusY, - 0, - 0, - Math.PI * 2 - ); - ctx.stroke(); - ctx.restore(); - }); - ctx.restore(); - } - /** - * Animate a "blip" circle at the given canvas coordinates. - * The circle expands with a bouncy overshoot, holds briefly, then shrinks to nothing. - * Uses a separate overlay canvas so it doesn't interfere with the main chart. - */ - drawBlip(cx, cy, color, options = {}) { - const resolvedOptions = options; - const maxRadius = resolvedOptions.maxRadius || 6; - const duration = resolvedOptions.duration || 600; - const canvas = this.canvas; - const parent = canvas.parentElement; - if (!parent) { - return; - } - const overlay = document.createElement("canvas"); - overlay.width = canvas.width; - overlay.height = canvas.height; - overlay.style.cssText = `position:absolute;top:0;left:0;width:${canvas.style.width || `${canvas.offsetWidth}px`};height:${canvas.style.height || `${canvas.offsetHeight}px`};pointer-events:none;z-index:2;`; - parent.style.position = parent.style.position || "relative"; - parent.appendChild(overlay); - const ctx = overlay.getContext("2d"); - if (!ctx) { - overlay.remove(); - return; - } - const dpr = window.devicePixelRatio || 1; - const pxCx = cx * dpr; - const pxCy = cy * dpr; - const pxMaxR = maxRadius * dpr; - const start = performance.now(); - const animate = (now) => { - const elapsed = now - start; - const t2 = Math.min(elapsed / duration, 1); - ctx.clearRect(0, 0, overlay.width, overlay.height); - let radius; - let alpha; - if (t2 < 0.35) { - const p2 = t2 / 0.35; - const bounce = p2 < 0.6 ? p2 / 0.6 * 1.3 : 1.3 - 0.3 * ((p2 - 0.6) / 0.4); - radius = pxMaxR * Math.min(bounce, 1.3); - alpha = Math.min(p2 * 2.5, 0.85); - } else if (t2 < 0.6) { - radius = pxMaxR; - alpha = 0.85; - } else { - const p2 = (t2 - 0.6) / 0.4; - const ease = 1 - (1 - p2) ** 3; - radius = pxMaxR * (1 - ease); - alpha = 0.85 * (1 - ease); - } - if (radius > 0.2 && alpha > 0.01) { - ctx.save(); - ctx.globalAlpha = alpha; - ctx.beginPath(); - ctx.arc(pxCx, pxCy, radius, 0, Math.PI * 2); - ctx.fillStyle = color; - ctx.fill(); - ctx.beginPath(); - ctx.arc(pxCx, pxCy, radius * 1.6, 0, Math.PI * 2); - ctx.strokeStyle = color; - ctx.lineWidth = 1.2 * dpr; - ctx.globalAlpha = alpha * 0.4; - ctx.stroke(); - ctx.restore(); - } - if (t2 < 1) { - requestAnimationFrame(animate); - } else { - overlay.remove(); - } - }; - requestAnimationFrame(animate); - } - getAnomalyClusterRegions(clusters, t0, t1, vMin, vMax, options = {}) { - if (!Array.isArray(clusters) || clusters.length === 0) { - return []; - } - const pointPadding = Number.isFinite(options.pointPadding) ? options.pointPadding : 10; - const minRadiusX = Number.isFinite(options.minRadiusX) ? options.minRadiusX : 10; - const minRadiusY = Number.isFinite(options.minRadiusY) ? options.minRadiusY : 10; - return clusters.flatMap((cluster) => { - if (!Array.isArray(cluster?.points) || cluster.points.length === 0) { - return []; - } - const xs = []; - const ys = []; - cluster.points.forEach((point) => { - xs.push(this.xOf(point.timeMs, t0, t1)); - ys.push(this.yOf(point.value, vMin, vMax)); - }); - const minX = Math.min(...xs); - const maxX = Math.max(...xs); - const minY = Math.min(...ys); - const maxY = Math.max(...ys); - return [ - { - centerX: (minX + maxX) / 2, - centerY: (minY + maxY) / 2, - radiusX: Math.max(minRadiusX, (maxX - minX) / 2 + pointPadding), - radiusY: Math.max(minRadiusY, (maxY - minY) / 2 + pointPadding), - cluster - } - ]; - }); - } + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/data/downsampling.ts + /** + * Map of human-readable interval strings to milliseconds. + * Used when configuring downsampling bucket widths. + */ + var SAMPLE_INTERVAL_MS = { + "1s": 1e3, + "5s": 5e3, + "10s": 1e4, + "15s": 15e3, + "30s": 3e4, + "1m": 6e4, + "2m": 2 * 6e4, + "5m": 5 * 6e4, + "10m": 10 * 6e4, + "15m": 15 * 6e4, + "30m": 30 * 6e4, + "1h": 60 * 6e4, + "2h": 120 * 6e4, + "3h": 180 * 6e4, + "4h": 240 * 6e4, + "6h": 360 * 6e4, + "12h": 720 * 6e4, + "24h": 1440 * 6e4 + }; + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/data/binary-labels.ts + /** + * Returns the human-readable "on" label for a binary sensor device class. + * Pass the lower-cased `device_class` attribute value. + */ + function binaryOnLabel(deviceClass) { + return { + battery: "low", + battery_charging: "charging", + carbon_monoxide: "detected", + cold: "cold", + connectivity: "connected", + door: "open", + garage_door: "open", + gas: "detected", + heat: "hot", + lock: "unlocked", + moisture: "wet", + motion: "motion", + moving: "moving", + occupancy: "occupied", + opening: "open", + plug: "plugged in", + power: "power", + presence: "present", + problem: "problem", + running: "running", + safety: "unsafe", + smoke: "smoke", + sound: "sound", + tamper: "tampered", + update: "update available", + vibration: "vibration", + window: "open" + }[deviceClass] || "on"; + } + /** + * Returns the human-readable "off" label for a binary sensor device class. + * Pass the lower-cased `device_class` attribute value. + */ + function binaryOffLabel(deviceClass) { + return { + battery: "normal", + battery_charging: "not charging", + carbon_monoxide: "clear", + cold: "normal", + connectivity: "disconnected", + door: "closed", + garage_door: "closed", + gas: "clear", + heat: "normal", + lock: "locked", + moisture: "dry", + motion: "clear", + moving: "still", + occupancy: "clear", + opening: "closed", + plug: "unplugged", + power: "off", + presence: "away", + problem: "ok", + running: "idle", + safety: "safe", + smoke: "clear", + sound: "quiet", + tamper: "clear", + update: "up to date", + vibration: "still", + window: "closed" + }[deviceClass] || "off"; + } + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/data/history-normalization.ts + /** + * Normalises a raw numeric sensor state list from the HA history API into an + * array of `{ lu, s }` records (non-finite values filtered out). + */ + function normalizeNumericHistory(_entityId, histStates) { + return (Array.isArray(histStates) ? histStates : []).map((state) => { + const value = parseFloat(state?.s ?? ""); + if (Number.isNaN(value)) return null; + const rawTimestamp = state?.lu ?? state?.lc ?? state?.last_changed ?? state?.last_updated; + const timeSec = typeof rawTimestamp === "number" ? rawTimestamp : new Date(rawTimestamp || 0).getTime() / 1e3; + if (!Number.isFinite(timeSec)) return null; + return { + lu: Math.round(timeSec * 1e3) / 1e3, + s: String(value) + }; + }).filter((entry) => entry !== null); + } + /** + * Extracts the state array for a given entity from the various response shapes + * returned by different versions of the HA history WebSocket API. + */ + function getHistoryStatesForEntity$1(histResult, entityId, entityIds = []) { + if (!histResult) return []; + const result = histResult; + if (Array.isArray(result[entityId])) return result[entityId]; + if (Array.isArray(histResult)) { + const entries = histResult; + const entityIndex = entityIds.indexOf(entityId); + if (entityIndex >= 0 && Array.isArray(entries[entityIndex])) return entries[entityIndex]; + if (entries.every((entry) => entry && typeof entry === "object" && !Array.isArray(entry))) return entries.filter((entry) => entry.entity_id === entityId); + } + if (histResult && typeof histResult === "object") { + const wrapped = histResult; + if (Array.isArray(wrapped.result?.[entityId])) return wrapped.result[entityId]; + if (Array.isArray(wrapped.result)) { + if (wrapped.result.every((entry) => entry && typeof entry === "object" && !Array.isArray(entry))) return wrapped.result.filter((entry) => entry.entity_id === entityId); + const entityIndex = entityIds.indexOf(entityId); + if (entityIndex >= 0 && Array.isArray(wrapped.result[entityIndex])) return wrapped.result[entityIndex]; + } + } + return []; + } + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/data/statistics-normalization.ts + /** + * Normalises statistics data from the HA statistics API for a single entity + * into a sorted array of `{ lu, s }` records. + */ + function normalizeStatisticsHistory(entityId, statsData) { + const statEntries = statsData && typeof statsData === "object" ? statsData[entityId] ?? [] : []; + return (Array.isArray(statEntries) ? statEntries : []).map((entry) => { + const value = Number(entry?.mean); + if (!Number.isFinite(value)) return null; + const rawTimestamp = entry?.start; + let timestamp; + if (typeof rawTimestamp === "number") if (rawTimestamp > 1e11) timestamp = rawTimestamp; + else timestamp = rawTimestamp * 1e3; + else timestamp = new Date(rawTimestamp).getTime(); + if (!Number.isFinite(timestamp)) return null; + return { + lu: Math.round(timestamp) / 1e3, + s: String(value) + }; + }).filter((entry) => entry !== null).sort((a, b) => a.lu - b.lu); + } + /** + * Merges a raw (high-resolution) history point list with a statistics + * (long-term) point list, preferring raw data for the period it covers and + * filling in statistics data outside that period. + */ + function mergeNumericHistoryWithStatistics(histPts, statsPts) { + const raw = Array.isArray(histPts) ? histPts : []; + const stats = Array.isArray(statsPts) ? statsPts : []; + if (!raw.length) return [...stats]; + if (!stats.length) return [...raw]; + const firstRawMs = raw[0].lu * 1e3; + const lastRawMs = raw[raw.length - 1].lu * 1e3; + const merged = [...stats.filter((entry) => { + const timeMs = entry.lu * 1e3; + return timeMs < firstRawMs || timeMs > lastRawMs; + }), ...raw]; + merged.sort((a, b) => a.lu - b.lu); + return merged; + } + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/data/axis-extent.ts + /** + * Computes the finite minimum and maximum of an array of values. + * Returns `null` if there are no finite values. + */ + function getAxisValueExtent(allValues) { + let min = Infinity; + let max = -Infinity; + for (const value of allValues) { + const numeric = Number(value); + if (!Number.isFinite(numeric)) continue; + if (numeric < min) min = numeric; + if (numeric > max) max = numeric; + } + if (!Number.isFinite(min) || !Number.isFinite(max)) return null; + return { + min, + max + }; + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/util/color.ts + function hexToRgba(hex, alpha) { + const h = hex.replace("#", ""); + return `rgba(${Number.parseInt(h.substring(0, 2), 16)},${Number.parseInt(h.substring(2, 4), 16)},${Number.parseInt(h.substring(4, 6), 16)},${alpha})`; + } + /** + * Return "#fff" or "#000" whichever has better contrast against the given hex + * background colour, using the WCAG relative-luminance formula. + */ + function contrastColor(hex) { + if (!hex || typeof hex !== "string") return "#fff"; + const h = hex.replace("#", ""); + if (h.length !== 6) return "#fff"; + const r = Number.parseInt(h.substring(0, 2), 16) / 255; + const g = Number.parseInt(h.substring(2, 4), 16) / 255; + const b = Number.parseInt(h.substring(4, 6), 16) / 255; + const lin = (c) => c <= .04045 ? c / 12.92 : ((c + .055) / 1.055) ** 2.4; + return .2126 * lin(r) + .7152 * lin(g) + .0722 * lin(b) > .179 ? "#000" : "#fff"; + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/chart/chart-renderer.ts + function createFallbackCanvasContext() { + return { + beginPath() {}, + moveTo() {}, + lineTo() {}, + stroke() {}, + fill() {}, + fillRect() {}, + clearRect() {}, + rect() {}, + clip() {}, + save() {}, + restore() {}, + scale() {}, + setLineDash() {}, + fillText() {}, + closePath() {}, + bezierCurveTo() {}, + arc() {}, + measureText() { + return { width: 0 }; + } + }; + } + /** + * Canvas-based chart renderer – grids, lines, annotations. + */ + var ChartRenderer = class ChartRenderer { + constructor(canvas, cssWidth, cssHeight) { + _defineProperty(this, "canvas", void 0); + _defineProperty(this, "ctx", void 0); + _defineProperty(this, "cssW", void 0); + _defineProperty(this, "cssH", void 0); + _defineProperty(this, "basePad", void 0); + _defineProperty(this, "pad", void 0); + _defineProperty(this, "labelColor", void 0); + _defineProperty(this, "_activeAxes", []); + this.canvas = canvas; + this.ctx = canvas.getContext("2d") || createFallbackCanvasContext(); + this.cssW = cssWidth; + this.cssH = cssHeight; + this.basePad = { + top: 24, + right: 12, + bottom: 48, + left: 12 + }; + this.pad = { ...this.basePad }; + this.labelColor = "rgba(214,218,224,0.92)"; + } + static get AXIS_SLOT_WIDTH() { + return 30; + } + get cw() { + return this.cssW - this.pad.left - this.pad.right; + } + get ch() { + return this.cssH - this.pad.top - this.pad.bottom; + } + xOf(t, t0, t1) { + return this.pad.left + (t - t0) / (t1 - t0) * this.cw; + } + yOf(v, vMin, vMax) { + return this.pad.top + this.ch - (v - vMin) / (vMax - vMin) * this.ch; + } + clear() { + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + } + _normalizeAxes(vMinOrAxes, vMax) { + const axisColumnWidth = ChartRenderer.AXIS_SLOT_WIDTH; + const inputAxes = Array.isArray(vMinOrAxes) ? vMinOrAxes : [{ + key: "default", + min: vMinOrAxes, + max: vMax ?? vMinOrAxes, + side: "left", + unit: "", + color: null + }]; + const leftAxes = []; + const rightAxes = []; + const axes = inputAxes.map((axis, index) => { + const normalized = { + key: axis.key || `axis-${index}`, + min: axis.min, + max: axis.max, + side: axis.side === "right" ? "right" : "left", + unit: axis.unit || "", + color: axis.color || "rgba(128,128,128,0.85)" + }; + const bucket = normalized.side === "right" ? rightAxes : leftAxes; + normalized.slot = bucket.length; + bucket.push(normalized); + return normalized; + }); + this.pad = { + top: this.basePad.top, + bottom: this.basePad.bottom, + left: this.basePad.left + Math.max(1, leftAxes.length) * axisColumnWidth, + right: this.basePad.right + rightAxes.length * axisColumnWidth + }; + this._activeAxes = axes; + return axes; + } + _formatAxisTick(v, _unit) { + return Math.abs(v) >= 1e3 ? `${(v / 1e3).toFixed(1).replace(/\.0$/, "")}k` : v.toFixed(v % 1 !== 0 ? 1 : 0); + } + _axisLabelX(axis) { + const columnWidth = ChartRenderer.AXIS_SLOT_WIDTH; + const leftAxisX = this.pad.left; + const rightAxisX = this.pad.left + this.cw; + if (axis.side === "right") return rightAxisX + 10 + (axis.slot ?? 0) * columnWidth; + return leftAxisX - 10 - (axis.slot ?? 0) * columnWidth; + } + _formatTimeTick(t, t0, t1, tickSpanMs = null) { + const value = new Date(t); + const spanMs = Math.max(0, t1 - t0); + const detailSpanMs = Number.isFinite(tickSpanMs) && (tickSpanMs ?? 0) > 0 ? tickSpanMs : spanMs; + const start = new Date(t0); + const end = new Date(t1); + const sameDay = start.getFullYear() === end.getFullYear() && start.getMonth() === end.getMonth() && start.getDate() === end.getDate(); + const sameMonth = start.getFullYear() === end.getFullYear() && start.getMonth() === end.getMonth(); + if (detailSpanMs <= 7200 * 1e3) return value.toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit" + }); + if (detailSpanMs <= 720 * 60 * 1e3) return value.toLocaleString([], { + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit" + }); + if (detailSpanMs <= 2880 * 60 * 1e3) return value.toLocaleString([], { + month: "short", + day: "numeric", + hour: "2-digit" + }); + if (sameDay) return value.toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit" + }); + if (detailSpanMs <= 360 * 60 * 1e3) return value.toLocaleString([], { + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit" + }); + if (detailSpanMs <= 1440 * 60 * 1e3) return value.toLocaleString([], { + month: "short", + day: "numeric", + hour: "2-digit" + }); + if (sameMonth && spanMs <= 336 * 60 * 60 * 1e3) return value.toLocaleDateString([], { day: "numeric" }); + if (spanMs >= 2880 * 60 * 1e3) return value.toLocaleDateString([], { + month: "short", + day: "numeric" + }); + if (spanMs >= 1440 * 60 * 1e3) return value.toLocaleString([], { + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit" + }); + return fmtTime(value.toISOString()); + } + _niceNumber(value, round) { + if (!Number.isFinite(value) || value <= 0) return 1; + const exponent = Math.floor(Math.log10(value)); + const fraction = value / 10 ** exponent; + let niceFraction; + if (round) if (fraction < 1.5) niceFraction = 1; + else if (fraction < 3) niceFraction = 2; + else if (fraction < 7) niceFraction = 5; + else niceFraction = 10; + else if (fraction <= 1) niceFraction = 1; + else if (fraction <= 2) niceFraction = 2; + else if (fraction <= 5) niceFraction = 5; + else niceFraction = 10; + return niceFraction * 10 ** exponent; + } + _buildNiceAxisScale(axis, tickCount) { + const rawMin = Number.isFinite(axis.min) ? axis.min : 0; + const rawMax = Number.isFinite(axis.max) ? axis.max : 1; + if (rawMin === rawMax) { + const pad = Math.abs(rawMin || 1); + const step = this._niceNumber(pad * 2 / Math.max(1, tickCount), true); + const niceMin = Math.floor((rawMin - pad) / step) * step; + const niceMax = Math.ceil((rawMax + pad) / step) * step; + const ticks = []; + for (let value = niceMin; value <= niceMax + step * .5; value += step) ticks.push(Number(value.toFixed(10))); + return { + min: niceMin, + max: niceMax, + step, + ticks + }; + } + const range = this._niceNumber(rawMax - rawMin, false); + const step = this._niceNumber(range / Math.max(1, tickCount), true); + const niceMin = Math.floor(rawMin / step) * step; + const niceMax = Math.ceil(rawMax / step) * step; + const ticks = []; + for (let value = niceMin; value <= niceMax + step * .5; value += step) ticks.push(Number(value.toFixed(10))); + return { + min: niceMin, + max: niceMax, + step, + ticks + }; + } + _alignTimeTick(timestamp, stepMs) { + const date = new Date(timestamp); + if (stepMs < 60 * 1e3) return Math.floor(timestamp / stepMs) * stepMs; + if (stepMs < 3600 * 1e3) { + const minutes = Math.max(1, Math.round(stepMs / (60 * 1e3))); + date.setSeconds(0, 0); + date.setMinutes(Math.floor(date.getMinutes() / minutes) * minutes); + return date.getTime(); + } + if (stepMs < 1440 * 60 * 1e3) { + const hours = Math.max(1, Math.round(stepMs / (3600 * 1e3))); + date.setMinutes(0, 0, 0); + date.setHours(Math.floor(date.getHours() / hours) * hours); + return date.getTime(); + } + if (stepMs < 10080 * 60 * 1e3) { + const days = Math.max(1, Math.round(stepMs / (1440 * 60 * 1e3))); + date.setHours(0, 0, 0, 0); + const dayOfMonth = date.getDate(); + date.setDate(dayOfMonth - (dayOfMonth - 1) % days); + return date.getTime(); + } + if (stepMs < 720 * 60 * 60 * 1e3) { + date.setHours(0, 0, 0, 0); + const offset = (date.getDay() + 6) % 7; + date.setDate(date.getDate() - offset); + return date.getTime(); + } + date.setHours(0, 0, 0, 0); + date.setDate(1); + return date.getTime(); + } + _getTimeTickStep(targetStepMs) { + const candidates = [ + 300 * 1e3, + 600 * 1e3, + 900 * 1e3, + 1800 * 1e3, + 3600 * 1e3, + 7200 * 1e3, + 10800 * 1e3, + 360 * 60 * 1e3, + 720 * 60 * 1e3, + 1440 * 60 * 1e3, + 2880 * 60 * 1e3, + 10080 * 60 * 1e3, + 336 * 60 * 60 * 1e3, + 720 * 60 * 60 * 1e3 + ]; + return candidates.find((step) => step >= targetStepMs) || candidates[candidates.length - 1]; + } + _buildTimeTicks(t0, t1) { + const approxTickCount = Math.max(2, Math.min(96, Math.floor(this.cw / 120))); + const stepMs = this._getTimeTickStep((t1 - t0) / Math.max(1, approxTickCount)); + const ticks = []; + let tick = this._alignTimeTick(t0, stepMs); + if (tick < t0) tick += stepMs; + while (tick <= t1) { + ticks.push(tick); + tick += stepMs; + } + if (!ticks.length) ticks.push(t0, t1); + return { + ticks, + stepMs + }; + } + drawGrid(t0, t1, vMin, vMax, yTicks = 5, options = {}) { + const { ctx, pad } = this; + const gridColor = "rgba(128,128,128,0.15)"; + const labelColor = this.labelColor; + const fixedAxisOverlay = !!options.fixedAxisOverlay; + const hideTimeLabels = !!options.hideTimeLabels; + const axes = this._normalizeAxes(vMin, vMax); + const unitCounts = axes.reduce((counts, axis) => { + if (!axis.unit) return counts; + counts.set(axis.unit, (counts.get(axis.unit) || 0) + 1); + return counts; + }, /* @__PURE__ */ new Map()); + const axisLabelColor = (axis) => { + if (!(!!axis?.unit && (unitCounts.get(axis.unit) || 0) > 1) || !axis?.color) return labelColor; + return axis.color; + }; + axes.forEach((axis) => { + const scale = this._buildNiceAxisScale(axis, yTicks); + axis.min = scale.min; + axis.max = scale.max; + axis.ticks = scale.ticks; + }); + const primaryAxis = axes[0]; + ctx.font = "12px sans-serif"; + for (const v of primaryAxis.ticks || []) { + const y = this.yOf(v, primaryAxis.min, primaryAxis.max); + ctx.strokeStyle = gridColor; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(pad.left, y); + ctx.lineTo(pad.left + this.cw, y); + ctx.stroke(); + if (!fixedAxisOverlay) { + ctx.fillStyle = axisLabelColor(primaryAxis); + ctx.textAlign = "right"; + ctx.textBaseline = "middle"; + ctx.fillText(this._formatAxisTick(v, primaryAxis.unit), this._axisLabelX(primaryAxis), y); + } + } + if (!fixedAxisOverlay) for (const axis of axes.slice(1)) for (const v of axis.ticks || []) { + const y = this.yOf(v, axis.min, axis.max); + ctx.fillStyle = axisLabelColor(axis); + ctx.textAlign = axis.side === "right" ? "left" : "right"; + ctx.textBaseline = "middle"; + ctx.fillText(this._formatAxisTick(v, axis.unit), this._axisLabelX(axis), y); + } + if (!fixedAxisOverlay) for (const axis of axes) { + if (!axis.unit) continue; + ctx.fillStyle = axisLabelColor(axis); + ctx.textAlign = axis.side === "right" ? "left" : "right"; + ctx.textBaseline = "bottom"; + ctx.fillText(axis.unit, this._axisLabelX(axis), pad.top - 6); + } + const { ticks: timeTicks, stepMs: tickSpanMs } = this._buildTimeTicks(t0, t1); + for (const t of timeTicks) { + const x = this.xOf(t, t0, t1); + const label = this._formatTimeTick(t, t0, t1, tickSpanMs); + ctx.strokeStyle = "rgba(128,128,128,0.08)"; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(x, pad.top); + ctx.lineTo(x, pad.top + this.ch); + ctx.stroke(); + if (!hideTimeLabels) { + ctx.fillStyle = labelColor; + ctx.textAlign = "center"; + ctx.textBaseline = "top"; + const labelWidth = ctx.measureText(label).width; + const labelX = Math.min(pad.left + this.cw - labelWidth / 2, Math.max(pad.left + labelWidth / 2, x)); + ctx.fillText(label, labelX, pad.top + this.ch + 6); + } + } + ctx.strokeStyle = "rgba(128,128,128,0.35)"; + ctx.lineWidth = 1; + ctx.beginPath(); + if (!fixedAxisOverlay) { + ctx.moveTo(pad.left, pad.top); + ctx.lineTo(pad.left, pad.top + this.ch); + } + ctx.moveTo(pad.left, pad.top + this.ch); + ctx.lineTo(pad.left + this.cw, pad.top + this.ch); + if (axes.some((axis) => axis.side === "right") && !fixedAxisOverlay) { + ctx.moveTo(pad.left + this.cw, pad.top); + ctx.lineTo(pad.left + this.cw, pad.top + this.ch); + } + ctx.stroke(); + } + drawRowLabel(text, color = "rgba(214,218,224,0.85)") { + if (!text) return; + const { ctx, pad } = this; + ctx.save(); + ctx.font = "bold 11px sans-serif"; + ctx.fillStyle = color; + ctx.textAlign = "left"; + ctx.textBaseline = "top"; + ctx.fillText(text, pad.left + 6, pad.top + 5); + ctx.restore(); + } + /** + * Append Catmull-Rom bezier segments to the current path, starting from pts[0] + * (caller must have already called moveTo(pts[0])). Each segment passes exactly + * through the data points with tangents derived from neighbouring points. + */ + static _catmullRomPath(ctx, pts) { + for (let i = 1; i < pts.length; i++) { + const pm1 = pts[Math.max(0, i - 2)]; + const p0 = pts[i - 1]; + const p1 = pts[i]; + const p2 = pts[Math.min(pts.length - 1, i + 1)]; + const cp1x = p0[0] + (p1[0] - pm1[0]) / 6; + const cp1y = p0[1] + (p1[1] - pm1[1]) / 6; + const cp2x = p1[0] - (p2[0] - p0[0]) / 6; + const cp2y = p1[1] - (p2[1] - p0[1]) / 6; + ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p1[0], p1[1]); + } + } + static _steppedPath(ctx, pts) { + for (let i = 1; i < pts.length; i++) { + const previous = pts[i - 1]; + const current = pts[i]; + ctx.lineTo(current[0], previous[1]); + ctx.lineTo(current[0], current[1]); + } + } + drawLine(points, color, t0, t1, vMin, vMax, options = {}) { + if (!points.length) return; + const { ctx, pad } = this; + const fillAlpha = Number.isFinite(options.fillAlpha) ? options.fillAlpha : 0; + const smooth = !!options.smooth; + const stepped = options.stepped === true; + const dashed = !!options.dashed; + const dotted = !!options.dotted; + const dashPattern = Array.isArray(options.dashPattern) ? options.dashPattern.filter((entry) => Number.isFinite(entry) && entry > 0) : null; + const lineOpacity = Number.isFinite(options.lineOpacity) ? options.lineOpacity : 1; + const lineWidth = Number.isFinite(options.lineWidth) ? options.lineWidth : 1.75; + const px = points.map(([t, v]) => [this.xOf(t, t0, t1), this.yOf(v, vMin, vMax)]); + const bottom = pad.top + this.ch; + ctx.save(); + ctx.beginPath(); + ctx.rect(pad.left, pad.top, this.cw, this.ch); + ctx.clip(); + if (dashPattern && dashPattern.length) ctx.setLineDash(dashPattern); + else if (dotted) { + ctx.setLineDash([1, 3]); + ctx.lineCap = "round"; + } else if (dashed) ctx.setLineDash([6, 4]); + if (lineOpacity < 1) ctx.globalAlpha = lineOpacity; + if (fillAlpha > 0) { + ctx.beginPath(); + ctx.moveTo(px[0][0], bottom); + ctx.lineTo(px[0][0], px[0][1]); + if (stepped) ChartRenderer._steppedPath(ctx, px); + else if (smooth) ChartRenderer._catmullRomPath(ctx, px); + else for (let i = 1; i < px.length; i++) ctx.lineTo(px[i][0], px[i][1]); + ctx.lineTo(px[px.length - 1][0], bottom); + ctx.closePath(); + ctx.fillStyle = hexToRgba(color, fillAlpha); + ctx.fill(); + } + ctx.beginPath(); + ctx.moveTo(px[0][0], px[0][1]); + if (stepped) ChartRenderer._steppedPath(ctx, px); + else if (smooth) ChartRenderer._catmullRomPath(ctx, px); + else for (let i = 1; i < px.length; i++) ctx.lineTo(px[i][0], px[i][1]); + ctx.strokeStyle = color; + ctx.lineWidth = lineWidth; + if (stepped) { + ctx.lineJoin = "miter"; + ctx.lineCap = "butt"; + } else { + ctx.lineJoin = "round"; + ctx.lineCap = "round"; + } + ctx.stroke(); + ctx.restore(); + } + drawBars(points, color, t0, t1, vMin, vMax, options = {}) { + if (!points.length) return; + const { ctx } = this; + const fillAlpha = Number.isFinite(options.fillAlpha) ? options.fillAlpha : .78; + const widthFactor = Number.isFinite(options.widthFactor) ? options.widthFactor : .72; + const baselineY = this.yOf(Math.max(vMin, 0), vMin, vMax); + const xs = points.map(([t]) => this.xOf(t, t0, t1)); + let minGap = this.cw / Math.max(points.length, 1); + for (let i = 1; i < xs.length; i++) minGap = Math.min(minGap, xs[i] - xs[i - 1]); + const barWidth = Math.max(3, Math.min(28, minGap * widthFactor)); + ctx.save(); + ctx.fillStyle = hexToRgba(color, fillAlpha); + ctx.strokeStyle = color; + ctx.lineWidth = 1; + for (let i = 0; i < points.length; i++) { + const [, v] = points[i]; + const x = xs[i]; + const y = this.yOf(v, vMin, vMax); + const top = Math.min(y, baselineY); + const height = Math.max(1, Math.abs(baselineY - y)); + const left = x - barWidth / 2; + ctx.fillRect(left, top, barWidth, height); + } + ctx.restore(); + } + drawStateBands(spans, t0, t1, color = "#03a9f4", alpha = .12) { + if (!spans?.length) return; + const { ctx, pad } = this; + ctx.save(); + ctx.fillStyle = hexToRgba(color, alpha); + for (const span of spans) { + const start = Math.max(t0, span.start); + const end = Math.min(t1, span.end); + if (!(start < end)) continue; + const x0 = this.xOf(start, t0, t1); + const x1 = this.xOf(end, t0, t1); + ctx.fillRect(x0, pad.top, Math.max(1, x1 - x0), this.ch); + } + ctx.restore(); + } + drawAnnotations(events, t0, t1, options = {}) { + const { ctx, pad } = this; + const hits = []; + const showLines = options.showLines !== false; + const showMarkers = options.showMarkers !== false; + for (const event of events) { + const t = new Date(event.timestamp).getTime(); + if (t < t0 || t > t1) continue; + const x = this.xOf(t, t0, t1); + const color = event.color || "#03a9f4"; + if (showLines) { + ctx.save(); + ctx.setLineDash([4, 3]); + ctx.strokeStyle = color; + ctx.lineWidth = 1.5; + ctx.globalAlpha = .75; + ctx.beginPath(); + ctx.moveTo(x, pad.top + 8); + ctx.lineTo(x, pad.top + this.ch); + ctx.stroke(); + ctx.restore(); + } + if (showMarkers) { + const d = 5; + ctx.save(); + ctx.fillStyle = color; + ctx.strokeStyle = "rgba(255,255,255,0.8)"; + ctx.lineWidth = 1.5; + ctx.beginPath(); + ctx.moveTo(x, pad.top - d); + ctx.lineTo(x + d, pad.top); + ctx.lineTo(x, pad.top + d); + ctx.lineTo(x - d, pad.top); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + ctx.restore(); + } + hits.push({ + event, + x, + y: pad.top + }); + } + return hits; + } + /** + * Draw sensor-style vertical annotation lines that terminate on the data line + * with a small circle marker. + */ + drawAnnotationLinesOnLine(events, allSeries, t0, t1, vMin, vMax) { + const { ctx, pad } = this; + const firstPts = allSeries.length ? allSeries[0].pts : []; + const hits = []; + for (const event of events) { + const t = new Date(event.timestamp).getTime(); + if (t < t0 || t > t1) continue; + const x = this.xOf(t, t0, t1); + const value = this._interpolateValue(firstPts, t); + if (value === null) continue; + const y = this.yOf(value, vMin, vMax); + const color = event.color || "#03a9f4"; + ctx.save(); + ctx.setLineDash([4, 3]); + ctx.strokeStyle = color; + ctx.lineWidth = 1.5; + ctx.globalAlpha = .75; + ctx.beginPath(); + ctx.moveTo(x, pad.top + this.ch); + ctx.lineTo(x, y); + ctx.stroke(); + ctx.restore(); + ctx.save(); + ctx.beginPath(); + ctx.arc(x, y, 4, 0, Math.PI * 2); + ctx.fillStyle = color; + ctx.fill(); + ctx.strokeStyle = "rgba(255,255,255,0.9)"; + ctx.lineWidth = 1.5; + ctx.stroke(); + ctx.restore(); + hits.push({ + event, + x, + y, + value + }); + } + return hits; + } + /** + * Interpolate the Y pixel position on a data series at a given timestamp. + * Uses linear interpolation between surrounding data points. + */ + _interpolateY(seriesPoints, t, _t0, _t1, vMin, vMax) { + if (!seriesPoints.length) return null; + if (t <= seriesPoints[0][0]) return this.yOf(seriesPoints[0][1], vMin, vMax); + if (t >= seriesPoints[seriesPoints.length - 1][0]) return this.yOf(seriesPoints[seriesPoints.length - 1][1], vMin, vMax); + for (let i = 0; i < seriesPoints.length - 1; i++) { + const [t1p, v1p] = seriesPoints[i]; + const [t2p, v2p] = seriesPoints[i + 1]; + if (t >= t1p && t <= t2p) { + const v = v1p + (t - t1p) / (t2p - t1p) * (v2p - v1p); + return this.yOf(v, vMin, vMax); + } + } + return null; + } + _interpolateValue(seriesPoints, t) { + const len = seriesPoints.length; + if (!len) return null; + if (t < seriesPoints[0][0]) return null; + if (t > seriesPoints[len - 1][0]) return null; + let lo = 0; + let hi = len - 1; + while (lo + 1 < hi) { + const mid = Math.floor((lo + hi) / 2); + if (seriesPoints[mid][0] <= t) lo = mid; + else hi = mid; + } + const [t0, v0] = seriesPoints[lo]; + const [t1, v1] = seriesPoints[hi]; + if (t0 === t1) return v0; + return v0 + (v1 - v0) * ((t - t0) / (t1 - t0)); + } + /** + * Draw annotation markers directly on a sensor data line. + * No vertical dotted line — only a coloured circle on the line. + * + * @param {Array} events Recorded events array + * @param {Array} allSeries Array of {pts} objects — first series used for Y + * @param {number} t0 Start time ms + * @param {number} t1 End time ms + * @param {number} vMin Y axis min + * @param {number} vMax Y axis max + * @returns {Array} Array of {event, x, y} for hit-testing + */ + drawAnnotationsOnLine(events, allSeries, t0, t1, vMin, vMax) { + const { ctx } = this; + const firstPts = allSeries.length ? allSeries[0].pts : []; + const hits = []; + for (const event of events) { + const t = new Date(event.timestamp).getTime(); + if (t < t0 || t > t1) continue; + const x = this.xOf(t, t0, t1); + const value = this._interpolateValue(firstPts, t); + if (value === null) continue; + const y = this.yOf(value, vMin, vMax); + const color = event.color || "#03a9f4"; + const r = 10; + ctx.save(); + ctx.beginPath(); + ctx.arc(x, y, r + 1.5, 0, Math.PI * 2); + ctx.fillStyle = "rgba(255,255,255,0.9)"; + ctx.fill(); + ctx.restore(); + ctx.save(); + ctx.beginPath(); + ctx.arc(x, y, r, 0, Math.PI * 2); + ctx.fillStyle = color; + ctx.fill(); + ctx.restore(); + hits.push({ + event, + x, + y, + value + }); + } + return hits; + } + /** + * Draw a gradient-filled band between two data values, fading from the edge + * value toward the midpoint value. Used for min/max shading that fades toward + * the mean line. + * + * @param {number} valueEdge Data value at the opaque edge (the min or max line) + * @param {number} valueMid Data value at the transparent end (the mean line) + * @param {string} color Hex color string (e.g. "#03a9f4") + * @param {number} t0 Render start time ms + * @param {number} t1 Render end time ms + * @param {number} vMin Y-axis minimum data value + * @param {number} vMax Y-axis maximum data value + * @param {object} options { fillAlpha } + */ + drawGradientBand(valueEdge, valueMid, color, _t0, _t1, vMin, vMax, options = {}) { + const fillAlpha = Number.isFinite(options.fillAlpha) ? options.fillAlpha : .08; + if (fillAlpha <= 0) return; + const hexMatch = String(color || "").match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i); + if (!hexMatch) return; + const r = parseInt(hexMatch[1], 16); + const g = parseInt(hexMatch[2], 16); + const b = parseInt(hexMatch[3], 16); + const yEdge = this.yOf(valueEdge, vMin, vMax); + const yMid = this.yOf(valueMid, vMin, vMax); + if (Math.abs(yMid - yEdge) < 1) return; + const { ctx, pad } = this; + const grad = ctx.createLinearGradient(0, yEdge, 0, yMid); + grad.addColorStop(0, `rgba(${r}, ${g}, ${b}, ${fillAlpha})`); + grad.addColorStop(1, `rgba(${r}, ${g}, ${b}, 0)`); + ctx.save(); + ctx.beginPath(); + ctx.rect(pad.left, pad.top, this.cw, this.ch); + ctx.clip(); + ctx.fillStyle = grad; + ctx.fillRect(pad.left, Math.min(yEdge, yMid), this.cw, Math.abs(yMid - yEdge)); + ctx.restore(); + } + drawThresholdArea(points, thresholdValue, color, t0, t1, vMin, vMax, options = {}) { + if (!Array.isArray(points) || points.length < 2) return; + if (!Number.isFinite(thresholdValue)) return; + const mode = options.mode === "below" ? "below" : "above"; + const fillAlpha = Number.isFinite(options.fillAlpha) ? options.fillAlpha : .12; + if (fillAlpha <= 0) return; + const segments = []; + let currentSegment = []; + const isInside = (value) => { + if (mode === "below") return value <= thresholdValue; + return value >= thresholdValue; + }; + const flushSegment = () => { + if (currentSegment.length >= 2) segments.push(currentSegment); + currentSegment = []; + }; + for (let index = 0; index < points.length - 1; index += 1) { + const startPoint = points[index]; + const endPoint = points[index + 1]; + const startInside = isInside(startPoint[1]); + const endInside = isInside(endPoint[1]); + if (startInside && currentSegment.length === 0) currentSegment.push(startPoint); + if (startInside && endInside) { + currentSegment.push(endPoint); + continue; + } + if (startInside !== endInside) { + const deltaValue = endPoint[1] - startPoint[1]; + if (deltaValue === 0) { + flushSegment(); + continue; + } + const fraction = (thresholdValue - startPoint[1]) / deltaValue; + const crossingPoint = [startPoint[0] + (endPoint[0] - startPoint[0]) * fraction, thresholdValue]; + if (startInside) { + currentSegment.push(crossingPoint); + flushSegment(); + } else { + currentSegment.push(crossingPoint); + currentSegment.push(endPoint); + } + continue; + } + if (!startInside && !endInside) flushSegment(); + } + flushSegment(); + if (!segments.length) return; + const { ctx, pad } = this; + const thresholdY = this.yOf(thresholdValue, vMin, vMax); + ctx.save(); + ctx.beginPath(); + ctx.rect(pad.left, pad.top, this.cw, this.ch); + ctx.clip(); + ctx.fillStyle = hexToRgba(color, fillAlpha); + segments.forEach((segment) => { + if (!Array.isArray(segment) || segment.length < 2) return; + ctx.beginPath(); + const firstPoint = segment[0]; + ctx.moveTo(this.xOf(firstPoint[0], t0, t1), thresholdY); + segment.forEach((point) => { + ctx.lineTo(this.xOf(point[0], t0, t1), this.yOf(point[1], vMin, vMax)); + }); + const lastPoint = segment[segment.length - 1]; + ctx.lineTo(this.xOf(lastPoint[0], t0, t1), thresholdY); + ctx.closePath(); + ctx.fill(); + }); + ctx.restore(); + } + /** + * Draw diagonal hash marks at gap boundary points to indicate the start/end + * of contiguous data ranges. + * + * @param {Array} boundaryPoints Array of [timeMs, value] pairs at gap edges + * @param {string} color Stroke colour + * @param {number} t0 Start time ms + * @param {number} t1 End time ms + * @param {number} vMin Y axis min + * @param {number} vMax Y axis max + */ + drawGapMarkers(boundaryPoints, color, t0, t1, vMin, vMax) { + if (!boundaryPoints.length) return; + const { ctx, pad } = this; + const h = 7; + const w = 3; + const gap = 2; + ctx.save(); + ctx.beginPath(); + ctx.rect(pad.left, pad.top, this.cw, this.ch); + ctx.clip(); + ctx.strokeStyle = color; + ctx.lineWidth = 1.5; + ctx.globalAlpha = .55; + for (let i = 0; i < boundaryPoints.length; i++) { + const [t, v] = boundaryPoints[i]; + const x = this.xOf(t, t0, t1); + const y = this.yOf(v, vMin, vMax); + const dir = i % 2 === 0 ? 1 : -1; + for (let d = -gap; d <= gap; d += gap * 2) { + ctx.beginPath(); + ctx.moveTo(x + d - w * dir, y - h); + ctx.lineTo(x + d + w * dir, y + h); + ctx.stroke(); + } + } + ctx.restore(); + } + drawAnomalyClusters(clusters, color, t0, t1, vMin, vMax, options = {}) { + if (!Array.isArray(clusters) || clusters.length === 0) return; + const strokeAlpha = Number.isFinite(options.strokeAlpha) ? options.strokeAlpha : .92; + const lineWidth = Number.isFinite(options.lineWidth) ? options.lineWidth : 2; + const haloWidth = Number.isFinite(options.haloWidth) ? options.haloWidth : Math.max(2.5, lineWidth + 1.5); + const haloColor = typeof options.haloColor === "string" && options.haloColor ? options.haloColor : "rgba(255,255,255,0.9)"; + const haloAlpha = Number.isFinite(options.haloAlpha) ? options.haloAlpha : .9; + const fillColor = typeof options.fillColor === "string" && options.fillColor ? options.fillColor : null; + const fillAlpha = Number.isFinite(options.fillAlpha) ? options.fillAlpha : 0; + const pointPadding = Number.isFinite(options.pointPadding) ? options.pointPadding : 10; + const minRadiusX = Number.isFinite(options.minRadiusX) ? options.minRadiusX : 10; + const minRadiusY = Number.isFinite(options.minRadiusY) ? options.minRadiusY : 10; + const clusterRegions = this.getAnomalyClusterRegions(clusters, t0, t1, vMin, vMax, { + pointPadding, + minRadiusX, + minRadiusY + }); + const { ctx, pad } = this; + ctx.save(); + ctx.beginPath(); + ctx.rect(pad.left, pad.top, this.cw, this.ch); + ctx.clip(); + clusterRegions.forEach((region) => { + ctx.save(); + ctx.setLineDash([]); + if (fillColor && fillAlpha > 0) { + ctx.globalAlpha = fillAlpha; + ctx.fillStyle = fillColor; + ctx.beginPath(); + ctx.ellipse(region.centerX, region.centerY, region.radiusX, region.radiusY, 0, 0, Math.PI * 2); + ctx.fill(); + } + ctx.globalAlpha = haloAlpha; + ctx.strokeStyle = haloColor; + ctx.lineWidth = haloWidth; + ctx.beginPath(); + ctx.ellipse(region.centerX, region.centerY, region.radiusX, region.radiusY, 0, 0, Math.PI * 2); + ctx.stroke(); + ctx.globalAlpha = strokeAlpha; + ctx.strokeStyle = color; + ctx.lineWidth = lineWidth; + ctx.beginPath(); + ctx.ellipse(region.centerX, region.centerY, region.radiusX, region.radiusY, 0, 0, Math.PI * 2); + ctx.stroke(); + ctx.restore(); + }); + ctx.restore(); + } + /** + * Animate a "blip" circle at the given canvas coordinates. + * The circle expands with a bouncy overshoot, holds briefly, then shrinks to nothing. + * Uses a separate overlay canvas so it doesn't interfere with the main chart. + */ + drawBlip(cx, cy, color, options = {}) { + const resolvedOptions = options; + const maxRadius = resolvedOptions.maxRadius || 6; + const duration = resolvedOptions.duration || 600; + const canvas = this.canvas; + const parent = canvas.parentElement; + if (!parent) return; + const overlay = document.createElement("canvas"); + overlay.width = canvas.width; + overlay.height = canvas.height; + overlay.style.cssText = `position:absolute;top:0;left:0;width:${canvas.style.width || `${canvas.offsetWidth}px`};height:${canvas.style.height || `${canvas.offsetHeight}px`};pointer-events:none;z-index:2;`; + parent.style.position = parent.style.position || "relative"; + parent.appendChild(overlay); + const ctx = overlay.getContext("2d"); + if (!ctx) { + overlay.remove(); + return; + } + const dpr = window.devicePixelRatio || 1; + const pxCx = cx * dpr; + const pxCy = cy * dpr; + const pxMaxR = maxRadius * dpr; + const start = performance.now(); + const animate = (now) => { + const elapsed = now - start; + const t = Math.min(elapsed / duration, 1); + ctx.clearRect(0, 0, overlay.width, overlay.height); + let radius; + let alpha; + if (t < .35) { + const p = t / .35; + const bounce = p < .6 ? p / .6 * 1.3 : 1.3 - .3 * ((p - .6) / .4); + radius = pxMaxR * Math.min(bounce, 1.3); + alpha = Math.min(p * 2.5, .85); + } else if (t < .6) { + radius = pxMaxR; + alpha = .85; + } else { + const ease = 1 - (1 - (t - .6) / .4) ** 3; + radius = pxMaxR * (1 - ease); + alpha = .85 * (1 - ease); + } + if (radius > .2 && alpha > .01) { + ctx.save(); + ctx.globalAlpha = alpha; + ctx.beginPath(); + ctx.arc(pxCx, pxCy, radius, 0, Math.PI * 2); + ctx.fillStyle = color; + ctx.fill(); + ctx.beginPath(); + ctx.arc(pxCx, pxCy, radius * 1.6, 0, Math.PI * 2); + ctx.strokeStyle = color; + ctx.lineWidth = 1.2 * dpr; + ctx.globalAlpha = alpha * .4; + ctx.stroke(); + ctx.restore(); + } + if (t < 1) requestAnimationFrame(animate); + else overlay.remove(); + }; + requestAnimationFrame(animate); + } + getAnomalyClusterRegions(clusters, t0, t1, vMin, vMax, options = {}) { + if (!Array.isArray(clusters) || clusters.length === 0) return []; + const pointPadding = Number.isFinite(options.pointPadding) ? options.pointPadding : 10; + const minRadiusX = Number.isFinite(options.minRadiusX) ? options.minRadiusX : 10; + const minRadiusY = Number.isFinite(options.minRadiusY) ? options.minRadiusY : 10; + return clusters.flatMap((cluster) => { + if (!Array.isArray(cluster?.points) || cluster.points.length === 0) return []; + const xs = []; + const ys = []; + cluster.points.forEach((point) => { + xs.push(this.xOf(point.timeMs, t0, t1)); + ys.push(this.yOf(point.value, vMin, vMax)); + }); + const minX = Math.min(...xs); + const maxX = Math.max(...xs); + const minY = Math.min(...ys); + const maxY = Math.max(...ys); + return [{ + centerX: (minX + maxX) / 2, + centerY: (minY + maxY) / 2, + radiusX: Math.max(minRadiusX, (maxX - minX) / 2 + pointPadding), + radiusY: Math.max(minRadiusY, (maxY - minY) / 2 + pointPadding), + cluster + }]; + }); + } + }; + //#endregion + //#region custom_components/hass_datapoints/src/charts/utils/chart-dom.ts + function getRoot$1(card) { + const rootNode = card.shadowRoot ?? card.getRootNode(); + if (rootNode instanceof ShadowRoot || rootNode instanceof Document) return rootNode; + return document; + } + /** + * Reads the HA `--secondary-text-color` CSS variable from the given element so + * canvas-drawn axis labels use the correct colour for the active light/dark theme. + * Falls back to the dark-mode default when the variable is unavailable. + */ + function resolveChartLabelColor(el) { + if (!el) return "rgba(214,218,224,0.92)"; + const raw = getComputedStyle(el).getPropertyValue("--secondary-text-color").trim(); + if (raw) return raw; + return "rgba(214,218,224,0.92)"; + } + function setupCanvas(canvas, container, cssHeight, cssWidth = null) { + const dpr = window.devicePixelRatio || 1; + const maxCssDim = Math.floor(16383 / dpr); + const styles = getComputedStyle(container); + const paddingX = (Number.parseFloat(styles.paddingLeft || "0") || 0) + (Number.parseFloat(styles.paddingRight || "0") || 0); + const paddingY = (Number.parseFloat(styles.paddingTop || "0") || 0) + (Number.parseFloat(styles.paddingBottom || "0") || 0); + const measuredWidth = cssWidth ?? (container.clientWidth || 360); + const w = Math.min(maxCssDim, Math.max(1, Math.round(measuredWidth - paddingX))); + const requestedHeight = cssHeight ?? container.clientHeight ?? 220; + const h = Math.min(maxCssDim, Math.max(40, Math.round(requestedHeight - paddingY))); + canvas.width = w * dpr; + canvas.height = h * dpr; + canvas.style.width = `${w}px`; + canvas.style.height = `${h}px`; + const ctx = canvas.getContext("2d"); + if (ctx && typeof ctx.scale === "function") ctx.scale(dpr, dpr); + return { + w, + h + }; + } + function renderChartAxisOverlays(card, renderer, axes = []) { + const leftEl = getRoot$1(card)?.getElementById("chart-axis-left"); + const rightEl = getRoot$1(card)?.getElementById("chart-axis-right"); + if (!leftEl || !rightEl || !renderer) return; + const leftWidth = Math.max(0, renderer.pad.left); + const rightWidth = Math.max(0, renderer.pad.right); + leftEl.style.width = `${leftWidth}px`; + rightEl.style.width = `${rightWidth}px`; + const chartWrap = getRoot$1(card).querySelector(".chart-wrap"); + const chartWrapEl = chartWrap instanceof HTMLElement ? chartWrap : card; + if (chartWrapEl) { + chartWrapEl.style.setProperty("--dp-chart-axis-left-width", `${leftWidth}px`); + chartWrapEl.style.setProperty("--dp-chart-axis-right-width", `${rightWidth}px`); + } + const axisSlotWidth = ChartRenderer.AXIS_SLOT_WIDTH; + const axisOffset = (axis) => 10 + (axis.slot ?? 0) * axisSlotWidth; + const unitCounts = axes.reduce((counts, axis) => { + if (!axis?.unit) return counts; + counts.set(axis.unit, (counts.get(axis.unit) || 0) + 1); + return counts; + }, /* @__PURE__ */ new Map()); + const axisTextStyle = (axis) => { + if (!(!!axis?.unit && (unitCounts.get(axis.unit) || 0) > 1) || !axis?.color) return ""; + return `color:${axis.color};`; + }; + const buildAxisMarkup = (axis) => { + return b`${(axis.ticks || []).map((tick) => { + const y = renderer.yOf(tick, axis.min, axis.max); + const sideStyle = axis.side === "left" ? `right:${axisOffset(axis)}px;text-align:right;` : `left:${axisOffset(axis)}px;text-align:left;`; + return b`
+ ${renderer._formatAxisTick(tick, axis.unit)} +
`; + })}${axis.unit ? b`
+ ${axis.unit} +
` : ""}`; + }; + const leftAxes = axes.filter((axis) => axis.side !== "right"); + const rightAxes = axes.filter((axis) => axis.side === "right"); + D(leftAxes.length ? b`
+ ${leftAxes.map((axis) => buildAxisMarkup(axis))}` : b``, leftEl); + D(rightAxes.length ? b`
+ ${rightAxes.map((axis) => buildAxisMarkup(axis))}` : b``, rightEl); + leftEl.classList.toggle("visible", !!leftAxes.length); + rightEl.classList.toggle("visible", !!rightAxes.length); + } + function renderChartAxisHoverDots(card, hoverValues = []) { + const root = getRoot$1(card); + const leftEl = root.getElementById("chart-axis-left"); + const rightEl = root.getElementById("chart-axis-right"); + const scrollViewport = root.getElementById("chart-scroll-viewport"); + if (!leftEl || !rightEl) return; + leftEl.querySelectorAll(".chart-axis-hover-dot").forEach((el) => el.remove()); + rightEl.querySelectorAll(".chart-axis-hover-dot").forEach((el) => el.remove()); + const verticalOffset = scrollViewport?.offsetTop || 0; + hoverValues.filter((entry) => entry?.hasValue !== false && Number.isFinite(entry?.y)).forEach((entry) => { + const target = entry.axisSide === "right" ? rightEl : leftEl; + const dot = document.createElement("span"); + dot.className = `chart-axis-hover-dot ${entry.axisSide === "right" ? "right" : "left"}`; + dot.style.top = `${verticalOffset + entry.y}px`; + dot.style.background = entry.color || "#03a9f4"; + dot.style.opacity = `${Number.isFinite(entry.opacity) ? entry.opacity : 1}`; + target.appendChild(dot); + }); + } + function positionTooltip(tooltip, clientX, clientY, bounds = null) { + tooltip.style.display = "block"; + const tipRect = tooltip.getBoundingClientRect(); + const tipW = tipRect.width || 220; + const tipH = tipRect.height || 64; + const gap = 12; + const minLeft = Number.isFinite(bounds?.left) ? bounds?.left : gap; + const maxLeft = Number.isFinite(bounds?.right) ? bounds?.right : window.innerWidth - gap; + const minTop = Number.isFinite(bounds?.top) ? bounds?.top : gap; + const maxTop = Number.isFinite(bounds?.bottom) ? bounds?.bottom : window.innerHeight - gap; + let left = clientX + gap; + if (left + tipW > maxLeft) left = clientX - tipW - gap; + let top = clientY - tipH - gap; + if (top < minTop) top = clientY + gap; + if (top + tipH > maxTop) top = Math.max(minTop, clientY - tipH - gap); + left = Math.min(Math.max(left, minLeft), Math.max(minLeft, maxLeft - tipW)); + top = Math.min(Math.max(top, minTop), Math.max(minTop, maxTop - tipH)); + tooltip.style.left = `${left}px`; + tooltip.style.top = `${top}px`; + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/chart/chart-shell.ts + function clampChartValue(value, min, max) { + return Math.min(max, Math.max(min, value)); + } + function formatTooltipValue(value, unit = "") { + if (value == null || value === "" || Number.isNaN(Number(value))) return ""; + return `${Number(value).toFixed(2).replace(/\.00$/, "")}${unit ? ` ${unit}` : ""}`; + } + function formatTooltipDisplayValue(value, unit = "") { + if (value == null || value === "") return "No value"; + if (typeof value === "string") return unit ? `${value} ${unit}` : value; + return formatTooltipValue(value, unit); + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/ha/entity-name.ts + function entityName(hass, entityId) { + if (!hass || !entityId) return entityId || ""; + const state = hass.states?.[entityId]; + return state && state.attributes && state.attributes.friendly_name || entityId; + } + function entityIcon(hass, entityId) { + if (!hass || !entityId) return "mdi:link-variant"; + const state = hass.states?.[entityId]; + if (state?.attributes?.icon) return state.attributes.icon; + const domain = String(entityId).split(".")[0]; + const entry = hass.entities?.[entityId]; + if (entry?.icon) return entry.icon; + switch (domain) { + case "light": return "mdi:lightbulb"; + case "switch": return "mdi:toggle-switch"; + case "binary_sensor": return "mdi:radiobox-marked"; + case "sensor": return "mdi:chart-line"; + case "climate": return "mdi:thermostat"; + case "cover": return "mdi:window-shutter"; + case "lock": return "mdi:lock"; + case "media_player": return "mdi:play-box"; + case "person": return "mdi:account"; + case "device_tracker": return "mdi:crosshairs-gps"; + default: return "mdi:link-variant"; + } + } + function entityRegistryEntries(hass) { + return Object.entries(hass?.entities || {}); + } + function firstRelatedEntityId(hass, matcher) { + return entityRegistryEntries(hass).find(([, entry]) => entry && typeof entry === "object" && matcher(entry))?.[0] || ""; + } + function deviceName(hass, deviceId) { + if (!hass || !deviceId) return deviceId || ""; + return hass.devices?.[deviceId]?.name ?? deviceId; + } + function deviceIcon(hass, deviceId) { + if (!hass || !deviceId) return "mdi:devices"; + const entityId = firstRelatedEntityId(hass, (entry) => (entry.device_id || entry.deviceId) === deviceId); + return entityId ? entityIcon(hass, entityId) : "mdi:devices"; + } + function areaName(hass, areaId) { + if (!hass || !areaId) return areaId || ""; + return hass.areas?.[areaId]?.name ?? areaId; + } + function areaIcon(hass, areaId) { + if (!hass || !areaId) return "mdi:floor-plan"; + const entityId = firstRelatedEntityId(hass, (entry) => (entry.area_id || entry.areaId) === areaId); + return entityId ? entityIcon(hass, entityId) : "mdi:floor-plan"; + } + function labelName(hass, labelId) { + if (!hass || !labelId) return labelId || ""; + return hass.labels?.[labelId]?.name ?? labelId; + } + function labelIcon(hass, labelId) { + if (!hass || !labelId) return "mdi:label-outline"; + const entityId = firstRelatedEntityId(hass, (entry) => { + return [...Array.isArray(entry.labels) ? entry.labels : [], ...Array.isArray(entry.label_ids) ? entry.label_ids : []].includes(labelId); + }); + return entityId ? entityIcon(hass, entityId) : "mdi:label-outline"; + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/chart/chart-interaction.ts + /** + * Returns the DOM root that contains the chart's elements. + * For cards with a shadow root (legacy HTMLElement-based cards) this is + * `card.shadowRoot`. For sub-components that render into the parent's shadow + * DOM (like `hass-datapoints-history-chart`) there is no own shadow root, so we fall back + * to `getRootNode()` which returns the ancestor ShadowRoot. + */ + function getRoot(card) { + const rootNode = card.shadowRoot ?? card.getRootNode(); + if (rootNode instanceof ShadowRoot || rootNode instanceof Document) return rootNode; + return document; + } + function getInteractionState(card) { + return card; + } + function toChartBounds(bounds) { + if (!bounds) return null; + return { + left: bounds.left + 8, + right: bounds.right - 8, + top: bounds.top + 8, + bottom: bounds.bottom - 8 + }; + } + function formatTooltipDateTimeFromMs(timeMs) { + if (!Number.isFinite(timeMs)) return ""; + return fmtDateTime(new Date(timeMs).toISOString()); + } + function t$2(key, ...values) { + let s = msg(key); + values.forEach((v, i) => { + s = s.replace(new RegExp(`\\{${i}\\}`, "g"), v); + }); + return s; + } + function getAnomalyMethodLabels() { + return { + trend_residual: msg("Trend deviation"), + rate_of_change: msg("Sudden change"), + iqr: msg("Statistical outlier (IQR)"), + rolling_zscore: msg("Rolling Z-score"), + persistence: msg("Flat-line / stuck"), + comparison_window: msg("Comparison window") + }; + } + function buildAnomalyMethodSection(region) { + if (!region?.cluster?.points?.length) return null; + const points = region.cluster.points; + const startPoint = points[0]; + const endPoint = points[points.length - 1]; + const peakPoint = points.reduce((peak, p) => !peak || Math.abs(p.residual) > Math.abs(peak.residual) ? p : peak, null); + if (!peakPoint) return null; + const label = region.label || region.relatedEntityId || "Series"; + const unit = region.unit || ""; + const cluster = region.cluster; + const method = cluster.anomalyMethod ?? "trend_residual"; + const methodLabel = getAnomalyMethodLabels()[method] || method; + let description; + let alert; + if (method === "rate_of_change") { + const rateUnit = unit ? `${unit}/h` : "units/h"; + description = t$2("{0} shows an unusual rate of change between {1} and {2}.", label, formatTooltipDateTimeFromMs(startPoint.timeMs), formatTooltipDateTimeFromMs(endPoint.timeMs)); + alert = t$2("Peak rate deviation: {0} from a typical rate of {1} at {2}.", formatTooltipValue(peakPoint.residual, rateUnit), formatTooltipValue(peakPoint.baselineValue, rateUnit), formatTooltipDateTimeFromMs(peakPoint.timeMs)); + } else if (method === "iqr") { + description = t$2("{0} contains statistical outliers between {1} and {2}.", label, formatTooltipDateTimeFromMs(startPoint.timeMs), formatTooltipDateTimeFromMs(endPoint.timeMs)); + alert = t$2("Peak value: {0}, deviating {1} from the median at {2}.", formatTooltipValue(peakPoint.value, unit), formatTooltipValue(Math.abs(peakPoint.residual), unit), formatTooltipDateTimeFromMs(peakPoint.timeMs)); + } else if (method === "rolling_zscore") { + description = t$2("{0} shows statistically unusual values between {1} and {2}.", label, formatTooltipDateTimeFromMs(startPoint.timeMs), formatTooltipDateTimeFromMs(endPoint.timeMs)); + alert = t$2("Peak deviation: {0} from a rolling mean of {1} at {2}.", formatTooltipValue(peakPoint.residual, unit), formatTooltipValue(peakPoint.baselineValue, unit), formatTooltipDateTimeFromMs(peakPoint.timeMs)); + } else if (method === "persistence") { + const flatRange = typeof cluster.flatRange === "number" ? cluster.flatRange : null; + const rangeStr = flatRange !== null ? t$2(" (range: {0})", formatTooltipValue(flatRange, unit)) : ""; + description = t$2("{0} appears stuck or flat between {1} and {2}{3}.", label, formatTooltipDateTimeFromMs(startPoint.timeMs), formatTooltipDateTimeFromMs(endPoint.timeMs), rangeStr); + alert = t$2("Value remained near {0} for an unusually long period.", formatTooltipValue(peakPoint.baselineValue, unit)); + } else if (method === "comparison_window") { + description = t$2("{0} deviates significantly from the comparison window between {1} and {2}.", label, formatTooltipDateTimeFromMs(startPoint.timeMs), formatTooltipDateTimeFromMs(endPoint.timeMs)); + alert = t$2("Peak deviation from comparison: {0} at {1}.", formatTooltipValue(peakPoint.residual, unit), formatTooltipDateTimeFromMs(peakPoint.timeMs)); + } else { + description = t$2("{0} deviates from its expected trend between {1} and {2}.", label, formatTooltipDateTimeFromMs(startPoint.timeMs), formatTooltipDateTimeFromMs(endPoint.timeMs)); + alert = t$2("Peak deviation: {0} from a baseline of {1} at {2}.", formatTooltipValue(peakPoint.residual, unit), formatTooltipValue(peakPoint.baselineValue, unit), formatTooltipDateTimeFromMs(peakPoint.timeMs)); + } + return { + methodLabel, + description, + alert + }; + } + function buildAnomalyTooltipContent(regions) { + let regionsArray; + if (Array.isArray(regions)) regionsArray = regions; + else if (regions) regionsArray = [regions]; + else regionsArray = []; + if (regionsArray.length === 0) return null; + const sections = regionsArray.map(buildAnomalyMethodSection).filter((section) => section !== null); + if (sections.length === 0) return null; + const instruction = msg("Click the highlighted circle to add an annotation.", { id: "Click the highlighted circle to add an annotation." }); + if (sections.length === 1) { + const section = sections[0]; + const cluster = regionsArray[0]?.cluster; + const detectedByMethods = Array.isArray(cluster?.detectedByMethods) && cluster.detectedByMethods.length > 1 ? cluster.detectedByMethods : null; + const isMultiMethod = detectedByMethods !== null; + const title = isMultiMethod ? msg("⚠️ Multi-method Anomaly") : msg("⚠️ Anomaly Insight"); + const labels = getAnomalyMethodLabels(); + const confirmedNote = isMultiMethod ? `\n${msg("Confirmed by")} ${detectedByMethods.length} ${msg("methods:")} ${detectedByMethods.map((method) => labels[method] || method).join(", ")}.` : ""; + return { + title, + description: section.description + confirmedNote, + alert: `${msg("Alert:")} ${section.alert}`, + instruction + }; + } + const description = sections.map((s) => `${s.methodLabel}:\n${s.description}`).join("\n\n"); + const alert = sections.map((s) => `${s.methodLabel}: ${s.alert}`).join("\n"); + return { + title: msg("⚠️ Multi-method Anomaly"), + description, + alert, + instruction + }; + } + function positionAnomalyTooltip(tooltip, clientX, clientY, mainTooltip, bounds = null) { + if (!tooltip) return; + tooltip.style.display = "block"; + const tipRect = tooltip.getBoundingClientRect(); + const tipW = tipRect.width || 220; + const tipH = tipRect.height || 64; + const gap = 12; + const minLeft = Number.isFinite(bounds?.left) ? bounds?.left : gap; + const maxLeft = Number.isFinite(bounds?.right) ? bounds?.right : window.innerWidth - gap; + const minTop = Number.isFinite(bounds?.top) ? bounds?.top : gap; + const maxTop = Number.isFinite(bounds?.bottom) ? bounds?.bottom : window.innerHeight - gap; + let left = clientX - gap - tipW; + if (left < minLeft) { + const mainRect = mainTooltip ? mainTooltip.getBoundingClientRect() : null; + left = mainRect ? mainRect.right + gap : clientX + gap; + } + const mainRect = mainTooltip ? mainTooltip.getBoundingClientRect() : null; + let top = mainRect ? mainRect.top : clientY - tipH - gap; + if (top + tipH > maxTop) top = Math.max(minTop, maxTop - tipH); + left = Math.min(Math.max(left, minLeft), Math.max(minLeft, maxLeft - tipW)); + top = Math.min(Math.max(top, minTop), Math.max(minTop, maxTop - tipH)); + tooltip.style.left = `${left}px`; + tooltip.style.top = `${top}px`; + } + function positionSecondaryTooltip(tooltip, anchorTooltip, bounds = null) { + if (!tooltip || !anchorTooltip) return; + tooltip.style.display = "block"; + const anchorRect = anchorTooltip.getBoundingClientRect(); + const tipRect = tooltip.getBoundingClientRect(); + const gap = 10; + const minLeft = Number.isFinite(bounds?.left) ? bounds?.left : gap; + const maxLeft = Number.isFinite(bounds?.right) ? bounds?.right : window.innerWidth - gap; + const minTop = Number.isFinite(bounds?.top) ? bounds?.top : gap; + const maxTop = Number.isFinite(bounds?.bottom) ? bounds?.bottom : window.innerHeight - gap; + let left = anchorRect.right + gap; + if (left + tipRect.width > maxLeft) left = anchorRect.left - tipRect.width - gap; + let top = anchorRect.top; + if (top + tipRect.height > maxTop) top = Math.max(minTop, maxTop - tipRect.height); + left = Math.min(Math.max(left, minLeft), Math.max(minLeft, maxLeft - tipRect.width)); + top = Math.min(Math.max(top, minTop), Math.max(minTop, maxTop - tipRect.height)); + tooltip.style.left = `${left}px`; + tooltip.style.top = `${top}px`; + } + function positionTooltipBelow(tooltip, anchorTooltip, bounds = null) { + if (!tooltip || !anchorTooltip) return; + tooltip.style.display = "block"; + const anchorRect = anchorTooltip.getBoundingClientRect(); + const tipRect = tooltip.getBoundingClientRect(); + const gap = 8; + const minLeft = Number.isFinite(bounds?.left) ? bounds?.left : gap; + const maxLeft = Number.isFinite(bounds?.right) ? bounds?.right : window.innerWidth - gap; + const minTop = Number.isFinite(bounds?.top) ? bounds?.top : gap; + const maxTop = Number.isFinite(bounds?.bottom) ? bounds?.bottom : window.innerHeight - gap; + let left = anchorRect.left; + if (left + tipRect.width > maxLeft) left = Math.max(minLeft, maxLeft - tipRect.width); + let top = anchorRect.bottom + gap; + if (top + tipRect.height > maxTop) top = Math.max(minTop, anchorRect.top - tipRect.height - gap); + left = Math.min(Math.max(left, minLeft), Math.max(minLeft, maxLeft - tipRect.width)); + top = Math.min(Math.max(top, minTop), Math.max(minTop, maxTop - tipRect.height)); + tooltip.style.left = `${left}px`; + tooltip.style.top = `${top}px`; + } + function getAnnotationTooltipContainer(card) { + if (!card || !getRoot(card)) return null; + return getRoot(card).getElementById("annotation-tooltips"); + } + function clearAnnotationTooltips(card) { + const container = getAnnotationTooltipContainer(card); + if (!container) return; + container.innerHTML = ""; + } + function buildAnnotationTooltip(card, event) { + const interactionState = getInteractionState(card); + const tooltip = document.createElement("div"); + tooltip.className = "tooltip secondary annotation-tooltip"; + const hasValue = event?.chart_value != null && event.chart_value !== ""; + const message = event?.message || "Data point"; + const annotation = event?.annotation && event.annotation !== event.message ? event.annotation : ""; + const chips = buildTooltipRelatedChips(interactionState._hass, event); + D(b` +
${fmtDateTime(event.timestamp)}
+ ${hasValue ? b`
+ ${formatTooltipValue(event.chart_value, event.chart_unit)} +
` : ""} +
+ + ${message} +
+
+ ${annotation} +
+
+ ${chips} +
+ `, tooltip); + return tooltip; + } + function renderAnnotationTooltips(card, hover, anchorTooltip, bounds = null) { + const container = getAnnotationTooltipContainer(card); + if (!container) return []; + clearAnnotationTooltips(card); + const annotationEvents = Array.isArray(hover?.events) ? hover.events : []; + if (!annotationEvents.length) return []; + const renderedTooltips = []; + let anchorEl = anchorTooltip; + for (const event of annotationEvents) { + const tooltip = buildAnnotationTooltip(card, event); + container.appendChild(tooltip); + if (renderedTooltips.length === 0) positionSecondaryTooltip(tooltip, anchorEl, bounds); + else positionTooltipBelow(tooltip, anchorEl, bounds); + renderedTooltips.push(tooltip); + anchorEl = tooltip; + } + return renderedTooltips; + } + function hideTooltip(card) { + const tooltip = getRoot(card).getElementById("tooltip"); + const anomalyTooltip = getRoot(card).getElementById("anomaly-tooltip"); + if (tooltip) tooltip.style.display = "none"; + if (anomalyTooltip) anomalyTooltip.style.display = "none"; + clearAnnotationTooltips(card); + } + function resolveTooltipSeriesLabel(entry) { + const isSubordinate = entry.grouped === true && entry.rawVisible === true; + const isComparisonDerived = entry.comparisonDerived === true && entry.grouped === true; + if (entry.comparison === true) { + const windowLabel = String(entry.windowLabel || msg("Date window")); + if (entry.grouped === true) return windowLabel; + return `${windowLabel}: ${String(entry.label || "")}`; + } + if (entry.trend === true) { + const trendLabel = msg("Trend"); + if (isSubordinate || isComparisonDerived) return trendLabel; + return `${trendLabel}: ${entry.baseLabel || entry.label || ""}`; + } + if (entry.rate === true) { + const rateLabel = msg("Rate"); + if (isSubordinate || isComparisonDerived) return rateLabel; + return `${rateLabel}: ${entry.baseLabel || entry.label || ""}`; + } + if (entry.delta === true) { + const deltaLabel = msg("Delta"); + if (isSubordinate || isComparisonDerived) return deltaLabel; + return `${deltaLabel}: ${entry.baseLabel || entry.label || ""}`; + } + if (entry.summary === true) { + const summaryLabel = String(entry.summaryType || "").toUpperCase(); + if (isSubordinate || isComparisonDerived) return summaryLabel; + return `${summaryLabel}: ${entry.baseLabel || entry.label || ""}`; + } + if (entry.threshold === true) { + const thresholdLabel = msg("Threshold"); + if (isSubordinate || isComparisonDerived) return thresholdLabel; + return `${thresholdLabel}: ${entry.baseLabel || entry.label || ""}`; + } + return String(entry.label || ""); + } + function showLineChartTooltip(card, hover, clientX, clientY) { + const root = getRoot(card); + const tooltip = root.getElementById("tooltip"); + const ttTime = root.getElementById("tt-time"); + const ttValue = root.getElementById("tt-value"); + const ttSeries = root.getElementById("tt-series"); + const anomalyTooltip = root.getElementById("anomaly-tooltip"); + const ttSecondaryTitle = root.getElementById("tt-secondary-title"); + const ttSecondaryDescription = root.getElementById("tt-secondary-description"); + const ttSecondaryAlert = root.getElementById("tt-secondary-alert"); + const ttSecondaryInstruction = root.getElementById("tt-secondary-instruction"); + const ttMessageRow = root.getElementById("tt-message-row"); + const ttMsg = root.getElementById("tt-message"); + const ttAnn = root.getElementById("tt-annotation"); + const ttEntities = root.getElementById("tt-entities"); + if (!tooltip || !ttTime || !ttValue || !ttMessageRow || !ttMsg || !ttAnn || !ttEntities) return; + const rangeStartMs = Number.isFinite(hover.rangeStartMs) ? hover.rangeStartMs : hover.timeMs; + const rangeEndMs = Number.isFinite(hover.rangeEndMs) ? hover.rangeEndMs : hover.timeMs; + ttTime.textContent = rangeStartMs === rangeEndMs ? fmtDateTime(new Date(hover.timeMs).toISOString()) : `${fmtDateTime(new Date(rangeStartMs).toISOString())} - ${fmtDateTime(new Date(rangeEndMs).toISOString())}`; + const values = Array.isArray(hover.values) ? hover.values : []; + const trendValues = Array.isArray(hover.trendValues) ? hover.trendValues : []; + const rateValues = Array.isArray(hover.rateValues) ? hover.rateValues : []; + const deltaValues = Array.isArray(hover.deltaValues) ? hover.deltaValues : []; + const summaryValues = Array.isArray(hover.summaryValues) ? hover.summaryValues : []; + const thresholdValues = Array.isArray(hover.thresholdValues) ? hover.thresholdValues : []; + const binaryValues = Array.isArray(hover.binaryValues) ? hover.binaryValues : []; + const comparisonValues = Array.isArray(hover.comparisonValues) ? hover.comparisonValues : []; + const displayRows = []; + const usedTrendRows = /* @__PURE__ */ new Set(); + const usedRateRows = /* @__PURE__ */ new Set(); + const usedDeltaRows = /* @__PURE__ */ new Set(); + const usedSummaryRows = /* @__PURE__ */ new Set(); + const usedThresholdRows = /* @__PURE__ */ new Set(); + const usedComparisonRows = /* @__PURE__ */ new Set(); + const pushComparisonDerivedRows = (comparisonEntry, comparisonIndex) => { + trendValues.forEach((trendEntry, trendIndex) => { + if (usedTrendRows.has(trendIndex)) return; + if (trendEntry.comparisonParentId !== comparisonEntry.entityId && !(trendEntry.relatedEntityId === comparisonEntry.relatedEntityId && trendEntry.windowLabel === comparisonEntry.windowLabel)) return; + usedTrendRows.add(trendIndex); + displayRows.push({ + ...trendEntry, + rawVisible: true, + comparisonDerived: true, + grouped: true, + key: `comparison-trend-${comparisonIndex}-${trendIndex}` + }); + }); + rateValues.forEach((rateEntry, rateIndex) => { + if (usedRateRows.has(rateIndex)) return; + if (rateEntry.comparisonParentId !== comparisonEntry.entityId && !(rateEntry.relatedEntityId === comparisonEntry.relatedEntityId && rateEntry.windowLabel === comparisonEntry.windowLabel)) return; + usedRateRows.add(rateIndex); + displayRows.push({ + ...rateEntry, + rawVisible: true, + comparisonDerived: true, + grouped: true, + key: `comparison-rate-${comparisonIndex}-${rateIndex}` + }); + }); + summaryValues.forEach((summaryEntry, summaryIndex) => { + if (usedSummaryRows.has(summaryIndex)) return; + if (summaryEntry.comparisonParentId !== comparisonEntry.entityId && !(summaryEntry.relatedEntityId === comparisonEntry.relatedEntityId && summaryEntry.windowLabel === comparisonEntry.windowLabel)) return; + usedSummaryRows.add(summaryIndex); + displayRows.push({ + ...summaryEntry, + rawVisible: true, + comparisonDerived: true, + grouped: true, + key: `comparison-summary-${comparisonIndex}-${summaryIndex}` + }); + }); + thresholdValues.forEach((thresholdEntry, thresholdIndex) => { + if (usedThresholdRows.has(thresholdIndex)) return; + if (thresholdEntry.comparisonParentId !== comparisonEntry.entityId && !(thresholdEntry.relatedEntityId === comparisonEntry.relatedEntityId && thresholdEntry.windowLabel === comparisonEntry.windowLabel)) return; + usedThresholdRows.add(thresholdIndex); + displayRows.push({ + ...thresholdEntry, + rawVisible: true, + comparisonDerived: true, + grouped: true, + key: `comparison-threshold-${comparisonIndex}-${thresholdIndex}` + }); + }); + }; + values.forEach((entry, index) => { + displayRows.push(entry); + trendValues.forEach((trendEntry, trendIndex) => { + if (usedTrendRows.has(trendIndex)) return; + const sameEntity = trendEntry.relatedEntityId && trendEntry.relatedEntityId === entry.entityId; + const sameLabel = !trendEntry.relatedEntityId && trendEntry.baseLabel && trendEntry.baseLabel === entry.label; + if (!sameEntity && !sameLabel) return; + usedTrendRows.add(trendIndex); + displayRows.push({ + ...trendEntry, + rawVisible: trendEntry.rawVisible !== false, + grouped: true, + key: `trend-${index}-${trendIndex}` + }); + }); + rateValues.forEach((rateEntry, rateIndex) => { + if (usedRateRows.has(rateIndex)) return; + const sameEntity = rateEntry.relatedEntityId && rateEntry.relatedEntityId === entry.entityId; + const sameLabel = !rateEntry.relatedEntityId && rateEntry.baseLabel && rateEntry.baseLabel === entry.label; + if (!sameEntity && !sameLabel) return; + usedRateRows.add(rateIndex); + displayRows.push({ + ...rateEntry, + rawVisible: rateEntry.rawVisible !== false, + grouped: true, + key: `rate-${index}-${rateIndex}` + }); + }); + deltaValues.forEach((deltaEntry, deltaIndex) => { + if (usedDeltaRows.has(deltaIndex)) return; + const sameEntity = deltaEntry.relatedEntityId && deltaEntry.relatedEntityId === entry.entityId; + const sameLabel = !deltaEntry.relatedEntityId && deltaEntry.baseLabel && deltaEntry.baseLabel === entry.label; + if (!sameEntity && !sameLabel) return; + usedDeltaRows.add(deltaIndex); + displayRows.push({ + ...deltaEntry, + rawVisible: deltaEntry.rawVisible !== false, + grouped: true, + key: `delta-${index}-${deltaIndex}` + }); + }); + summaryValues.forEach((summaryEntry, summaryIndex) => { + if (usedSummaryRows.has(summaryIndex)) return; + const sameEntity = summaryEntry.relatedEntityId && summaryEntry.relatedEntityId === entry.entityId; + const sameLabel = !summaryEntry.relatedEntityId && summaryEntry.baseLabel && summaryEntry.baseLabel === entry.label; + if (!sameEntity && !sameLabel) return; + usedSummaryRows.add(summaryIndex); + displayRows.push({ + ...summaryEntry, + rawVisible: summaryEntry.rawVisible !== false, + grouped: true, + key: `summary-${index}-${summaryIndex}` + }); + }); + thresholdValues.forEach((thresholdEntry, thresholdIndex) => { + if (usedThresholdRows.has(thresholdIndex)) return; + const sameEntity = thresholdEntry.relatedEntityId && thresholdEntry.relatedEntityId === entry.entityId; + const sameLabel = !thresholdEntry.relatedEntityId && thresholdEntry.baseLabel && thresholdEntry.baseLabel === entry.label; + if (!sameEntity && !sameLabel) return; + usedThresholdRows.add(thresholdIndex); + displayRows.push({ + ...thresholdEntry, + rawVisible: thresholdEntry.rawVisible !== false, + grouped: true, + key: `threshold-${index}-${thresholdIndex}` + }); + }); + comparisonValues.forEach((compEntry, compIndex) => { + if (usedComparisonRows.has(compIndex)) return; + if (!compEntry.relatedEntityId || compEntry.relatedEntityId !== entry.entityId) return; + usedComparisonRows.add(compIndex); + const groupedEntry = { + ...compEntry, + grouped: true, + comparison: true, + key: `comparison-${index}-${compIndex}` + }; + displayRows.push(groupedEntry); + pushComparisonDerivedRows(groupedEntry, compIndex); + }); + }); + trendValues.forEach((trendEntry, trendIndex) => { + if (usedTrendRows.has(trendIndex)) return; + if (trendEntry.comparisonDerived === true || typeof trendEntry.comparisonParentId === "string") return; + displayRows.push({ + ...trendEntry, + rawVisible: trendEntry.rawVisible !== false + }); + }); + rateValues.forEach((rateEntry, rateIndex) => { + if (usedRateRows.has(rateIndex)) return; + if (rateEntry.comparisonDerived === true || typeof rateEntry.comparisonParentId === "string") return; + displayRows.push({ + ...rateEntry, + rawVisible: rateEntry.rawVisible !== false + }); + }); + deltaValues.forEach((deltaEntry, deltaIndex) => { + if (usedDeltaRows.has(deltaIndex)) return; + displayRows.push({ + ...deltaEntry, + rawVisible: deltaEntry.rawVisible !== false + }); + }); + summaryValues.forEach((summaryEntry, summaryIndex) => { + if (usedSummaryRows.has(summaryIndex)) return; + if (summaryEntry.comparisonDerived === true || typeof summaryEntry.comparisonParentId === "string") return; + displayRows.push({ + ...summaryEntry, + rawVisible: summaryEntry.rawVisible !== false + }); + }); + thresholdValues.forEach((thresholdEntry, thresholdIndex) => { + if (usedThresholdRows.has(thresholdIndex)) return; + if (thresholdEntry.comparisonDerived === true || typeof thresholdEntry.comparisonParentId === "string") return; + displayRows.push({ + ...thresholdEntry, + rawVisible: thresholdEntry.rawVisible !== false + }); + }); + comparisonValues.forEach((compEntry, compIndex) => { + if (usedComparisonRows.has(compIndex)) return; + const groupedEntry = { + ...compEntry, + comparison: true + }; + displayRows.push(groupedEntry); + pushComparisonDerivedRows(groupedEntry, compIndex); + }); + displayRows.push(...binaryValues); + if (displayRows.length === 1 && trendValues.length === 0 && rateValues.length === 0 && deltaValues.length === 0 && summaryValues.length === 0 && thresholdValues.length === 0 && comparisonValues.length === 0 && binaryValues.length === 0 && displayRows[0]?.comparison !== true) { + const value = displayRows[0]; + ttValue.textContent = value ? formatTooltipDisplayValue(value.value, value.unit) : ""; + ttValue.style.display = value ? "block" : "none"; + if (ttSeries) { + D(b``, ttSeries); + ttSeries.style.display = "none"; + } + } else { + ttValue.textContent = ""; + ttValue.style.display = "none"; + if (ttSeries) { + D(b`${displayRows.map((entry) => b` +
+
+ ${entry.grouped === true && entry.rawVisible === true ? "" : b``} + ${resolveTooltipSeriesLabel(entry)} +
+ ${formatTooltipDisplayValue(entry.value, entry.unit)} +
+ `)}`, ttSeries); + ttSeries.style.display = displayRows.length ? "grid" : "none"; + } + } + ttMessageRow.style.display = "none"; + ttMsg.textContent = ""; + ttAnn.textContent = ""; + ttAnn.style.display = "none"; + D(b``, ttEntities); + ttEntities.style.display = "none"; + if (anomalyTooltip && ttSecondaryTitle && ttSecondaryDescription && ttSecondaryAlert && ttSecondaryInstruction) { + const anomalyContent = buildAnomalyTooltipContent(hover.anomalyRegions); + if (anomalyContent) { + ttSecondaryTitle.textContent = anomalyContent.title; + ttSecondaryDescription.textContent = anomalyContent.description; + ttSecondaryAlert.textContent = anomalyContent.alert; + ttSecondaryInstruction.textContent = anomalyContent.instruction; + } else { + ttSecondaryTitle.textContent = ""; + ttSecondaryDescription.textContent = ""; + ttSecondaryAlert.textContent = ""; + ttSecondaryInstruction.textContent = ""; + anomalyTooltip.style.display = "none"; + } + } + const chartBounds = (root.querySelector(".chart-wrap") ?? card).getBoundingClientRect(); + positionTooltip(tooltip, clientX, clientY, toChartBounds(chartBounds)); + if (anomalyTooltip && (hover.anomalyRegions?.length ?? 0) > 0) positionAnomalyTooltip(anomalyTooltip, clientX, clientY, tooltip, toChartBounds(chartBounds)); + if (Array.isArray(hover.events) && hover.events.length > 0) renderAnnotationTooltips(card, hover, tooltip, toChartBounds(chartBounds)); + else clearAnnotationTooltips(card); + } + function buildTooltipRelatedChips(hass, event) { + const entities = Array.isArray(event?.entity_ids) ? event.entity_ids : []; + const devices = Array.isArray(event?.device_ids) ? event.device_ids : []; + const areas = Array.isArray(event?.area_ids) ? event.area_ids : []; + const labels = Array.isArray(event?.label_ids) ? event.label_ids : []; + const chips = [ + ...entities.map((id) => ({ + icon: entityIcon(hass, id), + label: entityName(hass, id) + })), + ...devices.map((id) => ({ + icon: deviceIcon(hass, id), + label: deviceName(hass, id) + })), + ...areas.map((id) => ({ + icon: areaIcon(hass, id), + label: areaName(hass, id) + })), + ...labels.map((id) => ({ + icon: labelIcon(hass, id), + label: labelName(hass, id) + })) + ].filter((chip) => chip.label); + if (!chips.length) return null; + return b`${chips.map((chip) => b` + + + ${chip.label} + + `)}`; + } + function showLineChartCrosshair(card, renderer, hover) { + const overlay = getRoot(card).getElementById("chart-crosshair"); + const vertical = getRoot(card).getElementById("crosshair-vertical"); + const horizontal = getRoot(card).getElementById("crosshair-horizontal"); + const points = getRoot(card).getElementById("crosshair-points"); + const addButton = getRoot(card).getElementById("chart-add-annotation"); + if (!overlay || !vertical || !horizontal || !points) return; + overlay.hidden = false; + vertical.style.left = `${hover.x}px`; + if (hover.splitVertical) { + vertical.style.top = `${hover.splitVertical.top}px`; + vertical.style.height = `${hover.splitVertical.height}px`; + } else { + vertical.style.top = `${renderer.pad.top}px`; + vertical.style.height = `${renderer.ch}px`; + } + horizontal.hidden = true; + const crosshairValues = [ + ...hover.values || [], + ...hover.showTrendCrosshairs === true ? (hover.trendValues || []).filter((entry) => entry.showCrosshair === true) : [], + ...hover.showRateCrosshairs === true ? (hover.rateValues || []).filter((entry) => entry.showCrosshair === true) : [], + ...hover.comparisonValues || [] + ]; + D(b` + ${crosshairValues.filter((entry) => entry.hasValue !== false).map((entry) => b` + + `)} + ${crosshairValues.filter((entry) => entry.hasValue !== false).map((entry) => b` + + `)} + `, points); + renderChartAxisHoverDots(card, crosshairValues); + if (addButton && addButton.dataset.allowAddAnnotation !== "false") { + addButton.hidden = false; + addButton.style.left = `${hover.x}px`; + if (hover.splitVertical) addButton.style.top = `${hover.splitVertical.top + hover.splitVertical.height}px`; + else addButton.style.top = `${renderer.pad.top + renderer.ch}px`; + } + } + function dispatchLineChartHover(card, hover) { + card.dispatchEvent(new CustomEvent("hass-datapoints-chart-hover", { + bubbles: true, + composed: true, + detail: hover ? { timeMs: hover.timeMs } : { timeMs: null } + })); + } + function findNearestSeriesPointTime(seriesPoints, timeMs) { + if (!Array.isArray(seriesPoints) || seriesPoints.length === 0) return null; + let lo = 0; + let hi = seriesPoints.length - 1; + while (lo + 1 < hi) { + const mid = Math.floor((lo + hi) / 2); + if (seriesPoints[mid][0] <= timeMs) lo = mid; + else hi = mid; + } + const left = seriesPoints[lo]?.[0]; + const right = seriesPoints[hi]?.[0]; + if (!Number.isFinite(left) && !Number.isFinite(right)) return null; + if (!Number.isFinite(left)) return right; + if (!Number.isFinite(right)) return left; + return Math.abs(left - timeMs) <= Math.abs(right - timeMs) ? left : right; + } + function resolveLineChartHoverTime(series, timeMs, mode = "follow_series") { + if (mode !== "snap_to_data_points") return timeMs; + let bestTime = null; + let bestDistance = Infinity; + for (const seriesItem of Array.isArray(series) ? series : []) { + const candidateTime = findNearestSeriesPointTime(seriesItem?.pts, timeMs); + if (candidateTime == null || !Number.isFinite(candidateTime)) continue; + const distance = Math.abs(candidateTime - timeMs); + if (distance < bestDistance) { + bestDistance = distance; + bestTime = candidateTime; + } + } + return bestTime != null && Number.isFinite(bestTime) ? bestTime : timeMs; + } + function hideLineChartHover(card) { + dispatchLineChartHover(card, null); + hideTooltip(card); + const overlay = getRoot(card).getElementById("chart-crosshair"); + const points = getRoot(card).getElementById("crosshair-points"); + const addButton = getRoot(card).getElementById("chart-add-annotation"); + if (overlay) overlay.hidden = true; + if (points) D(b``, points); + renderChartAxisHoverDots(card, []); + const horizontal = getRoot(card).getElementById("crosshair-horizontal"); + if (horizontal) horizontal.hidden = true; + if (addButton) addButton.hidden = true; + } + function attachLineChartHover(card, canvas, renderer, series, events, t0, t1, vMin, vMax, axes = null, options = {}) { + const interactionState = getInteractionState(card); + if (!canvas || !renderer) return; + if (interactionState._chartHoverCleanup) { + interactionState._chartHoverCleanup(); + interactionState._chartHoverCleanup = null; + } + const resolvedSeries = Array.isArray(series) ? series : []; + const eventThresholdMs = renderer.cw ? 14 * ((t1 - t0) / renderer.cw) : 0; + const binaryStates = Array.isArray(options.binaryStates) ? options.binaryStates : []; + const comparisonSeries = Array.isArray(options.comparisonSeries) ? options.comparisonSeries : []; + const trendSeries = Array.isArray(options.trendSeries) ? options.trendSeries : []; + const rateSeries = Array.isArray(options.rateSeries) ? options.rateSeries : []; + const deltaSeries = Array.isArray(options.deltaSeries) ? options.deltaSeries : []; + const summarySeries = Array.isArray(options.summarySeries) ? options.summarySeries : []; + const thresholdSeries = Array.isArray(options.thresholdSeries) ? options.thresholdSeries : []; + const anomalyRegions = Array.isArray(options.anomalyRegions) ? options.anomalyRegions : []; + if (!resolvedSeries.length && !binaryStates.length && !comparisonSeries.length && !trendSeries.length && !rateSeries.length && !deltaSeries.length && !summarySeries.length && !thresholdSeries.length && !anomalyRegions.length) return; + const hoverSurfaceEl = options.hoverSurfaceEl || null; + const addAnnotationButton = getRoot(card)?.getElementById("chart-add-annotation") || null; + const resolveHoverAxis = (seriesItem) => seriesItem.axis || axes && axes[0] || { + min: vMin, + max: vMax + }; + const buildHoverValueEntry = (seriesItem, value, axis, extra = {}, entryOpts = {}) => { + const hasNumericValue = typeof value === "number" && Number.isFinite(value); + const includePosition = entryOpts.includePosition === true && hasNumericValue; + return { + entityId: seriesItem.entityId || "", + comparisonParentId: seriesItem.comparisonParentId || "", + relatedEntityId: seriesItem.relatedEntityId || "", + label: seriesItem.label || seriesItem.entityId || "", + baseLabel: seriesItem.baseLabel || "", + windowLabel: seriesItem.windowLabel || "", + value: hasNumericValue ? value : value ?? null, + unit: seriesItem.unit || "", + color: seriesItem.color, + opacity: Number.isFinite(seriesItem.hoverOpacity) ? seriesItem.hoverOpacity : 1, + hasValue: hasNumericValue || value != null, + x: includePosition ? entryOpts.x : void 0, + y: includePosition ? renderer.yOf(value, axis.min, axis.max) : void 0, + axisSide: axis.side === "right" ? "right" : "left", + axisSlot: Number.isFinite(axis.slot) ? axis.slot : 0, + rawVisible: seriesItem.rawVisible !== false, + comparisonDerived: seriesItem.comparisonDerived === true, + showCrosshair: seriesItem.showCrosshair === true, + ...extra + }; + }; + const findAnomalyRegions = (clientX, clientY) => { + const rect = canvas.getBoundingClientRect(); + if (!rect.width || !rect.height) return []; + const localX = clientX - rect.left; + const localY = clientY - rect.top; + const hits = []; + for (const region of anomalyRegions) { + const radiusX = Number(region?.radiusX) || 0; + const radiusY = Number(region?.radiusY) || 0; + if (radiusX <= 0 || radiusY <= 0) continue; + const dx = (localX - region.centerX) / radiusX; + const dy = (localY - region.centerY) / radiusY; + if (dx * dx + dy * dy <= 1) hits.push(region); + } + return hits; + }; + const buildHoverState = (clientX, clientY) => { + const rect = canvas.getBoundingClientRect(); + if (!rect.width || !rect.height || !renderer.cw || !renderer.ch) return null; + const localX = clampChartValue(clientX - rect.left, renderer.pad.left, renderer.pad.left + renderer.cw); + const localY = clampChartValue(clientY - rect.top, renderer.pad.top, renderer.pad.top + renderer.ch); + const timeMs = resolveLineChartHoverTime(resolvedSeries, t0 + (renderer.cw ? (localX - renderer.pad.left) / renderer.cw : 0) * (t1 - t0), options.hoverSnapMode || "follow_series"); + const x = renderer.xOf(timeMs, t0, t1); + const values = resolvedSeries.map((seriesItem) => { + const value = renderer._interpolateValue(seriesItem.pts || [], timeMs); + return buildHoverValueEntry(seriesItem, value, resolveHoverAxis(seriesItem), {}, { + includePosition: value != null, + x + }); + }); + const comparisonValues = comparisonSeries.map((seriesItem) => { + const value = renderer._interpolateValue(seriesItem.pts || [], timeMs); + return buildHoverValueEntry(seriesItem, value, resolveHoverAxis(seriesItem), { comparison: true }, { + includePosition: value != null, + x + }); + }); + const trendValues = trendSeries.map((seriesItem) => { + const value = renderer._interpolateValue(seriesItem.pts || [], timeMs); + return buildHoverValueEntry(seriesItem, value, resolveHoverAxis(seriesItem), { trend: true }, { + includePosition: value != null, + x + }); + }); + const rateValues = rateSeries.map((seriesItem) => { + const value = renderer._interpolateValue(seriesItem.pts || [], timeMs); + return buildHoverValueEntry(seriesItem, value, resolveHoverAxis(seriesItem), { rate: true }, { + includePosition: value != null, + x + }); + }); + const deltaValues = deltaSeries.map((seriesItem) => { + const value = renderer._interpolateValue(seriesItem.pts || [], timeMs); + return buildHoverValueEntry(seriesItem, value, resolveHoverAxis(seriesItem), { delta: true }, { + includePosition: value != null, + x + }); + }); + const summaryValues = summarySeries.map((seriesItem) => { + const axis = resolveHoverAxis(seriesItem); + return buildHoverValueEntry(seriesItem, Number(seriesItem.value), axis, { + summary: true, + summaryType: seriesItem.summaryType || "" + }); + }); + const thresholdValues = thresholdSeries.map((seriesItem) => { + const axis = resolveHoverAxis(seriesItem); + return buildHoverValueEntry(seriesItem, Number(seriesItem.value), axis, { threshold: true }); + }); + const plottedValues = [ + ...values.filter((entry) => entry?.hasValue !== false), + ...comparisonValues.filter((entry) => entry?.hasValue !== false), + ...rateValues.filter((entry) => entry?.hasValue !== false), + ...options.showTrendCrosshairs === true ? trendValues.filter((entry) => entry?.hasValue !== false && entry.showCrosshair === true) : [] + ]; + let rangeStartMs = timeMs; + let rangeEndMs = timeMs; + let primary = plottedValues[0] || null; + if (primary) { + for (const entry of plottedValues) if (Number.isFinite(entry.y) && Number.isFinite(primary.y) && Math.abs(entry.y - localY) < Math.abs(primary.y - localY)) primary = entry; + } + const activePrimarySeries = primary ? resolvedSeries.find((seriesItem) => seriesItem.entityId === primary.entityId) || null : null; + if (activePrimarySeries?.pts?.length) { + const pts = activePrimarySeries.pts; + const pLen = pts.length; + let lo = 0; + let hi = pLen - 1; + let previousIndex = -1; + if (pts[0][0] <= timeMs) { + while (lo + 1 < hi) { + const mid = Math.floor((lo + hi) / 2); + if (pts[mid][0] <= timeMs) lo = mid; + else hi = mid; + } + previousIndex = pts[hi][0] <= timeMs ? hi : lo; + } + const nextIndex = previousIndex < pLen - 1 ? previousIndex + 1 : -1; + const previous = previousIndex >= 0 ? pts[previousIndex] : null; + let next = null; + if (nextIndex >= 0) next = pts[nextIndex]; + else if (previousIndex < 0) next = pts[0]; + if (previous && next) { + const prevPrev = pts[Math.max(0, previousIndex - 1)] || previous; + const nextNext = pts[Math.min(pLen - 1, nextIndex + 1)] || next; + rangeStartMs = previous === next ? previous[0] : Math.round((previous[0] + prevPrev[0]) / 2); + rangeEndMs = previous === next ? next[0] : Math.round((next[0] + nextNext[0]) / 2); + } else if (previous) { + rangeStartMs = previous[0]; + rangeEndMs = previous[0]; + } else if (next) { + rangeStartMs = next[0]; + rangeEndMs = next[0]; + } + } + const binaryValues = binaryStates.map((entry) => { + const activeSpan = (entry.spans || []).find((span) => timeMs >= span.start && timeMs <= span.end); + return { + entityId: entry.entityId || "", + label: entry.label || entry.entityId || "", + value: activeSpan ? entry.onLabel || "on" : entry.offLabel || "off", + unit: "", + color: entry.color, + hasValue: true, + active: !!activeSpan + }; + }).filter((entry) => Boolean(entry.label)); + if (!values.length && !binaryValues.length && !trendValues.length && !rateValues.length && !deltaValues.length && !summaryValues.length && !thresholdValues.length && !comparisonValues.length) return null; + const fallbackY = renderer.pad.top + 12; + const hoverY = primary ? primary.y : fallbackY; + const hoveredEvents = []; + for (const event of events || []) { + const eventTime = new Date(event.timestamp).getTime(); + if (eventTime < t0 || eventTime > t1) continue; + const distance = Math.abs(eventTime - timeMs); + if (distance <= eventThresholdMs) hoveredEvents.push({ + ...event, + _hoverDistanceMs: distance + }); + } + hoveredEvents.sort((left, right) => { + const distanceDelta = (left._hoverDistanceMs || 0) - (right._hoverDistanceMs || 0); + if (distanceDelta !== 0) return distanceDelta; + return new Date(left.timestamp).getTime() - new Date(right.timestamp).getTime(); + }); + const normalizedHoveredEvents = hoveredEvents.map((event) => { + const { _hoverDistanceMs: _, ...normalizedEvent } = event; + return normalizedEvent; + }); + return { + x, + y: hoverY, + timeMs, + rangeStartMs, + rangeEndMs, + values, + trendValues, + rateValues, + deltaValues: options.showDeltaTooltip === true ? deltaValues : [], + summaryValues, + thresholdValues, + comparisonValues, + binaryValues, + primary, + event: normalizedHoveredEvents[0] || null, + events: normalizedHoveredEvents, + emphasizeGuides: options.emphasizeHoverGuides === true, + showTrendCrosshairs: options.showTrendCrosshairs === true, + showRateCrosshairs: options.showRateCrosshairs === true, + hideRawData: options.hideRawData === true + }; + }; + const showFromPointer = (clientX, clientY) => { + if (interactionState._chartZoomDragging) return; + const anomalyRegionsHit = findAnomalyRegions(clientX, clientY); + const hover = buildHoverState(clientX, clientY); + if (!hover) { + interactionState._chartLastHover = null; + hideLineChartHover(card); + canvas.style.cursor = "default"; + return; + } + hover.anomalyRegions = anomalyRegionsHit; + interactionState._chartLastHover = hover; + showLineChartCrosshair(card, renderer, hover); + if (options.showTooltip !== false || Array.isArray(hover.events) && hover.events.length > 0) showLineChartTooltip(card, hover, clientX, clientY); + else hideTooltip(card); + dispatchLineChartHover(card, hover); + canvas.style.cursor = anomalyRegionsHit.length > 0 ? "pointer" : "crosshair"; + }; + const hideHover = () => { + interactionState._chartLastHover = null; + hideLineChartHover(card); + canvas.style.cursor = "default"; + }; + let _rafHandle = null; + let _pendingX = 0; + let _pendingY = 0; + const onMouseMove = (ev) => { + _pendingX = ev.clientX; + _pendingY = ev.clientY; + if (_rafHandle !== null) return; + _rafHandle = requestAnimationFrame(() => { + _rafHandle = null; + showFromPointer(_pendingX, _pendingY); + }); + }; + const onMouseLeave = (ev) => { + const nextTarget = ev.relatedTarget; + if (nextTarget instanceof Node && hoverSurfaceEl && hoverSurfaceEl.contains(nextTarget)) return; + if (nextTarget instanceof Node && addAnnotationButton && addAnnotationButton.contains(nextTarget)) return; + hideHover(); + }; + const onOverlayMove = (ev) => { + showFromPointer(ev.clientX, ev.clientY); + }; + const onOverlayLeave = (ev) => { + const nextTarget = ev.relatedTarget; + if (nextTarget instanceof Node && canvas.contains(nextTarget)) return; + if (nextTarget instanceof Node && addAnnotationButton && addAnnotationButton.contains(nextTarget)) return; + hideHover(); + }; + const onAddButtonLeave = (ev) => { + const nextTarget = ev.relatedTarget; + if (nextTarget instanceof Node && (canvas.contains(nextTarget) || hoverSurfaceEl && hoverSurfaceEl.contains(nextTarget))) return; + hideHover(); + }; + const onAddButtonClick = (ev) => { + if (typeof options.onAddAnnotation !== "function" || !interactionState._chartLastHover) return; + ev.preventDefault(); + ev.stopPropagation(); + options.onAddAnnotation(interactionState._chartLastHover, ev); + }; + const onContextMenu = (ev) => { + if (typeof options.onContextMenu !== "function") return; + const hover = buildHoverState(ev.clientX, ev.clientY); + if (!hover) return; + ev.preventDefault(); + interactionState._chartLastHover = hover; + showLineChartCrosshair(card, renderer, hover); + showLineChartTooltip(card, hover, ev.clientX, ev.clientY); + dispatchLineChartHover(card, hover); + options.onContextMenu(hover, ev); + }; + const onClick = (ev) => { + if (typeof options.onAnomalyClick !== "function") return; + const regions = findAnomalyRegions(ev.clientX, ev.clientY); + if (!regions.length) return; + ev.preventDefault(); + ev.stopPropagation(); + options.onAnomalyClick(regions, ev); + }; + let touchTimer = null; + const scheduleTouchHide = () => { + if (touchTimer) window.clearTimeout(touchTimer); + touchTimer = window.setTimeout(() => hideHover(), 1800); + }; + const onTouchStart = (ev) => { + ev.preventDefault(); + const touch = ev.touches[0]; + if (!touch) return; + showFromPointer(touch.clientX, touch.clientY); + scheduleTouchHide(); + }; + const onTouchMove = (ev) => { + ev.preventDefault(); + const touch = ev.touches[0]; + if (!touch) return; + showFromPointer(touch.clientX, touch.clientY); + scheduleTouchHide(); + }; + const onTouchEnd = () => scheduleTouchHide(); + canvas.addEventListener("mousemove", onMouseMove); + canvas.addEventListener("mouseleave", onMouseLeave); + canvas.addEventListener("click", onClick); + canvas.addEventListener("contextmenu", onContextMenu); + canvas.addEventListener("touchstart", onTouchStart, { passive: false }); + canvas.addEventListener("touchmove", onTouchMove, { passive: false }); + canvas.addEventListener("touchend", onTouchEnd); + canvas.addEventListener("touchcancel", onTouchEnd); + hoverSurfaceEl?.addEventListener("mousemove", onOverlayMove); + hoverSurfaceEl?.addEventListener("mouseleave", onOverlayLeave); + addAnnotationButton?.addEventListener("mouseleave", onAddButtonLeave); + addAnnotationButton?.addEventListener("click", onAddButtonClick); + interactionState._chartHoverCleanup = () => { + canvas.removeEventListener("mousemove", onMouseMove); + canvas.removeEventListener("mouseleave", onMouseLeave); + canvas.removeEventListener("click", onClick); + canvas.removeEventListener("contextmenu", onContextMenu); + canvas.removeEventListener("touchstart", onTouchStart); + canvas.removeEventListener("touchmove", onTouchMove); + canvas.removeEventListener("touchend", onTouchEnd); + canvas.removeEventListener("touchcancel", onTouchEnd); + hoverSurfaceEl?.removeEventListener("mousemove", onOverlayMove); + hoverSurfaceEl?.removeEventListener("mouseleave", onOverlayLeave); + addAnnotationButton?.removeEventListener("mouseleave", onAddButtonLeave); + addAnnotationButton?.removeEventListener("click", onAddButtonClick); + if (_rafHandle !== null) { + cancelAnimationFrame(_rafHandle); + _rafHandle = null; + } + if (touchTimer) { + window.clearTimeout(touchTimer); + touchTimer = null; + } + hideHover(); + }; + } + function attachLineChartRangeZoom(card, canvas, renderer, t0, t1, options = {}) { + const interactionState = getInteractionState(card); + if (!canvas || !renderer) return; + if (interactionState._chartZoomCleanup) { + interactionState._chartZoomCleanup(); + interactionState._chartZoomCleanup = null; + } + const selection = getRoot(card).getElementById("chart-zoom-selection"); + if (!selection) return; + let pointerId = null; + let startX = 0; + let currentX = 0; + let dragging = false; + const hideSelection = () => { + selection.hidden = true; + selection.classList.remove("visible"); + }; + const clientXToTime = (clientX) => { + const localX = clampChartValue(clientX - canvas.getBoundingClientRect().left, renderer.pad.left, renderer.pad.left + renderer.cw); + return t0 + (renderer.cw ? (localX - renderer.pad.left) / renderer.cw : 0) * (t1 - t0); + }; + const inPlotBounds = (clientX, clientY) => { + const rect = canvas.getBoundingClientRect(); + const localX = clientX - rect.left; + const localY = clientY - rect.top; + return localX >= renderer.pad.left && localX <= renderer.pad.left + renderer.cw && localY >= renderer.pad.top && localY <= renderer.pad.top + renderer.ch; + }; + const renderSelection = () => { + const left = Math.min(startX, currentX); + const width = Math.abs(currentX - startX); + selection.style.left = `${left}px`; + selection.style.top = `${renderer.pad.top}px`; + selection.style.width = `${width}px`; + selection.style.height = `${renderer.ch}px`; + selection.hidden = false; + selection.classList.add("visible"); + }; + const emitPreview = () => { + if (!dragging || Math.abs(currentX - startX) < 8) { + options.onPreview?.(null); + return; + } + const rectLeft = canvas.getBoundingClientRect().left; + const startTime = Math.min(clientXToTime(rectLeft + startX), clientXToTime(rectLeft + currentX)); + const endTime = Math.max(clientXToTime(rectLeft + startX), clientXToTime(rectLeft + currentX)); + options.onPreview?.({ + startTime, + endTime + }); + }; + const resetDragging = (clearPreview = true) => { + pointerId = null; + dragging = false; + interactionState._chartZoomDragging = false; + hideSelection(); + if (clearPreview) options.onPreview?.(null); + }; + const onPointerMove = (ev) => { + if (pointerId == null || ev.pointerId !== pointerId) return; + currentX = clampChartValue(ev.clientX - canvas.getBoundingClientRect().left, renderer.pad.left, renderer.pad.left + renderer.cw); + const movedPx = Math.abs(currentX - startX); + if (!dragging && movedPx < 6) return; + dragging = true; + interactionState._chartZoomDragging = true; + hideLineChartHover(card); + renderSelection(); + emitPreview(); + ev.preventDefault(); + }; + const finish = (ev) => { + if (pointerId == null || ev.pointerId !== pointerId) return; + const didDrag = dragging; + const endX = currentX; + window.removeEventListener("pointermove", onPointerMove); + window.removeEventListener("pointerup", finish); + window.removeEventListener("pointercancel", finish); + if (!didDrag || Math.abs(endX - startX) < 8) { + resetDragging(true); + return; + } + const rectLeft = canvas.getBoundingClientRect().left; + const startTime = Math.min(clientXToTime(rectLeft + startX), clientXToTime(rectLeft + endX)); + const endTime = Math.max(clientXToTime(rectLeft + startX), clientXToTime(rectLeft + endX)); + options.onZoom?.({ + startTime, + endTime + }); + resetDragging(false); + }; + const onPointerDown = (ev) => { + if (ev.button !== 0 || !inPlotBounds(ev.clientX, ev.clientY)) return; + pointerId = ev.pointerId; + const rect = canvas.getBoundingClientRect(); + startX = clampChartValue(ev.clientX - rect.left, renderer.pad.left, renderer.pad.left + renderer.cw); + currentX = startX; + dragging = false; + interactionState._chartZoomDragging = false; + options.onPreview?.(null); + window.addEventListener("pointermove", onPointerMove); + window.addEventListener("pointerup", finish); + window.addEventListener("pointercancel", finish); + }; + const onDoubleClick = (ev) => { + if (!inPlotBounds(ev.clientX, ev.clientY)) return; + if (!options.onReset) return; + ev.preventDefault(); + options.onReset(); + }; + canvas.addEventListener("pointerdown", onPointerDown); + canvas.addEventListener("dblclick", onDoubleClick); + interactionState._chartZoomCleanup = () => { + canvas.removeEventListener("pointerdown", onPointerDown); + canvas.removeEventListener("dblclick", onDoubleClick); + window.removeEventListener("pointermove", onPointerMove); + window.removeEventListener("pointerup", finish); + window.removeEventListener("pointercancel", finish); + resetDragging(); + }; + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/data/cache.ts + /** + * Shared cache utilities for history/statistics/event lookups. + */ + var DATA_RANGE_CACHE_TTL_MS = 600 * 1e3; + var DATA_RANGE_CACHE_LIVE_EDGE_MS = 300 * 1e3; + var dataRangeCache = /* @__PURE__ */ new Map(); + function normalizeCacheIdList(values) { + return [...new Set((Array.isArray(values) ? values : []).filter(Boolean))].sort(); + } + function shouldUseStableRangeCache(endTime) { + const endMs = new Date(endTime || 0).getTime(); + if (!Number.isFinite(endMs)) return false; + return endMs < Date.now() - DATA_RANGE_CACHE_LIVE_EDGE_MS; + } + function getCachedRangePromise(key) { + const entry = dataRangeCache.get(key); + if (!entry) return null; + if (entry.expiresAt <= Date.now()) { + dataRangeCache.delete(key); + return null; + } + return entry.promise; + } + function setCachedRangePromise(key, promise) { + dataRangeCache.set(key, { + promise, + expiresAt: Date.now() + DATA_RANGE_CACHE_TTL_MS + }); + return promise; + } + function withStableRangeCache(key, endTime, loader) { + if (!shouldUseStableRangeCache(endTime)) return Promise.resolve().then(loader); + const cached = getCachedRangePromise(key); + if (cached) return cached; + return setCachedRangePromise(key, Promise.resolve().then(loader).catch((err) => { + dataRangeCache.delete(key); + throw err; + })); + } + function clearStableRangeCacheMatching(predicate) { + if (typeof predicate !== "function") return 0; + let deletedCount = 0; + [...dataRangeCache.keys()].forEach((key) => { + if (predicate(key) === true) { + dataRangeCache.delete(key); + deletedCount += 1; + } + }); + return deletedCount; + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/data/history-api.ts + var MAX_DOWNSAMPLED_HISTORY_RANGE_MS = 2160 * 60 * 60 * 1e3; + function parseIsoTimeMs(value) { + const timeMs = Date.parse(value); + if (!Number.isFinite(timeMs)) return null; + return timeMs; + } + function buildDownsampledHistoryChunks(startTime, endTime) { + const startTimeMs = parseIsoTimeMs(startTime); + const endTimeMs = parseIsoTimeMs(endTime); + if (startTimeMs == null || endTimeMs == null || endTimeMs <= startTimeMs || endTimeMs - startTimeMs <= MAX_DOWNSAMPLED_HISTORY_RANGE_MS) return [{ + startTime, + endTime + }]; + const chunks = []; + let chunkStartMs = startTimeMs; + while (chunkStartMs < endTimeMs) { + const chunkEndMs = Math.min(endTimeMs, chunkStartMs + MAX_DOWNSAMPLED_HISTORY_RANGE_MS); + chunks.push({ + startTime: new Date(chunkStartMs).toISOString(), + endTime: new Date(chunkEndMs).toISOString() + }); + if (chunkEndMs >= endTimeMs) break; + chunkStartMs = chunkEndMs + 1; + } + return chunks; + } + function fetchDownsampledHistory(hass, entityId, startTime, endTime, interval, aggregate) { + return withStableRangeCache(JSON.stringify({ + type: "hass_datapoints/history", + entity_id: entityId, + start_time: startTime, + end_time: endTime, + interval, + aggregate + }), endTime, async () => { + const chunks = buildDownsampledHistoryChunks(startTime, endTime); + const mergedPoints = (await Promise.all(chunks.map(async (chunk) => hass.connection.sendMessagePromise({ + type: "hass_datapoints/history", + entity_id: entityId, + start_time: chunk.startTime, + end_time: chunk.endTime, + interval, + aggregate + })))).flatMap((result) => result.pts || []); + if (!mergedPoints.length) return []; + const dedupedPoints = /* @__PURE__ */ new Map(); + for (const point of mergedPoints) if (Array.isArray(point) && point.length > 0) dedupedPoints.set(String(point[0]), point); + else dedupedPoints.set(JSON.stringify(point), point); + return [...dedupedPoints.values()]; + }); + } + function fetchAnomaliesFromBackend(hass, entityId, startTime, endTime, config) { + return hass.connection.sendMessagePromise({ + type: "hass_datapoints/anomalies", + entity_id: entityId, + start_time: startTime, + end_time: endTime, + anomaly_methods: config.anomaly_methods || [], + anomaly_sensitivity: config.anomaly_sensitivity || "medium", + anomaly_overlap_mode: config.anomaly_overlap_mode || "all", + anomaly_rate_window: config.anomaly_rate_window || "1h", + anomaly_zscore_window: config.anomaly_zscore_window || "24h", + anomaly_persistence_window: config.anomaly_persistence_window || "1h", + trend_method: config.trend_method || "rolling_average", + trend_window: config.trend_window || "24h", + ...config.anomaly_use_sampled_data !== false && config.sample_interval && config.sample_interval !== "raw" ? { + sample_interval: config.sample_interval, + sample_aggregate: config.sample_aggregate || "mean" + } : {}, + ...config.comparison_entity_id ? { + comparison_entity_id: config.comparison_entity_id, + comparison_start_time: config.comparison_start_time, + comparison_end_time: config.comparison_end_time, + comparison_time_offset_ms: config.comparison_time_offset_ms || 0 + } : {} + }).then((result) => result.anomaly_clusters || []); + } + async function fetchHistoryDuringPeriod(hass, startTime, endTime, entityIds, options = {}) { + const normalizedEntityIds = normalizeCacheIdList(entityIds); + return withStableRangeCache(JSON.stringify({ + type: "history/history_during_period", + start_time: startTime, + end_time: endTime, + entity_ids: normalizedEntityIds, + include_start_time_state: options.include_start_time_state !== false, + significant_changes_only: !!options.significant_changes_only, + no_attributes: options.no_attributes !== false + }), endTime, () => hass.connection.sendMessagePromise({ + type: "history/history_during_period", + start_time: startTime, + end_time: endTime, + entity_ids: normalizedEntityIds, + include_start_time_state: options.include_start_time_state !== false, + significant_changes_only: !!options.significant_changes_only, + no_attributes: options.no_attributes !== false + })); + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/domain/history-series.ts + var VALID_ANOMALY_METHODS = [ + "trend_residual", + "rate_of_change", + "iqr", + "rolling_zscore", + "persistence", + "comparison_window" + ]; + var VALID_SAMPLE_INTERVALS = [ + "raw", + "1s", + "5s", + "10s", + "15s", + "30s", + "1m", + "2m", + "5m", + "10m", + "15m", + "30m", + "1h", + "2h", + "3h", + "4h", + "6h", + "12h", + "24h" + ]; + var VALID_SAMPLE_AGGREGATES = [ + "mean", + "min", + "max", + "median", + "first", + "last" + ]; + function normalizeHistorySeriesAnalysis(analysis) { + const source = analysis && typeof analysis === "object" ? analysis : {}; + return { + expanded: source.expanded === true, + show_trend_lines: source.show_trend_lines === true, + trend_method: source.trend_method === "linear_trend" ? "linear_trend" : "rolling_average", + trend_window: typeof source.trend_window === "string" && source.trend_window ? source.trend_window : "24h", + show_trend_crosshairs: source.show_trend_crosshairs !== false, + show_summary_stats: source.show_summary_stats === true, + show_summary_stats_shading: source.show_summary_stats_shading === true, + show_rate_of_change: source.show_rate_of_change === true, + show_rate_crosshairs: source.show_rate_crosshairs !== false, + rate_window: typeof source.rate_window === "string" && source.rate_window ? source.rate_window : "1h", + show_threshold_analysis: source.show_threshold_analysis === true, + show_threshold_shading: source.show_threshold_shading === true, + threshold_value: typeof source.threshold_value === "string" || typeof source.threshold_value === "number" ? String(source.threshold_value).trim() : "", + threshold_direction: source.threshold_direction === "below" ? "below" : "above", + show_anomalies: source.show_anomalies === true, + anomaly_methods: (() => { + if (Array.isArray(source.anomaly_methods)) return source.anomaly_methods.filter((method) => typeof method === "string" && VALID_ANOMALY_METHODS.includes(method)); + const legacy = typeof source.anomaly_method === "string" && VALID_ANOMALY_METHODS.includes(source.anomaly_method) ? source.anomaly_method : null; + return legacy ? [legacy] : []; + })(), + anomaly_overlap_mode: source.anomaly_overlap_mode === "only" ? "only" : "all", + anomaly_sensitivity: typeof source.anomaly_sensitivity === "string" && source.anomaly_sensitivity ? source.anomaly_sensitivity : "medium", + anomaly_rate_window: typeof source.anomaly_rate_window === "string" && source.anomaly_rate_window ? source.anomaly_rate_window : "1h", + anomaly_zscore_window: typeof source.anomaly_zscore_window === "string" && source.anomaly_zscore_window ? source.anomaly_zscore_window : "24h", + anomaly_persistence_window: typeof source.anomaly_persistence_window === "string" && source.anomaly_persistence_window ? source.anomaly_persistence_window : "1h", + anomaly_comparison_window_id: typeof source.anomaly_comparison_window_id === "string" && source.anomaly_comparison_window_id ? source.anomaly_comparison_window_id : null, + show_delta_analysis: source.show_delta_analysis === true, + show_delta_tooltip: source.show_delta_tooltip !== false, + show_delta_lines: source.show_delta_lines === true, + hide_source_series: source.hide_source_series === true, + sample_interval: typeof source.sample_interval === "string" && VALID_SAMPLE_INTERVALS.includes(source.sample_interval) ? source.sample_interval : "1m", + sample_aggregate: typeof source.sample_aggregate === "string" && VALID_SAMPLE_AGGREGATES.includes(source.sample_aggregate) ? source.sample_aggregate : "mean", + stepped_series: source.stepped_series === true, + anomaly_use_sampled_data: source.anomaly_use_sampled_data !== false + }; + } + function normalizeHistorySeriesRows(rows) { + if (!Array.isArray(rows)) return []; + const seen = /* @__PURE__ */ new Set(); + const normalized = []; + rows.forEach((row, index) => { + const entityId = typeof row?.entity_id === "string" ? row.entity_id.trim() : ""; + if (!entityId || seen.has(entityId)) return; + seen.add(entityId); + normalized.push({ + entity_id: entityId, + color: typeof row?.color === "string" && /^#[0-9a-f]{6}$/i.test(row.color) ? row.color : COLORS[index % COLORS.length], + visible: row?.visible !== false, + analysis: normalizeHistorySeriesAnalysis(row?.analysis) + }); + }); + return normalized; + } + function buildHistorySeriesRows(entityIds, previousRows = []) { + const normalizedPrevious = normalizeHistorySeriesRows(previousRows); + const previousMap = new Map(normalizedPrevious.map((row) => [row.entity_id, row])); + const intervals = normalizedPrevious.map((row) => row.analysis.sample_interval); + const inheritedSampleSettings = intervals.length > 0 && intervals.every((interval) => interval === intervals[0]) ? { + sample_interval: intervals[0], + sample_aggregate: normalizedPrevious[0].analysis.sample_aggregate + } : null; + return normalizeEntityIds(entityIds).map((entityId, index) => { + const existing = previousMap.get(entityId); + if (existing) return existing; + return { + entity_id: entityId, + color: COLORS[index % COLORS.length], + visible: true, + analysis: normalizeHistorySeriesAnalysis(inheritedSampleSettings) + }; + }); + } + function slugifySeriesName(value) { + return String(value || "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""); + } + function parseSeriesColorsParam(value) { + if (!value || typeof value !== "string") return {}; + return value.split(",").reduce((acc, entry) => { + const [rawKey, rawColor] = entry.split(":"); + const key = decodeURIComponent(rawKey || "").trim(); + const color = String(rawColor || "").trim(); + if (!key || !/^#[0-9a-f]{6}$/i.test(color)) return acc; + acc[key] = color; + return acc; + }, {}); + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/workers/history-analysis.worker.ts?worker&inline + var jsContent$1 = "(function() {\n //#region custom_components/hass_datapoints/src/lib/workers/history-analysis.worker.ts\n const HOUR_MS = 3600 * 1e3;\n function getTrendWindowMs(value) {\n const windows = {\n \"1h\": 3600 * 1e3,\n \"6h\": 360 * 60 * 1e3,\n \"24h\": 1440 * 60 * 1e3,\n \"7d\": 10080 * 60 * 1e3,\n \"14d\": 336 * 60 * 60 * 1e3,\n \"21d\": 504 * 60 * 60 * 1e3,\n \"28d\": 672 * 60 * 60 * 1e3\n };\n return windows[value] || windows[\"24h\"];\n }\n function buildRollingAverageTrend(points, windowMs) {\n if (!Array.isArray(points) || points.length < 2 || !Number.isFinite(windowMs) || windowMs <= 0) return [];\n const trendPoints = [];\n let windowStartIndex = 0;\n let windowSum = 0;\n for (let index = 0; index < points.length; index += 1) {\n const [time, value] = points[index];\n windowSum += value;\n while (windowStartIndex < index && time - points[windowStartIndex][0] > windowMs) {\n windowSum -= points[windowStartIndex][1];\n windowStartIndex += 1;\n }\n const count = index - windowStartIndex + 1;\n if (count > 0) trendPoints.push([time, windowSum / count]);\n }\n return trendPoints;\n }\n function buildLinearTrend(points) {\n if (!Array.isArray(points) || points.length < 2) return [];\n const origin = points[0][0];\n let sumX = 0;\n let sumY = 0;\n let sumXX = 0;\n let sumXY = 0;\n for (const [time, value] of points) {\n const x = (time - origin) / HOUR_MS;\n sumX += x;\n sumY += value;\n sumXX += x * x;\n sumXY += x * value;\n }\n const count = points.length;\n const denominator = count * sumXX - sumX * sumX;\n if (!Number.isFinite(denominator) || Math.abs(denominator) < 1e-9) return [];\n const slope = (count * sumXY - sumX * sumY) / denominator;\n const intercept = (sumY - slope * sumX) / count;\n const firstTime = points[0][0];\n const lastTime = points[points.length - 1][0];\n const firstX = (firstTime - origin) / HOUR_MS;\n const lastX = (lastTime - origin) / HOUR_MS;\n return [[firstTime, intercept + slope * firstX], [lastTime, intercept + slope * lastX]];\n }\n function buildTrendPoints(points, method, trendWindow) {\n if (!Array.isArray(points) || points.length < 2) return [];\n if (method === \"linear_trend\") return buildLinearTrend(points);\n return buildRollingAverageTrend(points, getTrendWindowMs(trendWindow));\n }\n function normalizeSeriesAnalysis(analysis) {\n const source = analysis && typeof analysis === \"object\" ? analysis : {};\n return {\n show_trend_lines: source.show_trend_lines === true,\n trend_method: source.trend_method === \"linear_trend\" ? \"linear_trend\" : \"rolling_average\",\n trend_window: typeof source.trend_window === \"string\" && source.trend_window ? source.trend_window : \"24h\",\n show_summary_stats: source.show_summary_stats === true,\n show_rate_of_change: source.show_rate_of_change === true,\n rate_window: typeof source.rate_window === \"string\" && source.rate_window ? source.rate_window : \"1h\",\n show_delta_analysis: source.show_delta_analysis === true\n };\n }\n function interpolateSeriesValue(points, timeMs) {\n if (!Array.isArray(points) || points.length === 0) return null;\n if (timeMs < points[0][0] || timeMs > points[points.length - 1][0]) return null;\n if (timeMs === points[0][0]) return points[0][1];\n if (timeMs === points[points.length - 1][0]) return points[points.length - 1][1];\n for (let index = 0; index < points.length - 1; index += 1) {\n const [startTime, startValue] = points[index];\n const [endTime, endValue] = points[index + 1];\n if (timeMs >= startTime && timeMs <= endTime) {\n const fraction = (timeMs - startTime) / (endTime - startTime);\n return startValue + (endValue - startValue) * fraction;\n }\n }\n return null;\n }\n function buildRateOfChangePoints(points, rateWindow) {\n if (!Array.isArray(points) || points.length < 2) return [];\n const ratePoints = [];\n for (let index = 1; index < points.length; index += 1) {\n const [timeMs, value] = points[index];\n let comparisonPoint = null;\n if (rateWindow === \"point_to_point\") comparisonPoint = points[index - 1];\n else {\n const windowMs = getTrendWindowMs(rateWindow);\n if (!Number.isFinite(windowMs) || windowMs <= 0) continue;\n for (let candidateIndex = index - 1; candidateIndex >= 0; candidateIndex -= 1) {\n const candidatePoint = points[candidateIndex];\n if (timeMs - candidatePoint[0] >= windowMs) {\n comparisonPoint = candidatePoint;\n break;\n }\n }\n if (!comparisonPoint) comparisonPoint = points[0];\n }\n if (!Array.isArray(comparisonPoint) || comparisonPoint.length < 2) continue;\n const deltaMs = timeMs - comparisonPoint[0];\n if (!Number.isFinite(deltaMs) || deltaMs <= 0) continue;\n const deltaHours = deltaMs / HOUR_MS;\n if (!Number.isFinite(deltaHours) || deltaHours <= 0) continue;\n const rateValue = (value - comparisonPoint[1]) / deltaHours;\n if (!Number.isFinite(rateValue)) continue;\n ratePoints.push([timeMs, rateValue]);\n }\n return ratePoints;\n }\n function buildDeltaPoints(sourcePoints, comparisonPoints) {\n if (!Array.isArray(sourcePoints) || sourcePoints.length < 2 || !Array.isArray(comparisonPoints) || comparisonPoints.length < 2) return [];\n const deltaPoints = [];\n for (const [timeMs, value] of sourcePoints) {\n const comparisonValue = interpolateSeriesValue(comparisonPoints, timeMs);\n if (comparisonValue == null) continue;\n deltaPoints.push([timeMs, value - comparisonValue]);\n }\n return deltaPoints;\n }\n function buildSummaryStats(points) {\n if (!Array.isArray(points) || points.length === 0) return null;\n let min = Infinity;\n let max = -Infinity;\n let sum = 0;\n let count = 0;\n for (const point of points) {\n const value = Number(point?.[1]);\n if (!Number.isFinite(value)) continue;\n if (value < min) min = value;\n if (value > max) max = value;\n sum += value;\n count += 1;\n }\n if (!Number.isFinite(min) || !Number.isFinite(max) || count === 0) return null;\n return {\n min,\n max,\n mean: sum / count\n };\n }\n function computeHistoryAnalysis(payload) {\n const series = (Array.isArray(payload?.series) ? payload.series : []).map((seriesItem) => ({\n ...seriesItem,\n analysis: normalizeSeriesAnalysis(seriesItem?.analysis)\n }));\n const comparisonSeries = new Map((Array.isArray(payload?.comparisonSeries) ? payload.comparisonSeries : []).filter((entry) => entry?.entityId).map((entry) => [entry.entityId, entry]));\n const result = {\n trendSeries: [],\n rateSeries: [],\n deltaSeries: [],\n summaryStats: [],\n anomalySeries: [],\n comparisonWindowResults: {}\n };\n for (const seriesItem of series) {\n const points = Array.isArray(seriesItem?.pts) ? seriesItem.pts : [];\n const analysis = normalizeSeriesAnalysis(seriesItem?.analysis);\n if (points.length < 2) continue;\n if (analysis.show_trend_lines === true) {\n const trendPoints = buildTrendPoints(points, analysis.trend_method, analysis.trend_window);\n if (trendPoints.length >= 2) result.trendSeries.push({\n entityId: seriesItem.entityId,\n pts: trendPoints\n });\n }\n if (analysis.show_rate_of_change === true) {\n const ratePoints = buildRateOfChangePoints(points, analysis.rate_window);\n if (ratePoints.length >= 2) result.rateSeries.push({\n entityId: seriesItem.entityId,\n pts: ratePoints\n });\n }\n if (analysis.show_summary_stats === true) {\n const summaryStats = buildSummaryStats(points);\n if (summaryStats) result.summaryStats.push({\n entityId: seriesItem.entityId,\n ...summaryStats\n });\n }\n if (analysis.show_delta_analysis === true && payload?.hasSelectedComparisonWindow === true) {\n const comparisonPoints = comparisonSeries.get(seriesItem.entityId)?.pts ?? [];\n if (comparisonPoints.length >= 2) {\n const deltaPoints = buildDeltaPoints(points, comparisonPoints);\n if (deltaPoints.length >= 2) result.deltaSeries.push({\n entityId: seriesItem.entityId,\n pts: deltaPoints\n });\n }\n }\n }\n const seriesAnalysisConfigs = typeof payload?.seriesAnalysisConfigs === \"object\" && payload.seriesAnalysisConfigs !== null ? payload.seriesAnalysisConfigs : {};\n const allComparisonWindowsData = typeof payload?.allComparisonWindowsData === \"object\" && payload.allComparisonWindowsData !== null ? payload.allComparisonWindowsData : {};\n for (const [windowId, entityPtsMap] of Object.entries(allComparisonWindowsData)) {\n result.comparisonWindowResults[windowId] = {};\n for (const [entityId, pts] of Object.entries(entityPtsMap)) {\n const winAnalysis = normalizeSeriesAnalysis(seriesAnalysisConfigs[entityId]);\n result.comparisonWindowResults[windowId][entityId] = {\n trendPts: winAnalysis.show_trend_lines && pts.length >= 2 ? buildTrendPoints(pts, winAnalysis.trend_method, winAnalysis.trend_window) : [],\n ratePts: winAnalysis.show_rate_of_change && pts.length >= 2 ? buildRateOfChangePoints(pts, winAnalysis.rate_window) : [],\n summaryStats: winAnalysis.show_summary_stats ? buildSummaryStats(pts) : null\n };\n }\n }\n return result;\n }\n const workerScope = globalThis;\n workerScope.onmessage = (event) => {\n const { id, payload } = event.data || {};\n try {\n const result = computeHistoryAnalysis(payload);\n workerScope.postMessage({\n id,\n result\n });\n } catch (error) {\n workerScope.postMessage({\n id,\n error: error instanceof Error ? error.message : String(error)\n });\n }\n };\n //#endregion\n})();\n"; + var blob$1 = typeof self !== "undefined" && self.Blob && new Blob(["(self.URL || self.webkitURL).revokeObjectURL(self.location.href);", jsContent$1], { type: "text/javascript;charset=utf-8" }); + function WorkerWrapper$1(options) { + let objURL; + try { + objURL = blob$1 && (self.URL || self.webkitURL).createObjectURL(blob$1); + if (!objURL) throw ""; + const worker = new Worker(objURL, { name: options?.name }); + worker.addEventListener("error", () => { + (self.URL || self.webkitURL).revokeObjectURL(objURL); + }); + return worker; + } catch (e) { + return new Worker("data:text/javascript;charset=utf-8," + encodeURIComponent(jsContent$1), { name: options?.name }); + } + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/workers/history-analysis-client.ts + var workerInstance$1 = null; + var requestId$1 = 0; + var pending$1 = /* @__PURE__ */ new Map(); + function getHistoryAnalysisWorker() { + if (workerInstance$1) return workerInstance$1; + workerInstance$1 = new WorkerWrapper$1(); + workerInstance$1.addEventListener("message", (event) => { + const { id, result, error } = event.data || {}; + const handlers = pending$1.get(id || -1); + if (!handlers) return; + pending$1.delete(id || -1); + if (error) { + handlers.reject(new Error(error)); + return; + } + handlers.resolve(result); + }); + workerInstance$1.addEventListener("error", (error) => { + pending$1.forEach((handlers) => { + handlers.reject(error); + }); + pending$1.clear(); + workerInstance$1 = null; + }); + return workerInstance$1; + } + /** + * Abort all in-flight analysis requests and terminate the worker. + * Called when a new draw request supersedes an in-progress one so the worker + * is not left computing a result that will be thrown away. + */ + function terminateHistoryAnalysisWorker() { + if (pending$1.size > 0) { + pending$1.forEach(({ reject }) => { + reject(/* @__PURE__ */ new Error("Aborted: superseded by newer analysis")); + }); + pending$1.clear(); + } + if (workerInstance$1) { + workerInstance$1.terminate(); + workerInstance$1 = null; + } + } + function computeHistoryAnalysisInWorker(payload) { + const worker = getHistoryAnalysisWorker(); + return new Promise((resolve, reject) => { + const id = ++requestId$1; + pending$1.set(id, { + resolve, + reject + }); + worker.postMessage({ + id, + payload + }); + }); + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/workers/chart-data.worker.ts?worker&inline + var jsContent = "(function() {\n //#region custom_components/hass_datapoints/src/lib/workers/chart-data.worker.ts\n function downsamplePts(pts, intervalMs, aggregate) {\n if (!pts.length || intervalMs <= 0) return pts;\n const buckets = /* @__PURE__ */ new Map();\n const bucketRepTime = /* @__PURE__ */ new Map();\n for (const [time, value] of pts) {\n const idx = Math.floor(time / intervalMs);\n if (!buckets.has(idx)) {\n buckets.set(idx, []);\n bucketRepTime.set(idx, time);\n }\n buckets.get(idx)?.push(value);\n }\n const result = [];\n for (const idx of [...buckets.keys()].sort((a, b) => a - b)) {\n const values = buckets.get(idx) || [];\n const repTime = bucketRepTime.get(idx) || 0;\n let agg;\n if (aggregate === \"min\") agg = Math.min(...values);\n else if (aggregate === \"max\") agg = Math.max(...values);\n else if (aggregate === \"median\") {\n const sorted = [...values].sort((a, b) => a - b);\n const mid = Math.floor(sorted.length / 2);\n agg = sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;\n } else if (aggregate === \"first\") agg = values[0];\n else if (aggregate === \"last\") agg = values[values.length - 1];\n else agg = values.reduce((sum, current) => sum + current, 0) / values.length;\n result.push([repTime, agg]);\n }\n return result;\n }\n self.onmessage = ({ data }) => {\n const { id, type, payload } = data || {};\n try {\n let result;\n if (type === \"downsample\") result = downsamplePts(payload.pts, payload.intervalMs, payload.aggregate);\n else throw new Error(`Unknown message type: ${type}`);\n self.postMessage({\n id,\n result\n });\n } catch (err) {\n self.postMessage({\n id,\n error: String(err)\n });\n }\n };\n //#endregion\n})();\n"; + var blob = typeof self !== "undefined" && self.Blob && new Blob(["(self.URL || self.webkitURL).revokeObjectURL(self.location.href);", jsContent], { type: "text/javascript;charset=utf-8" }); + function WorkerWrapper(options) { + let objURL; + try { + objURL = blob && (self.URL || self.webkitURL).createObjectURL(blob); + if (!objURL) throw ""; + const worker = new Worker(objURL, { name: options?.name }); + worker.addEventListener("error", () => { + (self.URL || self.webkitURL).revokeObjectURL(objURL); + }); + return worker; + } catch (e) { + return new Worker("data:text/javascript;charset=utf-8," + encodeURIComponent(jsContent), { name: options?.name }); + } + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/workers/chart-data-client.ts + var workerInstance; + var requestId = 0; + var pending = /* @__PURE__ */ new Map(); + function getChartDataWorker() { + if (workerInstance !== void 0) return workerInstance; + try { + workerInstance = new WorkerWrapper(); + workerInstance.addEventListener("message", (event) => { + const { id, result, error } = event.data || {}; + const handlers = pending.get(id || -1); + if (!handlers) return; + pending.delete(id || -1); + if (error) handlers.reject(new Error(error)); + else handlers.resolve(result || []); + }); + workerInstance.addEventListener("error", (err) => { + pending.forEach(({ reject }) => { + reject(err); + }); + pending.clear(); + workerInstance = null; + }); + } catch { + workerInstance = null; + } + return workerInstance; + } + /** + * Downsample [[timeMs, value], ...] pts in a worker. + * Returns a Promise that resolves with the downsampled pts array. + */ + function downsampleInWorker(pts, intervalMs, aggregate) { + if (pts.length === 0) return Promise.resolve([]); + const worker = getChartDataWorker(); + if (!worker) return Promise.reject(/* @__PURE__ */ new Error("Worker not available")); + return new Promise((resolve, reject) => { + const id = ++requestId; + pending.set(id, { + resolve, + reject + }); + worker.postMessage({ + id, + type: "downsample", + payload: { + pts, + intervalMs, + aggregate + } + }); + }); + } + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/analysis/windows.ts + var HOUR_MS$1 = 3600 * 1e3; + function getTrendWindowMs(value) { + const windows = { + "1h": HOUR_MS$1, + "6h": 6 * HOUR_MS$1, + "24h": 24 * HOUR_MS$1, + "7d": 168 * HOUR_MS$1, + "14d": 336 * HOUR_MS$1, + "21d": 504 * HOUR_MS$1, + "28d": 672 * HOUR_MS$1 + }; + return windows[value] ?? windows["24h"]; + } + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/analysis/series.ts + function buildRollingAverageTrend(points, windowMs) { + if (!Array.isArray(points) || points.length < 2 || !Number.isFinite(windowMs) || windowMs <= 0) return []; + const trendPoints = []; + let windowStartIndex = 0; + let windowSum = 0; + for (let index = 0; index < points.length; index += 1) { + const [time, value] = points[index]; + windowSum += value; + while (windowStartIndex < index && time - points[windowStartIndex][0] > windowMs) { + windowSum -= points[windowStartIndex][1]; + windowStartIndex += 1; + } + const count = index - windowStartIndex + 1; + if (count > 0) trendPoints.push([time, windowSum / count]); + } + return trendPoints; + } + function buildLinearTrend(points) { + if (!Array.isArray(points) || points.length < 2) return []; + const origin = points[0][0]; + let sumX = 0; + let sumY = 0; + let sumXX = 0; + let sumXY = 0; + for (const [time, value] of points) { + const x = (time - origin) / (3600 * 1e3); + sumX += x; + sumY += value; + sumXX += x * x; + sumXY += x * value; + } + const count = points.length; + const denominator = count * sumXX - sumX * sumX; + if (!Number.isFinite(denominator) || Math.abs(denominator) < 1e-9) return []; + const slope = (count * sumXY - sumX * sumY) / denominator; + const intercept = (sumY - slope * sumX) / count; + const firstTime = points[0][0]; + const lastTime = points[points.length - 1][0]; + const firstX = (firstTime - origin) / (3600 * 1e3); + const lastX = (lastTime - origin) / (3600 * 1e3); + return [[firstTime, intercept + slope * firstX], [lastTime, intercept + slope * lastX]]; + } + function interpolateSeriesValue(points, timeMs) { + if (!Array.isArray(points) || !points.length) return null; + if (timeMs < points[0][0] || timeMs > points[points.length - 1][0]) return null; + if (timeMs === points[0][0]) return points[0][1]; + if (timeMs === points[points.length - 1][0]) return points[points.length - 1][1]; + for (let index = 0; index < points.length - 1; index += 1) { + const [startTime, startValue] = points[index]; + const [endTime, endValue] = points[index + 1]; + if (timeMs >= startTime && timeMs <= endTime) return startValue + (timeMs - startTime) / (endTime - startTime) * (endValue - startValue); + } + return null; + } + function buildRateOfChangePoints(points, rateWindow = "1h") { + if (!Array.isArray(points) || points.length < 2) return []; + const ratePoints = []; + for (let index = 1; index < points.length; index += 1) { + const [timeMs, value] = points[index]; + let comparisonPoint = null; + if (rateWindow === "point_to_point") comparisonPoint = points[index - 1]; + else { + const windowMs = getTrendWindowMs(rateWindow); + if (!Number.isFinite(windowMs) || windowMs <= 0) continue; + for (let candidateIndex = index - 1; candidateIndex >= 0; candidateIndex -= 1) { + const candidatePoint = points[candidateIndex]; + if (timeMs - candidatePoint[0] >= windowMs) { + comparisonPoint = candidatePoint; + break; + } + } + if (!comparisonPoint) comparisonPoint = points[0]; + } + if (!Array.isArray(comparisonPoint) || comparisonPoint.length < 2) continue; + const deltaMs = timeMs - comparisonPoint[0]; + if (!Number.isFinite(deltaMs) || deltaMs <= 0) continue; + const deltaHours = deltaMs / (3600 * 1e3); + if (!Number.isFinite(deltaHours) || deltaHours <= 0) continue; + const rateValue = (value - comparisonPoint[1]) / deltaHours; + if (!Number.isFinite(rateValue)) continue; + ratePoints.push([timeMs, rateValue]); + } + return ratePoints; + } + function buildDeltaPoints(sourcePoints, comparisonPoints) { + if (!Array.isArray(sourcePoints) || sourcePoints.length < 2 || !Array.isArray(comparisonPoints) || comparisonPoints.length < 2) return []; + const deltaPoints = []; + for (const [timeMs, value] of sourcePoints) { + const comparisonValue = interpolateSeriesValue(comparisonPoints, timeMs); + if (comparisonValue == null) continue; + deltaPoints.push([timeMs, value - comparisonValue]); + } + return deltaPoints; + } + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/analysis/summary.ts + function buildSummaryStats(points) { + if (!Array.isArray(points) || !points.length) return null; + let min = Infinity; + let max = -Infinity; + let sum = 0; + let count = 0; + for (const point of points) { + const value = Number(point?.[1]); + if (!Number.isFinite(value)) continue; + if (value < min) min = value; + if (value > max) max = value; + sum += value; + count += 1; + } + if (!Number.isFinite(min) || !Number.isFinite(max) || count === 0) return null; + return { + min, + max, + mean: sum / count + }; + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/domain/chart-zoom.ts + /** + * Date parsing and zoom state helpers shared by chart/timeline subsystems. + */ + function parseDateValue(value) { + if (!value) return null; + if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value; + const parsed = new Date(value); + return Number.isNaN(parsed.getTime()) ? null : parsed; + } + function createChartZoomRange(startValue, endValue) { + const startDate = parseDateValue(startValue); + const endDate = parseDateValue(endValue); + const start = startDate?.getTime(); + const end = endDate?.getTime(); + if (typeof start === "number" && Number.isFinite(start) && typeof end === "number" && Number.isFinite(end) && start < end) return { + start, + end + }; + return null; + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/history-page/history-url-state.ts + function makeDateWindowId(label, existingIds = /* @__PURE__ */ new Set()) { + const base = slugifySeriesName(label) || "date-window"; + let candidate = base; + let suffix = 2; + while (existingIds.has(candidate)) { + candidate = `${base}-${suffix}`; + suffix += 1; + } + return candidate; + } + function normalizeDateWindows(windows) { + if (!Array.isArray(windows)) return []; + const seen = /* @__PURE__ */ new Set(); + const normalized = []; + windows.forEach((window, index) => { + const label = String(window?.label || window?.name || "").trim(); + const start = parseDateValue(window?.start_time || window?.start); + const end = parseDateValue(window?.end_time || window?.end); + if (!label || !start || !end || start >= end) return; + const id = String(window?.id || "").trim() || makeDateWindowId(`${label}-${index + 1}`, seen); + if (seen.has(id)) return; + seen.add(id); + normalized.push({ + id, + label, + start_time: start.toISOString(), + end_time: end.toISOString() + }); + }); + return normalized; + } + function parseDateWindowsParam(value) { + if (!value || typeof value !== "string") return []; + return normalizeDateWindows(value.split("|").map((entry) => { + const [rawId, rawLabel, rawStart, rawEnd] = String(entry).split("~"); + return { + id: decodeURIComponent(rawId || ""), + label: decodeURIComponent(rawLabel || ""), + start_time: decodeURIComponent(rawStart || ""), + end_time: decodeURIComponent(rawEnd || "") + }; + })); + } + function serializeDateWindowsParam(windows) { + const normalized = normalizeDateWindows(windows); + if (!normalized.length) return ""; + return normalized.map((window) => [ + encodeURIComponent(window.id), + encodeURIComponent(window.label ?? ""), + encodeURIComponent(window.start_time), + encodeURIComponent(window.end_time) + ].join("~")).join("|"); + } + function parseHistoryPageStateParam(value) { + if (!value || typeof value !== "string") return null; + try { + const parsed = JSON.parse(value); + return parsed && typeof parsed === "object" ? parsed : null; + } catch { + return null; + } + } + function serializeHistoryPageStateParam(state) { + if (!state || typeof state !== "object") return ""; + try { + return JSON.stringify(state); + } catch { + return ""; + } + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/ha/navigation.ts + function buildDataPointsHistoryPath(target = {}, options = {}) { + const normalizedTarget = { + entity_id: [...new Set((target.entity_id || []).filter(Boolean))], + device_id: [...new Set((target.device_id || []).filter(Boolean))], + area_id: [...new Set((target.area_id || []).filter(Boolean))], + label_id: [...new Set((target.label_id || []).filter(Boolean))] + }; + const params = new URLSearchParams(); + if (normalizedTarget.entity_id.length) params.set("entity_id", normalizedTarget.entity_id.join(",")); + if (normalizedTarget.device_id.length) params.set("device_id", normalizedTarget.device_id.join(",")); + if (normalizedTarget.area_id.length) params.set("area_id", normalizedTarget.area_id.join(",")); + if (normalizedTarget.label_id.length) params.set("label_id", normalizedTarget.label_id.join(",")); + if (options.datapoint_scope === "all") params.set("datapoints_scope", "all"); + const start = options.start_time ? new Date(options.start_time) : null; + const end = options.end_time ? new Date(options.end_time) : null; + if (start && end && Number.isFinite(start.getTime()) && Number.isFinite(end.getTime()) && start < end) { + params.set("start_time", start.toISOString()); + params.set("end_time", end.toISOString()); + params.set("hours_to_show", String(Math.max(1, Math.round((end.getTime() - start.getTime()) / 36e5)))); + } + const zoomStart = options.zoom_start_time ? new Date(options.zoom_start_time) : null; + const zoomEnd = options.zoom_end_time ? new Date(options.zoom_end_time) : null; + if (zoomStart && zoomEnd && Number.isFinite(zoomStart.getTime()) && Number.isFinite(zoomEnd.getTime()) && zoomStart < zoomEnd) { + params.set("zoom_start_time", zoomStart.toISOString()); + params.set("zoom_end_time", zoomEnd.toISOString()); + } + const pageStateParam = serializeHistoryPageStateParam(options.page_state); + if (pageStateParam) params.set("page_state", pageStateParam); + return `/${PANEL_URL_PATH}?${params.toString()}`; + } + function navigateToDataPointsHistory(_card, target = {}, options = {}) { + const path = buildDataPointsHistoryPath(target, options); + if (window.history && window.history.pushState) { + window.history.pushState(null, "", path); + window.dispatchEvent(new Event("location-changed")); + return; + } + window.location.assign(path); + } + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/history-chart/history-chart.styles.ts + var styles$54 = ` + hass-datapoints-history-chart { + position: relative; + display: flex; + flex-direction: column; + height: 100%; + min-height: 0; + --dp-spacing-xs: calc(var(--spacing, 8px) * 0.5); + --dp-spacing-sm: var(--spacing, 8px); + --dp-spacing-md: calc(var(--spacing, 8px) * 1.5); + --dp-spacing-lg: calc(var(--spacing, 8px) * 2); + --dp-spacing-xl: calc(var(--spacing, 8px) * 2.5); + --ha-tooltip-background-color: color-mix(in srgb, #0f1218 96%, transparent); + --ha-tooltip-text-color: rgba(255, 255, 255, 0.96); + --ha-tooltip-padding: calc(var(--dp-spacing-sm) + 2px) calc(var(--dp-spacing-md) + 2px); + --ha-tooltip-border-radius: 10px; + --ha-tooltip-arrow-size: 10px; + --ha-tooltip-font-size: 0.86rem; + --ha-tooltip-line-height: 1.1; } - function getRoot$1(card) { - const rootNode = card.shadowRoot ?? card.getRootNode(); - if (rootNode instanceof ShadowRoot || rootNode instanceof Document) { - return rootNode; - } - return document; + ha-card { padding: 0; overflow: visible; height: 100%; display: flex; flex-direction: column; } + .card-header { + padding: var(--dp-spacing-lg); + font-size: 1.1em; + font-weight: 500; + color: var(--primary-text-color); + flex: 0 0 auto; + line-height: 1.3; } - function resolveChartLabelColor(el) { - if (!el) { - return "rgba(214,218,224,0.92)"; - } - const raw = getComputedStyle(el).getPropertyValue("--secondary-text-color").trim(); - if (raw) { - return raw; - } - return "rgba(214,218,224,0.92)"; - } - function setupCanvas(canvas, container, cssHeight, cssWidth = null) { - const dpr = window.devicePixelRatio || 1; - const maxCssDim = Math.floor(16383 / dpr); - const styles2 = getComputedStyle(container); - const paddingX = (Number.parseFloat(styles2.paddingLeft || "0") || 0) + (Number.parseFloat(styles2.paddingRight || "0") || 0); - const paddingY = (Number.parseFloat(styles2.paddingTop || "0") || 0) + (Number.parseFloat(styles2.paddingBottom || "0") || 0); - const measuredWidth = cssWidth ?? (container.clientWidth || 360); - const w = Math.min( - maxCssDim, - Math.max(1, Math.round(measuredWidth - paddingX)) - ); - const requestedHeight = cssHeight ?? container.clientHeight ?? 220; - const h2 = Math.min( - maxCssDim, - Math.max(40, Math.round(requestedHeight - paddingY)) - ); - canvas.width = w * dpr; - canvas.height = h2 * dpr; - canvas.style.width = `${w}px`; - canvas.style.height = `${h2}px`; - const ctx = canvas.getContext("2d"); - if (ctx && typeof ctx.scale === "function") { - ctx.scale(dpr, dpr); - } - return { w, h: h2 }; + .chart-top-slot[hidden] { + display: none; } - function renderChartAxisOverlays(card, renderer, axes = []) { - const leftEl = getRoot$1(card)?.getElementById("chart-axis-left"); - const rightEl = getRoot$1(card)?.getElementById("chart-axis-right"); - if (!leftEl || !rightEl || !renderer) { - return; - } - const leftWidth = Math.max(0, renderer.pad.left); - const rightWidth = Math.max(0, renderer.pad.right); - leftEl.style.width = `${leftWidth}px`; - rightEl.style.width = `${rightWidth}px`; - const chartWrap = getRoot$1(card).querySelector(".chart-wrap"); - const chartWrapEl = chartWrap instanceof HTMLElement ? chartWrap : card; - if (chartWrapEl) { - chartWrapEl.style.setProperty( - "--dp-chart-axis-left-width", - `${leftWidth}px` - ); - chartWrapEl.style.setProperty( - "--dp-chart-axis-right-width", - `${rightWidth}px` - ); - } - const axisSlotWidth = ChartRenderer.AXIS_SLOT_WIDTH; - const axisOffset = (axis) => 10 + (axis.slot ?? 0) * axisSlotWidth; - const unitCounts = axes.reduce((counts, axis) => { - if (!axis?.unit) { - return counts; - } - counts.set(axis.unit, (counts.get(axis.unit) || 0) + 1); - return counts; - }, /* @__PURE__ */ new Map()); - const axisTextStyle = (axis) => { - const duplicateUnit = !!axis?.unit && (unitCounts.get(axis.unit) || 0) > 1; - if (!duplicateUnit || !axis?.color) { - return ""; - } - return `color:${esc(axis.color)};`; - }; - const buildAxisMarkup = (axis) => { - const labels = (axis.ticks || []).map((tick) => { - const y2 = renderer.yOf(tick, axis.min, axis.max); - return `
${esc(renderer._formatAxisTick(tick, axis.unit))}
`; - }).join(""); - const unit = axis.unit ? `
${esc(axis.unit)}
` : ""; - return `${labels}${unit}`; - }; - const leftAxes = axes.filter((axis) => axis.side !== "right"); - const rightAxes = axes.filter((axis) => axis.side === "right"); - leftEl.innerHTML = leftAxes.length ? `
${leftAxes.map((axis) => buildAxisMarkup(axis)).join("")}` : ""; - rightEl.innerHTML = rightAxes.length ? `
${rightAxes.map((axis) => buildAxisMarkup(axis)).join("")}` : ""; - leftEl.classList.toggle("visible", !!leftAxes.length); - rightEl.classList.toggle("visible", !!rightAxes.length); - } - function renderChartAxisHoverDots(card, hoverValues = []) { - const root = getRoot$1(card); - const leftEl = root.getElementById("chart-axis-left"); - const rightEl = root.getElementById("chart-axis-right"); - const scrollViewport = root.getElementById("chart-scroll-viewport"); - if (!leftEl || !rightEl) { - return; - } - leftEl.querySelectorAll(".chart-axis-hover-dot").forEach((el) => el.remove()); - rightEl.querySelectorAll(".chart-axis-hover-dot").forEach((el) => el.remove()); - const verticalOffset = scrollViewport?.offsetTop || 0; - hoverValues.filter( - (entry) => entry?.hasValue !== false && Number.isFinite(entry?.y) - ).forEach((entry) => { - const target = entry.axisSide === "right" ? rightEl : leftEl; - const dot = document.createElement("span"); - dot.className = `chart-axis-hover-dot ${entry.axisSide === "right" ? "right" : "left"}`; - dot.style.top = `${verticalOffset + entry.y}px`; - dot.style.background = entry.color || "#03a9f4"; - dot.style.opacity = `${Number.isFinite(entry.opacity) ? entry.opacity : 1}`; - target.appendChild(dot); - }); - } - function positionTooltip(tooltip, clientX, clientY, bounds = null) { - tooltip.style.display = "block"; - const tipRect = tooltip.getBoundingClientRect(); - const tipW = tipRect.width || 220; - const tipH = tipRect.height || 64; - const gap = 12; - const minLeft = Number.isFinite(bounds?.left) ? bounds?.left : gap; - const maxLeft = Number.isFinite(bounds?.right) ? bounds?.right : window.innerWidth - gap; - const minTop = Number.isFinite(bounds?.top) ? bounds?.top : gap; - const maxTop = Number.isFinite(bounds?.bottom) ? bounds?.bottom : window.innerHeight - gap; - let left = clientX + gap; - if (left + tipW > maxLeft) { - left = clientX - tipW - gap; - } - let top = clientY - tipH - gap; - if (top < minTop) { - top = clientY + gap; - } - if (top + tipH > maxTop) { - top = Math.max(minTop, clientY - tipH - gap); - } - left = Math.min(Math.max(left, minLeft), Math.max(minLeft, maxLeft - tipW)); - top = Math.min(Math.max(top, minTop), Math.max(minTop, maxTop - tipH)); - tooltip.style.left = `${left}px`; - tooltip.style.top = `${top}px`; + .chart-top-slot { + position: relative; + flex: 0 0 auto; + min-width: 0; + margin-left: calc(var(--dp-spacing-md) * -1); + margin-right: calc(var(--dp-spacing-md) * -1); + margin-top: -5px; + z-index: 1; } - function clampChartValue(value, min, max) { - return Math.min(max, Math.max(min, value)); + .chart-wrap { + position: relative; + display: flex; + flex-direction: column; + flex: 1 1 auto; + min-height: 0; + padding: var(--dp-spacing-sm) var(--dp-spacing-md) var(--dp-spacing-md); + box-sizing: border-box; + overflow: visible; + isolation: isolate; + z-index: 3; } - function formatTooltipValue(value, unit = "") { - if (value == null || value === "" || Number.isNaN(Number(value))) { - return ""; - } - return `${Number(value).toFixed(2).replace(/\.00$/, "")}${unit ? ` ${unit}` : ""}`; + .chart-preview-overlay[hidden] { + display: none; } - function formatTooltipDisplayValue(value, unit = "") { - if (value == null || value === "") { - return "No value"; - } - if (typeof value === "string") { - return unit ? `${value} ${unit}` : value; - } - return formatTooltipValue(value, unit); + .chart-preview-overlay { + position: absolute; + top: calc(var(--dp-chart-top-slot-height, 0px) + var(--dp-spacing-sm)); + left: var(--dp-spacing-md); + display: flex; + flex-direction: column; + gap: 2px; + max-width: min(340px, calc(100% - (var(--dp-spacing-lg) * 2))); + padding: 8px 12px; + border-radius: 10px; + background: color-mix(in srgb, var(--card-background-color, #fff) 90%, transparent); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08); + backdrop-filter: blur(4px); + pointer-events: none; + z-index: 4; } - function entityName(hass, entityId) { - if (!hass || !entityId) { - return entityId || ""; - } - const state = hass.states?.[entityId]; - return state && state.attributes && state.attributes.friendly_name || entityId; + .chart-preview-kicker { + font-size: 0.68rem; + line-height: 1.15; + color: color-mix(in srgb, var(--warning-color, #f59e0b) 72%, var(--secondary-text-color, #6b7280)); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.04em; } - function entityIcon(hass, entityId) { - if (!hass || !entityId) { - return "mdi:link-variant"; - } - const state = hass.states?.[entityId]; - if (state?.attributes?.icon) { - return state.attributes.icon; - } - const domain = String(entityId).split(".")[0]; - const entry = hass.entities?.[entityId]; - if (entry?.icon) { - return entry.icon; - } - switch (domain) { - case "light": - return "mdi:lightbulb"; - case "switch": - return "mdi:toggle-switch"; - case "binary_sensor": - return "mdi:radiobox-marked"; - case "sensor": - return "mdi:chart-line"; - case "climate": - return "mdi:thermostat"; - case "cover": - return "mdi:window-shutter"; - case "lock": - return "mdi:lock"; - case "media_player": - return "mdi:play-box"; - case "person": - return "mdi:account"; - case "device_tracker": - return "mdi:crosshairs-gps"; - default: - return "mdi:link-variant"; - } + .chart-preview-title { + font-size: 0.84rem; + line-height: 1.2; + color: var(--primary-text-color); + font-weight: 600; } - function entityRegistryEntries(hass) { - return Object.entries(hass?.entities || {}); + .chart-preview-line { + font-size: 0.74rem; + line-height: 1.2; + color: var(--secondary-text-color); } - function firstRelatedEntityId(hass, matcher) { - return entityRegistryEntries(hass).find( - ([, entry]) => entry && typeof entry === "object" && matcher(entry) - )?.[0] || ""; + .chart-preview-line strong { + color: color-mix(in srgb, var(--warning-color, #f59e0b) 72%, var(--primary-text-color, #111)); + font-weight: 600; } - function deviceName(hass, deviceId) { - if (!hass || !deviceId) { - return deviceId || ""; - } - return hass.devices?.[deviceId]?.name ?? deviceId; + .chart-scroll-viewport { + position: relative; + flex: 1 1 auto; + min-height: 0; + overflow-x: auto; + overflow-y: hidden; + scrollbar-gutter: stable both-edges; + -webkit-overflow-scrolling: touch; } - function deviceIcon(hass, deviceId) { - if (!hass || !deviceId) { - return "mdi:devices"; - } - const entityId = firstRelatedEntityId( - hass, - (entry) => (entry.device_id || entry.deviceId) === deviceId - ); - return entityId ? entityIcon(hass, entityId) : "mdi:devices"; + .chart-stage { + position: relative; + min-height: 100%; } - function areaName(hass, areaId) { - if (!hass || !areaId) { - return areaId || ""; - } - return hass.areas?.[areaId]?.name ?? areaId; + .chart-icon-overlay { + position: absolute; + inset: 0; + pointer-events: none; + z-index: 2; } - function areaIcon(hass, areaId) { - if (!hass || !areaId) { - return "mdi:floor-plan"; - } - const entityId = firstRelatedEntityId( - hass, - (entry) => (entry.area_id || entry.areaId) === areaId - ); - return entityId ? entityIcon(hass, entityId) : "mdi:floor-plan"; + .chart-event-icon { + position: absolute; + width: 18px; + height: 18px; + transform: translate(-50%, -50%); + display: inline-flex; + align-items: center; + justify-content: center; + pointer-events: auto; + cursor: pointer; + border: 0; + padding: 0; + margin: 0; + background: transparent; + border-radius: 50%; } - function labelName(hass, labelId) { - if (!hass || !labelId) { - return labelId || ""; - } - return hass.labels?.[labelId]?.name ?? labelId; + .chart-event-icon ha-icon { + --mdc-icon-size: 14px; + pointer-events: none; } - function labelIcon(hass, labelId) { - if (!hass || !labelId) { - return "mdi:label-outline"; - } - const entityId = firstRelatedEntityId(hass, (entry) => { - const labels = [ - ...Array.isArray(entry.labels) ? entry.labels : [], - ...Array.isArray(entry.label_ids) ? entry.label_ids : [] - ]; - return labels.includes(labelId); - }); - return entityId ? entityIcon(hass, entityId) : "mdi:label-outline"; - } - function getRoot(card) { - const rootNode = card.shadowRoot ?? card.getRootNode(); - if (rootNode instanceof ShadowRoot || rootNode instanceof Document) { - return rootNode; - } - return document; + .chart-axis-overlay { + position: absolute; + top: calc(var(--dp-chart-top-slot-height, 0px) + 5px); + bottom: 0; + display: none; + pointer-events: none; + background: var(--card-background-color, var(--primary-background-color, #fff)); + overflow: hidden; + z-index: 3; + border-bottom-left-radius: 11px; } - function getInteractionState(card) { - return card; + .chart-axis-overlay.visible { + display: block; } - function toChartBounds(bounds) { - if (!bounds) { - return null; - } - return { - left: bounds.left + 8, - right: bounds.right - 8, - top: bounds.top + 8, - bottom: bounds.bottom - 8 - }; - } - function formatTooltipDateTimeFromMs(timeMs) { - if (!Number.isFinite(timeMs)) { - return ""; - } - return fmtDateTime(new Date(timeMs).toISOString()); - } - function t$2(key, ...values) { - let s2 = msg(key); - values.forEach((v2, i2) => { - s2 = s2.replace(new RegExp(`\\{${i2}\\}`, "g"), v2); - }); - return s2; - } - function getAnomalyMethodLabels() { - return { - trend_residual: msg("Trend deviation"), - rate_of_change: msg("Sudden change"), - iqr: msg("Statistical outlier (IQR)"), - rolling_zscore: msg("Rolling Z-score"), - persistence: msg("Flat-line / stuck"), - comparison_window: msg("Comparison window") - }; - } - function buildAnomalyMethodSection(region) { - if (!region?.cluster?.points?.length) { - return null; - } - const points = region.cluster.points; - const startPoint = points[0]; - const endPoint = points[points.length - 1]; - const peakPoint = points.reduce( - (peak, p2) => !peak || Math.abs(p2.residual) > Math.abs(peak.residual) ? p2 : peak, - null - ); - if (!peakPoint) { - return null; - } - const label = region.label || region.relatedEntityId || "Series"; - const unit = region.unit || ""; - const cluster = region.cluster; - const method = cluster.anomalyMethod ?? "trend_residual"; - const methodLabel = getAnomalyMethodLabels()[method] || method; - let description; - let alert; - if (method === "rate_of_change") { - const rateUnit = unit ? `${unit}/h` : "units/h"; - description = t$2( - "{0} shows an unusual rate of change between {1} and {2}.", - label, - formatTooltipDateTimeFromMs(startPoint.timeMs), - formatTooltipDateTimeFromMs(endPoint.timeMs) - ); - alert = t$2( - "Peak rate deviation: {0} from a typical rate of {1} at {2}.", - formatTooltipValue(peakPoint.residual, rateUnit), - formatTooltipValue(peakPoint.baselineValue, rateUnit), - formatTooltipDateTimeFromMs(peakPoint.timeMs) - ); - } else if (method === "iqr") { - description = t$2( - "{0} contains statistical outliers between {1} and {2}.", - label, - formatTooltipDateTimeFromMs(startPoint.timeMs), - formatTooltipDateTimeFromMs(endPoint.timeMs) - ); - alert = t$2( - "Peak value: {0}, deviating {1} from the median at {2}.", - formatTooltipValue(peakPoint.value, unit), - formatTooltipValue(Math.abs(peakPoint.residual), unit), - formatTooltipDateTimeFromMs(peakPoint.timeMs) - ); - } else if (method === "rolling_zscore") { - description = t$2( - "{0} shows statistically unusual values between {1} and {2}.", - label, - formatTooltipDateTimeFromMs(startPoint.timeMs), - formatTooltipDateTimeFromMs(endPoint.timeMs) - ); - alert = t$2( - "Peak deviation: {0} from a rolling mean of {1} at {2}.", - formatTooltipValue(peakPoint.residual, unit), - formatTooltipValue(peakPoint.baselineValue, unit), - formatTooltipDateTimeFromMs(peakPoint.timeMs) - ); - } else if (method === "persistence") { - const flatRange = typeof cluster.flatRange === "number" ? cluster.flatRange : null; - const rangeStr = flatRange !== null ? t$2(" (range: {0})", formatTooltipValue(flatRange, unit)) : ""; - description = t$2( - "{0} appears stuck or flat between {1} and {2}{3}.", - label, - formatTooltipDateTimeFromMs(startPoint.timeMs), - formatTooltipDateTimeFromMs(endPoint.timeMs), - rangeStr - ); - alert = t$2( - "Value remained near {0} for an unusually long period.", - formatTooltipValue(peakPoint.baselineValue, unit) - ); - } else if (method === "comparison_window") { - description = t$2( - "{0} deviates significantly from the comparison window between {1} and {2}.", - label, - formatTooltipDateTimeFromMs(startPoint.timeMs), - formatTooltipDateTimeFromMs(endPoint.timeMs) - ); - alert = t$2( - "Peak deviation from comparison: {0} at {1}.", - formatTooltipValue(peakPoint.residual, unit), - formatTooltipDateTimeFromMs(peakPoint.timeMs) - ); - } else { - description = t$2( - "{0} deviates from its expected trend between {1} and {2}.", - label, - formatTooltipDateTimeFromMs(startPoint.timeMs), - formatTooltipDateTimeFromMs(endPoint.timeMs) - ); - alert = t$2( - "Peak deviation: {0} from a baseline of {1} at {2}.", - formatTooltipValue(peakPoint.residual, unit), - formatTooltipValue(peakPoint.baselineValue, unit), - formatTooltipDateTimeFromMs(peakPoint.timeMs) - ); - } - return { methodLabel, description, alert }; - } - function buildAnomalyTooltipContent(regions) { - let regionsArray; - if (Array.isArray(regions)) { - regionsArray = regions; - } else if (regions) { - regionsArray = [regions]; - } else { - regionsArray = []; - } - if (regionsArray.length === 0) { - return null; - } - const sections = regionsArray.map(buildAnomalyMethodSection).filter((section) => section !== null); - if (sections.length === 0) { - return null; - } - const instruction = msg( - "Click the highlighted circle to add an annotation.", - { id: "Click the highlighted circle to add an annotation." } - ); - if (sections.length === 1) { - const section = sections[0]; - const cluster = regionsArray[0]?.cluster; - const detectedByMethods = Array.isArray(cluster?.detectedByMethods) && cluster.detectedByMethods.length > 1 ? cluster.detectedByMethods : null; - const isMultiMethod = detectedByMethods !== null; - const title = isMultiMethod ? msg("⚠️ Multi-method Anomaly") : msg("⚠️ Anomaly Insight"); - const labels = getAnomalyMethodLabels(); - const confirmedNote = isMultiMethod ? ` -${msg("Confirmed by")} ${detectedByMethods.length} ${msg("methods:")} ${detectedByMethods.map( - (method) => labels[method] || method - ).join(", ")}.` : ""; - return { - title, - description: section.description + confirmedNote, - alert: `${msg("Alert:")} ${section.alert}`, - instruction - }; - } - const description = sections.map((s2) => `${s2.methodLabel}: -${s2.description}`).join("\n\n"); - const alert = sections.map((s2) => `${s2.methodLabel}: ${s2.alert}`).join("\n"); - return { - title: msg("⚠️ Multi-method Anomaly"), - description, - alert, - instruction - }; - } - function positionAnomalyTooltip(tooltip, clientX, clientY, mainTooltip, bounds = null) { - if (!tooltip) { - return; - } - tooltip.style.display = "block"; - const tipRect = tooltip.getBoundingClientRect(); - const tipW = tipRect.width || 220; - const tipH = tipRect.height || 64; - const gap = 12; - const minLeft = Number.isFinite(bounds?.left) ? bounds?.left : gap; - const maxLeft = Number.isFinite(bounds?.right) ? bounds?.right : window.innerWidth - gap; - const minTop = Number.isFinite(bounds?.top) ? bounds?.top : gap; - const maxTop = Number.isFinite(bounds?.bottom) ? bounds?.bottom : window.innerHeight - gap; - let left = clientX - gap - tipW; - if (left < minLeft) { - const mainRect2 = mainTooltip ? mainTooltip.getBoundingClientRect() : null; - left = mainRect2 ? mainRect2.right + gap : clientX + gap; - } - const mainRect = mainTooltip ? mainTooltip.getBoundingClientRect() : null; - let top = mainRect ? mainRect.top : clientY - tipH - gap; - if (top + tipH > maxTop) { - top = Math.max(minTop, maxTop - tipH); - } - left = Math.min(Math.max(left, minLeft), Math.max(minLeft, maxLeft - tipW)); - top = Math.min(Math.max(top, minTop), Math.max(minTop, maxTop - tipH)); - tooltip.style.left = `${left}px`; - tooltip.style.top = `${top}px`; - } - function positionSecondaryTooltip(tooltip, anchorTooltip, bounds = null) { - if (!tooltip || !anchorTooltip) { - return; - } - tooltip.style.display = "block"; - const anchorRect = anchorTooltip.getBoundingClientRect(); - const tipRect = tooltip.getBoundingClientRect(); - const gap = 10; - const minLeft = Number.isFinite(bounds?.left) ? bounds?.left : gap; - const maxLeft = Number.isFinite(bounds?.right) ? bounds?.right : window.innerWidth - gap; - const minTop = Number.isFinite(bounds?.top) ? bounds?.top : gap; - const maxTop = Number.isFinite(bounds?.bottom) ? bounds?.bottom : window.innerHeight - gap; - let left = anchorRect.right + gap; - if (left + tipRect.width > maxLeft) { - left = anchorRect.left - tipRect.width - gap; - } - let top = anchorRect.top; - if (top + tipRect.height > maxTop) { - top = Math.max(minTop, maxTop - tipRect.height); - } - left = Math.min( - Math.max(left, minLeft), - Math.max(minLeft, maxLeft - tipRect.width) - ); - top = Math.min( - Math.max(top, minTop), - Math.max(minTop, maxTop - tipRect.height) - ); - tooltip.style.left = `${left}px`; - tooltip.style.top = `${top}px`; + .chart-axis-overlay.left { + left: 0; } - function positionTooltipBelow(tooltip, anchorTooltip, bounds = null) { - if (!tooltip || !anchorTooltip) { - return; - } - tooltip.style.display = "block"; - const anchorRect = anchorTooltip.getBoundingClientRect(); - const tipRect = tooltip.getBoundingClientRect(); - const gap = 8; - const minLeft = Number.isFinite(bounds?.left) ? bounds?.left : gap; - const maxLeft = Number.isFinite(bounds?.right) ? bounds?.right : window.innerWidth - gap; - const minTop = Number.isFinite(bounds?.top) ? bounds?.top : gap; - const maxTop = Number.isFinite(bounds?.bottom) ? bounds?.bottom : window.innerHeight - gap; - let left = anchorRect.left; - if (left + tipRect.width > maxLeft) { - left = Math.max(minLeft, maxLeft - tipRect.width); - } - let top = anchorRect.bottom + gap; - if (top + tipRect.height > maxTop) { - top = Math.max(minTop, anchorRect.top - tipRect.height - gap); - } - left = Math.min( - Math.max(left, minLeft), - Math.max(minLeft, maxLeft - tipRect.width) - ); - top = Math.min( - Math.max(top, minTop), - Math.max(minTop, maxTop - tipRect.height) - ); - tooltip.style.left = `${left}px`; - tooltip.style.top = `${top}px`; + .chart-axis-overlay.right { + right: 0; } - function getAnnotationTooltipContainer(card) { - if (!card || !getRoot(card)) { - return null; - } - return getRoot(card).getElementById("annotation-tooltips"); + .chart-axis-divider { + position: absolute; + top: 0; + bottom: 0; + width: 1px; + background: rgba(128,128,128,0.35); } - function clearAnnotationTooltips(card) { - const container = getAnnotationTooltipContainer(card); - if (!container) { - return; - } - container.innerHTML = ""; - } - function buildAnnotationTooltip(card, event) { - const interactionState = getInteractionState(card); - const tooltip = document.createElement("div"); - tooltip.className = "tooltip secondary annotation-tooltip"; - const hasValue = event?.chart_value != null && event.chart_value !== ""; - const valueMarkup = hasValue ? `
${esc(formatTooltipValue(event.chart_value, event.chart_unit))}
` : ""; - const message = event?.message || "Data point"; - const annotation = event?.annotation && event.annotation !== event.message ? event.annotation : ""; - const relatedMarkup = buildTooltipRelatedChips(interactionState._hass, event); - tooltip.innerHTML = ` -
${esc(fmtDateTime(event.timestamp))}
- ${valueMarkup} -
- - ${esc(message)} -
-
${esc(annotation)}
-
${relatedMarkup}
- `; - return tooltip; - } - function renderAnnotationTooltips(card, hover, anchorTooltip, bounds = null) { - const container = getAnnotationTooltipContainer(card); - if (!container) { - return []; - } - clearAnnotationTooltips(card); - const annotationEvents = Array.isArray(hover?.events) ? hover.events : []; - if (!annotationEvents.length) { - return []; - } - const renderedTooltips = []; - let anchorEl = anchorTooltip; - for (const event of annotationEvents) { - const tooltip = buildAnnotationTooltip(card, event); - container.appendChild(tooltip); - if (renderedTooltips.length === 0) { - positionSecondaryTooltip(tooltip, anchorEl, bounds); - } else { - positionTooltipBelow(tooltip, anchorEl, bounds); - } - renderedTooltips.push(tooltip); - anchorEl = tooltip; - } - return renderedTooltips; + .chart-axis-overlay.left .chart-axis-divider { + right: 0; } - function hideTooltip(card) { - const tooltip = getRoot(card).getElementById("tooltip"); - const anomalyTooltip = getRoot(card).getElementById("anomaly-tooltip"); - if (tooltip) { - tooltip.style.display = "none"; - } - if (anomalyTooltip) { - anomalyTooltip.style.display = "none"; - } - clearAnnotationTooltips(card); - } - function resolveTooltipSeriesLabel(entry) { - const isSubordinate = entry.grouped === true && entry.rawVisible === true; - const isComparisonDerived = entry.comparisonDerived === true && entry.grouped === true; - if (entry.comparison === true) { - const windowLabel = String(entry.windowLabel || msg("Date window")); - if (entry.grouped === true) { - return windowLabel; - } - return `${windowLabel}: ${String(entry.label || "")}`; - } - if (entry.trend === true) { - const trendLabel = msg("Trend"); - if (isSubordinate || isComparisonDerived) { - return trendLabel; - } - return `${trendLabel}: ${entry.baseLabel || entry.label || ""}`; - } - if (entry.rate === true) { - const rateLabel = msg("Rate"); - if (isSubordinate || isComparisonDerived) { - return rateLabel; - } - return `${rateLabel}: ${entry.baseLabel || entry.label || ""}`; - } - if (entry.delta === true) { - const deltaLabel = msg("Delta"); - if (isSubordinate || isComparisonDerived) { - return deltaLabel; - } - return `${deltaLabel}: ${entry.baseLabel || entry.label || ""}`; - } - if (entry.summary === true) { - const summaryLabel = String(entry.summaryType || "").toUpperCase(); - if (isSubordinate || isComparisonDerived) { - return summaryLabel; - } - return `${summaryLabel}: ${entry.baseLabel || entry.label || ""}`; - } - if (entry.threshold === true) { - const thresholdLabel = msg("Threshold"); - if (isSubordinate || isComparisonDerived) { - return thresholdLabel; - } - return `${thresholdLabel}: ${entry.baseLabel || entry.label || ""}`; - } - return String(entry.label || ""); - } - function showLineChartTooltip(card, hover, clientX, clientY) { - const root = getRoot(card); - const tooltip = root.getElementById("tooltip"); - const ttTime = root.getElementById("tt-time"); - const ttValue = root.getElementById("tt-value"); - const ttSeries = root.getElementById("tt-series"); - const anomalyTooltip = root.getElementById( - "anomaly-tooltip" - ); - const ttSecondaryTitle = root.getElementById("tt-secondary-title"); - const ttSecondaryDescription = root.getElementById( - "tt-secondary-description" - ); - const ttSecondaryAlert = root.getElementById("tt-secondary-alert"); - const ttSecondaryInstruction = root.getElementById( - "tt-secondary-instruction" - ); - const ttMessageRow = root.getElementById( - "tt-message-row" - ); - const ttMsg = root.getElementById("tt-message"); - const ttAnn = root.getElementById("tt-annotation"); - const ttEntities = root.getElementById( - "tt-entities" - ); - if (!tooltip || !ttTime || !ttValue || !ttMessageRow || !ttMsg || !ttAnn || !ttEntities) { - return; - } - const rangeStartMs = Number.isFinite(hover.rangeStartMs) ? hover.rangeStartMs : hover.timeMs; - const rangeEndMs = Number.isFinite(hover.rangeEndMs) ? hover.rangeEndMs : hover.timeMs; - ttTime.textContent = rangeStartMs === rangeEndMs ? fmtDateTime(new Date(hover.timeMs).toISOString()) : `${fmtDateTime(new Date(rangeStartMs).toISOString())} - ${fmtDateTime(new Date(rangeEndMs).toISOString())}`; - const values = Array.isArray(hover.values) ? hover.values : []; - const trendValues = Array.isArray(hover.trendValues) ? hover.trendValues : []; - const rateValues = Array.isArray(hover.rateValues) ? hover.rateValues : []; - const deltaValues = Array.isArray(hover.deltaValues) ? hover.deltaValues : []; - const summaryValues = Array.isArray(hover.summaryValues) ? hover.summaryValues : []; - const thresholdValues = Array.isArray(hover.thresholdValues) ? hover.thresholdValues : []; - const binaryValues = Array.isArray(hover.binaryValues) ? hover.binaryValues : []; - const comparisonValues = Array.isArray(hover.comparisonValues) ? hover.comparisonValues : []; - const displayRows = []; - const usedTrendRows = /* @__PURE__ */ new Set(); - const usedRateRows = /* @__PURE__ */ new Set(); - const usedDeltaRows = /* @__PURE__ */ new Set(); - const usedSummaryRows = /* @__PURE__ */ new Set(); - const usedThresholdRows = /* @__PURE__ */ new Set(); - const usedComparisonRows = /* @__PURE__ */ new Set(); - const pushComparisonDerivedRows = (comparisonEntry, comparisonIndex) => { - trendValues.forEach((trendEntry, trendIndex) => { - if (usedTrendRows.has(trendIndex)) { - return; - } - if (trendEntry.comparisonParentId !== comparisonEntry.entityId && !(trendEntry.relatedEntityId === comparisonEntry.relatedEntityId && trendEntry.windowLabel === comparisonEntry.windowLabel)) { - return; - } - usedTrendRows.add(trendIndex); - displayRows.push({ - ...trendEntry, - rawVisible: true, - comparisonDerived: true, - grouped: true, - key: `comparison-trend-${comparisonIndex}-${trendIndex}` - }); - }); - rateValues.forEach((rateEntry, rateIndex) => { - if (usedRateRows.has(rateIndex)) { - return; - } - if (rateEntry.comparisonParentId !== comparisonEntry.entityId && !(rateEntry.relatedEntityId === comparisonEntry.relatedEntityId && rateEntry.windowLabel === comparisonEntry.windowLabel)) { - return; - } - usedRateRows.add(rateIndex); - displayRows.push({ - ...rateEntry, - rawVisible: true, - comparisonDerived: true, - grouped: true, - key: `comparison-rate-${comparisonIndex}-${rateIndex}` - }); - }); - summaryValues.forEach((summaryEntry, summaryIndex) => { - if (usedSummaryRows.has(summaryIndex)) { - return; - } - if (summaryEntry.comparisonParentId !== comparisonEntry.entityId && !(summaryEntry.relatedEntityId === comparisonEntry.relatedEntityId && summaryEntry.windowLabel === comparisonEntry.windowLabel)) { - return; - } - usedSummaryRows.add(summaryIndex); - displayRows.push({ - ...summaryEntry, - rawVisible: true, - comparisonDerived: true, - grouped: true, - key: `comparison-summary-${comparisonIndex}-${summaryIndex}` - }); - }); - thresholdValues.forEach((thresholdEntry, thresholdIndex) => { - if (usedThresholdRows.has(thresholdIndex)) { - return; - } - if (thresholdEntry.comparisonParentId !== comparisonEntry.entityId && !(thresholdEntry.relatedEntityId === comparisonEntry.relatedEntityId && thresholdEntry.windowLabel === comparisonEntry.windowLabel)) { - return; - } - usedThresholdRows.add(thresholdIndex); - displayRows.push({ - ...thresholdEntry, - rawVisible: true, - comparisonDerived: true, - grouped: true, - key: `comparison-threshold-${comparisonIndex}-${thresholdIndex}` - }); - }); - }; - values.forEach((entry, index) => { - displayRows.push(entry); - trendValues.forEach((trendEntry, trendIndex) => { - if (usedTrendRows.has(trendIndex)) { - return; - } - const sameEntity = trendEntry.relatedEntityId && trendEntry.relatedEntityId === entry.entityId; - const sameLabel = !trendEntry.relatedEntityId && trendEntry.baseLabel && trendEntry.baseLabel === entry.label; - if (!sameEntity && !sameLabel) { - return; - } - usedTrendRows.add(trendIndex); - displayRows.push({ - ...trendEntry, - rawVisible: trendEntry.rawVisible !== false, - grouped: true, - key: `trend-${index}-${trendIndex}` - }); - }); - rateValues.forEach((rateEntry, rateIndex) => { - if (usedRateRows.has(rateIndex)) { - return; - } - const sameEntity = rateEntry.relatedEntityId && rateEntry.relatedEntityId === entry.entityId; - const sameLabel = !rateEntry.relatedEntityId && rateEntry.baseLabel && rateEntry.baseLabel === entry.label; - if (!sameEntity && !sameLabel) { - return; - } - usedRateRows.add(rateIndex); - displayRows.push({ - ...rateEntry, - rawVisible: rateEntry.rawVisible !== false, - grouped: true, - key: `rate-${index}-${rateIndex}` - }); - }); - deltaValues.forEach((deltaEntry, deltaIndex) => { - if (usedDeltaRows.has(deltaIndex)) { - return; - } - const sameEntity = deltaEntry.relatedEntityId && deltaEntry.relatedEntityId === entry.entityId; - const sameLabel = !deltaEntry.relatedEntityId && deltaEntry.baseLabel && deltaEntry.baseLabel === entry.label; - if (!sameEntity && !sameLabel) { - return; - } - usedDeltaRows.add(deltaIndex); - displayRows.push({ - ...deltaEntry, - rawVisible: deltaEntry.rawVisible !== false, - grouped: true, - key: `delta-${index}-${deltaIndex}` - }); - }); - summaryValues.forEach((summaryEntry, summaryIndex) => { - if (usedSummaryRows.has(summaryIndex)) { - return; - } - const sameEntity = summaryEntry.relatedEntityId && summaryEntry.relatedEntityId === entry.entityId; - const sameLabel = !summaryEntry.relatedEntityId && summaryEntry.baseLabel && summaryEntry.baseLabel === entry.label; - if (!sameEntity && !sameLabel) { - return; - } - usedSummaryRows.add(summaryIndex); - displayRows.push({ - ...summaryEntry, - rawVisible: summaryEntry.rawVisible !== false, - grouped: true, - key: `summary-${index}-${summaryIndex}` - }); - }); - thresholdValues.forEach((thresholdEntry, thresholdIndex) => { - if (usedThresholdRows.has(thresholdIndex)) { - return; - } - const sameEntity = thresholdEntry.relatedEntityId && thresholdEntry.relatedEntityId === entry.entityId; - const sameLabel = !thresholdEntry.relatedEntityId && thresholdEntry.baseLabel && thresholdEntry.baseLabel === entry.label; - if (!sameEntity && !sameLabel) { - return; - } - usedThresholdRows.add(thresholdIndex); - displayRows.push({ - ...thresholdEntry, - rawVisible: thresholdEntry.rawVisible !== false, - grouped: true, - key: `threshold-${index}-${thresholdIndex}` - }); - }); - comparisonValues.forEach((compEntry, compIndex) => { - if (usedComparisonRows.has(compIndex)) { - return; - } - if (!compEntry.relatedEntityId || compEntry.relatedEntityId !== entry.entityId) { - return; - } - usedComparisonRows.add(compIndex); - const groupedEntry = { - ...compEntry, - grouped: true, - comparison: true, - key: `comparison-${index}-${compIndex}` - }; - displayRows.push(groupedEntry); - pushComparisonDerivedRows(groupedEntry, compIndex); - }); - }); - trendValues.forEach((trendEntry, trendIndex) => { - if (usedTrendRows.has(trendIndex)) { - return; - } - if (trendEntry.comparisonDerived === true || typeof trendEntry.comparisonParentId === "string") { - return; - } - displayRows.push({ - ...trendEntry, - rawVisible: trendEntry.rawVisible !== false - }); - }); - rateValues.forEach((rateEntry, rateIndex) => { - if (usedRateRows.has(rateIndex)) { - return; - } - if (rateEntry.comparisonDerived === true || typeof rateEntry.comparisonParentId === "string") { - return; - } - displayRows.push({ - ...rateEntry, - rawVisible: rateEntry.rawVisible !== false - }); - }); - deltaValues.forEach((deltaEntry, deltaIndex) => { - if (usedDeltaRows.has(deltaIndex)) { - return; - } - displayRows.push({ - ...deltaEntry, - rawVisible: deltaEntry.rawVisible !== false - }); - }); - summaryValues.forEach((summaryEntry, summaryIndex) => { - if (usedSummaryRows.has(summaryIndex)) { - return; - } - if (summaryEntry.comparisonDerived === true || typeof summaryEntry.comparisonParentId === "string") { - return; - } - displayRows.push({ - ...summaryEntry, - rawVisible: summaryEntry.rawVisible !== false - }); - }); - thresholdValues.forEach((thresholdEntry, thresholdIndex) => { - if (usedThresholdRows.has(thresholdIndex)) { - return; - } - if (thresholdEntry.comparisonDerived === true || typeof thresholdEntry.comparisonParentId === "string") { - return; - } - displayRows.push({ - ...thresholdEntry, - rawVisible: thresholdEntry.rawVisible !== false - }); - }); - comparisonValues.forEach((compEntry, compIndex) => { - if (usedComparisonRows.has(compIndex)) { - return; - } - const groupedEntry = { ...compEntry, comparison: true }; - displayRows.push(groupedEntry); - pushComparisonDerivedRows(groupedEntry, compIndex); - }); - displayRows.push(...binaryValues); - const useSingleValueMode = displayRows.length === 1 && trendValues.length === 0 && rateValues.length === 0 && deltaValues.length === 0 && summaryValues.length === 0 && thresholdValues.length === 0 && comparisonValues.length === 0 && binaryValues.length === 0 && displayRows[0]?.comparison !== true; - if (useSingleValueMode) { - const value = displayRows[0]; - ttValue.textContent = value ? formatTooltipDisplayValue(value.value, value.unit) : ""; - ttValue.style.display = value ? "block" : "none"; - if (ttSeries) { - ttSeries.innerHTML = ""; - ttSeries.style.display = "none"; - } - } else { - ttValue.textContent = ""; - ttValue.style.display = "none"; - if (ttSeries) { - ttSeries.innerHTML = displayRows.map( - (entry) => ` -
-
- ${entry.grouped === true && entry.rawVisible === true ? "" : ``} - ${esc(resolveTooltipSeriesLabel(entry))} -
- ${esc(formatTooltipDisplayValue(entry.value, entry.unit))} -
- ` - ).join(""); - ttSeries.style.display = displayRows.length ? "grid" : "none"; - } - } - ttMessageRow.style.display = "none"; - ttMsg.textContent = ""; - ttAnn.textContent = ""; - ttAnn.style.display = "none"; - ttEntities.innerHTML = ""; - ttEntities.style.display = "none"; - if (anomalyTooltip && ttSecondaryTitle && ttSecondaryDescription && ttSecondaryAlert && ttSecondaryInstruction) { - const anomalyContent = buildAnomalyTooltipContent(hover.anomalyRegions); - if (anomalyContent) { - ttSecondaryTitle.textContent = anomalyContent.title; - ttSecondaryDescription.textContent = anomalyContent.description; - ttSecondaryAlert.textContent = anomalyContent.alert; - ttSecondaryInstruction.textContent = anomalyContent.instruction; - } else { - ttSecondaryTitle.textContent = ""; - ttSecondaryDescription.textContent = ""; - ttSecondaryAlert.textContent = ""; - ttSecondaryInstruction.textContent = ""; - anomalyTooltip.style.display = "none"; - } - } - const chartBounds = (root.querySelector(".chart-wrap") ?? card).getBoundingClientRect(); - positionTooltip(tooltip, clientX, clientY, toChartBounds(chartBounds)); - if (anomalyTooltip && (hover.anomalyRegions?.length ?? 0) > 0) { - positionAnomalyTooltip( - anomalyTooltip, - clientX, - clientY, - tooltip, - toChartBounds(chartBounds) - ); - } - if (Array.isArray(hover.events) && hover.events.length > 0) { - renderAnnotationTooltips(card, hover, tooltip, toChartBounds(chartBounds)); - } else { - clearAnnotationTooltips(card); - } + .chart-axis-overlay.right .chart-axis-divider { + left: 0; } - function buildTooltipRelatedChips(hass, event) { - const entities = Array.isArray(event?.entity_ids) ? event.entity_ids : []; - const devices = Array.isArray(event?.device_ids) ? event.device_ids : []; - const areas = Array.isArray(event?.area_ids) ? event.area_ids : []; - const labels = Array.isArray(event?.label_ids) ? event.label_ids : []; - const chips = [ - ...entities.map((id) => ({ - icon: entityIcon(hass, id), - label: entityName(hass, id) - })), - ...devices.map((id) => ({ - icon: deviceIcon(hass, id), - label: deviceName(hass, id) - })), - ...areas.map((id) => ({ - icon: areaIcon(hass, id), - label: areaName(hass, id) - })), - ...labels.map((id) => ({ - icon: labelIcon(hass, id), - label: labelName(hass, id) - })) - ].filter((chip) => chip.label); - if (!chips.length) return ""; - return chips.map( - (chip) => ` - - - ${esc(chip.label)} - - ` - ).join(""); - } - function showLineChartCrosshair(card, renderer, hover) { - const overlay = getRoot(card).getElementById("chart-crosshair"); - const vertical = getRoot(card).getElementById("crosshair-vertical"); - const horizontal = getRoot(card).getElementById("crosshair-horizontal"); - const points = getRoot(card).getElementById("crosshair-points"); - const addButton = getRoot(card).getElementById("chart-add-annotation"); - if (!overlay || !vertical || !horizontal || !points) return; - overlay.hidden = false; - vertical.style.left = `${hover.x}px`; - if (hover.splitVertical) { - vertical.style.top = `${hover.splitVertical.top}px`; - vertical.style.height = `${hover.splitVertical.height}px`; - } else { - vertical.style.top = `${renderer.pad.top}px`; - vertical.style.height = `${renderer.ch}px`; - } - horizontal.hidden = true; - const crosshairValues = [ - ...hover.values || [], - ...hover.showTrendCrosshairs === true ? (hover.trendValues || []).filter( - (entry) => entry.showCrosshair === true - ) : [], - ...hover.showRateCrosshairs === true ? (hover.rateValues || []).filter((entry) => entry.showCrosshair === true) : [], - ...hover.comparisonValues || [] - ]; - points.innerHTML = ` - ${crosshairValues.filter((entry) => entry.hasValue !== false).map( - (entry) => ` - - ` - ).join("")} - ${crosshairValues.filter((entry) => entry.hasValue !== false).map( - (entry) => ` - - ` - ).join("")} - `; - renderChartAxisHoverDots(card, crosshairValues); - if (addButton && addButton.dataset.allowAddAnnotation !== "false") { - addButton.hidden = false; - addButton.style.left = `${hover.x}px`; - if (hover.splitVertical) { - addButton.style.top = `${hover.splitVertical.top + hover.splitVertical.height}px`; - } else { - addButton.style.top = `${renderer.pad.top + renderer.ch}px`; - } - } + .chart-axis-label, + .chart-axis-unit { + position: absolute; + color: var(--secondary-text-color); + font: 12px sans-serif; + line-height: 1; + white-space: nowrap; } - function dispatchLineChartHover(card, hover) { - card.dispatchEvent( - new CustomEvent("hass-datapoints-chart-hover", { - bubbles: true, - composed: true, - detail: hover ? { timeMs: hover.timeMs } : { timeMs: null } - }) - ); + .chart-axis-label { + transform: translateY(calc(-50% + 6px)); } - function findNearestSeriesPointTime(seriesPoints, timeMs) { - if (!Array.isArray(seriesPoints) || seriesPoints.length === 0) { - return null; - } - let lo = 0; - let hi = seriesPoints.length - 1; - while (lo + 1 < hi) { - const mid = Math.floor((lo + hi) / 2); - if (seriesPoints[mid][0] <= timeMs) { - lo = mid; - } else { - hi = mid; - } - } - const left = seriesPoints[lo]?.[0]; - const right = seriesPoints[hi]?.[0]; - if (!Number.isFinite(left) && !Number.isFinite(right)) { - return null; - } - if (!Number.isFinite(left)) { - return right; - } - if (!Number.isFinite(right)) { - return left; - } - return Math.abs(left - timeMs) <= Math.abs(right - timeMs) ? left : right; + .chart-axis-unit { + font-weight: 500; } - function resolveLineChartHoverTime(series, timeMs, mode = "follow_series") { - if (mode !== "snap_to_data_points") { - return timeMs; - } - let bestTime = null; - let bestDistance = Infinity; - for (const seriesItem of Array.isArray(series) ? series : []) { - const candidateTime = findNearestSeriesPointTime(seriesItem?.pts, timeMs); - if (candidateTime == null || !Number.isFinite(candidateTime)) { - continue; - } - const distance = Math.abs(candidateTime - timeMs); - if (distance < bestDistance) { - bestDistance = distance; - bestTime = candidateTime; - } - } - return bestTime != null && Number.isFinite(bestTime) ? bestTime : timeMs; - } - function hideLineChartHover(card) { - dispatchLineChartHover(card, null); - hideTooltip(card); - const overlay = getRoot(card).getElementById("chart-crosshair"); - const points = getRoot(card).getElementById("crosshair-points"); - const addButton = getRoot(card).getElementById("chart-add-annotation"); - if (overlay) overlay.hidden = true; - if (points) points.innerHTML = ""; - renderChartAxisHoverDots(card, []); - const horizontal = getRoot(card).getElementById("crosshair-horizontal"); - if (horizontal) horizontal.hidden = true; - if (addButton) addButton.hidden = true; - } - function attachLineChartHover(card, canvas, renderer, series, events, t0, t1, vMin, vMax, axes = null, options = {}) { - const interactionState = getInteractionState(card); - if (!canvas || !renderer) { - return; - } - if (interactionState._chartHoverCleanup) { - interactionState._chartHoverCleanup(); - interactionState._chartHoverCleanup = null; - } - const resolvedSeries = Array.isArray(series) ? series : []; - const eventThresholdMs = renderer.cw ? 14 * ((t1 - t0) / renderer.cw) : 0; - const binaryStates = Array.isArray(options.binaryStates) ? options.binaryStates : []; - const comparisonSeries = Array.isArray(options.comparisonSeries) ? options.comparisonSeries : []; - const trendSeries = Array.isArray(options.trendSeries) ? options.trendSeries : []; - const rateSeries = Array.isArray(options.rateSeries) ? options.rateSeries : []; - const deltaSeries = Array.isArray(options.deltaSeries) ? options.deltaSeries : []; - const summarySeries = Array.isArray(options.summarySeries) ? options.summarySeries : []; - const thresholdSeries = Array.isArray(options.thresholdSeries) ? options.thresholdSeries : []; - const anomalyRegions = Array.isArray(options.anomalyRegions) ? options.anomalyRegions : []; - if (!resolvedSeries.length && !binaryStates.length && !comparisonSeries.length && !trendSeries.length && !rateSeries.length && !deltaSeries.length && !summarySeries.length && !thresholdSeries.length && !anomalyRegions.length) { - return; - } - const hoverSurfaceEl = options.hoverSurfaceEl || null; - const addAnnotationButton = getRoot(card)?.getElementById("chart-add-annotation") || null; - const resolveHoverAxis = (seriesItem) => seriesItem.axis || axes && axes[0] || { min: vMin, max: vMax }; - const buildHoverValueEntry = (seriesItem, value, axis, extra = {}, entryOpts = {}) => { - const hasNumericValue = typeof value === "number" && Number.isFinite(value); - const includePosition = entryOpts.includePosition === true && hasNumericValue; - return { - entityId: seriesItem.entityId || "", - comparisonParentId: seriesItem.comparisonParentId || "", - relatedEntityId: seriesItem.relatedEntityId || "", - label: seriesItem.label || seriesItem.entityId || "", - baseLabel: seriesItem.baseLabel || "", - windowLabel: seriesItem.windowLabel || "", - value: hasNumericValue ? value : value ?? null, - unit: seriesItem.unit || "", - color: seriesItem.color, - opacity: Number.isFinite(seriesItem.hoverOpacity) ? seriesItem.hoverOpacity : 1, - hasValue: hasNumericValue || value != null, - x: includePosition ? entryOpts.x : void 0, - y: includePosition ? renderer.yOf(value, axis.min, axis.max) : void 0, - axisSide: axis.side === "right" ? "right" : "left", - axisSlot: Number.isFinite(axis.slot) ? axis.slot : 0, - rawVisible: seriesItem.rawVisible !== false, - comparisonDerived: seriesItem.comparisonDerived === true, - showCrosshair: seriesItem.showCrosshair === true, - ...extra - }; - }; - const findAnomalyRegions = (clientX, clientY) => { - const rect = canvas.getBoundingClientRect(); - if (!rect.width || !rect.height) { - return []; - } - const localX = clientX - rect.left; - const localY = clientY - rect.top; - const hits = []; - for (const region of anomalyRegions) { - const radiusX = Number(region?.radiusX) || 0; - const radiusY = Number(region?.radiusY) || 0; - if (radiusX <= 0 || radiusY <= 0) { - continue; - } - const dx = (localX - region.centerX) / radiusX; - const dy = (localY - region.centerY) / radiusY; - if (dx * dx + dy * dy <= 1) { - hits.push(region); - } - } - return hits; - }; - const buildHoverState = (clientX, clientY) => { - const rect = canvas.getBoundingClientRect(); - if (!rect.width || !rect.height || !renderer.cw || !renderer.ch) { - return null; - } - const localX = clampChartValue( - clientX - rect.left, - renderer.pad.left, - renderer.pad.left + renderer.cw - ); - const localY = clampChartValue( - clientY - rect.top, - renderer.pad.top, - renderer.pad.top + renderer.ch - ); - const ratio = renderer.cw ? (localX - renderer.pad.left) / renderer.cw : 0; - const rawTimeMs = t0 + ratio * (t1 - t0); - const timeMs = resolveLineChartHoverTime( - resolvedSeries, - rawTimeMs, - options.hoverSnapMode || "follow_series" - ); - const x2 = renderer.xOf(timeMs, t0, t1); - const values = resolvedSeries.map((seriesItem) => { - const value = renderer._interpolateValue(seriesItem.pts || [], timeMs); - const axis = resolveHoverAxis(seriesItem); - return buildHoverValueEntry( - seriesItem, - value, - axis, - {}, - { - includePosition: value != null, - x: x2 - } - ); - }); - const comparisonValues = comparisonSeries.map((seriesItem) => { - const value = renderer._interpolateValue(seriesItem.pts || [], timeMs); - const axis = resolveHoverAxis(seriesItem); - return buildHoverValueEntry( - seriesItem, - value, - axis, - { comparison: true }, - { includePosition: value != null, x: x2 } - ); - }); - const trendValues = trendSeries.map((seriesItem) => { - const value = renderer._interpolateValue(seriesItem.pts || [], timeMs); - const axis = resolveHoverAxis(seriesItem); - return buildHoverValueEntry( - seriesItem, - value, - axis, - { trend: true }, - { includePosition: value != null, x: x2 } - ); - }); - const rateValues = rateSeries.map((seriesItem) => { - const value = renderer._interpolateValue(seriesItem.pts || [], timeMs); - const axis = resolveHoverAxis(seriesItem); - return buildHoverValueEntry( - seriesItem, - value, - axis, - { rate: true }, - { includePosition: value != null, x: x2 } - ); - }); - const deltaValues = deltaSeries.map((seriesItem) => { - const value = renderer._interpolateValue(seriesItem.pts || [], timeMs); - const axis = resolveHoverAxis(seriesItem); - return buildHoverValueEntry( - seriesItem, - value, - axis, - { delta: true }, - { includePosition: value != null, x: x2 } - ); - }); - const summaryValues = summarySeries.map((seriesItem) => { - const axis = resolveHoverAxis(seriesItem); - const value = Number(seriesItem.value); - return buildHoverValueEntry(seriesItem, value, axis, { - summary: true, - summaryType: seriesItem.summaryType || "" - }); - }); - const thresholdValues = thresholdSeries.map((seriesItem) => { - const axis = resolveHoverAxis(seriesItem); - const value = Number(seriesItem.value); - return buildHoverValueEntry(seriesItem, value, axis, { - threshold: true - }); - }); - const plottedValues = [ - ...values.filter((entry) => entry?.hasValue !== false), - ...comparisonValues.filter((entry) => entry?.hasValue !== false), - ...rateValues.filter((entry) => entry?.hasValue !== false), - ...options.showTrendCrosshairs === true ? trendValues.filter( - (entry) => entry?.hasValue !== false && entry.showCrosshair === true - ) : [] - ]; - let rangeStartMs = timeMs; - let rangeEndMs = timeMs; - let primary = plottedValues[0] || null; - if (primary) { - for (const entry of plottedValues) { - if (Number.isFinite(entry.y) && Number.isFinite(primary.y) && Math.abs(entry.y - localY) < Math.abs(primary.y - localY)) { - primary = entry; - } - } - } - const activePrimarySeries = primary ? resolvedSeries.find( - (seriesItem) => seriesItem.entityId === primary.entityId - ) || null : null; - if (activePrimarySeries?.pts?.length) { - const pts = activePrimarySeries.pts; - const pLen = pts.length; - let lo = 0; - let hi = pLen - 1; - let previousIndex = -1; - if (pts[0][0] <= timeMs) { - while (lo + 1 < hi) { - const mid = Math.floor((lo + hi) / 2); - if (pts[mid][0] <= timeMs) { - lo = mid; - } else { - hi = mid; - } - } - previousIndex = pts[hi][0] <= timeMs ? hi : lo; - } - const nextIndex = previousIndex < pLen - 1 ? previousIndex + 1 : -1; - const previous = previousIndex >= 0 ? pts[previousIndex] : null; - let next = null; - if (nextIndex >= 0) { - next = pts[nextIndex]; - } else if (previousIndex < 0) { - next = pts[0]; - } - if (previous && next) { - const prevPrev = pts[Math.max(0, previousIndex - 1)] || previous; - const nextNext = pts[Math.min(pLen - 1, nextIndex + 1)] || next; - rangeStartMs = previous === next ? previous[0] : Math.round((previous[0] + prevPrev[0]) / 2); - rangeEndMs = previous === next ? next[0] : Math.round((next[0] + nextNext[0]) / 2); - } else if (previous) { - rangeStartMs = previous[0]; - rangeEndMs = previous[0]; - } else if (next) { - rangeStartMs = next[0]; - rangeEndMs = next[0]; - } - } - const binaryValues = binaryStates.map((entry) => { - const activeSpan = (entry.spans || []).find( - (span) => timeMs >= span.start && timeMs <= span.end - ); - return { - entityId: entry.entityId || "", - label: entry.label || entry.entityId || "", - value: activeSpan ? entry.onLabel || "on" : entry.offLabel || "off", - unit: "", - color: entry.color, - hasValue: true, - active: !!activeSpan - }; - }).filter((entry) => Boolean(entry.label)); - if (!values.length && !binaryValues.length && !trendValues.length && !rateValues.length && !deltaValues.length && !summaryValues.length && !thresholdValues.length && !comparisonValues.length) { - return null; - } - const fallbackY = renderer.pad.top + 12; - const hoverY = primary ? primary.y : fallbackY; - const hoveredEvents = []; - for (const event of events || []) { - const eventTime = new Date(event.timestamp).getTime(); - if (eventTime < t0 || eventTime > t1) { - continue; - } - const distance = Math.abs(eventTime - timeMs); - if (distance <= eventThresholdMs) { - hoveredEvents.push({ - ...event, - _hoverDistanceMs: distance - }); - } - } - hoveredEvents.sort((left, right) => { - const distanceDelta = (left._hoverDistanceMs || 0) - (right._hoverDistanceMs || 0); - if (distanceDelta !== 0) { - return distanceDelta; - } - return new Date(left.timestamp).getTime() - new Date(right.timestamp).getTime(); - }); - const normalizedHoveredEvents = hoveredEvents.map( - (event) => { - const { _hoverDistanceMs: _2, ...normalizedEvent } = event; - return normalizedEvent; - } - ); - return { - x: x2, - y: hoverY, - timeMs, - rangeStartMs, - rangeEndMs, - values, - trendValues, - rateValues, - deltaValues: options.showDeltaTooltip === true ? deltaValues : [], - summaryValues, - thresholdValues, - comparisonValues, - binaryValues, - primary, - event: normalizedHoveredEvents[0] || null, - events: normalizedHoveredEvents, - emphasizeGuides: options.emphasizeHoverGuides === true, - showTrendCrosshairs: options.showTrendCrosshairs === true, - showRateCrosshairs: options.showRateCrosshairs === true, - hideRawData: options.hideRawData === true - }; - }; - const showFromPointer = (clientX, clientY) => { - if (interactionState._chartZoomDragging) { - return; - } - const anomalyRegionsHit = findAnomalyRegions(clientX, clientY); - const hover = buildHoverState(clientX, clientY); - if (!hover) { - interactionState._chartLastHover = null; - hideLineChartHover(card); - canvas.style.cursor = "default"; - return; - } - hover.anomalyRegions = anomalyRegionsHit; - interactionState._chartLastHover = hover; - showLineChartCrosshair(card, renderer, hover); - if (options.showTooltip !== false || Array.isArray(hover.events) && hover.events.length > 0) { - showLineChartTooltip(card, hover, clientX, clientY); - } else { - hideTooltip(card); - } - dispatchLineChartHover(card, hover); - canvas.style.cursor = anomalyRegionsHit.length > 0 ? "pointer" : "crosshair"; - }; - const hideHover = () => { - interactionState._chartLastHover = null; - hideLineChartHover(card); - canvas.style.cursor = "default"; - }; - let _rafHandle = null; - let _pendingX = 0; - let _pendingY = 0; - const onMouseMove = (ev) => { - _pendingX = ev.clientX; - _pendingY = ev.clientY; - if (_rafHandle !== null) { - return; - } - _rafHandle = requestAnimationFrame(() => { - _rafHandle = null; - showFromPointer(_pendingX, _pendingY); - }); - }; - const onMouseLeave = (ev) => { - const nextTarget = ev.relatedTarget; - if (nextTarget instanceof Node && hoverSurfaceEl && hoverSurfaceEl.contains(nextTarget)) { - return; - } - if (nextTarget instanceof Node && addAnnotationButton && addAnnotationButton.contains(nextTarget)) { - return; - } - hideHover(); - }; - const onOverlayMove = (ev) => { - showFromPointer(ev.clientX, ev.clientY); - }; - const onOverlayLeave = (ev) => { - const nextTarget = ev.relatedTarget; - if (nextTarget instanceof Node && canvas.contains(nextTarget)) { - return; - } - if (nextTarget instanceof Node && addAnnotationButton && addAnnotationButton.contains(nextTarget)) { - return; - } - hideHover(); - }; - const onAddButtonLeave = (ev) => { - const nextTarget = ev.relatedTarget; - if (nextTarget instanceof Node && (canvas.contains(nextTarget) || hoverSurfaceEl && hoverSurfaceEl.contains(nextTarget))) { - return; - } - hideHover(); - }; - const onAddButtonClick = (ev) => { - if (typeof options.onAddAnnotation !== "function" || !interactionState._chartLastHover) { - return; - } - ev.preventDefault(); - ev.stopPropagation(); - options.onAddAnnotation(interactionState._chartLastHover, ev); - }; - const onContextMenu = (ev) => { - if (typeof options.onContextMenu !== "function") { - return; - } - const hover = buildHoverState(ev.clientX, ev.clientY); - if (!hover) { - return; - } - ev.preventDefault(); - interactionState._chartLastHover = hover; - showLineChartCrosshair(card, renderer, hover); - showLineChartTooltip(card, hover, ev.clientX, ev.clientY); - dispatchLineChartHover(card, hover); - options.onContextMenu(hover, ev); - }; - const onClick = (ev) => { - if (typeof options.onAnomalyClick !== "function") { - return; - } - const regions = findAnomalyRegions(ev.clientX, ev.clientY); - if (!regions.length) { - return; - } - ev.preventDefault(); - ev.stopPropagation(); - options.onAnomalyClick(regions, ev); - }; - let touchTimer = null; - const scheduleTouchHide = () => { - if (touchTimer) { - window.clearTimeout(touchTimer); - } - touchTimer = window.setTimeout(() => hideHover(), 1800); - }; - const onTouchStart = (ev) => { - ev.preventDefault(); - const touch = ev.touches[0]; - if (!touch) { - return; - } - showFromPointer(touch.clientX, touch.clientY); - scheduleTouchHide(); - }; - const onTouchMove = (ev) => { - ev.preventDefault(); - const touch = ev.touches[0]; - if (!touch) { - return; - } - showFromPointer(touch.clientX, touch.clientY); - scheduleTouchHide(); - }; - const onTouchEnd = () => scheduleTouchHide(); - canvas.addEventListener("mousemove", onMouseMove); - canvas.addEventListener("mouseleave", onMouseLeave); - canvas.addEventListener("click", onClick); - canvas.addEventListener("contextmenu", onContextMenu); - canvas.addEventListener("touchstart", onTouchStart, { passive: false }); - canvas.addEventListener("touchmove", onTouchMove, { passive: false }); - canvas.addEventListener("touchend", onTouchEnd); - canvas.addEventListener("touchcancel", onTouchEnd); - hoverSurfaceEl?.addEventListener("mousemove", onOverlayMove); - hoverSurfaceEl?.addEventListener("mouseleave", onOverlayLeave); - addAnnotationButton?.addEventListener("mouseleave", onAddButtonLeave); - addAnnotationButton?.addEventListener("click", onAddButtonClick); - interactionState._chartHoverCleanup = () => { - canvas.removeEventListener("mousemove", onMouseMove); - canvas.removeEventListener("mouseleave", onMouseLeave); - canvas.removeEventListener("click", onClick); - canvas.removeEventListener("contextmenu", onContextMenu); - canvas.removeEventListener("touchstart", onTouchStart); - canvas.removeEventListener("touchmove", onTouchMove); - canvas.removeEventListener("touchend", onTouchEnd); - canvas.removeEventListener("touchcancel", onTouchEnd); - hoverSurfaceEl?.removeEventListener("mousemove", onOverlayMove); - hoverSurfaceEl?.removeEventListener("mouseleave", onOverlayLeave); - addAnnotationButton?.removeEventListener("mouseleave", onAddButtonLeave); - addAnnotationButton?.removeEventListener("click", onAddButtonClick); - if (_rafHandle !== null) { - cancelAnimationFrame(_rafHandle); - _rafHandle = null; - } - if (touchTimer) { - window.clearTimeout(touchTimer); - touchTimer = null; - } - hideHover(); - }; - } - function attachLineChartRangeZoom(card, canvas, renderer, t0, t1, options = {}) { - const interactionState = getInteractionState(card); - if (!canvas || !renderer) { - return; - } - if (interactionState._chartZoomCleanup) { - interactionState._chartZoomCleanup(); - interactionState._chartZoomCleanup = null; - } - const selection = getRoot(card).getElementById( - "chart-zoom-selection" - ); - if (!selection) { - return; - } - let pointerId = null; - let startX = 0; - let currentX = 0; - let dragging = false; - const hideSelection = () => { - selection.hidden = true; - selection.classList.remove("visible"); - }; - const clientXToTime = (clientX) => { - const rect = canvas.getBoundingClientRect(); - const localX = clampChartValue( - clientX - rect.left, - renderer.pad.left, - renderer.pad.left + renderer.cw - ); - const ratio = renderer.cw ? (localX - renderer.pad.left) / renderer.cw : 0; - return t0 + ratio * (t1 - t0); - }; - const inPlotBounds = (clientX, clientY) => { - const rect = canvas.getBoundingClientRect(); - const localX = clientX - rect.left; - const localY = clientY - rect.top; - return localX >= renderer.pad.left && localX <= renderer.pad.left + renderer.cw && localY >= renderer.pad.top && localY <= renderer.pad.top + renderer.ch; - }; - const renderSelection = () => { - const left = Math.min(startX, currentX); - const width = Math.abs(currentX - startX); - selection.style.left = `${left}px`; - selection.style.top = `${renderer.pad.top}px`; - selection.style.width = `${width}px`; - selection.style.height = `${renderer.ch}px`; - selection.hidden = false; - selection.classList.add("visible"); - }; - const emitPreview = () => { - if (!dragging || Math.abs(currentX - startX) < 8) { - options.onPreview?.(null); - return; - } - const rectLeft = canvas.getBoundingClientRect().left; - const startTime = Math.min( - clientXToTime(rectLeft + startX), - clientXToTime(rectLeft + currentX) - ); - const endTime = Math.max( - clientXToTime(rectLeft + startX), - clientXToTime(rectLeft + currentX) - ); - options.onPreview?.({ startTime, endTime }); - }; - const resetDragging = (clearPreview = true) => { - pointerId = null; - dragging = false; - interactionState._chartZoomDragging = false; - hideSelection(); - if (clearPreview) { - options.onPreview?.(null); - } - }; - const onPointerMove = (ev) => { - if (pointerId == null || ev.pointerId !== pointerId) { - return; - } - currentX = clampChartValue( - ev.clientX - canvas.getBoundingClientRect().left, - renderer.pad.left, - renderer.pad.left + renderer.cw - ); - const movedPx = Math.abs(currentX - startX); - if (!dragging && movedPx < 6) { - return; - } - dragging = true; - interactionState._chartZoomDragging = true; - hideLineChartHover(card); - renderSelection(); - emitPreview(); - ev.preventDefault(); - }; - const finish = (ev) => { - if (pointerId == null || ev.pointerId !== pointerId) { - return; - } - const didDrag = dragging; - const endX = currentX; - window.removeEventListener("pointermove", onPointerMove); - window.removeEventListener("pointerup", finish); - window.removeEventListener("pointercancel", finish); - if (!didDrag || Math.abs(endX - startX) < 8) { - resetDragging(true); - return; - } - const rectLeft = canvas.getBoundingClientRect().left; - const startTime = Math.min( - clientXToTime(rectLeft + startX), - clientXToTime(rectLeft + endX) - ); - const endTime = Math.max( - clientXToTime(rectLeft + startX), - clientXToTime(rectLeft + endX) - ); - options.onZoom?.({ startTime, endTime }); - resetDragging(false); - }; - const onPointerDown = (ev) => { - if (ev.button !== 0 || !inPlotBounds(ev.clientX, ev.clientY)) { - return; - } - pointerId = ev.pointerId; - const rect = canvas.getBoundingClientRect(); - startX = clampChartValue( - ev.clientX - rect.left, - renderer.pad.left, - renderer.pad.left + renderer.cw - ); - currentX = startX; - dragging = false; - interactionState._chartZoomDragging = false; - options.onPreview?.(null); - window.addEventListener("pointermove", onPointerMove); - window.addEventListener("pointerup", finish); - window.addEventListener("pointercancel", finish); - }; - const onDoubleClick = (ev) => { - if (!inPlotBounds(ev.clientX, ev.clientY)) { - return; - } - if (!options.onReset) { - return; - } - ev.preventDefault(); - options.onReset(); - }; - canvas.addEventListener("pointerdown", onPointerDown); - canvas.addEventListener("dblclick", onDoubleClick); - interactionState._chartZoomCleanup = () => { - canvas.removeEventListener("pointerdown", onPointerDown); - canvas.removeEventListener("dblclick", onDoubleClick); - window.removeEventListener("pointermove", onPointerMove); - window.removeEventListener("pointerup", finish); - window.removeEventListener("pointercancel", finish); - resetDragging(); - }; - } - const DATA_RANGE_CACHE_TTL_MS = 10 * 60 * 1e3; - const DATA_RANGE_CACHE_LIVE_EDGE_MS = 5 * 60 * 1e3; - const dataRangeCache = /* @__PURE__ */ new Map(); - function normalizeCacheIdList(values) { - return [ - ...new Set( - (Array.isArray(values) ? values : []).filter(Boolean) - ) - ].sort(); + canvas { display: block; } + .chart-loading { + position: absolute; + top: 50%; + left: 50%; + display: none; + align-items: center; + justify-content: center; + gap: var(--dp-spacing-sm); + min-width: calc(var(--spacing, 8px) * 12); + min-height: calc(var(--spacing, 8px) * 5); + padding: var(--dp-spacing-sm) var(--dp-spacing-md); + border-radius: 999px; + background: color-mix(in srgb, var(--card-background-color, #fff) 92%, transparent); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12); + z-index: 6; + pointer-events: none; + transform: translate(-50%, -50%); } - function shouldUseStableRangeCache(endTime) { - const endMs = new Date(endTime || 0).getTime(); - if (!Number.isFinite(endMs)) { - return false; - } - return endMs < Date.now() - DATA_RANGE_CACHE_LIVE_EDGE_MS; + .chart-loading.active { + display: inline-flex; } - function getCachedRangePromise(key) { - const entry = dataRangeCache.get(key); - if (!entry) { - return null; - } - if (entry.expiresAt <= Date.now()) { - dataRangeCache.delete(key); - return null; - } - return entry.promise; - } - function setCachedRangePromise(key, promise) { - dataRangeCache.set(key, { - promise, - expiresAt: Date.now() + DATA_RANGE_CACHE_TTL_MS - }); - return promise; - } - function withStableRangeCache(key, endTime, loader) { - if (!shouldUseStableRangeCache(endTime)) { - return Promise.resolve().then(loader); - } - const cached = getCachedRangePromise(key); - if (cached) { - return cached; - } - const promise = Promise.resolve().then(loader).catch((err) => { - dataRangeCache.delete(key); - throw err; - }); - return setCachedRangePromise(key, promise); - } - function clearStableRangeCacheMatching(predicate) { - if (typeof predicate !== "function") { - return 0; - } - let deletedCount = 0; - [...dataRangeCache.keys()].forEach((key) => { - if (predicate(key) === true) { - dataRangeCache.delete(key); - deletedCount += 1; - } - }); - return deletedCount; - } - const MAX_DOWNSAMPLED_HISTORY_RANGE_MS = 90 * 24 * 60 * 60 * 1e3; - function parseIsoTimeMs(value) { - const timeMs = Date.parse(value); - if (!Number.isFinite(timeMs)) { - return null; - } - return timeMs; + .chart-loading-spinner { + width: calc(var(--spacing, 8px) * 2); + height: calc(var(--spacing, 8px) * 2); + border-radius: 50%; + border: 2px solid color-mix(in srgb, var(--primary-color, #03a9f4) 22%, transparent); + border-top-color: var(--primary-color, #03a9f4); + animation: chart-spinner 0.9s linear infinite; } - function buildDownsampledHistoryChunks(startTime, endTime) { - const startTimeMs = parseIsoTimeMs(startTime); - const endTimeMs = parseIsoTimeMs(endTime); - if (startTimeMs == null || endTimeMs == null || endTimeMs <= startTimeMs || endTimeMs - startTimeMs <= MAX_DOWNSAMPLED_HISTORY_RANGE_MS) { - return [{ startTime, endTime }]; - } - const chunks = []; - let chunkStartMs = startTimeMs; - while (chunkStartMs < endTimeMs) { - const chunkEndMs = Math.min( - endTimeMs, - chunkStartMs + MAX_DOWNSAMPLED_HISTORY_RANGE_MS - ); - chunks.push({ - startTime: new Date(chunkStartMs).toISOString(), - endTime: new Date(chunkEndMs).toISOString() - }); - if (chunkEndMs >= endTimeMs) { - break; - } - chunkStartMs = chunkEndMs + 1; - } - return chunks; - } - function fetchDownsampledHistory(hass, entityId, startTime, endTime, interval, aggregate) { - const cacheKey = JSON.stringify({ - type: "hass_datapoints/history", - entity_id: entityId, - start_time: startTime, - end_time: endTime, - interval, - aggregate - }); - return withStableRangeCache(cacheKey, endTime, async () => { - const chunks = buildDownsampledHistoryChunks(startTime, endTime); - const responses = await Promise.all( - chunks.map( - async (chunk) => hass.connection.sendMessagePromise({ - type: "hass_datapoints/history", - entity_id: entityId, - start_time: chunk.startTime, - end_time: chunk.endTime, - interval, - aggregate - }) - ) - ); - const mergedPoints = responses.flatMap( - (result) => result.pts || [] - ); - if (!mergedPoints.length) { - return []; - } - const dedupedPoints = /* @__PURE__ */ new Map(); - for (const point of mergedPoints) { - if (Array.isArray(point) && point.length > 0) { - dedupedPoints.set(String(point[0]), point); - } else { - dedupedPoints.set(JSON.stringify(point), point); - } - } - return [...dedupedPoints.values()]; - }); - } - function fetchAnomaliesFromBackend(hass, entityId, startTime, endTime, config) { - return hass.connection.sendMessagePromise({ - type: "hass_datapoints/anomalies", - entity_id: entityId, - start_time: startTime, - end_time: endTime, - anomaly_methods: config.anomaly_methods || [], - anomaly_sensitivity: config.anomaly_sensitivity || "medium", - anomaly_overlap_mode: config.anomaly_overlap_mode || "all", - anomaly_rate_window: config.anomaly_rate_window || "1h", - anomaly_zscore_window: config.anomaly_zscore_window || "24h", - anomaly_persistence_window: config.anomaly_persistence_window || "1h", - trend_method: config.trend_method || "rolling_average", - trend_window: config.trend_window || "24h", - ...config.anomaly_use_sampled_data !== false && config.sample_interval && config.sample_interval !== "raw" ? { - sample_interval: config.sample_interval, - sample_aggregate: config.sample_aggregate || "mean" - } : {}, - ...config.comparison_entity_id ? { - comparison_entity_id: config.comparison_entity_id, - comparison_start_time: config.comparison_start_time, - comparison_end_time: config.comparison_end_time, - comparison_time_offset_ms: config.comparison_time_offset_ms || 0 - } : {} - }).then( - (result) => result.anomaly_clusters || [] - ); + .chart-loading::after { + content: none; } - async function fetchHistoryDuringPeriod(hass, startTime, endTime, entityIds, options = {}) { - const normalizedEntityIds = normalizeCacheIdList(entityIds); - const cacheKey = JSON.stringify({ - type: "history/history_during_period", - start_time: startTime, - end_time: endTime, - entity_ids: normalizedEntityIds, - include_start_time_state: options.include_start_time_state !== false, - significant_changes_only: !!options.significant_changes_only, - no_attributes: options.no_attributes !== false - }); - return withStableRangeCache( - cacheKey, - endTime, - () => hass.connection.sendMessagePromise({ - type: "history/history_during_period", - start_time: startTime, - end_time: endTime, - entity_ids: normalizedEntityIds, - include_start_time_state: options.include_start_time_state !== false, - significant_changes_only: !!options.significant_changes_only, - no_attributes: options.no_attributes !== false - }) - ); + .chart-loading-label { + color: var(--secondary-text-color); + font-size: 0.85rem; + font-weight: 500; } - const VALID_ANOMALY_METHODS = [ - "trend_residual", - "rate_of_change", - "iqr", - "rolling_zscore", - "persistence", - "comparison_window" - ]; - const VALID_SAMPLE_INTERVALS = [ - "raw", - "1s", - "5s", - "10s", - "15s", - "30s", - "1m", - "2m", - "5m", - "10m", - "15m", - "30m", - "1h", - "2h", - "3h", - "4h", - "6h", - "12h", - "24h" - ]; - const VALID_SAMPLE_AGGREGATES = [ - "mean", - "min", - "max", - "median", - "first", - "last" - ]; - function normalizeHistorySeriesAnalysis(analysis) { - const source = analysis && typeof analysis === "object" ? analysis : {}; - return { - expanded: source.expanded === true, - show_trend_lines: source.show_trend_lines === true, - trend_method: source.trend_method === "linear_trend" ? "linear_trend" : "rolling_average", - trend_window: typeof source.trend_window === "string" && source.trend_window ? source.trend_window : "24h", - show_trend_crosshairs: source.show_trend_crosshairs !== false, - show_summary_stats: source.show_summary_stats === true, - show_summary_stats_shading: source.show_summary_stats_shading === true, - show_rate_of_change: source.show_rate_of_change === true, - show_rate_crosshairs: source.show_rate_crosshairs !== false, - rate_window: typeof source.rate_window === "string" && source.rate_window ? source.rate_window : "1h", - show_threshold_analysis: source.show_threshold_analysis === true, - show_threshold_shading: source.show_threshold_shading === true, - threshold_value: typeof source.threshold_value === "string" || typeof source.threshold_value === "number" ? String(source.threshold_value).trim() : "", - threshold_direction: source.threshold_direction === "below" ? "below" : "above", - show_anomalies: source.show_anomalies === true, - anomaly_methods: (() => { - if (Array.isArray(source.anomaly_methods)) { - return source.anomaly_methods.filter( - (method) => typeof method === "string" && VALID_ANOMALY_METHODS.includes(method) - ); - } - const legacy = typeof source.anomaly_method === "string" && VALID_ANOMALY_METHODS.includes(source.anomaly_method) ? source.anomaly_method : null; - return legacy ? [legacy] : []; - })(), - anomaly_overlap_mode: source.anomaly_overlap_mode === "only" ? "only" : "all", - anomaly_sensitivity: typeof source.anomaly_sensitivity === "string" && source.anomaly_sensitivity ? source.anomaly_sensitivity : "medium", - anomaly_rate_window: typeof source.anomaly_rate_window === "string" && source.anomaly_rate_window ? source.anomaly_rate_window : "1h", - anomaly_zscore_window: typeof source.anomaly_zscore_window === "string" && source.anomaly_zscore_window ? source.anomaly_zscore_window : "24h", - anomaly_persistence_window: typeof source.anomaly_persistence_window === "string" && source.anomaly_persistence_window ? source.anomaly_persistence_window : "1h", - anomaly_comparison_window_id: typeof source.anomaly_comparison_window_id === "string" && source.anomaly_comparison_window_id ? source.anomaly_comparison_window_id : null, - show_delta_analysis: source.show_delta_analysis === true, - show_delta_tooltip: source.show_delta_tooltip !== false, - show_delta_lines: source.show_delta_lines === true, - hide_source_series: source.hide_source_series === true, - sample_interval: typeof source.sample_interval === "string" && VALID_SAMPLE_INTERVALS.includes(source.sample_interval) ? source.sample_interval : "1m", - sample_aggregate: typeof source.sample_aggregate === "string" && VALID_SAMPLE_AGGREGATES.includes(source.sample_aggregate) ? source.sample_aggregate : "mean", - stepped_series: source.stepped_series === true, - anomaly_use_sampled_data: source.anomaly_use_sampled_data !== false - }; - } - function normalizeHistorySeriesRows(rows) { - if (!Array.isArray(rows)) { - return []; - } - const seen = /* @__PURE__ */ new Set(); - const normalized = []; - rows.forEach((row, index) => { - const entityId = typeof row?.entity_id === "string" ? row.entity_id.trim() : ""; - if (!entityId || seen.has(entityId)) { - return; - } - seen.add(entityId); - normalized.push({ - entity_id: entityId, - color: typeof row?.color === "string" && /^#[0-9a-f]{6}$/i.test(row.color) ? row.color : COLORS[index % COLORS.length], - visible: row?.visible !== false, - analysis: normalizeHistorySeriesAnalysis(row?.analysis) - }); - }); - return normalized; - } - function buildHistorySeriesRows(entityIds, previousRows = []) { - const normalizedPrevious = normalizeHistorySeriesRows(previousRows); - const previousMap = new Map( - normalizedPrevious.map((row) => [row.entity_id, row]) - ); - const intervals = normalizedPrevious.map( - (row) => row.analysis.sample_interval - ); - const allSame = intervals.length > 0 && intervals.every((interval) => interval === intervals[0]); - const inheritedSampleSettings = allSame ? { - sample_interval: intervals[0], - sample_aggregate: normalizedPrevious[0].analysis.sample_aggregate - } : null; - return normalizeEntityIds(entityIds).map((entityId, index) => { - const existing = previousMap.get(entityId); - if (existing) { - return existing; - } - return { - entity_id: entityId, - color: COLORS[index % COLORS.length], - visible: true, - analysis: normalizeHistorySeriesAnalysis(inheritedSampleSettings) - }; - }); - } - function slugifySeriesName(value) { - return String(value || "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""); - } - function parseSeriesColorsParam(value) { - if (!value || typeof value !== "string") { - return {}; - } - return value.split(",").reduce((acc, entry) => { - const [rawKey, rawColor] = entry.split(":"); - const key = decodeURIComponent(rawKey || "").trim(); - const color = String(rawColor || "").trim(); - if (!key || !/^#[0-9a-f]{6}$/i.test(color)) { - return acc; - } - acc[key] = color; - return acc; - }, {}); - } - const jsContent$1 = '(function() {\n "use strict";\n const HOUR_MS = 60 * 60 * 1e3;\n function getTrendWindowMs(value) {\n const windows = {\n "1h": 60 * 60 * 1e3,\n "6h": 6 * 60 * 60 * 1e3,\n "24h": 24 * 60 * 60 * 1e3,\n "7d": 7 * 24 * 60 * 60 * 1e3,\n "14d": 14 * 24 * 60 * 60 * 1e3,\n "21d": 21 * 24 * 60 * 60 * 1e3,\n "28d": 28 * 24 * 60 * 60 * 1e3\n };\n return windows[value] || windows["24h"];\n }\n function buildRollingAverageTrend(points, windowMs) {\n if (!Array.isArray(points) || points.length < 2 || !Number.isFinite(windowMs) || windowMs <= 0) {\n return [];\n }\n const trendPoints = [];\n let windowStartIndex = 0;\n let windowSum = 0;\n for (let index = 0; index < points.length; index += 1) {\n const [time, value] = points[index];\n windowSum += value;\n while (windowStartIndex < index && time - points[windowStartIndex][0] > windowMs) {\n windowSum -= points[windowStartIndex][1];\n windowStartIndex += 1;\n }\n const count = index - windowStartIndex + 1;\n if (count > 0) {\n trendPoints.push([time, windowSum / count]);\n }\n }\n return trendPoints;\n }\n function buildLinearTrend(points) {\n if (!Array.isArray(points) || points.length < 2) {\n return [];\n }\n const origin = points[0][0];\n let sumX = 0;\n let sumY = 0;\n let sumXX = 0;\n let sumXY = 0;\n for (const [time, value] of points) {\n const x = (time - origin) / HOUR_MS;\n sumX += x;\n sumY += value;\n sumXX += x * x;\n sumXY += x * value;\n }\n const count = points.length;\n const denominator = count * sumXX - sumX * sumX;\n if (!Number.isFinite(denominator) || Math.abs(denominator) < 1e-9) {\n return [];\n }\n const slope = (count * sumXY - sumX * sumY) / denominator;\n const intercept = (sumY - slope * sumX) / count;\n const firstTime = points[0][0];\n const lastTime = points[points.length - 1][0];\n const firstX = (firstTime - origin) / HOUR_MS;\n const lastX = (lastTime - origin) / HOUR_MS;\n return [\n [firstTime, intercept + slope * firstX],\n [lastTime, intercept + slope * lastX]\n ];\n }\n function buildTrendPoints(points, method, trendWindow) {\n if (!Array.isArray(points) || points.length < 2) {\n return [];\n }\n if (method === "linear_trend") {\n return buildLinearTrend(points);\n }\n return buildRollingAverageTrend(points, getTrendWindowMs(trendWindow));\n }\n function normalizeSeriesAnalysis(analysis) {\n const source = analysis && typeof analysis === "object" ? analysis : {};\n return {\n show_trend_lines: source.show_trend_lines === true,\n trend_method: source.trend_method === "linear_trend" ? "linear_trend" : "rolling_average",\n trend_window: typeof source.trend_window === "string" && source.trend_window ? source.trend_window : "24h",\n show_summary_stats: source.show_summary_stats === true,\n show_rate_of_change: source.show_rate_of_change === true,\n rate_window: typeof source.rate_window === "string" && source.rate_window ? source.rate_window : "1h",\n show_delta_analysis: source.show_delta_analysis === true\n };\n }\n function interpolateSeriesValue(points, timeMs) {\n if (!Array.isArray(points) || points.length === 0) {\n return null;\n }\n if (timeMs < points[0][0] || timeMs > points[points.length - 1][0]) {\n return null;\n }\n if (timeMs === points[0][0]) {\n return points[0][1];\n }\n if (timeMs === points[points.length - 1][0]) {\n return points[points.length - 1][1];\n }\n for (let index = 0; index < points.length - 1; index += 1) {\n const [startTime, startValue] = points[index];\n const [endTime, endValue] = points[index + 1];\n if (timeMs >= startTime && timeMs <= endTime) {\n const fraction = (timeMs - startTime) / (endTime - startTime);\n return startValue + (endValue - startValue) * fraction;\n }\n }\n return null;\n }\n function buildRateOfChangePoints(points, rateWindow) {\n if (!Array.isArray(points) || points.length < 2) {\n return [];\n }\n const ratePoints = [];\n for (let index = 1; index < points.length; index += 1) {\n const [timeMs, value] = points[index];\n let comparisonPoint = null;\n if (rateWindow === "point_to_point") {\n comparisonPoint = points[index - 1];\n } else {\n const windowMs = getTrendWindowMs(rateWindow);\n if (!Number.isFinite(windowMs) || windowMs <= 0) {\n continue;\n }\n for (let candidateIndex = index - 1; candidateIndex >= 0; candidateIndex -= 1) {\n const candidatePoint = points[candidateIndex];\n if (timeMs - candidatePoint[0] >= windowMs) {\n comparisonPoint = candidatePoint;\n break;\n }\n }\n if (!comparisonPoint) {\n comparisonPoint = points[0];\n }\n }\n if (!Array.isArray(comparisonPoint) || comparisonPoint.length < 2) {\n continue;\n }\n const deltaMs = timeMs - comparisonPoint[0];\n if (!Number.isFinite(deltaMs) || deltaMs <= 0) {\n continue;\n }\n const deltaHours = deltaMs / HOUR_MS;\n if (!Number.isFinite(deltaHours) || deltaHours <= 0) {\n continue;\n }\n const rateValue = (value - comparisonPoint[1]) / deltaHours;\n if (!Number.isFinite(rateValue)) {\n continue;\n }\n ratePoints.push([timeMs, rateValue]);\n }\n return ratePoints;\n }\n function buildDeltaPoints(sourcePoints, comparisonPoints) {\n if (!Array.isArray(sourcePoints) || sourcePoints.length < 2 || !Array.isArray(comparisonPoints) || comparisonPoints.length < 2) {\n return [];\n }\n const deltaPoints = [];\n for (const [timeMs, value] of sourcePoints) {\n const comparisonValue = interpolateSeriesValue(comparisonPoints, timeMs);\n if (comparisonValue == null) {\n continue;\n }\n deltaPoints.push([timeMs, value - comparisonValue]);\n }\n return deltaPoints;\n }\n function buildSummaryStats(points) {\n if (!Array.isArray(points) || points.length === 0) {\n return null;\n }\n let min = Infinity;\n let max = -Infinity;\n let sum = 0;\n let count = 0;\n for (const point of points) {\n const value = Number(point?.[1]);\n if (!Number.isFinite(value)) {\n continue;\n }\n if (value < min) {\n min = value;\n }\n if (value > max) {\n max = value;\n }\n sum += value;\n count += 1;\n }\n if (!Number.isFinite(min) || !Number.isFinite(max) || count === 0) {\n return null;\n }\n return {\n min,\n max,\n mean: sum / count\n };\n }\n function computeHistoryAnalysis(payload) {\n const series = (Array.isArray(payload?.series) ? payload.series : []).map(\n (seriesItem) => ({\n ...seriesItem,\n analysis: normalizeSeriesAnalysis(seriesItem?.analysis)\n })\n );\n const comparisonSeries = new Map(\n (Array.isArray(payload?.comparisonSeries) ? payload.comparisonSeries : []).filter((entry) => entry?.entityId).map((entry) => [entry.entityId, entry])\n );\n const result = {\n trendSeries: [],\n rateSeries: [],\n deltaSeries: [],\n summaryStats: [],\n anomalySeries: [],\n comparisonWindowResults: {}\n };\n for (const seriesItem of series) {\n const points = Array.isArray(seriesItem?.pts) ? seriesItem.pts : [];\n const analysis = normalizeSeriesAnalysis(seriesItem?.analysis);\n if (points.length < 2) {\n continue;\n }\n if (analysis.show_trend_lines === true) {\n const trendPoints = buildTrendPoints(\n points,\n analysis.trend_method,\n analysis.trend_window\n );\n if (trendPoints.length >= 2) {\n result.trendSeries.push({\n entityId: seriesItem.entityId,\n pts: trendPoints\n });\n }\n }\n if (analysis.show_rate_of_change === true) {\n const ratePoints = buildRateOfChangePoints(points, analysis.rate_window);\n if (ratePoints.length >= 2) {\n result.rateSeries.push({\n entityId: seriesItem.entityId,\n pts: ratePoints\n });\n }\n }\n if (analysis.show_summary_stats === true) {\n const summaryStats = buildSummaryStats(points);\n if (summaryStats) {\n result.summaryStats.push({\n entityId: seriesItem.entityId,\n ...summaryStats\n });\n }\n }\n if (analysis.show_delta_analysis === true && payload?.hasSelectedComparisonWindow === true) {\n const comparisonEntry = comparisonSeries.get(seriesItem.entityId);\n const comparisonPoints = comparisonEntry?.pts ?? [];\n if (comparisonPoints.length >= 2) {\n const deltaPoints = buildDeltaPoints(points, comparisonPoints);\n if (deltaPoints.length >= 2) {\n result.deltaSeries.push({\n entityId: seriesItem.entityId,\n pts: deltaPoints\n });\n }\n }\n }\n }\n const seriesAnalysisConfigs = typeof payload?.seriesAnalysisConfigs === "object" && payload.seriesAnalysisConfigs !== null ? payload.seriesAnalysisConfigs : {};\n const allComparisonWindowsData = typeof payload?.allComparisonWindowsData === "object" && payload.allComparisonWindowsData !== null ? payload.allComparisonWindowsData : {};\n for (const [windowId, entityPtsMap] of Object.entries(\n allComparisonWindowsData\n )) {\n result.comparisonWindowResults[windowId] = {};\n for (const [entityId, pts] of Object.entries(entityPtsMap)) {\n const winAnalysis = normalizeSeriesAnalysis(\n seriesAnalysisConfigs[entityId]\n );\n result.comparisonWindowResults[windowId][entityId] = {\n trendPts: winAnalysis.show_trend_lines && pts.length >= 2 ? buildTrendPoints(\n pts,\n winAnalysis.trend_method,\n winAnalysis.trend_window\n ) : [],\n ratePts: winAnalysis.show_rate_of_change && pts.length >= 2 ? buildRateOfChangePoints(pts, winAnalysis.rate_window) : [],\n summaryStats: winAnalysis.show_summary_stats ? buildSummaryStats(pts) : null\n };\n }\n }\n return result;\n }\n const workerScope = globalThis;\n workerScope.onmessage = (event) => {\n const { id, payload } = event.data || {};\n try {\n const result = computeHistoryAnalysis(payload);\n workerScope.postMessage({ id, result });\n } catch (error) {\n workerScope.postMessage({\n id,\n error: error instanceof Error ? error.message : String(error)\n });\n }\n };\n})();\n'; - const blob$1 = typeof self !== "undefined" && self.Blob && new Blob(["(self.URL || self.webkitURL).revokeObjectURL(self.location.href);", jsContent$1], { type: "text/javascript;charset=utf-8" }); - function WorkerWrapper$1(options) { - let objURL; - try { - objURL = blob$1 && (self.URL || self.webkitURL).createObjectURL(blob$1); - if (!objURL) throw ""; - const worker = new Worker(objURL, { - name: options?.name - }); - worker.addEventListener("error", () => { - (self.URL || self.webkitURL).revokeObjectURL(objURL); - }); - return worker; - } catch (e2) { - return new Worker( - "data:text/javascript;charset=utf-8," + encodeURIComponent(jsContent$1), - { - name: options?.name - } - ); + @keyframes chart-spinner { + to { + transform: rotate(360deg); } } - let workerInstance$1 = null; - let requestId$1 = 0; - const pending$1 = /* @__PURE__ */ new Map(); - function getHistoryAnalysisWorker() { - if (workerInstance$1) { - return workerInstance$1; - } - workerInstance$1 = new WorkerWrapper$1(); - workerInstance$1.addEventListener( - "message", - (event) => { - const { id, result, error } = event.data || {}; - const handlers = pending$1.get(id || -1); - if (!handlers) { - return; - } - pending$1.delete(id || -1); - if (error) { - handlers.reject(new Error(error)); - return; - } - handlers.resolve(result); - } - ); - workerInstance$1.addEventListener("error", (error) => { - pending$1.forEach((handlers) => { - handlers.reject(error); - }); - pending$1.clear(); - workerInstance$1 = null; - }); - return workerInstance$1; - } - function terminateHistoryAnalysisWorker() { - if (pending$1.size > 0) { - pending$1.forEach(({ reject }) => { - reject(new Error("Aborted: superseded by newer analysis")); - }); - pending$1.clear(); - } - if (workerInstance$1) { - workerInstance$1.terminate(); - workerInstance$1 = null; - } + .chart-message { + position: absolute; + inset: 0; + display: none; + align-items: center; + justify-content: center; + padding: calc(var(--spacing, 8px) * 5) var(--dp-spacing-lg); + text-align: center; + color: var(--secondary-text-color); + font-size: 0.95rem; + pointer-events: none; + z-index: 2; } - function computeHistoryAnalysisInWorker(payload) { - const worker = getHistoryAnalysisWorker(); - return new Promise((resolve, reject) => { - const id = ++requestId$1; - pending$1.set(id, { resolve, reject }); - worker.postMessage({ id, payload }); - }); - } - const jsContent = '(function() {\n "use strict";\n function downsamplePts(pts, intervalMs, aggregate) {\n if (!pts.length || intervalMs <= 0) {\n return pts;\n }\n const buckets = /* @__PURE__ */ new Map();\n const bucketRepTime = /* @__PURE__ */ new Map();\n for (const [time, value] of pts) {\n const idx = Math.floor(time / intervalMs);\n if (!buckets.has(idx)) {\n buckets.set(idx, []);\n bucketRepTime.set(idx, time);\n }\n buckets.get(idx)?.push(value);\n }\n const result = [];\n for (const idx of [...buckets.keys()].sort((a, b) => a - b)) {\n const values = buckets.get(idx) || [];\n const repTime = bucketRepTime.get(idx) || 0;\n let agg;\n if (aggregate === "min") {\n agg = Math.min(...values);\n } else if (aggregate === "max") {\n agg = Math.max(...values);\n } else if (aggregate === "median") {\n const sorted = [...values].sort((a, b) => a - b);\n const mid = Math.floor(sorted.length / 2);\n agg = sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;\n } else if (aggregate === "first") {\n agg = values[0];\n } else if (aggregate === "last") {\n agg = values[values.length - 1];\n } else {\n agg = values.reduce((sum, current) => sum + current, 0) / values.length;\n }\n result.push([repTime, agg]);\n }\n return result;\n }\n self.onmessage = ({ data }) => {\n const { id, type, payload } = data || {};\n try {\n let result;\n if (type === "downsample") {\n result = downsamplePts(\n payload.pts,\n payload.intervalMs,\n payload.aggregate\n );\n } else {\n throw new Error(`Unknown message type: ${type}`);\n }\n self.postMessage({ id, result });\n } catch (err) {\n self.postMessage({\n id,\n error: String(err)\n });\n }\n };\n})();\n'; - const blob = typeof self !== "undefined" && self.Blob && new Blob(["(self.URL || self.webkitURL).revokeObjectURL(self.location.href);", jsContent], { type: "text/javascript;charset=utf-8" }); - function WorkerWrapper(options) { - let objURL; - try { - objURL = blob && (self.URL || self.webkitURL).createObjectURL(blob); - if (!objURL) throw ""; - const worker = new Worker(objURL, { - name: options?.name - }); - worker.addEventListener("error", () => { - (self.URL || self.webkitURL).revokeObjectURL(objURL); - }); - return worker; - } catch (e2) { - return new Worker( - "data:text/javascript;charset=utf-8," + encodeURIComponent(jsContent), - { - name: options?.name - } - ); - } + .chart-message.visible { + display: flex; } - let workerInstance; - let requestId = 0; - const pending = /* @__PURE__ */ new Map(); - function getChartDataWorker() { - if (workerInstance !== void 0) { - return workerInstance; - } - try { - workerInstance = new WorkerWrapper(); - workerInstance.addEventListener( - "message", - (event) => { - const { id, result, error } = event.data || {}; - const handlers = pending.get(id || -1); - if (!handlers) { - return; - } - pending.delete(id || -1); - if (error) { - handlers.reject(new Error(error)); - } else { - handlers.resolve(result || []); - } - } - ); - workerInstance.addEventListener("error", (err) => { - pending.forEach(({ reject }) => { - reject(err); - }); - pending.clear(); - workerInstance = null; - }); - } catch { - workerInstance = null; - } - return workerInstance; + .chart-crosshair { + position: absolute; + inset: 0; + pointer-events: none; } - function downsampleInWorker(pts, intervalMs, aggregate) { - if (pts.length === 0) { - return Promise.resolve([]); - } - const worker = getChartDataWorker(); - if (!worker) { - return Promise.reject(new Error("Worker not available")); - } - return new Promise((resolve, reject) => { - const id = ++requestId; - pending.set(id, { resolve, reject }); - worker.postMessage({ - id, - type: "downsample", - payload: { pts, intervalMs, aggregate } - }); - }); - } - const HOUR_MS$1 = 60 * 60 * 1e3; - function getTrendWindowMs(value) { - const windows = { - "1h": HOUR_MS$1, - "6h": 6 * HOUR_MS$1, - "24h": 24 * HOUR_MS$1, - "7d": 7 * 24 * HOUR_MS$1, - "14d": 14 * 24 * HOUR_MS$1, - "21d": 21 * 24 * HOUR_MS$1, - "28d": 28 * 24 * HOUR_MS$1 - }; - return windows[value] ?? windows["24h"]; - } - function buildRollingAverageTrend(points, windowMs) { - if (!Array.isArray(points) || points.length < 2 || !Number.isFinite(windowMs) || windowMs <= 0) { - return []; - } - const trendPoints = []; - let windowStartIndex = 0; - let windowSum = 0; - for (let index = 0; index < points.length; index += 1) { - const [time, value] = points[index]; - windowSum += value; - while (windowStartIndex < index && time - points[windowStartIndex][0] > windowMs) { - windowSum -= points[windowStartIndex][1]; - windowStartIndex += 1; - } - const count = index - windowStartIndex + 1; - if (count > 0) { - trendPoints.push([time, windowSum / count]); - } - } - return trendPoints; + .chart-crosshair[hidden] { + display: none; } - function buildLinearTrend(points) { - if (!Array.isArray(points) || points.length < 2) { - return []; - } - const origin = points[0][0]; - let sumX = 0; - let sumY = 0; - let sumXX = 0; - let sumXY = 0; - for (const [time, value] of points) { - const x2 = (time - origin) / (60 * 60 * 1e3); - sumX += x2; - sumY += value; - sumXX += x2 * x2; - sumXY += x2 * value; - } - const count = points.length; - const denominator = count * sumXX - sumX * sumX; - if (!Number.isFinite(denominator) || Math.abs(denominator) < 1e-9) { - return []; - } - const slope = (count * sumXY - sumX * sumY) / denominator; - const intercept = (sumY - slope * sumX) / count; - const firstTime = points[0][0]; - const lastTime = points[points.length - 1][0]; - const firstX = (firstTime - origin) / (60 * 60 * 1e3); - const lastX = (lastTime - origin) / (60 * 60 * 1e3); - return [ - [firstTime, intercept + slope * firstX], - [lastTime, intercept + slope * lastX] - ]; - } - function interpolateSeriesValue(points, timeMs) { - if (!Array.isArray(points) || !points.length) { - return null; - } - if (timeMs < points[0][0] || timeMs > points[points.length - 1][0]) { - return null; - } - if (timeMs === points[0][0]) { - return points[0][1]; - } - if (timeMs === points[points.length - 1][0]) { - return points[points.length - 1][1]; - } - for (let index = 0; index < points.length - 1; index += 1) { - const [startTime, startValue] = points[index]; - const [endTime, endValue] = points[index + 1]; - if (timeMs >= startTime && timeMs <= endTime) { - const fraction = (timeMs - startTime) / (endTime - startTime); - return startValue + fraction * (endValue - startValue); - } - } - return null; + .crosshair-line { + position: absolute; + background: color-mix(in srgb, var(--primary-text-color, #111) 24%, transparent); } - function buildRateOfChangePoints(points, rateWindow = "1h") { - if (!Array.isArray(points) || points.length < 2) { - return []; - } - const ratePoints = []; - for (let index = 1; index < points.length; index += 1) { - const [timeMs, value] = points[index]; - let comparisonPoint = null; - if (rateWindow === "point_to_point") { - comparisonPoint = points[index - 1]; - } else { - const windowMs = getTrendWindowMs(rateWindow); - if (!Number.isFinite(windowMs) || windowMs <= 0) { - continue; - } - for (let candidateIndex = index - 1; candidateIndex >= 0; candidateIndex -= 1) { - const candidatePoint = points[candidateIndex]; - if (timeMs - candidatePoint[0] >= windowMs) { - comparisonPoint = candidatePoint; - break; - } - } - if (!comparisonPoint) { - comparisonPoint = points[0]; - } - } - if (!Array.isArray(comparisonPoint) || comparisonPoint.length < 2) { - continue; - } - const deltaMs = timeMs - comparisonPoint[0]; - if (!Number.isFinite(deltaMs) || deltaMs <= 0) { - continue; - } - const deltaHours = deltaMs / (60 * 60 * 1e3); - if (!Number.isFinite(deltaHours) || deltaHours <= 0) { - continue; - } - const rateValue = (value - comparisonPoint[1]) / deltaHours; - if (!Number.isFinite(rateValue)) { - continue; - } - ratePoints.push([timeMs, rateValue]); - } - return ratePoints; + .crosshair-line.vertical { + width: 1px; + transform: translateX(-50%); } - function buildDeltaPoints(sourcePoints, comparisonPoints) { - if (!Array.isArray(sourcePoints) || sourcePoints.length < 2 || !Array.isArray(comparisonPoints) || comparisonPoints.length < 2) { - return []; - } - const deltaPoints = []; - for (const [timeMs, value] of sourcePoints) { - const comparisonValue = interpolateSeriesValue(comparisonPoints, timeMs); - if (comparisonValue == null) { - continue; - } - deltaPoints.push([timeMs, value - comparisonValue]); - } - return deltaPoints; + .crosshair-line.horizontal { + height: 1px; + transform: translateY(-50%); } - function buildSummaryStats(points) { - if (!Array.isArray(points) || !points.length) { - return null; - } - let min = Infinity; - let max = -Infinity; - let sum = 0; - let count = 0; - for (const point of points) { - const value = Number(point?.[1]); - if (!Number.isFinite(value)) { - continue; - } - if (value < min) { - min = value; - } - if (value > max) { - max = value; - } - sum += value; - count += 1; - } - if (!Number.isFinite(min) || !Number.isFinite(max) || count === 0) { - return null; - } - return { - min, - max, - mean: sum / count - }; - } - function parseDateValue(value) { - if (!value) { - return null; - } - if (value instanceof Date) { - return Number.isNaN(value.getTime()) ? null : value; - } - const parsed = new Date(value); - return Number.isNaN(parsed.getTime()) ? null : parsed; - } - function createChartZoomRange(startValue, endValue) { - const startDate = parseDateValue(startValue); - const endDate = parseDateValue(endValue); - const start = startDate?.getTime(); - const end = endDate?.getTime(); - if (typeof start === "number" && Number.isFinite(start) && typeof end === "number" && Number.isFinite(end) && start < end) { - return { start, end }; - } - return null; - } - function makeDateWindowId(label, existingIds = /* @__PURE__ */ new Set()) { - const base = slugifySeriesName(label) || "date-window"; - let candidate = base; - let suffix = 2; - while (existingIds.has(candidate)) { - candidate = `${base}-${suffix}`; - suffix += 1; - } - return candidate; + .crosshair-line.horizontal.series { + left: 0; + width: 100%; } - function normalizeDateWindows(windows) { - if (!Array.isArray(windows)) { - return []; - } - const seen = /* @__PURE__ */ new Set(); - const normalized = []; - windows.forEach((window2, index) => { - const label = String(window2?.label || window2?.name || "").trim(); - const start = parseDateValue( - window2?.start_time || window2?.start - ); - const end = parseDateValue( - window2?.end_time || window2?.end - ); - if (!label || !start || !end || start >= end) { - return; - } - const id = String(window2?.id || "").trim() || makeDateWindowId(`${label}-${index + 1}`, seen); - if (seen.has(id)) { - return; - } - seen.add(id); - normalized.push({ - id, - label, - start_time: start.toISOString(), - end_time: end.toISOString() - }); - }); - return normalized; - } - function parseDateWindowsParam(value) { - if (!value || typeof value !== "string") { - return []; - } - return normalizeDateWindows( - value.split("|").map((entry) => { - const [rawId, rawLabel, rawStart, rawEnd] = String(entry).split("~"); - return { - id: decodeURIComponent(rawId || ""), - label: decodeURIComponent(rawLabel || ""), - start_time: decodeURIComponent(rawStart || ""), - end_time: decodeURIComponent(rawEnd || "") - }; - }) - ); + .crosshair-line.horizontal.series.subtle { + background: currentColor; + opacity: 0.22; } - function serializeDateWindowsParam(windows) { - const normalized = normalizeDateWindows(windows); - if (!normalized.length) { - return ""; - } - return normalized.map( - (window2) => [ - encodeURIComponent(window2.id), - encodeURIComponent(window2.label ?? ""), - encodeURIComponent(window2.start_time), - encodeURIComponent(window2.end_time) - ].join("~") - ).join("|"); - } - function parseHistoryPageStateParam(value) { - if (!value || typeof value !== "string") { - return null; - } - try { - const parsed = JSON.parse(value); - return parsed && typeof parsed === "object" ? parsed : null; - } catch { - return null; - } + .crosshair-line.horizontal.series.emphasized { + height: 0; + background: transparent; + border-top: 1px dashed currentColor; + opacity: 0.9; } - function serializeHistoryPageStateParam(state) { - if (!state || typeof state !== "object") { - return ""; - } - try { - return JSON.stringify(state); - } catch { - return ""; - } + .crosshair-points { + position: absolute; + inset: 0; } - function buildDataPointsHistoryPath(target = {}, options = {}) { - const normalizedTarget = { - entity_id: [...new Set((target.entity_id || []).filter(Boolean))], - device_id: [...new Set((target.device_id || []).filter(Boolean))], - area_id: [...new Set((target.area_id || []).filter(Boolean))], - label_id: [...new Set((target.label_id || []).filter(Boolean))] - }; - const params = new URLSearchParams(); - if (normalizedTarget.entity_id.length) { - params.set("entity_id", normalizedTarget.entity_id.join(",")); - } - if (normalizedTarget.device_id.length) { - params.set("device_id", normalizedTarget.device_id.join(",")); - } - if (normalizedTarget.area_id.length) { - params.set("area_id", normalizedTarget.area_id.join(",")); - } - if (normalizedTarget.label_id.length) { - params.set("label_id", normalizedTarget.label_id.join(",")); - } - if (options.datapoint_scope === "all") { - params.set("datapoints_scope", "all"); - } - const start = options.start_time ? new Date(options.start_time) : null; - const end = options.end_time ? new Date(options.end_time) : null; - if (start && end && Number.isFinite(start.getTime()) && Number.isFinite(end.getTime()) && start < end) { - params.set("start_time", start.toISOString()); - params.set("end_time", end.toISOString()); - params.set( - "hours_to_show", - String( - Math.max(1, Math.round((end.getTime() - start.getTime()) / 36e5)) - ) - ); - } - const zoomStart = options.zoom_start_time ? new Date(options.zoom_start_time) : null; - const zoomEnd = options.zoom_end_time ? new Date(options.zoom_end_time) : null; - if (zoomStart && zoomEnd && Number.isFinite(zoomStart.getTime()) && Number.isFinite(zoomEnd.getTime()) && zoomStart < zoomEnd) { - params.set("zoom_start_time", zoomStart.toISOString()); - params.set("zoom_end_time", zoomEnd.toISOString()); - } - const pageStateParam = serializeHistoryPageStateParam(options.page_state); - if (pageStateParam) { - params.set("page_state", pageStateParam); - } - return `/${PANEL_URL_PATH}?${params.toString()}`; - } - function navigateToDataPointsHistory(_card, target = {}, options = {}) { - const path = buildDataPointsHistoryPath(target, options); - if (window.history && window.history.pushState) { - window.history.pushState(null, "", path); - window.dispatchEvent(new Event("location-changed")); - return; - } - window.location.assign(path); + .crosshair-point { + position: absolute; + width: 10px; + height: 10px; + border-radius: 50%; + border: 2px solid var(--card-background-color, #fff); + box-shadow: 0 2px 6px rgba(0,0,0,0.18); + transform: translate(-50%, -50%); } - const styles$S = ` - hass-datapoints-history-chart { - position: relative; - display: flex; - flex-direction: column; - height: 100%; - min-height: 0; - --dp-spacing-xs: calc(var(--spacing, 8px) * 0.5); - --dp-spacing-sm: var(--spacing, 8px); - --dp-spacing-md: calc(var(--spacing, 8px) * 1.5); - --dp-spacing-lg: calc(var(--spacing, 8px) * 2); - --dp-spacing-xl: calc(var(--spacing, 8px) * 2.5); - --ha-tooltip-background-color: color-mix(in srgb, #0f1218 96%, transparent); - --ha-tooltip-text-color: rgba(255, 255, 255, 0.96); - --ha-tooltip-padding: calc(var(--dp-spacing-sm) + 2px) calc(var(--dp-spacing-md) + 2px); - --ha-tooltip-border-radius: 10px; - --ha-tooltip-arrow-size: 10px; - --ha-tooltip-font-size: 0.86rem; - --ha-tooltip-line-height: 1.1; - } - ha-card { padding: 0; overflow: visible; height: 100%; display: flex; flex-direction: column; } - .card-header { - padding: var(--dp-spacing-lg); - font-size: 1.1em; - font-weight: 500; - color: var(--primary-text-color); - flex: 0 0 auto; - line-height: 1.3; - } - .chart-top-slot[hidden] { - display: none; - } - .chart-top-slot { - position: relative; - flex: 0 0 auto; - min-width: 0; - margin-left: calc(var(--dp-spacing-md) * -1); - margin-right: calc(var(--dp-spacing-md) * -1); - margin-top: -5px; - z-index: 1; - } - .chart-wrap { - position: relative; - display: flex; - flex-direction: column; - flex: 1 1 auto; - min-height: 0; - padding: var(--dp-spacing-sm) var(--dp-spacing-md) var(--dp-spacing-md); - box-sizing: border-box; - overflow: visible; - isolation: isolate; - z-index: 3; - } - .chart-preview-overlay[hidden] { - display: none; - } - .chart-preview-overlay { + .crosshair-axis-dot { position: absolute; - top: calc(var(--dp-chart-top-slot-height, 0px) + var(--dp-spacing-sm)); - left: var(--dp-spacing-md); - display: flex; - flex-direction: column; - gap: 2px; - max-width: min(340px, calc(100% - (var(--dp-spacing-lg) * 2))); - padding: 8px 12px; - border-radius: 10px; - background: color-mix(in srgb, var(--card-background-color, #fff) 90%, transparent); - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08); - backdrop-filter: blur(4px); - pointer-events: none; - z-index: 4; - } - .chart-preview-kicker { - font-size: 0.68rem; - line-height: 1.15; - color: color-mix(in srgb, var(--warning-color, #f59e0b) 72%, var(--secondary-text-color, #6b7280)); - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.04em; - } - .chart-preview-title { - font-size: 0.84rem; - line-height: 1.2; - color: var(--primary-text-color); - font-weight: 600; - } - .chart-preview-line { - font-size: 0.74rem; - line-height: 1.2; - color: var(--secondary-text-color); + width: 5px; + height: 5px; + border-radius: 50%; + border: 2px solid var(--card-background-color, #fff); + box-shadow: 0 1px 4px rgba(0,0,0,0.28); + transform: translate(-50%, -50%); } - .chart-preview-line strong { - color: color-mix(in srgb, var(--warning-color, #f59e0b) 72%, var(--primary-text-color, #111)); - font-weight: 600; + .chart-axis-hover-dot { + position: absolute; + width: 5px; + height: 5px; + border-radius: 50%; + border: 2px solid var(--card-background-color, #fff); + box-shadow: 0 1px 4px rgba(0,0,0,0.28); + top: 0; + transform: translateY(-50%); } - .chart-scroll-viewport { - position: relative; - flex: 1 1 auto; - min-height: 0; - overflow-x: auto; - overflow-y: hidden; - scrollbar-gutter: stable both-edges; - -webkit-overflow-scrolling: touch; + .chart-axis-hover-dot.left { + right: 0; + transform: translate(50%, -50%); } - .chart-stage { - position: relative; - min-height: 100%; + .chart-axis-hover-dot.right { + left: 0; + transform: translate(-50%, -50%); } - .chart-icon-overlay { + .chart-zoom-selection { position: absolute; - inset: 0; + border-radius: 6px; + border: 1px solid color-mix(in srgb, var(--primary-color, #03a9f4) 78%, transparent); + background: color-mix(in srgb, var(--primary-color, #03a9f4) 18%, transparent); pointer-events: none; - z-index: 2; + opacity: 0; + transition: opacity 120ms ease; } - .chart-event-icon { + .chart-zoom-selection.visible { + opacity: 1; + } + .chart-add-annotation { position: absolute; - width: 18px; - height: 18px; - transform: translate(-50%, -50%); + width: 24px; + height: 24px; display: inline-flex; align-items: center; justify-content: center; - pointer-events: auto; - cursor: pointer; - border: 0; padding: 0; margin: 0; - background: transparent; - border-radius: 50%; + border: 1px solid color-mix(in srgb, var(--secondary-text-color, #616161) 22%, transparent); + border-radius: 8px; + background: color-mix(in srgb, var(--secondary-background-color, #f3f4f6) 94%, transparent); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.16); + color: var(--secondary-text-color, #616161); + cursor: pointer; + z-index: 4; + transform: translate(-50%, -50%); } - .chart-event-icon ha-icon { + .chart-add-annotation ha-icon { --mdc-icon-size: 14px; pointer-events: none; } - .chart-axis-overlay { - position: absolute; - top: calc(var(--dp-chart-top-slot-height, 0px) + 5px); - bottom: 0; - display: none; - pointer-events: none; - background: var(--card-background-color, var(--primary-background-color, #fff)); - overflow: hidden; - z-index: 3; - border-bottom-left-radius: 11px; - } - .chart-axis-overlay.visible { - display: block; - } - .chart-axis-overlay.left { - left: 0; + .chart-add-annotation:hover, + .chart-add-annotation:focus-visible { + background: color-mix(in srgb, var(--secondary-background-color, #f3f4f6) 82%, transparent); + color: var(--primary-text-color); + outline: none; } - .chart-axis-overlay.right { - right: 0; + .chart-add-annotation[hidden] { + display: none; } - .chart-axis-divider { + .chart-zoom-out { position: absolute; - top: 0; - bottom: 0; - width: 1px; - background: rgba(128,128,128,0.35); - } - .chart-axis-overlay.left .chart-axis-divider { - right: 0; + top: calc(var(--dp-chart-top-slot-height, 0px) + var(--dp-spacing-sm)); + right: var(--dp-spacing-lg); + display: inline-flex; + align-items: center; + gap: calc(var(--spacing, 8px) * 0.75); + padding: calc(var(--spacing, 8px) * 0.875) var(--dp-spacing-md); + border: 1px solid color-mix(in srgb, var(--primary-color, #03a9f4) 26%, transparent); + border-radius: 999px; + background: color-mix(in srgb, var(--primary-color, #03a9f4) 12%, var(--card-background-color, #fff)); + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); + color: var(--primary-color, #03a9f4); + font: inherit; + font-size: 0.82rem; + font-weight: 500; + cursor: pointer; + z-index: 4; } - .chart-axis-overlay.right .chart-axis-divider { - left: 0; + .chart-zoom-out ha-icon { + --mdc-icon-size: 16px; } - .chart-axis-label, - .chart-axis-unit { - position: absolute; - color: var(--secondary-text-color); - font: 12px sans-serif; - line-height: 1; - white-space: nowrap; + .chart-zoom-out[hidden] { + display: none; } - .chart-axis-label { - transform: translateY(calc(-50% + 6px)); + .chart-zoom-out:hover, + .chart-zoom-out:focus-visible { + background: color-mix(in srgb, var(--primary-color, #03a9f4) 18%, var(--card-background-color, #fff)); + outline: none; } - .chart-axis-unit { + .chart-adjust-axis { + position: absolute; + left: calc(var(--dp-chart-axis-left-width, 0px) + var(--dp-spacing-sm, 8px)); + bottom: calc(var(--dp-chart-axis-bottom-height, 50px) + var(--dp-spacing-sm, 8px)); + display: inline-flex; + align-items: center; + gap: calc(var(--spacing, 8px) * 0.75); + padding: calc(var(--spacing, 8px) * 0.875) var(--dp-spacing-md); + border: 1px solid color-mix(in srgb, var(--primary-color, #03a9f4) 26%, transparent); + border-radius: 999px; + background: color-mix(in srgb, var(--primary-color, #03a9f4) 12%, var(--card-background-color, #fff)); + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); + color: var(--primary-color, #03a9f4); + font: inherit; + font-size: 0.82rem; font-weight: 500; + cursor: pointer; + z-index: 4; } - canvas { display: block; } - .chart-loading { - position: absolute; - top: 50%; - left: 50%; + .chart-adjust-axis[hidden] { display: none; - align-items: center; - justify-content: center; - gap: var(--dp-spacing-sm); - min-width: calc(var(--spacing, 8px) * 12); - min-height: calc(var(--spacing, 8px) * 5); - padding: var(--dp-spacing-sm) var(--dp-spacing-md); - border-radius: 999px; - background: color-mix(in srgb, var(--card-background-color, #fff) 92%, transparent); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12); - z-index: 6; - pointer-events: none; - transform: translate(-50%, -50%); - } - .chart-loading.active { - display: inline-flex; - } - .chart-loading-spinner { - width: calc(var(--spacing, 8px) * 2); - height: calc(var(--spacing, 8px) * 2); - border-radius: 50%; - border: 2px solid color-mix(in srgb, var(--primary-color, #03a9f4) 22%, transparent); - border-top-color: var(--primary-color, #03a9f4); - animation: chart-spinner 0.9s linear infinite; - } - .chart-loading::after { - content: none; - } - .chart-loading-label { - color: var(--secondary-text-color); - font-size: 0.85rem; - font-weight: 500; - } - @keyframes chart-spinner { - to { - transform: rotate(360deg); - } - } - .chart-message { - position: absolute; - inset: 0; - display: none; - align-items: center; - justify-content: center; - padding: calc(var(--spacing, 8px) * 5) var(--dp-spacing-lg); - text-align: center; - color: var(--secondary-text-color); - font-size: 0.95rem; - pointer-events: none; - z-index: 2; - } - .chart-message.visible { - display: flex; - } - .chart-crosshair { - position: absolute; - inset: 0; - pointer-events: none; - } - .chart-crosshair[hidden] { - display: none; - } - .crosshair-line { - position: absolute; - background: color-mix(in srgb, var(--primary-text-color, #111) 24%, transparent); - } - .crosshair-line.vertical { - width: 1px; - transform: translateX(-50%); - } - .crosshair-line.horizontal { - height: 1px; - transform: translateY(-50%); - } - .crosshair-line.horizontal.series { - left: 0; - width: 100%; - } - .crosshair-line.horizontal.series.subtle { - background: currentColor; - opacity: 0.22; - } - .crosshair-line.horizontal.series.emphasized { - height: 0; - background: transparent; - border-top: 1px dashed currentColor; - opacity: 0.9; - } - .crosshair-points { - position: absolute; - inset: 0; - } - .crosshair-point { - position: absolute; - width: 10px; - height: 10px; - border-radius: 50%; - border: 2px solid var(--card-background-color, #fff); - box-shadow: 0 2px 6px rgba(0,0,0,0.18); - transform: translate(-50%, -50%); - } - .crosshair-axis-dot { - position: absolute; - width: 5px; - height: 5px; - border-radius: 50%; - border: 2px solid var(--card-background-color, #fff); - box-shadow: 0 1px 4px rgba(0,0,0,0.28); - transform: translate(-50%, -50%); - } - .chart-axis-hover-dot { - position: absolute; - width: 5px; - height: 5px; - border-radius: 50%; - border: 2px solid var(--card-background-color, #fff); - box-shadow: 0 1px 4px rgba(0,0,0,0.28); - top: 0; - transform: translateY(-50%); - } - .chart-axis-hover-dot.left { - right: 0; - transform: translate(50%, -50%); - } - .chart-axis-hover-dot.right { - left: 0; - transform: translate(-50%, -50%); - } - .chart-zoom-selection { - position: absolute; - border-radius: 6px; - border: 1px solid color-mix(in srgb, var(--primary-color, #03a9f4) 78%, transparent); - background: color-mix(in srgb, var(--primary-color, #03a9f4) 18%, transparent); - pointer-events: none; - opacity: 0; - transition: opacity 120ms ease; - } - .chart-zoom-selection.visible { - opacity: 1; - } - .chart-add-annotation { - position: absolute; - width: 24px; - height: 24px; - display: inline-flex; - align-items: center; - justify-content: center; - padding: 0; - margin: 0; - border: 1px solid color-mix(in srgb, var(--secondary-text-color, #616161) 22%, transparent); - border-radius: 8px; - background: color-mix(in srgb, var(--secondary-background-color, #f3f4f6) 94%, transparent); - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.16); - color: var(--secondary-text-color, #616161); - cursor: pointer; - z-index: 4; - transform: translate(-50%, -50%); - } - .chart-add-annotation ha-icon { - --mdc-icon-size: 14px; - pointer-events: none; - } - .chart-add-annotation:hover, - .chart-add-annotation:focus-visible { - background: color-mix(in srgb, var(--secondary-background-color, #f3f4f6) 82%, transparent); - color: var(--primary-text-color); - outline: none; - } - .chart-add-annotation[hidden] { - display: none; - } - .chart-zoom-out { - position: absolute; - top: calc(var(--dp-chart-top-slot-height, 0px) + var(--dp-spacing-sm)); - right: var(--dp-spacing-lg); - display: inline-flex; - align-items: center; - gap: calc(var(--spacing, 8px) * 0.75); - padding: calc(var(--spacing, 8px) * 0.875) var(--dp-spacing-md); - border: 1px solid color-mix(in srgb, var(--primary-color, #03a9f4) 26%, transparent); - border-radius: 999px; - background: color-mix(in srgb, var(--primary-color, #03a9f4) 12%, var(--card-background-color, #fff)); - box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); - color: var(--primary-color, #03a9f4); - font: inherit; - font-size: 0.82rem; - font-weight: 500; - cursor: pointer; - z-index: 4; - } - .chart-zoom-out ha-icon { - --mdc-icon-size: 16px; - } - .chart-zoom-out[hidden] { - display: none; - } - .chart-zoom-out:hover, - .chart-zoom-out:focus-visible { - background: color-mix(in srgb, var(--primary-color, #03a9f4) 18%, var(--card-background-color, #fff)); - outline: none; - } - .chart-adjust-axis { - position: absolute; - left: calc(var(--dp-chart-axis-left-width, 0px) + var(--dp-spacing-sm, 8px)); - bottom: calc(var(--dp-chart-axis-bottom-height, 50px) + var(--dp-spacing-sm, 8px)); - display: inline-flex; - align-items: center; - gap: calc(var(--spacing, 8px) * 0.75); - padding: calc(var(--spacing, 8px) * 0.875) var(--dp-spacing-md); - border: 1px solid color-mix(in srgb, var(--primary-color, #03a9f4) 26%, transparent); - border-radius: 999px; - background: color-mix(in srgb, var(--primary-color, #03a9f4) 12%, var(--card-background-color, #fff)); - box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); - color: var(--primary-color, #03a9f4); - font: inherit; - font-size: 0.82rem; - font-weight: 500; - cursor: pointer; - z-index: 4; - } - .chart-adjust-axis[hidden] { - display: none; - } - .chart-adjust-axis:hover, - .chart-adjust-axis:focus-visible { - background: color-mix(in srgb, var(--primary-color, #03a9f4) 18%, var(--card-background-color, #fff)); - outline: none; - } - .legend { - display: flex; - flex-wrap: nowrap; + } + .chart-adjust-axis:hover, + .chart-adjust-axis:focus-visible { + background: color-mix(in srgb, var(--primary-color, #03a9f4) 18%, var(--card-background-color, #fff)); + outline: none; + } + .legend { + display: flex; + flex-wrap: nowrap; align-items: center; gap: var(--dp-spacing-sm); padding: var(--dp-spacing-sm) var(--dp-spacing-md) var(--dp-spacing-md); @@ -9004,100 +11066,298 @@ ${s2.description}`).join("\n\n"); text-overflow: ellipsis; } `; - var __defProp$V = Object.defineProperty; - var __defNormalProp$V = (obj, key, value) => key in obj ? __defProp$V(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$V = (obj, key, value) => __defNormalProp$V(obj, typeof key !== "symbol" ? key + "" : key, value); - const HISTORY_LEGEND_WRAP_ENABLE_HEIGHT_PX = 500; - const HISTORY_LEGEND_WRAP_DISABLE_HEIGHT_PX = 440; - const HISTORY_CHART_MAX_CANVAS_WIDTH_PX = Math.floor( - 16383 / (window.devicePixelRatio || 1) - ); - const HISTORY_CHART_MAX_ZOOM_MULTIPLIER = 365; - let HistoryChart$1 = class HistoryChart extends HTMLElement { - constructor() { - super(...arguments); - __publicField$V(this, "_hass", null); - __publicField$V(this, "_config", {}); - __publicField$V(this, "_legendWrapRows", false); - __publicField$V(this, "_adjustComparisonAxisScale", false); - __publicField$V(this, "_drawRequestId", 0); - __publicField$V(this, "_analysisCache", null); - __publicField$V(this, "_backendAnomalyByEntity", /* @__PURE__ */ new Map()); - __publicField$V(this, "_backendComparisonAnomalyByKey", /* @__PURE__ */ new Map()); - __publicField$V(this, "_pendingAnomalyEntityIds", /* @__PURE__ */ new Set()); - __publicField$V(this, "_pendingComparisonAnomalyKeys", /* @__PURE__ */ new Set()); - __publicField$V(this, "_chartHoverCleanup", null); - __publicField$V(this, "_chartZoomCleanup", null); - __publicField$V(this, "_chartZoomDragging", false); - __publicField$V(this, "_chartLastHover", null); - __publicField$V(this, "_scrollSyncSuspended", false); - __publicField$V(this, "_lastProgrammaticScrollLeft", null); - __publicField$V(this, "_ignoreNextProgrammaticScrollEvent", false); - __publicField$V(this, "_skipNextScrollViewportSync", false); - __publicField$V(this, "_creatingContextAnnotation", false); - __publicField$V(this, "_lastComparisonResults", null); - __publicField$V(this, "_hiddenSeries", /* @__PURE__ */ new Set()); - __publicField$V(this, "_entityIds", []); - __publicField$V(this, "_previousSeriesEndpoints", /* @__PURE__ */ new Map()); - __publicField$V(this, "_zoomRange", null); - __publicField$V(this, "_chartScrollViewportEl", null); - __publicField$V(this, "_chartStageEl", null); - __publicField$V(this, "_annotationDialog", null); - __publicField$V(this, "_scrollZoomApplyTimer", null); - __publicField$V(this, "_onChartScroll", () => { - if (this._scrollSyncSuspended || this._ignoreNextProgrammaticScrollEvent) { - this._ignoreNextProgrammaticScrollEvent = false; - return; - } - if (!this._chartScrollViewportEl || !this._zoomRange) return; - const viewport = this._chartScrollViewportEl; - const scrollLeft = viewport.scrollLeft; - const maxScrollLeft = Math.max( - 1, - viewport.scrollWidth - viewport.clientWidth - ); - const ratio = scrollLeft / maxScrollLeft; - const totalMs = Math.max(1, this._lastT1 - this._lastT0); - const spanMs = this._zoomRange.end - this._zoomRange.start; - const maxStartOffsetMs = Math.max(0, totalMs - spanMs); - const newStart = this._lastT0 + ratio * maxStartOffsetMs; - this._zoomRange = { start: newStart, end: newStart + spanMs }; - this._dispatchZoomPreview({ - startTime: newStart, - endTime: newStart + spanMs - }); - if (this._scrollZoomApplyTimer !== null) { - clearTimeout(this._scrollZoomApplyTimer); - } - this._scrollZoomApplyTimer = setTimeout(() => { - this._scrollZoomApplyTimer = null; - if (!this._zoomRange) return; - this.dispatchEvent( - new CustomEvent("hass-datapoints-zoom-apply", { - bubbles: true, - composed: true, - detail: { start: this._zoomRange.start, end: this._zoomRange.end } - }) - ); - }, 300); - }); - __publicField$V(this, "_lastAnomalyRegions", []); - __publicField$V(this, "_lastHistResult", null); - __publicField$V(this, "_lastStatsResult", null); - __publicField$V(this, "_lastEvents", null); - __publicField$V(this, "_hiddenEventIds", /* @__PURE__ */ new Set()); - __publicField$V(this, "_lastT0", 0); - __publicField$V(this, "_lastT1", 0); - __publicField$V(this, "_lastDrawArgs", []); - } - // ── No shadow DOM ─────────────────────────────────────────────────────────── - // Renders synchronously into its own children so the canvas and legend are - // accessible from the parent card's shadow root (required by existing tests). - /** Called once when the element is inserted into the DOM. */ - connectedCallback() { - this.style.cssText = "position:relative;display:flex;flex-direction:column;height:100%;min-height:0;padding:var(--dp-spacing-sm,8px) var(--dp-spacing-md,12px) var(--dp-spacing-md,12px);box-sizing:border-box;overflow:visible;isolation:isolate;z-index:3;"; - if (this.querySelector("#chart")) return; - this.innerHTML = ` + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/history-chart/history-chart.ts + /** + * history-chart.ts + * + * LitElement sub-component that owns the canvas, the chart DOM shell + * (loading spinner, tooltips, crosshair, legend, axis overlays), and all + * drawing/interaction state for the history chart card. + * + * IMPORTANT: No shadow DOM — renders into its own children so the canvas and + * legend remain accessible from the parent card's shadow root. + */ + var HISTORY_LEGEND_WRAP_ENABLE_HEIGHT_PX = 500; + var HISTORY_LEGEND_WRAP_DISABLE_HEIGHT_PX = 440; + var HISTORY_CHART_MAX_CANVAS_WIDTH_PX = Math.floor(16383 / (window.devicePixelRatio || 1)); + var HISTORY_CHART_MAX_ZOOM_MULTIPLIER = 365; + var HistoryChart$1 = class extends HTMLElement { + constructor(..._args) { + super(..._args); + _defineProperty(this, "_hass", null); + _defineProperty(this, "_config", {}); + _defineProperty( + this, + /** Whether the legend should wrap to multiple rows. */ + "_legendWrapRows", + false + ); + _defineProperty( + this, + /** When true, the comparison axis scale has been manually adjusted by the user. */ + "_adjustComparisonAxisScale", + false + ); + _defineProperty( + this, + /** Monotonically-incrementing request ID — stale draws are discarded. */ + "_drawRequestId", + 0 + ); + _defineProperty( + this, + /** Cached analysis result keyed by a content hash. */ + "_analysisCache", + null + ); + _defineProperty( + this, + /** Backend anomaly data keyed by entity ID. */ + "_backendAnomalyByEntity", + /* @__PURE__ */ new Map() + ); + _defineProperty( + this, + /** Backend anomaly data keyed by comparison window ID + entity ID. */ + "_backendComparisonAnomalyByKey", + /* @__PURE__ */ new Map() + ); + _defineProperty( + this, + /** Entity IDs whose anomaly fetch is currently in-flight. */ + "_pendingAnomalyEntityIds", + /* @__PURE__ */ new Set() + ); + _defineProperty( + this, + /** Comparison window + entity keys whose anomaly fetch is currently in-flight. */ + "_pendingComparisonAnomalyKeys", + /* @__PURE__ */ new Set() + ); + _defineProperty( + this, + /** + * Cleanup function returned by attachLineChartHover. + * Must be called before re-attaching hover or before unmount. + */ + "_chartHoverCleanup", + null + ); + _defineProperty( + this, + /** + * Cleanup function returned by attachLineChartRangeZoom. + * Must be called before re-attaching zoom or before unmount. + */ + "_chartZoomCleanup", + null + ); + _defineProperty( + this, + /** True while the user is drag-zooming on the split chart overlay. */ + "_chartZoomDragging", + false + ); + _defineProperty( + this, + /** Last computed hover object — used by split-chart crosshair/tooltip. */ + "_chartLastHover", + null + ); + _defineProperty( + this, + /** + * When true, scroll sync events from the secondary (comparison) chart + * viewport are temporarily ignored to prevent feedback loops. + */ + "_scrollSyncSuspended", + false + ); + _defineProperty( + this, + /** + * The scrollLeft value of the last programmatic scroll, used to detect + * and ignore the scroll event that the browser fires for that scroll. + */ + "_lastProgrammaticScrollLeft", + null + ); + _defineProperty( + this, + /** + * Set to true immediately before a programmatic scroll so the resulting + * scroll event can be identified and skipped by the scroll handler. + */ + "_ignoreNextProgrammaticScrollEvent", + false + ); + _defineProperty( + this, + /** + * When true, the next call to _syncChartViewportScroll will skip + * repositioning the viewport and clear this flag. Used to prevent + * a snap-back immediately after the user commits a zoom via scroll. + */ + "_skipNextScrollViewportSync", + false + ); + _defineProperty( + this, + /** + * True while the user is in the process of creating a context annotation + * (i.e. after clicking the "+" button but before the dialog closes). + */ + "_creatingContextAnnotation", + false + ); + _defineProperty( + this, + /** Last comparison results fetched by the card. */ + "_lastComparisonResults", + null + ); + _defineProperty( + this, + /** Series entity IDs that are currently hidden from the chart. */ + "_hiddenSeries", + /* @__PURE__ */ new Set() + ); + _defineProperty( + this, + /** Entity IDs tracked by this card instance. */ + "_entityIds", + [] + ); + _defineProperty( + this, + /** Previous drawn endpoints per entity, used for live-update logging. */ + "_previousSeriesEndpoints", + /* @__PURE__ */ new Map() + ); + _defineProperty( + this, + /** The currently active zoom range, or null if not zoomed. */ + "_zoomRange", + null + ); + _defineProperty( + this, + /** The chart scroll viewport element (set during draw). */ + "_chartScrollViewportEl", + null + ); + _defineProperty( + this, + /** The chart stage element (set during draw). */ + "_chartStageEl", + null + ); + _defineProperty( + this, + /** The annotation dialog controller. */ + "_annotationDialog", + null + ); + _defineProperty( + this, + /** Debounce timer for dispatching zoom-apply after a user scroll. */ + "_scrollZoomApplyTimer", + null + ); + _defineProperty( + this, + /** Bound scroll handler — wired to _chartScrollViewportEl. */ + "_onChartScroll", + () => { + if (this._scrollSyncSuspended || this._ignoreNextProgrammaticScrollEvent) { + this._ignoreNextProgrammaticScrollEvent = false; + return; + } + if (!this._chartScrollViewportEl || !this._zoomRange) return; + const viewport = this._chartScrollViewportEl; + const ratio = viewport.scrollLeft / Math.max(1, viewport.scrollWidth - viewport.clientWidth); + const totalMs = Math.max(1, this._lastT1 - this._lastT0); + const spanMs = this._zoomRange.end - this._zoomRange.start; + const maxStartOffsetMs = Math.max(0, totalMs - spanMs); + const newStart = this._lastT0 + ratio * maxStartOffsetMs; + this._zoomRange = { + start: newStart, + end: newStart + spanMs + }; + this._dispatchZoomPreview({ + startTime: newStart, + endTime: newStart + spanMs + }); + if (this._scrollZoomApplyTimer !== null) clearTimeout(this._scrollZoomApplyTimer); + this._scrollZoomApplyTimer = setTimeout(() => { + this._scrollZoomApplyTimer = null; + if (!this._zoomRange) return; + this.dispatchEvent(new CustomEvent("hass-datapoints-zoom-apply", { + bubbles: true, + composed: true, + detail: { + start: this._zoomRange.start, + end: this._zoomRange.end + } + })); + }, 300); + } + ); + _defineProperty( + this, + /** Last drawn anomaly regions (for hover hit-testing). */ + "_lastAnomalyRegions", + [] + ); + _defineProperty( + this, + /** Cache of last drawn history result. */ + "_lastHistResult", + null + ); + _defineProperty( + this, + /** Cache of last drawn statistics result. */ + "_lastStatsResult", + null + ); + _defineProperty( + this, + /** Cache of last drawn events list. */ + "_lastEvents", + null + ); + _defineProperty( + this, + /** IDs of datapoint events currently hidden from the chart. */ + "_hiddenEventIds", + /* @__PURE__ */ new Set() + ); + _defineProperty( + this, + /** Cache of last draw time range start (ms). */ + "_lastT0", + 0 + ); + _defineProperty( + this, + /** Cache of last draw time range end (ms). */ + "_lastT1", + 0 + ); + _defineProperty( + this, + /** Cache of last _drawChart argument list. */ + "_lastDrawArgs", + [] + ); + } + /** Called once when the element is inserted into the DOM. */ + connectedCallback() { + this.style.cssText = "position:relative;display:flex;flex-direction:column;height:100%;min-height:0;padding:var(--dp-spacing-sm,8px) var(--dp-spacing-md,12px) var(--dp-spacing-md,12px);box-sizing:border-box;overflow:visible;isolation:isolate;z-index:3;"; + if (this.querySelector("#chart")) return; + this.innerHTML = `
@@ -9119,4772 +11379,3326 @@ ${s2.description}`).join("\n\n"); Create Data Point -
- -
-
-
- -
-
- - - - - -
-
-
-
-
-
-
-
-
-
-
`; - } - disconnectedCallback() { - if (this._chartHoverCleanup) { - this._chartHoverCleanup(); - this._chartHoverCleanup = null; - } - if (this._chartZoomCleanup) { - this._chartZoomCleanup(); - this._chartZoomCleanup = null; - } - if (this._chartScrollViewportEl) { - this._chartScrollViewportEl.removeEventListener( - "scroll", - this._onChartScroll - ); - } - if (this._scrollZoomApplyTimer !== null) { - clearTimeout(this._scrollZoomApplyTimer); - this._scrollZoomApplyTimer = null; - } - } - get hass() { - return this._hass; - } - set hass(value) { - this._hass = value; - } - /** True when the current user may create a data point via the chart + button. */ - get _canAddAnnotation() { - return this._config.show_add_annotation_button !== false && this._hass?.user?.is_admin === true; - } - // ── DOM helpers ───────────────────────────────────────────────────────────── - // Use `this.querySelector` / `this.querySelectorAll` instead of - // `this.shadowRoot.querySelector` because there is no shadow root. - _el(id) { - return this.querySelector(`#${id}`); - } - _els(selector) { - return this.querySelectorAll(selector); - } - // ── Public draw-state helpers ─────────────────────────────────────────────── - /** - * Show or hide the loading spinner. - * Mirrors _setChartLoading from card-chart-base-legacy.js. - */ - _setChartLoading(isLoading) { - const loadingEl = this._el("loading"); - if (!loadingEl) { - return; - } - loadingEl.classList.toggle("active", !!isLoading); - } - /** - * Set or clear the chart message (shown when data is unavailable). - * Mirrors _setChartMessage from card-chart-base-legacy.js. - */ - _setChartMessage(message = "") { - const messageEl = this._el("chart-message"); - if (!messageEl) { - return; - } - messageEl.textContent = message || ""; - messageEl.classList.toggle("visible", !!message); - } - // ── Chart frame helpers ───────────────────────────────────────────────────── - /** - * Draw an empty grid frame (shown while data is loading for the first time). - * Ported from _drawEmptyChartFrame in card-history.js. - */ - _drawEmptyChartFrame(t0, t1) { - const canvas = this._el("chart"); - const wrap = this; - const scrollViewport = this._el( - "chart-scroll-viewport" - ); - const chartStage = this._el("chart-stage"); - if (!canvas) { - return; - } - this._syncTopSlotOffset(); - const availableHeight = this._getAvailableChartHeight(280); - const viewportWidth = Math.max( - scrollViewport?.clientWidth || wrap?.clientWidth || 360, - 360 - ); - if (chartStage) { - chartStage.style.width = `${viewportWidth}px`; - chartStage.style.height = `${availableHeight}px`; - } - const { w, h: h2 } = setupCanvas(canvas, chartStage || wrap, availableHeight, viewportWidth); - const renderer = new ChartRenderer(canvas, w, h2); - renderer.labelColor = resolveChartLabelColor(this); - renderer.clear(); - renderer.drawGrid( - t0, - t1, - [ - { - key: "placeholder", - min: 0, - max: 1, - side: "left", - unit: "", - color: null - } - ], - void 0, - 5, - { fixedAxisOverlay: true } - ); - renderChartAxisOverlays( - this, - renderer, - renderer._activeAxes || [] - ); - } - /** - * Compute the chart canvas height from the surrounding card layout. - * Ported from _getAvailableChartHeight in card-history.js. - * When called from within this component (no shadow root) all selectors - * target the host element's light DOM ancestors via `closest`. - */ - _getAvailableChartHeight(minChartHeight = 280) { - const card = this.closest("ha-card"); - const header = card?.querySelector(".card-header"); - const topSlot = this._el("chart-top-slot"); - const legend = this._el("legend"); - const scrollViewport = this._el( - "chart-scroll-viewport" - ); - const wrap = this; - const cardHeight = card?.clientHeight || 0; - const occupiedHeight = (header?.offsetHeight || 0) + (topSlot && !topSlot.hidden ? topSlot.offsetHeight || 0 : 0) + (legend?.offsetHeight || 0); - const cardDerivedHeight = cardHeight ? Math.max(0, cardHeight - occupiedHeight) : 0; - const viewportHeight = scrollViewport?.clientHeight || 0; - const wrapHeight = wrap?.clientHeight || 0; - return Math.max( - minChartHeight, - cardDerivedHeight || viewportHeight || wrapHeight || 0 - ); - } - _syncTopSlotOffset() { - const topSlot = this._el("chart-top-slot"); - const topSlotHeight = topSlot && !topSlot.hidden ? topSlot.offsetHeight || 0 : 0; - this.style.setProperty("--dp-chart-top-slot-height", `${topSlotHeight}px`); - } - /** - * Toggle the legend `wrap-rows` class based on the card height. - * Ported from _updateLegendLayout in card-history.js. - */ - _updateLegendLayout(legendEl) { - if (!legendEl) { - return; - } - const card = this.closest("ha-card"); - const cardHeight = card?.clientHeight || 0; - if (this._legendWrapRows) { - this._legendWrapRows = cardHeight >= HISTORY_LEGEND_WRAP_DISABLE_HEIGHT_PX; - } else { - this._legendWrapRows = cardHeight >= HISTORY_LEGEND_WRAP_ENABLE_HEIGHT_PX; - } - legendEl.classList.toggle("wrap-rows", this._legendWrapRows); - } - /** - * Show or hide the "Adjust Y-Axis" button and wire up its click handler. - * Ported from _setAdjustAxisButtonVisibility in card-history.js. - */ - _setAdjustAxisButtonVisibility(visible, onAdjust) { - const button = this._el("chart-adjust-axis"); - if (!button) { - return; - } - button.hidden = !visible; - if (visible) { - button.onclick = () => { - this._adjustComparisonAxisScale = true; - onAdjust?.(); - this._redrawLastDraw(); - }; - return; - } - button.onclick = null; - } - /** - * Render (or clear) the comparison preview overlay badge. - * Ported from _renderComparisonPreviewOverlay in card-history.js. - */ - _renderComparisonPreviewOverlay(renderer = null) { - const overlayEl = this._el( - "chart-preview-overlay" - ); - if (!overlayEl) { - return; - } - const overlay = this._config?.comparison_preview_overlay || null; - if (!overlay?.window_range_label || !overlay?.actual_range_label) { - overlayEl.hidden = true; - overlayEl.innerHTML = ""; - return; - } - if (renderer?.pad?.left != null) { - overlayEl.style.left = `${Math.max(8, renderer.pad.left + 8)}px`; - } else { - overlayEl.style.left = ""; - } - overlayEl.innerHTML = ` -
${msg("Date window:")} ${esc(overlay.window_range_label)}
-
${msg("Actual:")} ${esc(overlay.actual_range_label)}
- `; - overlayEl.hidden = false; - } - // ── Draw queue ────────────────────────────────────────────────────────────── - /** - * Queue an async draw, discarding any in-flight stale requests. - * Ported from _queueDrawChart in card-history.js. - */ - _queueDrawChart(histResult, statsResult, events, t0, t1, options = {}) { - const drawRequestId = ++this._drawRequestId; - logger$1.log("[hass-datapoints history-card] draw queued", { - drawRequestId, - loading: options.loading ?? false - }); - this._drawChart(histResult, statsResult, events, t0, t1, { - ...options, - drawRequestId - }).catch((error) => { - if (drawRequestId !== this._drawRequestId) { - return; - } - logger$1.error("[hass-datapoints history-card] draw failed", error); - this._setChartLoading(false); - this._setChartMessage("Failed to render chart."); - }); - } - _redrawLastDraw() { - if (this._lastDrawArgs.length !== 5 && this._lastDrawArgs.length !== 6) { - return; - } - const [histResult, statsResult, events, t0, t1, options = {}] = this._lastDrawArgs; - this._queueDrawChart(histResult, statsResult, events, t0, t1, options); - } - _buildBackendAnomalyConfig(analysis) { - const config = { - anomaly_methods: Array.isArray(analysis.anomaly_methods) ? analysis.anomaly_methods : void 0, - anomaly_sensitivity: typeof analysis.anomaly_sensitivity === "string" ? analysis.anomaly_sensitivity : void 0, - anomaly_overlap_mode: typeof analysis.anomaly_overlap_mode === "string" ? analysis.anomaly_overlap_mode : void 0, - anomaly_rate_window: typeof analysis.anomaly_rate_window === "string" ? analysis.anomaly_rate_window : void 0, - anomaly_zscore_window: typeof analysis.anomaly_zscore_window === "string" ? analysis.anomaly_zscore_window : void 0, - anomaly_persistence_window: typeof analysis.anomaly_persistence_window === "string" ? analysis.anomaly_persistence_window : void 0, - trend_method: typeof analysis.trend_method === "string" ? analysis.trend_method : void 0, - trend_window: typeof analysis.trend_window === "string" ? analysis.trend_window : void 0, - anomaly_use_sampled_data: analysis.anomaly_use_sampled_data !== false - }; - if (analysis.anomaly_use_sampled_data !== false) { - config.sample_interval = typeof analysis.sample_interval === "string" ? analysis.sample_interval : null; - config.sample_aggregate = typeof analysis.sample_aggregate === "string" ? analysis.sample_aggregate : null; - } - return config; - } - // ── Instance method stubs — implemented by the parent card / JS layer ──────── - /** Build normalised state list for an entity from histResult/statsResult. Overridden by parent. */ - _buildEntityStateList(entityId, histResult, statsResult) { - const entityIds = this._seriesSettings?.map((seriesSetting) => seriesSetting.entity_id).filter(Boolean) ?? []; - const historyStates = getHistoryStatesForEntity$1( - histResult, - entityId, - entityIds - ); - const rawHistory = normalizeNumericHistory(entityId, historyStates); - const statsHistory = normalizeStatisticsHistory(entityId, statsResult); - return mergeNumericHistoryWithStatistics(rawHistory, statsHistory); - } - /** Build binary state spans from a state list. */ - _buildBinaryStateSpans(stateList, t0, t1) { - const spans = []; - if (!stateList.length) return spans; - let current = stateList[0]; - for (let i2 = 1; i2 < stateList.length; i2++) { - const next = stateList[i2]; - const start = Math.max(current.lu * 1e3, t0); - const end = Math.min(next.lu * 1e3, t1); - if (end > start) { - spans.push({ start, end, state: current.s }); - } - current = next; - } - const lastStart = Math.max(current.lu * 1e3, t0); - if (t1 > lastStart) { - spans.push({ start: lastStart, end: t1, state: current.s }); - } - return spans; - } - /** Return the "on" label for a binary_sensor entity. */ - _binaryOnLabel(entityId) { - const dc = this._hass?.states?.[entityId]?.attributes?.device_class; - return binaryOnLabel(dc ?? ""); - } - /** Return the "off" label for a binary_sensor entity. */ - _binaryOffLabel(entityId) { - const dc = this._hass?.states?.[entityId]?.attributes?.device_class; - return binaryOffLabel(dc ?? ""); - } - /** Normalise the statistics history array for an entity. */ - _normalizeStatisticsHistory(entityId, statsData) { - return normalizeStatisticsHistory(entityId, statsData); - } - /** Render legend items for the given series and binary backgrounds. */ - _renderLegend(series, binaryBackgrounds) { - const legendEl = this.querySelector("#legend"); - if (!legendEl) return; - const allItems = [ - ...series, - ...binaryBackgrounds - ]; - this._updateLegendLayout(legendEl); - legendEl.innerHTML = allItems.map((item) => { - const hidden = this._hiddenSeries?.has(item.entityId); - return `
- -
`; - }).join(""); - legendEl.querySelectorAll(".legend-toggle").forEach((btn) => { - btn.addEventListener("click", () => { - const entityId = btn.dataset.entityId; - if (!entityId || !this._hiddenSeries) return; - if (this._hiddenSeries.has(entityId)) { - this._hiddenSeries.delete(entityId); - } else { - this._hiddenSeries.add(entityId); - } - btn.setAttribute( - "aria-pressed", - this._hiddenSeries.has(entityId) ? "false" : "true" - ); - this._redrawLastDraw(); - }); - }); - } - /** Draw a single series line onto the renderer, with optional data-gap rendering. */ - _drawSeriesLine(renderer, pts, color, t0, t1, min, max, opts) { - const r2 = renderer; - const config = this._config; - const showGaps = config?.show_data_gaps !== false; - const gapThresholdKey = config?.data_gap_threshold || "2h"; - const gapThresholdMs = SAMPLE_INTERVAL_MS[gapThresholdKey] ?? 2 * 60 * 6e4; - if (!showGaps || pts.length < 2 || !Number.isFinite(gapThresholdMs)) { - r2.drawLine(pts, color, t0, t1, min, max, opts); - return; - } - const segments = []; - const gapBridges = []; - let current = [pts[0]]; - for (let i2 = 1; i2 < pts.length; i2++) { - const dt = pts[i2][0] - pts[i2 - 1][0]; - if (dt > gapThresholdMs) { - segments.push(current); - gapBridges.push([pts[i2 - 1], pts[i2]]); - current = [pts[i2]]; - } else { - current.push(pts[i2]); - } - } - segments.push(current); - if (segments.length === 1) { - r2.drawLine(pts, color, t0, t1, min, max, opts); - return; - } - for (const seg of segments) { - r2.drawLine(seg, color, t0, t1, min, max, opts); - } - const gapColor = color.startsWith("rgba") ? color.replace(/[\d.]+\)$/, "0.35)") : `${color}59`; - for (const [lastPt, firstPt] of gapBridges) { - r2.drawLine([lastPt, firstPt], gapColor, t0, t1, min, max, { - dashed: true, - lineWidth: 1.2, - lineOpacity: 0.5 - }); - } - const boundaryPoints = gapBridges.flatMap(([a2, b2]) => [ - a2, - b2 - ]); - r2.drawGapMarkers(boundaryPoints, color, t0, t1, min, max); - } - /** Draw event point icons onto the renderer. */ - _drawRecordedEventPoints(_renderer, _visibleSeries, _events2, _t0, _t1, _opts) { - const renderer = _renderer; - const series = _visibleSeries; - const events = _events2; - const opts = _opts; - const overlay = this.querySelector( - "#chart-icon-overlay" - ); - if (overlay && !opts.skipOverlayClear) { - overlay.innerHTML = ""; - } - if (!renderer || !events?.length) { - return []; - } - const yOffset = Number.isFinite(opts.yOffset) ? opts.yOffset : 0; - const hits = []; - const { ctx } = renderer; - const showIcons = opts.showIcons !== false; - const fallbackHighlightedIds = Array.isArray( - this._config?.hovered_event_ids - ) ? this._config.hovered_event_ids : []; - const highlightedEventIds = new Set( - Array.isArray(opts.highlightedEventIds) ? opts.highlightedEventIds : fallbackHighlightedIds - ); - for (const event of events) { - const timestamp = new Date(event.timestamp).getTime(); - if (timestamp < _t0 || timestamp > _t1) continue; - const eventEntityIds = Array.isArray(event.entity_ids) ? event.entity_ids : []; - const x2 = renderer.xOf(timestamp, _t0, _t1); - const findSeriesWithValue = (candidates) => { - for (const candidate of candidates) { - if (!candidate?.pts?.length || !candidate.axis) continue; - const candidateValue = renderer._interpolateValue( - candidate.pts, - timestamp - ); - if (candidateValue == null) continue; - return { series: candidate, value: candidateValue }; - } - return null; - }; - const matchingSeriesCandidates = eventEntityIds.map((entityId) => series.find((entry) => entry.entityId === entityId)).filter((s2) => s2 != null); - const matchingSeriesHit = findSeriesWithValue(matchingSeriesCandidates); - const linkedToOtherTarget = eventEntityIds.length > 0 && matchingSeriesCandidates.length === 0; - const fallbackSeriesHit = matchingSeriesHit || (linkedToOtherTarget ? null : findSeriesWithValue([...series].reverse())); - const targetSeries = fallbackSeriesHit?.series || null; - const hasNumericTarget = !!(targetSeries?.pts?.length && targetSeries.axis); - const value = hasNumericTarget ? fallbackSeriesHit?.value ?? null : null; - if (hasNumericTarget && value == null) continue; - let y2; - if (hasNumericTarget) { - y2 = renderer.yOf( - value, - targetSeries.axis.min, - targetSeries.axis.max - ); - } else if (linkedToOtherTarget) { - y2 = renderer.pad.top + renderer.ch; - } else { - y2 = renderer.pad.top + 12; - } - const color = event.color || targetSeries?.color || "#03a9f4"; - const isHighlighted = highlightedEventIds.has(String(event.id || "")); - const highlightOuterRadius = showIcons ? 18 : 10; - const highlightInnerRadius = showIcons ? 15 : 8; - const outerRadius = showIcons ? 13 : 6; - const innerRadius = showIcons ? 11 : 4; - if (isHighlighted) { - ctx.save(); - ctx.beginPath(); - ctx.arc(x2, y2, highlightOuterRadius, 0, Math.PI * 2); - ctx.fillStyle = hexToRgba(color, 0.18); - ctx.fill(); - ctx.restore(); - ctx.save(); - ctx.beginPath(); - ctx.arc(x2, y2, highlightInnerRadius, 0, Math.PI * 2); - ctx.strokeStyle = color; - ctx.lineWidth = 2; - ctx.stroke(); - ctx.restore(); - } - ctx.save(); - ctx.beginPath(); - ctx.arc(x2, y2, outerRadius, 0, Math.PI * 2); - ctx.fillStyle = "rgba(255,255,255,0.92)"; - ctx.fill(); - ctx.restore(); - ctx.save(); - ctx.beginPath(); - ctx.arc(x2, y2, innerRadius, 0, Math.PI * 2); - ctx.fillStyle = color; - ctx.fill(); - ctx.restore(); - const navigateToHistory = (ev) => { - ev.preventDefault(); - ev.stopPropagation(); - navigateToDataPointsHistory( - this, - { - entity_id: event.entity_ids || [], - device_id: [], - area_id: [], - label_id: [] - }, - { - start_time: this._config?.start_time || null, - end_time: this._config?.end_time || null, - zoom_start_time: this._config?.zoom_start_time || null, - zoom_end_time: this._config?.zoom_end_time || null, - datapoint_scope: this._config?.datapoint_scope - } - ); - }; - if (overlay && showIcons) { - const iconEl = document.createElement("button"); - iconEl.type = "button"; - iconEl.className = "chart-event-icon"; - iconEl.style.left = `${x2}px`; - iconEl.style.top = `${y2 + yOffset}px`; - iconEl.title = event.message || "Open related history"; - iconEl.setAttribute( - "aria-label", - event.message || "Open related history" - ); - iconEl.innerHTML = ``; - iconEl.addEventListener("click", navigateToHistory); - overlay.appendChild(iconEl); - } else if (overlay) { - const hitEl = document.createElement("button"); - hitEl.type = "button"; - hitEl.className = "chart-event-icon"; - hitEl.style.left = `${x2}px`; - hitEl.style.top = `${y2 + yOffset}px`; - hitEl.title = event.message || "Open related history"; - hitEl.setAttribute( - "aria-label", - event.message || "Open related history" - ); - hitEl.addEventListener("click", navigateToHistory); - overlay.appendChild(hitEl); - } - hits.push({ - event, - entityId: targetSeries?.entityId || null, - unit: targetSeries?.unit || "", - value, - x: x2, - y: y2 - }); - } - return hits; - } - /** Get [min, max] extent for an axis value array. */ - _getAxisValueExtent(values) { - return getAxisValueExtent(values); - } - _getComparisonWindowLineStyle(isHovered, isSelected, hoveringDifferentComparison) { - if (isHovered) { - return { - lineOpacity: 1, - dashed: false, - hoverOpacity: 0.85 - }; - } - if (hoveringDifferentComparison && isSelected) { - return { - lineOpacity: 0.25, - lineWidth: 1.25, - dashed: false, - hoverOpacity: 0.25 - }; - } - return { - lineOpacity: 0.85, - dashed: false, - hoverOpacity: 0.85 - }; - } - /** Sync chart viewport scroll to the current zoom range. */ - _syncChartViewportScroll(_t0, _t1, _canvasWidth) { - if (!this._chartScrollViewportEl) { - return; - } - const viewport = this._chartScrollViewportEl; - if (!this._zoomRange) { - if (viewport.scrollLeft !== 0) { - this._scrollSyncSuspended = true; - this._ignoreNextProgrammaticScrollEvent = true; - viewport.scrollLeft = 0; - window.requestAnimationFrame(() => { - this._scrollSyncSuspended = false; - }); - } - return; - } - if (this._skipNextScrollViewportSync) { - this._skipNextScrollViewportSync = false; - return; - } - const viewportWidth = viewport.clientWidth; - const totalMs = Math.max(1, _t1 - _t0); - const visibleSpanMs = totalMs * Math.min(1, viewportWidth / Math.max(_canvasWidth, viewportWidth)); - const maxScrollLeft = Math.max( - 0, - Math.max(_canvasWidth, viewportWidth) - viewportWidth - ); - const maxStartOffsetMs = Math.max(0, totalMs - visibleSpanMs); - const clampedStart = clampChartValue( - this._zoomRange.start, - _t0, - _t1 - visibleSpanMs - ); - const ratio = maxStartOffsetMs > 0 ? (clampedStart - _t0) / maxStartOffsetMs : 0; - const nextLeft = ratio * maxScrollLeft; - const currentLeft = viewport.scrollLeft; - if (Math.abs(currentLeft - nextLeft) < 2) { - return; - } - this._scrollSyncSuspended = true; - this._lastProgrammaticScrollLeft = nextLeft; - this._ignoreNextProgrammaticScrollEvent = true; - viewport.scrollLeft = nextLeft; - window.requestAnimationFrame(() => { - this._scrollSyncSuspended = false; - }); - } - /** Ensure the context annotation dialog element is present. */ - _ensureContextAnnotationDialog() { - const parentCard = this.getRootNode()?.host ?? null; - if (parentCard?._annotationDialog && typeof parentCard._annotationDialog.ensureDialog === "function") { - parentCard._annotationDialog.ensureDialog(); - } - } - /** Open the context annotation dialog with the given hover data. */ - _openContextAnnotationDialog(_hover) { - const parentCard = this.getRootNode()?.host ?? null; - if (parentCard?._annotationDialog && typeof parentCard._annotationDialog.open === "function") { - parentCard._annotationDialog.open( - _hover - ); - } - } - /** Handle context menu on the chart canvas. */ - async _handleChartContextMenu(_hover) { - const parentCard = this.getRootNode()?.host ?? null; - const annotationDialog = parentCard?._annotationDialog; - if (!_hover || !this._hass || annotationDialog?.isOpen?.()) { - return; - } - this._openContextAnnotationDialog(_hover); - } - /** Handle add-annotation click from the chart hover layer. */ - _handleChartAddAnnotation(_hover) { - const parentCard = this.getRootNode()?.host ?? null; - const annotationDialog = parentCard?._annotationDialog; - if (!_hover || !this._hass || !this._hass.user?.is_admin || annotationDialog?.isOpen?.()) { - return; - } - this._openContextAnnotationDialog(_hover); - } - /** Handle anomaly cluster click — opens the annotation dialog prefilled with anomaly details. */ - _handleAnomalyAddAnnotation(_regions) { - const regions = _regions; - if (!regions?.length || !this._hass) return; - const prefill = this._buildAnomalyAnnotationPrefill(regions); - const firstRegion = regions[0]; - const points = firstRegion?.cluster?.points; - const peakPoint = points?.reduce( - (peak, p2) => !peak || Math.abs(p2.residual) > Math.abs(peak.residual) ? p2 : peak, - null - ); - const timeMs = peakPoint?.timeMs ?? this._chartLastHover?.timeMs ?? Date.now(); - this._openContextAnnotationDialog({ timeMs, annotationPrefill: prefill }); - } - /** Build an annotation prefill object from anomaly regions. */ - _buildAnomalyAnnotationPrefill(_regions) { - const regions = _regions; - if (!regions?.length) return {}; - const content = buildAnomalyTooltipContent(regions); - const firstRegion = regions[0]; - const entityId = firstRegion?.relatedEntityId ?? null; - return { - message: content ? `${content.description} -${content.alert}` : "", - icon: "mdi:alert-circle", - linkedTarget: entityId ? { entity_id: [entityId] } : null - }; - } - /** Fire backend anomaly detection requests. */ - _fireBackendAnomalyRequests(_anomalyEntityIds, _analysisMap, _startIso, _endIso) { - if (!_anomalyEntityIds || !_anomalyEntityIds.length || !this._hass) { - return; - } - const hass = this._hass; - _anomalyEntityIds.forEach((entityId) => { - const analysis = _analysisMap.get(entityId); - if (!analysis) { - return; - } - const config = this._buildBackendAnomalyConfig(analysis); - const configKey = JSON.stringify({ - ...config, - anomaly_methods: [ - ...config.anomaly_methods || [] - ].sort() - }); - const cached = this._backendAnomalyByEntity.get(entityId); - if (cached && cached.configKey === configKey) return; - this._pendingAnomalyEntityIds.add(entityId); - this._setChartLoading(true); - fetchAnomaliesFromBackend(hass, entityId, _startIso, _endIso, config).then((clusters) => { - this._pendingAnomalyEntityIds.delete(entityId); - if (_startIso !== new Date(this._lastT0).toISOString() || _endIso !== new Date(this._lastT1).toISOString()) { - if (this._pendingAnomalyEntityIds.size === 0) { - this._setChartLoading(false); - } - return; - } - this._backendAnomalyByEntity.set(entityId, { configKey, clusters }); - if (this._analysisCache?.result) { - const existing = this._analysisCache.result.anomalySeries || []; - const idx = existing.findIndex( - (entry2) => entry2.entityId === entityId - ); - const entry = { entityId, anomalyClusters: clusters }; - if (idx >= 0) { - existing[idx] = entry; - } else { - existing.push(entry); - } - } - if (this._lastHistResult && this._lastEvents) { - this._queueDrawChart( - this._lastHistResult, - this._lastStatsResult || {}, - this._filterEvents(this._lastEvents), - this._lastT0, - this._lastT1, - { loading: this._pendingAnomalyEntityIds.size > 0 } - ); - } else if (this._pendingAnomalyEntityIds.size === 0) { - this._setChartLoading(false); - } - }).catch((err) => { - this._pendingAnomalyEntityIds.delete(entityId); - if (this._pendingAnomalyEntityIds.size === 0) { - this._setChartLoading(false); - } - logger$1.warn( - "[hass-datapoints history-card] backend anomaly fetch failed", - { entityId, err } - ); - }); - }); - } - /** Dispatch a zoom preview event. */ - _dispatchZoomPreview(_range) { - const range = _range; - this.dispatchEvent( - new CustomEvent("hass-datapoints-chart-zoom", { - bubbles: true, - composed: true, - detail: range ? { - startTime: range.startTime, - endTime: range.endTime, - preview: true - } : { startTime: null, endTime: null, preview: true } - }) - ); - } - /** Apply a zoom range to the chart. */ - _applyZoomRange(_startTime2, _endTime2) { - const start = Math.min(_startTime2, _endTime2); - const end = Math.max(_startTime2, _endTime2); - if (!(start < end)) { - return; - } - this._zoomRange = { start, end }; - this.dispatchEvent( - new CustomEvent("hass-datapoints-zoom-apply", { - bubbles: true, - composed: true, - detail: { start, end } - }) - ); - this._redrawLastDraw(); - } - /** Clear the active zoom range. */ - _clearZoomRange() { - if (!this._zoomRange) { - return; - } - this._zoomRange = null; - this.dispatchEvent( - new CustomEvent("hass-datapoints-zoom-apply", { - bubbles: true, - composed: true, - detail: null - }) - ); - this._redrawLastDraw(); - } - /** Filter events list by hidden IDs and message filter. */ - _filterEvents(_events2) { - const events = _events2; - const query = String( - this._config?.message_filter || "" - ).trim().toLowerCase(); - const visibleEvents = events.filter( - (event) => !this._hiddenEventIds.has(event?.id ?? "") - ); - if (!query) { - return visibleEvents; - } - return visibleEvents.filter((event) => { - const haystack = [ - event?.message || "", - event?.annotation || "", - ...(event?.entity_ids || []).filter(Boolean) - ].join("\n").toLowerCase(); - return haystack.includes(query); - }); - } - /** Build correlated anomaly spans across series. */ - _buildCorrelatedAnomalySpans(_visibleSeries, _anomalyClustersMap, _analysisMap) { - const visibleSeries = _visibleSeries; - const anomalyClustersMap = _anomalyClustersMap; - const seriesIntervals = []; - for (const seriesItem of visibleSeries) { - const analysis = _analysisMap.get(seriesItem.entityId); - if (analysis?.show_anomalies !== true) continue; - const clusters = anomalyClustersMap.get(seriesItem.entityId) || []; - if (!clusters.length) continue; - const pts = seriesItem.pts; - let tolerance = 6e4; - if (Array.isArray(pts) && pts.length >= 2) { - const intervals = []; - for (let i2 = 1; i2 < pts.length; i2++) { - const diff = pts[i2][0] - pts[i2 - 1][0]; - if (diff > 0) intervals.push(diff); - } - if (intervals.length) { - intervals.sort((a2, b2) => a2 - b2); - const mid = Math.floor(intervals.length / 2); - tolerance = intervals.length % 2 === 0 ? (intervals[mid - 1] + intervals[mid]) / 2 : intervals[mid]; - tolerance = Math.max(tolerance, 1e3); - } - } - const entityIntervals = []; - for (const cluster of clusters) { - if (!Array.isArray(cluster.points) || cluster.points.length === 0) - continue; - const startTime = cluster.points[0]?.timeMs; - const endTime = cluster.points[cluster.points.length - 1]?.timeMs; - if (!Number.isFinite(startTime) || !Number.isFinite(endTime)) continue; - entityIntervals.push({ - start: Math.min(startTime, endTime) - tolerance, - end: Math.max(startTime, endTime) + tolerance - }); - } - if (entityIntervals.length) { - seriesIntervals.push({ - entityId: seriesItem.entityId, - intervals: entityIntervals - }); - } - } - if (seriesIntervals.length < 2) return []; - const events = []; - for (const { entityId, intervals } of seriesIntervals) { - for (const { start, end } of intervals) { - events.push({ time: start, delta: 1, entityId }); - events.push({ time: end, delta: -1, entityId }); - } - } - events.sort((a2, b2) => a2.time - b2.time || a2.delta - b2.delta); - const activeCounts = /* @__PURE__ */ new Map(); - const spans = []; - let spanStart = null; - for (const event of events) { - const prev = activeCounts.get(event.entityId) || 0; - const next = prev + event.delta; - if (next <= 0) { - activeCounts.delete(event.entityId); - } else { - activeCounts.set(event.entityId, next); - } - const activeCount = activeCounts.size; - if (spanStart === null && activeCount >= 2) { - spanStart = event.time; - } else if (spanStart !== null && activeCount < 2) { - spans.push({ start: spanStart, end: event.time }); - spanStart = null; - } - } - if (spanStart !== null && events.length > 0) { - spans.push({ start: spanStart, end: events[events.length - 1].time }); - } - return spans; - } - /** Filter out annotated anomaly clusters. */ - _filterAnnotatedAnomalyClusters(_seriesItem, _events2) { - const seriesItem = _seriesItem; - if (!Array.isArray(seriesItem?.anomalyClusters) || seriesItem.anomalyClusters.length === 0) { - return []; - } - const visibleEvents = Array.isArray(_events2) ? _events2 : []; - if (visibleEvents.length === 0) { - return seriesItem.anomalyClusters; - } - const getClusterRange = (cluster) => { - if (!Array.isArray(cluster.points) || cluster.points.length === 0) - return null; - const startTime = cluster.points[0]?.timeMs; - const endTime = cluster.points[cluster.points.length - 1]?.timeMs; - if (!Number.isFinite(startTime) || !Number.isFinite(endTime)) return null; - return { - startTime: Math.min(startTime, endTime), - endTime: Math.max(startTime, endTime) - }; - }; - return seriesItem.anomalyClusters.filter((cluster) => { - const clusterRange = getClusterRange(cluster); - if (!clusterRange) return true; - return !visibleEvents.some((event) => { - const eventEntityIds = Array.isArray(event.entity_ids) ? event.entity_ids.filter(Boolean) : []; - if (!eventEntityIds.includes(seriesItem.entityId)) return false; - const eventTime = new Date(event.timestamp).getTime(); - if (!Number.isFinite(eventTime)) return false; - return eventTime >= clusterRange.startTime && eventTime <= clusterRange.endTime; - }); - }); - } - _fireComparisonBackendAnomalyRequests(drawableComparisonResults, analysisMap, renderT0, renderT1) { - if (!drawableComparisonResults.length || !this._hass) { - return; - } - const startIso = new Date(renderT0).toISOString(); - const endIso = new Date(renderT1).toISOString(); - drawableComparisonResults.forEach((comparisonWindow) => { - const comparisonStartIso = new Date( - renderT0 + comparisonWindow.time_offset_ms - ).toISOString(); - const comparisonEndIso = new Date( - renderT1 + comparisonWindow.time_offset_ms - ).toISOString(); - this._seriesSettings.forEach((seriesSetting) => { - const entityId = String(seriesSetting.entity_id || ""); - if (!entityId || this._hiddenSeries.has(entityId)) { - return; - } - const analysis = analysisMap.get(entityId); - if (!analysis || analysis.show_anomalies !== true) { - return; - } - const config = this._buildBackendAnomalyConfig(analysis); - const configKey = JSON.stringify({ - ...config, - windowId: comparisonWindow.id, - startIso, - endIso, - anomaly_methods: [ - ...config.anomaly_methods || [] - ].sort() - }); - const cacheKey = this._getComparisonAnomalyCacheKey( - comparisonWindow.id, - entityId - ); - const cached = this._backendComparisonAnomalyByKey.get(cacheKey); - if (cached && cached.configKey === configKey) { - return; - } - if (this._pendingComparisonAnomalyKeys.has(cacheKey)) { - return; - } - this._pendingComparisonAnomalyKeys.add(cacheKey); - const hass = this._hass; - if (!hass) { - this._pendingComparisonAnomalyKeys.delete(cacheKey); - return; - } - fetchAnomaliesFromBackend( - hass, - entityId, - comparisonStartIso, - comparisonEndIso, - config - ).then((clusters) => { - this._pendingComparisonAnomalyKeys.delete(cacheKey); - if (startIso !== new Date(this._lastT0).toISOString() || endIso !== new Date(this._lastT1).toISOString()) { - return; - } - const shiftedClusters = this._shiftComparisonAnomalyClusters( - Array.isArray(clusters) ? clusters : [], - comparisonWindow.time_offset_ms - ); - this._backendComparisonAnomalyByKey.set(cacheKey, { - configKey, - clusters: shiftedClusters - }); - if (this._lastHistResult && this._lastEvents) { - this._queueDrawChart( - this._lastHistResult, - this._lastStatsResult || {}, - this._filterEvents(this._lastEvents), - this._lastT0, - this._lastT1, - { loading: false } - ); - } - }).catch(() => { - this._pendingComparisonAnomalyKeys.delete(cacheKey); - }); - }); - }); - } - /** Render per-row axis overlays for split-view chart. */ - _renderSplitAxisOverlays(_tracks) { - const tracks = _tracks; - const leftEl = this.querySelector( - "#chart-axis-left" - ); - const rightEl = this.querySelector( - "#chart-axis-right" - ); - if (!leftEl || !rightEl || !tracks.length) { - return; - } - const primaryRenderer = tracks[0].renderer; - const leftWidth = Math.max(0, primaryRenderer.pad.left); - const bottomHeight = Math.max(0, primaryRenderer.pad.bottom); - leftEl.style.width = `${leftWidth}px`; - rightEl.style.width = "0px"; - this.style.setProperty("--dp-chart-axis-left-width", `${leftWidth}px`); - this.style.setProperty("--dp-chart-axis-right-width", "0px"); - this.style.setProperty( - "--dp-chart-axis-bottom-height", - `${bottomHeight}px` - ); - const labelRight = 10; - let labelsHtml = ""; - for (const { renderer, axis, rowOffset } of tracks) { - if (!axis?.ticks?.length) continue; - for (const tick of axis.ticks) { - const y2 = rowOffset + renderer.yOf(tick, axis.min, axis.max); - const formatted = renderer._formatAxisTick(tick, axis.unit); - labelsHtml += `
${esc(formatted)}
`; - } - if (axis.unit) { - const unitY = rowOffset + Math.max(0, primaryRenderer.pad.top - 18); - labelsHtml += `
${esc(axis.unit)}
`; - } - } - leftEl.innerHTML = `
${labelsHtml}`; - leftEl.classList.add("visible"); - rightEl.innerHTML = ""; - rightEl.classList.remove("visible"); - } - // ── State stubs — owned by the parent card / JS layer ───────────────────────── - /** Ordered series settings from the card config. */ - get _seriesSettings() { - return Array.isArray( - this._config?.series_settings - ) ? this._config.series_settings : []; - } - /** Active comparison windows from the card config. */ - get _comparisonWindows() { - return Array.isArray( - this._config?.comparison_windows - ) ? this._config.comparison_windows : []; - } - _getDrawableComparisonResults(comparisonResults) { - const drawableComparisonWindowIds = new Set( - this._comparisonWindows.map( - (window2) => String(window2?.id || "") - ).filter((id) => id.length > 0) - ); - const selectedComparisonWindowId = String( - this._config?.selected_comparison_window_id || "" - ); - const hoveredComparisonWindowId = String( - this._config?.hovered_comparison_window_id || "" - ); - if (selectedComparisonWindowId) { - drawableComparisonWindowIds.add(selectedComparisonWindowId); - } - if (hoveredComparisonWindowId) { - drawableComparisonWindowIds.add(hoveredComparisonWindowId); - } - if (drawableComparisonWindowIds.size === 0) { - return []; - } - return comparisonResults.filter( - (window2) => drawableComparisonWindowIds.has(String(window2.id || "")) - ); - } - _resolveAnomalyClusterDisplay(anomalyClusters, overlapMode, correlatedSpans = []) { - const normalClusters = anomalyClusters.filter( - (c2) => !c2.isOverlap - ); - const overlapClusters = anomalyClusters.filter( - (c2) => c2.isOverlap === true - ); - if (overlapMode === "only") { - const overlapOnlyClusters = this._filterClustersByCorrelatedSpans( - anomalyClusters, - correlatedSpans - ); - return { - baseClusters: overlapOnlyClusters, - regionClusters: overlapOnlyClusters, - showCorrelatedSpans: true - }; - } - return { - baseClusters: [...normalClusters, ...overlapClusters], - regionClusters: [...normalClusters, ...overlapClusters], - showCorrelatedSpans: false - }; - } - _filterClustersByCorrelatedSpans(anomalyClusters, correlatedSpans) { - if (!Array.isArray(anomalyClusters) || anomalyClusters.length === 0) { - return []; - } - if (!Array.isArray(correlatedSpans) || correlatedSpans.length === 0) { - return []; - } - return anomalyClusters.filter((cluster) => { - const points = cluster.points; - if (!Array.isArray(points) || points.length === 0) { - return false; - } - const startTime = Number(points[0]?.timeMs); - const endTime = Number(points[points.length - 1]?.timeMs); - if (!Number.isFinite(startTime) || !Number.isFinite(endTime)) { - return false; - } - const clusterStart = Math.min(startTime, endTime); - const clusterEnd = Math.max(startTime, endTime); - return correlatedSpans.some((span) => { - const spanStart = Number(span.start); - const spanEnd = Number(span.end); - if (!Number.isFinite(spanStart) || !Number.isFinite(spanEnd)) { - return false; - } - return clusterEnd >= spanStart && clusterStart <= spanEnd; - }); - }); - } - _getComparisonAnomalyCacheKey(windowId, entityId) { - return `${windowId}:${entityId}`; - } - _shiftComparisonAnomalyClusters(clusters, timeOffsetMs) { - return (Array.isArray(clusters) ? clusters : []).map((cluster) => ({ - ...cluster, - points: Array.isArray(cluster.points) ? cluster.points.map((point) => ({ - ...point, - timeMs: Number(point.timeMs) - timeOffsetMs - })) : [] - })); - } - async _resolveComparisonWindowPoints(entityId, comparisonWindow, analysis, renderT0, renderT1) { - const stateList = this._buildEntityStateList( - entityId, - comparisonWindow.histResult, - comparisonWindow.statsResult || {} - ); - const rawPoints = []; - for (const state of stateList) { - const value = parseFloat(state.s); - if (!Number.isNaN(value)) { - rawPoints.push([ - Math.round(state.lu * 1e3) - comparisonWindow.time_offset_ms, - value - ]); - } - } - const interval = analysis.sample_interval || "raw"; - if (interval === "raw" || !this._hass) { - return rawPoints; - } - const winStartIso = new Date( - renderT0 + comparisonWindow.time_offset_ms - ).toISOString(); - const winEndIso = new Date( - renderT1 + comparisonWindow.time_offset_ms - ).toISOString(); - const sampledPts = await fetchDownsampledHistory( - this._hass, - entityId, - winStartIso, - winEndIso, - interval, - analysis.sample_aggregate || "mean" - ); - if (!Array.isArray(sampledPts) || sampledPts.length === 0) { - const targetIntervalMs = SAMPLE_INTERVAL_MS[interval] ?? 0; - if (targetIntervalMs <= 0 || rawPoints.length === 0) { - return rawPoints; - } - const sampleAggregate = analysis.sample_aggregate || "mean"; - return downsampleInWorker(rawPoints, targetIntervalMs, sampleAggregate); - } - let finalPts = sampledPts.map( - ([timestamp, value]) => [timestamp - comparisonWindow.time_offset_ms, value] - ); - const statsForEntity = this._normalizeStatisticsHistory( - entityId, - comparisonWindow.statsResult || {} - ); - const statsPts = statsForEntity.map( - (state) => [ - Math.round(state.lu * 1e3) - comparisonWindow.time_offset_ms, - parseFloat(state.s) - ] - ).filter(([, value]) => !Number.isNaN(value)); - if (statsPts.length > 0) { - const firstSampledMs = finalPts[0][0]; - const lastSampledMs = finalPts[finalPts.length - 1][0]; - const rawOutsidePts = statsPts.filter( - ([timestamp]) => timestamp < firstSampledMs || timestamp > lastSampledMs - ); - if (rawOutsidePts.length > 0) { - const targetIntervalMs = SAMPLE_INTERVAL_MS[interval] ?? 0; - const sampleAggregate = analysis.sample_aggregate || "mean"; - const outsideStatsPts = targetIntervalMs > 0 ? await downsampleInWorker( - rawOutsidePts, - targetIntervalMs, - sampleAggregate - ) : rawOutsidePts; - finalPts = [...outsideStatsPts, ...finalPts]; - finalPts.sort((a2, b2) => a2[0] - b2[0]); - } - } - return finalPts; - } - _drawComparisonAnalysisOverlays({ - renderer, - entityId, - seriesColor, - comparisonPts, - analysis, - renderT0, - renderT1, - axis, - rateAxis = null, - events = [], - comparisonWindowId - }) { - const precomputed = this._analysisCache?.result?.comparisonWindowResults?.[comparisonWindowId]?.[entityId]; - if (analysis.show_threshold_analysis === true) { - const thresholdValue = Number(analysis.threshold_value); - if (Number.isFinite(thresholdValue)) { - if (analysis.show_threshold_shading === true && comparisonPts.length) { - renderer.drawThresholdArea( - comparisonPts, - thresholdValue, - seriesColor, - renderT0, - renderT1, - axis.min, - axis.max, - { - mode: analysis.threshold_direction === "below" ? "below" : "above", - fillAlpha: 0.08 - } - ); - } - renderer.drawLine( - [ - [renderT0, thresholdValue], - [renderT1, thresholdValue] - ], - hexToRgba(seriesColor, 0.28), - renderT0, - renderT1, - axis.min, - axis.max, - { lineOpacity: 0.34, lineWidth: 1.05, dashed: true } - ); - } - } - if (analysis.show_summary_stats === true) { - const summaryStats = precomputed?.summaryStats ?? this._buildSummaryStats(comparisonPts); - if (Number.isFinite(summaryStats.min) && Number.isFinite(summaryStats.max) && Number.isFinite(summaryStats.mean)) { - if (analysis.show_summary_stats_shading === true) { - renderer.drawGradientBand( - summaryStats.min, - summaryStats.mean, - seriesColor, - renderT0, - renderT1, - axis.min, - axis.max, - { fillAlpha: 0.04 } - ); - renderer.drawGradientBand( - summaryStats.max, - summaryStats.mean, - seriesColor, - renderT0, - renderT1, - axis.min, - axis.max, - { fillAlpha: 0.04 } - ); - } - const summaryEntries = [ - { value: summaryStats.min, alpha: 0.24, width: 1, dotted: true }, - { value: summaryStats.mean, alpha: 0.44, width: 1.45, dotted: false }, - { value: summaryStats.max, alpha: 0.24, width: 1, dotted: true } - ]; - for (const entry of summaryEntries) { - renderer.drawLine( - [ - [renderT0, entry.value], - [renderT1, entry.value] - ], - hexToRgba(seriesColor, entry.alpha), - renderT0, - renderT1, - axis.min, - axis.max, - { - lineOpacity: entry.alpha + 0.08, - lineWidth: entry.width, - dotted: entry.dotted - } - ); - } - } - } - if (analysis.show_trend_lines === true && comparisonPts.length >= 2) { - const trendPts = precomputed?.trendPts ?? this._buildTrendPoints( - comparisonPts, - analysis.trend_method, - analysis.trend_window - ); - if (trendPts.length >= 2) { - const trendOptions = this._getTrendRenderOptions( - analysis.trend_method, - false - ); - renderer.drawLine( - trendPts, - hexToRgba(seriesColor, Math.max(0.3, trendOptions.colorAlpha - 0.18)), - renderT0, - renderT1, - axis.min, - axis.max, - { - lineOpacity: Math.max(0.3, trendOptions.lineOpacity - 0.18), - lineWidth: Math.max(1.35, trendOptions.lineWidth - 0.35), - dashed: trendOptions.dashed, - dotted: trendOptions.dotted - } - ); - } - } - if (analysis.show_rate_of_change === true && rateAxis && comparisonPts.length >= 2) { - const ratePts = precomputed?.ratePts ?? this._buildRateOfChangePoints(comparisonPts, analysis.rate_window); - if (ratePts.length >= 2) { - renderer.drawLine( - ratePts, - hexToRgba(seriesColor, 0.52), - renderT0, - renderT1, - rateAxis.min, - rateAxis.max, - { - lineOpacity: 0.46, - lineWidth: 1.35, - dashPattern: [6, 3, 1.5, 3] - } - ); - } - } - if (analysis.show_anomalies === true) { - const cacheKey = this._getComparisonAnomalyCacheKey( - comparisonWindowId, - entityId - ); - const cached = this._backendComparisonAnomalyByKey.get(cacheKey); - const clusters = Array.isArray(cached?.clusters) ? cached.clusters : []; - if (clusters.length > 0) { - const regionOptions = { - strokeAlpha: 0.72, - lineWidth: 1.6, - haloWidth: 3.4, - haloColor: "rgba(255,255,255,0.72)", - haloAlpha: 0.64, - fillColor: hexToRgba(seriesColor, 0.06), - fillAlpha: 1, - pointPadding: 8, - minRadiusX: 8, - minRadiusY: 8 - }; - const filteredClusters = this._filterAnnotatedAnomalyClusters( - { - entityId, - anomalyClusters: clusters - }, - events - ); - if (filteredClusters.length > 0) { - renderer.drawAnomalyClusters( - filteredClusters, - hexToRgba(seriesColor, 0.74), - renderT0, - renderT1, - axis.min, - axis.max, - regionOptions - ); - } - } - } - } - /** Backend anomaly cache keyed by entity ID. Already declared above. */ - // _backendAnomalyByEntity — declared above - /** - * Full chart draw implementation. - * Ported from _drawChart in card-history.js. - */ - async _drawChart(histResult, statsResult, events, t0, t1, options = {}) { - const chartEvents = Array.isArray(events) ? events : []; - hideTooltip(this); - this._syncTopSlotOffset(); - const canvas = this.querySelector("#chart"); - const zoomOutButton = this.querySelector( - "#chart-zoom-out" - ); - const wrap = this; - const scrollViewport = this.querySelector( - "#chart-scroll-viewport" - ); - const chartStage = this.querySelector( - "#chart-stage" - ); - this._chartScrollViewportEl = scrollViewport; - this._chartStageEl = chartStage; - const series = []; - const axes = []; - const axisMap = /* @__PURE__ */ new Map(); - const binaryBackgrounds = []; - const seriesSettings = this._seriesSettings; - const analysisMap = this._getSeriesAnalysisMap(); - const comparisonResults = Array.isArray(this._lastComparisonResults) ? this._lastComparisonResults : []; - const drawableComparisonResults = this._getDrawableComparisonResults(comparisonResults); - const selectedComparisonWindowId = this._config?.selected_comparison_window_id || null; - const hoveredComparisonWindowId = this._config?.hovered_comparison_window_id || null; - const comparisonPreviewActive = this._comparisonWindows.length > 0; - const delinkYAxis = this._config?.delink_y_axis === true; - const autoAdjustHoveredComparisonAxis = comparisonPreviewActive && !!hoveredComparisonWindowId && !this._zoomRange && !this._chartZoomDragging; - const hoveringDifferentComparison = !!hoveredComparisonWindowId && !!selectedComparisonWindowId && hoveredComparisonWindowId !== selectedComparisonWindowId; - const hasSelectedComparisonWindow = !!selectedComparisonWindowId; - seriesSettings.forEach((seriesSetting, i2) => { - const entityId = seriesSetting.entity_id; - const domain = entityId.split(".")[0]; - if (domain === "binary_sensor") { - const stateList2 = this._buildEntityStateList( - entityId, - histResult, - statsResult - ); - const spans = this._buildBinaryStateSpans(stateList2, t0, t1); - if (spans.length) { - binaryBackgrounds.push({ - entityId, - label: entityName(this._hass, entityId) || entityId, - color: seriesSetting.color || COLORS[i2 % COLORS.length], - onLabel: this._binaryOnLabel(entityId), - offLabel: this._binaryOffLabel(entityId), - spans - }); - } - return; - } - const stateList = this._buildEntityStateList( - entityId, - histResult, - statsResult - ); - const pts = []; - const unit = this._hass?.states?.[entityId]?.attributes?.unit_of_measurement || ""; - const axisKey = delinkYAxis ? `${unit || "__unitless__"}::${entityId}` : unit || "__unitless__"; - let axis = axisMap.get(axisKey); - if (!axis) { - axis = { - key: axisKey, - unit, - color: seriesSetting.color || COLORS[i2 % COLORS.length], - side: axisMap.size === 0 ? "left" : "right", - values: [] - }; - axisMap.set(axisKey, axis); - axes.push(axis); - } - for (const s2 of stateList) { - const v2 = parseFloat(s2.s); - if (!Number.isNaN(v2)) { - pts.push([Math.round(s2.lu * 1e3), v2]); - axis.values.push(v2); - } - } - if (pts.length) { - series.push({ - entityId, - legendEntityId: entityId, - label: entityName(this._hass, entityId) || entityId, - unit, - pts, - color: seriesSetting.color || COLORS[i2 % COLORS.length], - axisKey - }); - } - }); - const _startIso = new Date(t0).toISOString(); - const _endIso = new Date(t1).toISOString(); - if (series.length && this._hass) { - if (t0 !== this._lastT0 || t1 !== this._lastT1) { - this._backendAnomalyByEntity.clear(); - this._backendComparisonAnomalyByKey.clear(); - } - await Promise.all( - series.map(async (seriesItem) => { - const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); - const interval = analysis.sample_interval || "raw"; - if (interval === "raw") return; - const hass = this._hass; - if (!hass) { - return; - } - try { - const targetIntervalMs = SAMPLE_INTERVAL_MS[interval] ?? 0; - const sampleAggregate = analysis.sample_aggregate || "mean"; - const sampledPts = await fetchDownsampledHistory( - hass, - seriesItem.entityId, - _startIso, - _endIso, - interval, - sampleAggregate - ); - if (Array.isArray(sampledPts) && sampledPts.length > 0) { - const statsForEntity = this._normalizeStatisticsHistory( - seriesItem.entityId, - statsResult - ); - const statsPts = statsForEntity.map( - (s2) => [Math.round(s2.lu * 1e3), parseFloat(s2.s)] - ).filter(([, v2]) => !Number.isNaN(v2)); - let finalPts = sampledPts; - if (statsPts.length > 0) { - const firstSampledMs = sampledPts[0][0]; - const lastSampledMs = sampledPts[sampledPts.length - 1][0]; - const rawOutsidePts = statsPts.filter( - ([t2]) => t2 < firstSampledMs || t2 > lastSampledMs - ); - if (rawOutsidePts.length > 0) { - const outsideStatsPts = targetIntervalMs > 0 ? await downsampleInWorker( - rawOutsidePts, - targetIntervalMs, - sampleAggregate - ) : rawOutsidePts; - finalPts = [...outsideStatsPts, ...sampledPts]; - finalPts.sort((a2, b2) => a2[0] - b2[0]); - } - } - seriesItem.pts = finalPts; - const axisEntry = axes.find( - (ax) => ax.key === seriesItem.axisKey - ); - if (axisEntry) { - axisEntry.values = finalPts.map(([, v2]) => v2).filter(Number.isFinite); - } - } else if (Array.isArray(seriesItem.pts) && seriesItem.pts.length > 0 && targetIntervalMs > 0) { - const fallbackPts = await downsampleInWorker( - seriesItem.pts, - targetIntervalMs, - sampleAggregate - ); - if (fallbackPts.length > 0) { - seriesItem.pts = fallbackPts; - const axisEntry = axes.find( - (ax) => ax.key === seriesItem.axisKey - ); - if (axisEntry) { - axisEntry.values = fallbackPts.map(([, v2]) => v2).filter(Number.isFinite); - } - } - } - } catch (err) { - logger$1.warn( - "[hass-datapoints history-card] downsampled history fetch failed", - { entityId: seriesItem.entityId, err } - ); - } - }) - ); - } - for (const seriesItem of series) { - if (!seriesItem.pts.length) { - continue; - } - const lastPt = seriesItem.pts[seriesItem.pts.length - 1]; - const prev = this._previousSeriesEndpoints.get(seriesItem.entityId); - if (!prev) { - logger$1.log("[hass-datapoints history-card] series initial draw", { - entityId: seriesItem.entityId, - pointCount: seriesItem.pts.length, - lastPt - }); - } else if (lastPt[0] !== prev.t || lastPt[1] !== prev.v) { - logger$1.log( - "[hass-datapoints history-card] series updated — live update detected", - { - entityId: seriesItem.entityId, - pointCount: seriesItem.pts.length, - prev, - lastPt - } - ); - } else { - logger$1.log( - "[hass-datapoints history-card] series unchanged — no new data", - { - entityId: seriesItem.entityId, - pointCount: seriesItem.pts.length, - lastPt - } - ); - } - this._previousSeriesEndpoints.set(seriesItem.entityId, { - t: lastPt[0], - v: lastPt[1] - }); - } - if (!series.length && !binaryBackgrounds.length) { - this._setAdjustAxisButtonVisibility(false); - this._renderComparisonPreviewOverlay(); - const sameRangeAsLastDraw = Number.isFinite(this._lastT0) && Number.isFinite(this._lastT1) && this._lastT0 === t0 && this._lastT1 === t1 && Array.isArray(this._lastDrawArgs) && this._lastDrawArgs.length > 0; - this._setChartLoading(!!options.loading); - this._setChartMessage( - options.loading ? "" : "No numeric data in the selected time range." - ); - if (sameRangeAsLastDraw) { - return; - } - this._lastHistResult = histResult; - this._lastStatsResult = statsResult; - this._lastEvents = chartEvents; - this._lastT0 = t0; - this._lastT1 = t1; - this._lastDrawArgs = [ - histResult, - statsResult, - chartEvents, - t0, - t1, - options - ]; - return; - } - this._lastHistResult = histResult; - this._lastStatsResult = statsResult; - this._lastEvents = chartEvents; - this._lastT0 = t0; - this._lastT1 = t1; - this._lastDrawArgs = [ - histResult, - statsResult, - chartEvents, - t0, - t1, - options - ]; - const visibleSeries = series.filter( - (entry) => !this._hiddenSeries.has(entry.legendEntityId || entry.entityId) - ); - const selectedComparisonResult = drawableComparisonResults.find( - (window2) => window2.id === selectedComparisonWindowId - ) || null; - const selectedComparisonSeriesMap = /* @__PURE__ */ new Map(); - if (selectedComparisonResult) { - for (let index = 0; index < seriesSettings.length; index += 1) { - const seriesSetting = seriesSettings[index]; - const entityId = seriesSetting.entity_id; - if (entityId.split(".")[0] === "binary_sensor") { - continue; - } - if (this._hiddenSeries.has(entityId)) { - continue; - } - const unit = this._hass?.states?.[entityId]?.attributes?.unit_of_measurement || ""; - const analysis = analysisMap.get(entityId) || normalizeHistorySeriesAnalysis(null); - const points = await this._resolveComparisonWindowPoints( - entityId, - selectedComparisonResult, - analysis, - t0, - t1 - ); - if (!points.length) { - continue; - } - selectedComparisonSeriesMap.set(entityId, { - entityId, - label: entityName(this._hass, entityId) || entityId, - unit, - color: seriesSetting.color || COLORS[index % COLORS.length], - pts: points - }); - } - } - const allComparisonWindowsData = {}; - for (const seriesItem of visibleSeries) { - const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); - if (analysis.show_anomalies !== true || !analysis.anomaly_methods?.includes( - "comparison_window" - ) || !analysis.anomaly_comparison_window_id) { - continue; - } - const windowId = analysis.anomaly_comparison_window_id; - if (!allComparisonWindowsData[windowId]) { - allComparisonWindowsData[windowId] = {}; - } - if (!allComparisonWindowsData[windowId][seriesItem.entityId]) { - const compResult = comparisonResults.find((win) => win.id === windowId); - if (compResult) { - const pts = await this._resolveComparisonWindowPoints( - seriesItem.entityId, - compResult, - analysis, - t0, - t1 - ); - if (pts.length) { - allComparisonWindowsData[windowId][seriesItem.entityId] = pts; - } - } - } - } - const analysisEntityIds = visibleSeries.filter((s2) => { - const a2 = analysisMap.get(s2.entityId) || {}; - return a2.show_trend_lines || a2.show_summary_stats || a2.show_rate_of_change; - }).map((s2) => s2.entityId); - const onAnalysisProgress = analysisEntityIds.length ? (progress) => { - this.dispatchEvent( - new CustomEvent("hass-datapoints-analysis-computing", { - bubbles: true, - composed: true, - detail: { - computing: true, - entityIds: analysisEntityIds, - progress - } - }) - ); - } : null; - const analysisResult = await this._computeHistoryAnalysis( - visibleSeries, - selectedComparisonSeriesMap, - analysisMap, - hasSelectedComparisonWindow, - allComparisonWindowsData, - t0, - t1, - onAnalysisProgress - ); - if (analysisEntityIds.length) { - this.dispatchEvent( - new CustomEvent("hass-datapoints-analysis-computing", { - bubbles: true, - composed: true, - detail: { - computing: false, - entityIds: analysisEntityIds, - progress: 100 - } - }) - ); - } - const _anomalyEntityIds = visibleSeries.filter((s2) => { - const a2 = analysisMap.get(s2.entityId); - return a2 && a2.show_anomalies === true && Array.isArray(a2.anomaly_methods) && a2.anomaly_methods.length > 0; - }).map((s2) => s2.entityId); - if (_anomalyEntityIds.length > 0) { - const merged2 = _anomalyEntityIds.map((entityId) => { - const cached = this._backendAnomalyByEntity.get(entityId); - if (!cached) return null; - return { entityId, anomalyClusters: cached.clusters }; - }).filter(Boolean); - analysisResult.anomalySeries = merged2; - } - if (options.drawRequestId && options.drawRequestId !== this._drawRequestId) { - return; - } - if (canvas) { - canvas.style.display = ""; - } - chartStage?.querySelectorAll(".split-series-row").forEach((el) => el.remove()); - chartStage?.querySelector("#chart-split-overlay")?.remove(); - const axisLeftEl = this.querySelector( - "#chart-axis-left" - ); - const axisRightEl = this.querySelector( - "#chart-axis-right" - ); - if (axisLeftEl) { - axisLeftEl.style.display = ""; - } - if (axisRightEl) { - axisRightEl.style.display = ""; - } - let minChartHeight; - if (series.length) { - minChartHeight = 280; - } else if (binaryBackgrounds.length) { - minChartHeight = 100; - } else { - minChartHeight = 280; - } - const availableHeight = this._getAvailableChartHeight(minChartHeight); - const viewportWidth = Math.max( - scrollViewport?.clientWidth || wrap?.clientWidth || 360, - 360 - ); - const totalSpanMs = Math.max(1, t1 - t0); - const zoomSpanMs = this._zoomRange ? Math.max(1, this._zoomRange.end - this._zoomRange.start) : null; - const rawZoomMultiplier = zoomSpanMs ? totalSpanMs / zoomSpanMs : 1; - const zoomMultiplier = clampChartValue( - rawZoomMultiplier, - 1, - HISTORY_CHART_MAX_ZOOM_MULTIPLIER - ); - const canvasWidth = Math.min( - HISTORY_CHART_MAX_CANVAS_WIDTH_PX, - zoomSpanMs ? Math.max(viewportWidth, Math.round(viewportWidth * zoomMultiplier)) : viewportWidth - ); - if (this._config?.split_view === true && visibleSeries.length >= 2) { - if (zoomOutButton) { - zoomOutButton.hidden = !this._zoomRange; - zoomOutButton.onclick = () => this._clearZoomRange(); - } - this._renderLegend(series, binaryBackgrounds); - await this._drawSplitChart({ - visibleSeries, - binaryBackgrounds, - events, - renderT0: t0, - renderT1: t1, - canvasWidth, - availableHeight, - chartStage, - canvas, - wrap, - options, - drawableComparisonResults, - selectedComparisonWindowId, - hoveredComparisonWindowId, - comparisonPreviewActive, - hoveringDifferentComparison, - analysisResult, - analysisMap, - hasSelectedComparisonWindow - }); - if (this._chartScrollViewportEl) { - this._chartScrollViewportEl.removeEventListener( - "scroll", - this._onChartScroll - ); - this._chartScrollViewportEl.addEventListener( - "scroll", - this._onChartScroll, - { passive: true } - ); - this._syncChartViewportScroll(t0, t1, canvasWidth); - } - this._fireBackendAnomalyRequests( - _anomalyEntityIds, - analysisMap, - _startIso, - _endIso - ); - return; - } - if (chartStage) { - chartStage.style.width = `${canvasWidth}px`; - chartStage.style.height = `${availableHeight}px`; - } - if (scrollViewport) { - scrollViewport.style.overflowY = ""; - } - const { w, h: h2 } = setupCanvas(canvas, chartStage || wrap, availableHeight, canvasWidth); - const renderer = new ChartRenderer(canvas, w, h2); - renderer.labelColor = resolveChartLabelColor(this); - renderer.clear(); - const renderT0 = t0; - const renderT1 = t1; - const trendPointsMap = new Map( - (analysisResult?.trendSeries || []).map((entry) => [ - entry.entityId, - entry.pts - ]) - ); - const ratePointsMap = new Map( - (analysisResult?.rateSeries || []).map((entry) => [ - entry.entityId, - entry.pts - ]) - ); - const deltaPointsMap = new Map( - (analysisResult?.deltaSeries || []).map((entry) => [ - entry.entityId, - entry.pts - ]) - ); - const summaryStatsMap = new Map( - (analysisResult?.summaryStats || []).map((entry) => [ - entry.entityId, - entry - ]) - ); - const anomalyClustersMap = new Map( - (analysisResult?.anomalySeries || []).map((entry) => [ - entry.entityId, - entry.anomalyClusters - ]) - ); - const hiddenSourceEntityIds = /* @__PURE__ */ new Set(); - const hiddenComparisonEntityIds = /* @__PURE__ */ new Set(); - visibleSeries.forEach((seriesItem) => { - const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); - if (this._seriesShouldHideSource( - analysis, - hasSelectedComparisonWindow - )) { - hiddenSourceEntityIds.add(seriesItem.entityId); - } - if (analysis.hide_source_series === true && analysis.show_delta_analysis === true && hasSelectedComparisonWindow) { - hiddenComparisonEntityIds.add(seriesItem.entityId); - } - }); - const anyTrendCrosshairs = visibleSeries.some((seriesItem) => { - const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); - return analysis.show_trend_lines === true && analysis.show_trend_crosshairs === true; - }); - const anyRateCrosshairs = visibleSeries.some((seriesItem) => { - const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); - return analysis.show_rate_of_change === true && analysis.show_rate_crosshairs === true; - }); - this._setChartLoading(!!options.loading); - this._setChartMessage(""); - if (zoomOutButton) { - zoomOutButton.hidden = !this._zoomRange; - zoomOutButton.onclick = () => this._clearZoomRange(); - } - const comparisonAxisValues = /* @__PURE__ */ new Map(); - if (this._adjustComparisonAxisScale || autoAdjustHoveredComparisonAxis) { - for (const win of drawableComparisonResults) { - if (autoAdjustHoveredComparisonAxis && hoveredComparisonWindowId && win.id !== hoveredComparisonWindowId) { - continue; - } - for (const seriesSetting of seriesSettings) { - const entityId = seriesSetting.entity_id; - if (entityId.split(".")[0] === "binary_sensor") { - continue; - } - if (this._hiddenSeries.has(entityId)) { - continue; - } - const unit = this._hass?.states?.[entityId]?.attributes?.unit_of_measurement || ""; - const axisKey = delinkYAxis ? `${unit || "__unitless__"}::${entityId}` : unit || "__unitless__"; - const analysis = analysisMap.get(entityId) || normalizeHistorySeriesAnalysis(null); - const points = await this._resolveComparisonWindowPoints( - entityId, - win, - analysis, - renderT0, - renderT1 - ); - for (const [, numericValue] of points) { - if (!comparisonAxisValues.has(axisKey)) { - comparisonAxisValues.set(axisKey, []); - } - comparisonAxisValues.get(axisKey).push(numericValue); - } - } - } - } - const deltaAxisMap = /* @__PURE__ */ new Map(); - const rateAxisMap = /* @__PURE__ */ new Map(); - visibleSeries.forEach((seriesItem) => { - const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); - if (analysis.show_delta_analysis === true && hasSelectedComparisonWindow && analysis.show_delta_lines === true) { - const deltaPoints = deltaPointsMap.get(seriesItem.entityId) || []; - if (deltaPoints.length) { - const axisKey = `delta:${seriesItem.axisKey}`; - const unitLabel = seriesItem.unit ? `Δ ${seriesItem.unit}` : "Δ"; - let axis = deltaAxisMap.get(axisKey); - if (!axis) { - axis = { - key: axisKey, - unit: unitLabel, - color: seriesItem.color, - side: "right", - values: [] - }; - deltaAxisMap.set(axisKey, axis); - } - deltaPoints.forEach((point) => { - axis.values.push(point[1]); - }); - } - } - if (analysis.show_rate_of_change === true) { - const ratePoints = ratePointsMap.get(seriesItem.entityId) || []; - if (ratePoints.length) { - const axisKey = `rate:${seriesItem.axisKey}`; - const unitLabel = seriesItem.unit ? `${seriesItem.unit}/h` : "Rate/h"; - let axis = rateAxisMap.get(axisKey); - if (!axis) { - axis = { - key: axisKey, - unit: unitLabel, - color: seriesItem.color, - side: "right", - values: [] - }; - rateAxisMap.set(axisKey, axis); - } - ratePoints.forEach((point) => { - axis.values.push(point[1]); - }); - } - } - }); - const resolvedAxes = axes.filter((axis) => axis.values.length).map((axis) => { - const axisValues = series.filter((entry) => entry.axisKey === axis.key).flatMap((entry) => entry.pts.map((point) => point[1])); - if ((this._adjustComparisonAxisScale || autoAdjustHoveredComparisonAxis) && comparisonAxisValues.has(axis.key)) { - axisValues.push(...comparisonAxisValues.get(axis.key)); - } - const extent = this._getAxisValueExtent(axisValues); - if (!extent) { - return null; - } - const { min, max } = extent; - const pad = (max - min) * 0.1 || 1; - return { - key: axis.key, - unit: axis.unit, - color: axis.color, - side: axis.side === "right" ? "right" : "left", - min: min - pad, - max: max + pad - }; - }).filter((ax) => ax !== null); - const deltaResolvedAxes = Array.from(deltaAxisMap.values()).filter((axis) => axis.values.length).map((axis) => { - const extent = this._getAxisValueExtent(axis.values); - if (!extent) { - return null; - } - const { min, max } = extent; - const pad = (max - min) * 0.1 || 1; - return { - key: axis.key, - unit: axis.unit, - color: axis.color, - side: axis.side === "right" ? "right" : "left", - min: min - pad, - max: max + pad - }; - }).filter((ax) => ax !== null); - const rateResolvedAxes = Array.from(rateAxisMap.values()).filter((axis) => axis.values.length).map((axis) => { - const extent = this._getAxisValueExtent(axis.values); - if (!extent) { - return null; - } - const { min, max } = extent; - const pad = (max - min) * 0.1 || 1; - return { - key: axis.key, - unit: axis.unit, - color: axis.color, - side: axis.side === "right" ? "right" : "left", - min: min - pad, - max: max + pad - }; - }).filter((ax) => ax !== null); - const gridAxes = resolvedAxes.length || deltaResolvedAxes.length ? [...resolvedAxes, ...deltaResolvedAxes, ...rateResolvedAxes] : [ - { - key: "binary", - min: 0, - max: 1, - side: "left", - unit: "", - color: null - } - ]; - renderer.drawGrid(renderT0, renderT1, gridAxes, void 0, 5, { - fixedAxisOverlay: true - }); - this._renderComparisonPreviewOverlay( - renderer - ); - const activeAxes = resolvedAxes.length ? renderer._activeAxes || [] : []; - const axisLookup = new Map( - activeAxes.map((axis) => [axis.key, axis]) - ); - series.forEach((s2) => { - s2.axis = axisLookup.get(s2.axisKey) || activeAxes[0] || resolvedAxes[0]; - }); - renderChartAxisOverlays(this, renderer, activeAxes); - this.style.setProperty( - "--dp-chart-axis-bottom-height", - `${Math.max(0, renderer.pad.bottom)}px` - ); - binaryBackgrounds.forEach((binaryBackground) => { - if (binaryBackground?.spans?.length && !this._hiddenSeries.has(binaryBackground.entityId)) { - renderer.drawStateBands( - binaryBackground.spans, - renderT0, - renderT1, - binaryBackground.color, - 0.12 - ); - } - }); - const shouldUseCorrelatedSpans = this._config?.show_correlated_anomalies === true || this._config?.anomaly_overlap_mode === "only"; - const shouldDrawCorrelatedSpans = this._config?.show_correlated_anomalies === true; - const correlatedSpans = shouldUseCorrelatedSpans ? this._buildCorrelatedAnomalySpans( - visibleSeries, - anomalyClustersMap, - analysisMap - ) : []; - if (shouldDrawCorrelatedSpans) { - if (correlatedSpans.length) { - renderer.drawStateBands( - correlatedSpans, - renderT0, - renderT1, - "#ef4444", - 0.1 - ); - } - } - let comparisonOutOfBounds = false; - let mainSeriesHoverOpacity; - if (!comparisonPreviewActive) { - mainSeriesHoverOpacity = 1; - } else if (hoveringDifferentComparison) { - mainSeriesHoverOpacity = 0.15; - } else { - mainSeriesHoverOpacity = 0.25; - } - const anyHiddenSourceSeries = hiddenSourceEntityIds.size > 0; - const hoverSeries = visibleSeries.filter((seriesItem) => !hiddenSourceEntityIds.has(seriesItem.entityId)).map((seriesItem) => ({ - ...seriesItem, - hoverOpacity: mainSeriesHoverOpacity - })); - const summaryHoverSeries = visibleSeries.flatMap((seriesItem) => { - const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); - if (analysis.show_summary_stats !== true) { - return []; - } - const stats = summaryStatsMap.get(seriesItem.entityId) || null; - if (!stats) { - return []; - } - const seriesWithAxis = seriesItem; - return [ - { - entityId: `summary:min:${seriesItem.entityId}`, - relatedEntityId: seriesItem.entityId, - label: seriesItem.label, - baseLabel: seriesItem.label, - unit: seriesItem.unit || "", - value: stats.min, - color: hexToRgba( - seriesItem.color, - anyHiddenSourceSeries ? 0.94 : 0.78 - ), - baseColor: seriesItem.color, - axis: seriesWithAxis.axis, - hoverOpacity: anyHiddenSourceSeries ? 0.94 : 0.72, - summaryType: "min", - summary: true - }, - { - entityId: `summary:mean:${seriesItem.entityId}`, - relatedEntityId: seriesItem.entityId, - label: seriesItem.label, - baseLabel: seriesItem.label, - unit: seriesItem.unit || "", - value: stats.mean, - color: hexToRgba( - seriesItem.color, - anyHiddenSourceSeries ? 0.94 : 0.78 - ), - baseColor: seriesItem.color, - axis: seriesWithAxis.axis, - hoverOpacity: anyHiddenSourceSeries ? 0.94 : 0.72, - summaryType: "mean", - summary: true - }, - { - entityId: `summary:max:${seriesItem.entityId}`, - relatedEntityId: seriesItem.entityId, - label: seriesItem.label, - baseLabel: seriesItem.label, - unit: seriesItem.unit || "", - value: stats.max, - color: hexToRgba( - seriesItem.color, - anyHiddenSourceSeries ? 0.94 : 0.78 - ), - baseColor: seriesItem.color, - axis: seriesWithAxis.axis, - hoverOpacity: anyHiddenSourceSeries ? 0.94 : 0.72, - summaryType: "max", - summary: true - } - ]; - }); - const thresholdHoverSeries = visibleSeries.flatMap((seriesItem) => { - const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); - if (analysis.show_threshold_analysis !== true) { - return []; - } - const rawThreshold = analysis.threshold_value; - const thresholdValue = Number(rawThreshold); - if (!Number.isFinite(thresholdValue)) { - return []; - } - const seriesWithAxis = seriesItem; - return [ - { - entityId: `threshold:${seriesItem.entityId}`, - relatedEntityId: seriesItem.entityId, - label: seriesItem.label, - baseLabel: seriesItem.label, - unit: seriesItem.unit || "", - value: thresholdValue, - baseColor: seriesItem.color, - color: hexToRgba( - seriesItem.color, - anyHiddenSourceSeries ? 0.82 : 0.46 - ), - axis: seriesWithAxis.axis, - hoverOpacity: anyHiddenSourceSeries ? 0.84 : 0.48, - direction: analysis.threshold_direction === "below" ? "below" : "above", - threshold: true - } - ]; - }); - const trendSeries = visibleSeries.map((seriesItem) => ({ - ...seriesItem, - trendPts: trendPointsMap.get(seriesItem.entityId) || [] - })).filter( - (seriesItem) => Array.isArray(seriesItem.trendPts) && seriesItem.trendPts.length >= 2 - ); - const rateSeries = visibleSeries.map((seriesItem) => { - const ratePoints = ratePointsMap.get(seriesItem.entityId) || []; - if (!Array.isArray(ratePoints) || ratePoints.length < 2) { - return null; - } - const axis = axisLookup.get(`rate:${seriesItem.axisKey}`); - if (!axis) { - return null; - } - return { - ...seriesItem, - ratePts: ratePoints, - rateAxis: axis - }; - }).filter((s2) => s2 !== null); - const anomalySeries = visibleSeries.map((seriesItem) => { - const anomalyClusters = anomalyClustersMap.get(seriesItem.entityId) || []; - if (!Array.isArray(anomalyClusters) || anomalyClusters.length === 0) { - return null; - } - return { - ...seriesItem, - anomalyClusters - }; - }).filter((s2) => s2 !== null); - const comparisonHoverSeries = []; - const comparisonTrendHoverSeries = []; - const comparisonRateHoverSeries = []; - const comparisonSummaryHoverSeries = []; - const comparisonThresholdHoverSeries = []; - const deltaHoverSeries = []; - for (const win of drawableComparisonResults) { - for (const seriesSetting of seriesSettings) { - const entityId = seriesSetting.entity_id; - if (entityId.split(".")[0] === "binary_sensor") { - continue; - } - if (this._hiddenSeries.has(entityId)) { - continue; - } - const analysis = analysisMap.get(entityId) || normalizeHistorySeriesAnalysis(null); - const winPts = await this._resolveComparisonWindowPoints( - entityId, - win, - analysis, - renderT0, - renderT1 - ); - if (!winPts.length) { - continue; - } - const unit = this._hass?.states?.[entityId]?.attributes?.unit_of_measurement || ""; - const axisKey = delinkYAxis ? `${unit || "__unitless__"}::${entityId}` : unit || "__unitless__"; - const axis = axisLookup.get(axisKey); - if (!axis) continue; - if (!this._adjustComparisonAxisScale && !autoAdjustHoveredComparisonAxis) { - const hasOutOfBoundsPoint = winPts.some( - (point) => point[1] < axis.min || point[1] > axis.max - ); - if (hasOutOfBoundsPoint) { - comparisonOutOfBounds = true; - } - } - const baseColor = seriesSetting.color || COLORS[seriesSettings.indexOf(seriesSetting) % COLORS.length]; - const isHoveredComparison = !!hoveredComparisonWindowId && win.id === hoveredComparisonWindowId; - const isSelectedComparison = !!selectedComparisonWindowId && win.id === selectedComparisonWindowId; - const comparisonLineStyle = this._getComparisonWindowLineStyle( - isHoveredComparison, - isSelectedComparison, - hoveringDifferentComparison - ); - comparisonHoverSeries.push({ - entityId: `${win.id}:${entityId}`, - relatedEntityId: entityId, - label: seriesSetting.label || entityName(this._hass, entityId) || entityId, - windowLabel: win.label || "Date window", - unit, - pts: winPts, - color: baseColor, - axis, - hoverOpacity: comparisonLineStyle.hoverOpacity - }); - if (analysis.show_trend_lines === true && winPts.length >= 2) { - const winPrecomputed = this._analysisCache?.result?.comparisonWindowResults?.[win.id]?.[entityId]; - const trendPts = winPrecomputed?.trendPts ?? this._buildTrendPoints( - winPts, - analysis.trend_method, - analysis.trend_window - ); - if (trendPts.length >= 2) { - const trendOptions = this._getTrendRenderOptions( - analysis.trend_method, - false - ); - comparisonTrendHoverSeries.push({ - entityId: `trend:${win.id}:${entityId}`, - relatedEntityId: entityId, - comparisonParentId: `${win.id}:${entityId}`, - label: seriesSetting.label || entityName(this._hass, entityId) || entityId, - baseLabel: seriesSetting.label || entityName(this._hass, entityId) || entityId, - windowLabel: win.label || "Date window", - unit, - pts: trendPts, - color: hexToRgba( - baseColor, - Math.max(0.3, trendOptions.colorAlpha - 0.18) - ), - axis, - rawVisible: true, - hoverOpacity: Math.max(0.3, trendOptions.lineOpacity - 0.18), - trend: true - }); - } - } - if (analysis.show_rate_of_change === true && winPts.length >= 2) { - const winPrecomputed = this._analysisCache?.result?.comparisonWindowResults?.[win.id]?.[entityId]; - const ratePts = winPrecomputed?.ratePts ?? this._buildRateOfChangePoints(winPts, analysis.rate_window); - const rateAxis2 = axisLookup.get(`rate:${axisKey}`) || axis; - if (ratePts.length >= 2) { - comparisonRateHoverSeries.push({ - entityId: `rate:${win.id}:${entityId}`, - relatedEntityId: entityId, - comparisonParentId: `${win.id}:${entityId}`, - label: seriesSetting.label || entityName(this._hass, entityId) || entityId, - baseLabel: seriesSetting.label || entityName(this._hass, entityId) || entityId, - windowLabel: win.label || "Date window", - unit: unit ? `${unit}/h` : "/h", - pts: ratePts, - color: hexToRgba(baseColor, 0.52), - axis: rateAxis2, - rawVisible: true, - hoverOpacity: 0.46, - rate: true - }); - } - } - if (analysis.show_summary_stats === true) { - const winPrecomputed = this._analysisCache?.result?.comparisonWindowResults?.[win.id]?.[entityId]; - const summaryStats = winPrecomputed?.summaryStats ?? this._buildSummaryStats(winPts); - [ - { type: "min", value: summaryStats.min }, - { type: "mean", value: summaryStats.mean }, - { type: "max", value: summaryStats.max } - ].forEach((entry) => { - if (!Number.isFinite(entry.value)) { - return; - } - comparisonSummaryHoverSeries.push({ - entityId: `summary:${entry.type}:${win.id}:${entityId}`, - relatedEntityId: entityId, - comparisonParentId: `${win.id}:${entityId}`, - label: seriesSetting.label || entityName(this._hass, entityId) || entityId, - baseLabel: seriesSetting.label || entityName(this._hass, entityId) || entityId, - windowLabel: win.label || "Date window", - unit, - value: entry.value, - color: hexToRgba(baseColor, entry.type === "mean" ? 0.44 : 0.24), - axis, - rawVisible: true, - hoverOpacity: entry.type === "mean" ? 0.52 : 0.3, - summaryType: entry.type, - summary: true - }); - }); - } - if (analysis.show_threshold_analysis === true) { - const thresholdValue = Number(analysis.threshold_value); - if (Number.isFinite(thresholdValue)) { - comparisonThresholdHoverSeries.push({ - entityId: `threshold:${win.id}:${entityId}`, - relatedEntityId: entityId, - comparisonParentId: `${win.id}:${entityId}`, - label: seriesSetting.label || entityName(this._hass, entityId) || entityId, - baseLabel: seriesSetting.label || entityName(this._hass, entityId) || entityId, - windowLabel: win.label || "Date window", - unit, - value: thresholdValue, - color: hexToRgba(baseColor, 0.28), - axis, - rawVisible: true, - hoverOpacity: 0.34, - threshold: true - }); - } - } - if (!hiddenComparisonEntityIds.has(entityId)) { - renderer.drawLine( - winPts, - baseColor, - renderT0, - renderT1, - axis.min, - axis.max, - { - lineOpacity: comparisonLineStyle.lineOpacity, - lineWidth: comparisonLineStyle.lineWidth, - dashed: comparisonLineStyle.dashed, - dashPattern: comparisonLineStyle.dashPattern, - stepped: analysis.stepped_series === true - } - ); - } - const rateAxis = axisLookup.get(`rate:${axisKey}`) || null; - this._drawComparisonAnalysisOverlays({ - renderer, - entityId, - seriesColor: baseColor, - comparisonPts: winPts, - analysis, - renderT0, - renderT1, - axis, - rateAxis, - events, - comparisonWindowId: win.id - }); - } - } - this._setAdjustAxisButtonVisibility( - comparisonPreviewActive && comparisonOutOfBounds && !this._adjustComparisonAxisScale && !autoAdjustHoveredComparisonAxis - ); - for (const s2 of visibleSeries) { - if (hiddenSourceEntityIds.has(s2.entityId)) { - continue; - } - const sWithAxis = s2; - this._drawSeriesLine( - renderer, - s2.pts, - s2.color, - renderT0, - renderT1, - sWithAxis.axis.min, - sWithAxis.axis.max, - { - lineOpacity: mainSeriesHoverOpacity, - lineWidth: this._config?.comparison_hover_active === true ? 1.25 : void 0, - stepped: (analysisMap.get(s2.entityId) || normalizeHistorySeriesAnalysis(null)).stepped_series === true - } - ); - } - const trendHoverSeries = trendSeries.map((seriesItem) => { - const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); - const hiddenSource = hiddenSourceEntityIds.has(seriesItem.entityId); - const trendRenderOptions = this._getTrendRenderOptions( - analysis.trend_method, - hiddenSource - ); - const seriesWithAxis = seriesItem; - return { - entityId: `trend:${seriesItem.entityId}`, - relatedEntityId: seriesItem.entityId, - label: seriesItem.label, - baseLabel: seriesItem.label, - unit: seriesItem.unit || "", - pts: seriesItem.trendPts, - color: hexToRgba(seriesItem.color, trendRenderOptions.colorAlpha), - axis: seriesWithAxis.axis, - rawVisible: !hiddenSource, - showCrosshair: analysis.show_trend_crosshairs === true, - hoverOpacity: comparisonPreviewActive ? Math.max(0.25, Math.min(0.9, mainSeriesHoverOpacity + 0.12)) : trendRenderOptions.lineOpacity, - trend: true - }; - }); - const rateHoverSeries = rateSeries.map((seriesItem) => ({ - entityId: `rate:${seriesItem.entityId}`, - relatedEntityId: seriesItem.entityId, - label: seriesItem.label, - baseLabel: seriesItem.label, - unit: seriesItem.unit ? `${seriesItem.unit}/h` : "/h", - pts: seriesItem.ratePts, - color: hexToRgba(seriesItem.color, anyHiddenSourceSeries ? 0.9 : 0.72), - axis: seriesItem.rateAxis, - rawVisible: !hiddenSourceEntityIds.has(seriesItem.entityId), - showCrosshair: (analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null)).show_rate_crosshairs === true, - hoverOpacity: anyHiddenSourceSeries ? 0.88 : 0.66, - rate: true - })); - for (const trend of trendSeries) { - const analysis = analysisMap.get(trend.entityId) || normalizeHistorySeriesAnalysis(null); - const trendRenderOptions = this._getTrendRenderOptions( - analysis.trend_method, - hiddenSourceEntityIds.has(trend.entityId) - ); - const trendWithAxis = trend; - renderer.drawLine( - trend.trendPts, - hexToRgba(trend.color, trendRenderOptions.colorAlpha), - renderT0, - renderT1, - trendWithAxis.axis.min, - trendWithAxis.axis.max, - { - lineOpacity: comparisonPreviewActive ? Math.max(0.25, Math.min(0.9, mainSeriesHoverOpacity + 0.12)) : trendRenderOptions.lineOpacity, - lineWidth: trendRenderOptions.lineWidth, - dashed: trendRenderOptions.dashed, - dotted: trendRenderOptions.dotted - } - ); - } - for (const rateSeriesItem of rateSeries) { - renderer.drawLine( - rateSeriesItem.ratePts, - hexToRgba(rateSeriesItem.color, anyHiddenSourceSeries ? 0.96 : 0.82), - renderT0, - renderT1, - rateSeriesItem.rateAxis.min, - rateSeriesItem.rateAxis.max, - { - lineOpacity: anyHiddenSourceSeries ? 0.88 : 0.66, - lineWidth: 1.55, - dashed: false, - dotted: false, - dashPattern: [7, 3, 1.5, 3] - } - ); - } - for (const seriesItem of visibleSeries) { - const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); - if (!(analysis.show_delta_analysis === true && hasSelectedComparisonWindow === true)) { - continue; - } - const deltaAxis = axisLookup.get(`delta:${seriesItem.axisKey}`); - if (!deltaAxis) { - continue; - } - const deltaPoints = deltaPointsMap.get(seriesItem.entityId) || []; - if (!deltaPoints.length) { - continue; - } - if (analysis.show_delta_tooltip === true) { - deltaHoverSeries.push({ - entityId: `delta:${seriesItem.entityId}`, - relatedEntityId: seriesItem.entityId, - label: seriesItem.label, - baseLabel: seriesItem.label, - unit: seriesItem.unit || "", - pts: deltaPoints, - color: hexToRgba(seriesItem.color, 0.92), - axis: deltaAxis, - rawVisible: !hiddenSourceEntityIds.has(seriesItem.entityId), - hoverOpacity: 0.82, - delta: true - }); - } - if (analysis.show_delta_lines === true) { - renderer.drawLine( - deltaPoints, - hexToRgba(seriesItem.color, 0.92), - renderT0, - renderT1, - deltaAxis.min, - deltaAxis.max, - { - lineOpacity: 0.82, - lineWidth: 1.9, - dashed: true - } - ); - } - } - visibleSeries.forEach((seriesItem) => { - const shadingAnalysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); - if (shadingAnalysis.show_summary_stats !== true || shadingAnalysis.show_summary_stats_shading !== true) { - return; - } - const stats = summaryStatsMap.get(seriesItem.entityId); - const seriesWithAxis = seriesItem; - const axis = seriesWithAxis.axis; - if (!stats || !axis) { - return; - } - if (!Number.isFinite(stats.min) || !Number.isFinite(stats.max) || !Number.isFinite(stats.mean)) { - return; - } - const fillAlpha = anyHiddenSourceSeries ? 0.1 : 0.06; - renderer.drawGradientBand( - stats.min, - stats.mean, - seriesItem.color, - renderT0, - renderT1, - axis.min, - axis.max, - { fillAlpha } - ); - renderer.drawGradientBand( - stats.max, - stats.mean, - seriesItem.color, - renderT0, - renderT1, - axis.min, - axis.max, - { fillAlpha } - ); - }); - summaryHoverSeries.forEach((summarySeries) => { - const axis = summarySeries.axis; - if (!axis) { - return; - } - renderer.drawLine( - [ - [renderT0, summarySeries.value], - [renderT1, summarySeries.value] - ], - summarySeries.color, - renderT0, - renderT1, - axis.min, - axis.max, - { - lineOpacity: summarySeries.hoverOpacity, - lineWidth: 1.8, - dashed: false, - dotted: false - } - ); - }); - thresholdHoverSeries.forEach((thresholdSeries) => { - const axis = thresholdSeries.axis; - if (!axis) { - return; - } - const thresholdAnalysis = analysisMap.get(thresholdSeries.relatedEntityId) || normalizeHistorySeriesAnalysis(null); - if (thresholdAnalysis.show_threshold_shading === true) { - const relatedSeries = visibleSeries.find( - (seriesItem) => seriesItem.entityId === thresholdSeries.relatedEntityId - ); - if (relatedSeries?.pts?.length) { - renderer.drawThresholdArea( - relatedSeries.pts, - thresholdSeries.value, - thresholdSeries.baseColor || relatedSeries.color, - renderT0, - renderT1, - axis.min, - axis.max, - { - mode: thresholdSeries.direction === "below" ? "below" : "above", - fillAlpha: anyHiddenSourceSeries ? 0.24 : 0.14 - } - ); - } - } - renderer.drawLine( - [ - [renderT0, thresholdSeries.value], - [renderT1, thresholdSeries.value] - ], - thresholdSeries.color, - renderT0, - renderT1, - axis.min, - axis.max, - { - lineOpacity: thresholdSeries.hoverOpacity, - lineWidth: 1.15 - } - ); - }); - if (anomalySeries.length) { - const anomalyRegions = []; - anomalySeries.forEach((seriesItem) => { - const seriesWithAxis = seriesItem; - const axis = seriesWithAxis.axis; - if (!axis) { - return; - } - const visibleAnomalyClusters = this._filterAnnotatedAnomalyClusters( - seriesItem, - events - ); - if (visibleAnomalyClusters.length === 0) { - return; - } - const overlapMode = (analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null)).anomaly_overlap_mode; - const { baseClusters, regionClusters } = this._resolveAnomalyClusterDisplay( - visibleAnomalyClusters, - overlapMode, - correlatedSpans - ); - const baseColor = hexToRgba( - seriesItem.color, - anyHiddenSourceSeries ? 0.96 : 0.86 - ); - const regionOptions = { - strokeAlpha: anyHiddenSourceSeries ? 0.98 : 0.9, - lineWidth: anyHiddenSourceSeries ? 2.5 : 2.1, - haloWidth: anyHiddenSourceSeries ? 5.5 : 4.8, - haloColor: "rgba(255,255,255,0.88)", - haloAlpha: anyHiddenSourceSeries ? 0.92 : 0.82, - fillColor: hexToRgba( - seriesItem.color, - anyHiddenSourceSeries ? 0.14 : 0.1 - ), - fillAlpha: 1, - pointPadding: anyHiddenSourceSeries ? 12 : 10, - minRadiusX: 10, - minRadiusY: 10 - }; - if (baseClusters.length > 0) { - renderer.drawAnomalyClusters( - baseClusters, - baseColor, - renderT0, - renderT1, - axis.min, - axis.max, - regionOptions - ); - } - const clusterRegions = renderer.getAnomalyClusterRegions( - regionClusters, - renderT0, - renderT1, - axis.min, - axis.max, - regionOptions - ); - clusterRegions.forEach((region) => { - const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); - anomalyRegions.push({ - ...region, - relatedEntityId: seriesItem.entityId, - label: String(seriesItem.label), - unit: String(seriesItem.unit || ""), - color: seriesItem.color || null, - sensitivity: analysis.anomaly_sensitivity - }); - }); - }); - this._lastAnomalyRegions = anomalyRegions; - } else { - this._lastAnomalyRegions = []; - } - const effectiveComparisonHoverSeries = comparisonHoverSeries.filter( - (entry) => !hiddenComparisonEntityIds.has( - entry.relatedEntityId || entry.entityId - ) - ); - renderer.drawAnnotations(chartEvents, renderT0, renderT1, { - showLines: this._config.show_event_lines !== false, - showMarkers: this._config.show_event_lines !== false - }); - const eventHits = this._drawRecordedEventPoints( - renderer, - visibleSeries, - chartEvents, - renderT0, - renderT1, - { - showIcons: this._config.show_event_markers !== false - } - ); - this._renderLegend(series, binaryBackgrounds); - const eventValueMap = new Map(eventHits.map((hit) => [hit.event.id, hit])); - const enrichedEvents = chartEvents.map( - (event) => { - const hit = eventValueMap.get(event.id || ""); - return hit ? { - ...event, - chart_value: hit.value, - chart_unit: hit.unit - } : event; - } - ); - const addButton = this.querySelector("#chart-add-annotation"); - if (addButton) { - addButton.dataset.allowAddAnnotation = this._canAddAnnotation ? "true" : "false"; - if (addButton.dataset.allowAddAnnotation === "false") { - addButton.hidden = true; - } - } - if (visibleSeries.length) { - this._ensureContextAnnotationDialog(); - attachLineChartHover( - this, - canvas, - renderer, - hoverSeries, - enrichedEvents, - renderT0, - renderT1, - 0, - 0, - activeAxes, - { - onContextMenu: (hover) => this._handleChartContextMenu(hover), - onAddAnnotation: this._canAddAnnotation ? (hover) => this._handleChartAddAnnotation(hover) : void 0, - binaryStates: binaryBackgrounds.filter( - (entry) => !this._hiddenSeries.has(entry.entityId) - ), - comparisonSeries: effectiveComparisonHoverSeries, - trendSeries: [ - ...trendHoverSeries, - ...comparisonTrendHoverSeries - ], - rateSeries: [ - ...rateHoverSeries, - ...comparisonRateHoverSeries - ], - deltaSeries: deltaHoverSeries, - summarySeries: [ - ...summaryHoverSeries, - ...comparisonSummaryHoverSeries - ], - thresholdSeries: [ - ...thresholdHoverSeries, - ...comparisonThresholdHoverSeries - ], - anomalyRegions: Array.isArray(this._lastAnomalyRegions) ? this._lastAnomalyRegions : [], - hoverSurfaceEl: this.querySelector( - "#chart-icon-overlay" - ), - showTooltip: this._config.show_tooltips !== false, - emphasizeHoverGuides: this._config.emphasize_hover_guides === true, - hoverSnapMode: this._config.hover_snap_mode === "snap_to_data_points" ? "snap_to_data_points" : "follow_series", - showTrendCrosshairs: anyTrendCrosshairs, - showRateCrosshairs: anyRateCrosshairs, - hideRawData: hiddenSourceEntityIds.size === visibleSeries.length && visibleSeries.length > 0, - showDeltaTooltip: deltaHoverSeries.length > 0, - onAnomalyClick: (regions) => this._handleAnomalyAddAnnotation(regions) - } - ); - attachLineChartRangeZoom(this, canvas, renderer, renderT0, renderT1, { - onPreview: (range) => this._dispatchZoomPreview(range), - onZoom: ({ - startTime, - endTime - }) => this._applyZoomRange(startTime, endTime), - onReset: () => this._clearZoomRange() - }); - } else { - if (this._chartHoverCleanup) { - this._chartHoverCleanup(); - this._chartHoverCleanup = null; - } - if (this._chartZoomCleanup) { - this._chartZoomCleanup(); - this._chartZoomCleanup = null; - } - } - if (this._chartScrollViewportEl) { - this._chartScrollViewportEl.removeEventListener( - "scroll", - this._onChartScroll - ); - this._chartScrollViewportEl.addEventListener( - "scroll", - this._onChartScroll, - { passive: true } - ); - this._syncChartViewportScroll(t0, t1, w); - } - this._fireBackendAnomalyRequests( - _anomalyEntityIds, - analysisMap, - _startIso, - _endIso - ); - this._fireComparisonBackendAnomalyRequests( - drawableComparisonResults, - analysisMap, - renderT0, - renderT1 - ); - } - // ── Analysis cache helpers ────────────────────────────────────────────────── - /** - * Build a lightweight string key that captures all inputs that affect the - * analysis result. Used to skip the worker when data and settings haven't - * changed (e.g. on zoom/pan). - * Ported from _buildAnalysisCacheKey in card-history.js. - */ - _buildAnalysisCacheKey(visibleSeries, selectedComparisonSeriesMap, analysisMap, allComparisonWindowsData, t0, t1) { - const ANALYSIS_FIELDS = [ - "show_trend_lines", - "trend_method", - "trend_window", - "show_rate_of_change", - "rate_window", - "show_delta_analysis", - "show_summary_stats", - "show_anomalies", - "anomaly_methods", - "anomaly_sensitivity", - "anomaly_overlap_mode", - "anomaly_rate_window", - "anomaly_zscore_window", - "anomaly_persistence_window", - "anomaly_comparison_window_id", - "anomaly_use_sampled_data" - ]; - const seriesPart = visibleSeries.map((s2) => { - const a2 = analysisMap.get(s2.entityId) || normalizeHistorySeriesAnalysis(null); - const first = s2.pts[0]?.[0] ?? 0; - const last = s2.pts[s2.pts.length - 1]?.[0] ?? 0; - const aKey = ANALYSIS_FIELDS.map((f2) => JSON.stringify(a2[f2])).join(","); - return `${s2.entityId}:${s2.pts.length}:${first}:${last}:${aKey}`; - }).join("|"); - const cmpPart = Array.from(selectedComparisonSeriesMap.values()).map((s2) => { - const first = s2.pts[0]?.[0] ?? 0; - const last = s2.pts[s2.pts.length - 1]?.[0] ?? 0; - return `${s2.entityId}:${s2.pts.length}:${first}:${last}`; - }).sort().join("|"); - const allCmpPart = Object.entries(allComparisonWindowsData).flatMap( - ([windowId, entities]) => Object.entries(entities).map(([entityId, pts]) => { - const first = pts[0]?.[0] ?? 0; - const last = pts[pts.length - 1]?.[0] ?? 0; - return `${windowId}:${entityId}:${pts.length}:${first}:${last}`; - }) - ).sort().join("|"); - return `${t0}:${t1}|${seriesPart}|${cmpPart}|${allCmpPart}`; - } - /** - * Build the serialisable payload sent to the history-analysis worker. - * Ported from _buildHistoryAnalysisPayload in card-history.js. - */ - _buildHistoryAnalysisPayload(visibleSeries, selectedComparisonSeriesMap, analysisMap, hasSelectedComparisonWindow, allComparisonWindowsData = {}) { - const defaultAnalysis = normalizeHistorySeriesAnalysis(null); - const series = visibleSeries.map( - (seriesItem) => ({ - entityId: seriesItem.entityId, - pts: seriesItem.pts, - analysis: analysisMap.get(seriesItem.entityId) || defaultAnalysis - }) - ); - const comparisonSeries = Array.from(selectedComparisonSeriesMap.values()).map((seriesItem) => ({ - entityId: seriesItem.entityId, - pts: seriesItem.pts - })); - const seriesAnalysisConfigs = {}; - for (const seriesItem of visibleSeries) { - seriesAnalysisConfigs[seriesItem.entityId] = analysisMap.get(seriesItem.entityId) || defaultAnalysis; - } - return { - series, - comparisonSeries, - hasSelectedComparisonWindow: hasSelectedComparisonWindow === true, - allComparisonWindowsData, - seriesAnalysisConfigs - }; - } - /** - * Run or cache the history analysis computation. - * Terminates any in-flight worker before starting a new one. - * Falls back to a synchronous main-thread path when the worker fails. - * Ported from _computeHistoryAnalysis in card-history.js. - */ - async _computeHistoryAnalysis(visibleSeries, selectedComparisonSeriesMap, analysisMap, hasSelectedComparisonWindow, allComparisonWindowsData = {}, t0 = 0, t1 = 0, onProgress = null) { - terminateHistoryAnalysisWorker(); - const cacheKey = this._buildAnalysisCacheKey( - visibleSeries, - selectedComparisonSeriesMap, - analysisMap, - allComparisonWindowsData, - t0, - t1 - ); - if (this._analysisCache?.key === cacheKey) { - return this._analysisCache.result; - } - onProgress?.(0); - const payload = this._buildHistoryAnalysisPayload( - visibleSeries, - selectedComparisonSeriesMap, - analysisMap, - hasSelectedComparisonWindow, - allComparisonWindowsData - ); - try { - const result = await computeHistoryAnalysisInWorker( - payload - ); - this._analysisCache = { key: cacheKey, result }; - return result; - } catch (error) { - const errorMessage = error?.message ?? ""; - if (errorMessage.startsWith("Aborted")) { - return { - trendSeries: [], - rateSeries: [], - deltaSeries: [], - summaryStats: [], - anomalySeries: [] - }; - } - logger$1.warn("[hass-datapoints history-card] analysis worker fallback", { - message: errorMessage || String(error) - }); - try { - return { - trendSeries: visibleSeries.map((seriesItem) => { - const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); - if (analysis.show_trend_lines !== true) { - return null; - } - return { - entityId: seriesItem.entityId, - pts: this._buildTrendPoints( - seriesItem.pts, - analysis.trend_method, - analysis.trend_window - ) - }; - }).filter(Boolean).filter( - (s2) => Array.isArray(s2.pts) && s2.pts.length >= 2 - ), - rateSeries: visibleSeries.map((seriesItem) => { - const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); - if (analysis.show_rate_of_change !== true) { - return null; - } - return { - entityId: seriesItem.entityId, - pts: this._buildRateOfChangePoints( - seriesItem.pts, - analysis.rate_window - ) - }; - }).filter(Boolean).filter( - (s2) => Array.isArray(s2.pts) && s2.pts.length >= 2 - ), - deltaSeries: visibleSeries.map((seriesItem) => { - const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); - if (!(analysis.show_delta_analysis === true && hasSelectedComparisonWindow === true)) { - return null; - } - const comparisonSeries = selectedComparisonSeriesMap.get( - seriesItem.entityId - ); - return { - entityId: seriesItem.entityId, - pts: comparisonSeries ? this._buildDeltaPoints(seriesItem.pts, comparisonSeries.pts) : [] - }; - }).filter(Boolean).filter( - (s2) => Array.isArray(s2.pts) && s2.pts.length >= 2 - ), - summaryStats: visibleSeries.map((seriesItem) => { - const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); - if (analysis.show_summary_stats !== true) { - return null; - } - return { - entityId: seriesItem.entityId, - ...this._buildSummaryStats(seriesItem.pts) - }; - }).filter( - (entry) => entry && Number.isFinite( - entry.min - ) && Number.isFinite( - entry.max - ) && Number.isFinite( - entry.mean - ) - ), - // Anomaly detection intentionally omitted in the fallback path. - anomalySeries: [] - }; - } catch (fallbackError) { - logger$1.error( - "[hass-datapoints history-card] analysis fallback failed", - fallbackError - ); - return { - trendSeries: [], - rateSeries: [], - deltaSeries: [], - summaryStats: [], - anomalySeries: [] - }; - } - } - } - // ── Analysis helper stubs ─────────────────────────────────────────────────── - // These are called by _computeHistoryAnalysis fallback path. - // Implementations live in history/analysis/index.ts / the parent card. - /** Build trend points from raw series data. */ - _buildTrendPoints(_pts, _method, _window) { - if (!Array.isArray(_pts) || _pts.length < 2) { - return []; - } - const method = _method || "rolling_average"; - if (method === "linear_trend") { - return buildLinearTrend(_pts); - } - return buildRollingAverageTrend(_pts, getTrendWindowMs(_window || "24h")); - } - /** Build rate-of-change points from raw series data. */ - _buildRateOfChangePoints(_pts, _window) { - if (!Array.isArray(_pts) || _pts.length < 2) { - return []; - } - return buildRateOfChangePoints(_pts, _window || "1h"); - } - /** Build delta points (main vs comparison series). */ - _buildDeltaPoints(_pts, _comparisonPts) { - return buildDeltaPoints(_pts, _comparisonPts); - } - /** Build summary statistics from raw series data. */ - _buildSummaryStats(_pts) { - return buildSummaryStats(_pts) ?? { min: 0, max: 0, mean: 0 }; - } - // ── Series analysis helpers ───────────────────────────────────────────────── - /** - * Build a Map of entityId → normalised analysis settings from config. - * Ported from _getSeriesAnalysisMap in card-history.js. - */ - _getSeriesAnalysisMap() { - const seriesSettings = Array.isArray(this._config?.series_settings) ? this._config.series_settings : []; - return new Map( - seriesSettings.filter((entry) => entry?.entity_id != null).map((entry) => [ - entry.entity_id, - normalizeHistorySeriesAnalysis(entry?.analysis) - ]) - ); - } - /** - * Get the normalised analysis settings for a single entity. - * Ported from _getSeriesAnalysis in card-history.js. - */ - _getSeriesAnalysis(entityId, analysisMap = null) { - const map = analysisMap || this._getSeriesAnalysisMap(); - return normalizeHistorySeriesAnalysis(map.get(entityId)); - } - /** - * Returns true if any analysis feature is active for the given series. - * Ported from _seriesHasActiveAnalysis in card-history.js. - */ - _seriesHasActiveAnalysis(analysis, hasSelectedComparisonWindow = false) { - return !!(analysis.show_trend_lines || analysis.show_summary_stats || analysis.show_rate_of_change || analysis.show_threshold_analysis || analysis.show_anomalies || analysis.show_delta_analysis && hasSelectedComparisonWindow); - } - /** - * Returns true if the source series should be hidden (replaced by its - * analysis overlay series). - * Ported from _seriesShouldHideSource in card-history.js. - */ - _seriesShouldHideSource(analysis, hasSelectedComparisonWindow = false) { - return analysis.hide_source_series === true && this._seriesHasActiveAnalysis(analysis, hasSelectedComparisonWindow); - } - /** - * Return ChartRenderer render options for the active trend method. - * Ported from _getTrendRenderOptions in card-history.js. - */ - _getTrendRenderOptions(method = "rolling_average", hideRawData = false) { - if (method === "linear_trend") { - return { - colorAlpha: hideRawData ? 0.94 : 0.88, - lineOpacity: hideRawData ? 0.86 : 0.74, - lineWidth: 2.1, - dashed: true, - dotted: false - }; - } - return { - colorAlpha: hideRawData ? 0.9 : 0.82, - lineOpacity: hideRawData ? 0.84 : 0.62, - lineWidth: 2.2, - dashed: false, - dotted: true - }; - } - // ── Split-view chart rendering ─────────────────────────────────────────────── - async _drawSplitChart({ - visibleSeries, - binaryBackgrounds, - events, - renderT0, - renderT1, - canvasWidth, - availableHeight, - chartStage, - canvas, - wrap, - options, - drawableComparisonResults, - selectedComparisonWindowId, - hoveredComparisonWindowId, - comparisonPreviewActive, - hoveringDifferentComparison, - analysisResult, - analysisMap, - hasSelectedComparisonWindow - }) { - if (canvas) { - canvas.style.display = "none"; - } - const N2 = visibleSeries.length; - const MIN_ROW_HEIGHT = 140; - const rowHeight = Math.max(MIN_ROW_HEIGHT, Math.floor(availableHeight / N2)); - const totalHeight = rowHeight * N2; - if (chartStage) { - chartStage.style.width = `${canvasWidth}px`; - chartStage.style.height = `${totalHeight}px`; - } - const splitScrollViewport = this.querySelector("#chart-scroll-viewport"); - if (splitScrollViewport) { - splitScrollViewport.style.overflowY = totalHeight > availableHeight ? "auto" : "hidden"; - } - this._setChartLoading(!!options.loading); - this._setChartMessage(""); - const iconOverlay = this.querySelector("#chart-icon-overlay"); - if (iconOverlay) { - iconOverlay.innerHTML = ""; - } - const trendPointsMap = new Map( - (analysisResult?.trendSeries || []).map((entry) => [ - entry.entityId, - entry.pts - ]) - ); - const ratePointsMap = new Map( - (analysisResult?.rateSeries || []).map((entry) => [ - entry.entityId, - entry.pts - ]) - ); - const deltaPointsMap = new Map( - (analysisResult?.deltaSeries || []).map((entry) => [ - entry.entityId, - entry.pts - ]) - ); - const summaryStatsMap = new Map( - (analysisResult?.summaryStats || []).map((entry) => [ - entry.entityId, - entry - ]) - ); - const anomalyClustersMap = new Map( - (analysisResult?.anomalySeries || []).map( - (entry) => [ - entry.entityId, - entry.anomalyClusters - ] - ) - ); - const effectiveAnalysisMap = analysisMap || /* @__PURE__ */ new Map(); - const shouldUseCorrelatedAnomalySpans = this._config?.show_correlated_anomalies === true || this._config?.anomaly_overlap_mode === "only"; - const shouldDrawCorrelatedAnomalySpans = this._config?.show_correlated_anomalies === true; - const correlatedAnomalySpans = shouldUseCorrelatedAnomalySpans ? this._buildCorrelatedAnomalySpans( - visibleSeries, - anomalyClustersMap, - effectiveAnalysisMap - ) : []; - const tracks = []; - for (let i2 = 0; i2 < N2; i2 += 1) { - const isLastRow = i2 === N2 - 1; - const seriesItem = visibleSeries[i2]; - const rowOffset = i2 * rowHeight; - const rowDiv = document.createElement("div"); - rowDiv.className = "split-series-row"; - rowDiv.style.cssText = `position:absolute;left:0;top:${rowOffset}px;width:${canvasWidth}px;height:${rowHeight}px;pointer-events:none;overflow:hidden;`; - const rowCanvas = document.createElement("canvas"); - rowCanvas.className = "split-series-canvas"; - rowDiv.appendChild(rowCanvas); - chartStage?.appendChild(rowDiv); - const { w, h: h2 } = setupCanvas(rowCanvas, chartStage || wrap, rowHeight, canvasWidth); - const renderer = new ChartRenderer(rowCanvas, w, h2); - renderer.labelColor = resolveChartLabelColor(this); - renderer.basePad = { - top: 24, - right: 12, - bottom: isLastRow ? 48 : 10, - left: 12 - }; - renderer.clear(); - const rowAnalysis = effectiveAnalysisMap.get( - seriesItem.entityId - ) || normalizeHistorySeriesAnalysis(null); - const rowTrendPts = rowAnalysis.show_trend_lines === true ? trendPointsMap.get( - seriesItem.entityId - ) || [] : []; - const rowRatePts = rowAnalysis.show_rate_of_change === true ? ratePointsMap.get( - seriesItem.entityId - ) || [] : []; - const rowDeltaPts = rowAnalysis.show_delta_analysis === true && hasSelectedComparisonWindow ? deltaPointsMap.get( - seriesItem.entityId - ) || [] : []; - const rowSummaryStats = rowAnalysis.show_summary_stats === true ? summaryStatsMap.get(seriesItem.entityId) || null : null; - const rowAnomalyClusters = rowAnalysis.show_anomalies === true ? anomalyClustersMap.get( - seriesItem.entityId - ) || [] : []; - const rowHideSource = this._seriesShouldHideSource( - rowAnalysis, - hasSelectedComparisonWindow - ); - const axisValues = seriesItem.pts.map( - ([, v2]) => v2 - ); - const extent = this._getAxisValueExtent(axisValues); - let axisMin = 0; - let axisMax = 1; - if (extent) { - const pad = (extent.max - extent.min) * 0.1 || 1; - axisMin = extent.min - pad; - axisMax = extent.max + pad; - } - const primaryAxisKey = seriesItem.axisKey || seriesItem.unit || "__unitless__"; - const axis = { - key: primaryAxisKey, - unit: String(seriesItem.unit || ""), - color: seriesItem.color || null, - side: "left", - min: axisMin, - max: axisMax, - values: axisValues - }; - const rowAxes = [axis]; - let rowRateAxisKey = null; - if (rowRatePts.length >= 2) { - const rateVals = rowRatePts.map(([, v2]) => v2); - const rateExt = this._getAxisValueExtent(rateVals); - if (rateExt) { - const pad = (rateExt.max - rateExt.min) * 0.1 || 1; - rowRateAxisKey = `rate:${primaryAxisKey}`; - rowAxes.push({ - key: rowRateAxisKey, - unit: seriesItem.unit ? `${String(seriesItem.unit)}/h` : "Rate/h", - color: seriesItem.color || null, - side: "right", - min: rateExt.min - pad, - max: rateExt.max + pad, - values: rateVals - }); - } - } - let rowDeltaAxisKey = null; - if (rowDeltaPts.length >= 2) { - const deltaVals = rowDeltaPts.map(([, v2]) => v2); - const deltaExt = this._getAxisValueExtent(deltaVals); - if (deltaExt) { - const pad = (deltaExt.max - deltaExt.min) * 0.1 || 1; - rowDeltaAxisKey = `delta:${primaryAxisKey}`; - rowAxes.push({ - key: rowDeltaAxisKey, - unit: seriesItem.unit ? `Δ ${String(seriesItem.unit)}` : "Δ", - color: seriesItem.color || null, - side: "right", - min: deltaExt.min - pad, - max: deltaExt.max + pad, - values: deltaVals - }); - } - } - renderer.drawGrid(renderT0, renderT1, rowAxes, void 0, 4, { - fixedAxisOverlay: true, - hideTimeLabels: !isLastRow - }); - const activeAxes = renderer._activeAxes; - const resolvedAxis = activeAxes?.[0] || axis; - const resolvedRateAxis = rowRateAxisKey ? activeAxes?.find((a2) => a2.key === rowRateAxisKey) || null : null; - const resolvedDeltaAxis = rowDeltaAxisKey ? activeAxes?.find((a2) => a2.key === rowDeltaAxisKey) || null : null; - seriesItem.axis = resolvedAxis; - let mainSeriesOpacity; - if (!comparisonPreviewActive) { - mainSeriesOpacity = 1; - } else if (hoveringDifferentComparison) { - mainSeriesOpacity = 0.15; - } else { - mainSeriesOpacity = 0.25; - } - if (!rowHideSource) { - this._drawSeriesLine( - renderer, - seriesItem.pts, - seriesItem.color, - renderT0, - renderT1, - resolvedAxis.min, - resolvedAxis.max, - { - lineWidth: comparisonPreviewActive ? 1.25 : 1.75, - lineOpacity: mainSeriesOpacity, - stepped: rowAnalysis.stepped_series === true - } - ); - } - for (const win of drawableComparisonResults || []) { - const winPts = await this._resolveComparisonWindowPoints( - seriesItem.entityId, - win, - rowAnalysis, - renderT0, - renderT1 - ); - if (!winPts.length) { - continue; - } - const isHovered = !!hoveredComparisonWindowId && win.id === hoveredComparisonWindowId; - const isSelected = !!selectedComparisonWindowId && win.id === selectedComparisonWindowId; - const comparisonLineStyle = this._getComparisonWindowLineStyle( - isHovered, - isSelected, - hoveringDifferentComparison - ); - renderer.drawLine( - winPts, - seriesItem.color, - renderT0, - renderT1, - resolvedAxis.min, - resolvedAxis.max, - { - lineOpacity: comparisonLineStyle.lineOpacity, - lineWidth: comparisonLineStyle.lineWidth, - dashed: comparisonLineStyle.dashed, - dashPattern: comparisonLineStyle.dashPattern, - stepped: rowAnalysis.stepped_series === true - } - ); - this._drawComparisonAnalysisOverlays({ - renderer, - entityId: seriesItem.entityId, - seriesColor: seriesItem.color, - comparisonPts: winPts, - analysis: rowAnalysis, - renderT0, - renderT1, - axis: resolvedAxis, - rateAxis: resolvedRateAxis, - events, - comparisonWindowId: String(win.id || "") - }); - } - binaryBackgrounds.forEach((bg) => { - const bgItem = bg; - if (!this._hiddenSeries.has(bgItem.entityId) && bgItem.spans?.length) { - renderer.drawStateBands( - bgItem.spans, - renderT0, - renderT1, - bgItem.color, - 0.1 - ); - } - }); - if (shouldDrawCorrelatedAnomalySpans && correlatedAnomalySpans.length) { - renderer.drawStateBands( - correlatedAnomalySpans, - renderT0, - renderT1, - "#ef4444", - 0.1 - ); - } - renderer.drawAnnotations( - events, - renderT0, - renderT1, - { - showLines: this._config.show_event_lines !== false, - showMarkers: this._config.show_event_lines !== false - } - ); - this._drawRecordedEventPoints( - renderer, - [seriesItem], - events, - renderT0, - renderT1, - { - showIcons: this._config.show_event_markers !== false, - yOffset: rowOffset, - skipOverlayClear: true - } - ); - if (rowAnalysis.show_threshold_analysis === true) { - const thresholdValue = Number(rowAnalysis.threshold_value); - if (Number.isFinite(thresholdValue)) { - if (rowAnalysis.show_threshold_shading === true && seriesItem.pts.length) { - renderer.drawThresholdArea( - seriesItem.pts, - thresholdValue, - seriesItem.color, - renderT0, - renderT1, - resolvedAxis.min, - resolvedAxis.max, - { - mode: rowAnalysis.threshold_direction === "below" ? "below" : "above", - fillAlpha: rowHideSource ? 0.24 : 0.14 - } - ); - } - renderer.drawLine( - [ - [renderT0, thresholdValue], - [renderT1, thresholdValue] - ], - hexToRgba(seriesItem.color, rowHideSource ? 0.82 : 0.46), - renderT0, - renderT1, - resolvedAxis.min, - resolvedAxis.max, - { lineOpacity: rowHideSource ? 0.84 : 0.48, lineWidth: 1.15 } - ); - } - } - if (rowSummaryStats) { - if (rowAnalysis.show_summary_stats_shading === true) { - const fillAlpha = rowHideSource ? 0.1 : 0.06; - renderer.drawGradientBand( - rowSummaryStats.min, - rowSummaryStats.mean, - seriesItem.color, - renderT0, - renderT1, - resolvedAxis.min, - resolvedAxis.max, - { fillAlpha } - ); - renderer.drawGradientBand( - rowSummaryStats.max, - rowSummaryStats.mean, - seriesItem.color, - renderT0, - renderT1, - resolvedAxis.min, - resolvedAxis.max, - { fillAlpha } - ); - } - const summaryEntries = [ - { - type: "min", - value: rowSummaryStats.min, - alpha: rowHideSource ? 0.78 : 0.42, - width: 1.1, - dotted: true - }, - { - type: "mean", - value: rowSummaryStats.mean, - alpha: rowHideSource ? 0.94 : 0.78, - width: 1.8, - dotted: false - }, - { - type: "max", - value: rowSummaryStats.max, - alpha: rowHideSource ? 0.78 : 0.42, - width: 1.1, - dotted: true - } - ]; - for (const entry of summaryEntries) { - if (!Number.isFinite(entry.value)) continue; - renderer.drawLine( - [ - [renderT0, entry.value], - [renderT1, entry.value] - ], - hexToRgba(seriesItem.color, entry.alpha), - renderT0, - renderT1, - resolvedAxis.min, - resolvedAxis.max, - { - lineOpacity: rowHideSource ? 0.82 : 0.34, - lineWidth: entry.width, - dotted: entry.dotted - } - ); - } - } - if (rowTrendPts.length >= 2) { - const trendOpts = this._getTrendRenderOptions( - rowAnalysis.trend_method, - rowHideSource - ); - renderer.drawLine( - rowTrendPts, - hexToRgba(seriesItem.color, trendOpts.colorAlpha), - renderT0, - renderT1, - resolvedAxis.min, - resolvedAxis.max, - { - lineOpacity: trendOpts.lineOpacity, - lineWidth: trendOpts.lineWidth, - dashed: trendOpts.dashed, - dotted: trendOpts.dotted - } - ); - } - if (rowRatePts.length >= 2 && resolvedRateAxis) { - renderer.drawLine( - rowRatePts, - hexToRgba(seriesItem.color, rowHideSource ? 0.96 : 0.82), - renderT0, - renderT1, - resolvedRateAxis.min, - resolvedRateAxis.max, - { - lineOpacity: rowHideSource ? 0.88 : 0.66, - lineWidth: 1.55, - dashPattern: [7, 3, 1.5, 3] - } - ); - } - if (rowDeltaPts.length >= 2 && resolvedDeltaAxis && rowAnalysis.show_delta_lines === true) { - renderer.drawLine( - rowDeltaPts, - hexToRgba(seriesItem.color, 0.92), - renderT0, - renderT1, - resolvedDeltaAxis.min, - resolvedDeltaAxis.max, - { lineOpacity: 0.82, lineWidth: 1.9, dashed: true } - ); - } - let rowAnomalyRegions = []; - if (rowAnomalyClusters.length) { - const filteredClusters = this._filterAnnotatedAnomalyClusters( - { - entityId: seriesItem.entityId, - anomalyClusters: rowAnomalyClusters - }, - events - ); - if (filteredClusters.length > 0) { - const { baseClusters, regionClusters } = this._resolveAnomalyClusterDisplay( - filteredClusters, - rowAnalysis.anomaly_overlap_mode, - correlatedAnomalySpans - ); - const baseColor = hexToRgba( - seriesItem.color, - rowHideSource ? 0.96 : 0.86 - ); - const regionOpts = { - strokeAlpha: rowHideSource ? 0.98 : 0.9, - lineWidth: rowHideSource ? 2.5 : 2.1, - haloWidth: rowHideSource ? 5.5 : 4.8, - haloColor: "rgba(255,255,255,0.88)", - haloAlpha: rowHideSource ? 0.92 : 0.82, - fillColor: hexToRgba( - seriesItem.color, - rowHideSource ? 0.14 : 0.1 - ), - fillAlpha: 1, - pointPadding: rowHideSource ? 12 : 10, - minRadiusX: 10, - minRadiusY: 10 - }; - if (baseClusters.length > 0) { - renderer.drawAnomalyClusters( - baseClusters, - baseColor, - renderT0, - renderT1, - resolvedAxis.min, - resolvedAxis.max, - regionOpts - ); - } - rowAnomalyRegions = renderer.getAnomalyClusterRegions( - regionClusters, - renderT0, - renderT1, - resolvedAxis.min, - resolvedAxis.max, - regionOpts - ).map( - (region) => ({ - ...region, - relatedEntityId: String(seriesItem.entityId), - label: String(seriesItem.label), - unit: String(seriesItem.unit || ""), - color: seriesItem.color || null, - sensitivity: String(rowAnalysis.anomaly_sensitivity || "") - }) - ); - } - } - tracks.push({ - canvas: rowCanvas, - renderer, - series: seriesItem, - axis: resolvedAxis, - rowOffset, - analysis: rowAnalysis, - summaryStats: rowSummaryStats, - trendPts: rowTrendPts, - ratePts: rowRatePts, - rateAxis: resolvedRateAxis, - deltaPts: rowDeltaPts, - deltaAxis: resolvedDeltaAxis, - anomalyRegions: rowAnomalyRegions - }); - } - this._renderSplitAxisOverlays(tracks); - this._renderComparisonPreviewOverlay( - tracks[0] ? tracks[0].renderer : null - ); - const comparisonHoverSeries = []; - for (const track of tracks) { - const trackSeries = track.series; - const trackAnalysis = track.analysis || normalizeHistorySeriesAnalysis(null); - for (const win of drawableComparisonResults || []) { - const winPts = await this._resolveComparisonWindowPoints( - trackSeries.entityId, - win, - trackAnalysis, - renderT0, - renderT1 - ); - if (!winPts.length) { - continue; - } - const isHovered = !!hoveredComparisonWindowId && win.id === hoveredComparisonWindowId; - const isSelected = !!selectedComparisonWindowId && win.id === selectedComparisonWindowId; - const comparisonLineStyle = this._getComparisonWindowLineStyle( - isHovered, - isSelected, - hoveringDifferentComparison - ); - const winId = String(win.id || ""); - const splitPrecomputed = this._analysisCache?.result?.comparisonWindowResults?.[winId]?.[trackSeries.entityId]; - comparisonHoverSeries.push({ - entityId: `${winId}:${trackSeries.entityId}`, - relatedEntityId: trackSeries.entityId, - comparisonParentId: `${winId}:${trackSeries.entityId}`, - label: trackSeries.label, - windowLabel: win.label || "Date window", - unit: trackSeries.unit, - pts: winPts, - trendPts: trackAnalysis.show_trend_lines === true && winPts.length >= 2 ? splitPrecomputed?.trendPts ?? this._buildTrendPoints( - winPts, - trackAnalysis.trend_method, - trackAnalysis.trend_window - ) : [], - ratePts: trackAnalysis.show_rate_of_change === true && winPts.length >= 2 ? splitPrecomputed?.ratePts ?? this._buildRateOfChangePoints( - winPts, - trackAnalysis.rate_window - ) : [], - summaryStats: trackAnalysis.show_summary_stats === true ? splitPrecomputed?.summaryStats ?? this._buildSummaryStats(winPts) : null, - thresholdValue: trackAnalysis.show_threshold_analysis === true ? Number(trackAnalysis.threshold_value) : null, - color: trackSeries.color, - hoverOpacity: comparisonLineStyle.hoverOpacity, - track - }); - } - } - this._attachSplitHover( - tracks, - comparisonHoverSeries, - events, - renderT0, - renderT1, - chartStage, - options, - effectiveAnalysisMap, - hasSelectedComparisonWindow - ); - this._fireComparisonBackendAnomalyRequests( - drawableComparisonResults, - effectiveAnalysisMap, - renderT0, - renderT1 - ); - } - // ── Split-view hover / zoom / scroll interaction ───────────────────────────── - _attachSplitHover(tracks, comparisonHoverSeries, events, t0, t1, chartStage, options, analysisMap, hasSelectedComparisonWindow) { - if (this._chartHoverCleanup) { - this._chartHoverCleanup(); - this._chartHoverCleanup = null; - } - if (!tracks.length || !chartStage) { - return; - } - const primaryRenderer = tracks[0].renderer; - const lastTrack = tracks[tracks.length - 1]; - const eventThresholdMs = primaryRenderer.cw ? 14 * ((t1 - t0) / primaryRenderer.cw) : 0; - const splitSelTop = tracks[0].rowOffset + primaryRenderer.pad.top; - const splitSelBottom = lastTrack.rowOffset + lastTrack.renderer.pad.top + lastTrack.renderer.ch; - const splitSelHeight = splitSelBottom - splitSelTop; - const overlayEl = document.createElement("div"); - overlayEl.id = "chart-split-overlay"; - overlayEl.style.cssText = "position:absolute;inset:0;pointer-events:auto;z-index:2;cursor:crosshair;"; - chartStage.appendChild(overlayEl); - const overlayRelX = (clientX) => { - const rect = overlayEl.getBoundingClientRect(); - return clampChartValue( - clientX - rect.left, - primaryRenderer.pad.left, - primaryRenderer.pad.left + primaryRenderer.cw - ); - }; - const stageXToTime = (stageX) => { - const ratio = primaryRenderer.cw ? (stageX - primaryRenderer.pad.left) / primaryRenderer.cw : 0; - return t0 + ratio * (t1 - t0); - }; - const inPlotBoundsX = (clientX) => { - const rect = overlayEl.getBoundingClientRect(); - const localX = clientX - rect.left; - return localX >= primaryRenderer.pad.left && localX <= primaryRenderer.pad.left + primaryRenderer.cw; - }; - const buildSplitHover = (clientX) => { - const baseRect = tracks[0].canvas.getBoundingClientRect(); - if (!baseRect.width || !primaryRenderer.cw) { - return null; - } - const localX = clampChartValue( - clientX - baseRect.left, - primaryRenderer.pad.left, - primaryRenderer.pad.left + primaryRenderer.cw - ); - const ratio = (localX - primaryRenderer.pad.left) / primaryRenderer.cw; - const rawTimeMs = t0 + ratio * (t1 - t0); - const hoverSnapMode = this._config.hover_snap_mode === "snap_to_data_points" ? "snap_to_data_points" : "follow_series"; - const splitHoverSeries = tracks.map( - (track) => ({ - pts: track.series?.pts || [] - }) - ); - const timeMs = resolveLineChartHoverTime( - splitHoverSeries, - rawTimeMs, - hoverSnapMode - ); - const x2 = primaryRenderer.xOf(timeMs, t0, t1); - const values = tracks.map( - (trackItem) => { - const { - renderer: trackRenderer, - series, - axis, - rowOffset - } = trackItem; - const value = trackRenderer._interpolateValue( - series.pts, - timeMs - ); - if (value == null) { - return { - entityId: String( - series.entityId || "" - ), - label: String(series.label || ""), - value: null, - unit: String(series.unit || ""), - color: String(series.color || ""), - opacity: 1, - hasValue: false, - axisSide: "left", - axisSlot: 0 - }; - } - return { - entityId: String( - series.entityId || "" - ), - label: String(series.label || ""), - value, - unit: String(series.unit || ""), - color: String(series.color || ""), - opacity: 1, - hasValue: true, - x: x2, - y: rowOffset + trackRenderer.yOf( - value, - axis.min, - axis.max - ), - axisSide: "left", - axisSlot: 0 - }; - } - ); - const hoveredEvents = []; - for (const event of events || []) { - const eventTime = new Date( - event.timestamp - ).getTime(); - if (eventTime < t0 || eventTime > t1) { - continue; - } - const distance = Math.abs(eventTime - timeMs); - if (distance <= eventThresholdMs) { - hoveredEvents.push({ - ...event, - _hoverDistanceMs: distance - }); - } - } - hoveredEvents.sort( - (a2, b2) => (a2._hoverDistanceMs || 0) - (b2._hoverDistanceMs || 0) - ); - const comparisonValues = (comparisonHoverSeries || []).map((chs) => { - const { - pts, - entityId, - relatedEntityId, - comparisonParentId, - label, - windowLabel, - unit, - color, - hoverOpacity, - track: cTrack - } = chs; - const value = cTrack.renderer._interpolateValue(pts, timeMs); - if (value == null) { - return { - entityId: String(entityId || ""), - label: String(label || ""), - value: null, - unit: String(unit || ""), - color, - opacity: Number(hoverOpacity) || 1, - hasValue: false, - axisSide: "left", - axisSlot: 0 - }; - } - return { - entityId: String(entityId || ""), - relatedEntityId: String(relatedEntityId || ""), - comparisonParentId: String(comparisonParentId || ""), - label: String(label || ""), - windowLabel: String(windowLabel || ""), - value, - unit: String(unit || ""), - color, - opacity: Number(hoverOpacity) || 1, - hasValue: true, - x: x2, - y: cTrack.rowOffset + cTrack.renderer.yOf( - value, - cTrack.axis.min, - cTrack.axis.max - ), - axisSide: "left", - axisSlot: 0 - }; - }); - const trendValues = []; - const rateValues = []; - const deltaValues = []; - const summaryValues = []; - const thresholdValues = []; - const anomalyRegions = []; - let showTrendCrosshairs = false; - let showRateCrosshairs = false; - for (const track of tracks) { - const { - renderer: trackRenderer, - series: trackSeries, - axis: trackAxis, - rowOffset: trackRowOffset, - analysis: trackAnalysis, - summaryStats: trackSummaryStats, - trendPts: trackTrendPts, - ratePts: trackRatePts, - rateAxis: trackRateAxis, - deltaPts: trackDeltaPts, - deltaAxis: trackDeltaAxis, - anomalyRegions: trackAnomalyRegions - } = track; - const effectiveAnalysis = trackAnalysis || (analysisMap || /* @__PURE__ */ new Map()).get( - trackSeries.entityId - ) || normalizeHistorySeriesAnalysis(null); - const trackHideSource = this._seriesShouldHideSource( - effectiveAnalysis, - hasSelectedComparisonWindow - ); - if (effectiveAnalysis.show_trend_lines === true && Array.isArray(trackTrendPts) && trackTrendPts.length >= 2) { - if (effectiveAnalysis.show_trend_crosshairs === true) - showTrendCrosshairs = true; - const trendOpts = this._getTrendRenderOptions( - effectiveAnalysis.trend_method, - trackHideSource - ); - const trendVal = trackRenderer._interpolateValue(trackTrendPts, timeMs); - trendValues.push({ - entityId: `trend:${trackSeries.entityId}`, - relatedEntityId: String( - trackSeries.entityId || "" - ), - label: String(trackSeries.label || ""), - baseLabel: String( - trackSeries.label || "" - ), - unit: String(trackSeries.unit || ""), - color: hexToRgba( - trackSeries.color, - trendOpts.colorAlpha - ), - opacity: trendOpts.lineOpacity, - hasValue: trendVal != null, - value: trendVal ?? null, - ...trendVal != null ? { - x: x2, - y: trackRowOffset + trackRenderer.yOf( - trendVal, - trackAxis.min, - trackAxis.max - ) - } : {}, - axisSide: "left", - axisSlot: 0, - trend: true, - rawVisible: !trackHideSource, - showCrosshair: effectiveAnalysis.show_trend_crosshairs === true - }); - } - if (effectiveAnalysis.show_rate_of_change === true && Array.isArray(trackRatePts) && trackRatePts.length >= 2 && trackRateAxis) { - if (effectiveAnalysis.show_rate_crosshairs === true) { - showRateCrosshairs = true; - } - const rateVal = trackRenderer._interpolateValue(trackRatePts, timeMs); - rateValues.push({ - entityId: `rate:${trackSeries.entityId}`, - relatedEntityId: String( - trackSeries.entityId || "" - ), - label: String(trackSeries.label || ""), - baseLabel: String( - trackSeries.label || "" - ), - unit: trackSeries.unit ? `${String(trackSeries.unit)}/h` : "/h", - color: hexToRgba( - trackSeries.color, - trackHideSource ? 0.96 : 0.82 - ), - opacity: trackHideSource ? 0.88 : 0.66, - hasValue: rateVal != null, - value: rateVal ?? null, - ...rateVal != null ? { - x: x2, - y: trackRowOffset + trackRenderer.yOf( - rateVal, - trackRateAxis.min, - trackRateAxis.max - ) - } : {}, - axisSide: "right", - axisSlot: 0, - rate: true, - rawVisible: !trackHideSource, - showCrosshair: effectiveAnalysis.show_rate_crosshairs === true - }); - } - if (effectiveAnalysis.show_delta_analysis === true && effectiveAnalysis.show_delta_tooltip === true && Array.isArray(trackDeltaPts) && trackDeltaPts.length >= 2 && trackDeltaAxis) { - const deltaVal = trackRenderer._interpolateValue(trackDeltaPts, timeMs); - deltaValues.push({ - entityId: `delta:${trackSeries.entityId}`, - relatedEntityId: String( - trackSeries.entityId || "" - ), - label: String(trackSeries.label || ""), - baseLabel: String( - trackSeries.label || "" - ), - unit: String(trackSeries.unit || ""), - color: hexToRgba( - trackSeries.color, - 0.92 - ), - opacity: 0.82, - hasValue: deltaVal != null, - value: deltaVal ?? null, - ...deltaVal != null ? { - x: x2, - y: trackRowOffset + trackRenderer.yOf( - deltaVal, - trackDeltaAxis.min, - trackDeltaAxis.max - ) - } : {}, - axisSide: "right", - axisSlot: 0, - delta: true, - rawVisible: !trackHideSource - }); - } - if (effectiveAnalysis.show_summary_stats === true && trackSummaryStats) { - const summaryEntries = [ - { - type: "min", - value: trackSummaryStats.min, - alphaV: trackHideSource ? 0.94 : 0.78, - opac: trackHideSource ? 0.94 : 0.72 - }, - { - type: "mean", - value: trackSummaryStats.mean, - alphaV: trackHideSource ? 0.94 : 0.78, - opac: trackHideSource ? 0.94 : 0.72 - }, - { - type: "max", - value: trackSummaryStats.max, - alphaV: trackHideSource ? 0.94 : 0.78, - opac: trackHideSource ? 0.94 : 0.72 - } - ]; - for (const entry of summaryEntries) { - if (!Number.isFinite(entry.value)) continue; - summaryValues.push({ - entityId: `summary:${entry.type}:${trackSeries.entityId}`, - relatedEntityId: String( - trackSeries.entityId || "" - ), - label: String( - trackSeries.label || "" - ), - baseLabel: String( - trackSeries.label || "" - ), - unit: String(trackSeries.unit || ""), - color: hexToRgba( - trackSeries.color, - entry.alphaV - ), - opacity: entry.opac, - hasValue: true, - value: entry.value, - axisSide: "left", - axisSlot: 0, - summaryType: entry.type, - summary: true, - rawVisible: !trackHideSource - }); - } - } - if (effectiveAnalysis.show_threshold_analysis === true) { - const thresholdValue = Number(effectiveAnalysis.threshold_value); - if (Number.isFinite(thresholdValue)) { - thresholdValues.push({ - entityId: `threshold:${trackSeries.entityId}`, - relatedEntityId: String( - trackSeries.entityId || "" - ), - label: String( - trackSeries.label || "" - ), - baseLabel: String( - trackSeries.label || "" - ), - unit: String(trackSeries.unit || ""), - color: hexToRgba( - trackSeries.color, - trackHideSource ? 0.82 : 0.46 - ), - opacity: trackHideSource ? 0.84 : 0.48, - hasValue: true, - value: thresholdValue, - axisSide: "left", - axisSlot: 0, - threshold: true, - rawVisible: !trackHideSource - }); - } - } - if (Array.isArray(trackAnomalyRegions)) { - for (const region of trackAnomalyRegions) { - const clusterPoints = region?.cluster ? region.cluster?.points : void 0; - const regionStartMs = clusterPoints?.[0] ? clusterPoints[0]?.timeMs ?? region.startTime : region.startTime; - const regionEndMs = clusterPoints?.length ? clusterPoints[clusterPoints.length - 1]?.timeMs ?? region.endTime : region.endTime; - if (Number.isFinite(regionStartMs) && Number.isFinite(regionEndMs) && timeMs >= regionStartMs && timeMs <= regionEndMs) { - anomalyRegions.push(region); - } - } - } - } - for (const comparisonSeries of comparisonHoverSeries) { - const seriesEntry = comparisonSeries; - const track = seriesEntry.track; - const trackRenderer = track.renderer; - const trackAxis = track.axis; - const trackRateAxis = track.rateAxis || null; - const trackRowOffset = track.rowOffset; - const trendPts = Array.isArray(seriesEntry.trendPts) ? seriesEntry.trendPts : []; - if (trendPts.length >= 2) { - const trendVal = trackRenderer._interpolateValue(trendPts, timeMs); - trendValues.push({ - entityId: `trend:${seriesEntry.entityId}`, - relatedEntityId: String(seriesEntry.relatedEntityId || ""), - comparisonParentId: String(seriesEntry.comparisonParentId || ""), - label: String(seriesEntry.label || ""), - baseLabel: String(seriesEntry.label || ""), - windowLabel: String(seriesEntry.windowLabel || "Date window"), - unit: String(seriesEntry.unit || ""), - color: hexToRgba(seriesEntry.color, 0.34), - opacity: 0.34, - hasValue: trendVal != null, - value: trendVal ?? null, - ...trendVal != null ? { - x: x2, - y: trackRowOffset + trackRenderer.yOf(trendVal, trackAxis.min, trackAxis.max) - } : {}, - axisSide: "left", - axisSlot: 0, - trend: true, - rawVisible: true, - comparisonDerived: true - }); - } - const ratePts = Array.isArray(seriesEntry.ratePts) ? seriesEntry.ratePts : []; - if (ratePts.length >= 2 && trackRateAxis) { - const rateVal = trackRenderer._interpolateValue(ratePts, timeMs); - rateValues.push({ - entityId: `rate:${seriesEntry.entityId}`, - relatedEntityId: String(seriesEntry.relatedEntityId || ""), - comparisonParentId: String(seriesEntry.comparisonParentId || ""), - label: String(seriesEntry.label || ""), - baseLabel: String(seriesEntry.label || ""), - windowLabel: String(seriesEntry.windowLabel || "Date window"), - unit: seriesEntry.unit ? `${String(seriesEntry.unit)}/h` : "/h", - color: hexToRgba(seriesEntry.color, 0.46), - opacity: 0.46, - hasValue: rateVal != null, - value: rateVal ?? null, - ...rateVal != null ? { - x: x2, - y: trackRowOffset + trackRenderer.yOf( - rateVal, - trackRateAxis.min, - trackRateAxis.max - ) - } : {}, - axisSide: "right", - axisSlot: 0, - rate: true, - rawVisible: true, - comparisonDerived: true, - showCrosshair: analysisMap.get(String(seriesEntry.relatedEntityId || ""))?.show_rate_crosshairs === true - }); - } - const summaryStats = seriesEntry.summaryStats; - if (summaryStats) { - [ - { type: "min", value: summaryStats.min }, - { type: "mean", value: summaryStats.mean }, - { type: "max", value: summaryStats.max } - ].forEach((entry) => { - if (!Number.isFinite(entry.value)) { - return; - } - summaryValues.push({ - entityId: `summary:${entry.type}:${seriesEntry.entityId}`, - relatedEntityId: String(seriesEntry.relatedEntityId || ""), - comparisonParentId: String(seriesEntry.comparisonParentId || ""), - label: String(seriesEntry.label || ""), - baseLabel: String(seriesEntry.label || ""), - windowLabel: String(seriesEntry.windowLabel || "Date window"), - unit: String(seriesEntry.unit || ""), - color: hexToRgba( - seriesEntry.color, - entry.type === "mean" ? 0.44 : 0.24 - ), - opacity: entry.type === "mean" ? 0.52 : 0.3, - hasValue: true, - value: entry.value, - axisSide: "left", - axisSlot: 0, - summaryType: entry.type, - summary: true, - rawVisible: true, - comparisonDerived: true - }); - }); - } - const thresholdValue = Number(seriesEntry.thresholdValue); - if (Number.isFinite(thresholdValue)) { - thresholdValues.push({ - entityId: `threshold:${seriesEntry.entityId}`, - relatedEntityId: String(seriesEntry.relatedEntityId || ""), - comparisonParentId: String(seriesEntry.comparisonParentId || ""), - label: String(seriesEntry.label || ""), - baseLabel: String(seriesEntry.label || ""), - windowLabel: String(seriesEntry.windowLabel || "Date window"), - unit: String(seriesEntry.unit || ""), - color: hexToRgba(seriesEntry.color, 0.28), - opacity: 0.34, - hasValue: true, - value: thresholdValue, - axisSide: "left", - axisSlot: 0, - threshold: true, - rawVisible: true, - comparisonDerived: true - }); - } - } - const hideRawData = tracks.every((track) => { - const eff = track.analysis || (analysisMap || /* @__PURE__ */ new Map()).get( - track.series.entityId - ) || normalizeHistorySeriesAnalysis(null); - return this._seriesShouldHideSource( - eff, - hasSelectedComparisonWindow - ); - }); - const firstWithValue = values.find((value) => value.hasValue === true); - return { - x: x2, - y: firstWithValue?.y ?? splitSelTop + 12, - timeMs, - rangeStartMs: timeMs, - rangeEndMs: timeMs, - values: values.filter((value) => value.hasValue === true), - trendValues, - rateValues, - deltaValues, - summaryValues, - thresholdValues, - comparisonValues: comparisonValues.filter( - (value) => value.hasValue === true - ), - binaryValues: [], - primary: firstWithValue ?? null, - event: hoveredEvents.length > 0 ? (({ _hoverDistanceMs: _2, ...event }) => event)(hoveredEvents[0]) : null, - events: hoveredEvents.map( - ({ _hoverDistanceMs: _d, ...event }) => event - ), - anomalyRegions, - emphasizeGuides: options.emphasizeHoverGuides === true, - showTrendCrosshairs, - showRateCrosshairs, - hideRawData, - splitVertical: { top: splitSelTop, height: splitSelHeight } - }; - }; - const showFromPointer = (clientX, clientY) => { - if (this._chartZoomDragging) { - return; - } - const hover = buildSplitHover(clientX); - if (!hover) { - this._chartLastHover = null; - hideLineChartHover(this); - overlayEl.style.cursor = "default"; - return; - } - this._chartLastHover = hover; - showLineChartCrosshair(this, primaryRenderer, hover); - if (this._config.show_tooltips !== false) { - showLineChartTooltip(this, hover, clientX, clientY); - } else { - hideTooltip(this); - } - dispatchLineChartHover(this, hover); - overlayEl.style.cursor = "crosshair"; - }; - const hideHover = () => { - this._chartLastHover = null; - hideLineChartHover(this); - }; - const onMouseMove = (ev) => showFromPointer(ev.clientX, ev.clientY); - const onMouseLeave = (ev) => { - const nextTarget = ev.relatedTarget; - const addButton = this.querySelector("#chart-add-annotation"); - if (nextTarget && addButton instanceof HTMLElement && addButton.contains(nextTarget)) { - return; - } - hideHover(); - }; - const onTouchMove = (ev) => { - if (ev.touches.length === 1) { - showFromPointer(ev.touches[0].clientX, ev.touches[0].clientY); - } - }; - const onTouchEnd = () => hideHover(); - const onOverlayClick = (ev) => { - if (this._chartZoomDragging) return; - const hover = this._chartLastHover; - const regions = hover?.anomalyRegions; - if (!regions?.length) return; - ev.preventDefault(); - ev.stopPropagation(); - this._handleAnomalyAddAnnotation(regions); - }; - const addAnnotationBtn = this.querySelector( - "#chart-add-annotation" - ); - const onAddBtnClick = (ev) => { - if (!this._canAddAnnotation || !this._chartLastHover) { - return; - } - ev.preventDefault(); - ev.stopPropagation(); - this._handleChartAddAnnotation(this._chartLastHover); - }; - const onAddBtnLeave = (ev) => { - const nextTarget = ev.relatedTarget; - if (nextTarget instanceof Node && overlayEl.contains(nextTarget)) { - return; - } - hideHover(); - }; - overlayEl.addEventListener("mousemove", onMouseMove); - overlayEl.addEventListener("mouseleave", onMouseLeave); - overlayEl.addEventListener("touchmove", onTouchMove, { passive: true }); - overlayEl.addEventListener("touchend", onTouchEnd); - overlayEl.addEventListener("click", onOverlayClick); - addAnnotationBtn?.addEventListener("click", onAddBtnClick); - addAnnotationBtn?.addEventListener("mouseleave", onAddBtnLeave); - this._chartHoverCleanup = () => { - overlayEl.removeEventListener("mousemove", onMouseMove); - overlayEl.removeEventListener("mouseleave", onMouseLeave); - overlayEl.removeEventListener("touchmove", onTouchMove); - overlayEl.removeEventListener("touchend", onTouchEnd); - overlayEl.removeEventListener("click", onOverlayClick); - addAnnotationBtn?.removeEventListener("click", onAddBtnClick); - addAnnotationBtn?.removeEventListener("mouseleave", onAddBtnLeave); - }; - const selection = this.querySelector("#chart-zoom-selection"); - const hideZoomSelection = () => { - if (!selection) { - return; - } - selection.hidden = true; - selection.classList.remove("visible"); - }; - const renderZoomSelection = (startX, currentX) => { - if (!selection) { - return; - } - const left = Math.min(startX, currentX); - const width = Math.abs(currentX - startX); - selection.style.left = `${left}px`; - selection.style.top = `${splitSelTop}px`; - selection.style.width = `${width}px`; - selection.style.height = `${splitSelHeight}px`; - selection.hidden = false; - selection.classList.add("visible"); - }; - let zoomPointerId = null; - let zoomStartX = 0; - let zoomCurrentX = 0; - let zoomDragging = false; - const onZoomPointerMove = (ev) => { - if (zoomPointerId == null || ev.pointerId !== zoomPointerId) { - return; - } - zoomCurrentX = overlayRelX(ev.clientX); - if (!zoomDragging && Math.abs(zoomCurrentX - zoomStartX) < 6) { - return; - } - zoomDragging = true; - this._chartZoomDragging = true; - hideLineChartHover(this); - renderZoomSelection(zoomStartX, zoomCurrentX); - ev.preventDefault(); - }; - const finishZoom = (ev) => { - if (zoomPointerId == null || ev.pointerId !== zoomPointerId) { - return; - } - const didDrag = zoomDragging; - const endX = zoomCurrentX; - window.removeEventListener("pointermove", onZoomPointerMove); - window.removeEventListener("pointerup", finishZoom); - window.removeEventListener("pointercancel", finishZoom); - zoomPointerId = null; - zoomDragging = false; - this._chartZoomDragging = false; - hideZoomSelection(); - if (!didDrag || Math.abs(endX - zoomStartX) < 8) { - return; - } - const startTime = Math.min(stageXToTime(zoomStartX), stageXToTime(endX)); - const endTime = Math.max(stageXToTime(zoomStartX), stageXToTime(endX)); - this._applyZoomRange(startTime, endTime); - }; - const onZoomPointerDown = (ev) => { - if (ev.button !== 0 || !inPlotBoundsX(ev.clientX)) { - return; - } - zoomPointerId = ev.pointerId; - zoomStartX = overlayRelX(ev.clientX); - zoomCurrentX = zoomStartX; - zoomDragging = false; - this._chartZoomDragging = false; - window.addEventListener("pointermove", onZoomPointerMove); - window.addEventListener("pointerup", finishZoom); - window.addEventListener("pointercancel", finishZoom); - }; - const onZoomDoubleClick = (ev) => { - if (!inPlotBoundsX(ev.clientX)) { - return; - } - ev.preventDefault(); - this._clearZoomRange(); - }; - overlayEl.addEventListener("pointerdown", onZoomPointerDown); - overlayEl.addEventListener("dblclick", onZoomDoubleClick); - if (this._chartZoomCleanup) { - this._chartZoomCleanup(); - } - this._chartZoomCleanup = () => { - overlayEl.removeEventListener("pointerdown", onZoomPointerDown); - overlayEl.removeEventListener("dblclick", onZoomDoubleClick); - window.removeEventListener("pointermove", onZoomPointerMove); - window.removeEventListener("pointerup", finishZoom); - window.removeEventListener("pointercancel", finishZoom); - zoomPointerId = null; - zoomDragging = false; - this._chartZoomDragging = false; - hideZoomSelection(); - }; - } - }; - if (!customElements.get("hass-datapoints-history-chart")) { - customElements.define("hass-datapoints-history-chart", HistoryChart$1); - } - function createHiddenSeriesSet(seriesSettings = []) { - return new Set( - (Array.isArray(seriesSettings) ? seriesSettings : []).filter((entry) => entry?.visible === false).map((entry) => entry.entity_id || entry.entity || entry.entityId).filter((value) => Boolean(value)) - ); - } - function createHiddenEventIdSet(hiddenEventIds = []) { - return new Set( - (Array.isArray(hiddenEventIds) ? hiddenEventIds : []).filter(Boolean) - ); - } - async function fetchEvents(hass, startTime, endTime, entityIds) { - try { - const normalizedEntityIds = normalizeCacheIdList(entityIds); - const cacheKey = JSON.stringify({ - type: `${DOMAIN}/events`, - start_time: startTime, - end_time: endTime, - entity_ids: normalizedEntityIds - }); - return await withStableRangeCache(cacheKey, endTime, async () => { - const msg2 = { - type: `${DOMAIN}/events`, - start_time: startTime, - end_time: endTime - }; - if (normalizedEntityIds.length) { - msg2.entity_ids = normalizedEntityIds; - } - const result = await hass.connection.sendMessagePromise( - msg2 - ); - return result.events || []; - }); - } catch (err) { - logger.warn("[hass-datapoints] fetchEvents failed:", err); - return []; - } - } - function invalidateEventsCache() { - return clearStableRangeCacheMatching((key) => { - if (typeof key !== "string") { - return false; - } - return key.includes(`"type":"${DOMAIN}/events"`); - }); - } - async function fetchEventBounds(hass) { - try { - const result = await hass.connection.sendMessagePromise({ - type: `${DOMAIN}/events_bounds` - }); - return { - start: result?.start_time || null, - end: result?.end_time || null - }; - } catch (err) { - logger.warn("[hass-datapoints] fetchEventBounds failed:", err); - return { start: null, end: null }; - } - } - async function deleteEvent(hass, eventId) { - const result = await hass.connection.sendMessagePromise({ - type: `${DOMAIN}/events/delete`, - event_id: eventId - }); - invalidateEventsCache(); - window.dispatchEvent(new CustomEvent("hass-datapoints-event-recorded")); - return result; - } - async function updateEvent(hass, eventId, fields) { - const result = await hass.connection.sendMessagePromise({ - type: `${DOMAIN}/events/update`, - event_id: eventId, - ...fields - }); - invalidateEventsCache(); - window.dispatchEvent(new CustomEvent("hass-datapoints-event-recorded")); - return result; - } - const t$1 = { ATTRIBUTE: 1, CHILD: 2 }, e$1 = (t2) => (...e2) => ({ _$litDirective$: t2, values: e2 }); - let i$1 = class i { - constructor(t2) { - } - get _$AU() { - return this._$AM._$AU; - } - _$AT(t2, e2, i2) { - this._$Ct = t2, this._$AM = e2, this._$Ci = i2; - } - _$AS(t2, e2) { - return this.update(t2, e2); - } - update(t2, e2) { - return this.render(...e2); - } - }; - const { I: t } = j, i = (o2) => o2, s = () => document.createComment(""), v = (o2, n2, e2) => { - const l2 = o2._$AA.parentNode, d2 = void 0 === n2 ? o2._$AB : n2._$AA; - if (void 0 === e2) { - const i2 = l2.insertBefore(s(), d2), n3 = l2.insertBefore(s(), d2); - e2 = new t(i2, n3, o2, o2.options); - } else { - const t2 = e2._$AB.nextSibling, n3 = e2._$AM, c2 = n3 !== o2; - if (c2) { - let t3; - e2._$AQ?.(o2), e2._$AM = o2, void 0 !== e2._$AP && (t3 = o2._$AU) !== n3._$AU && e2._$AP(t3); - } - if (t2 !== d2 || c2) { - let o3 = e2._$AA; - for (; o3 !== t2; ) { - const t3 = i(o3).nextSibling; - i(l2).insertBefore(o3, d2), o3 = t3; - } - } - } - return e2; - }, u$1 = (o2, t2, i2 = o2) => (o2._$AI(t2, i2), o2), m = {}, p = (o2, t2 = m) => o2._$AH = t2, M = (o2) => o2._$AH, h = (o2) => { - o2._$AR(), o2._$AA.remove(); - }; - const u = (e2, s2, t2) => { - const r2 = /* @__PURE__ */ new Map(); - for (let l2 = s2; l2 <= t2; l2++) r2.set(e2[l2], l2); - return r2; - }, c = e$1(class extends i$1 { - constructor(e2) { - if (super(e2), e2.type !== t$1.CHILD) throw Error("repeat() can only be used in text expressions"); - } - dt(e2, s2, t2) { - let r2; - void 0 === t2 ? t2 = s2 : void 0 !== s2 && (r2 = s2); - const l2 = [], o2 = []; - let i2 = 0; - for (const s3 of e2) l2[i2] = r2 ? r2(s3, i2) : i2, o2[i2] = t2(s3, i2), i2++; - return { values: o2, keys: l2 }; - } - render(e2, s2, t2) { - return this.dt(e2, s2, t2).values; - } - update(s2, [t2, r2, c2]) { - const d2 = M(s2), { values: p$12, keys: a2 } = this.dt(t2, r2, c2); - if (!Array.isArray(d2)) return this.ut = a2, p$12; - const h$12 = this.ut ??= [], v$12 = []; - let m2, y2, x2 = 0, j2 = d2.length - 1, k2 = 0, w = p$12.length - 1; - for (; x2 <= j2 && k2 <= w; ) if (null === d2[x2]) x2++; - else if (null === d2[j2]) j2--; - else if (h$12[x2] === a2[k2]) v$12[k2] = u$1(d2[x2], p$12[k2]), x2++, k2++; - else if (h$12[j2] === a2[w]) v$12[w] = u$1(d2[j2], p$12[w]), j2--, w--; - else if (h$12[x2] === a2[w]) v$12[w] = u$1(d2[x2], p$12[w]), v(s2, v$12[w + 1], d2[x2]), x2++, w--; - else if (h$12[j2] === a2[k2]) v$12[k2] = u$1(d2[j2], p$12[k2]), v(s2, d2[x2], d2[j2]), j2--, k2++; - else if (void 0 === m2 && (m2 = u(a2, k2, w), y2 = u(h$12, x2, j2)), m2.has(h$12[x2])) if (m2.has(h$12[j2])) { - const e2 = y2.get(a2[k2]), t3 = void 0 !== e2 ? d2[e2] : null; - if (null === t3) { - const e3 = v(s2, d2[x2]); - u$1(e3, p$12[k2]), v$12[k2] = e3; - } else v$12[k2] = u$1(t3, p$12[k2]), v(s2, d2[x2], t3), d2[e2] = null; - k2++; - } else h(d2[j2]), j2--; - else h(d2[x2]), x2++; - for (; k2 <= w; ) { - const e2 = v(s2, v$12[w + 1]); - u$1(e2, p$12[k2]), v$12[k2++] = e2; - } - for (; x2 <= j2; ) { - const e2 = d2[x2++]; - null !== e2 && h(e2); - } - return this.ut = a2, p(s2, v$12), E; - } - }); - const styles$R = i$5` + + + +
+
+ +
+
+ + + + + +
+
+
+
+
+
+
+
+
+
+
`; + } + disconnectedCallback() { + if (this._chartHoverCleanup) { + this._chartHoverCleanup(); + this._chartHoverCleanup = null; + } + if (this._chartZoomCleanup) { + this._chartZoomCleanup(); + this._chartZoomCleanup = null; + } + if (this._chartScrollViewportEl) this._chartScrollViewportEl.removeEventListener("scroll", this._onChartScroll); + if (this._scrollZoomApplyTimer !== null) { + clearTimeout(this._scrollZoomApplyTimer); + this._scrollZoomApplyTimer = null; + } + } + get hass() { + return this._hass; + } + set hass(value) { + this._hass = value; + } + /** True when the current user may create a data point via the chart + button. */ + get _canAddAnnotation() { + return this._config.show_add_annotation_button !== false && this._hass?.user?.is_admin === true; + } + _el(id) { + return this.querySelector(`#${id}`); + } + _els(selector) { + return this.querySelectorAll(selector); + } + /** + * Show or hide the loading spinner. + * Mirrors _setChartLoading from card-chart-base-legacy.js. + */ + _setChartLoading(isLoading) { + const loadingEl = this._el("loading"); + if (!loadingEl) return; + loadingEl.classList.toggle("active", !!isLoading); + } + /** + * Set or clear the chart message (shown when data is unavailable). + * Mirrors _setChartMessage from card-chart-base-legacy.js. + */ + _setChartMessage(message = "") { + const messageEl = this._el("chart-message"); + if (!messageEl) return; + messageEl.textContent = message || ""; + messageEl.classList.toggle("visible", !!message); + } + /** + * Draw an empty grid frame (shown while data is loading for the first time). + * Ported from _drawEmptyChartFrame in card-history.js. + */ + _drawEmptyChartFrame(t0, t1) { + const canvas = this._el("chart"); + const wrap = this; + const scrollViewport = this._el("chart-scroll-viewport"); + const chartStage = this._el("chart-stage"); + if (!canvas) return; + this._syncTopSlotOffset(); + const availableHeight = this._getAvailableChartHeight(280); + const viewportWidth = Math.max(scrollViewport?.clientWidth || wrap?.clientWidth || 360, 360); + if (chartStage) { + chartStage.style.width = `${viewportWidth}px`; + chartStage.style.height = `${availableHeight}px`; + } + const { w, h } = setupCanvas(canvas, chartStage || wrap, availableHeight, viewportWidth); + const renderer = new ChartRenderer(canvas, w, h); + renderer.labelColor = resolveChartLabelColor(this); + renderer.clear(); + renderer.drawGrid(t0, t1, [{ + key: "placeholder", + min: 0, + max: 1, + side: "left", + unit: "", + color: null + }], void 0, 5, { fixedAxisOverlay: true }); + renderChartAxisOverlays(this, renderer, renderer._activeAxes || []); + } + /** + * Compute the chart canvas height from the surrounding card layout. + * Ported from _getAvailableChartHeight in card-history.js. + * When called from within this component (no shadow root) all selectors + * target the host element's light DOM ancestors via `closest`. + */ + _getAvailableChartHeight(minChartHeight = 280) { + const card = this.closest("ha-card"); + const header = card?.querySelector(".card-header"); + const topSlot = this._el("chart-top-slot"); + const legend = this._el("legend"); + const scrollViewport = this._el("chart-scroll-viewport"); + const wrap = this; + const cardHeight = card?.clientHeight || 0; + const occupiedHeight = (header?.offsetHeight || 0) + (topSlot && !topSlot.hidden ? topSlot.offsetHeight || 0 : 0) + (legend?.offsetHeight || 0); + const cardDerivedHeight = cardHeight ? Math.max(0, cardHeight - occupiedHeight) : 0; + const viewportHeight = scrollViewport?.clientHeight || 0; + const wrapHeight = wrap?.clientHeight || 0; + return Math.max(minChartHeight, cardDerivedHeight || viewportHeight || wrapHeight || 0); + } + _syncTopSlotOffset() { + const topSlot = this._el("chart-top-slot"); + const topSlotHeight = topSlot && !topSlot.hidden ? topSlot.offsetHeight || 0 : 0; + this.style.setProperty("--dp-chart-top-slot-height", `${topSlotHeight}px`); + } + /** + * Toggle the legend `wrap-rows` class based on the card height. + * Ported from _updateLegendLayout in card-history.js. + */ + _updateLegendLayout(legendEl) { + if (!legendEl) return; + const cardHeight = this.closest("ha-card")?.clientHeight || 0; + if (this._legendWrapRows) this._legendWrapRows = cardHeight >= HISTORY_LEGEND_WRAP_DISABLE_HEIGHT_PX; + else this._legendWrapRows = cardHeight >= HISTORY_LEGEND_WRAP_ENABLE_HEIGHT_PX; + legendEl.classList.toggle("wrap-rows", this._legendWrapRows); + } + /** + * Show or hide the "Adjust Y-Axis" button and wire up its click handler. + * Ported from _setAdjustAxisButtonVisibility in card-history.js. + */ + _setAdjustAxisButtonVisibility(visible, onAdjust) { + const button = this._el("chart-adjust-axis"); + if (!button) return; + button.hidden = !visible; + if (visible) { + button.onclick = () => { + this._adjustComparisonAxisScale = true; + onAdjust?.(); + this._redrawLastDraw(); + }; + return; + } + button.onclick = null; + } + /** + * Render (or clear) the comparison preview overlay badge. + * Ported from _renderComparisonPreviewOverlay in card-history.js. + */ + _renderComparisonPreviewOverlay(renderer = null) { + const overlayEl = this._el("chart-preview-overlay"); + if (!overlayEl) return; + const overlay = this._config?.comparison_preview_overlay || null; + if (!overlay?.window_range_label || !overlay?.actual_range_label) { + overlayEl.hidden = true; + overlayEl.innerHTML = ""; + return; + } + if (renderer?.pad?.left != null) overlayEl.style.left = `${Math.max(8, renderer.pad.left + 8)}px`; + else overlayEl.style.left = ""; + D(b` +
+ ${msg("Date window:")} ${overlay.window_range_label} +
+
+ ${msg("Actual:")} ${overlay.actual_range_label} +
+ `, overlayEl); + overlayEl.hidden = false; + } + /** + * Queue an async draw, discarding any in-flight stale requests. + * Ported from _queueDrawChart in card-history.js. + */ + _queueDrawChart(histResult, statsResult, events, t0, t1, options = {}) { + const drawRequestId = ++this._drawRequestId; + logger$1.log("[hass-datapoints history-card] draw queued", { + drawRequestId, + loading: options.loading ?? false + }); + this._drawChart(histResult, statsResult, events, t0, t1, { + ...options, + drawRequestId + }).catch((error) => { + if (drawRequestId !== this._drawRequestId) return; + logger$1.error("[hass-datapoints history-card] draw failed", error); + this._setChartLoading(false); + this._setChartMessage("Failed to render chart."); + }); + } + _redrawLastDraw() { + if (this._lastDrawArgs.length !== 5 && this._lastDrawArgs.length !== 6) return; + const [histResult, statsResult, events, t0, t1, options = {}] = this._lastDrawArgs; + this._queueDrawChart(histResult, statsResult, events, t0, t1, options); + } + _buildBackendAnomalyConfig(analysis) { + const config = { + anomaly_methods: Array.isArray(analysis.anomaly_methods) ? analysis.anomaly_methods : void 0, + anomaly_sensitivity: typeof analysis.anomaly_sensitivity === "string" ? analysis.anomaly_sensitivity : void 0, + anomaly_overlap_mode: typeof analysis.anomaly_overlap_mode === "string" ? analysis.anomaly_overlap_mode : void 0, + anomaly_rate_window: typeof analysis.anomaly_rate_window === "string" ? analysis.anomaly_rate_window : void 0, + anomaly_zscore_window: typeof analysis.anomaly_zscore_window === "string" ? analysis.anomaly_zscore_window : void 0, + anomaly_persistence_window: typeof analysis.anomaly_persistence_window === "string" ? analysis.anomaly_persistence_window : void 0, + trend_method: typeof analysis.trend_method === "string" ? analysis.trend_method : void 0, + trend_window: typeof analysis.trend_window === "string" ? analysis.trend_window : void 0, + anomaly_use_sampled_data: analysis.anomaly_use_sampled_data !== false + }; + if (analysis.anomaly_use_sampled_data !== false) { + config.sample_interval = typeof analysis.sample_interval === "string" ? analysis.sample_interval : null; + config.sample_aggregate = typeof analysis.sample_aggregate === "string" ? analysis.sample_aggregate : null; + } + return config; + } + /** Build normalised state list for an entity from histResult/statsResult. Overridden by parent. */ + _buildEntityStateList(entityId, histResult, statsResult) { + return mergeNumericHistoryWithStatistics(normalizeNumericHistory(entityId, getHistoryStatesForEntity$1(histResult, entityId, this._seriesSettings?.map((seriesSetting) => seriesSetting.entity_id).filter(Boolean) ?? [])), normalizeStatisticsHistory(entityId, statsResult)); + } + /** Build binary state spans from a state list. */ + _buildBinaryStateSpans(stateList, t0, t1) { + const spans = []; + if (!stateList.length) return spans; + let current = stateList[0]; + for (let i = 1; i < stateList.length; i++) { + const next = stateList[i]; + const start = Math.max(current.lu * 1e3, t0); + const end = Math.min(next.lu * 1e3, t1); + if (end > start) spans.push({ + start, + end, + state: current.s + }); + current = next; + } + const lastStart = Math.max(current.lu * 1e3, t0); + if (t1 > lastStart) spans.push({ + start: lastStart, + end: t1, + state: current.s + }); + return spans; + } + /** Return the "on" label for a binary_sensor entity. */ + _binaryOnLabel(entityId) { + const dc = this._hass?.states?.[entityId]?.attributes?.device_class; + return binaryOnLabel(dc ?? ""); + } + /** Return the "off" label for a binary_sensor entity. */ + _binaryOffLabel(entityId) { + const dc = this._hass?.states?.[entityId]?.attributes?.device_class; + return binaryOffLabel(dc ?? ""); + } + /** Normalise the statistics history array for an entity. */ + _normalizeStatisticsHistory(entityId, statsData) { + return normalizeStatisticsHistory(entityId, statsData); + } + /** Render legend items for the given series and binary backgrounds. */ + _renderLegend(series, binaryBackgrounds) { + const legendEl = this.querySelector("#legend"); + if (!legendEl) return; + const allItems = [...series, ...binaryBackgrounds]; + this._updateLegendLayout(legendEl); + D(b`${allItems.map((item) => { + return b` +
+ +
+ `; + })}`, legendEl); + } + /** Draw a single series line onto the renderer, with optional data-gap rendering. */ + _drawSeriesLine(renderer, pts, color, t0, t1, min, max, opts) { + const r = renderer; + const config = this._config; + const showGaps = config?.show_data_gaps !== false; + const gapThresholdMs = SAMPLE_INTERVAL_MS[config?.data_gap_threshold || "2h"] ?? 120 * 6e4; + if (!showGaps || pts.length < 2 || !Number.isFinite(gapThresholdMs)) { + r.drawLine(pts, color, t0, t1, min, max, opts); + return; + } + const segments = []; + const gapBridges = []; + let current = [pts[0]]; + for (let i = 1; i < pts.length; i++) if (pts[i][0] - pts[i - 1][0] > gapThresholdMs) { + segments.push(current); + gapBridges.push([pts[i - 1], pts[i]]); + current = [pts[i]]; + } else current.push(pts[i]); + segments.push(current); + if (segments.length === 1) { + r.drawLine(pts, color, t0, t1, min, max, opts); + return; + } + for (const seg of segments) r.drawLine(seg, color, t0, t1, min, max, opts); + const gapColor = color.startsWith("rgba") ? color.replace(/[\d.]+\)$/, "0.35)") : `${color}59`; + for (const [lastPt, firstPt] of gapBridges) r.drawLine([lastPt, firstPt], gapColor, t0, t1, min, max, { + dashed: true, + lineWidth: 1.2, + lineOpacity: .5 + }); + const boundaryPoints = gapBridges.flatMap(([a, b]) => [a, b]); + r.drawGapMarkers(boundaryPoints, color, t0, t1, min, max); + } + /** Draw event point icons onto the renderer. */ + _drawRecordedEventPoints(_renderer, _visibleSeries, _events, _t0, _t1, _opts) { + const renderer = _renderer; + const series = _visibleSeries; + const events = _events; + const opts = _opts; + const overlay = this.querySelector("#chart-icon-overlay"); + if (overlay && !opts.skipOverlayClear) overlay.innerHTML = ""; + if (!renderer || !events?.length) return []; + const yOffset = Number.isFinite(opts.yOffset) ? opts.yOffset : 0; + const hits = []; + const { ctx } = renderer; + const showIcons = opts.showIcons !== false; + const fallbackHighlightedIds = Array.isArray(this._config?.hovered_event_ids) ? this._config.hovered_event_ids : []; + const highlightedEventIds = new Set(Array.isArray(opts.highlightedEventIds) ? opts.highlightedEventIds : fallbackHighlightedIds); + for (const event of events) { + const timestamp = new Date(event.timestamp).getTime(); + if (timestamp < _t0 || timestamp > _t1) continue; + const eventEntityIds = Array.isArray(event.entity_ids) ? event.entity_ids : []; + const x = renderer.xOf(timestamp, _t0, _t1); + const findSeriesWithValue = (candidates) => { + for (const candidate of candidates) { + if (!candidate?.pts?.length || !candidate.axis) continue; + const candidateValue = renderer._interpolateValue(candidate.pts, timestamp); + if (candidateValue == null) continue; + return { + series: candidate, + value: candidateValue + }; + } + return null; + }; + const matchingSeriesCandidates = eventEntityIds.map((entityId) => series.find((entry) => entry.entityId === entityId)).filter((s) => s != null); + const matchingSeriesHit = findSeriesWithValue(matchingSeriesCandidates); + const linkedToOtherTarget = eventEntityIds.length > 0 && matchingSeriesCandidates.length === 0; + const fallbackSeriesHit = matchingSeriesHit || (linkedToOtherTarget ? null : findSeriesWithValue([...series].reverse())); + const targetSeries = fallbackSeriesHit?.series || null; + const hasNumericTarget = !!(targetSeries?.pts?.length && targetSeries.axis); + const value = hasNumericTarget ? fallbackSeriesHit?.value ?? null : null; + if (hasNumericTarget && value == null) continue; + let y; + if (hasNumericTarget) y = renderer.yOf(value, targetSeries.axis.min, targetSeries.axis.max); + else if (linkedToOtherTarget) y = renderer.pad.top + renderer.ch; + else y = renderer.pad.top + 12; + const color = event.color || targetSeries?.color || "#03a9f4"; + const isHighlighted = highlightedEventIds.has(String(event.id || "")); + const highlightOuterRadius = showIcons ? 18 : 10; + const highlightInnerRadius = showIcons ? 15 : 8; + const outerRadius = showIcons ? 13 : 6; + const innerRadius = showIcons ? 11 : 4; + if (isHighlighted) { + ctx.save(); + ctx.beginPath(); + ctx.arc(x, y, highlightOuterRadius, 0, Math.PI * 2); + ctx.fillStyle = hexToRgba(color, .18); + ctx.fill(); + ctx.restore(); + ctx.save(); + ctx.beginPath(); + ctx.arc(x, y, highlightInnerRadius, 0, Math.PI * 2); + ctx.strokeStyle = color; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + } + ctx.save(); + ctx.beginPath(); + ctx.arc(x, y, outerRadius, 0, Math.PI * 2); + ctx.fillStyle = "rgba(255,255,255,0.92)"; + ctx.fill(); + ctx.restore(); + ctx.save(); + ctx.beginPath(); + ctx.arc(x, y, innerRadius, 0, Math.PI * 2); + ctx.fillStyle = color; + ctx.fill(); + ctx.restore(); + const navigateToHistory = (ev) => { + ev.preventDefault(); + ev.stopPropagation(); + navigateToDataPointsHistory(this, { + entity_id: event.entity_ids || [], + device_id: [], + area_id: [], + label_id: [] + }, { + start_time: this._config?.start_time || null, + end_time: this._config?.end_time || null, + zoom_start_time: this._config?.zoom_start_time || null, + zoom_end_time: this._config?.zoom_end_time || null, + datapoint_scope: this._config?.datapoint_scope + }); + }; + if (overlay && showIcons) { + const iconEl = document.createElement("button"); + iconEl.type = "button"; + iconEl.className = "chart-event-icon"; + iconEl.style.left = `${x}px`; + iconEl.style.top = `${y + yOffset}px`; + iconEl.title = event.message || "Open related history"; + iconEl.setAttribute("aria-label", event.message || "Open related history"); + D(b``, iconEl); + iconEl.addEventListener("click", navigateToHistory); + overlay.appendChild(iconEl); + } else if (overlay) { + const hitEl = document.createElement("button"); + hitEl.type = "button"; + hitEl.className = "chart-event-icon"; + hitEl.style.left = `${x}px`; + hitEl.style.top = `${y + yOffset}px`; + hitEl.title = event.message || "Open related history"; + hitEl.setAttribute("aria-label", event.message || "Open related history"); + hitEl.addEventListener("click", navigateToHistory); + overlay.appendChild(hitEl); + } + hits.push({ + event, + entityId: targetSeries?.entityId || null, + unit: targetSeries?.unit || "", + value, + x, + y + }); + } + return hits; + } + /** Get [min, max] extent for an axis value array. */ + _getAxisValueExtent(values) { + return getAxisValueExtent(values); + } + _getComparisonWindowLineStyle(isHovered, isSelected, hoveringDifferentComparison) { + if (isHovered) return { + lineOpacity: 1, + dashed: false, + hoverOpacity: .85 + }; + if (hoveringDifferentComparison && isSelected) return { + lineOpacity: .25, + lineWidth: 1.25, + dashed: false, + hoverOpacity: .25 + }; + return { + lineOpacity: .85, + dashed: false, + hoverOpacity: .85 + }; + } + /** Sync chart viewport scroll to the current zoom range. */ + _syncChartViewportScroll(_t0, _t1, _canvasWidth) { + if (!this._chartScrollViewportEl) return; + const viewport = this._chartScrollViewportEl; + if (!this._zoomRange) { + if (viewport.scrollLeft !== 0) { + this._scrollSyncSuspended = true; + this._ignoreNextProgrammaticScrollEvent = true; + viewport.scrollLeft = 0; + window.requestAnimationFrame(() => { + this._scrollSyncSuspended = false; + }); + } + return; + } + if (this._skipNextScrollViewportSync) { + this._skipNextScrollViewportSync = false; + return; + } + const viewportWidth = viewport.clientWidth; + const totalMs = Math.max(1, _t1 - _t0); + const visibleSpanMs = totalMs * Math.min(1, viewportWidth / Math.max(_canvasWidth, viewportWidth)); + const maxScrollLeft = Math.max(0, Math.max(_canvasWidth, viewportWidth) - viewportWidth); + const maxStartOffsetMs = Math.max(0, totalMs - visibleSpanMs); + const clampedStart = clampChartValue(this._zoomRange.start, _t0, _t1 - visibleSpanMs); + const nextLeft = (maxStartOffsetMs > 0 ? (clampedStart - _t0) / maxStartOffsetMs : 0) * maxScrollLeft; + const currentLeft = viewport.scrollLeft; + if (Math.abs(currentLeft - nextLeft) < 2) return; + this._scrollSyncSuspended = true; + this._lastProgrammaticScrollLeft = nextLeft; + this._ignoreNextProgrammaticScrollEvent = true; + viewport.scrollLeft = nextLeft; + window.requestAnimationFrame(() => { + this._scrollSyncSuspended = false; + }); + } + /** Ensure the context annotation dialog element is present. */ + _ensureContextAnnotationDialog() { + const parentCard = this.getRootNode()?.host ?? null; + if (parentCard?._annotationDialog && typeof parentCard._annotationDialog.ensureDialog === "function") parentCard._annotationDialog.ensureDialog(); + } + /** Open the context annotation dialog with the given hover data. */ + _openContextAnnotationDialog(_hover) { + const parentCard = this.getRootNode()?.host ?? null; + if (parentCard?._annotationDialog && typeof parentCard._annotationDialog.open === "function") parentCard._annotationDialog.open(_hover); + } + /** Handle context menu on the chart canvas. */ + async _handleChartContextMenu(_hover) { + const annotationDialog = (this.getRootNode()?.host ?? null)?._annotationDialog; + if (!_hover || !this._hass || annotationDialog?.isOpen?.()) return; + this._openContextAnnotationDialog(_hover); + } + /** Handle add-annotation click from the chart hover layer. */ + _handleChartAddAnnotation(_hover) { + const annotationDialog = (this.getRootNode()?.host ?? null)?._annotationDialog; + if (!_hover || !this._hass || !this._hass.user?.is_admin || annotationDialog?.isOpen?.()) return; + this._openContextAnnotationDialog(_hover); + } + /** Handle anomaly cluster click — opens the annotation dialog prefilled with anomaly details. */ + _handleAnomalyAddAnnotation(_regions) { + const regions = _regions; + if (!regions?.length || !this._hass) return; + const prefill = this._buildAnomalyAnnotationPrefill(regions); + const timeMs = ((regions[0]?.cluster?.points)?.reduce((peak, p) => !peak || Math.abs(p.residual) > Math.abs(peak.residual) ? p : peak, null))?.timeMs ?? this._chartLastHover?.timeMs ?? Date.now(); + this._openContextAnnotationDialog({ + timeMs, + annotationPrefill: prefill + }); + } + /** Build an annotation prefill object from anomaly regions. */ + _buildAnomalyAnnotationPrefill(_regions) { + const regions = _regions; + if (!regions?.length) return {}; + const content = buildAnomalyTooltipContent(regions); + const entityId = regions[0]?.relatedEntityId ?? null; + return { + message: content ? `${content.description}\n${content.alert}` : "", + icon: "mdi:alert-circle", + linkedTarget: entityId ? { entity_id: [entityId] } : null + }; + } + /** Fire backend anomaly detection requests. */ + _fireBackendAnomalyRequests(_anomalyEntityIds, _analysisMap, _startIso, _endIso) { + if (!_anomalyEntityIds || !_anomalyEntityIds.length || !this._hass) return; + const hass = this._hass; + _anomalyEntityIds.forEach((entityId) => { + const analysis = _analysisMap.get(entityId); + if (!analysis) return; + const config = this._buildBackendAnomalyConfig(analysis); + const configKey = JSON.stringify({ + ...config, + anomaly_methods: [...config.anomaly_methods || []].sort() + }); + const cached = this._backendAnomalyByEntity.get(entityId); + if (cached && cached.configKey === configKey) return; + this._pendingAnomalyEntityIds.add(entityId); + this._setChartLoading(true); + fetchAnomaliesFromBackend(hass, entityId, _startIso, _endIso, config).then((clusters) => { + this._pendingAnomalyEntityIds.delete(entityId); + if (_startIso !== new Date(this._lastT0).toISOString() || _endIso !== new Date(this._lastT1).toISOString()) { + if (this._pendingAnomalyEntityIds.size === 0) this._setChartLoading(false); + return; + } + this._backendAnomalyByEntity.set(entityId, { + configKey, + clusters + }); + if (this._analysisCache?.result) { + const existing = this._analysisCache.result.anomalySeries || []; + const idx = existing.findIndex((entry) => entry.entityId === entityId); + const entry = { + entityId, + anomalyClusters: clusters + }; + if (idx >= 0) existing[idx] = entry; + else existing.push(entry); + } + if (this._lastHistResult && this._lastEvents) this._queueDrawChart(this._lastHistResult, this._lastStatsResult || {}, this._filterEvents(this._lastEvents), this._lastT0, this._lastT1, { loading: this._pendingAnomalyEntityIds.size > 0 }); + else if (this._pendingAnomalyEntityIds.size === 0) this._setChartLoading(false); + }).catch((err) => { + this._pendingAnomalyEntityIds.delete(entityId); + if (this._pendingAnomalyEntityIds.size === 0) this._setChartLoading(false); + logger$1.warn("[hass-datapoints history-card] backend anomaly fetch failed", { + entityId, + err + }); + }); + }); + } + /** Dispatch a zoom preview event. */ + _dispatchZoomPreview(_range) { + const range = _range; + this.dispatchEvent(new CustomEvent("hass-datapoints-chart-zoom", { + bubbles: true, + composed: true, + detail: range ? { + startTime: range.startTime, + endTime: range.endTime, + preview: true + } : { + startTime: null, + endTime: null, + preview: true + } + })); + } + /** Apply a zoom range to the chart. */ + _applyZoomRange(_startTime, _endTime) { + const start = Math.min(_startTime, _endTime); + const end = Math.max(_startTime, _endTime); + if (!(start < end)) return; + this._zoomRange = { + start, + end + }; + this.dispatchEvent(new CustomEvent("hass-datapoints-zoom-apply", { + bubbles: true, + composed: true, + detail: { + start, + end + } + })); + this._redrawLastDraw(); + } + /** Clear the active zoom range. */ + _clearZoomRange() { + if (!this._zoomRange) return; + this._zoomRange = null; + this.dispatchEvent(new CustomEvent("hass-datapoints-zoom-apply", { + bubbles: true, + composed: true, + detail: null + })); + this._redrawLastDraw(); + } + /** Filter events list by hidden IDs and message filter. */ + _filterEvents(_events) { + const events = _events; + const query = String(this._config?.message_filter || "").trim().toLowerCase(); + const visibleEvents = events.filter((event) => !this._hiddenEventIds.has(event?.id ?? "")); + if (!query) return visibleEvents; + return visibleEvents.filter((event) => { + return [ + event?.message || "", + event?.annotation || "", + ...(event?.entity_ids || []).filter(Boolean) + ].join("\n").toLowerCase().includes(query); + }); + } + /** Build correlated anomaly spans across series. */ + _buildCorrelatedAnomalySpans(_visibleSeries, _anomalyClustersMap, _analysisMap) { + const visibleSeries = _visibleSeries; + const anomalyClustersMap = _anomalyClustersMap; + const seriesIntervals = []; + for (const seriesItem of visibleSeries) { + if (_analysisMap.get(seriesItem.entityId)?.show_anomalies !== true) continue; + const clusters = anomalyClustersMap.get(seriesItem.entityId) || []; + if (!clusters.length) continue; + const pts = seriesItem.pts; + let tolerance = 6e4; + if (Array.isArray(pts) && pts.length >= 2) { + const intervals = []; + for (let i = 1; i < pts.length; i++) { + const diff = pts[i][0] - pts[i - 1][0]; + if (diff > 0) intervals.push(diff); + } + if (intervals.length) { + intervals.sort((a, b) => a - b); + const mid = Math.floor(intervals.length / 2); + tolerance = intervals.length % 2 === 0 ? (intervals[mid - 1] + intervals[mid]) / 2 : intervals[mid]; + tolerance = Math.max(tolerance, 1e3); + } + } + const entityIntervals = []; + for (const cluster of clusters) { + if (!Array.isArray(cluster.points) || cluster.points.length === 0) continue; + const startTime = cluster.points[0]?.timeMs; + const endTime = cluster.points[cluster.points.length - 1]?.timeMs; + if (!Number.isFinite(startTime) || !Number.isFinite(endTime)) continue; + entityIntervals.push({ + start: Math.min(startTime, endTime) - tolerance, + end: Math.max(startTime, endTime) + tolerance + }); + } + if (entityIntervals.length) seriesIntervals.push({ + entityId: seriesItem.entityId, + intervals: entityIntervals + }); + } + if (seriesIntervals.length < 2) return []; + const events = []; + for (const { entityId, intervals } of seriesIntervals) for (const { start, end } of intervals) { + events.push({ + time: start, + delta: 1, + entityId + }); + events.push({ + time: end, + delta: -1, + entityId + }); + } + events.sort((a, b) => a.time - b.time || a.delta - b.delta); + const activeCounts = /* @__PURE__ */ new Map(); + const spans = []; + let spanStart = null; + for (const event of events) { + const next = (activeCounts.get(event.entityId) || 0) + event.delta; + if (next <= 0) activeCounts.delete(event.entityId); + else activeCounts.set(event.entityId, next); + const activeCount = activeCounts.size; + if (spanStart === null && activeCount >= 2) spanStart = event.time; + else if (spanStart !== null && activeCount < 2) { + spans.push({ + start: spanStart, + end: event.time + }); + spanStart = null; + } + } + if (spanStart !== null && events.length > 0) spans.push({ + start: spanStart, + end: events[events.length - 1].time + }); + return spans; + } + /** Filter out annotated anomaly clusters. */ + _filterAnnotatedAnomalyClusters(_seriesItem, _events) { + const seriesItem = _seriesItem; + if (!Array.isArray(seriesItem?.anomalyClusters) || seriesItem.anomalyClusters.length === 0) return []; + const visibleEvents = Array.isArray(_events) ? _events : []; + if (visibleEvents.length === 0) return seriesItem.anomalyClusters; + const getClusterRange = (cluster) => { + if (!Array.isArray(cluster.points) || cluster.points.length === 0) return null; + const startTime = cluster.points[0]?.timeMs; + const endTime = cluster.points[cluster.points.length - 1]?.timeMs; + if (!Number.isFinite(startTime) || !Number.isFinite(endTime)) return null; + return { + startTime: Math.min(startTime, endTime), + endTime: Math.max(startTime, endTime) + }; + }; + return seriesItem.anomalyClusters.filter((cluster) => { + const clusterRange = getClusterRange(cluster); + if (!clusterRange) return true; + return !visibleEvents.some((event) => { + if (!(Array.isArray(event.entity_ids) ? event.entity_ids.filter(Boolean) : []).includes(seriesItem.entityId)) return false; + const eventTime = new Date(event.timestamp).getTime(); + if (!Number.isFinite(eventTime)) return false; + return eventTime >= clusterRange.startTime && eventTime <= clusterRange.endTime; + }); + }); + } + _fireComparisonBackendAnomalyRequests(drawableComparisonResults, analysisMap, renderT0, renderT1) { + if (!drawableComparisonResults.length || !this._hass) return; + const startIso = new Date(renderT0).toISOString(); + const endIso = new Date(renderT1).toISOString(); + drawableComparisonResults.forEach((comparisonWindow) => { + const comparisonStartIso = new Date(renderT0 + comparisonWindow.time_offset_ms).toISOString(); + const comparisonEndIso = new Date(renderT1 + comparisonWindow.time_offset_ms).toISOString(); + this._seriesSettings.forEach((seriesSetting) => { + const entityId = String(seriesSetting.entity_id || ""); + if (!entityId || this._hiddenSeries.has(entityId)) return; + const analysis = analysisMap.get(entityId); + if (!analysis || analysis.show_anomalies !== true) return; + const config = this._buildBackendAnomalyConfig(analysis); + const configKey = JSON.stringify({ + ...config, + windowId: comparisonWindow.id, + startIso, + endIso, + anomaly_methods: [...config.anomaly_methods || []].sort() + }); + const cacheKey = this._getComparisonAnomalyCacheKey(comparisonWindow.id, entityId); + const cached = this._backendComparisonAnomalyByKey.get(cacheKey); + if (cached && cached.configKey === configKey) return; + if (this._pendingComparisonAnomalyKeys.has(cacheKey)) return; + this._pendingComparisonAnomalyKeys.add(cacheKey); + const hass = this._hass; + if (!hass) { + this._pendingComparisonAnomalyKeys.delete(cacheKey); + return; + } + fetchAnomaliesFromBackend(hass, entityId, comparisonStartIso, comparisonEndIso, config).then((clusters) => { + this._pendingComparisonAnomalyKeys.delete(cacheKey); + if (startIso !== new Date(this._lastT0).toISOString() || endIso !== new Date(this._lastT1).toISOString()) return; + const shiftedClusters = this._shiftComparisonAnomalyClusters(Array.isArray(clusters) ? clusters : [], comparisonWindow.time_offset_ms); + this._backendComparisonAnomalyByKey.set(cacheKey, { + configKey, + clusters: shiftedClusters + }); + if (this._lastHistResult && this._lastEvents) this._queueDrawChart(this._lastHistResult, this._lastStatsResult || {}, this._filterEvents(this._lastEvents), this._lastT0, this._lastT1, { loading: false }); + }).catch(() => { + this._pendingComparisonAnomalyKeys.delete(cacheKey); + }); + }); + }); + } + /** Render per-row axis overlays for split-view chart. */ + _renderSplitAxisOverlays(_tracks) { + const tracks = _tracks; + const leftEl = this.querySelector("#chart-axis-left"); + const rightEl = this.querySelector("#chart-axis-right"); + if (!leftEl || !rightEl || !tracks.length) return; + const primaryRenderer = tracks[0].renderer; + const leftWidth = Math.max(0, primaryRenderer.pad.left); + const bottomHeight = Math.max(0, primaryRenderer.pad.bottom); + leftEl.style.width = `${leftWidth}px`; + rightEl.style.width = "0px"; + this.style.setProperty("--dp-chart-axis-left-width", `${leftWidth}px`); + this.style.setProperty("--dp-chart-axis-right-width", "0px"); + this.style.setProperty("--dp-chart-axis-bottom-height", `${bottomHeight}px`); + const labelRight = 10; + const labelItems = []; + for (const { renderer, axis, rowOffset } of tracks) { + if (!axis?.ticks?.length) continue; + for (const tick of axis.ticks) { + const y = rowOffset + renderer.yOf(tick, axis.min, axis.max); + const formatted = renderer._formatAxisTick(tick, axis.unit); + labelItems.push(b`
+ ${formatted} +
`); + } + if (axis.unit) { + const unitY = rowOffset + Math.max(0, primaryRenderer.pad.top - 18); + labelItems.push(b`
+ ${axis.unit} +
`); + } + } + D(b`
+ ${labelItems}`, leftEl); + leftEl.classList.add("visible"); + rightEl.innerHTML = ""; + rightEl.classList.remove("visible"); + } + /** Ordered series settings from the card config. */ + get _seriesSettings() { + return Array.isArray(this._config?.series_settings) ? this._config.series_settings : []; + } + /** Active comparison windows from the card config. */ + get _comparisonWindows() { + return Array.isArray(this._config?.comparison_windows) ? this._config.comparison_windows : []; + } + _getDrawableComparisonResults(comparisonResults) { + const drawableComparisonWindowIds = new Set(this._comparisonWindows.map((window) => String(window?.id || "")).filter((id) => id.length > 0)); + const selectedComparisonWindowId = String(this._config?.selected_comparison_window_id || ""); + const hoveredComparisonWindowId = String(this._config?.hovered_comparison_window_id || ""); + if (selectedComparisonWindowId) drawableComparisonWindowIds.add(selectedComparisonWindowId); + if (hoveredComparisonWindowId) drawableComparisonWindowIds.add(hoveredComparisonWindowId); + if (drawableComparisonWindowIds.size === 0) return []; + return comparisonResults.filter((window) => drawableComparisonWindowIds.has(String(window.id || ""))); + } + _resolveAnomalyClusterDisplay(anomalyClusters, overlapMode, correlatedSpans = []) { + const normalClusters = anomalyClusters.filter((c) => !c.isOverlap); + const overlapClusters = anomalyClusters.filter((c) => c.isOverlap === true); + if (overlapMode === "only") { + const overlapOnlyClusters = this._filterClustersByCorrelatedSpans(anomalyClusters, correlatedSpans); + return { + baseClusters: overlapOnlyClusters, + regionClusters: overlapOnlyClusters, + showCorrelatedSpans: true + }; + } + return { + baseClusters: [...normalClusters, ...overlapClusters], + regionClusters: [...normalClusters, ...overlapClusters], + showCorrelatedSpans: false + }; + } + _filterClustersByCorrelatedSpans(anomalyClusters, correlatedSpans) { + if (!Array.isArray(anomalyClusters) || anomalyClusters.length === 0) return []; + if (!Array.isArray(correlatedSpans) || correlatedSpans.length === 0) return []; + return anomalyClusters.filter((cluster) => { + const points = cluster.points; + if (!Array.isArray(points) || points.length === 0) return false; + const startTime = Number(points[0]?.timeMs); + const endTime = Number(points[points.length - 1]?.timeMs); + if (!Number.isFinite(startTime) || !Number.isFinite(endTime)) return false; + const clusterStart = Math.min(startTime, endTime); + const clusterEnd = Math.max(startTime, endTime); + return correlatedSpans.some((span) => { + const spanStart = Number(span.start); + const spanEnd = Number(span.end); + if (!Number.isFinite(spanStart) || !Number.isFinite(spanEnd)) return false; + return clusterEnd >= spanStart && clusterStart <= spanEnd; + }); + }); + } + _getComparisonAnomalyCacheKey(windowId, entityId) { + return `${windowId}:${entityId}`; + } + _shiftComparisonAnomalyClusters(clusters, timeOffsetMs) { + return (Array.isArray(clusters) ? clusters : []).map((cluster) => ({ + ...cluster, + points: Array.isArray(cluster.points) ? cluster.points.map((point) => ({ + ...point, + timeMs: Number(point.timeMs) - timeOffsetMs + })) : [] + })); + } + async _resolveComparisonWindowPoints(entityId, comparisonWindow, analysis, renderT0, renderT1) { + const stateList = this._buildEntityStateList(entityId, comparisonWindow.histResult, comparisonWindow.statsResult || {}); + const rawPoints = []; + for (const state of stateList) { + const value = parseFloat(state.s); + if (!Number.isNaN(value)) rawPoints.push([Math.round(state.lu * 1e3) - comparisonWindow.time_offset_ms, value]); + } + const interval = analysis.sample_interval || "raw"; + if (interval === "raw" || !this._hass) return rawPoints; + const winStartIso = new Date(renderT0 + comparisonWindow.time_offset_ms).toISOString(); + const winEndIso = new Date(renderT1 + comparisonWindow.time_offset_ms).toISOString(); + const sampledPts = await fetchDownsampledHistory(this._hass, entityId, winStartIso, winEndIso, interval, analysis.sample_aggregate || "mean"); + if (!Array.isArray(sampledPts) || sampledPts.length === 0) { + const targetIntervalMs = SAMPLE_INTERVAL_MS[interval] ?? 0; + if (targetIntervalMs <= 0 || rawPoints.length === 0) return rawPoints; + return downsampleInWorker(rawPoints, targetIntervalMs, analysis.sample_aggregate || "mean"); + } + let finalPts = sampledPts.map(([timestamp, value]) => [timestamp - comparisonWindow.time_offset_ms, value]); + const statsPts = this._normalizeStatisticsHistory(entityId, comparisonWindow.statsResult || {}).map((state) => [Math.round(state.lu * 1e3) - comparisonWindow.time_offset_ms, parseFloat(state.s)]).filter(([, value]) => !Number.isNaN(value)); + if (statsPts.length > 0) { + const firstSampledMs = finalPts[0][0]; + const lastSampledMs = finalPts[finalPts.length - 1][0]; + const rawOutsidePts = statsPts.filter(([timestamp]) => timestamp < firstSampledMs || timestamp > lastSampledMs); + if (rawOutsidePts.length > 0) { + const targetIntervalMs = SAMPLE_INTERVAL_MS[interval] ?? 0; + const sampleAggregate = analysis.sample_aggregate || "mean"; + finalPts = [...targetIntervalMs > 0 ? await downsampleInWorker(rawOutsidePts, targetIntervalMs, sampleAggregate) : rawOutsidePts, ...finalPts]; + finalPts.sort((a, b) => a[0] - b[0]); + } + } + return finalPts; + } + _drawComparisonAnalysisOverlays({ renderer, entityId, seriesColor, comparisonPts, analysis, renderT0, renderT1, axis, rateAxis = null, events = [], comparisonWindowId }) { + const precomputed = this._analysisCache?.result?.comparisonWindowResults?.[comparisonWindowId]?.[entityId]; + if (analysis.show_threshold_analysis === true) { + const thresholdValue = Number(analysis.threshold_value); + if (Number.isFinite(thresholdValue)) { + if (analysis.show_threshold_shading === true && comparisonPts.length) renderer.drawThresholdArea(comparisonPts, thresholdValue, seriesColor, renderT0, renderT1, axis.min, axis.max, { + mode: analysis.threshold_direction === "below" ? "below" : "above", + fillAlpha: .08 + }); + renderer.drawLine([[renderT0, thresholdValue], [renderT1, thresholdValue]], hexToRgba(seriesColor, .28), renderT0, renderT1, axis.min, axis.max, { + lineOpacity: .34, + lineWidth: 1.05, + dashed: true + }); + } + } + if (analysis.show_summary_stats === true) { + const summaryStats = precomputed?.summaryStats ?? this._buildSummaryStats(comparisonPts); + if (Number.isFinite(summaryStats.min) && Number.isFinite(summaryStats.max) && Number.isFinite(summaryStats.mean)) { + if (analysis.show_summary_stats_shading === true) { + renderer.drawGradientBand(summaryStats.min, summaryStats.mean, seriesColor, renderT0, renderT1, axis.min, axis.max, { fillAlpha: .04 }); + renderer.drawGradientBand(summaryStats.max, summaryStats.mean, seriesColor, renderT0, renderT1, axis.min, axis.max, { fillAlpha: .04 }); + } + const summaryEntries = [ + { + value: summaryStats.min, + alpha: .24, + width: 1, + dotted: true + }, + { + value: summaryStats.mean, + alpha: .44, + width: 1.45, + dotted: false + }, + { + value: summaryStats.max, + alpha: .24, + width: 1, + dotted: true + } + ]; + for (const entry of summaryEntries) renderer.drawLine([[renderT0, entry.value], [renderT1, entry.value]], hexToRgba(seriesColor, entry.alpha), renderT0, renderT1, axis.min, axis.max, { + lineOpacity: entry.alpha + .08, + lineWidth: entry.width, + dotted: entry.dotted + }); + } + } + if (analysis.show_trend_lines === true && comparisonPts.length >= 2) { + const trendPts = precomputed?.trendPts ?? this._buildTrendPoints(comparisonPts, analysis.trend_method, analysis.trend_window); + if (trendPts.length >= 2) { + const trendOptions = this._getTrendRenderOptions(analysis.trend_method, false); + renderer.drawLine(trendPts, hexToRgba(seriesColor, Math.max(.3, trendOptions.colorAlpha - .18)), renderT0, renderT1, axis.min, axis.max, { + lineOpacity: Math.max(.3, trendOptions.lineOpacity - .18), + lineWidth: Math.max(1.35, trendOptions.lineWidth - .35), + dashed: trendOptions.dashed, + dotted: trendOptions.dotted + }); + } + } + if (analysis.show_rate_of_change === true && rateAxis && comparisonPts.length >= 2) { + const ratePts = precomputed?.ratePts ?? this._buildRateOfChangePoints(comparisonPts, analysis.rate_window); + if (ratePts.length >= 2) renderer.drawLine(ratePts, hexToRgba(seriesColor, .52), renderT0, renderT1, rateAxis.min, rateAxis.max, { + lineOpacity: .46, + lineWidth: 1.35, + dashPattern: [ + 6, + 3, + 1.5, + 3 + ] + }); + } + if (analysis.show_anomalies === true) { + const cacheKey = this._getComparisonAnomalyCacheKey(comparisonWindowId, entityId); + const cached = this._backendComparisonAnomalyByKey.get(cacheKey); + const clusters = Array.isArray(cached?.clusters) ? cached.clusters : []; + if (clusters.length > 0) { + const regionOptions = { + strokeAlpha: .72, + lineWidth: 1.6, + haloWidth: 3.4, + haloColor: "rgba(255,255,255,0.72)", + haloAlpha: .64, + fillColor: hexToRgba(seriesColor, .06), + fillAlpha: 1, + pointPadding: 8, + minRadiusX: 8, + minRadiusY: 8 + }; + const filteredClusters = this._filterAnnotatedAnomalyClusters({ + entityId, + anomalyClusters: clusters + }, events); + if (filteredClusters.length > 0) renderer.drawAnomalyClusters(filteredClusters, hexToRgba(seriesColor, .74), renderT0, renderT1, axis.min, axis.max, regionOptions); + } + } + } + /** Backend anomaly cache keyed by entity ID. Already declared above. */ + /** + * Full chart draw implementation. + * Ported from _drawChart in card-history.js. + */ + async _drawChart(histResult, statsResult, events, t0, t1, options = {}) { + const chartEvents = Array.isArray(events) ? events : []; + hideTooltip(this); + this._syncTopSlotOffset(); + const canvas = this.querySelector("#chart"); + const zoomOutButton = this.querySelector("#chart-zoom-out"); + const wrap = this; + const scrollViewport = this.querySelector("#chart-scroll-viewport"); + const chartStage = this.querySelector("#chart-stage"); + this._chartScrollViewportEl = scrollViewport; + this._chartStageEl = chartStage; + const series = []; + const axes = []; + const axisMap = /* @__PURE__ */ new Map(); + const binaryBackgrounds = []; + const seriesSettings = this._seriesSettings; + const analysisMap = this._getSeriesAnalysisMap(); + const comparisonResults = Array.isArray(this._lastComparisonResults) ? this._lastComparisonResults : []; + const drawableComparisonResults = this._getDrawableComparisonResults(comparisonResults); + const selectedComparisonWindowId = this._config?.selected_comparison_window_id || null; + const hoveredComparisonWindowId = this._config?.hovered_comparison_window_id || null; + const comparisonPreviewActive = this._comparisonWindows.length > 0; + const delinkYAxis = this._config?.delink_y_axis === true; + const autoAdjustHoveredComparisonAxis = comparisonPreviewActive && !!hoveredComparisonWindowId && !this._zoomRange && !this._chartZoomDragging; + const hoveringDifferentComparison = !!hoveredComparisonWindowId && !!selectedComparisonWindowId && hoveredComparisonWindowId !== selectedComparisonWindowId; + const hasSelectedComparisonWindow = !!selectedComparisonWindowId; + seriesSettings.forEach((seriesSetting, i) => { + const entityId = seriesSetting.entity_id; + if (entityId.split(".")[0] === "binary_sensor") { + const stateList = this._buildEntityStateList(entityId, histResult, statsResult); + const spans = this._buildBinaryStateSpans(stateList, t0, t1); + if (spans.length) binaryBackgrounds.push({ + entityId, + label: entityName(this._hass, entityId) || entityId, + color: seriesSetting.color || COLORS[i % COLORS.length], + onLabel: this._binaryOnLabel(entityId), + offLabel: this._binaryOffLabel(entityId), + spans + }); + return; + } + const stateList = this._buildEntityStateList(entityId, histResult, statsResult); + const pts = []; + const unit = this._hass?.states?.[entityId]?.attributes?.unit_of_measurement || ""; + const axisKey = delinkYAxis ? `${unit || "__unitless__"}::${entityId}` : unit || "__unitless__"; + let axis = axisMap.get(axisKey); + if (!axis) { + axis = { + key: axisKey, + unit, + color: seriesSetting.color || COLORS[i % COLORS.length], + side: axisMap.size === 0 ? "left" : "right", + values: [] + }; + axisMap.set(axisKey, axis); + axes.push(axis); + } + for (const s of stateList) { + const v = parseFloat(s.s); + if (!Number.isNaN(v)) { + pts.push([Math.round(s.lu * 1e3), v]); + axis.values.push(v); + } + } + if (pts.length) series.push({ + entityId, + legendEntityId: entityId, + label: entityName(this._hass, entityId) || entityId, + unit, + pts, + color: seriesSetting.color || COLORS[i % COLORS.length], + axisKey + }); + }); + const _startIso = new Date(t0).toISOString(); + const _endIso = new Date(t1).toISOString(); + if (series.length && this._hass) { + if (t0 !== this._lastT0 || t1 !== this._lastT1) { + this._backendAnomalyByEntity.clear(); + this._backendComparisonAnomalyByKey.clear(); + } + await Promise.all(series.map(async (seriesItem) => { + const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); + const interval = analysis.sample_interval || "raw"; + if (interval === "raw") return; + const hass = this._hass; + if (!hass) return; + try { + const targetIntervalMs = SAMPLE_INTERVAL_MS[interval] ?? 0; + const sampleAggregate = analysis.sample_aggregate || "mean"; + const sampledPts = await fetchDownsampledHistory(hass, seriesItem.entityId, _startIso, _endIso, interval, sampleAggregate); + if (Array.isArray(sampledPts) && sampledPts.length > 0) { + const statsPts = this._normalizeStatisticsHistory(seriesItem.entityId, statsResult).map((s) => [Math.round(s.lu * 1e3), parseFloat(s.s)]).filter(([, v]) => !Number.isNaN(v)); + let finalPts = sampledPts; + if (statsPts.length > 0) { + const firstSampledMs = sampledPts[0][0]; + const lastSampledMs = sampledPts[sampledPts.length - 1][0]; + const rawOutsidePts = statsPts.filter(([t]) => t < firstSampledMs || t > lastSampledMs); + if (rawOutsidePts.length > 0) { + finalPts = [...targetIntervalMs > 0 ? await downsampleInWorker(rawOutsidePts, targetIntervalMs, sampleAggregate) : rawOutsidePts, ...sampledPts]; + finalPts.sort((a, b) => a[0] - b[0]); + } + } + seriesItem.pts = finalPts; + const axisEntry = axes.find((ax) => ax.key === seriesItem.axisKey); + if (axisEntry) axisEntry.values = finalPts.map(([, v]) => v).filter(Number.isFinite); + } else if (Array.isArray(seriesItem.pts) && seriesItem.pts.length > 0 && targetIntervalMs > 0) { + const fallbackPts = await downsampleInWorker(seriesItem.pts, targetIntervalMs, sampleAggregate); + if (fallbackPts.length > 0) { + seriesItem.pts = fallbackPts; + const axisEntry = axes.find((ax) => ax.key === seriesItem.axisKey); + if (axisEntry) axisEntry.values = fallbackPts.map(([, v]) => v).filter(Number.isFinite); + } + } + } catch (err) { + logger$1.warn("[hass-datapoints history-card] downsampled history fetch failed", { + entityId: seriesItem.entityId, + err + }); + } + })); + } + for (const seriesItem of series) { + if (!seriesItem.pts.length) continue; + const lastPt = seriesItem.pts[seriesItem.pts.length - 1]; + const prev = this._previousSeriesEndpoints.get(seriesItem.entityId); + if (!prev) logger$1.log("[hass-datapoints history-card] series initial draw", { + entityId: seriesItem.entityId, + pointCount: seriesItem.pts.length, + lastPt + }); + else if (lastPt[0] !== prev.t || lastPt[1] !== prev.v) logger$1.log("[hass-datapoints history-card] series updated — live update detected", { + entityId: seriesItem.entityId, + pointCount: seriesItem.pts.length, + prev, + lastPt + }); + else logger$1.log("[hass-datapoints history-card] series unchanged — no new data", { + entityId: seriesItem.entityId, + pointCount: seriesItem.pts.length, + lastPt + }); + this._previousSeriesEndpoints.set(seriesItem.entityId, { + t: lastPt[0], + v: lastPt[1] + }); + } + if (!series.length && !binaryBackgrounds.length) { + this._setAdjustAxisButtonVisibility(false); + this._renderComparisonPreviewOverlay(); + const sameRangeAsLastDraw = Number.isFinite(this._lastT0) && Number.isFinite(this._lastT1) && this._lastT0 === t0 && this._lastT1 === t1 && Array.isArray(this._lastDrawArgs) && this._lastDrawArgs.length > 0; + this._setChartLoading(!!options.loading); + this._setChartMessage(options.loading ? "" : "No numeric data in the selected time range."); + if (sameRangeAsLastDraw) return; + this._lastHistResult = histResult; + this._lastStatsResult = statsResult; + this._lastEvents = chartEvents; + this._lastT0 = t0; + this._lastT1 = t1; + this._lastDrawArgs = [ + histResult, + statsResult, + chartEvents, + t0, + t1, + options + ]; + return; + } + this._lastHistResult = histResult; + this._lastStatsResult = statsResult; + this._lastEvents = chartEvents; + this._lastT0 = t0; + this._lastT1 = t1; + this._lastDrawArgs = [ + histResult, + statsResult, + chartEvents, + t0, + t1, + options + ]; + const visibleSeries = series.filter((entry) => !this._hiddenSeries.has(entry.legendEntityId || entry.entityId)); + const selectedComparisonResult = drawableComparisonResults.find((window) => window.id === selectedComparisonWindowId) || null; + const selectedComparisonSeriesMap = /* @__PURE__ */ new Map(); + if (selectedComparisonResult) for (let index = 0; index < seriesSettings.length; index += 1) { + const seriesSetting = seriesSettings[index]; + const entityId = seriesSetting.entity_id; + if (entityId.split(".")[0] === "binary_sensor") continue; + if (this._hiddenSeries.has(entityId)) continue; + const unit = this._hass?.states?.[entityId]?.attributes?.unit_of_measurement || ""; + const analysis = analysisMap.get(entityId) || normalizeHistorySeriesAnalysis(null); + const points = await this._resolveComparisonWindowPoints(entityId, selectedComparisonResult, analysis, t0, t1); + if (!points.length) continue; + selectedComparisonSeriesMap.set(entityId, { + entityId, + label: entityName(this._hass, entityId) || entityId, + unit, + color: seriesSetting.color || COLORS[index % COLORS.length], + pts: points + }); + } + const allComparisonWindowsData = {}; + for (const seriesItem of visibleSeries) { + const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); + if (analysis.show_anomalies !== true || !analysis.anomaly_methods?.includes("comparison_window") || !analysis.anomaly_comparison_window_id) continue; + const windowId = analysis.anomaly_comparison_window_id; + if (!allComparisonWindowsData[windowId]) allComparisonWindowsData[windowId] = {}; + if (!allComparisonWindowsData[windowId][seriesItem.entityId]) { + const compResult = comparisonResults.find((win) => win.id === windowId); + if (compResult) { + const pts = await this._resolveComparisonWindowPoints(seriesItem.entityId, compResult, analysis, t0, t1); + if (pts.length) allComparisonWindowsData[windowId][seriesItem.entityId] = pts; + } + } + } + const analysisEntityIds = visibleSeries.filter((s) => { + const a = analysisMap.get(s.entityId) || {}; + return a.show_trend_lines || a.show_summary_stats || a.show_rate_of_change; + }).map((s) => s.entityId); + const onAnalysisProgress = analysisEntityIds.length ? (progress) => { + this.dispatchEvent(new CustomEvent("hass-datapoints-analysis-computing", { + bubbles: true, + composed: true, + detail: { + computing: true, + entityIds: analysisEntityIds, + progress + } + })); + } : null; + const analysisResult = await this._computeHistoryAnalysis(visibleSeries, selectedComparisonSeriesMap, analysisMap, hasSelectedComparisonWindow, allComparisonWindowsData, t0, t1, onAnalysisProgress); + if (analysisEntityIds.length) this.dispatchEvent(new CustomEvent("hass-datapoints-analysis-computing", { + bubbles: true, + composed: true, + detail: { + computing: false, + entityIds: analysisEntityIds, + progress: 100 + } + })); + const _anomalyEntityIds = visibleSeries.filter((s) => { + const a = analysisMap.get(s.entityId); + return a && a.show_anomalies === true && Array.isArray(a.anomaly_methods) && a.anomaly_methods.length > 0; + }).map((s) => s.entityId); + if (_anomalyEntityIds.length > 0) analysisResult.anomalySeries = _anomalyEntityIds.map((entityId) => { + const cached = this._backendAnomalyByEntity.get(entityId); + if (!cached) return null; + return { + entityId, + anomalyClusters: cached.clusters + }; + }).filter(Boolean); + if (options.drawRequestId && options.drawRequestId !== this._drawRequestId) return; + if (canvas) canvas.style.display = ""; + chartStage?.querySelectorAll(".split-series-row").forEach((el) => el.remove()); + chartStage?.querySelector("#chart-split-overlay")?.remove(); + const axisLeftEl = this.querySelector("#chart-axis-left"); + const axisRightEl = this.querySelector("#chart-axis-right"); + if (axisLeftEl) axisLeftEl.style.display = ""; + if (axisRightEl) axisRightEl.style.display = ""; + let minChartHeight; + if (series.length) minChartHeight = 280; + else if (binaryBackgrounds.length) minChartHeight = 100; + else minChartHeight = 280; + const availableHeight = this._getAvailableChartHeight(minChartHeight); + const viewportWidth = Math.max(scrollViewport?.clientWidth || wrap?.clientWidth || 360, 360); + const totalSpanMs = Math.max(1, t1 - t0); + const zoomSpanMs = this._zoomRange ? Math.max(1, this._zoomRange.end - this._zoomRange.start) : null; + const zoomMultiplier = clampChartValue(zoomSpanMs ? totalSpanMs / zoomSpanMs : 1, 1, HISTORY_CHART_MAX_ZOOM_MULTIPLIER); + const canvasWidth = Math.min(HISTORY_CHART_MAX_CANVAS_WIDTH_PX, zoomSpanMs ? Math.max(viewportWidth, Math.round(viewportWidth * zoomMultiplier)) : viewportWidth); + if (this._config?.split_view === true && visibleSeries.length >= 2) { + if (zoomOutButton) { + zoomOutButton.hidden = !this._zoomRange; + zoomOutButton.onclick = () => this._clearZoomRange(); + } + this._renderLegend(series, binaryBackgrounds); + await this._drawSplitChart({ + visibleSeries, + binaryBackgrounds, + events, + renderT0: t0, + renderT1: t1, + canvasWidth, + availableHeight, + chartStage, + canvas, + wrap, + options, + drawableComparisonResults, + selectedComparisonWindowId, + hoveredComparisonWindowId, + comparisonPreviewActive, + hoveringDifferentComparison, + analysisResult, + analysisMap, + hasSelectedComparisonWindow + }); + if (this._chartScrollViewportEl) { + this._chartScrollViewportEl.removeEventListener("scroll", this._onChartScroll); + this._chartScrollViewportEl.addEventListener("scroll", this._onChartScroll, { passive: true }); + this._syncChartViewportScroll(t0, t1, canvasWidth); + } + this._fireBackendAnomalyRequests(_anomalyEntityIds, analysisMap, _startIso, _endIso); + return; + } + if (chartStage) { + chartStage.style.width = `${canvasWidth}px`; + chartStage.style.height = `${availableHeight}px`; + } + if (scrollViewport) scrollViewport.style.overflowY = ""; + const { w, h } = setupCanvas(canvas, chartStage || wrap, availableHeight, canvasWidth); + const renderer = new ChartRenderer(canvas, w, h); + renderer.labelColor = resolveChartLabelColor(this); + renderer.clear(); + const renderT0 = t0; + const renderT1 = t1; + const trendPointsMap = new Map((analysisResult?.trendSeries || []).map((entry) => [entry.entityId, entry.pts])); + const ratePointsMap = new Map((analysisResult?.rateSeries || []).map((entry) => [entry.entityId, entry.pts])); + const deltaPointsMap = new Map((analysisResult?.deltaSeries || []).map((entry) => [entry.entityId, entry.pts])); + const summaryStatsMap = new Map((analysisResult?.summaryStats || []).map((entry) => [entry.entityId, entry])); + const anomalyClustersMap = new Map((analysisResult?.anomalySeries || []).map((entry) => [entry.entityId, entry.anomalyClusters])); + const hiddenSourceEntityIds = /* @__PURE__ */ new Set(); + const hiddenComparisonEntityIds = /* @__PURE__ */ new Set(); + visibleSeries.forEach((seriesItem) => { + const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); + if (this._seriesShouldHideSource(analysis, hasSelectedComparisonWindow)) hiddenSourceEntityIds.add(seriesItem.entityId); + if (analysis.hide_source_series === true && analysis.show_delta_analysis === true && hasSelectedComparisonWindow) hiddenComparisonEntityIds.add(seriesItem.entityId); + }); + const anyTrendCrosshairs = visibleSeries.some((seriesItem) => { + const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); + return analysis.show_trend_lines === true && analysis.show_trend_crosshairs === true; + }); + const anyRateCrosshairs = visibleSeries.some((seriesItem) => { + const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); + return analysis.show_rate_of_change === true && analysis.show_rate_crosshairs === true; + }); + this._setChartLoading(!!options.loading); + this._setChartMessage(""); + if (zoomOutButton) { + zoomOutButton.hidden = !this._zoomRange; + zoomOutButton.onclick = () => this._clearZoomRange(); + } + const comparisonAxisValues = /* @__PURE__ */ new Map(); + if (this._adjustComparisonAxisScale || autoAdjustHoveredComparisonAxis) for (const win of drawableComparisonResults) { + if (autoAdjustHoveredComparisonAxis && hoveredComparisonWindowId && win.id !== hoveredComparisonWindowId) continue; + for (const seriesSetting of seriesSettings) { + const entityId = seriesSetting.entity_id; + if (entityId.split(".")[0] === "binary_sensor") continue; + if (this._hiddenSeries.has(entityId)) continue; + const unit = this._hass?.states?.[entityId]?.attributes?.unit_of_measurement || ""; + const axisKey = delinkYAxis ? `${unit || "__unitless__"}::${entityId}` : unit || "__unitless__"; + const analysis = analysisMap.get(entityId) || normalizeHistorySeriesAnalysis(null); + const points = await this._resolveComparisonWindowPoints(entityId, win, analysis, renderT0, renderT1); + for (const [, numericValue] of points) { + if (!comparisonAxisValues.has(axisKey)) comparisonAxisValues.set(axisKey, []); + comparisonAxisValues.get(axisKey).push(numericValue); + } + } + } + const deltaAxisMap = /* @__PURE__ */ new Map(); + const rateAxisMap = /* @__PURE__ */ new Map(); + visibleSeries.forEach((seriesItem) => { + const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); + if (analysis.show_delta_analysis === true && hasSelectedComparisonWindow && analysis.show_delta_lines === true) { + const deltaPoints = deltaPointsMap.get(seriesItem.entityId) || []; + if (deltaPoints.length) { + const axisKey = `delta:${seriesItem.axisKey}`; + const unitLabel = seriesItem.unit ? `Δ ${seriesItem.unit}` : "Δ"; + let axis = deltaAxisMap.get(axisKey); + if (!axis) { + axis = { + key: axisKey, + unit: unitLabel, + color: seriesItem.color, + side: "right", + values: [] + }; + deltaAxisMap.set(axisKey, axis); + } + deltaPoints.forEach((point) => { + axis.values.push(point[1]); + }); + } + } + if (analysis.show_rate_of_change === true) { + const ratePoints = ratePointsMap.get(seriesItem.entityId) || []; + if (ratePoints.length) { + const axisKey = `rate:${seriesItem.axisKey}`; + const unitLabel = seriesItem.unit ? `${seriesItem.unit}/h` : "Rate/h"; + let axis = rateAxisMap.get(axisKey); + if (!axis) { + axis = { + key: axisKey, + unit: unitLabel, + color: seriesItem.color, + side: "right", + values: [] + }; + rateAxisMap.set(axisKey, axis); + } + ratePoints.forEach((point) => { + axis.values.push(point[1]); + }); + } + } + }); + const resolvedAxes = axes.filter((axis) => axis.values.length).map((axis) => { + const axisValues = series.filter((entry) => entry.axisKey === axis.key).flatMap((entry) => entry.pts.map((point) => point[1])); + if ((this._adjustComparisonAxisScale || autoAdjustHoveredComparisonAxis) && comparisonAxisValues.has(axis.key)) axisValues.push(...comparisonAxisValues.get(axis.key)); + const extent = this._getAxisValueExtent(axisValues); + if (!extent) return null; + const { min, max } = extent; + const pad = (max - min) * .1 || 1; + return { + key: axis.key, + unit: axis.unit, + color: axis.color, + side: axis.side === "right" ? "right" : "left", + min: min - pad, + max: max + pad + }; + }).filter((ax) => ax !== null); + const deltaResolvedAxes = Array.from(deltaAxisMap.values()).filter((axis) => axis.values.length).map((axis) => { + const extent = this._getAxisValueExtent(axis.values); + if (!extent) return null; + const { min, max } = extent; + const pad = (max - min) * .1 || 1; + return { + key: axis.key, + unit: axis.unit, + color: axis.color, + side: axis.side === "right" ? "right" : "left", + min: min - pad, + max: max + pad + }; + }).filter((ax) => ax !== null); + const rateResolvedAxes = Array.from(rateAxisMap.values()).filter((axis) => axis.values.length).map((axis) => { + const extent = this._getAxisValueExtent(axis.values); + if (!extent) return null; + const { min, max } = extent; + const pad = (max - min) * .1 || 1; + return { + key: axis.key, + unit: axis.unit, + color: axis.color, + side: axis.side === "right" ? "right" : "left", + min: min - pad, + max: max + pad + }; + }).filter((ax) => ax !== null); + const gridAxes = resolvedAxes.length || deltaResolvedAxes.length ? [ + ...resolvedAxes, + ...deltaResolvedAxes, + ...rateResolvedAxes + ] : [{ + key: "binary", + min: 0, + max: 1, + side: "left", + unit: "", + color: null + }]; + renderer.drawGrid(renderT0, renderT1, gridAxes, void 0, 5, { fixedAxisOverlay: true }); + this._renderComparisonPreviewOverlay(renderer); + const activeAxes = resolvedAxes.length ? renderer._activeAxes || [] : []; + const axisLookup = new Map(activeAxes.map((axis) => [axis.key, axis])); + series.forEach((s) => { + s.axis = axisLookup.get(s.axisKey) || activeAxes[0] || resolvedAxes[0]; + }); + renderChartAxisOverlays(this, renderer, activeAxes); + this.style.setProperty("--dp-chart-axis-bottom-height", `${Math.max(0, renderer.pad.bottom)}px`); + binaryBackgrounds.forEach((binaryBackground) => { + if (binaryBackground?.spans?.length && !this._hiddenSeries.has(binaryBackground.entityId)) renderer.drawStateBands(binaryBackground.spans, renderT0, renderT1, binaryBackground.color, .12); + }); + const shouldUseCorrelatedSpans = this._config?.show_correlated_anomalies === true || this._config?.anomaly_overlap_mode === "only"; + const shouldDrawCorrelatedSpans = this._config?.show_correlated_anomalies === true; + const correlatedSpans = shouldUseCorrelatedSpans ? this._buildCorrelatedAnomalySpans(visibleSeries, anomalyClustersMap, analysisMap) : []; + if (shouldDrawCorrelatedSpans) { + if (correlatedSpans.length) renderer.drawStateBands(correlatedSpans, renderT0, renderT1, "#ef4444", .1); + } + let comparisonOutOfBounds = false; + let mainSeriesHoverOpacity; + if (!comparisonPreviewActive) mainSeriesHoverOpacity = 1; + else if (hoveringDifferentComparison) mainSeriesHoverOpacity = .15; + else mainSeriesHoverOpacity = .25; + const anyHiddenSourceSeries = hiddenSourceEntityIds.size > 0; + const hoverSeries = visibleSeries.filter((seriesItem) => !hiddenSourceEntityIds.has(seriesItem.entityId)).map((seriesItem) => ({ + ...seriesItem, + hoverOpacity: mainSeriesHoverOpacity + })); + const summaryHoverSeries = visibleSeries.flatMap((seriesItem) => { + if ((analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null)).show_summary_stats !== true) return []; + const stats = summaryStatsMap.get(seriesItem.entityId) || null; + if (!stats) return []; + const seriesWithAxis = seriesItem; + return [ + { + entityId: `summary:min:${seriesItem.entityId}`, + relatedEntityId: seriesItem.entityId, + label: seriesItem.label, + baseLabel: seriesItem.label, + unit: seriesItem.unit || "", + value: stats.min, + color: hexToRgba(seriesItem.color, anyHiddenSourceSeries ? .94 : .78), + baseColor: seriesItem.color, + axis: seriesWithAxis.axis, + hoverOpacity: anyHiddenSourceSeries ? .94 : .72, + summaryType: "min", + summary: true + }, + { + entityId: `summary:mean:${seriesItem.entityId}`, + relatedEntityId: seriesItem.entityId, + label: seriesItem.label, + baseLabel: seriesItem.label, + unit: seriesItem.unit || "", + value: stats.mean, + color: hexToRgba(seriesItem.color, anyHiddenSourceSeries ? .94 : .78), + baseColor: seriesItem.color, + axis: seriesWithAxis.axis, + hoverOpacity: anyHiddenSourceSeries ? .94 : .72, + summaryType: "mean", + summary: true + }, + { + entityId: `summary:max:${seriesItem.entityId}`, + relatedEntityId: seriesItem.entityId, + label: seriesItem.label, + baseLabel: seriesItem.label, + unit: seriesItem.unit || "", + value: stats.max, + color: hexToRgba(seriesItem.color, anyHiddenSourceSeries ? .94 : .78), + baseColor: seriesItem.color, + axis: seriesWithAxis.axis, + hoverOpacity: anyHiddenSourceSeries ? .94 : .72, + summaryType: "max", + summary: true + } + ]; + }); + const thresholdHoverSeries = visibleSeries.flatMap((seriesItem) => { + const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); + if (analysis.show_threshold_analysis !== true) return []; + const rawThreshold = analysis.threshold_value; + const thresholdValue = Number(rawThreshold); + if (!Number.isFinite(thresholdValue)) return []; + const seriesWithAxis = seriesItem; + return [{ + entityId: `threshold:${seriesItem.entityId}`, + relatedEntityId: seriesItem.entityId, + label: seriesItem.label, + baseLabel: seriesItem.label, + unit: seriesItem.unit || "", + value: thresholdValue, + baseColor: seriesItem.color, + color: hexToRgba(seriesItem.color, anyHiddenSourceSeries ? .82 : .46), + axis: seriesWithAxis.axis, + hoverOpacity: anyHiddenSourceSeries ? .84 : .48, + direction: analysis.threshold_direction === "below" ? "below" : "above", + threshold: true + }]; + }); + const trendSeries = visibleSeries.map((seriesItem) => ({ + ...seriesItem, + trendPts: trendPointsMap.get(seriesItem.entityId) || [] + })).filter((seriesItem) => Array.isArray(seriesItem.trendPts) && seriesItem.trendPts.length >= 2); + const rateSeries = visibleSeries.map((seriesItem) => { + const ratePoints = ratePointsMap.get(seriesItem.entityId) || []; + if (!Array.isArray(ratePoints) || ratePoints.length < 2) return null; + const axis = axisLookup.get(`rate:${seriesItem.axisKey}`); + if (!axis) return null; + return { + ...seriesItem, + ratePts: ratePoints, + rateAxis: axis + }; + }).filter((s) => s !== null); + const anomalySeries = visibleSeries.map((seriesItem) => { + const anomalyClusters = anomalyClustersMap.get(seriesItem.entityId) || []; + if (!Array.isArray(anomalyClusters) || anomalyClusters.length === 0) return null; + return { + ...seriesItem, + anomalyClusters + }; + }).filter((s) => s !== null); + const comparisonHoverSeries = []; + const comparisonTrendHoverSeries = []; + const comparisonRateHoverSeries = []; + const comparisonSummaryHoverSeries = []; + const comparisonThresholdHoverSeries = []; + const deltaHoverSeries = []; + for (const win of drawableComparisonResults) for (const seriesSetting of seriesSettings) { + const entityId = seriesSetting.entity_id; + if (entityId.split(".")[0] === "binary_sensor") continue; + if (this._hiddenSeries.has(entityId)) continue; + const analysis = analysisMap.get(entityId) || normalizeHistorySeriesAnalysis(null); + const winPts = await this._resolveComparisonWindowPoints(entityId, win, analysis, renderT0, renderT1); + if (!winPts.length) continue; + const unit = this._hass?.states?.[entityId]?.attributes?.unit_of_measurement || ""; + const axisKey = delinkYAxis ? `${unit || "__unitless__"}::${entityId}` : unit || "__unitless__"; + const axis = axisLookup.get(axisKey); + if (!axis) continue; + if (!this._adjustComparisonAxisScale && !autoAdjustHoveredComparisonAxis) { + if (winPts.some((point) => point[1] < axis.min || point[1] > axis.max)) comparisonOutOfBounds = true; + } + const baseColor = seriesSetting.color || COLORS[seriesSettings.indexOf(seriesSetting) % COLORS.length]; + const isHoveredComparison = !!hoveredComparisonWindowId && win.id === hoveredComparisonWindowId; + const isSelectedComparison = !!selectedComparisonWindowId && win.id === selectedComparisonWindowId; + const comparisonLineStyle = this._getComparisonWindowLineStyle(isHoveredComparison, isSelectedComparison, hoveringDifferentComparison); + comparisonHoverSeries.push({ + entityId: `${win.id}:${entityId}`, + relatedEntityId: entityId, + label: seriesSetting.label || entityName(this._hass, entityId) || entityId, + windowLabel: win.label || "Date window", + unit, + pts: winPts, + color: baseColor, + axis, + hoverOpacity: comparisonLineStyle.hoverOpacity + }); + if (analysis.show_trend_lines === true && winPts.length >= 2) { + const trendPts = (this._analysisCache?.result?.comparisonWindowResults?.[win.id]?.[entityId])?.trendPts ?? this._buildTrendPoints(winPts, analysis.trend_method, analysis.trend_window); + if (trendPts.length >= 2) { + const trendOptions = this._getTrendRenderOptions(analysis.trend_method, false); + comparisonTrendHoverSeries.push({ + entityId: `trend:${win.id}:${entityId}`, + relatedEntityId: entityId, + comparisonParentId: `${win.id}:${entityId}`, + label: seriesSetting.label || entityName(this._hass, entityId) || entityId, + baseLabel: seriesSetting.label || entityName(this._hass, entityId) || entityId, + windowLabel: win.label || "Date window", + unit, + pts: trendPts, + color: hexToRgba(baseColor, Math.max(.3, trendOptions.colorAlpha - .18)), + axis, + rawVisible: true, + hoverOpacity: Math.max(.3, trendOptions.lineOpacity - .18), + trend: true + }); + } + } + if (analysis.show_rate_of_change === true && winPts.length >= 2) { + const ratePts = (this._analysisCache?.result?.comparisonWindowResults?.[win.id]?.[entityId])?.ratePts ?? this._buildRateOfChangePoints(winPts, analysis.rate_window); + const rateAxis = axisLookup.get(`rate:${axisKey}`) || axis; + if (ratePts.length >= 2) comparisonRateHoverSeries.push({ + entityId: `rate:${win.id}:${entityId}`, + relatedEntityId: entityId, + comparisonParentId: `${win.id}:${entityId}`, + label: seriesSetting.label || entityName(this._hass, entityId) || entityId, + baseLabel: seriesSetting.label || entityName(this._hass, entityId) || entityId, + windowLabel: win.label || "Date window", + unit: unit ? `${unit}/h` : "/h", + pts: ratePts, + color: hexToRgba(baseColor, .52), + axis: rateAxis, + rawVisible: true, + hoverOpacity: .46, + rate: true + }); + } + if (analysis.show_summary_stats === true) { + const summaryStats = (this._analysisCache?.result?.comparisonWindowResults?.[win.id]?.[entityId])?.summaryStats ?? this._buildSummaryStats(winPts); + [ + { + type: "min", + value: summaryStats.min + }, + { + type: "mean", + value: summaryStats.mean + }, + { + type: "max", + value: summaryStats.max + } + ].forEach((entry) => { + if (!Number.isFinite(entry.value)) return; + comparisonSummaryHoverSeries.push({ + entityId: `summary:${entry.type}:${win.id}:${entityId}`, + relatedEntityId: entityId, + comparisonParentId: `${win.id}:${entityId}`, + label: seriesSetting.label || entityName(this._hass, entityId) || entityId, + baseLabel: seriesSetting.label || entityName(this._hass, entityId) || entityId, + windowLabel: win.label || "Date window", + unit, + value: entry.value, + color: hexToRgba(baseColor, entry.type === "mean" ? .44 : .24), + axis, + rawVisible: true, + hoverOpacity: entry.type === "mean" ? .52 : .3, + summaryType: entry.type, + summary: true + }); + }); + } + if (analysis.show_threshold_analysis === true) { + const thresholdValue = Number(analysis.threshold_value); + if (Number.isFinite(thresholdValue)) comparisonThresholdHoverSeries.push({ + entityId: `threshold:${win.id}:${entityId}`, + relatedEntityId: entityId, + comparisonParentId: `${win.id}:${entityId}`, + label: seriesSetting.label || entityName(this._hass, entityId) || entityId, + baseLabel: seriesSetting.label || entityName(this._hass, entityId) || entityId, + windowLabel: win.label || "Date window", + unit, + value: thresholdValue, + color: hexToRgba(baseColor, .28), + axis, + rawVisible: true, + hoverOpacity: .34, + threshold: true + }); + } + if (!hiddenComparisonEntityIds.has(entityId)) renderer.drawLine(winPts, baseColor, renderT0, renderT1, axis.min, axis.max, { + lineOpacity: comparisonLineStyle.lineOpacity, + lineWidth: comparisonLineStyle.lineWidth, + dashed: comparisonLineStyle.dashed, + dashPattern: comparisonLineStyle.dashPattern, + stepped: analysis.stepped_series === true + }); + const rateAxis = axisLookup.get(`rate:${axisKey}`) || null; + this._drawComparisonAnalysisOverlays({ + renderer, + entityId, + seriesColor: baseColor, + comparisonPts: winPts, + analysis, + renderT0, + renderT1, + axis, + rateAxis, + events, + comparisonWindowId: win.id + }); + } + this._setAdjustAxisButtonVisibility(comparisonPreviewActive && comparisonOutOfBounds && !this._adjustComparisonAxisScale && !autoAdjustHoveredComparisonAxis); + for (const s of visibleSeries) { + if (hiddenSourceEntityIds.has(s.entityId)) continue; + const sWithAxis = s; + this._drawSeriesLine(renderer, s.pts, s.color, renderT0, renderT1, sWithAxis.axis.min, sWithAxis.axis.max, { + lineOpacity: mainSeriesHoverOpacity, + lineWidth: this._config?.comparison_hover_active === true ? 1.25 : void 0, + stepped: (analysisMap.get(s.entityId) || normalizeHistorySeriesAnalysis(null)).stepped_series === true + }); + } + const trendHoverSeries = trendSeries.map((seriesItem) => { + const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); + const hiddenSource = hiddenSourceEntityIds.has(seriesItem.entityId); + const trendRenderOptions = this._getTrendRenderOptions(analysis.trend_method, hiddenSource); + const seriesWithAxis = seriesItem; + return { + entityId: `trend:${seriesItem.entityId}`, + relatedEntityId: seriesItem.entityId, + label: seriesItem.label, + baseLabel: seriesItem.label, + unit: seriesItem.unit || "", + pts: seriesItem.trendPts, + color: hexToRgba(seriesItem.color, trendRenderOptions.colorAlpha), + axis: seriesWithAxis.axis, + rawVisible: !hiddenSource, + showCrosshair: analysis.show_trend_crosshairs === true, + hoverOpacity: comparisonPreviewActive ? Math.max(.25, Math.min(.9, mainSeriesHoverOpacity + .12)) : trendRenderOptions.lineOpacity, + trend: true + }; + }); + const rateHoverSeries = rateSeries.map((seriesItem) => ({ + entityId: `rate:${seriesItem.entityId}`, + relatedEntityId: seriesItem.entityId, + label: seriesItem.label, + baseLabel: seriesItem.label, + unit: seriesItem.unit ? `${seriesItem.unit}/h` : "/h", + pts: seriesItem.ratePts, + color: hexToRgba(seriesItem.color, anyHiddenSourceSeries ? .9 : .72), + axis: seriesItem.rateAxis, + rawVisible: !hiddenSourceEntityIds.has(seriesItem.entityId), + showCrosshair: (analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null)).show_rate_crosshairs === true, + hoverOpacity: anyHiddenSourceSeries ? .88 : .66, + rate: true + })); + for (const trend of trendSeries) { + const analysis = analysisMap.get(trend.entityId) || normalizeHistorySeriesAnalysis(null); + const trendRenderOptions = this._getTrendRenderOptions(analysis.trend_method, hiddenSourceEntityIds.has(trend.entityId)); + const trendWithAxis = trend; + renderer.drawLine(trend.trendPts, hexToRgba(trend.color, trendRenderOptions.colorAlpha), renderT0, renderT1, trendWithAxis.axis.min, trendWithAxis.axis.max, { + lineOpacity: comparisonPreviewActive ? Math.max(.25, Math.min(.9, mainSeriesHoverOpacity + .12)) : trendRenderOptions.lineOpacity, + lineWidth: trendRenderOptions.lineWidth, + dashed: trendRenderOptions.dashed, + dotted: trendRenderOptions.dotted + }); + } + for (const rateSeriesItem of rateSeries) renderer.drawLine(rateSeriesItem.ratePts, hexToRgba(rateSeriesItem.color, anyHiddenSourceSeries ? .96 : .82), renderT0, renderT1, rateSeriesItem.rateAxis.min, rateSeriesItem.rateAxis.max, { + lineOpacity: anyHiddenSourceSeries ? .88 : .66, + lineWidth: 1.55, + dashed: false, + dotted: false, + dashPattern: [ + 7, + 3, + 1.5, + 3 + ] + }); + for (const seriesItem of visibleSeries) { + const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); + if (!(analysis.show_delta_analysis === true && hasSelectedComparisonWindow === true)) continue; + const deltaAxis = axisLookup.get(`delta:${seriesItem.axisKey}`); + if (!deltaAxis) continue; + const deltaPoints = deltaPointsMap.get(seriesItem.entityId) || []; + if (!deltaPoints.length) continue; + if (analysis.show_delta_tooltip === true) deltaHoverSeries.push({ + entityId: `delta:${seriesItem.entityId}`, + relatedEntityId: seriesItem.entityId, + label: seriesItem.label, + baseLabel: seriesItem.label, + unit: seriesItem.unit || "", + pts: deltaPoints, + color: hexToRgba(seriesItem.color, .92), + axis: deltaAxis, + rawVisible: !hiddenSourceEntityIds.has(seriesItem.entityId), + hoverOpacity: .82, + delta: true + }); + if (analysis.show_delta_lines === true) renderer.drawLine(deltaPoints, hexToRgba(seriesItem.color, .92), renderT0, renderT1, deltaAxis.min, deltaAxis.max, { + lineOpacity: .82, + lineWidth: 1.9, + dashed: true + }); + } + visibleSeries.forEach((seriesItem) => { + const shadingAnalysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); + if (shadingAnalysis.show_summary_stats !== true || shadingAnalysis.show_summary_stats_shading !== true) return; + const stats = summaryStatsMap.get(seriesItem.entityId); + const axis = seriesItem.axis; + if (!stats || !axis) return; + if (!Number.isFinite(stats.min) || !Number.isFinite(stats.max) || !Number.isFinite(stats.mean)) return; + const fillAlpha = anyHiddenSourceSeries ? .1 : .06; + renderer.drawGradientBand(stats.min, stats.mean, seriesItem.color, renderT0, renderT1, axis.min, axis.max, { fillAlpha }); + renderer.drawGradientBand(stats.max, stats.mean, seriesItem.color, renderT0, renderT1, axis.min, axis.max, { fillAlpha }); + }); + summaryHoverSeries.forEach((summarySeries) => { + const axis = summarySeries.axis; + if (!axis) return; + renderer.drawLine([[renderT0, summarySeries.value], [renderT1, summarySeries.value]], summarySeries.color, renderT0, renderT1, axis.min, axis.max, { + lineOpacity: summarySeries.hoverOpacity, + lineWidth: 1.8, + dashed: false, + dotted: false + }); + }); + thresholdHoverSeries.forEach((thresholdSeries) => { + const axis = thresholdSeries.axis; + if (!axis) return; + if ((analysisMap.get(thresholdSeries.relatedEntityId) || normalizeHistorySeriesAnalysis(null)).show_threshold_shading === true) { + const relatedSeries = visibleSeries.find((seriesItem) => seriesItem.entityId === thresholdSeries.relatedEntityId); + if (relatedSeries?.pts?.length) renderer.drawThresholdArea(relatedSeries.pts, thresholdSeries.value, thresholdSeries.baseColor || relatedSeries.color, renderT0, renderT1, axis.min, axis.max, { + mode: thresholdSeries.direction === "below" ? "below" : "above", + fillAlpha: anyHiddenSourceSeries ? .24 : .14 + }); + } + renderer.drawLine([[renderT0, thresholdSeries.value], [renderT1, thresholdSeries.value]], thresholdSeries.color, renderT0, renderT1, axis.min, axis.max, { + lineOpacity: thresholdSeries.hoverOpacity, + lineWidth: 1.15 + }); + }); + if (anomalySeries.length) { + const anomalyRegions = []; + anomalySeries.forEach((seriesItem) => { + const axis = seriesItem.axis; + if (!axis) return; + const visibleAnomalyClusters = this._filterAnnotatedAnomalyClusters(seriesItem, events); + if (visibleAnomalyClusters.length === 0) return; + const overlapMode = (analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null)).anomaly_overlap_mode; + const { baseClusters, regionClusters } = this._resolveAnomalyClusterDisplay(visibleAnomalyClusters, overlapMode, correlatedSpans); + const baseColor = hexToRgba(seriesItem.color, anyHiddenSourceSeries ? .96 : .86); + const regionOptions = { + strokeAlpha: anyHiddenSourceSeries ? .98 : .9, + lineWidth: anyHiddenSourceSeries ? 2.5 : 2.1, + haloWidth: anyHiddenSourceSeries ? 5.5 : 4.8, + haloColor: "rgba(255,255,255,0.88)", + haloAlpha: anyHiddenSourceSeries ? .92 : .82, + fillColor: hexToRgba(seriesItem.color, anyHiddenSourceSeries ? .14 : .1), + fillAlpha: 1, + pointPadding: anyHiddenSourceSeries ? 12 : 10, + minRadiusX: 10, + minRadiusY: 10 + }; + if (baseClusters.length > 0) renderer.drawAnomalyClusters(baseClusters, baseColor, renderT0, renderT1, axis.min, axis.max, regionOptions); + renderer.getAnomalyClusterRegions(regionClusters, renderT0, renderT1, axis.min, axis.max, regionOptions).forEach((region) => { + const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); + anomalyRegions.push({ + ...region, + relatedEntityId: seriesItem.entityId, + label: String(seriesItem.label), + unit: String(seriesItem.unit || ""), + color: seriesItem.color || null, + sensitivity: analysis.anomaly_sensitivity + }); + }); + }); + this._lastAnomalyRegions = anomalyRegions; + } else this._lastAnomalyRegions = []; + const effectiveComparisonHoverSeries = comparisonHoverSeries.filter((entry) => !hiddenComparisonEntityIds.has(entry.relatedEntityId || entry.entityId)); + renderer.drawAnnotations(chartEvents, renderT0, renderT1, { + showLines: this._config.show_event_lines !== false, + showMarkers: this._config.show_event_lines !== false + }); + const eventHits = this._drawRecordedEventPoints(renderer, visibleSeries, chartEvents, renderT0, renderT1, { showIcons: this._config.show_event_markers !== false }); + this._renderLegend(series, binaryBackgrounds); + const eventValueMap = new Map(eventHits.map((hit) => [hit.event.id, hit])); + const enrichedEvents = chartEvents.map((event) => { + const hit = eventValueMap.get(event.id || ""); + return hit ? { + ...event, + chart_value: hit.value, + chart_unit: hit.unit + } : event; + }); + const addButton = this.querySelector("#chart-add-annotation"); + if (addButton) { + addButton.dataset.allowAddAnnotation = this._canAddAnnotation ? "true" : "false"; + if (addButton.dataset.allowAddAnnotation === "false") addButton.hidden = true; + } + if (visibleSeries.length) { + this._ensureContextAnnotationDialog(); + attachLineChartHover(this, canvas, renderer, hoverSeries, enrichedEvents, renderT0, renderT1, 0, 0, activeAxes, { + onContextMenu: (hover) => this._handleChartContextMenu(hover), + onAddAnnotation: this._canAddAnnotation ? (hover) => this._handleChartAddAnnotation(hover) : void 0, + binaryStates: binaryBackgrounds.filter((entry) => !this._hiddenSeries.has(entry.entityId)), + comparisonSeries: effectiveComparisonHoverSeries, + trendSeries: [...trendHoverSeries, ...comparisonTrendHoverSeries], + rateSeries: [...rateHoverSeries, ...comparisonRateHoverSeries], + deltaSeries: deltaHoverSeries, + summarySeries: [...summaryHoverSeries, ...comparisonSummaryHoverSeries], + thresholdSeries: [...thresholdHoverSeries, ...comparisonThresholdHoverSeries], + anomalyRegions: Array.isArray(this._lastAnomalyRegions) ? this._lastAnomalyRegions : [], + hoverSurfaceEl: this.querySelector("#chart-icon-overlay"), + showTooltip: this._config.show_tooltips !== false, + emphasizeHoverGuides: this._config.emphasize_hover_guides === true, + hoverSnapMode: this._config.hover_snap_mode === "snap_to_data_points" ? "snap_to_data_points" : "follow_series", + showTrendCrosshairs: anyTrendCrosshairs, + showRateCrosshairs: anyRateCrosshairs, + hideRawData: hiddenSourceEntityIds.size === visibleSeries.length && visibleSeries.length > 0, + showDeltaTooltip: deltaHoverSeries.length > 0, + onAnomalyClick: (regions) => this._handleAnomalyAddAnnotation(regions) + }); + attachLineChartRangeZoom(this, canvas, renderer, renderT0, renderT1, { + onPreview: (range) => this._dispatchZoomPreview(range), + onZoom: ({ startTime, endTime }) => this._applyZoomRange(startTime, endTime), + onReset: () => this._clearZoomRange() + }); + } else { + if (this._chartHoverCleanup) { + this._chartHoverCleanup(); + this._chartHoverCleanup = null; + } + if (this._chartZoomCleanup) { + this._chartZoomCleanup(); + this._chartZoomCleanup = null; + } + } + if (this._chartScrollViewportEl) { + this._chartScrollViewportEl.removeEventListener("scroll", this._onChartScroll); + this._chartScrollViewportEl.addEventListener("scroll", this._onChartScroll, { passive: true }); + this._syncChartViewportScroll(t0, t1, w); + } + this._fireBackendAnomalyRequests(_anomalyEntityIds, analysisMap, _startIso, _endIso); + this._fireComparisonBackendAnomalyRequests(drawableComparisonResults, analysisMap, renderT0, renderT1); + } + /** + * Build a lightweight string key that captures all inputs that affect the + * analysis result. Used to skip the worker when data and settings haven't + * changed (e.g. on zoom/pan). + * Ported from _buildAnalysisCacheKey in card-history.js. + */ + _buildAnalysisCacheKey(visibleSeries, selectedComparisonSeriesMap, analysisMap, allComparisonWindowsData, t0, t1) { + const ANALYSIS_FIELDS = [ + "show_trend_lines", + "trend_method", + "trend_window", + "show_rate_of_change", + "rate_window", + "show_delta_analysis", + "show_summary_stats", + "show_anomalies", + "anomaly_methods", + "anomaly_sensitivity", + "anomaly_overlap_mode", + "anomaly_rate_window", + "anomaly_zscore_window", + "anomaly_persistence_window", + "anomaly_comparison_window_id", + "anomaly_use_sampled_data" + ]; + return `${t0}:${t1}|${visibleSeries.map((s) => { + const a = analysisMap.get(s.entityId) || normalizeHistorySeriesAnalysis(null); + const first = s.pts[0]?.[0] ?? 0; + const last = s.pts[s.pts.length - 1]?.[0] ?? 0; + const aKey = ANALYSIS_FIELDS.map((f) => JSON.stringify(a[f])).join(","); + return `${s.entityId}:${s.pts.length}:${first}:${last}:${aKey}`; + }).join("|")}|${Array.from(selectedComparisonSeriesMap.values()).map((s) => { + const first = s.pts[0]?.[0] ?? 0; + const last = s.pts[s.pts.length - 1]?.[0] ?? 0; + return `${s.entityId}:${s.pts.length}:${first}:${last}`; + }).sort().join("|")}|${Object.entries(allComparisonWindowsData).flatMap(([windowId, entities]) => Object.entries(entities).map(([entityId, pts]) => { + const first = pts[0]?.[0] ?? 0; + const last = pts[pts.length - 1]?.[0] ?? 0; + return `${windowId}:${entityId}:${pts.length}:${first}:${last}`; + })).sort().join("|")}`; + } + /** + * Build the serialisable payload sent to the history-analysis worker. + * Ported from _buildHistoryAnalysisPayload in card-history.js. + */ + _buildHistoryAnalysisPayload(visibleSeries, selectedComparisonSeriesMap, analysisMap, hasSelectedComparisonWindow, allComparisonWindowsData = {}) { + const defaultAnalysis = normalizeHistorySeriesAnalysis(null); + const series = visibleSeries.map((seriesItem) => ({ + entityId: seriesItem.entityId, + pts: seriesItem.pts, + analysis: analysisMap.get(seriesItem.entityId) || defaultAnalysis + })); + const comparisonSeries = Array.from(selectedComparisonSeriesMap.values()).map((seriesItem) => ({ + entityId: seriesItem.entityId, + pts: seriesItem.pts + })); + const seriesAnalysisConfigs = {}; + for (const seriesItem of visibleSeries) seriesAnalysisConfigs[seriesItem.entityId] = analysisMap.get(seriesItem.entityId) || defaultAnalysis; + return { + series, + comparisonSeries, + hasSelectedComparisonWindow: hasSelectedComparisonWindow === true, + allComparisonWindowsData, + seriesAnalysisConfigs + }; + } + /** + * Run or cache the history analysis computation. + * Terminates any in-flight worker before starting a new one. + * Falls back to a synchronous main-thread path when the worker fails. + * Ported from _computeHistoryAnalysis in card-history.js. + */ + async _computeHistoryAnalysis(visibleSeries, selectedComparisonSeriesMap, analysisMap, hasSelectedComparisonWindow, allComparisonWindowsData = {}, t0 = 0, t1 = 0, onProgress = null) { + terminateHistoryAnalysisWorker(); + const cacheKey = this._buildAnalysisCacheKey(visibleSeries, selectedComparisonSeriesMap, analysisMap, allComparisonWindowsData, t0, t1); + if (this._analysisCache?.key === cacheKey) return this._analysisCache.result; + onProgress?.(0); + const payload = this._buildHistoryAnalysisPayload(visibleSeries, selectedComparisonSeriesMap, analysisMap, hasSelectedComparisonWindow, allComparisonWindowsData); + try { + const result = await computeHistoryAnalysisInWorker(payload); + this._analysisCache = { + key: cacheKey, + result + }; + return result; + } catch (error) { + const errorMessage = error?.message ?? ""; + if (errorMessage.startsWith("Aborted")) return { + trendSeries: [], + rateSeries: [], + deltaSeries: [], + summaryStats: [], + anomalySeries: [] + }; + logger$1.warn("[hass-datapoints history-card] analysis worker fallback", { message: errorMessage || String(error) }); + try { + return { + trendSeries: visibleSeries.map((seriesItem) => { + const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); + if (analysis.show_trend_lines !== true) return null; + return { + entityId: seriesItem.entityId, + pts: this._buildTrendPoints(seriesItem.pts, analysis.trend_method, analysis.trend_window) + }; + }).filter(Boolean).filter((s) => Array.isArray(s.pts) && s.pts.length >= 2), + rateSeries: visibleSeries.map((seriesItem) => { + const analysis = analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); + if (analysis.show_rate_of_change !== true) return null; + return { + entityId: seriesItem.entityId, + pts: this._buildRateOfChangePoints(seriesItem.pts, analysis.rate_window) + }; + }).filter(Boolean).filter((s) => Array.isArray(s.pts) && s.pts.length >= 2), + deltaSeries: visibleSeries.map((seriesItem) => { + if (!((analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null)).show_delta_analysis === true && hasSelectedComparisonWindow === true)) return null; + const comparisonSeries = selectedComparisonSeriesMap.get(seriesItem.entityId); + return { + entityId: seriesItem.entityId, + pts: comparisonSeries ? this._buildDeltaPoints(seriesItem.pts, comparisonSeries.pts) : [] + }; + }).filter(Boolean).filter((s) => Array.isArray(s.pts) && s.pts.length >= 2), + summaryStats: visibleSeries.map((seriesItem) => { + if ((analysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null)).show_summary_stats !== true) return null; + return { + entityId: seriesItem.entityId, + ...this._buildSummaryStats(seriesItem.pts) + }; + }).filter((entry) => entry && Number.isFinite(entry.min) && Number.isFinite(entry.max) && Number.isFinite(entry.mean)), + anomalySeries: [] + }; + } catch (fallbackError) { + logger$1.error("[hass-datapoints history-card] analysis fallback failed", fallbackError); + return { + trendSeries: [], + rateSeries: [], + deltaSeries: [], + summaryStats: [], + anomalySeries: [] + }; + } + } + } + /** Build trend points from raw series data. */ + _buildTrendPoints(_pts, _method, _window) { + if (!Array.isArray(_pts) || _pts.length < 2) return []; + if ((_method || "rolling_average") === "linear_trend") return buildLinearTrend(_pts); + return buildRollingAverageTrend(_pts, getTrendWindowMs(_window || "24h")); + } + /** Build rate-of-change points from raw series data. */ + _buildRateOfChangePoints(_pts, _window) { + if (!Array.isArray(_pts) || _pts.length < 2) return []; + return buildRateOfChangePoints(_pts, _window || "1h"); + } + /** Build delta points (main vs comparison series). */ + _buildDeltaPoints(_pts, _comparisonPts) { + return buildDeltaPoints(_pts, _comparisonPts); + } + /** Build summary statistics from raw series data. */ + _buildSummaryStats(_pts) { + return buildSummaryStats(_pts) ?? { + min: 0, + max: 0, + mean: 0 + }; + } + /** + * Build a Map of entityId → normalised analysis settings from config. + * Ported from _getSeriesAnalysisMap in card-history.js. + */ + _getSeriesAnalysisMap() { + const seriesSettings = Array.isArray(this._config?.series_settings) ? this._config.series_settings : []; + return new Map(seriesSettings.filter((entry) => entry?.entity_id != null).map((entry) => [entry.entity_id, normalizeHistorySeriesAnalysis(entry?.analysis)])); + } + /** + * Get the normalised analysis settings for a single entity. + * Ported from _getSeriesAnalysis in card-history.js. + */ + _getSeriesAnalysis(entityId, analysisMap = null) { + return normalizeHistorySeriesAnalysis((analysisMap || this._getSeriesAnalysisMap()).get(entityId)); + } + /** + * Returns true if any analysis feature is active for the given series. + * Ported from _seriesHasActiveAnalysis in card-history.js. + */ + _seriesHasActiveAnalysis(analysis, hasSelectedComparisonWindow = false) { + return !!(analysis.show_trend_lines || analysis.show_summary_stats || analysis.show_rate_of_change || analysis.show_threshold_analysis || analysis.show_anomalies || analysis.show_delta_analysis && hasSelectedComparisonWindow); + } + /** + * Returns true if the source series should be hidden (replaced by its + * analysis overlay series). + * Ported from _seriesShouldHideSource in card-history.js. + */ + _seriesShouldHideSource(analysis, hasSelectedComparisonWindow = false) { + return analysis.hide_source_series === true && this._seriesHasActiveAnalysis(analysis, hasSelectedComparisonWindow); + } + /** + * Return ChartRenderer render options for the active trend method. + * Ported from _getTrendRenderOptions in card-history.js. + */ + _getTrendRenderOptions(method = "rolling_average", hideRawData = false) { + if (method === "linear_trend") return { + colorAlpha: hideRawData ? .94 : .88, + lineOpacity: hideRawData ? .86 : .74, + lineWidth: 2.1, + dashed: true, + dotted: false + }; + return { + colorAlpha: hideRawData ? .9 : .82, + lineOpacity: hideRawData ? .84 : .62, + lineWidth: 2.2, + dashed: false, + dotted: true + }; + } + async _drawSplitChart({ visibleSeries, binaryBackgrounds, events, renderT0, renderT1, canvasWidth, availableHeight, chartStage, canvas, wrap, options, drawableComparisonResults, selectedComparisonWindowId, hoveredComparisonWindowId, comparisonPreviewActive, hoveringDifferentComparison, analysisResult, analysisMap, hasSelectedComparisonWindow }) { + if (canvas) canvas.style.display = "none"; + const N = visibleSeries.length; + const rowHeight = Math.max(140, Math.floor(availableHeight / N)); + const totalHeight = rowHeight * N; + if (chartStage) { + chartStage.style.width = `${canvasWidth}px`; + chartStage.style.height = `${totalHeight}px`; + } + const splitScrollViewport = this.querySelector("#chart-scroll-viewport"); + if (splitScrollViewport) splitScrollViewport.style.overflowY = totalHeight > availableHeight ? "auto" : "hidden"; + this._setChartLoading(!!options.loading); + this._setChartMessage(""); + const iconOverlay = this.querySelector("#chart-icon-overlay"); + if (iconOverlay) iconOverlay.innerHTML = ""; + const trendPointsMap = new Map((analysisResult?.trendSeries || []).map((entry) => [entry.entityId, entry.pts])); + const ratePointsMap = new Map((analysisResult?.rateSeries || []).map((entry) => [entry.entityId, entry.pts])); + const deltaPointsMap = new Map((analysisResult?.deltaSeries || []).map((entry) => [entry.entityId, entry.pts])); + const summaryStatsMap = new Map((analysisResult?.summaryStats || []).map((entry) => [entry.entityId, entry])); + const anomalyClustersMap = new Map((analysisResult?.anomalySeries || []).map((entry) => [entry.entityId, entry.anomalyClusters])); + const effectiveAnalysisMap = analysisMap || /* @__PURE__ */ new Map(); + const shouldUseCorrelatedAnomalySpans = this._config?.show_correlated_anomalies === true || this._config?.anomaly_overlap_mode === "only"; + const shouldDrawCorrelatedAnomalySpans = this._config?.show_correlated_anomalies === true; + const correlatedAnomalySpans = shouldUseCorrelatedAnomalySpans ? this._buildCorrelatedAnomalySpans(visibleSeries, anomalyClustersMap, effectiveAnalysisMap) : []; + const tracks = []; + for (let i = 0; i < N; i += 1) { + const isLastRow = i === N - 1; + const seriesItem = visibleSeries[i]; + const rowOffset = i * rowHeight; + const rowDiv = document.createElement("div"); + rowDiv.className = "split-series-row"; + rowDiv.style.cssText = `position:absolute;left:0;top:${rowOffset}px;width:${canvasWidth}px;height:${rowHeight}px;pointer-events:none;overflow:hidden;`; + const rowCanvas = document.createElement("canvas"); + rowCanvas.className = "split-series-canvas"; + rowDiv.appendChild(rowCanvas); + chartStage?.appendChild(rowDiv); + const { w, h } = setupCanvas(rowCanvas, chartStage || wrap, rowHeight, canvasWidth); + const renderer = new ChartRenderer(rowCanvas, w, h); + renderer.labelColor = resolveChartLabelColor(this); + renderer.basePad = { + top: 24, + right: 12, + bottom: isLastRow ? 48 : 10, + left: 12 + }; + renderer.clear(); + const rowAnalysis = effectiveAnalysisMap.get(seriesItem.entityId) || normalizeHistorySeriesAnalysis(null); + const rowTrendPts = rowAnalysis.show_trend_lines === true ? trendPointsMap.get(seriesItem.entityId) || [] : []; + const rowRatePts = rowAnalysis.show_rate_of_change === true ? ratePointsMap.get(seriesItem.entityId) || [] : []; + const rowDeltaPts = rowAnalysis.show_delta_analysis === true && hasSelectedComparisonWindow ? deltaPointsMap.get(seriesItem.entityId) || [] : []; + const rowSummaryStats = rowAnalysis.show_summary_stats === true ? summaryStatsMap.get(seriesItem.entityId) || null : null; + const rowAnomalyClusters = rowAnalysis.show_anomalies === true ? anomalyClustersMap.get(seriesItem.entityId) || [] : []; + const rowHideSource = this._seriesShouldHideSource(rowAnalysis, hasSelectedComparisonWindow); + const axisValues = seriesItem.pts.map(([, v]) => v); + const extent = this._getAxisValueExtent(axisValues); + let axisMin = 0; + let axisMax = 1; + if (extent) { + const pad = (extent.max - extent.min) * .1 || 1; + axisMin = extent.min - pad; + axisMax = extent.max + pad; + } + const primaryAxisKey = seriesItem.axisKey || seriesItem.unit || "__unitless__"; + const axis = { + key: primaryAxisKey, + unit: String(seriesItem.unit || ""), + color: seriesItem.color || null, + side: "left", + min: axisMin, + max: axisMax, + values: axisValues + }; + const rowAxes = [axis]; + let rowRateAxisKey = null; + if (rowRatePts.length >= 2) { + const rateVals = rowRatePts.map(([, v]) => v); + const rateExt = this._getAxisValueExtent(rateVals); + if (rateExt) { + const pad = (rateExt.max - rateExt.min) * .1 || 1; + rowRateAxisKey = `rate:${primaryAxisKey}`; + rowAxes.push({ + key: rowRateAxisKey, + unit: seriesItem.unit ? `${String(seriesItem.unit)}/h` : "Rate/h", + color: seriesItem.color || null, + side: "right", + min: rateExt.min - pad, + max: rateExt.max + pad, + values: rateVals + }); + } + } + let rowDeltaAxisKey = null; + if (rowDeltaPts.length >= 2) { + const deltaVals = rowDeltaPts.map(([, v]) => v); + const deltaExt = this._getAxisValueExtent(deltaVals); + if (deltaExt) { + const pad = (deltaExt.max - deltaExt.min) * .1 || 1; + rowDeltaAxisKey = `delta:${primaryAxisKey}`; + rowAxes.push({ + key: rowDeltaAxisKey, + unit: seriesItem.unit ? `Δ ${String(seriesItem.unit)}` : "Δ", + color: seriesItem.color || null, + side: "right", + min: deltaExt.min - pad, + max: deltaExt.max + pad, + values: deltaVals + }); + } + } + renderer.drawGrid(renderT0, renderT1, rowAxes, void 0, 4, { + fixedAxisOverlay: true, + hideTimeLabels: !isLastRow + }); + const activeAxes = renderer._activeAxes; + const resolvedAxis = activeAxes?.[0] || axis; + const resolvedRateAxis = rowRateAxisKey ? activeAxes?.find((a) => a.key === rowRateAxisKey) || null : null; + const resolvedDeltaAxis = rowDeltaAxisKey ? activeAxes?.find((a) => a.key === rowDeltaAxisKey) || null : null; + seriesItem.axis = resolvedAxis; + let mainSeriesOpacity; + if (!comparisonPreviewActive) mainSeriesOpacity = 1; + else if (hoveringDifferentComparison) mainSeriesOpacity = .15; + else mainSeriesOpacity = .25; + if (!rowHideSource) this._drawSeriesLine(renderer, seriesItem.pts, seriesItem.color, renderT0, renderT1, resolvedAxis.min, resolvedAxis.max, { + lineWidth: comparisonPreviewActive ? 1.25 : 1.75, + lineOpacity: mainSeriesOpacity, + stepped: rowAnalysis.stepped_series === true + }); + for (const win of drawableComparisonResults || []) { + const winPts = await this._resolveComparisonWindowPoints(seriesItem.entityId, win, rowAnalysis, renderT0, renderT1); + if (!winPts.length) continue; + const isHovered = !!hoveredComparisonWindowId && win.id === hoveredComparisonWindowId; + const isSelected = !!selectedComparisonWindowId && win.id === selectedComparisonWindowId; + const comparisonLineStyle = this._getComparisonWindowLineStyle(isHovered, isSelected, hoveringDifferentComparison); + renderer.drawLine(winPts, seriesItem.color, renderT0, renderT1, resolvedAxis.min, resolvedAxis.max, { + lineOpacity: comparisonLineStyle.lineOpacity, + lineWidth: comparisonLineStyle.lineWidth, + dashed: comparisonLineStyle.dashed, + dashPattern: comparisonLineStyle.dashPattern, + stepped: rowAnalysis.stepped_series === true + }); + this._drawComparisonAnalysisOverlays({ + renderer, + entityId: seriesItem.entityId, + seriesColor: seriesItem.color, + comparisonPts: winPts, + analysis: rowAnalysis, + renderT0, + renderT1, + axis: resolvedAxis, + rateAxis: resolvedRateAxis, + events, + comparisonWindowId: String(win.id || "") + }); + } + binaryBackgrounds.forEach((bg) => { + const bgItem = bg; + if (!this._hiddenSeries.has(bgItem.entityId) && bgItem.spans?.length) renderer.drawStateBands(bgItem.spans, renderT0, renderT1, bgItem.color, .1); + }); + if (shouldDrawCorrelatedAnomalySpans && correlatedAnomalySpans.length) renderer.drawStateBands(correlatedAnomalySpans, renderT0, renderT1, "#ef4444", .1); + renderer.drawAnnotations(events, renderT0, renderT1, { + showLines: this._config.show_event_lines !== false, + showMarkers: this._config.show_event_lines !== false + }); + this._drawRecordedEventPoints(renderer, [seriesItem], events, renderT0, renderT1, { + showIcons: this._config.show_event_markers !== false, + yOffset: rowOffset, + skipOverlayClear: true + }); + if (rowAnalysis.show_threshold_analysis === true) { + const thresholdValue = Number(rowAnalysis.threshold_value); + if (Number.isFinite(thresholdValue)) { + if (rowAnalysis.show_threshold_shading === true && seriesItem.pts.length) renderer.drawThresholdArea(seriesItem.pts, thresholdValue, seriesItem.color, renderT0, renderT1, resolvedAxis.min, resolvedAxis.max, { + mode: rowAnalysis.threshold_direction === "below" ? "below" : "above", + fillAlpha: rowHideSource ? .24 : .14 + }); + renderer.drawLine([[renderT0, thresholdValue], [renderT1, thresholdValue]], hexToRgba(seriesItem.color, rowHideSource ? .82 : .46), renderT0, renderT1, resolvedAxis.min, resolvedAxis.max, { + lineOpacity: rowHideSource ? .84 : .48, + lineWidth: 1.15 + }); + } + } + if (rowSummaryStats) { + if (rowAnalysis.show_summary_stats_shading === true) { + const fillAlpha = rowHideSource ? .1 : .06; + renderer.drawGradientBand(rowSummaryStats.min, rowSummaryStats.mean, seriesItem.color, renderT0, renderT1, resolvedAxis.min, resolvedAxis.max, { fillAlpha }); + renderer.drawGradientBand(rowSummaryStats.max, rowSummaryStats.mean, seriesItem.color, renderT0, renderT1, resolvedAxis.min, resolvedAxis.max, { fillAlpha }); + } + const summaryEntries = [ + { + type: "min", + value: rowSummaryStats.min, + alpha: rowHideSource ? .78 : .42, + width: 1.1, + dotted: true + }, + { + type: "mean", + value: rowSummaryStats.mean, + alpha: rowHideSource ? .94 : .78, + width: 1.8, + dotted: false + }, + { + type: "max", + value: rowSummaryStats.max, + alpha: rowHideSource ? .78 : .42, + width: 1.1, + dotted: true + } + ]; + for (const entry of summaryEntries) { + if (!Number.isFinite(entry.value)) continue; + renderer.drawLine([[renderT0, entry.value], [renderT1, entry.value]], hexToRgba(seriesItem.color, entry.alpha), renderT0, renderT1, resolvedAxis.min, resolvedAxis.max, { + lineOpacity: rowHideSource ? .82 : .34, + lineWidth: entry.width, + dotted: entry.dotted + }); + } + } + if (rowTrendPts.length >= 2) { + const trendOpts = this._getTrendRenderOptions(rowAnalysis.trend_method, rowHideSource); + renderer.drawLine(rowTrendPts, hexToRgba(seriesItem.color, trendOpts.colorAlpha), renderT0, renderT1, resolvedAxis.min, resolvedAxis.max, { + lineOpacity: trendOpts.lineOpacity, + lineWidth: trendOpts.lineWidth, + dashed: trendOpts.dashed, + dotted: trendOpts.dotted + }); + } + if (rowRatePts.length >= 2 && resolvedRateAxis) renderer.drawLine(rowRatePts, hexToRgba(seriesItem.color, rowHideSource ? .96 : .82), renderT0, renderT1, resolvedRateAxis.min, resolvedRateAxis.max, { + lineOpacity: rowHideSource ? .88 : .66, + lineWidth: 1.55, + dashPattern: [ + 7, + 3, + 1.5, + 3 + ] + }); + if (rowDeltaPts.length >= 2 && resolvedDeltaAxis && rowAnalysis.show_delta_lines === true) renderer.drawLine(rowDeltaPts, hexToRgba(seriesItem.color, .92), renderT0, renderT1, resolvedDeltaAxis.min, resolvedDeltaAxis.max, { + lineOpacity: .82, + lineWidth: 1.9, + dashed: true + }); + let rowAnomalyRegions = []; + if (rowAnomalyClusters.length) { + const filteredClusters = this._filterAnnotatedAnomalyClusters({ + entityId: seriesItem.entityId, + anomalyClusters: rowAnomalyClusters + }, events); + if (filteredClusters.length > 0) { + const { baseClusters, regionClusters } = this._resolveAnomalyClusterDisplay(filteredClusters, rowAnalysis.anomaly_overlap_mode, correlatedAnomalySpans); + const baseColor = hexToRgba(seriesItem.color, rowHideSource ? .96 : .86); + const regionOpts = { + strokeAlpha: rowHideSource ? .98 : .9, + lineWidth: rowHideSource ? 2.5 : 2.1, + haloWidth: rowHideSource ? 5.5 : 4.8, + haloColor: "rgba(255,255,255,0.88)", + haloAlpha: rowHideSource ? .92 : .82, + fillColor: hexToRgba(seriesItem.color, rowHideSource ? .14 : .1), + fillAlpha: 1, + pointPadding: rowHideSource ? 12 : 10, + minRadiusX: 10, + minRadiusY: 10 + }; + if (baseClusters.length > 0) renderer.drawAnomalyClusters(baseClusters, baseColor, renderT0, renderT1, resolvedAxis.min, resolvedAxis.max, regionOpts); + rowAnomalyRegions = renderer.getAnomalyClusterRegions(regionClusters, renderT0, renderT1, resolvedAxis.min, resolvedAxis.max, regionOpts).map((region) => ({ + ...region, + relatedEntityId: String(seriesItem.entityId), + label: String(seriesItem.label), + unit: String(seriesItem.unit || ""), + color: seriesItem.color || null, + sensitivity: String(rowAnalysis.anomaly_sensitivity || "") + })); + } + } + tracks.push({ + canvas: rowCanvas, + renderer, + series: seriesItem, + axis: resolvedAxis, + rowOffset, + analysis: rowAnalysis, + summaryStats: rowSummaryStats, + trendPts: rowTrendPts, + ratePts: rowRatePts, + rateAxis: resolvedRateAxis, + deltaPts: rowDeltaPts, + deltaAxis: resolvedDeltaAxis, + anomalyRegions: rowAnomalyRegions + }); + } + this._renderSplitAxisOverlays(tracks); + this._renderComparisonPreviewOverlay(tracks[0] ? tracks[0].renderer : null); + const comparisonHoverSeries = []; + for (const track of tracks) { + const trackSeries = track.series; + const trackAnalysis = track.analysis || normalizeHistorySeriesAnalysis(null); + for (const win of drawableComparisonResults || []) { + const winPts = await this._resolveComparisonWindowPoints(trackSeries.entityId, win, trackAnalysis, renderT0, renderT1); + if (!winPts.length) continue; + const isHovered = !!hoveredComparisonWindowId && win.id === hoveredComparisonWindowId; + const isSelected = !!selectedComparisonWindowId && win.id === selectedComparisonWindowId; + const comparisonLineStyle = this._getComparisonWindowLineStyle(isHovered, isSelected, hoveringDifferentComparison); + const winId = String(win.id || ""); + const splitPrecomputed = this._analysisCache?.result?.comparisonWindowResults?.[winId]?.[trackSeries.entityId]; + comparisonHoverSeries.push({ + entityId: `${winId}:${trackSeries.entityId}`, + relatedEntityId: trackSeries.entityId, + comparisonParentId: `${winId}:${trackSeries.entityId}`, + label: trackSeries.label, + windowLabel: win.label || "Date window", + unit: trackSeries.unit, + pts: winPts, + trendPts: trackAnalysis.show_trend_lines === true && winPts.length >= 2 ? splitPrecomputed?.trendPts ?? this._buildTrendPoints(winPts, trackAnalysis.trend_method, trackAnalysis.trend_window) : [], + ratePts: trackAnalysis.show_rate_of_change === true && winPts.length >= 2 ? splitPrecomputed?.ratePts ?? this._buildRateOfChangePoints(winPts, trackAnalysis.rate_window) : [], + summaryStats: trackAnalysis.show_summary_stats === true ? splitPrecomputed?.summaryStats ?? this._buildSummaryStats(winPts) : null, + thresholdValue: trackAnalysis.show_threshold_analysis === true ? Number(trackAnalysis.threshold_value) : null, + color: trackSeries.color, + hoverOpacity: comparisonLineStyle.hoverOpacity, + track + }); + } + } + this._attachSplitHover(tracks, comparisonHoverSeries, events, renderT0, renderT1, chartStage, options, effectiveAnalysisMap, hasSelectedComparisonWindow); + this._fireComparisonBackendAnomalyRequests(drawableComparisonResults, effectiveAnalysisMap, renderT0, renderT1); + } + _attachSplitHover(tracks, comparisonHoverSeries, events, t0, t1, chartStage, options, analysisMap, hasSelectedComparisonWindow) { + if (this._chartHoverCleanup) { + this._chartHoverCleanup(); + this._chartHoverCleanup = null; + } + if (!tracks.length || !chartStage) return; + const primaryRenderer = tracks[0].renderer; + const lastTrack = tracks[tracks.length - 1]; + const eventThresholdMs = primaryRenderer.cw ? 14 * ((t1 - t0) / primaryRenderer.cw) : 0; + const splitSelTop = tracks[0].rowOffset + primaryRenderer.pad.top; + const splitSelHeight = lastTrack.rowOffset + lastTrack.renderer.pad.top + lastTrack.renderer.ch - splitSelTop; + const overlayEl = document.createElement("div"); + overlayEl.id = "chart-split-overlay"; + overlayEl.style.cssText = "position:absolute;inset:0;pointer-events:auto;z-index:2;cursor:crosshair;"; + chartStage.appendChild(overlayEl); + const overlayRelX = (clientX) => { + return clampChartValue(clientX - overlayEl.getBoundingClientRect().left, primaryRenderer.pad.left, primaryRenderer.pad.left + primaryRenderer.cw); + }; + const stageXToTime = (stageX) => { + return t0 + (primaryRenderer.cw ? (stageX - primaryRenderer.pad.left) / primaryRenderer.cw : 0) * (t1 - t0); + }; + const inPlotBoundsX = (clientX) => { + const localX = clientX - overlayEl.getBoundingClientRect().left; + return localX >= primaryRenderer.pad.left && localX <= primaryRenderer.pad.left + primaryRenderer.cw; + }; + const buildSplitHover = (clientX) => { + const baseRect = tracks[0].canvas.getBoundingClientRect(); + if (!baseRect.width || !primaryRenderer.cw) return null; + const rawTimeMs = t0 + (clampChartValue(clientX - baseRect.left, primaryRenderer.pad.left, primaryRenderer.pad.left + primaryRenderer.cw) - primaryRenderer.pad.left) / primaryRenderer.cw * (t1 - t0); + const hoverSnapMode = this._config.hover_snap_mode === "snap_to_data_points" ? "snap_to_data_points" : "follow_series"; + const timeMs = resolveLineChartHoverTime(tracks.map((track) => ({ pts: track.series?.pts || [] })), rawTimeMs, hoverSnapMode); + const x = primaryRenderer.xOf(timeMs, t0, t1); + const values = tracks.map((trackItem) => { + const { renderer: trackRenderer, series, axis, rowOffset } = trackItem; + const value = trackRenderer._interpolateValue(series.pts, timeMs); + if (value == null) return { + entityId: String(series.entityId || ""), + label: String(series.label || ""), + value: null, + unit: String(series.unit || ""), + color: String(series.color || ""), + opacity: 1, + hasValue: false, + axisSide: "left", + axisSlot: 0 + }; + return { + entityId: String(series.entityId || ""), + label: String(series.label || ""), + value, + unit: String(series.unit || ""), + color: String(series.color || ""), + opacity: 1, + hasValue: true, + x, + y: rowOffset + trackRenderer.yOf(value, axis.min, axis.max), + axisSide: "left", + axisSlot: 0 + }; + }); + const hoveredEvents = []; + for (const event of events || []) { + const eventTime = new Date(event.timestamp).getTime(); + if (eventTime < t0 || eventTime > t1) continue; + const distance = Math.abs(eventTime - timeMs); + if (distance <= eventThresholdMs) hoveredEvents.push({ + ...event, + _hoverDistanceMs: distance + }); + } + hoveredEvents.sort((a, b) => (a._hoverDistanceMs || 0) - (b._hoverDistanceMs || 0)); + const comparisonValues = (comparisonHoverSeries || []).map((chs) => { + const { pts, entityId, relatedEntityId, comparisonParentId, label, windowLabel, unit, color, hoverOpacity, track: cTrack } = chs; + const value = cTrack.renderer._interpolateValue(pts, timeMs); + if (value == null) return { + entityId: String(entityId || ""), + label: String(label || ""), + value: null, + unit: String(unit || ""), + color, + opacity: Number(hoverOpacity) || 1, + hasValue: false, + axisSide: "left", + axisSlot: 0 + }; + return { + entityId: String(entityId || ""), + relatedEntityId: String(relatedEntityId || ""), + comparisonParentId: String(comparisonParentId || ""), + label: String(label || ""), + windowLabel: String(windowLabel || ""), + value, + unit: String(unit || ""), + color, + opacity: Number(hoverOpacity) || 1, + hasValue: true, + x, + y: cTrack.rowOffset + cTrack.renderer.yOf(value, cTrack.axis.min, cTrack.axis.max), + axisSide: "left", + axisSlot: 0 + }; + }); + const trendValues = []; + const rateValues = []; + const deltaValues = []; + const summaryValues = []; + const thresholdValues = []; + const anomalyRegions = []; + let showTrendCrosshairs = false; + let showRateCrosshairs = false; + for (const track of tracks) { + const { renderer: trackRenderer, series: trackSeries, axis: trackAxis, rowOffset: trackRowOffset, analysis: trackAnalysis, summaryStats: trackSummaryStats, trendPts: trackTrendPts, ratePts: trackRatePts, rateAxis: trackRateAxis, deltaPts: trackDeltaPts, deltaAxis: trackDeltaAxis, anomalyRegions: trackAnomalyRegions } = track; + const effectiveAnalysis = trackAnalysis || (analysisMap || /* @__PURE__ */ new Map()).get(trackSeries.entityId) || normalizeHistorySeriesAnalysis(null); + const trackHideSource = this._seriesShouldHideSource(effectiveAnalysis, hasSelectedComparisonWindow); + if (effectiveAnalysis.show_trend_lines === true && Array.isArray(trackTrendPts) && trackTrendPts.length >= 2) { + if (effectiveAnalysis.show_trend_crosshairs === true) showTrendCrosshairs = true; + const trendOpts = this._getTrendRenderOptions(effectiveAnalysis.trend_method, trackHideSource); + const trendVal = trackRenderer._interpolateValue(trackTrendPts, timeMs); + trendValues.push({ + entityId: `trend:${trackSeries.entityId}`, + relatedEntityId: String(trackSeries.entityId || ""), + label: String(trackSeries.label || ""), + baseLabel: String(trackSeries.label || ""), + unit: String(trackSeries.unit || ""), + color: hexToRgba(trackSeries.color, trendOpts.colorAlpha), + opacity: trendOpts.lineOpacity, + hasValue: trendVal != null, + value: trendVal ?? null, + ...trendVal != null ? { + x, + y: trackRowOffset + trackRenderer.yOf(trendVal, trackAxis.min, trackAxis.max) + } : {}, + axisSide: "left", + axisSlot: 0, + trend: true, + rawVisible: !trackHideSource, + showCrosshair: effectiveAnalysis.show_trend_crosshairs === true + }); + } + if (effectiveAnalysis.show_rate_of_change === true && Array.isArray(trackRatePts) && trackRatePts.length >= 2 && trackRateAxis) { + if (effectiveAnalysis.show_rate_crosshairs === true) showRateCrosshairs = true; + const rateVal = trackRenderer._interpolateValue(trackRatePts, timeMs); + rateValues.push({ + entityId: `rate:${trackSeries.entityId}`, + relatedEntityId: String(trackSeries.entityId || ""), + label: String(trackSeries.label || ""), + baseLabel: String(trackSeries.label || ""), + unit: trackSeries.unit ? `${String(trackSeries.unit)}/h` : "/h", + color: hexToRgba(trackSeries.color, trackHideSource ? .96 : .82), + opacity: trackHideSource ? .88 : .66, + hasValue: rateVal != null, + value: rateVal ?? null, + ...rateVal != null ? { + x, + y: trackRowOffset + trackRenderer.yOf(rateVal, trackRateAxis.min, trackRateAxis.max) + } : {}, + axisSide: "right", + axisSlot: 0, + rate: true, + rawVisible: !trackHideSource, + showCrosshair: effectiveAnalysis.show_rate_crosshairs === true + }); + } + if (effectiveAnalysis.show_delta_analysis === true && effectiveAnalysis.show_delta_tooltip === true && Array.isArray(trackDeltaPts) && trackDeltaPts.length >= 2 && trackDeltaAxis) { + const deltaVal = trackRenderer._interpolateValue(trackDeltaPts, timeMs); + deltaValues.push({ + entityId: `delta:${trackSeries.entityId}`, + relatedEntityId: String(trackSeries.entityId || ""), + label: String(trackSeries.label || ""), + baseLabel: String(trackSeries.label || ""), + unit: String(trackSeries.unit || ""), + color: hexToRgba(trackSeries.color, .92), + opacity: .82, + hasValue: deltaVal != null, + value: deltaVal ?? null, + ...deltaVal != null ? { + x, + y: trackRowOffset + trackRenderer.yOf(deltaVal, trackDeltaAxis.min, trackDeltaAxis.max) + } : {}, + axisSide: "right", + axisSlot: 0, + delta: true, + rawVisible: !trackHideSource + }); + } + if (effectiveAnalysis.show_summary_stats === true && trackSummaryStats) { + const summaryEntries = [ + { + type: "min", + value: trackSummaryStats.min, + alphaV: trackHideSource ? .94 : .78, + opac: trackHideSource ? .94 : .72 + }, + { + type: "mean", + value: trackSummaryStats.mean, + alphaV: trackHideSource ? .94 : .78, + opac: trackHideSource ? .94 : .72 + }, + { + type: "max", + value: trackSummaryStats.max, + alphaV: trackHideSource ? .94 : .78, + opac: trackHideSource ? .94 : .72 + } + ]; + for (const entry of summaryEntries) { + if (!Number.isFinite(entry.value)) continue; + summaryValues.push({ + entityId: `summary:${entry.type}:${trackSeries.entityId}`, + relatedEntityId: String(trackSeries.entityId || ""), + label: String(trackSeries.label || ""), + baseLabel: String(trackSeries.label || ""), + unit: String(trackSeries.unit || ""), + color: hexToRgba(trackSeries.color, entry.alphaV), + opacity: entry.opac, + hasValue: true, + value: entry.value, + axisSide: "left", + axisSlot: 0, + summaryType: entry.type, + summary: true, + rawVisible: !trackHideSource + }); + } + } + if (effectiveAnalysis.show_threshold_analysis === true) { + const thresholdValue = Number(effectiveAnalysis.threshold_value); + if (Number.isFinite(thresholdValue)) thresholdValues.push({ + entityId: `threshold:${trackSeries.entityId}`, + relatedEntityId: String(trackSeries.entityId || ""), + label: String(trackSeries.label || ""), + baseLabel: String(trackSeries.label || ""), + unit: String(trackSeries.unit || ""), + color: hexToRgba(trackSeries.color, trackHideSource ? .82 : .46), + opacity: trackHideSource ? .84 : .48, + hasValue: true, + value: thresholdValue, + axisSide: "left", + axisSlot: 0, + threshold: true, + rawVisible: !trackHideSource + }); + } + if (Array.isArray(trackAnomalyRegions)) for (const region of trackAnomalyRegions) { + const clusterPoints = region?.cluster ? region.cluster?.points : void 0; + const regionStartMs = clusterPoints?.[0] ? clusterPoints[0]?.timeMs ?? region.startTime : region.startTime; + const regionEndMs = clusterPoints?.length ? clusterPoints[clusterPoints.length - 1]?.timeMs ?? region.endTime : region.endTime; + if (Number.isFinite(regionStartMs) && Number.isFinite(regionEndMs) && timeMs >= regionStartMs && timeMs <= regionEndMs) anomalyRegions.push(region); + } + } + for (const comparisonSeries of comparisonHoverSeries) { + const seriesEntry = comparisonSeries; + const track = seriesEntry.track; + const trackRenderer = track.renderer; + const trackAxis = track.axis; + const trackRateAxis = track.rateAxis || null; + const trackRowOffset = track.rowOffset; + const trendPts = Array.isArray(seriesEntry.trendPts) ? seriesEntry.trendPts : []; + if (trendPts.length >= 2) { + const trendVal = trackRenderer._interpolateValue(trendPts, timeMs); + trendValues.push({ + entityId: `trend:${seriesEntry.entityId}`, + relatedEntityId: String(seriesEntry.relatedEntityId || ""), + comparisonParentId: String(seriesEntry.comparisonParentId || ""), + label: String(seriesEntry.label || ""), + baseLabel: String(seriesEntry.label || ""), + windowLabel: String(seriesEntry.windowLabel || "Date window"), + unit: String(seriesEntry.unit || ""), + color: hexToRgba(seriesEntry.color, .34), + opacity: .34, + hasValue: trendVal != null, + value: trendVal ?? null, + ...trendVal != null ? { + x, + y: trackRowOffset + trackRenderer.yOf(trendVal, trackAxis.min, trackAxis.max) + } : {}, + axisSide: "left", + axisSlot: 0, + trend: true, + rawVisible: true, + comparisonDerived: true + }); + } + const ratePts = Array.isArray(seriesEntry.ratePts) ? seriesEntry.ratePts : []; + if (ratePts.length >= 2 && trackRateAxis) { + const rateVal = trackRenderer._interpolateValue(ratePts, timeMs); + rateValues.push({ + entityId: `rate:${seriesEntry.entityId}`, + relatedEntityId: String(seriesEntry.relatedEntityId || ""), + comparisonParentId: String(seriesEntry.comparisonParentId || ""), + label: String(seriesEntry.label || ""), + baseLabel: String(seriesEntry.label || ""), + windowLabel: String(seriesEntry.windowLabel || "Date window"), + unit: seriesEntry.unit ? `${String(seriesEntry.unit)}/h` : "/h", + color: hexToRgba(seriesEntry.color, .46), + opacity: .46, + hasValue: rateVal != null, + value: rateVal ?? null, + ...rateVal != null ? { + x, + y: trackRowOffset + trackRenderer.yOf(rateVal, trackRateAxis.min, trackRateAxis.max) + } : {}, + axisSide: "right", + axisSlot: 0, + rate: true, + rawVisible: true, + comparisonDerived: true, + showCrosshair: analysisMap.get(String(seriesEntry.relatedEntityId || ""))?.show_rate_crosshairs === true + }); + } + const summaryStats = seriesEntry.summaryStats; + if (summaryStats) [ + { + type: "min", + value: summaryStats.min + }, + { + type: "mean", + value: summaryStats.mean + }, + { + type: "max", + value: summaryStats.max + } + ].forEach((entry) => { + if (!Number.isFinite(entry.value)) return; + summaryValues.push({ + entityId: `summary:${entry.type}:${seriesEntry.entityId}`, + relatedEntityId: String(seriesEntry.relatedEntityId || ""), + comparisonParentId: String(seriesEntry.comparisonParentId || ""), + label: String(seriesEntry.label || ""), + baseLabel: String(seriesEntry.label || ""), + windowLabel: String(seriesEntry.windowLabel || "Date window"), + unit: String(seriesEntry.unit || ""), + color: hexToRgba(seriesEntry.color, entry.type === "mean" ? .44 : .24), + opacity: entry.type === "mean" ? .52 : .3, + hasValue: true, + value: entry.value, + axisSide: "left", + axisSlot: 0, + summaryType: entry.type, + summary: true, + rawVisible: true, + comparisonDerived: true + }); + }); + const thresholdValue = Number(seriesEntry.thresholdValue); + if (Number.isFinite(thresholdValue)) thresholdValues.push({ + entityId: `threshold:${seriesEntry.entityId}`, + relatedEntityId: String(seriesEntry.relatedEntityId || ""), + comparisonParentId: String(seriesEntry.comparisonParentId || ""), + label: String(seriesEntry.label || ""), + baseLabel: String(seriesEntry.label || ""), + windowLabel: String(seriesEntry.windowLabel || "Date window"), + unit: String(seriesEntry.unit || ""), + color: hexToRgba(seriesEntry.color, .28), + opacity: .34, + hasValue: true, + value: thresholdValue, + axisSide: "left", + axisSlot: 0, + threshold: true, + rawVisible: true, + comparisonDerived: true + }); + } + const hideRawData = tracks.every((track) => { + const eff = track.analysis || (analysisMap || /* @__PURE__ */ new Map()).get(track.series.entityId) || normalizeHistorySeriesAnalysis(null); + return this._seriesShouldHideSource(eff, hasSelectedComparisonWindow); + }); + const firstWithValue = values.find((value) => value.hasValue === true); + return { + x, + y: firstWithValue?.y ?? splitSelTop + 12, + timeMs, + rangeStartMs: timeMs, + rangeEndMs: timeMs, + values: values.filter((value) => value.hasValue === true), + trendValues, + rateValues, + deltaValues, + summaryValues, + thresholdValues, + comparisonValues: comparisonValues.filter((value) => value.hasValue === true), + binaryValues: [], + primary: firstWithValue ?? null, + event: hoveredEvents.length > 0 ? (({ _hoverDistanceMs: _, ...event }) => event)(hoveredEvents[0]) : null, + events: hoveredEvents.map(({ _hoverDistanceMs: _d, ...event }) => event), + anomalyRegions, + emphasizeGuides: options.emphasizeHoverGuides === true, + showTrendCrosshairs, + showRateCrosshairs, + hideRawData, + splitVertical: { + top: splitSelTop, + height: splitSelHeight + } + }; + }; + const showFromPointer = (clientX, clientY) => { + if (this._chartZoomDragging) return; + const hover = buildSplitHover(clientX); + if (!hover) { + this._chartLastHover = null; + hideLineChartHover(this); + overlayEl.style.cursor = "default"; + return; + } + this._chartLastHover = hover; + showLineChartCrosshair(this, primaryRenderer, hover); + if (this._config.show_tooltips !== false) showLineChartTooltip(this, hover, clientX, clientY); + else hideTooltip(this); + dispatchLineChartHover(this, hover); + overlayEl.style.cursor = "crosshair"; + }; + const hideHover = () => { + this._chartLastHover = null; + hideLineChartHover(this); + }; + const onMouseMove = (ev) => showFromPointer(ev.clientX, ev.clientY); + const onMouseLeave = (ev) => { + const nextTarget = ev.relatedTarget; + const addButton = this.querySelector("#chart-add-annotation"); + if (nextTarget && addButton instanceof HTMLElement && addButton.contains(nextTarget)) return; + hideHover(); + }; + const onTouchMove = (ev) => { + if (ev.touches.length === 1) showFromPointer(ev.touches[0].clientX, ev.touches[0].clientY); + }; + const onTouchEnd = () => hideHover(); + const onOverlayClick = (ev) => { + if (this._chartZoomDragging) return; + const regions = this._chartLastHover?.anomalyRegions; + if (!regions?.length) return; + ev.preventDefault(); + ev.stopPropagation(); + this._handleAnomalyAddAnnotation(regions); + }; + const addAnnotationBtn = this.querySelector("#chart-add-annotation"); + const onAddBtnClick = (ev) => { + if (!this._canAddAnnotation || !this._chartLastHover) return; + ev.preventDefault(); + ev.stopPropagation(); + this._handleChartAddAnnotation(this._chartLastHover); + }; + const onAddBtnLeave = (ev) => { + const nextTarget = ev.relatedTarget; + if (nextTarget instanceof Node && overlayEl.contains(nextTarget)) return; + hideHover(); + }; + overlayEl.addEventListener("mousemove", onMouseMove); + overlayEl.addEventListener("mouseleave", onMouseLeave); + overlayEl.addEventListener("touchmove", onTouchMove, { passive: true }); + overlayEl.addEventListener("touchend", onTouchEnd); + overlayEl.addEventListener("click", onOverlayClick); + addAnnotationBtn?.addEventListener("click", onAddBtnClick); + addAnnotationBtn?.addEventListener("mouseleave", onAddBtnLeave); + this._chartHoverCleanup = () => { + overlayEl.removeEventListener("mousemove", onMouseMove); + overlayEl.removeEventListener("mouseleave", onMouseLeave); + overlayEl.removeEventListener("touchmove", onTouchMove); + overlayEl.removeEventListener("touchend", onTouchEnd); + overlayEl.removeEventListener("click", onOverlayClick); + addAnnotationBtn?.removeEventListener("click", onAddBtnClick); + addAnnotationBtn?.removeEventListener("mouseleave", onAddBtnLeave); + }; + const selection = this.querySelector("#chart-zoom-selection"); + const hideZoomSelection = () => { + if (!selection) return; + selection.hidden = true; + selection.classList.remove("visible"); + }; + const renderZoomSelection = (startX, currentX) => { + if (!selection) return; + const left = Math.min(startX, currentX); + const width = Math.abs(currentX - startX); + selection.style.left = `${left}px`; + selection.style.top = `${splitSelTop}px`; + selection.style.width = `${width}px`; + selection.style.height = `${splitSelHeight}px`; + selection.hidden = false; + selection.classList.add("visible"); + }; + let zoomPointerId = null; + let zoomStartX = 0; + let zoomCurrentX = 0; + let zoomDragging = false; + const onZoomPointerMove = (ev) => { + if (zoomPointerId == null || ev.pointerId !== zoomPointerId) return; + zoomCurrentX = overlayRelX(ev.clientX); + if (!zoomDragging && Math.abs(zoomCurrentX - zoomStartX) < 6) return; + zoomDragging = true; + this._chartZoomDragging = true; + hideLineChartHover(this); + renderZoomSelection(zoomStartX, zoomCurrentX); + ev.preventDefault(); + }; + const finishZoom = (ev) => { + if (zoomPointerId == null || ev.pointerId !== zoomPointerId) return; + const didDrag = zoomDragging; + const endX = zoomCurrentX; + window.removeEventListener("pointermove", onZoomPointerMove); + window.removeEventListener("pointerup", finishZoom); + window.removeEventListener("pointercancel", finishZoom); + zoomPointerId = null; + zoomDragging = false; + this._chartZoomDragging = false; + hideZoomSelection(); + if (!didDrag || Math.abs(endX - zoomStartX) < 8) return; + const startTime = Math.min(stageXToTime(zoomStartX), stageXToTime(endX)); + const endTime = Math.max(stageXToTime(zoomStartX), stageXToTime(endX)); + this._applyZoomRange(startTime, endTime); + }; + const onZoomPointerDown = (ev) => { + if (ev.button !== 0 || !inPlotBoundsX(ev.clientX)) return; + zoomPointerId = ev.pointerId; + zoomStartX = overlayRelX(ev.clientX); + zoomCurrentX = zoomStartX; + zoomDragging = false; + this._chartZoomDragging = false; + window.addEventListener("pointermove", onZoomPointerMove); + window.addEventListener("pointerup", finishZoom); + window.addEventListener("pointercancel", finishZoom); + }; + const onZoomDoubleClick = (ev) => { + if (!inPlotBoundsX(ev.clientX)) return; + ev.preventDefault(); + this._clearZoomRange(); + }; + overlayEl.addEventListener("pointerdown", onZoomPointerDown); + overlayEl.addEventListener("dblclick", onZoomDoubleClick); + if (this._chartZoomCleanup) this._chartZoomCleanup(); + this._chartZoomCleanup = () => { + overlayEl.removeEventListener("pointerdown", onZoomPointerDown); + overlayEl.removeEventListener("dblclick", onZoomDoubleClick); + window.removeEventListener("pointermove", onZoomPointerMove); + window.removeEventListener("pointerup", finishZoom); + window.removeEventListener("pointercancel", finishZoom); + zoomPointerId = null; + zoomDragging = false; + this._chartZoomDragging = false; + hideZoomSelection(); + }; + } + }; + if (!customElements.get("hass-datapoints-history-chart")) customElements.define("hass-datapoints-history-chart", HistoryChart$1); + //#endregion + //#region custom_components/hass_datapoints/src/lib/chart/chart-state.ts + function createHiddenSeriesSet(seriesSettings = []) { + return new Set((Array.isArray(seriesSettings) ? seriesSettings : []).filter((entry) => entry?.visible === false).map((entry) => entry.entity_id || entry.entity || entry.entityId).filter((value) => Boolean(value))); + } + function createHiddenEventIdSet(hiddenEventIds = []) { + return new Set((Array.isArray(hiddenEventIds) ? hiddenEventIds : []).filter(Boolean)); + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/data/events-api.ts + async function fetchEvents(hass, startTime, endTime, entityIds) { + try { + const normalizedEntityIds = normalizeCacheIdList(entityIds); + return await withStableRangeCache(JSON.stringify({ + type: `${DOMAIN}/events`, + start_time: startTime, + end_time: endTime, + entity_ids: normalizedEntityIds + }), endTime, async () => { + const msg = { + type: `${DOMAIN}/events`, + start_time: startTime, + end_time: endTime + }; + if (normalizedEntityIds.length) msg.entity_ids = normalizedEntityIds; + return (await hass.connection.sendMessagePromise(msg)).events || []; + }); + } catch (err) { + logger.warn("[hass-datapoints] fetchEvents failed:", err); + return []; + } + } + function invalidateEventsCache() { + return clearStableRangeCacheMatching((key) => { + if (typeof key !== "string") return false; + return key.includes(`"type":"${DOMAIN}/events"`); + }); + } + async function fetchEventBounds(hass) { + try { + const result = await hass.connection.sendMessagePromise({ type: `${DOMAIN}/events_bounds` }); + return { + start: result?.start_time || null, + end: result?.end_time || null + }; + } catch (err) { + logger.warn("[hass-datapoints] fetchEventBounds failed:", err); + return { + start: null, + end: null + }; + } + } + async function deleteEvent(hass, eventId) { + const result = await hass.connection.sendMessagePromise({ + type: `${DOMAIN}/events/delete`, + event_id: eventId + }); + invalidateEventsCache(); + window.dispatchEvent(new CustomEvent("hass-datapoints-event-recorded")); + return result; + } + async function updateEvent(hass, eventId, fields) { + const result = await hass.connection.sendMessagePromise({ + type: `${DOMAIN}/events/update`, + event_id: eventId, + ...fields + }); + invalidateEventsCache(); + window.dispatchEvent(new CustomEvent("hass-datapoints-event-recorded")); + return result; + } + //#endregion + //#region node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/directive.js + /** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ + var t$1 = { + ATTRIBUTE: 1, + CHILD: 2, + PROPERTY: 3, + BOOLEAN_ATTRIBUTE: 4, + EVENT: 5, + ELEMENT: 6 + }, e$2 = (t) => (...e) => ({ + _$litDirective$: t, + values: e + }); + var i$1 = class { + constructor(t) {} + get _$AU() { + return this._$AM._$AU; + } + _$AT(t, e, i) { + this._$Ct = t, this._$AM = e, this._$Ci = i; + } + _$AS(t, e) { + return this.update(t, e); + } + update(t, e) { + return this.render(...e); + } + }, { I: t } = j$1, i = (o) => o, s = () => document.createComment(""), v = (o, n, e) => { + const l = o._$AA.parentNode, d = void 0 === n ? o._$AB : n._$AA; + if (void 0 === e) e = new t(l.insertBefore(s(), d), l.insertBefore(s(), d), o, o.options); + else { + const t = e._$AB.nextSibling, n = e._$AM, c = n !== o; + if (c) { + let t; + e._$AQ?.(o), e._$AM = o, void 0 !== e._$AP && (t = o._$AU) !== n._$AU && e._$AP(t); + } + if (t !== d || c) { + let o = e._$AA; + for (; o !== t;) { + const t = i(o).nextSibling; + i(l).insertBefore(o, d), o = t; + } + } + } + return e; + }, u$1 = (o, t, i = o) => (o._$AI(t, i), o), m = {}, p = (o, t = m) => o._$AH = t, M = (o) => o._$AH, h = (o) => { + o._$AR(), o._$AA.remove(); + }; + //#endregion + //#region node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/directives/repeat.js + /** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ + var u = (e, s, t) => { + const r = /* @__PURE__ */ new Map(); + for (let l = s; l <= t; l++) r.set(e[l], l); + return r; + }, c = e$2(class extends i$1 { + constructor(e) { + if (super(e), e.type !== t$1.CHILD) throw Error("repeat() can only be used in text expressions"); + } + dt(e, s, t) { + let r; + void 0 === t ? t = s : void 0 !== s && (r = s); + const l = [], o = []; + let i = 0; + for (const s of e) l[i] = r ? r(s, i) : i, o[i] = t(s, i), i++; + return { + values: o, + keys: l + }; + } + render(e, s, t) { + return this.dt(e, s, t).values; + } + update(s, [t, r, c]) { + const d = M(s), { values: p$3, keys: a } = this.dt(t, r, c); + if (!Array.isArray(d)) return this.ut = a, p$3; + const h$3 = this.ut ??= [], v$2 = []; + let m, y, x = 0, j = d.length - 1, k = 0, w = p$3.length - 1; + for (; x <= j && k <= w;) if (null === d[x]) x++; + else if (null === d[j]) j--; + else if (h$3[x] === a[k]) v$2[k] = u$1(d[x], p$3[k]), x++, k++; + else if (h$3[j] === a[w]) v$2[w] = u$1(d[j], p$3[w]), j--, w--; + else if (h$3[x] === a[w]) v$2[w] = u$1(d[x], p$3[w]), v(s, v$2[w + 1], d[x]), x++, w--; + else if (h$3[j] === a[k]) v$2[k] = u$1(d[j], p$3[k]), v(s, d[x], d[j]), j--, k++; + else if (void 0 === m && (m = u(a, k, w), y = u(h$3, x, j)), m.has(h$3[x])) if (m.has(h$3[j])) { + const e = y.get(a[k]), t = void 0 !== e ? d[e] : null; + if (null === t) { + const e = v(s, d[x]); + u$1(e, p$3[k]), v$2[k] = e; + } else v$2[k] = u$1(t, p$3[k]), v(s, d[x], t), d[e] = null; + k++; + } else h(d[j]), j--; + else h(d[x]), x++; + for (; k <= w;) { + const e = v(s, v$2[w + 1]); + u$1(e, p$3[k]), v$2[k++] = e; + } + for (; x <= j;) { + const e = d[x++]; + null !== e && h(e); + } + return this.ut = a, p(s, v$2), E; + } + }); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/annotation-chip-row/annotation-chip-row.styles.ts + var styles$53 = i$5` :host { display: block; } @@ -13913,7 +14727,9 @@ ${content.alert}` : "", gap: 6px; } `; - const styles$Q = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/annotation-chip/annotation-chip.styles.ts + var styles$52 = i$5` :host { display: inline-flex; } @@ -13974,74 +14790,80 @@ ${content.alert}` : "", pointer-events: none; } `; - var __create$J = Object.create; - var __defProp$U = Object.defineProperty; - var __getOwnPropDesc$J = Object.getOwnPropertyDescriptor; - var __knownSymbol$J = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$J = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$U = (obj, key, value) => key in obj ? __defProp$U(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$J = (base) => [, , , __create$J(base?.[__knownSymbol$J("metadata")] ?? null)]; - var __decoratorStrings$J = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$J = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$J("Function expected") : fn; - var __decoratorContext$J = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$J[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$J("Already initialized") : fns.push(__expectFn$J(fn || null)) }); - var __decoratorMetadata$J = (array, target) => __defNormalProp$U(target, __knownSymbol$J("metadata"), array[3]); - var __runInitializers$J = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$J = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$J[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$J({ get [name]() { - return __privateGet$I(this, extra); - }, set [name](x2) { - return __privateSet$I(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$J(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$J(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$J("Object expected"); - else __expectFn$J(fn = it.get) && (desc.get = fn), __expectFn$J(fn = it.set) && (desc.set = fn), __expectFn$J(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$U(target, name, desc), target; - }; - var __publicField$U = (obj, key, value) => __defNormalProp$U(obj, key + "", value); - var __accessCheck$I = (obj, member, msg2) => member.has(obj) || __typeError$J("Cannot " + msg2); - var __privateGet$I = (obj, member, getter) => (__accessCheck$I(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$I = (obj, member, value) => member.has(obj) ? __typeError$J("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$I = (obj, member, value, setter) => (__accessCheck$I(obj, member, "write to private field"), member.set(obj, value), value); - var _hass_dec$9, _stateObj_dec$2, _secondaryText_dec, _name_dec$3, _icon_dec$1, _itemId_dec, _type_dec, _a$J, _init$J, _type, _itemId, _icon$1, _name$3, _secondaryText, _stateObj$2, _hass$9; - class AnnotationChip extends (_a$J = i$2, _type_dec = [n({ type: String })], _itemId_dec = [n({ type: String, attribute: "item-id" })], _icon_dec$1 = [n({ type: String })], _name_dec$3 = [n({ type: String })], _secondaryText_dec = [n({ type: String, attribute: "secondary-text" })], _stateObj_dec$2 = [n({ type: Object, attribute: false })], _hass_dec$9 = [n({ type: Object, attribute: false })], _a$J) { - constructor() { - super(...arguments); - __privateAdd$I(this, _type, __runInitializers$J(_init$J, 8, this, "")), __runInitializers$J(_init$J, 11, this); - __privateAdd$I(this, _itemId, __runInitializers$J(_init$J, 12, this, "")), __runInitializers$J(_init$J, 15, this); - __privateAdd$I(this, _icon$1, __runInitializers$J(_init$J, 16, this, "")), __runInitializers$J(_init$J, 19, this); - __privateAdd$I(this, _name$3, __runInitializers$J(_init$J, 20, this, "")), __runInitializers$J(_init$J, 23, this); - __privateAdd$I(this, _secondaryText, __runInitializers$J(_init$J, 24, this, "")), __runInitializers$J(_init$J, 27, this); - __privateAdd$I(this, _stateObj$2, __runInitializers$J(_init$J, 28, this, null)), __runInitializers$J(_init$J, 31, this); - __privateAdd$I(this, _hass$9, __runInitializers$J(_init$J, 32, this, null)), __runInitializers$J(_init$J, 35, this); - } - _onRemove() { - this.dispatchEvent( - new CustomEvent("dp-chip-remove", { - detail: { type: this.type, itemId: this.itemId }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/annotation-chip/annotation-chip.ts + var _type_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _itemId_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _icon_accessor_storage$2 = /* @__PURE__ */ new WeakMap(); + var _name_accessor_storage$3 = /* @__PURE__ */ new WeakMap(); + var _secondaryText_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _stateObj_accessor_storage$2 = /* @__PURE__ */ new WeakMap(); + var _hass_accessor_storage$9 = /* @__PURE__ */ new WeakMap(); + var AnnotationChip = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _type_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _itemId_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _icon_accessor_storage$2, ""); + _classPrivateFieldInitSpec(this, _name_accessor_storage$3, ""); + _classPrivateFieldInitSpec(this, _secondaryText_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _stateObj_accessor_storage$2, null); + _classPrivateFieldInitSpec(this, _hass_accessor_storage$9, null); + } + get type() { + return _classPrivateFieldGet2(_type_accessor_storage, this); + } + set type(value) { + _classPrivateFieldSet2(_type_accessor_storage, this, value); + } + get itemId() { + return _classPrivateFieldGet2(_itemId_accessor_storage, this); + } + set itemId(value) { + _classPrivateFieldSet2(_itemId_accessor_storage, this, value); + } + get icon() { + return _classPrivateFieldGet2(_icon_accessor_storage$2, this); + } + set icon(value) { + _classPrivateFieldSet2(_icon_accessor_storage$2, this, value); + } + get name() { + return _classPrivateFieldGet2(_name_accessor_storage$3, this); + } + set name(value) { + _classPrivateFieldSet2(_name_accessor_storage$3, this, value); + } + get secondaryText() { + return _classPrivateFieldGet2(_secondaryText_accessor_storage, this); + } + set secondaryText(value) { + _classPrivateFieldSet2(_secondaryText_accessor_storage, this, value); + } + get stateObj() { + return _classPrivateFieldGet2(_stateObj_accessor_storage$2, this); + } + set stateObj(value) { + _classPrivateFieldSet2(_stateObj_accessor_storage$2, this, value); + } + get hass() { + return _classPrivateFieldGet2(_hass_accessor_storage$9, this); + } + set hass(value) { + _classPrivateFieldSet2(_hass_accessor_storage$9, this, value); + } + _onRemove() { + this.dispatchEvent(new CustomEvent("dp-chip-remove", { + detail: { + type: this.type, + itemId: this.itemId + }, + bubbles: true, + composed: true + })); + } + render() { + return b` ${this.stateObj ? b` `; - } - } - _init$J = __decoratorStart$J(_a$J); - _type = /* @__PURE__ */ new WeakMap(); - _itemId = /* @__PURE__ */ new WeakMap(); - _icon$1 = /* @__PURE__ */ new WeakMap(); - _name$3 = /* @__PURE__ */ new WeakMap(); - _secondaryText = /* @__PURE__ */ new WeakMap(); - _stateObj$2 = /* @__PURE__ */ new WeakMap(); - _hass$9 = /* @__PURE__ */ new WeakMap(); - __decorateElement$J(_init$J, 4, "type", _type_dec, AnnotationChip, _type); - __decorateElement$J(_init$J, 4, "itemId", _itemId_dec, AnnotationChip, _itemId); - __decorateElement$J(_init$J, 4, "icon", _icon_dec$1, AnnotationChip, _icon$1); - __decorateElement$J(_init$J, 4, "name", _name_dec$3, AnnotationChip, _name$3); - __decorateElement$J(_init$J, 4, "secondaryText", _secondaryText_dec, AnnotationChip, _secondaryText); - __decorateElement$J(_init$J, 4, "stateObj", _stateObj_dec$2, AnnotationChip, _stateObj$2); - __decorateElement$J(_init$J, 4, "hass", _hass_dec$9, AnnotationChip, _hass$9); - __decoratorMetadata$J(_init$J, AnnotationChip); - __publicField$U(AnnotationChip, "styles", styles$Q); - customElements.define("annotation-chip", AnnotationChip); - var __create$I = Object.create; - var __defProp$T = Object.defineProperty; - var __getOwnPropDesc$I = Object.getOwnPropertyDescriptor; - var __knownSymbol$I = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$I = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$T = (obj, key, value) => key in obj ? __defProp$T(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$I = (base) => [, , , __create$I(base?.[__knownSymbol$I("metadata")] ?? null)]; - var __decoratorStrings$I = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$I = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$I("Function expected") : fn; - var __decoratorContext$I = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$I[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$I("Already initialized") : fns.push(__expectFn$I(fn || null)) }); - var __decoratorMetadata$I = (array, target) => __defNormalProp$T(target, __knownSymbol$I("metadata"), array[3]); - var __runInitializers$I = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$I = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$I[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$I({ get [name]() { - return __privateGet$H(this, extra); - }, set [name](x2) { - return __privateSet$H(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$I(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$I(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$I("Object expected"); - else __expectFn$I(fn = it.get) && (desc.get = fn), __expectFn$I(fn = it.set) && (desc.set = fn), __expectFn$I(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$T(target, name, desc), target; - }; - var __publicField$T = (obj, key, value) => __defNormalProp$T(obj, key + "", value); - var __accessCheck$H = (obj, member, msg2) => member.has(obj) || __typeError$I("Cannot " + msg2); - var __privateGet$H = (obj, member, getter) => (__accessCheck$H(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$H = (obj, member, value) => member.has(obj) ? __typeError$I("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$H = (obj, member, value, setter) => (__accessCheck$H(obj, member, "write to private field"), member.set(obj, value), value); - var _emptyText_dec, _helpText_dec, _label_dec$9, _hass_dec$8, _chips_dec, _a$I, _init$I, _chips, _hass$8, _label$9, _helpText, _emptyText; - class AnnotationChipRow extends (_a$I = i$2, _chips_dec = [n({ type: Array })], _hass_dec$8 = [n({ type: Object, attribute: false })], _label_dec$9 = [n({ type: String })], _helpText_dec = [n({ type: String, attribute: "help-text" })], _emptyText_dec = [n({ type: String, attribute: "empty-text" })], _a$I) { - constructor() { - super(...arguments); - __privateAdd$H(this, _chips, __runInitializers$I(_init$I, 8, this, [])), __runInitializers$I(_init$I, 11, this); - __privateAdd$H(this, _hass$8, __runInitializers$I(_init$I, 12, this, null)), __runInitializers$I(_init$I, 15, this); - __privateAdd$H(this, _label$9, __runInitializers$I(_init$I, 16, this, "Linked targets")), __runInitializers$I(_init$I, 19, this); - __privateAdd$H(this, _helpText, __runInitializers$I(_init$I, 20, this, "These targets will be associated with the new data point by default. Remove any that should not be linked.")), __runInitializers$I(_init$I, 23, this); - __privateAdd$H(this, _emptyText, __runInitializers$I(_init$I, 24, this, "No linked targets will be associated with this data point.")), __runInitializers$I(_init$I, 27, this); - } - _onChipRemove(ev) { - const detail = ev.detail; - ev.stopPropagation(); - this.dispatchEvent( - new CustomEvent("dp-target-remove", { - detail: { type: detail.type, id: detail.itemId }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b` + } + }; + _defineProperty(AnnotationChip, "styles", styles$52); + __decorate([n$1({ type: String })], AnnotationChip.prototype, "type", null); + __decorate([n$1({ + type: String, + attribute: "item-id" + })], AnnotationChip.prototype, "itemId", null); + __decorate([n$1({ type: String })], AnnotationChip.prototype, "icon", null); + __decorate([n$1({ type: String })], AnnotationChip.prototype, "name", null); + __decorate([n$1({ + type: String, + attribute: "secondary-text" + })], AnnotationChip.prototype, "secondaryText", null); + __decorate([n$1({ + type: Object, + attribute: false + })], AnnotationChip.prototype, "stateObj", null); + __decorate([n$1({ + type: Object, + attribute: false + })], AnnotationChip.prototype, "hass", null); + customElements.define("annotation-chip", AnnotationChip); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/annotation-chip-row/annotation-chip-row.ts + var _chips_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _hass_accessor_storage$8 = /* @__PURE__ */ new WeakMap(); + var _label_accessor_storage$9 = /* @__PURE__ */ new WeakMap(); + var _helpText_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _emptyText_accessor_storage = /* @__PURE__ */ new WeakMap(); + /** + * `annotation-chip-row` renders a labelled group of removable annotation + * target chips. When the list is empty it shows an empty-state help text instead. + * + * @fires dp-target-remove - `{ type: string, id: string }` fired when a chip's + * remove button is clicked. + */ + var AnnotationChipRow = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _chips_accessor_storage, []); + _classPrivateFieldInitSpec(this, _hass_accessor_storage$8, null); + _classPrivateFieldInitSpec(this, _label_accessor_storage$9, "Linked targets"); + _classPrivateFieldInitSpec(this, _helpText_accessor_storage, "These targets will be associated with the new data point by default. Remove any that should not be linked."); + _classPrivateFieldInitSpec(this, _emptyText_accessor_storage, "No linked targets will be associated with this data point."); + } + get chips() { + return _classPrivateFieldGet2(_chips_accessor_storage, this); + } + set chips(value) { + _classPrivateFieldSet2(_chips_accessor_storage, this, value); + } + get hass() { + return _classPrivateFieldGet2(_hass_accessor_storage$8, this); + } + set hass(value) { + _classPrivateFieldSet2(_hass_accessor_storage$8, this, value); + } + get label() { + return _classPrivateFieldGet2(_label_accessor_storage$9, this); + } + set label(value) { + _classPrivateFieldSet2(_label_accessor_storage$9, this, value); + } + get helpText() { + return _classPrivateFieldGet2(_helpText_accessor_storage, this); + } + set helpText(value) { + _classPrivateFieldSet2(_helpText_accessor_storage, this, value); + } + get emptyText() { + return _classPrivateFieldGet2(_emptyText_accessor_storage, this); + } + set emptyText(value) { + _classPrivateFieldSet2(_emptyText_accessor_storage, this, value); + } + _onChipRemove(ev) { + const detail = ev.detail; + ev.stopPropagation(); + this.dispatchEvent(new CustomEvent("dp-target-remove", { + detail: { + type: detail.type, + id: detail.itemId + }, + bubbles: true, + composed: true + })); + } + render() { + return b`
-
${this.chips.length > 0 ? this.helpText : this.emptyText}
@@ -14161,10 +14984,7 @@ ${content.alert}` : "", class="context-chip-row" @dp-chip-remove=${this._onChipRemove} > - ${c( - this.chips, - (chip) => `${chip.type}:${chip.itemId}`, - (chip) => b` + ${c(this.chips, (chip) => `${chip.type}:${chip.itemId}`, (chip) => b` - ` - )} + `)}
` : A} `; - } - } - _init$I = __decoratorStart$I(_a$I); - _chips = /* @__PURE__ */ new WeakMap(); - _hass$8 = /* @__PURE__ */ new WeakMap(); - _label$9 = /* @__PURE__ */ new WeakMap(); - _helpText = /* @__PURE__ */ new WeakMap(); - _emptyText = /* @__PURE__ */ new WeakMap(); - __decorateElement$I(_init$I, 4, "chips", _chips_dec, AnnotationChipRow, _chips); - __decorateElement$I(_init$I, 4, "hass", _hass_dec$8, AnnotationChipRow, _hass$8); - __decorateElement$I(_init$I, 4, "label", _label_dec$9, AnnotationChipRow, _label$9); - __decorateElement$I(_init$I, 4, "helpText", _helpText_dec, AnnotationChipRow, _helpText); - __decorateElement$I(_init$I, 4, "emptyText", _emptyText_dec, AnnotationChipRow, _emptyText); - __decoratorMetadata$I(_init$I, AnnotationChipRow); - __publicField$T(AnnotationChipRow, "styles", styles$R); - customElements.define("annotation-chip-row", AnnotationChipRow); - var __defProp$S = Object.defineProperty; - var __defNormalProp$S = (obj, key, value) => key in obj ? __defProp$S(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$S = (obj, key, value) => __defNormalProp$S(obj, typeof key !== "symbol" ? key + "" : key, value); - class HistoryAnnotationDialogController { - constructor(host) { - __publicField$S(this, "_host"); - __publicField$S(this, "_dialogEl"); - __publicField$S(this, "_panelEl"); - __publicField$S(this, "_chipRowEl"); - __publicField$S(this, "_linkedTarget"); - __publicField$S(this, "_target"); - this._host = host; - this._dialogEl = null; - this._panelEl = null; - this._chipRowEl = null; - this._linkedTarget = normalizeTargetSelection({}); - this._target = normalizeTargetSelection({}); - } - isOpen() { - return !!this._dialogEl?.open; - } - ensureDialog() { - if (this._dialogEl || !this._host.shadowRoot) { - return; - } - const dialog = document.createElement( - "ha-dialog" - ); - dialog.id = "chart-context-dialog"; - dialog.scrimClickAction = true; - dialog.escapeKeyAction = true; - dialog.open = false; - dialog.headerTitle = "Create data point"; - dialog.style.setProperty( - "--dialog-content-padding", - "0 var(--dp-spacing-lg, 24px) var(--dp-spacing-lg, 24px)" - ); - dialog.style.setProperty("--mdc-dialog-min-width", "min(920px, 96vw)"); - dialog.style.setProperty("--mdc-dialog-max-width", "96vw"); - if (this._host._hass) { - dialog.hass = this._host._hass; - } - dialog.innerHTML = ` + } + }; + _defineProperty(AnnotationChipRow, "styles", styles$53); + __decorate([n$1({ type: Array })], AnnotationChipRow.prototype, "chips", null); + __decorate([n$1({ + type: Object, + attribute: false + })], AnnotationChipRow.prototype, "hass", null); + __decorate([n$1({ type: String })], AnnotationChipRow.prototype, "label", null); + __decorate([n$1({ + type: String, + attribute: "help-text" + })], AnnotationChipRow.prototype, "helpText", null); + __decorate([n$1({ + type: String, + attribute: "empty-text" + })], AnnotationChipRow.prototype, "emptyText", null); + customElements.define("annotation-chip-row", AnnotationChipRow); + //#endregion + //#region custom_components/hass_datapoints/src/components/annotation-dialog/annotation-dialog.ts + var HistoryAnnotationDialogController = class { + constructor(host) { + _defineProperty(this, "_host", void 0); + _defineProperty(this, "_dialogEl", void 0); + _defineProperty(this, "_panelEl", void 0); + _defineProperty(this, "_chipRowEl", void 0); + _defineProperty(this, "_linkedTarget", void 0); + _defineProperty(this, "_target", void 0); + this._host = host; + this._dialogEl = null; + this._panelEl = null; + this._chipRowEl = null; + this._linkedTarget = normalizeTargetSelection({}); + this._target = normalizeTargetSelection({}); + } + isOpen() { + return !!this._dialogEl?.open; + } + ensureDialog() { + if (this._dialogEl || !this._host.shadowRoot) return; + const dialog = document.createElement("ha-dialog"); + dialog.id = "chart-context-dialog"; + dialog.scrimClickAction = true; + dialog.escapeKeyAction = true; + dialog.open = false; + dialog.headerTitle = "Create data point"; + dialog.style.setProperty("--dialog-content-padding", "0 var(--dp-spacing-lg, 24px) var(--dp-spacing-lg, 24px)"); + dialog.style.setProperty("--mdc-dialog-min-width", "min(920px, 96vw)"); + dialog.style.setProperty("--mdc-dialog-max-width", "96vw"); + if (this._host._hass) dialog.hass = this._host._hass; + dialog.innerHTML = `
`; - dialog.addEventListener("closed", () => this.finalizeClose()); - this._host.shadowRoot.appendChild(dialog); - this._dialogEl = dialog; - this._panelEl = dialog.querySelector("#chart-context-dialog-panel"); - } - teardown() { - this.resetFormState(); - this._dialogEl?.remove(); - this._dialogEl = null; - this._panelEl = null; - this._chipRowEl = null; - } - resetFormState() { - this._linkedTarget = normalizeTargetSelection({}); - this._target = normalizeTargetSelection({}); - } - finalizeClose() { - this.teardown(); - this._host._creatingContextAnnotation = false; - } - _shake() { - if (!this._dialogEl) return; - const shadowRoot = this._host.shadowRoot; - if (!shadowRoot) return; - if (!shadowRoot.getElementById("dp-dialog-shake-style")) { - const style = document.createElement("style"); - style.id = "dp-dialog-shake-style"; - style.textContent = ` + dialog.addEventListener("closed", () => this.finalizeClose()); + this._host.shadowRoot.appendChild(dialog); + this._dialogEl = dialog; + this._panelEl = dialog.querySelector("#chart-context-dialog-panel"); + } + teardown() { + this.resetFormState(); + this._dialogEl?.remove(); + this._dialogEl = null; + this._panelEl = null; + this._chipRowEl = null; + } + resetFormState() { + this._linkedTarget = normalizeTargetSelection({}); + this._target = normalizeTargetSelection({}); + } + finalizeClose() { + this.teardown(); + this._host._creatingContextAnnotation = false; + } + _shake() { + if (!this._dialogEl) return; + const shadowRoot = this._host.shadowRoot; + if (!shadowRoot) return; + if (!shadowRoot.getElementById("dp-dialog-shake-style")) { + const style = document.createElement("style"); + style.id = "dp-dialog-shake-style"; + style.textContent = ` @keyframes dp-dialog-shake { 10%, 90% { transform: translate3d(-2px, 0, 0); } 20%, 80% { transform: translate3d(4px, 0, 0); } @@ -14279,284 +15090,199 @@ ${content.alert}` : "", animation: dp-dialog-shake 0.5s cubic-bezier(0.36, 0.07, 0.19, 0.97) both; } `; - shadowRoot.appendChild(style); - } - this._dialogEl.classList.remove("dp-shaking"); - void this._dialogEl.offsetWidth; - this._dialogEl.classList.add("dp-shaking"); - this._dialogEl.addEventListener( - "animationend", - () => this._dialogEl?.classList.remove("dp-shaking"), - { once: true } - ); - } - formatDate(timeMs) { - const value = new Date(timeMs); - const yyyy = value.getFullYear(); - const mm = String(value.getMonth() + 1).padStart(2, "0"); - const dd = String(value.getDate()).padStart(2, "0"); - const hh = String(value.getHours()).padStart(2, "0"); - const min = String(value.getMinutes()).padStart(2, "0"); - return `${yyyy}-${mm}-${dd}T${hh}:${min}`; - } - _buildChips(target) { - return [ - ...(target.entity_id || []).map((id) => { - const name = entityName(this._host._hass, id); - return { - type: "entity_id", - itemId: id, - icon: entityIcon(this._host._hass, id), - name, - secondaryText: name !== id ? id : "", - stateObj: this._host?._hass?.states?.[id] ?? null - }; - }), - ...(target.device_id || []).map((id) => ({ - type: "device_id", - itemId: id, - icon: deviceIcon(this._host._hass, id), - name: deviceName(this._host._hass, id) - })), - ...(target.area_id || []).map((id) => ({ - type: "area_id", - itemId: id, - icon: areaIcon(this._host._hass, id), - name: areaName(this._host._hass, id) - })), - ...(target.label_id || []).map((id) => ({ - type: "label_id", - itemId: id, - icon: labelIcon(this._host._hass, id), - name: labelName(this._host._hass, id) - })) - ]; - } - removeLinkedTarget(type, id) { - if (!this._linkedTarget || !type || !id) { - return; - } - const current = normalizeTargetSelection(this._linkedTarget); - if (!current[type]) { - return; - } - current[type] = current[type].filter((value) => value !== id); - this._linkedTarget = current; - if (this._chipRowEl) { - this._chipRowEl.hass = this._host._hass ?? null; - this._chipRowEl.chips = this._buildChips(this._linkedTarget); - } - } - bindTargetChipActions() { - } - _getDefaultLinkedTarget() { - const config = this._host?._config || {}; - const hiddenSeries = this._host?._hiddenSeries instanceof Set ? this._host._hiddenSeries : /* @__PURE__ */ new Set(); - const seriesSettings = Array.isArray(config.series_settings) ? config.series_settings : []; - const visibleEntityIds = seriesSettings.map( - (entry) => entry?.entity_id || entry?.entity || entry?.entityId || null - ).filter( - (entityId) => typeof entityId === "string" && entityId.length > 0 && !hiddenSeries.has(entityId) - ); - if (visibleEntityIds.length > 0) { - return normalizeTargetSelection({ - entity_id: [...new Set(visibleEntityIds)] - }); - } - return normalizeTargetSelection({ - entity_id: (this._host?._entityIds || []).filter(Boolean) - }); - } - bindFields(hover) { - if (!this._panelEl) { - return; - } - const prefill = hover?.annotationPrefill && typeof hover.annotationPrefill === "object" ? hover.annotationPrefill : {}; - const messageEl = this._panelEl.querySelector( - "#chart-context-message" - ); - const annotationEl = this._panelEl.querySelector( - "#chart-context-annotation" - ); - const iconPicker = this._panelEl.querySelector( - "#chart-context-icon" - ); - if (iconPicker) { - iconPicker.hass = this._host._hass; - iconPicker.value = prefill.icon || hover?.event?.icon || "mdi:bookmark"; - } - const targetSel = this._panelEl.querySelector( - "#chart-context-target" - ); - if (targetSel) { - targetSel.hass = this._host._hass; - targetSel.value = "{}"; - targetSel.addEventListener("value-changed", (ev) => { - this._target = normalizeTargetSelection( - ev.detail.value || {} - ); - }); - } - if (messageEl) { - messageEl.value = prefill.message || ""; - } - if (annotationEl) { - annotationEl.value = prefill.annotation || ""; - } - const chipContainer = this._panelEl.querySelector( - "#chart-context-linked-targets" - ); - if (chipContainer && !this._chipRowEl) { - const chipRow = document.createElement( - "annotation-chip-row" - ); - chipRow.hass = this._host._hass ?? null; - chipRow.addEventListener("dp-target-remove", (ev) => { - const detail = ev.detail; - this.removeLinkedTarget(detail.type, detail.id); - }); - chipContainer.appendChild(chipRow); - this._chipRowEl = chipRow; - } - if (this._chipRowEl) { - this._chipRowEl.chips = this._buildChips(this._linkedTarget); - } - this.bindTargetChipActions(); - const colorInput = this._panelEl.querySelector( - "#chart-context-color" - ); - const colorPreview = this._panelEl.querySelector( - "#chart-context-color-preview" - ); - const syncColor = () => { - if (colorPreview && colorInput) { - colorPreview.style.background = colorInput.value; - } - }; - if (colorInput) { - syncColor(); - colorInput.addEventListener("input", syncColor); - colorInput.addEventListener("change", syncColor); - } - this._dialogEl?.querySelector("#chart-context-cancel")?.addEventListener("click", () => this.close()); - this._dialogEl?.querySelector("#chart-context-save")?.addEventListener("click", () => this.submit()); - [messageEl, annotationEl].forEach((field) => { - field?.addEventListener("keydown", (ev) => { - const kev = ev; - if (kev.key === "Enter" && (kev.ctrlKey || kev.metaKey)) { - kev.preventDefault(); - this.submit(); - } - }); - }); - } - async submit() { - if (!this._panelEl || !this._host._hass) { - return; - } - const messageEl = this._panelEl.querySelector( - "#chart-context-message" - ); - const annotationEl = this._panelEl.querySelector( - "#chart-context-annotation" - ); - const dateEl = this._panelEl.querySelector( - "#chart-context-date" - ); - const iconPicker = this._panelEl.querySelector( - "#chart-context-icon" - ); - const colorInput = this._panelEl.querySelector( - "#chart-context-color" - ); - const saveButton = this._dialogEl?.querySelector( - "#chart-context-save" - ); - const feedbackEl = this._panelEl.querySelector( - "#chart-context-feedback" - ); - const message = (messageEl?.value || "").trim(); - if (!message) { - messageEl?.focus?.(); - this._shake(); - return; - } - const mergedTarget = mergeTargetSelections( - this._linkedTarget, - this._target || {} - ); - const payload = { message }; - const annotation = (annotationEl?.value || "").trim(); - if (annotation) { - payload.annotation = annotation; - } - const dateVal = (dateEl?.value || "").trim(); - if (dateVal) { - const parsedDate = new Date(dateVal); - payload.date = Number.isFinite(parsedDate.getTime()) ? parsedDate.toISOString() : dateVal; - } - const icon = iconPicker?.value; - if (icon) { - payload.icon = icon; - } - payload.color = colorInput?.value || "#03a9f4"; - if (mergedTarget.entity_id.length) { - payload.entity_ids = mergedTarget.entity_id; - } - if (mergedTarget.device_id.length) { - payload.device_ids = mergedTarget.device_id; - } - if (mergedTarget.area_id.length) { - payload.area_ids = mergedTarget.area_id; - } - if (mergedTarget.label_id.length) { - payload.label_ids = mergedTarget.label_id; - } - if (saveButton) saveButton.disabled = true; - if (feedbackEl) { - feedbackEl.hidden = true; - feedbackEl.textContent = ""; - } - try { - await this._host._hass.callService(DOMAIN, "record", payload); - invalidateEventsCache(); - window.dispatchEvent(new CustomEvent("hass-datapoints-event-recorded")); - this.close(); - } catch (err) { - if (feedbackEl) { - feedbackEl.hidden = false; - feedbackEl.textContent = err?.message || "Failed to create annotation."; - } - this._shake(); - logger$1.error("[hass-datapoints history-card]", err); - } finally { - if (saveButton) saveButton.disabled = false; - } - } - open(hover) { - if (this._dialogEl && !this._dialogEl.open) { - this.teardown(); - } - this.ensureDialog(); - if (!this._dialogEl || !this._panelEl || this._dialogEl.open) return; - this.resetFormState(); - const prefillLinkedTarget = hover?.annotationPrefill?.linkedTarget; - if (prefillLinkedTarget && typeof prefillLinkedTarget === "object") { - this._linkedTarget = normalizeTargetSelection(prefillLinkedTarget); - } else { - this._linkedTarget = this._getDefaultLinkedTarget(); - } - const defaultColor = hover?.annotationPrefill?.color || hover?.primary?.color || hover?.event?.color || "#03a9f4"; - this._panelEl.innerHTML = ` + shadowRoot.appendChild(style); + } + this._dialogEl.classList.remove("dp-shaking"); + this._dialogEl.offsetWidth; + this._dialogEl.classList.add("dp-shaking"); + this._dialogEl.addEventListener("animationend", () => this._dialogEl?.classList.remove("dp-shaking"), { once: true }); + } + formatDate(timeMs) { + const value = new Date(timeMs); + return `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, "0")}-${String(value.getDate()).padStart(2, "0")}T${String(value.getHours()).padStart(2, "0")}:${String(value.getMinutes()).padStart(2, "0")}`; + } + _buildChips(target) { + return [ + ...(target.entity_id || []).map((id) => { + const name = entityName(this._host._hass, id); + return { + type: "entity_id", + itemId: id, + icon: entityIcon(this._host._hass, id), + name, + secondaryText: name !== id ? id : "", + stateObj: (this._host?._hass?.states)?.[id] ?? null + }; + }), + ...(target.device_id || []).map((id) => ({ + type: "device_id", + itemId: id, + icon: deviceIcon(this._host._hass, id), + name: deviceName(this._host._hass, id) + })), + ...(target.area_id || []).map((id) => ({ + type: "area_id", + itemId: id, + icon: areaIcon(this._host._hass, id), + name: areaName(this._host._hass, id) + })), + ...(target.label_id || []).map((id) => ({ + type: "label_id", + itemId: id, + icon: labelIcon(this._host._hass, id), + name: labelName(this._host._hass, id) + })) + ]; + } + removeLinkedTarget(type, id) { + if (!this._linkedTarget || !type || !id) return; + const current = normalizeTargetSelection(this._linkedTarget); + if (!current[type]) return; + current[type] = current[type].filter((value) => value !== id); + this._linkedTarget = current; + if (this._chipRowEl) { + this._chipRowEl.hass = this._host._hass ?? null; + this._chipRowEl.chips = this._buildChips(mergeTargetSelections(this._linkedTarget, this._target)); + } + } + bindTargetChipActions() {} + _getDefaultLinkedTarget() { + const config = this._host?._config || {}; + const hiddenSeries = this._host?._hiddenSeries instanceof Set ? this._host._hiddenSeries : /* @__PURE__ */ new Set(); + const visibleEntityIds = (Array.isArray(config.series_settings) ? config.series_settings : []).map((entry) => entry?.entity_id || entry?.entity || entry?.entityId || null).filter((entityId) => typeof entityId === "string" && entityId.length > 0 && !hiddenSeries.has(entityId)); + if (visibleEntityIds.length > 0) return normalizeTargetSelection({ entity_id: [...new Set(visibleEntityIds)] }); + return normalizeTargetSelection({ entity_id: (this._host?._entityIds || []).filter(Boolean) }); + } + bindFields(hover) { + if (!this._panelEl) return; + const prefill = hover?.annotationPrefill && typeof hover.annotationPrefill === "object" ? hover.annotationPrefill : {}; + const messageEl = this._panelEl.querySelector("#chart-context-message"); + const annotationEl = this._panelEl.querySelector("#chart-context-annotation"); + const iconPicker = this._panelEl.querySelector("#chart-context-icon"); + if (iconPicker) { + iconPicker.hass = this._host._hass; + iconPicker.value = prefill.icon || hover?.event?.icon || "mdi:bookmark"; + } + if (messageEl) messageEl.value = prefill.message || ""; + if (annotationEl) annotationEl.value = prefill.annotation || ""; + const chipContainer = this._panelEl.querySelector("#chart-context-linked-targets"); + if (chipContainer && !this._chipRowEl) { + const chipRow = document.createElement("annotation-chip-row"); + chipRow.hass = this._host._hass ?? null; + chipRow.addEventListener("dp-target-remove", (ev) => { + const detail = ev.detail; + this.removeLinkedTarget(detail.type, detail.id); + }); + chipContainer.appendChild(chipRow); + this._chipRowEl = chipRow; + } + if (this._chipRowEl) this._chipRowEl.chips = this._buildChips(this._linkedTarget); + const targetSel = this._panelEl.querySelector("#chart-context-target"); + if (targetSel) { + targetSel.hass = this._host._hass; + targetSel.selector = { target: {} }; + targetSel.addEventListener("value-changed", (ev) => { + this._target = mergeTargetSelections(this._target, normalizeTargetSelection(ev.detail.value || {})); + if (this._chipRowEl) { + this._chipRowEl.hass = this._host._hass ?? null; + this._chipRowEl.chips = this._buildChips(mergeTargetSelections(this._linkedTarget, this._target)); + } + }); + } + this.bindTargetChipActions(); + const colorInput = this._panelEl.querySelector("#chart-context-color"); + const colorPreview = this._panelEl.querySelector("#chart-context-color-preview"); + const syncColor = () => { + if (colorPreview && colorInput) colorPreview.style.background = colorInput.value; + }; + if (colorInput) { + syncColor(); + colorInput.addEventListener("input", syncColor); + colorInput.addEventListener("change", syncColor); + } + this._dialogEl?.querySelector("#chart-context-cancel")?.addEventListener("click", () => this.close()); + this._dialogEl?.querySelector("#chart-context-save")?.addEventListener("click", () => this.submit()); + [messageEl, annotationEl].forEach((field) => { + field?.addEventListener("keydown", (ev) => { + const kev = ev; + if (kev.key === "Enter" && (kev.ctrlKey || kev.metaKey)) { + kev.preventDefault(); + this.submit(); + } + }); + }); + } + async submit() { + if (!this._panelEl || !this._host._hass) return; + const messageEl = this._panelEl.querySelector("#chart-context-message"); + const annotationEl = this._panelEl.querySelector("#chart-context-annotation"); + const dateEl = this._panelEl.querySelector("#chart-context-date"); + const iconPicker = this._panelEl.querySelector("#chart-context-icon"); + const colorInput = this._panelEl.querySelector("#chart-context-color"); + const saveButton = this._dialogEl?.querySelector("#chart-context-save"); + const feedbackEl = this._panelEl.querySelector("#chart-context-feedback"); + const message = (messageEl?.value || "").trim(); + if (!message) { + messageEl?.focus?.(); + this._shake(); + return; + } + const mergedTarget = mergeTargetSelections(this._linkedTarget, this._target || {}); + const payload = { message }; + const annotation = (annotationEl?.value || "").trim(); + if (annotation) payload.annotation = annotation; + const dateVal = (dateEl?.value || "").trim(); + if (dateVal) { + const parsedDate = new Date(dateVal); + payload.date = Number.isFinite(parsedDate.getTime()) ? parsedDate.toISOString() : dateVal; + } + const icon = iconPicker?.value; + if (icon) payload.icon = icon; + payload.color = colorInput?.value || "#03a9f4"; + if (mergedTarget.entity_id.length) payload.entity_ids = mergedTarget.entity_id; + if (mergedTarget.device_id.length) payload.device_ids = mergedTarget.device_id; + if (mergedTarget.area_id.length) payload.area_ids = mergedTarget.area_id; + if (mergedTarget.label_id.length) payload.label_ids = mergedTarget.label_id; + if (saveButton) saveButton.disabled = true; + if (feedbackEl) { + feedbackEl.hidden = true; + feedbackEl.textContent = ""; + } + try { + await this._host._hass.callService(DOMAIN, "record", payload); + invalidateEventsCache(); + window.dispatchEvent(new CustomEvent("hass-datapoints-event-recorded")); + this.close(); + } catch (err) { + if (feedbackEl) { + feedbackEl.hidden = false; + feedbackEl.textContent = err?.message || "Failed to create annotation."; + } + this._shake(); + logger$1.error("[hass-datapoints history-card]", err); + } finally { + if (saveButton) saveButton.disabled = false; + } + } + open(hover) { + if (this._dialogEl && !this._dialogEl.open) this.teardown(); + this.ensureDialog(); + if (!this._dialogEl || !this._panelEl || this._dialogEl.open) return; + this.resetFormState(); + const prefillLinkedTarget = hover?.annotationPrefill?.linkedTarget; + if (prefillLinkedTarget && typeof prefillLinkedTarget === "object") this._linkedTarget = normalizeTargetSelection(prefillLinkedTarget); + else this._linkedTarget = this._getDefaultLinkedTarget(); + const defaultColor = hover?.annotationPrefill?.color || hover?.primary?.color || hover?.event?.color || "#03a9f4"; + this._panelEl.innerHTML = `
-
+ +
Use a short title that will be shown in the chart tooltip and records list.
- -
Add any longer context, outcome, or note you want to keep with this data point.
- -
-
-
- -
Optionally add more entities, devices, areas, or labels that should also be linked to this annotation.
- -
-
-
-
The annotation will be placed at this exact moment on the chart.
-
+
+ +
+ +
Add any longer context, outcome, or note you want to keep with this data point.
+ +
+ +
+ +
+ +
+ +
+
Choose the icon shown for this data point in the chart and records list.
@@ -14634,1381 +15367,1024 @@ ${content.alert}` : "",
`; - if (this._dialogEl) { - this._dialogEl.hass = this._host._hass; - this._dialogEl.dialogInitialFocus = "#chart-context-message"; - } - const targetSel = this._panelEl.querySelector( - "#chart-context-target" - ); - if (targetSel) { - targetSel.selector = { target: {} }; - } - this.bindFields(hover); - this._dialogEl.open = true; - this._host._creatingContextAnnotation = true; - window.requestAnimationFrame(() => { - this._panelEl?.querySelector( - "#chart-context-message" - )?.focus?.(); - }); - } - close() { - this._host._creatingContextAnnotation = false; - if (this._dialogEl) this._dialogEl.open = false; - window.setTimeout(() => this.finalizeClose(), 0); - } - } - async function fetchStatisticsDuringPeriod(hass, startTime, endTime, statisticIds, options = {}) { - const normalizedStatisticIds = normalizeCacheIdList(statisticIds); - const normalizedTypes = normalizeCacheIdList(options.types); - const cacheKey = JSON.stringify({ - type: "recorder/statistics_during_period", - start_time: startTime, - end_time: endTime, - statistic_ids: normalizedStatisticIds, - period: options.period || "hour", - types: normalizedTypes - }); - return withStableRangeCache( - cacheKey, - endTime, - () => hass.connection.sendMessagePromise({ - type: "recorder/statistics_during_period", - start_time: startTime, - end_time: endTime, - statistic_ids: normalizedStatisticIds, - period: options.period || "hour", - types: normalizedTypes, - units: options.units || {} - }) - ); - } - const PANEL_HISTORY_PREFERENCES_KEY = `${DOMAIN}:panel_history_preferences`; - const PANEL_HISTORY_SESSION_KEY = `${DOMAIN}:panel_history_session`; - function readHistoryPageSessionState() { - try { - const raw = window.sessionStorage?.getItem(PANEL_HISTORY_SESSION_KEY); - if (!raw) { - return null; - } - const parsed = JSON.parse(raw); - return parsed && typeof parsed === "object" ? parsed : null; - } catch { - return null; - } - } - function buildHistoryPageSessionState(source) { - return { - entities: source._entities, - series_rows: source._seriesRows, - target_selection: source._targetSelection, - target_selection_raw: source._targetSelectionRaw, - datapoint_scope: source._datapointScope, - show_chart_datapoint_icons: source._showChartDatapointIcons, - show_chart_datapoint_lines: source._showChartDatapointLines, - show_chart_tooltips: source._showChartTooltips, - show_chart_emphasized_hover_guides: source._showChartEmphasizedHoverGuides, - chart_hover_snap_mode: source._chartHoverSnapMode, - delink_chart_y_axis: source._delinkChartYAxis, - split_chart_view: source._splitChartView, - show_chart_trend_lines: false, - show_chart_summary_stats: false, - show_chart_rate_of_change: false, - show_chart_threshold_analysis: false, - show_chart_threshold_shading: false, - show_chart_anomalies: false, - chart_anomaly_method: "trend_residual", - chart_anomaly_rate_window: "1h", - chart_anomaly_zscore_window: "24h", - chart_anomaly_persistence_window: "1h", - chart_anomaly_comparison_window_id: null, - hide_chart_source_series: false, - show_chart_trend_crosshairs: false, - chart_trend_method: "rolling_average", - chart_trend_window: "24h", - chart_rate_window: "1h", - chart_anomaly_sensitivity: "medium", - chart_threshold_values: {}, - chart_threshold_directions: {}, - show_chart_delta_analysis: false, - show_chart_delta_tooltip: true, - show_chart_delta_lines: false, - show_chart_correlated_anomalies: source._showCorrelatedAnomalies, - chart_anomaly_overlap_mode: source._chartAnomalyOverlapMode || "all", - show_data_gaps: source._showDataGaps, - data_gap_threshold: source._dataGapThreshold, - content_split_ratio: source._contentSplitRatio, - start_time: source._startTime?.toISOString() || null, - end_time: source._endTime?.toISOString() || null, - zoom_start_time: source._chartZoomCommittedRange ? new Date(source._chartZoomCommittedRange.start).toISOString() : null, - zoom_end_time: source._chartZoomCommittedRange ? new Date(source._chartZoomCommittedRange.end).toISOString() : null, - date_windows: normalizeDateWindows(source._comparisonWindows), - hours: source._hours, - sidebar_collapsed: source._sidebarCollapsed, - sidebar_accordion_targets_open: source._sidebarAccordionTargetsOpen !== false, - sidebar_accordion_datapoints_open: source._sidebarAccordionDatapointsOpen !== false, - sidebar_accordion_analysis_open: source._sidebarAccordionAnalysisOpen !== false, - sidebar_accordion_chart_open: source._sidebarAccordionChartOpen !== false - }; - } - function writeHistoryPageSessionState(source) { - try { - window.sessionStorage?.setItem( - PANEL_HISTORY_SESSION_KEY, - JSON.stringify(buildHistoryPageSessionState(source)) - ); - } catch { - } - } - function normalizeHistoryPagePreferences(preferences, options = {}) { - const zoomValues = new Set( - (options.zoomOptions || []).map((option) => option.value) - ); - const snapValues = new Set( - (options.snapOptions || []).map((option) => option.value) - ); - let shouldPersistDefaults = false; - const normalized = { - zoomLevel: "auto", - dateSnapping: "hour", - preferredSeriesColors: {}, - comparisonWindows: [], - pageState: null, - shouldPersistDefaults - }; - if (preferences && typeof preferences === "object") { - if (preferences.zoom_level && zoomValues.has(preferences.zoom_level)) { - normalized.zoomLevel = preferences.zoom_level; - } else { - shouldPersistDefaults = true; - } - if (preferences.date_snapping && snapValues.has(preferences.date_snapping)) { - normalized.dateSnapping = preferences.date_snapping; - } else { - shouldPersistDefaults = true; - } - normalized.preferredSeriesColors = preferences.series_colors && typeof preferences.series_colors === "object" ? Object.entries( - preferences.series_colors - ).reduce((acc, [entityId, color]) => { - if (typeof entityId === "string" && /^#[0-9a-f]{6}$/i.test(color || "")) { - acc[entityId] = color; - } - return acc; - }, {}) : {}; - normalized.comparisonWindows = normalizeDateWindows( - preferences.date_windows - ); - normalized.pageState = preferences.page_state && typeof preferences.page_state === "object" ? { - ...preferences.page_state, - date_windows: normalizeDateWindows( - preferences.page_state?.date_windows - ) - } : null; - } else { - shouldPersistDefaults = true; - } - normalized.shouldPersistDefaults = shouldPersistDefaults; - return normalized; - } - function buildHistoryPagePreferencesPayload(source) { - const preferredSeriesColors = source._seriesRows.reduce( - (acc, row) => { - const entityId = typeof row === "object" && row && "entity_id" in row ? row.entity_id : null; - const color = typeof row === "object" && row && "color" in row ? row.color : null; - if (typeof entityId === "string" && /^#[0-9a-f]{6}$/i.test(String(color || ""))) { - acc[entityId] = String(color); - } - return acc; - }, - { ...source._preferredSeriesColors } - ); - return { - zoom_level: source._zoomLevel, - date_snapping: source._dateSnapping, - series_colors: preferredSeriesColors, - date_windows: normalizeDateWindows(source._comparisonWindows), - page_state: buildHistoryPageSessionState(source) - }; - } - var __defProp$R = Object.defineProperty; - var __defNormalProp$R = (obj, key, value) => key in obj ? __defProp$R(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$R = (obj, key, value) => __defNormalProp$R(obj, typeof key !== "symbol" ? key + "" : key, value); - class HassRecordsHistoryCard extends ChartCardBase { - // ── Constructor ──────────────────────────────────────────────────────────── - constructor() { - super(); - __publicField$R(this, "_hiddenSeries", /* @__PURE__ */ new Set()); - __publicField$R(this, "_hiddenEventIds", /* @__PURE__ */ new Set()); - __publicField$R(this, "_zoomRange", null); - __publicField$R(this, "_configKey"); - __publicField$R(this, "_comparisonRequestId", 0); - __publicField$R(this, "_comparisonDataCache", /* @__PURE__ */ new Map()); - __publicField$R(this, "_lastComparisonResults", null); - __publicField$R(this, "_lastHistResult", null); - __publicField$R(this, "_lastStatsResult", null); - __publicField$R(this, "_lastEvents", null); - __publicField$R(this, "_lastT0", 0); - __publicField$R(this, "_lastT1", 0); - __publicField$R(this, "_scrollSyncSuspended", false); - __publicField$R(this, "_lastProgrammaticScrollLeft", null); - __publicField$R(this, "_ignoreNextProgrammaticScrollEvent", false); - __publicField$R(this, "_adjustComparisonAxisScale", false); - __publicField$R(this, "_drawRequestId", 0); - __publicField$R(this, "_zoomReloadTimer", null); - __publicField$R(this, "_chartScrollViewportEl", null); - __publicField$R(this, "_annotationDialog"); - __publicField$R(this, "_onWindowKeyDown"); - __publicField$R(this, "_onChartScroll"); - __publicField$R(this, "_onZoomApply"); - this._annotationDialog = new HistoryAnnotationDialogController( - this - ); - this._onWindowKeyDown = (ev) => this._handleWindowKeyDown(ev); - this._onChartScroll = () => this._handleChartScroll(); - this._onZoomApply = (ev) => { - const detail = ev.detail; - this._zoomRange = detail ? { start: detail.start, end: detail.end } : null; - this._dispatchZoomRange(detail ? "zoom" : "reset"); - if (this._lastHistResult && this._lastEvents) { - this._queueDrawChart( - this._lastHistResult, - this._lastStatsResult || {}, - this._lastEvents, - this._lastT0, - this._lastT1 - ); - } - this._scheduleZoomReload(); - }; - } - // ── Hass setter — suppress reload-on-every-state-change ─────────────────── - /** - * Override the base setter to prevent a full history refetch on every HA - * state update. History data is fetched from the history/statistics APIs and - * does not change when entity states tick; reloads are already triggered by: - * - config changes (setConfig) - * - new datapoints recorded (hass_datapoints_event_recorded subscription) - * - auto-refresh interval (base class) - * - * We still call requestUpdate() so that entity-friendly-names in the legend - * stay fresh, and we propagate the new hass reference down to hass-datapoints-history-chart. - */ - set hass(hass) { - this._hass = hass; - this.requestUpdate(); - const chartEl = this._chartEl(); - if (chartEl) { - chartEl.hass = hass; - } - } - get hass() { - return this._hass; - } - // ── Lifecycle ────────────────────────────────────────────────────────────── - connectedCallback() { - window.addEventListener("keydown", this._onWindowKeyDown); - this.addEventListener("hass-datapoints-zoom-apply", this._onZoomApply); - super.connectedCallback(); - } - disconnectedCallback() { - window.removeEventListener("keydown", this._onWindowKeyDown); - this.removeEventListener("hass-datapoints-zoom-apply", this._onZoomApply); - if (this._zoomReloadTimer != null) { - window.clearTimeout(this._zoomReloadTimer); - this._zoomReloadTimer = null; - } - if (this._chartScrollViewportEl) { - this._chartScrollViewportEl.removeEventListener( - "scroll", - this._onChartScroll - ); - } - this._annotationDialog?.teardown(); - super.disconnectedCallback(); - } - // ── Configuration ────────────────────────────────────────────────────────── - setConfig(config) { - if (!config.entity && !config.entities && !config.target && !(Array.isArray(config.series_settings) && config.series_settings.length > 0)) { - throw new Error( - "hass-datapoints-history-card: define `target`, `entity`, `entities` or `series_settings`" - ); - } - const nextConfig = { - hours_to_show: 24, - ...config, - target: config.target ? normalizeTargetValue(config.target) : void 0, - series_settings: Array.isArray(config.series_settings) ? config.series_settings.map((entry) => ({ - ...entry, - analysis: entry?.analysis && typeof entry.analysis === "object" ? { ...entry.analysis } : entry?.analysis - })) : config.series_settings, - hidden_event_ids: Array.isArray(config.hidden_event_ids) ? [...config.hidden_event_ids] : config.hidden_event_ids, - hovered_event_ids: Array.isArray(config.hovered_event_ids) ? [...config.hovered_event_ids] : config.hovered_event_ids, - comparison_windows: Array.isArray(config.comparison_windows) ? config.comparison_windows.map((entry) => ({ - ...entry - })) : config.comparison_windows, - preload_comparison_windows: Array.isArray( - config.preload_comparison_windows - ) ? config.preload_comparison_windows.map( - (entry) => ({ ...entry }) - ) : config.preload_comparison_windows, - comparison_preview_overlay: config.comparison_preview_overlay ? { ...config.comparison_preview_overlay } : null, - selected_comparison_window_id: config.selected_comparison_window_id || null, - hovered_comparison_window_id: config.hovered_comparison_window_id || null, - show_trend_lines: config.show_trend_lines === true, - show_summary_stats: config.show_summary_stats === true, - show_rate_of_change: config.show_rate_of_change === true, - show_threshold_analysis: config.show_threshold_analysis === true, - show_threshold_shading: config.show_threshold_shading === true, - hide_raw_data: config.hide_raw_data === true, - show_trend_crosshairs: config.show_trend_crosshairs === true, - trend_method: config.trend_method || "rolling_average", - trend_window: config.trend_window || "24h", - rate_window: config.rate_window || "1h", - threshold_values: config.threshold_values && typeof config.threshold_values === "object" ? { ...config.threshold_values } : {}, - threshold_directions: config.threshold_directions && typeof config.threshold_directions === "object" ? { ...config.threshold_directions } : {}, - show_delta_analysis: config.show_delta_analysis === true, - show_delta_tooltip: config.show_delta_tooltip !== false, - show_delta_lines: config.show_delta_lines === true, - hide_delta_source_series: config.hide_delta_source_series === true, - delink_y_axis: config.delink_y_axis === true, - split_view: config.split_view === true, - show_data_gaps: config.show_data_gaps !== false, - data_gap_threshold: config.data_gap_threshold || "2h" - }; - const currentConfig = this._config || {}; - const currentDataKey = JSON.stringify({ - target: currentConfig.target || null, - entities: currentConfig.entities, - entity: currentConfig.entity, - series_entities: Array.isArray( - currentConfig.series_settings - ) ? currentConfig.series_settings.map( - (entry) => entry?.entity_id || entry?.entity || entry?.entityId || null - ) : null, - datapoint_scope: currentConfig.datapoint_scope, - hours_to_show: currentConfig.hours_to_show, - start_time: currentConfig.start_time, - end_time: currentConfig.end_time - }); - const nextDataKey = JSON.stringify({ - target: nextConfig.target || null, - entities: nextConfig.entities, - entity: nextConfig.entity, - series_entities: Array.isArray(nextConfig.series_settings) ? nextConfig.series_settings.map( - (entry) => entry?.entity_id || entry?.entity || entry?.entityId || null - ) : null, - datapoint_scope: nextConfig.datapoint_scope, - hours_to_show: nextConfig.hours_to_show, - start_time: nextConfig.start_time, - end_time: nextConfig.end_time - }); - const currentViewKey = JSON.stringify({ - series_settings: currentConfig.series_settings || [], - zoom_start_time: currentConfig.zoom_start_time, - zoom_end_time: currentConfig.zoom_end_time, - message_filter: currentConfig.message_filter || "", - hidden_event_ids: currentConfig.hidden_event_ids || [], - hovered_event_ids: currentConfig.hovered_event_ids || [], - show_event_markers: currentConfig.show_event_markers !== false, - show_event_lines: currentConfig.show_event_lines !== false, - show_tooltips: currentConfig.show_tooltips !== false, - emphasize_hover_guides: currentConfig.emphasize_hover_guides === true, - hover_snap_mode: currentConfig.hover_snap_mode || "follow_series", - show_correlated_anomalies: currentConfig.show_correlated_anomalies === true, - show_trend_lines: currentConfig.show_trend_lines === true, - show_summary_stats: currentConfig.show_summary_stats === true, - show_rate_of_change: currentConfig.show_rate_of_change === true, - show_threshold_analysis: currentConfig.show_threshold_analysis === true, - show_threshold_shading: currentConfig.show_threshold_shading === true, - show_anomalies: currentConfig.show_anomalies === true, - hide_raw_data: currentConfig.hide_raw_data === true, - show_trend_crosshairs: currentConfig.show_trend_crosshairs === true, - trend_method: currentConfig.trend_method || "rolling_average", - trend_window: currentConfig.trend_window || "24h", - rate_window: currentConfig.rate_window || "1h", - anomaly_sensitivity: currentConfig.anomaly_sensitivity || "medium", - threshold_values: currentConfig.threshold_values || {}, - threshold_directions: currentConfig.threshold_directions || {}, - show_delta_analysis: currentConfig.show_delta_analysis === true, - show_delta_tooltip: currentConfig.show_delta_tooltip !== false, - show_delta_lines: currentConfig.show_delta_lines === true, - hide_delta_source_series: currentConfig.hide_delta_source_series === true, - delink_y_axis: currentConfig.delink_y_axis === true, - split_view: currentConfig.split_view === true, - show_data_gaps: currentConfig.show_data_gaps !== false, - data_gap_threshold: currentConfig.data_gap_threshold || "2h", - comparison_hover_active: currentConfig.comparison_hover_active === true, - selected_comparison_window_id: currentConfig.selected_comparison_window_id || null, - hovered_comparison_window_id: currentConfig.hovered_comparison_window_id || null - }); - const nextViewKey = JSON.stringify({ - series_settings: nextConfig.series_settings || [], - zoom_start_time: nextConfig.zoom_start_time, - zoom_end_time: nextConfig.zoom_end_time, - message_filter: nextConfig.message_filter || "", - hidden_event_ids: nextConfig.hidden_event_ids || [], - hovered_event_ids: nextConfig.hovered_event_ids || [], - show_event_markers: nextConfig.show_event_markers !== false, - show_event_lines: nextConfig.show_event_lines !== false, - show_tooltips: nextConfig.show_tooltips !== false, - emphasize_hover_guides: nextConfig.emphasize_hover_guides === true, - hover_snap_mode: nextConfig.hover_snap_mode || "follow_series", - show_correlated_anomalies: nextConfig.show_correlated_anomalies === true, - show_trend_lines: nextConfig.show_trend_lines === true, - show_summary_stats: nextConfig.show_summary_stats === true, - show_rate_of_change: nextConfig.show_rate_of_change === true, - show_threshold_analysis: nextConfig.show_threshold_analysis === true, - show_threshold_shading: nextConfig.show_threshold_shading === true, - show_anomalies: nextConfig.show_anomalies === true, - hide_raw_data: nextConfig.hide_raw_data === true, - show_trend_crosshairs: nextConfig.show_trend_crosshairs === true, - trend_method: nextConfig.trend_method || "rolling_average", - trend_window: nextConfig.trend_window || "24h", - rate_window: nextConfig.rate_window || "1h", - anomaly_sensitivity: nextConfig.anomaly_sensitivity || "medium", - threshold_values: nextConfig.threshold_values || {}, - threshold_directions: nextConfig.threshold_directions || {}, - show_delta_analysis: nextConfig.show_delta_analysis === true, - show_delta_tooltip: nextConfig.show_delta_tooltip !== false, - show_delta_lines: nextConfig.show_delta_lines === true, - hide_delta_source_series: nextConfig.hide_delta_source_series === true, - delink_y_axis: nextConfig.delink_y_axis === true, - split_view: nextConfig.split_view === true, - show_data_gaps: nextConfig.show_data_gaps !== false, - data_gap_threshold: nextConfig.data_gap_threshold || "2h", - comparison_hover_active: nextConfig.comparison_hover_active === true, - selected_comparison_window_id: nextConfig.selected_comparison_window_id || null, - hovered_comparison_window_id: nextConfig.hovered_comparison_window_id || null - }); - const currentComparisonKey = JSON.stringify( - currentConfig.comparison_windows || [] - ); - const nextComparisonKey = JSON.stringify( - nextConfig.comparison_windows || [] - ); - const currentPreloadComparisonKey = JSON.stringify( - currentConfig.preload_comparison_windows || [] - ); - const nextPreloadComparisonKey = JSON.stringify( - nextConfig.preload_comparison_windows || [] - ); - const currentComparisonOverlayKey = JSON.stringify( - currentConfig.comparison_preview_overlay || null - ); - const nextComparisonOverlayKey = JSON.stringify( - nextConfig.comparison_preview_overlay || null - ); - const dataChanged = currentDataKey !== nextDataKey; - const viewChanged = currentViewKey !== nextViewKey; - const comparisonChanged = currentComparisonKey !== nextComparisonKey; - const preloadComparisonChanged = currentPreloadComparisonKey !== nextPreloadComparisonKey; - const comparisonOverlayChanged = currentComparisonOverlayKey !== nextComparisonOverlayKey; - if (!dataChanged && !viewChanged && !comparisonChanged && !preloadComparisonChanged && !comparisonOverlayChanged && this._configKey) { - return; - } - this._config = nextConfig; - this._configKey = JSON.stringify(nextConfig); - this._hiddenSeries = createHiddenSeriesSet( - nextConfig.series_settings - ); - this._hiddenEventIds = createHiddenEventIdSet( - nextConfig.hidden_event_ids - ); - this._zoomRange = createChartZoomRange( - nextConfig.zoom_start_time, - nextConfig.zoom_end_time - ); - if (dataChanged || comparisonChanged || preloadComparisonChanged) { - this._pruneComparisonDataCache(nextConfig); - this._lastComparisonResults = null; - } - const chartEl = this._chartEl(); - if (chartEl) { - chartEl.hass = this._hass; - chartEl._config = this._config; - chartEl._hiddenSeries = this._hiddenSeries; - chartEl._hiddenEventIds = this._hiddenEventIds; - chartEl._zoomRange = this._zoomRange; - chartEl._lastComparisonResults = this._getResolvedComparisonResults(); - if (comparisonOverlayChanged && typeof chartEl._renderComparisonPreviewOverlay === "function") { - chartEl._renderComparisonPreviewOverlay(); - } - } - if (dataChanged || !Array.isArray(nextConfig.comparison_windows) || !nextConfig.comparison_windows.length) { - this._adjustComparisonAxisScale = false; - } - if (this._hass && dataChanged) { - this._load(); - return; - } - if (this._hass && comparisonChanged) { - this._loadComparisonWindows({ redraw: true, showLoading: true }); - return; - } - if (this._hass && preloadComparisonChanged) { - this._preloadComparisonWindows().catch(() => { - }); - } - if (this._hass && comparisonOverlayChanged && this._lastHistResult && this._lastEvents) { - this._queueDrawChart( - this._lastHistResult, - this._lastStatsResult || {}, - this._lastEvents, - this._lastT0, - this._lastT1 - ); - return; - } - if (this._hass && viewChanged && this._lastHistResult && this._lastEvents) { - this._queueDrawChart( - this._lastHistResult, - this._lastStatsResult || {}, - this._lastEvents, - this._lastT0, - this._lastT1 - ); - } - } - // ── Entity helpers ───────────────────────────────────────────────────────── - get _entityIds() { - if (Array.isArray(this._config.series_settings)) { - const ids = this._config.series_settings.map( - (entry) => entry?.entity_id || entry?.entity || entry?.entityId || null - ).filter( - (value) => typeof value === "string" && !!value - ); - if (ids.length) { - return [...new Set(ids)]; - } - } - if (this._config.target) { - return resolveEntityIdsFromTarget(this._hass, this._config.target); - } - if (this._config.entities) { - return this._config.entities.map((e2) => typeof e2 === "string" ? e2 : e2.entity_id ?? e2.entity); - } - return [this._config.entity]; - } - get _statisticsEntityIds() { - return this._entityIds.filter( - (entityId) => !String(entityId).startsWith("binary_sensor.") - ); - } - // ── Time range ───────────────────────────────────────────────────────────── - _getRange() { - const end = this._config.end_time ? new Date(this._config.end_time) : /* @__PURE__ */ new Date(); - const start = this._config.start_time ? new Date(this._config.start_time) : new Date( - end.getTime() - this._config.hours_to_show * 3600 * 1e3 - ); - return { start, end }; - } - // ── Comparison windows ───────────────────────────────────────────────────── - _pruneComparisonDataCache(config) { - if (!this._comparisonDataCache.size) { - return; - } - const end = config.end_time ? new Date(config.end_time) : /* @__PURE__ */ new Date(); - const start = config.start_time ? new Date(config.start_time) : new Date( - end.getTime() - Number(config.hours_to_show || 24) * 3600 * 1e3 - ); - const windows = [ - ...Array.isArray(config.comparison_windows) ? config.comparison_windows : [], - ...Array.isArray(config.preload_comparison_windows) ? config.preload_comparison_windows : [] - ]; - const keepKeys = /* @__PURE__ */ new Set(); - for (const win of windows) { - if (win?.time_offset_ms == null) { - continue; - } - const winStart = new Date(start.getTime() + win.time_offset_ms); - const winEnd = new Date(end.getTime() + win.time_offset_ms); - keepKeys.add(this._getComparisonCacheKey(win, winStart, winEnd)); - } - for (const cacheKey of this._comparisonDataCache.keys()) { - if (!keepKeys.has(cacheKey)) { - this._comparisonDataCache.delete(cacheKey); - } - } - } - get _comparisonWindows() { - return Array.isArray(this._config?.comparison_windows) ? this._config.comparison_windows.filter( - (w) => w?.time_offset_ms != null - ) : []; - } - get _preloadComparisonWindowsConfig() { - return Array.isArray(this._config?.preload_comparison_windows) ? this._config.preload_comparison_windows.filter( - (w) => w?.time_offset_ms != null - ) : []; - } - _getComparisonCacheKey(win, start, end) { - return JSON.stringify({ - id: win?.id || "", - start: start?.toISOString?.() || "", - end: end?.toISOString?.() || "", - entities: this._entityIds, - statistics_entities: this._statisticsEntityIds - }); - } - _getResolvedComparisonResults() { - const { start, end } = this._getRange(); - const seenWindowIds = /* @__PURE__ */ new Set(); - const resolvedResults = []; - const comparisonWindows = [ - ...this._comparisonWindows, - ...this._preloadComparisonWindowsConfig - ]; - for (const win of comparisonWindows) { - const id = String(win?.id || ""); - if (!id || seenWindowIds.has(id)) { - continue; - } - seenWindowIds.add(id); - const winStart = new Date(start.getTime() + win.time_offset_ms); - const winEnd = new Date(end.getTime() + win.time_offset_ms); - const cacheKey = this._getComparisonCacheKey(win, winStart, winEnd); - const cached = this._comparisonDataCache.get(cacheKey); - if (!cached) { - continue; - } - resolvedResults.push(cached); - } - return resolvedResults; - } - async _loadComparisonWindowData(win, start, end) { - const hass = this._hass; - if (!hass) { - return { - id: win.id, - time_offset_ms: win.time_offset_ms, - histResult: {}, - statsResult: {} - }; - } - const cacheKey = this._getComparisonCacheKey(win, start, end); - const cached = this._comparisonDataCache.get(cacheKey); - if (cached) { - return cached; - } - const historyPromise = fetchHistoryDuringPeriod( - hass, - start.toISOString(), - end.toISOString(), - this._entityIds, - { - include_start_time_state: true, - significant_changes_only: false, - no_attributes: true - } - ).catch(() => ({})); - const statisticsPromise = this._statisticsEntityIds.length ? fetchStatisticsDuringPeriod( - hass, - start.toISOString(), - end.toISOString(), - this._statisticsEntityIds, - { - period: "hour", - types: ["mean"], - units: {} - } - ).catch(() => ({})) : Promise.resolve({}); - const [histResult, statsResult] = await Promise.all([ - historyPromise, - statisticsPromise - ]); - const result = { - ...win, - histResult: histResult || {}, - statsResult: statsResult || {} - }; - this._comparisonDataCache.set(cacheKey, result); - return result; - } - _preloadComparisonWindows() { - const { start, end } = this._getRange(); - const comparisonWindows = this._preloadComparisonWindowsConfig; - if (!comparisonWindows.length) { - return Promise.resolve([]); - } - return Promise.all( - comparisonWindows.map(async (win) => { - const winStart = new Date(start.getTime() + win.time_offset_ms); - const winEnd = new Date(end.getTime() + win.time_offset_ms); - const result = await this._loadComparisonWindowData( - win, - winStart, - winEnd - ); - return { - ...win, - id: result.id, - histResult: result.histResult, - statsResult: result.statsResult - }; - }) - ).then((results) => { - this._lastComparisonResults = this._getResolvedComparisonResults(); - if (this._config?.hovered_comparison_window_id && this._lastHistResult && this._lastEvents) { - this._queueDrawChart( - this._lastHistResult, - this._lastStatsResult || {}, - this._lastEvents, - this._lastT0, - this._lastT1 - ); - } - return results; - }).catch((error) => { - logger$1.warn( - "[hass-datapoints history-card] comparison preload:failed", - { - message: error?.message || String(error) - } - ); - return []; - }); - } - _loadComparisonWindows({ - redraw = false, - requestId: requestId2 = null, - showLoading = false - } = {}) { - const { start, end } = this._getRange(); - const comparisonWindows = this._comparisonWindows; - const targetRequestId = requestId2 ?? this._loadRequestId; - const comparisonRequestId = ++this._comparisonRequestId; - if (!comparisonWindows.length) { - this._lastComparisonResults = []; - this.dispatchEvent( - new CustomEvent("hass-datapoints-comparison-loading", { - bubbles: true, - composed: true, - detail: { ids: [], loading: false } - }) - ); - if (redraw && this._lastHistResult && this._lastEvents) { - this._queueDrawChart( - this._lastHistResult, - this._lastStatsResult || {}, - this._lastEvents, - this._lastT0, - this._lastT1 - ); - } - return Promise.resolve([]); - } - const cachedResults = []; - const windowsToFetch = []; - for (const win of comparisonWindows) { - const winStart = new Date(start.getTime() + win.time_offset_ms); - const winEnd = new Date(end.getTime() + win.time_offset_ms); - const cacheKey = this._getComparisonCacheKey(win, winStart, winEnd); - const cached = this._comparisonDataCache.get(cacheKey); - if (cached) { - cachedResults.push(cached); - } else { - windowsToFetch.push({ win, winStart, winEnd }); - } - } - if (!windowsToFetch.length) { - this._lastComparisonResults = this._getResolvedComparisonResults(); - if (redraw && this._lastHistResult && this._lastEvents) { - this._queueDrawChart( - this._lastHistResult, - this._lastStatsResult || {}, - this._lastEvents, - this._lastT0, - this._lastT1 - ); - } - return Promise.resolve(this._lastComparisonResults); - } - this._lastComparisonResults = cachedResults.length ? this._getResolvedComparisonResults() : null; - if (showLoading) { - this._setChartLoading(true); - } - this.dispatchEvent( - new CustomEvent("hass-datapoints-comparison-loading", { - bubbles: true, - composed: true, - detail: { - ids: windowsToFetch.map(({ win }) => win.id).filter(Boolean), - loading: true - } - }) - ); - return Promise.all( - windowsToFetch.map( - async ({ win, winStart, winEnd }) => this._loadComparisonWindowData(win, winStart, winEnd) - ) - ).then(() => { - if (comparisonRequestId !== this._comparisonRequestId) { - return this._lastComparisonResults || []; - } - if (targetRequestId != null && targetRequestId !== this._loadRequestId) { - return this._lastComparisonResults || []; - } - this._lastComparisonResults = this._getResolvedComparisonResults(); - this.dispatchEvent( - new CustomEvent("hass-datapoints-comparison-loading", { - bubbles: true, - composed: true, - detail: { - ids: windowsToFetch.map(({ win }) => win.id).filter(Boolean), - loading: false - } - }) - ); - if (redraw && this._lastHistResult && this._lastEvents) { - this._queueDrawChart( - this._lastHistResult, - this._lastStatsResult || {}, - this._lastEvents, - this._lastT0, - this._lastT1 - ); - } else if (showLoading) { - this._setChartLoading(false); - } - return this._lastComparisonResults; - }).catch(() => { - if (comparisonRequestId === this._comparisonRequestId) { - this._lastComparisonResults = []; - logger$1.warn("[hass-datapoints history-card] comparison load:failed", { - comparisonRequestId, - ids: comparisonWindows.map((win) => win.id).filter(Boolean) - }); - this.dispatchEvent( - new CustomEvent("hass-datapoints-comparison-loading", { - bubbles: true, - composed: true, - detail: { - ids: windowsToFetch.map(({ win }) => win.id).filter(Boolean), - loading: false - } - }) - ); - if (redraw && this._lastHistResult && this._lastEvents) { - this._queueDrawChart( - this._lastHistResult, - this._lastStatsResult || {}, - this._lastEvents, - this._lastT0, - this._lastT1 - ); - } else if (showLoading) { - this._setChartLoading(false); - } - } else if (showLoading) { - this._setChartLoading(false); - } - return []; - }); - } - // ── Data loading ─────────────────────────────────────────────────────────── - async _load() { - const { start, end } = this._getRange(); - const t0 = start.getTime(); - const t1 = end.getTime(); - const requestId2 = ++this._loadRequestId; - this._setChartLoading(true); - logger$1.log("[hass-datapoints history-card] load triggered", { - requestId: requestId2, - entityIds: this._entityIds, - start: start.toISOString(), - end: end.toISOString() - }); - const partial = { - histResult: null, - statsResult: this._statisticsEntityIds.length ? null : {}, - events: null, - histDone: false, - statsDone: !this._statisticsEntityIds.length, - eventsDone: false, - histFailed: false, - statsFailed: false, - eventsFailed: false, - hasDrawnDrawable: false, - lastDrawState: null, - lastDrawQuality: null - }; - const logChartRedrawData = (reason, histResult, statsResult, events) => { - logger$1.log("[hass-datapoints history] redraw data update", { - reason, - requestId: requestId2, - entityIds: this._entityIds, - start: start.toISOString(), - end: end.toISOString(), - histResult, - statsResult, - events - }); - }; - const maybeDraw = () => { - if (requestId2 !== this._loadRequestId) { - return; - } - const hasDrawableData = this._hasDrawableHistoryData( - partial.histResult || {}, - partial.statsResult || {} - ); - const numericRequestsFinished = partial.histDone && partial.statsDone; - if (!hasDrawableData && !numericRequestsFinished) { - return; - } - if (partial.hasDrawnDrawable) { - const drawQuality = hasDrawableData ? this._getDrawableHistoryQuality( - partial.histResult || {}, - partial.statsResult || {} - ) : null; - const redrawForHistory = hasDrawableData && !partial.lastDrawState?.histDone && partial.histDone; - const redrawForStats = hasDrawableData && !partial.lastDrawState?.statsDone && partial.statsDone; - const redrawForEvents = hasDrawableData && !partial.lastDrawState?.eventsDone && partial.eventsDone; - const shouldRedraw = redrawForHistory || redrawForStats || redrawForEvents; - const wouldDowngradeDraw = !!drawQuality && !!partial.lastDrawQuality && drawQuality.totalPoints < partial.lastDrawQuality.totalPoints; - if (!shouldRedraw) { - if (partial.histDone && partial.statsDone && partial.eventsDone) { - this._setChartLoading(false); - } - return; - } - if (wouldDowngradeDraw) { - if (partial.histDone && partial.statsDone && partial.eventsDone) { - this._setChartLoading(false); - } - return; - } - if (redrawForEvents && !redrawForHistory && this._lastHistResult && Number.isFinite(this._lastT0) && Number.isFinite(this._lastT1)) { - const filteredEvents2 = this._filterEvents(partial.events || []); - logChartRedrawData( - "events_update", - this._lastHistResult, - this._lastStatsResult || {}, - filteredEvents2 - ); - partial.lastDrawState = { - histDone: partial.histDone, - statsDone: partial.statsDone, - eventsDone: partial.eventsDone - }; - this._queueDrawChart( - this._lastHistResult, - this._lastStatsResult || {}, - partial.events || [], - this._lastT0, - this._lastT1, - { - loading: !(partial.histDone && partial.statsDone && partial.eventsDone) - } - ); - return; - } - } - if (hasDrawableData) { - const drawQuality = this._getDrawableHistoryQuality( - partial.histResult || {}, - partial.statsResult || {} - ); - partial.hasDrawnDrawable = true; - partial.lastDrawState = { - histDone: partial.histDone, - statsDone: partial.statsDone, - eventsDone: partial.eventsDone - }; - partial.lastDrawQuality = drawQuality; - } - const filteredEvents = this._filterEvents(partial.events || []); - logChartRedrawData( - partial.hasDrawnDrawable ? "data_update" : "initial_data_draw", - partial.histResult || {}, - partial.statsResult || {}, - filteredEvents - ); - this._queueDrawChart( - partial.histResult || {}, - partial.statsResult || {}, - partial.events || [], - t0, - t1, - { - loading: !(partial.histDone && partial.statsDone && partial.eventsDone) - } - ); - }; - const finalize = () => { - if (requestId2 !== this._loadRequestId) { - return; - } - if (!(partial.histDone && partial.statsDone && partial.eventsDone)) { - return; - } - if (partial.histFailed && partial.statsFailed || partial.histResult == null && partial.statsResult == null) { - this._setChartMessage("Failed to load data."); - this._setChartLoading(false); - return; - } - if (partial.hasDrawnDrawable) { - this._setChartLoading(false); - } - this._preloadComparisonWindows().catch(() => { - }); - }; - this._loadComparisonWindows({ redraw: true, requestId: requestId2 }).catch(() => { - }); - try { - const hass = this._hass; - if (!hass) { - this._setChartMessage("Failed to load data."); - this._setChartLoading(false); - return; - } - fetchHistoryDuringPeriod( - hass, - start.toISOString(), - end.toISOString(), - this._entityIds, - { - include_start_time_state: true, - significant_changes_only: false, - no_attributes: true - } - ).then((histResult) => { - partial.histResult = histResult || {}; - partial.histDone = true; - maybeDraw(); - finalize(); - }).catch((err) => { - partial.histDone = true; - partial.histFailed = true; - logger$1.error( - "[hass-datapoints history-card] history load failed", - err - ); - maybeDraw(); - finalize(); - }); - if (this._statisticsEntityIds.length) { - fetchStatisticsDuringPeriod( - hass, - start.toISOString(), - end.toISOString(), - this._statisticsEntityIds, - { - period: "hour", - types: ["mean"], - units: {} - } - ).then((statsResult) => { - partial.statsResult = statsResult || {}; - partial.statsDone = true; - maybeDraw(); - finalize(); - }).catch((err) => { - partial.statsDone = true; - partial.statsFailed = true; - logger$1.error( - "[hass-datapoints history-card] statistics load failed", - err - ); - maybeDraw(); - finalize(); - }); - } - if (this._config.datapoint_scope === "hidden") { - partial.events = []; - partial.eventsDone = true; - maybeDraw(); - finalize(); - } else { - fetchEvents( - hass, - start.toISOString(), - end.toISOString(), - this._config.datapoint_scope === "all" ? void 0 : this._entityIds - ).then((events) => { - partial.events = events || []; - partial.eventsDone = true; - maybeDraw(); - finalize(); - }).catch((err) => { - partial.eventsDone = true; - partial.eventsFailed = true; - logger$1.error( - "[hass-datapoints history-card] event load failed", - err - ); - maybeDraw(); - finalize(); - }); - } - } catch (err) { - this._setChartMessage("Failed to load data."); - this._setChartLoading(false); - logger$1.error("[hass-datapoints history-card]", err); - } - } - // ── Drawing ──────────────────────────────────────────────────────────────── - /** Delegates to the hass-datapoints-history-chart sub-component for resize-replay. */ - _drawChart(...args) { - const chartEl = this._chartEl(); - if (chartEl) { - chartEl.hass = this._hass; - chartEl._config = this._config; - chartEl._hiddenSeries = this._hiddenSeries; - chartEl._hiddenEventIds = this._hiddenEventIds; - chartEl._zoomRange = this._zoomRange; - chartEl._lastComparisonResults = this._lastComparisonResults; - chartEl._drawChart(...args); - } - } - /** Returns the hass-datapoints-history-chart element once it is in the shadow DOM. */ - _chartEl() { - return this.shadowRoot?.querySelector( - "hass-datapoints-history-chart" - ) ?? null; - } - /** - * Queue a draw cycle. Delegates to hass-datapoints-history-chart and also records last - * draw args so the base-class ResizeObserver can replay via _drawChart(). - */ - _queueDrawChart(histResult, statsResult, events, t0, t1, options = {}) { - const drawRequestId = ++this._drawRequestId; - const filteredEvents = this._filterEvents(events); - this._lastHistResult = histResult; - this._lastStatsResult = statsResult; - this._lastEvents = events; - this._lastT0 = t0; - this._lastT1 = t1; - this._lastDrawArgs = [ - histResult, - statsResult, - events, - t0, - t1, - { ...options, drawRequestId } - ]; - const chartEl = this._chartEl(); - if (chartEl) { - chartEl.hass = this._hass; - chartEl._config = this._config; - chartEl._hiddenSeries = this._hiddenSeries; - chartEl._hiddenEventIds = this._hiddenEventIds; - chartEl._zoomRange = this._zoomRange; - chartEl._lastComparisonResults = this._lastComparisonResults; - chartEl._queueDrawChart( - histResult, - statsResult, - filteredEvents, - t0, - t1, - { ...options, drawRequestId } - ); - } - } - // ── Loading/message UI helpers ───────────────────────────────────────────── - _setChartLoading(isLoading) { - const chartEl = this._chartEl(); - if (chartEl?._setChartLoading) { - chartEl._setChartLoading(isLoading); - } - } - _setChartMessage(message = "") { - const chartEl = this._chartEl(); - if (chartEl?._setChartMessage) { - chartEl._setChartMessage(message); - } - } - // ── History data quality helpers ─────────────────────────────────────────── - /** - * Returns true if there is at least one entity with drawable data points - * in either the history or statistics result. - */ - _hasDrawableHistoryData(histResult, statsResult) { - return this._entityIds.some((entityId) => { - const histData = histResult[entityId]; - const statsData = statsResult[entityId]; - const histPoints = Array.isArray(histData) ? histData.length : 0; - const statsPoints = Array.isArray(statsData) ? statsData.length : 0; - return histPoints > 0 || statsPoints > 0; - }); - } - /** - * Returns a quality descriptor for the current drawable data — used to - * avoid downgrading a high-resolution draw with a lower-resolution one. - */ - _getDrawableHistoryQuality(histResult, statsResult) { - let totalPoints = 0; - for (const entityId of this._entityIds) { - const histData = histResult[entityId]; - const statsData = statsResult[entityId]; - totalPoints += Array.isArray(histData) ? histData.length : 0; - totalPoints += Array.isArray(statsData) ? statsData.length : 0; - } - return { totalPoints }; - } - // ── Event filtering ──────────────────────────────────────────────────────── - _filterEvents(events) { - const query = String(this._config?.message_filter || "").trim().toLowerCase(); - const visibleEvents = events.filter( - (event) => !this._hiddenEventIds.has(event?.id ?? "") - ); - if (!query) { - return visibleEvents; - } - return visibleEvents.filter((event) => { - const ev = event; - const haystack = [ - ev?.message || "", - ev?.annotation || "", - ...(ev?.entity_ids || []).filter(Boolean) - ].join("\n").toLowerCase(); - return haystack.includes(query); - }); - } - _buildNavigationPageState() { - const range = this._getRange(); - const fallbackZoomRange = typeof this._config.zoom_start_time !== "undefined" && typeof this._config.zoom_end_time !== "undefined" ? { - start: this._config.zoom_start_time, - end: this._config.zoom_end_time - } : null; - const targetSelection = this._config.target && typeof this._config.target === "object" ? normalizeTargetValue(this._config.target) : { entity_id: this._entityIds }; - const baseState = buildHistoryPageSessionState({ - _entities: this._entityIds, - _seriesRows: Array.isArray(this._config.series_settings) ? [...this._config.series_settings] : this._entityIds.map((entityId) => ({ entity_id: entityId })), - _targetSelection: targetSelection, - _targetSelectionRaw: targetSelection, - _datapointScope: this._config.datapoint_scope || "linked", - _showChartDatapointIcons: this._config.show_event_markers !== false, - _showChartDatapointLines: this._config.show_event_lines !== false, - _showChartTooltips: this._config.show_tooltips !== false, - _showChartEmphasizedHoverGuides: this._config.emphasize_hover_guides === true, - _chartHoverSnapMode: this._config.hover_snap_mode || "follow_series", - _delinkChartYAxis: this._config.delink_y_axis === true, - _splitChartView: this._config.split_view === true, - _showCorrelatedAnomalies: this._config.show_correlated_anomalies === true, - _chartAnomalyOverlapMode: "all", - _showDataGaps: this._config.show_data_gaps !== false, - _dataGapThreshold: this._config.data_gap_threshold || "2h", - _contentSplitRatio: 0.62, - _startTime: range.start, - _endTime: range.end, - _chartZoomCommittedRange: this._zoomRange ? { - start: this._zoomRange.start, - end: this._zoomRange.end - } : fallbackZoomRange, - _comparisonWindows: Array.isArray(this._config.comparison_windows) ? [...this._config.comparison_windows] : [], - _hours: Number(this._config.hours_to_show || 24), - _sidebarCollapsed: false, - _sidebarAccordionTargetsOpen: true, - _sidebarAccordionDatapointsOpen: true, - _sidebarAccordionAnalysisOpen: true, - _sidebarAccordionChartOpen: true - }); - return { - ...baseState, - show_chart_trend_lines: this._config.show_trend_lines === true, - show_chart_summary_stats: this._config.show_summary_stats === true, - show_chart_rate_of_change: this._config.show_rate_of_change === true, - show_chart_threshold_analysis: this._config.show_threshold_analysis === true, - show_chart_threshold_shading: this._config.show_threshold_shading === true, - show_chart_anomalies: this._config.show_anomalies === true, - show_chart_trend_crosshairs: this._config.show_trend_crosshairs === true, - chart_trend_method: this._config.trend_method || "rolling_average", - chart_trend_window: this._config.trend_window || "24h", - chart_rate_window: this._config.rate_window || "1h", - chart_anomaly_sensitivity: this._config.anomaly_sensitivity || "medium", - chart_threshold_values: this._config.threshold_values || {}, - chart_threshold_directions: this._config.threshold_directions || {}, - show_chart_delta_analysis: this._config.show_delta_analysis === true, - show_chart_delta_tooltip: this._config.show_delta_tooltip !== false, - show_chart_delta_lines: this._config.show_delta_lines === true, - hide_chart_source_series: this._config.hide_raw_data === true, - chart_anomaly_rate_window: this._config.rate_window || "1h" - }; - } - _navigateToPanel(event) { - event.preventDefault(); - event.stopPropagation(); - const range = this._getRange(); - const zoomStartTime = this._zoomRange?.start ? this._zoomRange.start : this._config.zoom_start_time; - const zoomEndTime = this._zoomRange?.end ? this._zoomRange.end : this._config.zoom_end_time; - navigateToDataPointsHistory( - this, - this._config.target && typeof this._config.target === "object" ? normalizeTargetValue(this._config.target) : { entity_id: this._entityIds }, - { - datapoint_scope: this._config.datapoint_scope || void 0, - start_time: Number.isFinite(this._lastT0) ? this._lastT0 : range.start, - end_time: Number.isFinite(this._lastT1) ? this._lastT1 : range.end, - zoom_start_time: zoomStartTime, - zoom_end_time: zoomEndTime, - page_state: this._buildNavigationPageState() - } - ); - } - // ── Keyboard / scroll handlers ───────────────────────────────────────────── - _handleWindowKeyDown(ev) { - if (ev.key !== "Escape") { - return; - } - if (this._annotationDialog?.isOpen?.()) { - ev.preventDefault(); - return; - } - if (!this._zoomRange) { - return; - } - ev.preventDefault(); - this._zoomRange = null; - this._dispatchZoomRange("reset"); - } - _handleChartScroll() { - if (this._scrollSyncSuspended || !this._zoomRange) { - return; - } - if (this._ignoreNextProgrammaticScrollEvent) { - this._ignoreNextProgrammaticScrollEvent = false; - this._lastProgrammaticScrollLeft = null; - return; - } - if (this._lastProgrammaticScrollLeft != null && Math.abs( - (this._chartScrollViewportEl?.scrollLeft || 0) - this._lastProgrammaticScrollLeft - ) < 1) { - this._lastProgrammaticScrollLeft = null; - return; - } - this._lastProgrammaticScrollLeft = null; - } - _dispatchZoomRange(source) { - this.dispatchEvent( - new CustomEvent("hass-datapoints-chart-zoom", { - bubbles: true, - composed: true, - detail: this._zoomRange ? { - startTime: this._zoomRange.start, - endTime: this._zoomRange.end, - preview: false, - source - } : { - startTime: null, - endTime: null, - preview: false, - source - } - }) - ); - } - _scheduleZoomReload() { - if (this._zoomReloadTimer != null) { - window.clearTimeout(this._zoomReloadTimer); - } - this._zoomReloadTimer = window.setTimeout(() => { - this._zoomReloadTimer = null; - this._load(); - }, 140); - } - // ── Render ───────────────────────────────────────────────────────────────── - render() { - return b` + if (this._dialogEl) { + this._dialogEl.hass = this._host._hass; + this._dialogEl.dialogInitialFocus = "#chart-context-message"; + } + this.bindFields(hover); + this._dialogEl.open = true; + this._host._creatingContextAnnotation = true; + window.requestAnimationFrame(() => { + (this._panelEl?.querySelector("#chart-context-message"))?.focus?.(); + }); + } + close() { + this._host._creatingContextAnnotation = false; + if (this._dialogEl) this._dialogEl.open = false; + window.setTimeout(() => this.finalizeClose(), 0); + } + }; + //#endregion + //#region custom_components/hass_datapoints/src/lib/data/statistics-api.ts + async function fetchStatisticsDuringPeriod(hass, startTime, endTime, statisticIds, options = {}) { + const normalizedStatisticIds = normalizeCacheIdList(statisticIds); + const normalizedTypes = normalizeCacheIdList(options.types); + return withStableRangeCache(JSON.stringify({ + type: "recorder/statistics_during_period", + start_time: startTime, + end_time: endTime, + statistic_ids: normalizedStatisticIds, + period: options.period || "hour", + types: normalizedTypes + }), endTime, () => hass.connection.sendMessagePromise({ + type: "recorder/statistics_during_period", + start_time: startTime, + end_time: endTime, + statistic_ids: normalizedStatisticIds, + period: options.period || "hour", + types: normalizedTypes, + units: options.units || {} + })); + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/history-page/history-session-state.ts + /** + * Session and preference ownership helpers for the history page. + */ + var PANEL_HISTORY_PREFERENCES_KEY = `${DOMAIN}:panel_history_preferences`; + var PANEL_HISTORY_SESSION_KEY = `${DOMAIN}:panel_history_session`; + function readHistoryPageSessionState() { + try { + const raw = window.sessionStorage?.getItem(PANEL_HISTORY_SESSION_KEY); + if (!raw) return null; + const parsed = JSON.parse(raw); + return parsed && typeof parsed === "object" ? parsed : null; + } catch { + return null; + } + } + function buildHistoryPageSessionState(source) { + return { + entities: source._entities, + series_rows: source._seriesRows, + target_selection: source._targetSelection, + target_selection_raw: source._targetSelectionRaw, + datapoint_scope: source._datapointScope, + show_chart_datapoint_icons: source._showChartDatapointIcons, + show_chart_datapoint_lines: source._showChartDatapointLines, + show_chart_tooltips: source._showChartTooltips, + show_chart_emphasized_hover_guides: source._showChartEmphasizedHoverGuides, + chart_hover_snap_mode: source._chartHoverSnapMode, + delink_chart_y_axis: source._delinkChartYAxis, + split_chart_view: source._splitChartView, + show_chart_trend_lines: false, + show_chart_summary_stats: false, + show_chart_rate_of_change: false, + show_chart_threshold_analysis: false, + show_chart_threshold_shading: false, + show_chart_anomalies: false, + chart_anomaly_method: "trend_residual", + chart_anomaly_rate_window: "1h", + chart_anomaly_zscore_window: "24h", + chart_anomaly_persistence_window: "1h", + chart_anomaly_comparison_window_id: null, + hide_chart_source_series: false, + show_chart_trend_crosshairs: false, + chart_trend_method: "rolling_average", + chart_trend_window: "24h", + chart_rate_window: "1h", + chart_anomaly_sensitivity: "medium", + chart_threshold_values: {}, + chart_threshold_directions: {}, + show_chart_delta_analysis: false, + show_chart_delta_tooltip: true, + show_chart_delta_lines: false, + show_chart_correlated_anomalies: source._showCorrelatedAnomalies, + chart_anomaly_overlap_mode: source._chartAnomalyOverlapMode || "all", + show_data_gaps: source._showDataGaps, + data_gap_threshold: source._dataGapThreshold, + content_split_ratio: source._contentSplitRatio, + start_time: source._startTime?.toISOString() || null, + end_time: source._endTime?.toISOString() || null, + zoom_start_time: source._chartZoomCommittedRange ? new Date(source._chartZoomCommittedRange.start).toISOString() : null, + zoom_end_time: source._chartZoomCommittedRange ? new Date(source._chartZoomCommittedRange.end).toISOString() : null, + date_windows: normalizeDateWindows(source._comparisonWindows), + hours: source._hours, + sidebar_collapsed: source._sidebarCollapsed, + sidebar_accordion_targets_open: source._sidebarAccordionTargetsOpen !== false, + sidebar_accordion_datapoints_open: source._sidebarAccordionDatapointsOpen !== false, + sidebar_accordion_analysis_open: source._sidebarAccordionAnalysisOpen !== false, + sidebar_accordion_chart_open: source._sidebarAccordionChartOpen !== false + }; + } + function writeHistoryPageSessionState(source) { + try { + window.sessionStorage?.setItem(PANEL_HISTORY_SESSION_KEY, JSON.stringify(buildHistoryPageSessionState(source))); + } catch {} + } + function normalizeHistoryPagePreferences(preferences, options = {}) { + const zoomValues = new Set((options.zoomOptions || []).map((option) => option.value)); + const snapValues = new Set((options.snapOptions || []).map((option) => option.value)); + let shouldPersistDefaults = false; + const normalized = { + zoomLevel: "auto", + dateSnapping: "hour", + preferredSeriesColors: {}, + comparisonWindows: [], + pageState: null, + shouldPersistDefaults + }; + if (preferences && typeof preferences === "object") { + if (preferences.zoom_level && zoomValues.has(preferences.zoom_level)) normalized.zoomLevel = preferences.zoom_level; + else shouldPersistDefaults = true; + if (preferences.date_snapping && snapValues.has(preferences.date_snapping)) normalized.dateSnapping = preferences.date_snapping; + else shouldPersistDefaults = true; + normalized.preferredSeriesColors = preferences.series_colors && typeof preferences.series_colors === "object" ? Object.entries(preferences.series_colors).reduce((acc, [entityId, color]) => { + if (typeof entityId === "string" && /^#[0-9a-f]{6}$/i.test(color || "")) acc[entityId] = color; + return acc; + }, {}) : {}; + normalized.comparisonWindows = normalizeDateWindows(preferences.date_windows); + normalized.pageState = preferences.page_state && typeof preferences.page_state === "object" ? { + ...preferences.page_state, + date_windows: normalizeDateWindows(preferences.page_state?.date_windows) + } : null; + } else shouldPersistDefaults = true; + normalized.shouldPersistDefaults = shouldPersistDefaults; + return normalized; + } + function buildHistoryPagePreferencesPayload(source) { + const preferredSeriesColors = source._seriesRows.reduce((acc, row) => { + const entityId = typeof row === "object" && row && "entity_id" in row ? row.entity_id : null; + const color = typeof row === "object" && row && "color" in row ? row.color : null; + if (typeof entityId === "string" && /^#[0-9a-f]{6}$/i.test(String(color || ""))) acc[entityId] = String(color); + return acc; + }, { ...source._preferredSeriesColors }); + return { + zoom_level: source._zoomLevel, + date_snapping: source._dateSnapping, + series_colors: preferredSeriesColors, + date_windows: normalizeDateWindows(source._comparisonWindows), + page_state: buildHistoryPageSessionState(source) + }; + } + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/history.ts + var HassRecordsHistoryCard = class extends ChartCardBase { + constructor() { + super(); + _defineProperty(this, "_hiddenSeries", /* @__PURE__ */ new Set()); + _defineProperty(this, "_hiddenEventIds", /* @__PURE__ */ new Set()); + _defineProperty(this, "_zoomRange", null); + _defineProperty(this, "_configKey", void 0); + _defineProperty(this, "_comparisonRequestId", 0); + _defineProperty(this, "_comparisonDataCache", /* @__PURE__ */ new Map()); + _defineProperty(this, "_lastComparisonResults", null); + _defineProperty(this, "_lastHistResult", null); + _defineProperty(this, "_lastStatsResult", null); + _defineProperty(this, "_lastEvents", null); + _defineProperty(this, "_lastT0", 0); + _defineProperty(this, "_lastT1", 0); + _defineProperty(this, "_scrollSyncSuspended", false); + _defineProperty(this, "_lastProgrammaticScrollLeft", null); + _defineProperty(this, "_ignoreNextProgrammaticScrollEvent", false); + _defineProperty(this, "_adjustComparisonAxisScale", false); + _defineProperty(this, "_drawRequestId", 0); + _defineProperty(this, "_zoomReloadTimer", null); + _defineProperty(this, "_chartScrollViewportEl", null); + _defineProperty(this, "_annotationDialog", void 0); + _defineProperty(this, "_onWindowKeyDown", void 0); + _defineProperty(this, "_onChartScroll", void 0); + _defineProperty(this, "_onZoomApply", void 0); + this._annotationDialog = new HistoryAnnotationDialogController(this); + this._onWindowKeyDown = (ev) => this._handleWindowKeyDown(ev); + this._onChartScroll = () => this._handleChartScroll(); + this._onZoomApply = (ev) => { + const detail = ev.detail; + this._zoomRange = detail ? { + start: detail.start, + end: detail.end + } : null; + this._dispatchZoomRange(detail ? "zoom" : "reset"); + if (this._lastHistResult && this._lastEvents) this._queueDrawChart(this._lastHistResult, this._lastStatsResult || {}, this._lastEvents, this._lastT0, this._lastT1); + this._scheduleZoomReload(); + }; + } + /** + * Override the base setter to prevent a full history refetch on every HA + * state update. History data is fetched from the history/statistics APIs and + * does not change when entity states tick; reloads are already triggered by: + * - config changes (setConfig) + * - new datapoints recorded (hass_datapoints_event_recorded subscription) + * - auto-refresh interval (base class) + * + * We still call requestUpdate() so that entity-friendly-names in the legend + * stay fresh, and we propagate the new hass reference down to hass-datapoints-history-chart. + */ + set hass(hass) { + this._hass = hass; + this.requestUpdate(); + const chartEl = this._chartEl(); + if (chartEl) chartEl.hass = hass; + } + get hass() { + return this._hass; + } + connectedCallback() { + window.addEventListener("keydown", this._onWindowKeyDown); + this.addEventListener("hass-datapoints-zoom-apply", this._onZoomApply); + super.connectedCallback(); + } + disconnectedCallback() { + window.removeEventListener("keydown", this._onWindowKeyDown); + this.removeEventListener("hass-datapoints-zoom-apply", this._onZoomApply); + if (this._zoomReloadTimer != null) { + window.clearTimeout(this._zoomReloadTimer); + this._zoomReloadTimer = null; + } + if (this._chartScrollViewportEl) this._chartScrollViewportEl.removeEventListener("scroll", this._onChartScroll); + this._annotationDialog?.teardown(); + super.disconnectedCallback(); + } + setConfig(config) { + if (!config.entity && !config.entities && !config.target && !(Array.isArray(config.series_settings) && config.series_settings.length > 0)) throw new Error("hass-datapoints-history-card: define `target`, `entity`, `entities` or `series_settings`"); + const nextConfig = { + hours_to_show: 24, + ...config, + target: config.target ? normalizeTargetValue(config.target) : void 0, + series_settings: Array.isArray(config.series_settings) ? config.series_settings.map((entry) => ({ + ...entry, + analysis: entry?.analysis && typeof entry.analysis === "object" ? { ...entry.analysis } : entry?.analysis + })) : config.series_settings, + hidden_event_ids: Array.isArray(config.hidden_event_ids) ? [...config.hidden_event_ids] : config.hidden_event_ids, + hovered_event_ids: Array.isArray(config.hovered_event_ids) ? [...config.hovered_event_ids] : config.hovered_event_ids, + comparison_windows: Array.isArray(config.comparison_windows) ? config.comparison_windows.map((entry) => ({ ...entry })) : config.comparison_windows, + preload_comparison_windows: Array.isArray(config.preload_comparison_windows) ? config.preload_comparison_windows.map((entry) => ({ ...entry })) : config.preload_comparison_windows, + comparison_preview_overlay: config.comparison_preview_overlay ? { ...config.comparison_preview_overlay } : null, + selected_comparison_window_id: config.selected_comparison_window_id || null, + hovered_comparison_window_id: config.hovered_comparison_window_id || null, + show_trend_lines: config.show_trend_lines === true, + show_summary_stats: config.show_summary_stats === true, + show_rate_of_change: config.show_rate_of_change === true, + show_threshold_analysis: config.show_threshold_analysis === true, + show_threshold_shading: config.show_threshold_shading === true, + hide_raw_data: config.hide_raw_data === true, + show_trend_crosshairs: config.show_trend_crosshairs === true, + trend_method: config.trend_method || "rolling_average", + trend_window: config.trend_window || "24h", + rate_window: config.rate_window || "1h", + threshold_values: config.threshold_values && typeof config.threshold_values === "object" ? { ...config.threshold_values } : {}, + threshold_directions: config.threshold_directions && typeof config.threshold_directions === "object" ? { ...config.threshold_directions } : {}, + show_delta_analysis: config.show_delta_analysis === true, + show_delta_tooltip: config.show_delta_tooltip !== false, + show_delta_lines: config.show_delta_lines === true, + hide_delta_source_series: config.hide_delta_source_series === true, + delink_y_axis: config.delink_y_axis === true, + split_view: config.split_view === true, + show_data_gaps: config.show_data_gaps !== false, + data_gap_threshold: config.data_gap_threshold || "2h" + }; + const currentConfig = this._config || {}; + const currentDataKey = JSON.stringify({ + target: currentConfig.target || null, + entities: currentConfig.entities, + entity: currentConfig.entity, + series_entities: Array.isArray(currentConfig.series_settings) ? currentConfig.series_settings.map((entry) => entry?.entity_id || entry?.entity || entry?.entityId || null) : null, + datapoint_scope: currentConfig.datapoint_scope, + hours_to_show: currentConfig.hours_to_show, + start_time: currentConfig.start_time, + end_time: currentConfig.end_time + }); + const nextDataKey = JSON.stringify({ + target: nextConfig.target || null, + entities: nextConfig.entities, + entity: nextConfig.entity, + series_entities: Array.isArray(nextConfig.series_settings) ? nextConfig.series_settings.map((entry) => entry?.entity_id || entry?.entity || entry?.entityId || null) : null, + datapoint_scope: nextConfig.datapoint_scope, + hours_to_show: nextConfig.hours_to_show, + start_time: nextConfig.start_time, + end_time: nextConfig.end_time + }); + const currentViewKey = JSON.stringify({ + series_settings: currentConfig.series_settings || [], + zoom_start_time: currentConfig.zoom_start_time, + zoom_end_time: currentConfig.zoom_end_time, + message_filter: currentConfig.message_filter || "", + hidden_event_ids: currentConfig.hidden_event_ids || [], + hovered_event_ids: currentConfig.hovered_event_ids || [], + show_event_markers: currentConfig.show_event_markers !== false, + show_event_lines: currentConfig.show_event_lines !== false, + show_tooltips: currentConfig.show_tooltips !== false, + emphasize_hover_guides: currentConfig.emphasize_hover_guides === true, + hover_snap_mode: currentConfig.hover_snap_mode || "follow_series", + show_correlated_anomalies: currentConfig.show_correlated_anomalies === true, + show_trend_lines: currentConfig.show_trend_lines === true, + show_summary_stats: currentConfig.show_summary_stats === true, + show_rate_of_change: currentConfig.show_rate_of_change === true, + show_threshold_analysis: currentConfig.show_threshold_analysis === true, + show_threshold_shading: currentConfig.show_threshold_shading === true, + show_anomalies: currentConfig.show_anomalies === true, + hide_raw_data: currentConfig.hide_raw_data === true, + show_trend_crosshairs: currentConfig.show_trend_crosshairs === true, + trend_method: currentConfig.trend_method || "rolling_average", + trend_window: currentConfig.trend_window || "24h", + rate_window: currentConfig.rate_window || "1h", + anomaly_sensitivity: currentConfig.anomaly_sensitivity || "medium", + threshold_values: currentConfig.threshold_values || {}, + threshold_directions: currentConfig.threshold_directions || {}, + show_delta_analysis: currentConfig.show_delta_analysis === true, + show_delta_tooltip: currentConfig.show_delta_tooltip !== false, + show_delta_lines: currentConfig.show_delta_lines === true, + hide_delta_source_series: currentConfig.hide_delta_source_series === true, + delink_y_axis: currentConfig.delink_y_axis === true, + split_view: currentConfig.split_view === true, + show_data_gaps: currentConfig.show_data_gaps !== false, + data_gap_threshold: currentConfig.data_gap_threshold || "2h", + comparison_hover_active: currentConfig.comparison_hover_active === true, + selected_comparison_window_id: currentConfig.selected_comparison_window_id || null, + hovered_comparison_window_id: currentConfig.hovered_comparison_window_id || null + }); + const nextViewKey = JSON.stringify({ + series_settings: nextConfig.series_settings || [], + zoom_start_time: nextConfig.zoom_start_time, + zoom_end_time: nextConfig.zoom_end_time, + message_filter: nextConfig.message_filter || "", + hidden_event_ids: nextConfig.hidden_event_ids || [], + hovered_event_ids: nextConfig.hovered_event_ids || [], + show_event_markers: nextConfig.show_event_markers !== false, + show_event_lines: nextConfig.show_event_lines !== false, + show_tooltips: nextConfig.show_tooltips !== false, + emphasize_hover_guides: nextConfig.emphasize_hover_guides === true, + hover_snap_mode: nextConfig.hover_snap_mode || "follow_series", + show_correlated_anomalies: nextConfig.show_correlated_anomalies === true, + show_trend_lines: nextConfig.show_trend_lines === true, + show_summary_stats: nextConfig.show_summary_stats === true, + show_rate_of_change: nextConfig.show_rate_of_change === true, + show_threshold_analysis: nextConfig.show_threshold_analysis === true, + show_threshold_shading: nextConfig.show_threshold_shading === true, + show_anomalies: nextConfig.show_anomalies === true, + hide_raw_data: nextConfig.hide_raw_data === true, + show_trend_crosshairs: nextConfig.show_trend_crosshairs === true, + trend_method: nextConfig.trend_method || "rolling_average", + trend_window: nextConfig.trend_window || "24h", + rate_window: nextConfig.rate_window || "1h", + anomaly_sensitivity: nextConfig.anomaly_sensitivity || "medium", + threshold_values: nextConfig.threshold_values || {}, + threshold_directions: nextConfig.threshold_directions || {}, + show_delta_analysis: nextConfig.show_delta_analysis === true, + show_delta_tooltip: nextConfig.show_delta_tooltip !== false, + show_delta_lines: nextConfig.show_delta_lines === true, + hide_delta_source_series: nextConfig.hide_delta_source_series === true, + delink_y_axis: nextConfig.delink_y_axis === true, + split_view: nextConfig.split_view === true, + show_data_gaps: nextConfig.show_data_gaps !== false, + data_gap_threshold: nextConfig.data_gap_threshold || "2h", + comparison_hover_active: nextConfig.comparison_hover_active === true, + selected_comparison_window_id: nextConfig.selected_comparison_window_id || null, + hovered_comparison_window_id: nextConfig.hovered_comparison_window_id || null + }); + const currentComparisonKey = JSON.stringify(currentConfig.comparison_windows || []); + const nextComparisonKey = JSON.stringify(nextConfig.comparison_windows || []); + const currentPreloadComparisonKey = JSON.stringify(currentConfig.preload_comparison_windows || []); + const nextPreloadComparisonKey = JSON.stringify(nextConfig.preload_comparison_windows || []); + const currentComparisonOverlayKey = JSON.stringify(currentConfig.comparison_preview_overlay || null); + const nextComparisonOverlayKey = JSON.stringify(nextConfig.comparison_preview_overlay || null); + const dataChanged = currentDataKey !== nextDataKey; + const viewChanged = currentViewKey !== nextViewKey; + const comparisonChanged = currentComparisonKey !== nextComparisonKey; + const preloadComparisonChanged = currentPreloadComparisonKey !== nextPreloadComparisonKey; + const comparisonOverlayChanged = currentComparisonOverlayKey !== nextComparisonOverlayKey; + if (!dataChanged && !viewChanged && !comparisonChanged && !preloadComparisonChanged && !comparisonOverlayChanged && this._configKey) return; + this._config = nextConfig; + this._configKey = JSON.stringify(nextConfig); + this._hiddenSeries = createHiddenSeriesSet(nextConfig.series_settings); + this._hiddenEventIds = createHiddenEventIdSet(nextConfig.hidden_event_ids); + this._zoomRange = createChartZoomRange(nextConfig.zoom_start_time, nextConfig.zoom_end_time); + if (dataChanged || comparisonChanged || preloadComparisonChanged) { + this._pruneComparisonDataCache(nextConfig); + this._lastComparisonResults = null; + } + const chartEl = this._chartEl(); + if (chartEl) { + chartEl.hass = this._hass; + chartEl._config = this._config; + chartEl._hiddenSeries = this._hiddenSeries; + chartEl._hiddenEventIds = this._hiddenEventIds; + chartEl._zoomRange = this._zoomRange; + chartEl._lastComparisonResults = this._getResolvedComparisonResults(); + if (comparisonOverlayChanged && typeof chartEl._renderComparisonPreviewOverlay === "function") chartEl._renderComparisonPreviewOverlay(); + } + if (dataChanged || !Array.isArray(nextConfig.comparison_windows) || !nextConfig.comparison_windows.length) this._adjustComparisonAxisScale = false; + if (this._hass && dataChanged) { + this._load(); + return; + } + if (this._hass && comparisonChanged) { + this._loadComparisonWindows({ + redraw: true, + showLoading: true + }); + return; + } + if (this._hass && preloadComparisonChanged) this._preloadComparisonWindows().catch(() => {}); + if (this._hass && comparisonOverlayChanged && this._lastHistResult && this._lastEvents) { + this._queueDrawChart(this._lastHistResult, this._lastStatsResult || {}, this._lastEvents, this._lastT0, this._lastT1); + return; + } + if (this._hass && viewChanged && this._lastHistResult && this._lastEvents) this._queueDrawChart(this._lastHistResult, this._lastStatsResult || {}, this._lastEvents, this._lastT0, this._lastT1); + } + get _entityIds() { + if (Array.isArray(this._config.series_settings)) { + const ids = this._config.series_settings.map((entry) => entry?.entity_id || entry?.entity || entry?.entityId || null).filter((value) => typeof value === "string" && !!value); + if (ids.length) return [...new Set(ids)]; + } + if (this._config.target) return resolveEntityIdsFromTarget(this._hass, this._config.target); + if (this._config.entities) return this._config.entities.map((e) => typeof e === "string" ? e : e.entity_id ?? e.entity); + return [this._config.entity]; + } + get _statisticsEntityIds() { + return this._entityIds.filter((entityId) => !String(entityId).startsWith("binary_sensor.")); + } + _getRange() { + const end = this._config.end_time ? new Date(this._config.end_time) : /* @__PURE__ */ new Date(); + return { + start: this._config.start_time ? new Date(this._config.start_time) : /* @__PURE__ */ new Date(end.getTime() - this._config.hours_to_show * 3600 * 1e3), + end + }; + } + _pruneComparisonDataCache(config) { + if (!this._comparisonDataCache.size) return; + const end = config.end_time ? new Date(config.end_time) : /* @__PURE__ */ new Date(); + const start = config.start_time ? new Date(config.start_time) : /* @__PURE__ */ new Date(end.getTime() - Number(config.hours_to_show || 24) * 3600 * 1e3); + const windows = [...Array.isArray(config.comparison_windows) ? config.comparison_windows : [], ...Array.isArray(config.preload_comparison_windows) ? config.preload_comparison_windows : []]; + const keepKeys = /* @__PURE__ */ new Set(); + for (const win of windows) { + if (win?.time_offset_ms == null) continue; + const winStart = new Date(start.getTime() + win.time_offset_ms); + const winEnd = new Date(end.getTime() + win.time_offset_ms); + keepKeys.add(this._getComparisonCacheKey(win, winStart, winEnd)); + } + for (const cacheKey of this._comparisonDataCache.keys()) if (!keepKeys.has(cacheKey)) this._comparisonDataCache.delete(cacheKey); + } + get _comparisonWindows() { + return Array.isArray(this._config?.comparison_windows) ? this._config.comparison_windows.filter((w) => w?.time_offset_ms != null) : []; + } + get _preloadComparisonWindowsConfig() { + return Array.isArray(this._config?.preload_comparison_windows) ? this._config.preload_comparison_windows.filter((w) => w?.time_offset_ms != null) : []; + } + _getComparisonCacheKey(win, start, end) { + return JSON.stringify({ + id: win?.id || "", + start: start?.toISOString?.() || "", + end: end?.toISOString?.() || "", + entities: this._entityIds, + statistics_entities: this._statisticsEntityIds + }); + } + _getResolvedComparisonResults() { + const { start, end } = this._getRange(); + const seenWindowIds = /* @__PURE__ */ new Set(); + const resolvedResults = []; + const comparisonWindows = [...this._comparisonWindows, ...this._preloadComparisonWindowsConfig]; + for (const win of comparisonWindows) { + const id = String(win?.id || ""); + if (!id || seenWindowIds.has(id)) continue; + seenWindowIds.add(id); + const winStart = new Date(start.getTime() + win.time_offset_ms); + const winEnd = new Date(end.getTime() + win.time_offset_ms); + const cacheKey = this._getComparisonCacheKey(win, winStart, winEnd); + const cached = this._comparisonDataCache.get(cacheKey); + if (!cached) continue; + resolvedResults.push(cached); + } + return resolvedResults; + } + async _loadComparisonWindowData(win, start, end) { + const hass = this._hass; + if (!hass) return { + id: win.id, + time_offset_ms: win.time_offset_ms, + histResult: {}, + statsResult: {} + }; + const cacheKey = this._getComparisonCacheKey(win, start, end); + const cached = this._comparisonDataCache.get(cacheKey); + if (cached) return cached; + const historyPromise = fetchHistoryDuringPeriod(hass, start.toISOString(), end.toISOString(), this._entityIds, { + include_start_time_state: true, + significant_changes_only: false, + no_attributes: true + }).catch(() => ({})); + const statisticsPromise = this._statisticsEntityIds.length ? fetchStatisticsDuringPeriod(hass, start.toISOString(), end.toISOString(), this._statisticsEntityIds, { + period: "hour", + types: ["mean"], + units: {} + }).catch(() => ({})) : Promise.resolve({}); + const [histResult, statsResult] = await Promise.all([historyPromise, statisticsPromise]); + const result = { + ...win, + histResult: histResult || {}, + statsResult: statsResult || {} + }; + this._comparisonDataCache.set(cacheKey, result); + return result; + } + _preloadComparisonWindows() { + const { start, end } = this._getRange(); + const comparisonWindows = this._preloadComparisonWindowsConfig; + if (!comparisonWindows.length) return Promise.resolve([]); + return Promise.all(comparisonWindows.map(async (win) => { + const winStart = new Date(start.getTime() + win.time_offset_ms); + const winEnd = new Date(end.getTime() + win.time_offset_ms); + const result = await this._loadComparisonWindowData(win, winStart, winEnd); + return { + ...win, + id: result.id, + histResult: result.histResult, + statsResult: result.statsResult + }; + })).then((results) => { + this._lastComparisonResults = this._getResolvedComparisonResults(); + if (this._config?.hovered_comparison_window_id && this._lastHistResult && this._lastEvents) this._queueDrawChart(this._lastHistResult, this._lastStatsResult || {}, this._lastEvents, this._lastT0, this._lastT1); + return results; + }).catch((error) => { + logger$1.warn("[hass-datapoints history-card] comparison preload:failed", { message: error?.message || String(error) }); + return []; + }); + } + _loadComparisonWindows({ redraw = false, requestId = null, showLoading = false } = {}) { + const { start, end } = this._getRange(); + const comparisonWindows = this._comparisonWindows; + const targetRequestId = requestId ?? this._loadRequestId; + const comparisonRequestId = ++this._comparisonRequestId; + if (!comparisonWindows.length) { + this._lastComparisonResults = []; + this.dispatchEvent(new CustomEvent("hass-datapoints-comparison-loading", { + bubbles: true, + composed: true, + detail: { + ids: [], + loading: false + } + })); + if (redraw && this._lastHistResult && this._lastEvents) this._queueDrawChart(this._lastHistResult, this._lastStatsResult || {}, this._lastEvents, this._lastT0, this._lastT1); + return Promise.resolve([]); + } + const cachedResults = []; + const windowsToFetch = []; + for (const win of comparisonWindows) { + const winStart = new Date(start.getTime() + win.time_offset_ms); + const winEnd = new Date(end.getTime() + win.time_offset_ms); + const cacheKey = this._getComparisonCacheKey(win, winStart, winEnd); + const cached = this._comparisonDataCache.get(cacheKey); + if (cached) cachedResults.push(cached); + else windowsToFetch.push({ + win, + winStart, + winEnd + }); + } + if (!windowsToFetch.length) { + this._lastComparisonResults = this._getResolvedComparisonResults(); + if (redraw && this._lastHistResult && this._lastEvents) this._queueDrawChart(this._lastHistResult, this._lastStatsResult || {}, this._lastEvents, this._lastT0, this._lastT1); + return Promise.resolve(this._lastComparisonResults); + } + this._lastComparisonResults = cachedResults.length ? this._getResolvedComparisonResults() : null; + if (showLoading) this._setChartLoading(true); + this.dispatchEvent(new CustomEvent("hass-datapoints-comparison-loading", { + bubbles: true, + composed: true, + detail: { + ids: windowsToFetch.map(({ win }) => win.id).filter(Boolean), + loading: true + } + })); + return Promise.all(windowsToFetch.map(async ({ win, winStart, winEnd }) => this._loadComparisonWindowData(win, winStart, winEnd))).then(() => { + if (comparisonRequestId !== this._comparisonRequestId) return this._lastComparisonResults || []; + if (targetRequestId != null && targetRequestId !== this._loadRequestId) return this._lastComparisonResults || []; + this._lastComparisonResults = this._getResolvedComparisonResults(); + this.dispatchEvent(new CustomEvent("hass-datapoints-comparison-loading", { + bubbles: true, + composed: true, + detail: { + ids: windowsToFetch.map(({ win }) => win.id).filter(Boolean), + loading: false + } + })); + if (redraw && this._lastHistResult && this._lastEvents) this._queueDrawChart(this._lastHistResult, this._lastStatsResult || {}, this._lastEvents, this._lastT0, this._lastT1); + else if (showLoading) this._setChartLoading(false); + return this._lastComparisonResults; + }).catch(() => { + if (comparisonRequestId === this._comparisonRequestId) { + this._lastComparisonResults = []; + logger$1.warn("[hass-datapoints history-card] comparison load:failed", { + comparisonRequestId, + ids: comparisonWindows.map((win) => win.id).filter(Boolean) + }); + this.dispatchEvent(new CustomEvent("hass-datapoints-comparison-loading", { + bubbles: true, + composed: true, + detail: { + ids: windowsToFetch.map(({ win }) => win.id).filter(Boolean), + loading: false + } + })); + if (redraw && this._lastHistResult && this._lastEvents) this._queueDrawChart(this._lastHistResult, this._lastStatsResult || {}, this._lastEvents, this._lastT0, this._lastT1); + else if (showLoading) this._setChartLoading(false); + } else if (showLoading) this._setChartLoading(false); + return []; + }); + } + async _load() { + const { start, end } = this._getRange(); + const t0 = start.getTime(); + const t1 = end.getTime(); + const requestId = ++this._loadRequestId; + this._setChartLoading(true); + logger$1.log("[hass-datapoints history-card] load triggered", { + requestId, + entityIds: this._entityIds, + start: start.toISOString(), + end: end.toISOString() + }); + const partial = { + histResult: null, + statsResult: this._statisticsEntityIds.length ? null : {}, + events: null, + histDone: false, + statsDone: !this._statisticsEntityIds.length, + eventsDone: false, + histFailed: false, + statsFailed: false, + eventsFailed: false, + hasDrawnDrawable: false, + lastDrawState: null, + lastDrawQuality: null + }; + const logChartRedrawData = (reason, histResult, statsResult, events) => { + logger$1.log("[hass-datapoints history] redraw data update", { + reason, + requestId, + entityIds: this._entityIds, + start: start.toISOString(), + end: end.toISOString(), + histResult, + statsResult, + events + }); + }; + const maybeDraw = () => { + if (requestId !== this._loadRequestId) return; + const hasDrawableData = this._hasDrawableHistoryData(partial.histResult || {}, partial.statsResult || {}); + const numericRequestsFinished = partial.histDone && partial.statsDone; + if (!hasDrawableData && !numericRequestsFinished) return; + if (partial.hasDrawnDrawable) { + const drawQuality = hasDrawableData ? this._getDrawableHistoryQuality(partial.histResult || {}, partial.statsResult || {}) : null; + const redrawForHistory = hasDrawableData && !partial.lastDrawState?.histDone && partial.histDone; + const redrawForStats = hasDrawableData && !partial.lastDrawState?.statsDone && partial.statsDone; + const redrawForEvents = hasDrawableData && !partial.lastDrawState?.eventsDone && partial.eventsDone; + const shouldRedraw = redrawForHistory || redrawForStats || redrawForEvents; + const wouldDowngradeDraw = !!drawQuality && !!partial.lastDrawQuality && drawQuality.totalPoints < partial.lastDrawQuality.totalPoints; + if (!shouldRedraw) { + if (partial.histDone && partial.statsDone && partial.eventsDone) this._setChartLoading(false); + return; + } + if (wouldDowngradeDraw) { + if (partial.histDone && partial.statsDone && partial.eventsDone) this._setChartLoading(false); + return; + } + if (redrawForEvents && !redrawForHistory && this._lastHistResult && Number.isFinite(this._lastT0) && Number.isFinite(this._lastT1)) { + const filteredEvents = this._filterEvents(partial.events || []); + logChartRedrawData("events_update", this._lastHistResult, this._lastStatsResult || {}, filteredEvents); + partial.lastDrawState = { + histDone: partial.histDone, + statsDone: partial.statsDone, + eventsDone: partial.eventsDone + }; + this._queueDrawChart(this._lastHistResult, this._lastStatsResult || {}, partial.events || [], this._lastT0, this._lastT1, { loading: !(partial.histDone && partial.statsDone && partial.eventsDone) }); + return; + } + } + if (hasDrawableData) { + const drawQuality = this._getDrawableHistoryQuality(partial.histResult || {}, partial.statsResult || {}); + partial.hasDrawnDrawable = true; + partial.lastDrawState = { + histDone: partial.histDone, + statsDone: partial.statsDone, + eventsDone: partial.eventsDone + }; + partial.lastDrawQuality = drawQuality; + } + const filteredEvents = this._filterEvents(partial.events || []); + logChartRedrawData(partial.hasDrawnDrawable ? "data_update" : "initial_data_draw", partial.histResult || {}, partial.statsResult || {}, filteredEvents); + this._queueDrawChart(partial.histResult || {}, partial.statsResult || {}, partial.events || [], t0, t1, { loading: !(partial.histDone && partial.statsDone && partial.eventsDone) }); + }; + const finalize = () => { + if (requestId !== this._loadRequestId) return; + if (!(partial.histDone && partial.statsDone && partial.eventsDone)) return; + if (partial.histFailed && partial.statsFailed || partial.histResult == null && partial.statsResult == null) { + this._setChartMessage("Failed to load data."); + this._setChartLoading(false); + return; + } + if (partial.hasDrawnDrawable) this._setChartLoading(false); + this._preloadComparisonWindows().catch(() => {}); + }; + this._loadComparisonWindows({ + redraw: true, + requestId + }).catch(() => {}); + try { + const hass = this._hass; + if (!hass) { + this._setChartMessage("Failed to load data."); + this._setChartLoading(false); + return; + } + fetchHistoryDuringPeriod(hass, start.toISOString(), end.toISOString(), this._entityIds, { + include_start_time_state: true, + significant_changes_only: false, + no_attributes: true + }).then((histResult) => { + partial.histResult = histResult || {}; + partial.histDone = true; + maybeDraw(); + finalize(); + }).catch((err) => { + partial.histDone = true; + partial.histFailed = true; + logger$1.error("[hass-datapoints history-card] history load failed", err); + maybeDraw(); + finalize(); + }); + if (this._statisticsEntityIds.length) fetchStatisticsDuringPeriod(hass, start.toISOString(), end.toISOString(), this._statisticsEntityIds, { + period: "hour", + types: ["mean"], + units: {} + }).then((statsResult) => { + partial.statsResult = statsResult || {}; + partial.statsDone = true; + maybeDraw(); + finalize(); + }).catch((err) => { + partial.statsDone = true; + partial.statsFailed = true; + logger$1.error("[hass-datapoints history-card] statistics load failed", err); + maybeDraw(); + finalize(); + }); + if (this._config.datapoint_scope === "hidden") { + partial.events = []; + partial.eventsDone = true; + maybeDraw(); + finalize(); + } else fetchEvents(hass, start.toISOString(), end.toISOString(), this._config.datapoint_scope === "all" ? void 0 : this._entityIds).then((events) => { + partial.events = events || []; + partial.eventsDone = true; + maybeDraw(); + finalize(); + }).catch((err) => { + partial.eventsDone = true; + partial.eventsFailed = true; + logger$1.error("[hass-datapoints history-card] event load failed", err); + maybeDraw(); + finalize(); + }); + } catch (err) { + this._setChartMessage("Failed to load data."); + this._setChartLoading(false); + logger$1.error("[hass-datapoints history-card]", err); + } + } + /** Delegates to the hass-datapoints-history-chart sub-component for resize-replay. */ + _drawChart(...args) { + const chartEl = this._chartEl(); + if (chartEl) { + chartEl.hass = this._hass; + chartEl._config = this._config; + chartEl._hiddenSeries = this._hiddenSeries; + chartEl._hiddenEventIds = this._hiddenEventIds; + chartEl._zoomRange = this._zoomRange; + chartEl._lastComparisonResults = this._lastComparisonResults; + chartEl._drawChart(...args); + } + } + /** Returns the hass-datapoints-history-chart element once it is in the shadow DOM. */ + _chartEl() { + return this.shadowRoot?.querySelector("hass-datapoints-history-chart") ?? null; + } + /** + * Queue a draw cycle. Delegates to hass-datapoints-history-chart and also records last + * draw args so the base-class ResizeObserver can replay via _drawChart(). + */ + _queueDrawChart(histResult, statsResult, events, t0, t1, options = {}) { + const drawRequestId = ++this._drawRequestId; + const filteredEvents = this._filterEvents(events); + this._lastHistResult = histResult; + this._lastStatsResult = statsResult; + this._lastEvents = events; + this._lastT0 = t0; + this._lastT1 = t1; + this._lastDrawArgs = [ + histResult, + statsResult, + events, + t0, + t1, + { + ...options, + drawRequestId + } + ]; + const chartEl = this._chartEl(); + if (chartEl) { + chartEl.hass = this._hass; + chartEl._config = this._config; + chartEl._hiddenSeries = this._hiddenSeries; + chartEl._hiddenEventIds = this._hiddenEventIds; + chartEl._zoomRange = this._zoomRange; + chartEl._lastComparisonResults = this._lastComparisonResults; + chartEl._queueDrawChart(histResult, statsResult, filteredEvents, t0, t1, { + ...options, + drawRequestId + }); + } + } + _setChartLoading(isLoading) { + const chartEl = this._chartEl(); + if (chartEl?._setChartLoading) chartEl._setChartLoading(isLoading); + } + _setChartMessage(message = "") { + const chartEl = this._chartEl(); + if (chartEl?._setChartMessage) chartEl._setChartMessage(message); + } + /** + * Returns true if there is at least one entity with drawable data points + * in either the history or statistics result. + */ + _hasDrawableHistoryData(histResult, statsResult) { + return this._entityIds.some((entityId) => { + const histData = histResult[entityId]; + const statsData = statsResult[entityId]; + const histPoints = Array.isArray(histData) ? histData.length : 0; + const statsPoints = Array.isArray(statsData) ? statsData.length : 0; + return histPoints > 0 || statsPoints > 0; + }); + } + /** + * Returns a quality descriptor for the current drawable data — used to + * avoid downgrading a high-resolution draw with a lower-resolution one. + */ + _getDrawableHistoryQuality(histResult, statsResult) { + let totalPoints = 0; + for (const entityId of this._entityIds) { + const histData = histResult[entityId]; + const statsData = statsResult[entityId]; + totalPoints += Array.isArray(histData) ? histData.length : 0; + totalPoints += Array.isArray(statsData) ? statsData.length : 0; + } + return { totalPoints }; + } + _filterEvents(events) { + const query = String(this._config?.message_filter || "").trim().toLowerCase(); + const visibleEvents = events.filter((event) => !this._hiddenEventIds.has(event?.id ?? "")); + if (!query) return visibleEvents; + return visibleEvents.filter((event) => { + const ev = event; + return [ + ev?.message || "", + ev?.annotation || "", + ...(ev?.entity_ids || []).filter(Boolean) + ].join("\n").toLowerCase().includes(query); + }); + } + _buildNavigationPageState() { + const range = this._getRange(); + const fallbackZoomRange = typeof this._config.zoom_start_time !== "undefined" && typeof this._config.zoom_end_time !== "undefined" ? { + start: this._config.zoom_start_time, + end: this._config.zoom_end_time + } : null; + const targetSelection = this._config.target && typeof this._config.target === "object" ? normalizeTargetValue(this._config.target) : { entity_id: this._entityIds }; + return { + ...buildHistoryPageSessionState({ + _entities: this._entityIds, + _seriesRows: Array.isArray(this._config.series_settings) ? [...this._config.series_settings] : this._entityIds.map((entityId) => ({ entity_id: entityId })), + _targetSelection: targetSelection, + _targetSelectionRaw: targetSelection, + _datapointScope: this._config.datapoint_scope || "linked", + _showChartDatapointIcons: this._config.show_event_markers !== false, + _showChartDatapointLines: this._config.show_event_lines !== false, + _showChartTooltips: this._config.show_tooltips !== false, + _showChartEmphasizedHoverGuides: this._config.emphasize_hover_guides === true, + _chartHoverSnapMode: this._config.hover_snap_mode || "follow_series", + _delinkChartYAxis: this._config.delink_y_axis === true, + _splitChartView: this._config.split_view === true, + _showCorrelatedAnomalies: this._config.show_correlated_anomalies === true, + _chartAnomalyOverlapMode: "all", + _showDataGaps: this._config.show_data_gaps !== false, + _dataGapThreshold: this._config.data_gap_threshold || "2h", + _contentSplitRatio: .62, + _startTime: range.start, + _endTime: range.end, + _chartZoomCommittedRange: this._zoomRange ? { + start: this._zoomRange.start, + end: this._zoomRange.end + } : fallbackZoomRange, + _comparisonWindows: Array.isArray(this._config.comparison_windows) ? [...this._config.comparison_windows] : [], + _hours: Number(this._config.hours_to_show || 24), + _sidebarCollapsed: false, + _sidebarAccordionTargetsOpen: true, + _sidebarAccordionDatapointsOpen: true, + _sidebarAccordionAnalysisOpen: true, + _sidebarAccordionChartOpen: true, + _zoomLevel: "auto", + _dateSnapping: "hour", + _preferredSeriesColors: {} + }), + show_chart_trend_lines: this._config.show_trend_lines === true, + show_chart_summary_stats: this._config.show_summary_stats === true, + show_chart_rate_of_change: this._config.show_rate_of_change === true, + show_chart_threshold_analysis: this._config.show_threshold_analysis === true, + show_chart_threshold_shading: this._config.show_threshold_shading === true, + show_chart_anomalies: this._config.show_anomalies === true, + show_chart_trend_crosshairs: this._config.show_trend_crosshairs === true, + chart_trend_method: this._config.trend_method || "rolling_average", + chart_trend_window: this._config.trend_window || "24h", + chart_rate_window: this._config.rate_window || "1h", + chart_anomaly_sensitivity: this._config.anomaly_sensitivity || "medium", + chart_threshold_values: this._config.threshold_values || {}, + chart_threshold_directions: this._config.threshold_directions || {}, + show_chart_delta_analysis: this._config.show_delta_analysis === true, + show_chart_delta_tooltip: this._config.show_delta_tooltip !== false, + show_chart_delta_lines: this._config.show_delta_lines === true, + hide_chart_source_series: this._config.hide_raw_data === true, + chart_anomaly_rate_window: this._config.rate_window || "1h" + }; + } + _navigateToPanel(event) { + event.preventDefault(); + event.stopPropagation(); + const range = this._getRange(); + const zoomStartTime = this._zoomRange?.start ? this._zoomRange.start : this._config.zoom_start_time; + const zoomEndTime = this._zoomRange?.end ? this._zoomRange.end : this._config.zoom_end_time; + navigateToDataPointsHistory(this, this._config.target && typeof this._config.target === "object" ? normalizeTargetValue(this._config.target) : { entity_id: this._entityIds }, { + datapoint_scope: this._config.datapoint_scope || void 0, + start_time: Number.isFinite(this._lastT0) ? this._lastT0 : range.start, + end_time: Number.isFinite(this._lastT1) ? this._lastT1 : range.end, + zoom_start_time: zoomStartTime, + zoom_end_time: zoomEndTime, + page_state: this._buildNavigationPageState() + }); + } + _handleWindowKeyDown(ev) { + if (ev.key !== "Escape") return; + if (this._annotationDialog?.isOpen?.()) { + ev.preventDefault(); + return; + } + if (!this._zoomRange) return; + ev.preventDefault(); + this._zoomRange = null; + this._dispatchZoomRange("reset"); + } + _handleChartScroll() { + if (this._scrollSyncSuspended || !this._zoomRange) return; + if (this._ignoreNextProgrammaticScrollEvent) { + this._ignoreNextProgrammaticScrollEvent = false; + this._lastProgrammaticScrollLeft = null; + return; + } + if (this._lastProgrammaticScrollLeft != null && Math.abs((this._chartScrollViewportEl?.scrollLeft || 0) - this._lastProgrammaticScrollLeft) < 1) { + this._lastProgrammaticScrollLeft = null; + return; + } + this._lastProgrammaticScrollLeft = null; + } + _dispatchZoomRange(source) { + this.dispatchEvent(new CustomEvent("hass-datapoints-chart-zoom", { + bubbles: true, + composed: true, + detail: this._zoomRange ? { + startTime: this._zoomRange.start, + endTime: this._zoomRange.end, + preview: false, + source + } : { + startTime: null, + endTime: null, + preview: false, + source + } + })); + } + _scheduleZoomReload() { + if (this._zoomReloadTimer != null) window.clearTimeout(this._zoomReloadTimer); + this._zoomReloadTimer = window.setTimeout(() => { + this._zoomReloadTimer = null; + this._load(); + }, 140); + } + render() { + return b` ${this._config?.title ? b`

@@ -16027,23 +16403,26 @@ ${content.alert}` : "", `; - } - // ── Static API ───────────────────────────────────────────────────────────── - static getStubConfig() { - return { - title: "History with Events", - entity: "sensor.example", - hours_to_show: 24 - }; - } - static getConfigElement() { - return document.createElement("hass-datapoints-history-card-editor"); - } - } - __publicField$R(HassRecordsHistoryCard, "styles", styles$T); - customElements.define("hass-datapoints-history-card", HassRecordsHistoryCard); - const styles$P = i$5``; - const sharedStyles = i$5` + } + static getStubConfig() { + return { + title: "History with Events", + entity: "sensor.example", + hours_to_show: 24 + }; + } + static getConfigElement() { + return document.createElement("hass-datapoints-history-card-editor"); + } + }; + _defineProperty(HassRecordsHistoryCard, "styles", styles$55); + customElements.define("hass-datapoints-history-card", HassRecordsHistoryCard); + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/editor.styles.ts + var styles$51 = i$5``; + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-group-shared/analysis-group-shared.styles.ts + var sharedStyles = i$5` :host { display: block; --dp-spacing-xs: calc(var(--spacing, 8px) * 0.5); @@ -16106,8 +16485,12 @@ ${content.alert}` : "", cursor: pointer; } `; - const styles$O = i$5``; - const styles$N = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-sample-group/analysis-sample-group.styles.ts + var styles$50 = i$5``; + //#endregion + //#region custom_components/hass_datapoints/src/atoms/analysis/analysis-group/analysis-group.styles.ts + var styles$49 = i$5` :host { display: block; --dp-spacing-xs: calc(var(--spacing, 8px) * 0.5); @@ -16162,81 +16545,61 @@ ${content.alert}` : "", padding-top: 2px; } `; - var __create$H = Object.create; - var __defProp$Q = Object.defineProperty; - var __getOwnPropDesc$H = Object.getOwnPropertyDescriptor; - var __knownSymbol$H = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$H = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$Q = (obj, key, value) => key in obj ? __defProp$Q(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$H = (base) => [, , , __create$H(base?.[__knownSymbol$H("metadata")] ?? null)]; - var __decoratorStrings$H = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$H = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$H("Function expected") : fn; - var __decoratorContext$H = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$H[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$H("Already initialized") : fns.push(__expectFn$H(fn || null)) }); - var __decoratorMetadata$H = (array, target) => __defNormalProp$Q(target, __knownSymbol$H("metadata"), array[3]); - var __runInitializers$H = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$H = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$H[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$H({ get [name]() { - return __privateGet$G(this, extra); - }, set [name](x2) { - return __privateSet$G(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$H(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$H(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$H("Object expected"); - else __expectFn$H(fn = it.get) && (desc.get = fn), __expectFn$H(fn = it.set) && (desc.set = fn), __expectFn$H(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$Q(target, name, desc), target; - }; - var __publicField$Q = (obj, key, value) => __defNormalProp$Q(obj, key + "", value); - var __accessCheck$G = (obj, member, msg2) => member.has(obj) || __typeError$H("Cannot " + msg2); - var __privateGet$G = (obj, member, getter) => (__accessCheck$G(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$G = (obj, member, value) => member.has(obj) ? __typeError$H("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$G = (obj, member, value, setter) => (__accessCheck$G(obj, member, "write to private field"), member.set(obj, value), value); - var _alignTop_dec, _disabled_dec$1, _checked_dec, _label_dec$8, _a$H, _init$H, _label$8, _checked, _disabled$1, _alignTop; - class AnalysisGroup extends (_a$H = i$2, _label_dec$8 = [n({ type: String })], _checked_dec = [n({ type: Boolean })], _disabled_dec$1 = [n({ type: Boolean })], _alignTop_dec = [n({ type: Boolean, attribute: "align-top" })], _a$H) { - constructor() { - super(...arguments); - __privateAdd$G(this, _label$8, __runInitializers$H(_init$H, 8, this, "")), __runInitializers$H(_init$H, 11, this); - __privateAdd$G(this, _checked, __runInitializers$H(_init$H, 12, this, false)), __runInitializers$H(_init$H, 15, this); - __privateAdd$G(this, _disabled$1, __runInitializers$H(_init$H, 16, this, false)), __runInitializers$H(_init$H, 19, this); - __privateAdd$G(this, _alignTop, __runInitializers$H(_init$H, 20, this, false)), __runInitializers$H(_init$H, 23, this); - } - _onChange(e2) { - const checked = e2.target.checked; - this.checked = checked; - this.dispatchEvent( - new CustomEvent("dp-group-change", { - detail: { checked }, - bubbles: true, - composed: true - }) - ); - } - render() { - const groupClass = ["group", this.checked ? "is-open" : ""].filter(Boolean).join(" "); - const optionClass = [ - "option", - this.alignTop ? "top" : "", - this.disabled ? "is-disabled" : "" - ].filter(Boolean).join(" "); - return b` -
- - ${a2.show_threshold_shading ? b` + ${a.show_threshold_shading ? b` ` : A} `; - } - } - _init$C = __decoratorStart$C(_a$C); - _analysis$3 = /* @__PURE__ */ new WeakMap(); - _entityId$3 = /* @__PURE__ */ new WeakMap(); - _unit$1 = /* @__PURE__ */ new WeakMap(); - __decorateElement$C(_init$C, 4, "analysis", _analysis_dec$3, AnalysisThresholdGroup, _analysis$3); - __decorateElement$C(_init$C, 4, "entityId", _entityId_dec$3, AnalysisThresholdGroup, _entityId$3); - __decorateElement$C(_init$C, 4, "unit", _unit_dec$1, AnalysisThresholdGroup, _unit$1); - AnalysisThresholdGroup = __decorateElement$C(_init$C, 0, "AnalysisThresholdGroup", _AnalysisThresholdGroup_decorators, AnalysisThresholdGroup); - __publicField$L(AnalysisThresholdGroup, "styles", [sharedStyles, styles$J]); - __runInitializers$C(_init$C, 1, AnalysisThresholdGroup); - customElements.define("analysis-threshold-group", AnalysisThresholdGroup); - const styles$I = i$5` + } + }, _defineProperty(_AnalysisThresholdGroup, "styles", [sharedStyles, styles$45]), _AnalysisThresholdGroup); + __decorate([n$1({ type: Object })], AnalysisThresholdGroup.prototype, "analysis", null); + __decorate([n$1({ + type: String, + attribute: "entity-id" + })], AnalysisThresholdGroup.prototype, "entityId", null); + __decorate([n$1({ type: String })], AnalysisThresholdGroup.prototype, "unit", null); + AnalysisThresholdGroup = __decorate([localized()], AnalysisThresholdGroup); + customElements.define("analysis-threshold-group", AnalysisThresholdGroup); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-anomaly-group/analysis-anomaly-group.styles.ts + var styles$44 = i$5` .method-computing-indicator { display: inline-flex; align-items: center; @@ -17051,7 +17325,9 @@ ${content.alert}` : "", outline-offset: 2px; } `; - const styles$H = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/analysis/analysis-method-subopts/analysis-method-subopts.styles.ts + var styles$43 = i$5` :host { display: block; --dp-spacing-sm: var(--spacing, 8px); @@ -17066,300 +17342,295 @@ ${content.alert}` : "", margin-left: 5px; } `; - var __defProp$K = Object.defineProperty; - var __defNormalProp$K = (obj, key, value) => key in obj ? __defProp$K(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$K = (obj, key, value) => __defNormalProp$K(obj, key + "", value); - class AnalysisMethodSubopts extends i$2 { - render() { - return b`
`; - } - } - __publicField$K(AnalysisMethodSubopts, "styles", styles$H); - customElements.define("analysis-method-subopts", AnalysisMethodSubopts); - var __create$B = Object.create; - var __defProp$J = Object.defineProperty; - var __getOwnPropDesc$B = Object.getOwnPropertyDescriptor; - var __knownSymbol$B = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$B = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$J = (obj, key, value) => key in obj ? __defProp$J(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __name$e = (target, value) => __defProp$J(target, "name", { value, configurable: true }); - var __decoratorStart$B = (base) => [, , , __create$B(base?.[__knownSymbol$B("metadata")] ?? null)]; - var __decoratorStrings$B = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$B = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$B("Function expected") : fn; - var __decoratorContext$B = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$B[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$B("Already initialized") : fns.push(__expectFn$B(fn || null)) }); - var __decoratorMetadata$B = (array, target) => __defNormalProp$J(target, __knownSymbol$B("metadata"), array[3]); - var __runInitializers$B = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$B = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = !!(flags & 8), p2 = !!(flags & 16); - var j2 = k2 > 3 ? array.length + 1 : k2 ? s2 ? 1 : 2 : 0, key = __decoratorStrings$B[k2 + 5]; - var initializers = k2 > 3 && (array[j2 - 1] = []), extraInitializers = array[j2] || (array[j2] = []); - var desc = k2 && (!p2 && !s2 && (target = target.prototype), k2 < 5 && (k2 > 3 || !p2) && __getOwnPropDesc$B(k2 < 4 ? target : { get [name]() { - return __privateGet$A(this, extra); - }, set [name](x2) { - return __privateSet$A(this, extra, x2); - } }, name)); - k2 ? p2 && k2 < 4 && __name$e(extra, (k2 > 2 ? "set " : k2 > 1 ? "get " : "") + name) : __name$e(target, name); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$B(k2, name, done = {}, array[3], extraInitializers); - if (k2) { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: p2 ? (x2) => __privateIn$d(target, x2) : (x2) => name in x2 }; - if (k2 ^ 3) access.get = p2 ? (x2) => (k2 ^ 1 ? __privateGet$A : __privateMethod$d)(x2, target, k2 ^ 4 ? extra : desc.get) : (x2) => x2[name]; - if (k2 > 2) access.set = p2 ? (x2, y2) => __privateSet$A(x2, target, y2, k2 ^ 4 ? extra : desc.set) : (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])(k2 ? k2 < 4 ? p2 ? extra : desc[key] : k2 > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; - if (k2 ^ 4 || it === void 0) __expectFn$B(it) && (k2 > 4 ? initializers.unshift(it) : k2 ? p2 ? extra = it : desc[key] = it : target = it); - else if (typeof it !== "object" || it === null) __typeError$B("Object expected"); - else __expectFn$B(fn = it.get) && (desc.get = fn), __expectFn$B(fn = it.set) && (desc.set = fn), __expectFn$B(fn = it.init) && initializers.unshift(fn); - } - return k2 || __decoratorMetadata$B(array, target), desc && __defProp$J(target, name, desc), p2 ? k2 ^ 4 ? extra : desc : target; - }; - var __publicField$J = (obj, key, value) => __defNormalProp$J(obj, key + "", value); - var __accessCheck$A = (obj, member, msg2) => member.has(obj) || __typeError$B("Cannot " + msg2); - var __privateIn$d = (member, obj) => Object(obj) !== obj ? __typeError$B('Cannot use the "in" operator on this value') : member.has(obj); - var __privateGet$A = (obj, member, getter) => (__accessCheck$A(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); - var __privateAdd$A = (obj, member, value) => member.has(obj) ? __typeError$B("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$A = (obj, member, value, setter) => (__accessCheck$A(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); - var __privateMethod$d = (obj, member, method) => (__accessCheck$A(obj, member, "access private method"), method); - var _computingMethods_dec$1, _computingProgress_dec$1, _computing_dec$1, _comparisonWindows_dec$3, _entityId_dec$2, _analysis_dec$2, _a$B, _AnalysisAnomalyGroup_decorators, _init$B, _analysis$2, _entityId$2, _comparisonWindows$3, _computing$1, _computingProgress$1, _computingMethods$1; - const ANALYSIS_ANOMALY_SENSITIVITY_OPTIONS = [ - { value: "low", label: "Low" }, - { value: "medium", label: "Medium" }, - { value: "high", label: "High" } - ]; - const ANALYSIS_ANOMALY_METHOD_OPTIONS = [ - { - value: "trend_residual", - label: "Trend deviation", - help: "Flags points that deviate significantly from a fitted trend line. Good for catching gradual drift or sudden jumps away from a steady baseline." - }, - { - value: "rate_of_change", - label: "Sudden change", - help: "Flags unusually fast rises or drops compared to the typical rate of change. Best for detecting spikes, crashes, or rapid transitions." - }, - { - value: "iqr", - label: "Statistical outlier (IQR)", - help: "Uses the interquartile range to flag values far outside the normal spread of data. Robust against outliers that skew averages." - }, - { - value: "rolling_zscore", - label: "Rolling Z-score", - help: "Compares each value to a rolling mean and standard deviation. Catches unusual readings relative to recent context rather than the whole series." - }, - { - value: "persistence", - label: "Flat-line / stuck value", - help: "Flags when a sensor reports nearly the same value for an unusually long time. Useful for detecting stuck sensors or frozen readings." - }, - { - value: "comparison_window", - label: "Comparison window deviation", - help: "Compares the current period to a reference date window. Highlights differences from an expected historical pattern, such as last week or the same day last year." - } - ]; - const ANALYSIS_ANOMALY_RATE_WINDOW_OPTIONS = [ - { value: "1h", label: "1 hour" }, - { value: "6h", label: "6 hours" }, - { value: "24h", label: "24 hours" } - ]; - const ANALYSIS_ANOMALY_ZSCORE_WINDOW_OPTIONS = [ - { value: "1h", label: "1 hour" }, - { value: "6h", label: "6 hours" }, - { value: "24h", label: "24 hours" }, - { value: "7d", label: "7 days" } - ]; - const ANALYSIS_ANOMALY_PERSISTENCE_WINDOW_OPTIONS = [ - { value: "30m", label: "30 minutes" }, - { value: "1h", label: "1 hour" }, - { value: "3h", label: "3 hours" }, - { value: "6h", label: "6 hours" }, - { value: "12h", label: "12 hours" }, - { value: "24h", label: "24 hours" } - ]; - const ANALYSIS_ANOMALY_OVERLAP_MODE_OPTIONS$2 = [ - { value: "all", label: "Show all anomalies" }, - { value: "only", label: "Overlaps only" } - ]; - _AnalysisAnomalyGroup_decorators = [localized()]; - class AnalysisAnomalyGroup extends (_a$B = i$2, _analysis_dec$2 = [n({ type: Object })], _entityId_dec$2 = [n({ type: String, attribute: "entity-id" })], _comparisonWindows_dec$3 = [n({ type: Array, attribute: "comparison-windows" })], _computing_dec$1 = [n({ type: Boolean, attribute: false })], _computingProgress_dec$1 = [n({ type: Number, attribute: false })], _computingMethods_dec$1 = [n({ type: Object, attribute: false })], _a$B) { - constructor() { - super(...arguments); - __privateAdd$A(this, _analysis$2, __runInitializers$B(_init$B, 8, this, {})), __runInitializers$B(_init$B, 11, this); - __privateAdd$A(this, _entityId$2, __runInitializers$B(_init$B, 12, this, "")), __runInitializers$B(_init$B, 15, this); - __privateAdd$A(this, _comparisonWindows$3, __runInitializers$B(_init$B, 16, this, [])), __runInitializers$B(_init$B, 19, this); - __privateAdd$A(this, _computing$1, __runInitializers$B(_init$B, 20, this, false)), __runInitializers$B(_init$B, 23, this); - __privateAdd$A(this, _computingProgress$1, __runInitializers$B(_init$B, 24, this, 0)), __runInitializers$B(_init$B, 27, this); - __privateAdd$A(this, _computingMethods$1, __runInitializers$B(_init$B, 28, this, /* @__PURE__ */ new Set())), __runInitializers$B(_init$B, 31, this); - } - _emit(key, value) { - this.dispatchEvent( - new CustomEvent("dp-group-analysis-change", { - detail: { entityId: this.entityId, key, value }, - bubbles: true, - composed: true - }) - ); - } - _renderSelect(key, options, value) { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/analysis/analysis-method-subopts/analysis-method-subopts.ts + var AnalysisMethodSubopts = class extends i$2 { + render() { + return b`
`; + } + }; + _defineProperty(AnalysisMethodSubopts, "styles", styles$43); + customElements.define("analysis-method-subopts", AnalysisMethodSubopts); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-anomaly-group/analysis-anomaly-group.ts + var _AnalysisAnomalyGroup, _analysis_accessor_storage$2, _entityId_accessor_storage$2, _comparisonWindows_accessor_storage$3, _computing_accessor_storage$1, _computingProgress_accessor_storage$1, _computingMethods_accessor_storage$1; + var ANALYSIS_ANOMALY_SENSITIVITY_OPTIONS = [ + { + value: "low", + label: "Low" + }, + { + value: "medium", + label: "Medium" + }, + { + value: "high", + label: "High" + } + ]; + var ANALYSIS_ANOMALY_METHOD_OPTIONS = [ + { + value: "trend_residual", + label: "Trend deviation", + help: "Flags points that deviate significantly from a fitted trend line. Good for catching gradual drift or sudden jumps away from a steady baseline." + }, + { + value: "rate_of_change", + label: "Sudden change", + help: "Flags unusually fast rises or drops compared to the typical rate of change. Best for detecting spikes, crashes, or rapid transitions." + }, + { + value: "iqr", + label: "Statistical outlier (IQR)", + help: "Uses the interquartile range to flag values far outside the normal spread of data. Robust against outliers that skew averages." + }, + { + value: "rolling_zscore", + label: "Rolling Z-score", + help: "Compares each value to a rolling mean and standard deviation. Catches unusual readings relative to recent context rather than the whole series." + }, + { + value: "persistence", + label: "Flat-line / stuck value", + help: "Flags when a sensor reports nearly the same value for an unusually long time. Useful for detecting stuck sensors or frozen readings." + }, + { + value: "comparison_window", + label: "Comparison window deviation", + help: "Compares the current period to a reference date window. Highlights differences from an expected historical pattern, such as last week or the same day last year." + } + ]; + var ANALYSIS_ANOMALY_RATE_WINDOW_OPTIONS = [ + { + value: "1h", + label: "1 hour" + }, + { + value: "6h", + label: "6 hours" + }, + { + value: "24h", + label: "24 hours" + } + ]; + var ANALYSIS_ANOMALY_ZSCORE_WINDOW_OPTIONS = [ + { + value: "1h", + label: "1 hour" + }, + { + value: "6h", + label: "6 hours" + }, + { + value: "24h", + label: "24 hours" + }, + { + value: "7d", + label: "7 days" + } + ]; + var ANALYSIS_ANOMALY_PERSISTENCE_WINDOW_OPTIONS = [ + { + value: "30m", + label: "30 minutes" + }, + { + value: "1h", + label: "1 hour" + }, + { + value: "3h", + label: "3 hours" + }, + { + value: "6h", + label: "6 hours" + }, + { + value: "12h", + label: "12 hours" + }, + { + value: "24h", + label: "24 hours" + } + ]; + var ANALYSIS_ANOMALY_OVERLAP_MODE_OPTIONS$2 = [{ + value: "all", + label: "Show all anomalies" + }, { + value: "only", + label: "Overlaps only" + }]; + var AnalysisAnomalyGroup = (_analysis_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _entityId_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _comparisonWindows_accessor_storage$3 = /* @__PURE__ */ new WeakMap(), _computing_accessor_storage$1 = /* @__PURE__ */ new WeakMap(), _computingProgress_accessor_storage$1 = /* @__PURE__ */ new WeakMap(), _computingMethods_accessor_storage$1 = /* @__PURE__ */ new WeakMap(), _AnalysisAnomalyGroup = class AnalysisAnomalyGroup extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _analysis_accessor_storage$2, {}); + _classPrivateFieldInitSpec(this, _entityId_accessor_storage$2, ""); + _classPrivateFieldInitSpec(this, _comparisonWindows_accessor_storage$3, []); + _classPrivateFieldInitSpec(this, _computing_accessor_storage$1, false); + _classPrivateFieldInitSpec(this, _computingProgress_accessor_storage$1, 0); + _classPrivateFieldInitSpec(this, _computingMethods_accessor_storage$1, /* @__PURE__ */ new Set()); + } + get analysis() { + return _classPrivateFieldGet2(_analysis_accessor_storage$2, this); + } + set analysis(value) { + _classPrivateFieldSet2(_analysis_accessor_storage$2, this, value); + } + get entityId() { + return _classPrivateFieldGet2(_entityId_accessor_storage$2, this); + } + set entityId(value) { + _classPrivateFieldSet2(_entityId_accessor_storage$2, this, value); + } + get comparisonWindows() { + return _classPrivateFieldGet2(_comparisonWindows_accessor_storage$3, this); + } + set comparisonWindows(value) { + _classPrivateFieldSet2(_comparisonWindows_accessor_storage$3, this, value); + } + get computing() { + return _classPrivateFieldGet2(_computing_accessor_storage$1, this); + } + set computing(value) { + _classPrivateFieldSet2(_computing_accessor_storage$1, this, value); + } + get computingProgress() { + return _classPrivateFieldGet2(_computingProgress_accessor_storage$1, this); + } + set computingProgress(value) { + _classPrivateFieldSet2(_computingProgress_accessor_storage$1, this, value); + } + get computingMethods() { + return _classPrivateFieldGet2(_computingMethods_accessor_storage$1, this); + } + set computingMethods(value) { + _classPrivateFieldSet2(_computingMethods_accessor_storage$1, this, value); + } + _emit(key, value) { + this.dispatchEvent(new CustomEvent("dp-group-analysis-change", { + detail: { + entityId: this.entityId, + key, + value + }, + bubbles: true, + composed: true + })); + } + _renderSelect(key, options, value) { + return b` `; - } - _onGroupChange(e2) { - this._emit("show_anomalies", e2.detail.checked); - } - _localizedOptions(options) { - return options.map((option) => ({ - ...option, - label: msg(option.label), - help: option.help ? msg(option.help) : void 0 - })); - } - _renderMethodSubopts(opt, a2) { - if (opt.value === "rate_of_change") { - return b` + } + _onGroupChange(e) { + this._emit("show_anomalies", e.detail.checked); + } + _localizedOptions(options) { + return options.map((option) => ({ + ...option, + label: msg(option.label), + help: option.help ? msg(option.help) : void 0 + })); + } + _renderMethodSubopts(opt, a) { + if (opt.value === "rate_of_change") return b` `; - } - if (opt.value === "rolling_zscore") { - return b` + if (opt.value === "rolling_zscore") return b` `; - } - if (opt.value === "persistence") { - return b` + if (opt.value === "persistence") return b` `; - } - if (opt.value === "comparison_window") { - return b` + if (opt.value === "comparison_window") return b` `; - } - return A; - } - render() { - const a2 = this.analysis; - const sensitivityOptions = this._localizedOptions( - ANALYSIS_ANOMALY_SENSITIVITY_OPTIONS - ); - const methodOptions = this._localizedOptions( - ANALYSIS_ANOMALY_METHOD_OPTIONS - ); - const overlapOptions = this._localizedOptions( - ANALYSIS_ANOMALY_OVERLAP_MODE_OPTIONS$2 - ); - return b` + return A; + } + render() { + const a = this.analysis; + const sensitivityOptions = this._localizedOptions(ANALYSIS_ANOMALY_SENSITIVITY_OPTIONS); + const methodOptions = this._localizedOptions(ANALYSIS_ANOMALY_METHOD_OPTIONS); + const overlapOptions = this._localizedOptions(ANALYSIS_ANOMALY_OVERLAP_MODE_OPTIONS$2); + return b` - ${a2.sample_interval && a2.sample_interval !== "raw" ? b` + ${a.sample_interval && a.sample_interval !== "raw" ? b` ` : A}
${methodOptions.map((opt) => { - const isChecked = Array.isArray(a2.anomaly_methods) && a2.anomaly_methods.includes(opt.value); - const isComputing = isChecked && (this.computingMethods?.has(opt.value) ?? false); - const helpId = `anomaly-help-${opt.value}`; - return b` + const isChecked = Array.isArray(a.anomaly_methods) && a.anomaly_methods.includes(opt.value); + const isComputing = isChecked && (this.computingMethods?.has(opt.value) ?? false); + const helpId = `anomaly-help-${opt.value}`; + return b`
- ${isChecked ? this._renderMethodSubopts(opt, a2) : A} + ${isChecked ? this._renderMethodSubopts(opt, a) : A}
`; - })} + })}
- ${Array.isArray(a2.anomaly_methods) && a2.anomaly_methods.length >= 2 ? b` + ${Array.isArray(a.anomaly_methods) && a.anomaly_methods.length >= 2 ? b` ` : A}
`; - } - } - _init$B = __decoratorStart$B(_a$B); - _analysis$2 = /* @__PURE__ */ new WeakMap(); - _entityId$2 = /* @__PURE__ */ new WeakMap(); - _comparisonWindows$3 = /* @__PURE__ */ new WeakMap(); - _computing$1 = /* @__PURE__ */ new WeakMap(); - _computingProgress$1 = /* @__PURE__ */ new WeakMap(); - _computingMethods$1 = /* @__PURE__ */ new WeakMap(); - __decorateElement$B(_init$B, 4, "analysis", _analysis_dec$2, AnalysisAnomalyGroup, _analysis$2); - __decorateElement$B(_init$B, 4, "entityId", _entityId_dec$2, AnalysisAnomalyGroup, _entityId$2); - __decorateElement$B(_init$B, 4, "comparisonWindows", _comparisonWindows_dec$3, AnalysisAnomalyGroup, _comparisonWindows$3); - __decorateElement$B(_init$B, 4, "computing", _computing_dec$1, AnalysisAnomalyGroup, _computing$1); - __decorateElement$B(_init$B, 4, "computingProgress", _computingProgress_dec$1, AnalysisAnomalyGroup, _computingProgress$1); - __decorateElement$B(_init$B, 4, "computingMethods", _computingMethods_dec$1, AnalysisAnomalyGroup, _computingMethods$1); - AnalysisAnomalyGroup = __decorateElement$B(_init$B, 0, "AnalysisAnomalyGroup", _AnalysisAnomalyGroup_decorators, AnalysisAnomalyGroup); - __publicField$J(AnalysisAnomalyGroup, "styles", [sharedStyles, styles$I]); - __runInitializers$B(_init$B, 1, AnalysisAnomalyGroup); - customElements.define("analysis-anomaly-group", AnalysisAnomalyGroup); - const styles$G = i$5` + } + }, _defineProperty(_AnalysisAnomalyGroup, "styles", [sharedStyles, styles$44]), _AnalysisAnomalyGroup); + __decorate([n$1({ type: Object })], AnalysisAnomalyGroup.prototype, "analysis", null); + __decorate([n$1({ + type: String, + attribute: "entity-id" + })], AnalysisAnomalyGroup.prototype, "entityId", null); + __decorate([n$1({ + type: Array, + attribute: "comparison-windows" + })], AnalysisAnomalyGroup.prototype, "comparisonWindows", null); + __decorate([n$1({ + type: Boolean, + attribute: false + })], AnalysisAnomalyGroup.prototype, "computing", null); + __decorate([n$1({ + type: Number, + attribute: false + })], AnalysisAnomalyGroup.prototype, "computingProgress", null); + __decorate([n$1({ + type: Object, + attribute: false + })], AnalysisAnomalyGroup.prototype, "computingMethods", null); + AnalysisAnomalyGroup = __decorate([localized()], AnalysisAnomalyGroup); + customElements.define("analysis-anomaly-group", AnalysisAnomalyGroup); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-delta-group/analysis-delta-group.styles.ts + var styles$42 = i$5` .help-text { display: inline-block; color: var(--secondary-text-color); @@ -17438,83 +17713,55 @@ ${content.alert}` : "", padding-top: 2px; } `; - var __create$A = Object.create; - var __defProp$I = Object.defineProperty; - var __getOwnPropDesc$A = Object.getOwnPropertyDescriptor; - var __knownSymbol$A = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$A = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$I = (obj, key, value) => key in obj ? __defProp$I(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __name$d = (target, value) => __defProp$I(target, "name", { value, configurable: true }); - var __decoratorStart$A = (base) => [, , , __create$A(base?.[__knownSymbol$A("metadata")] ?? null)]; - var __decoratorStrings$A = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$A = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$A("Function expected") : fn; - var __decoratorContext$A = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$A[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$A("Already initialized") : fns.push(__expectFn$A(fn || null)) }); - var __decoratorMetadata$A = (array, target) => __defNormalProp$I(target, __knownSymbol$A("metadata"), array[3]); - var __runInitializers$A = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$A = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = !!(flags & 8), p2 = !!(flags & 16); - var j2 = k2 > 3 ? array.length + 1 : k2 ? s2 ? 1 : 2 : 0, key = __decoratorStrings$A[k2 + 5]; - var initializers = k2 > 3 && (array[j2 - 1] = []), extraInitializers = array[j2] || (array[j2] = []); - var desc = k2 && (!p2 && !s2 && (target = target.prototype), k2 < 5 && (k2 > 3 || !p2) && __getOwnPropDesc$A(k2 < 4 ? target : { get [name]() { - return __privateGet$z(this, extra); - }, set [name](x2) { - return __privateSet$z(this, extra, x2); - } }, name)); - k2 ? p2 && k2 < 4 && __name$d(extra, (k2 > 2 ? "set " : k2 > 1 ? "get " : "") + name) : __name$d(target, name); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$A(k2, name, done = {}, array[3], extraInitializers); - if (k2) { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: p2 ? (x2) => __privateIn$c(target, x2) : (x2) => name in x2 }; - if (k2 ^ 3) access.get = p2 ? (x2) => (k2 ^ 1 ? __privateGet$z : __privateMethod$c)(x2, target, k2 ^ 4 ? extra : desc.get) : (x2) => x2[name]; - if (k2 > 2) access.set = p2 ? (x2, y2) => __privateSet$z(x2, target, y2, k2 ^ 4 ? extra : desc.set) : (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])(k2 ? k2 < 4 ? p2 ? extra : desc[key] : k2 > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; - if (k2 ^ 4 || it === void 0) __expectFn$A(it) && (k2 > 4 ? initializers.unshift(it) : k2 ? p2 ? extra = it : desc[key] = it : target = it); - else if (typeof it !== "object" || it === null) __typeError$A("Object expected"); - else __expectFn$A(fn = it.get) && (desc.get = fn), __expectFn$A(fn = it.set) && (desc.set = fn), __expectFn$A(fn = it.init) && initializers.unshift(fn); - } - return k2 || __decoratorMetadata$A(array, target), desc && __defProp$I(target, name, desc), p2 ? k2 ^ 4 ? extra : desc : target; - }; - var __publicField$I = (obj, key, value) => __defNormalProp$I(obj, key + "", value); - var __accessCheck$z = (obj, member, msg2) => member.has(obj) || __typeError$A("Cannot " + msg2); - var __privateIn$c = (member, obj) => Object(obj) !== obj ? __typeError$A('Cannot use the "in" operator on this value') : member.has(obj); - var __privateGet$z = (obj, member, getter) => (__accessCheck$z(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); - var __privateAdd$z = (obj, member, value) => member.has(obj) ? __typeError$A("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$z = (obj, member, value, setter) => (__accessCheck$z(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); - var __privateMethod$c = (obj, member, method) => (__accessCheck$z(obj, member, "access private method"), method); - var _canShowDeltaAnalysis_dec$3, _entityId_dec$1, _analysis_dec$1, _a$A, _AnalysisDeltaGroup_decorators, _init$A, _analysis$1, _entityId$1, _canShowDeltaAnalysis$3; - _AnalysisDeltaGroup_decorators = [localized()]; - class AnalysisDeltaGroup extends (_a$A = i$2, _analysis_dec$1 = [n({ type: Object })], _entityId_dec$1 = [n({ type: String, attribute: "entity-id" })], _canShowDeltaAnalysis_dec$3 = [n({ type: Boolean, attribute: "can-show-delta-analysis" })], _a$A) { - constructor() { - super(...arguments); - __privateAdd$z(this, _analysis$1, __runInitializers$A(_init$A, 8, this, {})), __runInitializers$A(_init$A, 11, this); - __privateAdd$z(this, _entityId$1, __runInitializers$A(_init$A, 12, this, "")), __runInitializers$A(_init$A, 15, this); - __privateAdd$z(this, _canShowDeltaAnalysis$3, __runInitializers$A(_init$A, 16, this, false)), __runInitializers$A(_init$A, 19, this); - } - _emit(key, value) { - this.dispatchEvent( - new CustomEvent("dp-group-analysis-change", { - detail: { entityId: this.entityId, key, value }, - bubbles: true, - composed: true - }) - ); - } - _onGroupChange(e2) { - this._emit("show_delta_analysis", e2.detail.checked); - } - _onCheckbox(key, e2) { - this._emit(key, e2.target.checked); - } - render() { - const a2 = this.analysis; - const isOpen = a2.show_delta_analysis && this.canShowDeltaAnalysis; - return b` + //#endregion + //#region custom_components/hass_datapoints/src/molecules/analysis-delta-group/analysis-delta-group.ts + var _AnalysisDeltaGroup, _analysis_accessor_storage$1, _entityId_accessor_storage$1, _canShowDeltaAnalysis_accessor_storage$3; + var AnalysisDeltaGroup = (_analysis_accessor_storage$1 = /* @__PURE__ */ new WeakMap(), _entityId_accessor_storage$1 = /* @__PURE__ */ new WeakMap(), _canShowDeltaAnalysis_accessor_storage$3 = /* @__PURE__ */ new WeakMap(), _AnalysisDeltaGroup = class AnalysisDeltaGroup extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _analysis_accessor_storage$1, {}); + _classPrivateFieldInitSpec(this, _entityId_accessor_storage$1, ""); + _classPrivateFieldInitSpec(this, _canShowDeltaAnalysis_accessor_storage$3, false); + } + get analysis() { + return _classPrivateFieldGet2(_analysis_accessor_storage$1, this); + } + set analysis(value) { + _classPrivateFieldSet2(_analysis_accessor_storage$1, this, value); + } + get entityId() { + return _classPrivateFieldGet2(_entityId_accessor_storage$1, this); + } + set entityId(value) { + _classPrivateFieldSet2(_entityId_accessor_storage$1, this, value); + } + get canShowDeltaAnalysis() { + return _classPrivateFieldGet2(_canShowDeltaAnalysis_accessor_storage$3, this); + } + set canShowDeltaAnalysis(value) { + _classPrivateFieldSet2(_canShowDeltaAnalysis_accessor_storage$3, this, value); + } + _emit(key, value) { + this.dispatchEvent(new CustomEvent("dp-group-analysis-change", { + detail: { + entityId: this.entityId, + key, + value + }, + bubbles: true, + composed: true + })); + } + _onGroupChange(e) { + this._emit("show_delta_analysis", e.detail.checked); + } + _onCheckbox(key, e) { + this._emit(key, e.target.checked); + } + render() { + const a = this.analysis; + const isOpen = a.show_delta_analysis && this.canShowDeltaAnalysis; + return b`
${msg( - "Select a date window tab to enable delta analysis." - )}${msg("Select a date window tab to enable delta analysis.")} ` : A}
`; - } - } - _init$A = __decoratorStart$A(_a$A); - _analysis$1 = /* @__PURE__ */ new WeakMap(); - _entityId$1 = /* @__PURE__ */ new WeakMap(); - _canShowDeltaAnalysis$3 = /* @__PURE__ */ new WeakMap(); - __decorateElement$A(_init$A, 4, "analysis", _analysis_dec$1, AnalysisDeltaGroup, _analysis$1); - __decorateElement$A(_init$A, 4, "entityId", _entityId_dec$1, AnalysisDeltaGroup, _entityId$1); - __decorateElement$A(_init$A, 4, "canShowDeltaAnalysis", _canShowDeltaAnalysis_dec$3, AnalysisDeltaGroup, _canShowDeltaAnalysis$3); - AnalysisDeltaGroup = __decorateElement$A(_init$A, 0, "AnalysisDeltaGroup", _AnalysisDeltaGroup_decorators, AnalysisDeltaGroup); - __publicField$I(AnalysisDeltaGroup, "styles", [sharedStyles, styles$G]); - __runInitializers$A(_init$A, 1, AnalysisDeltaGroup); - customElements.define("analysis-delta-group", AnalysisDeltaGroup); - const styles$F = i$5` + } + }, _defineProperty(_AnalysisDeltaGroup, "styles", [sharedStyles, styles$42]), _AnalysisDeltaGroup); + __decorate([n$1({ type: Object })], AnalysisDeltaGroup.prototype, "analysis", null); + __decorate([n$1({ + type: String, + attribute: "entity-id" + })], AnalysisDeltaGroup.prototype, "entityId", null); + __decorate([n$1({ + type: Boolean, + attribute: "can-show-delta-analysis" + })], AnalysisDeltaGroup.prototype, "canShowDeltaAnalysis", null); + AnalysisDeltaGroup = __decorate([localized()], AnalysisDeltaGroup); + customElements.define("analysis-delta-group", AnalysisDeltaGroup); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/sidebar-options/sections/sidebar-datapoints-section.styles.ts + var styles$41 = i$5` :host { display: block; } `; - const styles$E = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/display/sidebar-options-section/sidebar-options-section.styles.ts + var styles$40 = i$5` :host { display: block; --dp-spacing-xs: calc(var(--spacing, 8px) * 0.5); @@ -17579,7 +17828,9 @@ ${content.alert}` : "", gap: var(--dp-spacing-sm); } `; - const styles$D = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/display/sidebar-section-header/sidebar-section-header.styles.ts + var styles$39 = i$5` :host { display: block; } @@ -17637,87 +17888,66 @@ ${content.alert}` : "", transform: rotate(180deg); } `; - var __create$z = Object.create; - var __defProp$H = Object.defineProperty; - var __getOwnPropDesc$z = Object.getOwnPropertyDescriptor; - var __knownSymbol$z = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$z = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$H = (obj, key, value) => key in obj ? __defProp$H(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$z = (base) => [, , , __create$z(base?.[__knownSymbol$z("metadata")] ?? null)]; - var __decoratorStrings$z = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$z = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$z("Function expected") : fn; - var __decoratorContext$z = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$z[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$z("Already initialized") : fns.push(__expectFn$z(fn || null)) }); - var __decoratorMetadata$z = (array, target) => __defNormalProp$H(target, __knownSymbol$z("metadata"), array[3]); - var __runInitializers$z = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$z = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$z[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$z({ get [name]() { - return __privateGet$y(this, extra); - }, set [name](x2) { - return __privateSet$y(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$z(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$z(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$z("Object expected"); - else __expectFn$z(fn = it.get) && (desc.get = fn), __expectFn$z(fn = it.set) && (desc.set = fn), __expectFn$z(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$H(target, name, desc), target; - }; - var __publicField$H = (obj, key, value) => __defNormalProp$H(obj, key + "", value); - var __accessCheck$y = (obj, member, msg2) => member.has(obj) || __typeError$z("Cannot " + msg2); - var __privateGet$y = (obj, member, getter) => (__accessCheck$y(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$y = (obj, member, value) => member.has(obj) ? __typeError$z("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$y = (obj, member, value, setter) => (__accessCheck$y(obj, member, "write to private field"), member.set(obj, value), value); - var _open_dec$7, _collapsible_dec$5, _subtitle_dec$1, _title_dec$1, _a$z, _init$z, _title$1, _subtitle$1, _collapsible$5, _open$7; - class SidebarSectionHeader extends (_a$z = i$2, _title_dec$1 = [n({ type: String })], _subtitle_dec$1 = [n({ type: String })], _collapsible_dec$5 = [n({ type: Boolean })], _open_dec$7 = [n({ type: Boolean })], _a$z) { - constructor() { - super(...arguments); - __privateAdd$y(this, _title$1, __runInitializers$z(_init$z, 8, this, "")), __runInitializers$z(_init$z, 11, this); - __privateAdd$y(this, _subtitle$1, __runInitializers$z(_init$z, 12, this, "")), __runInitializers$z(_init$z, 15, this); - __privateAdd$y(this, _collapsible$5, __runInitializers$z(_init$z, 16, this, false)), __runInitializers$z(_init$z, 19, this); - __privateAdd$y(this, _open$7, __runInitializers$z(_init$z, 20, this, true)), __runInitializers$z(_init$z, 23, this); - } - _emitToggle() { - this.dispatchEvent( - new CustomEvent("dp-section-toggle", { bubbles: true, composed: true }) - ); - } - _onHeaderClick() { - if (!this.collapsible) { - return; - } - this._emitToggle(); - } - _onHeaderKeydown(e2) { - if (!this.collapsible) { - return; - } - if (e2.key !== "Enter" && e2.key !== " ") { - return; - } - e2.preventDefault(); - this._emitToggle(); - } - _onButtonClick(e2) { - e2.stopPropagation(); - this._emitToggle(); - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/display/sidebar-section-header/sidebar-section-header.ts + var _title_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _subtitle_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _collapsible_accessor_storage$5 = /* @__PURE__ */ new WeakMap(); + var _open_accessor_storage$7 = /* @__PURE__ */ new WeakMap(); + var SidebarSectionHeader = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _title_accessor_storage$1, ""); + _classPrivateFieldInitSpec(this, _subtitle_accessor_storage$1, ""); + _classPrivateFieldInitSpec(this, _collapsible_accessor_storage$5, false); + _classPrivateFieldInitSpec(this, _open_accessor_storage$7, true); + } + get title() { + return _classPrivateFieldGet2(_title_accessor_storage$1, this); + } + set title(value) { + _classPrivateFieldSet2(_title_accessor_storage$1, this, value); + } + get subtitle() { + return _classPrivateFieldGet2(_subtitle_accessor_storage$1, this); + } + set subtitle(value) { + _classPrivateFieldSet2(_subtitle_accessor_storage$1, this, value); + } + get collapsible() { + return _classPrivateFieldGet2(_collapsible_accessor_storage$5, this); + } + set collapsible(value) { + _classPrivateFieldSet2(_collapsible_accessor_storage$5, this, value); + } + get open() { + return _classPrivateFieldGet2(_open_accessor_storage$7, this); + } + set open(value) { + _classPrivateFieldSet2(_open_accessor_storage$7, this, value); + } + _emitToggle() { + this.dispatchEvent(new CustomEvent("dp-section-toggle", { + bubbles: true, + composed: true + })); + } + _onHeaderClick() { + if (!this.collapsible) return; + this._emitToggle(); + } + _onHeaderKeydown(e) { + if (!this.collapsible) return; + if (e.key !== "Enter" && e.key !== " ") return; + e.preventDefault(); + this._emitToggle(); + } + _onButtonClick(e) { + e.stopPropagation(); + this._emitToggle(); + } + render() { + return b` ` : A}
`; - } - } - _init$z = __decoratorStart$z(_a$z); - _title$1 = /* @__PURE__ */ new WeakMap(); - _subtitle$1 = /* @__PURE__ */ new WeakMap(); - _collapsible$5 = /* @__PURE__ */ new WeakMap(); - _open$7 = /* @__PURE__ */ new WeakMap(); - __decorateElement$z(_init$z, 4, "title", _title_dec$1, SidebarSectionHeader, _title$1); - __decorateElement$z(_init$z, 4, "subtitle", _subtitle_dec$1, SidebarSectionHeader, _subtitle$1); - __decorateElement$z(_init$z, 4, "collapsible", _collapsible_dec$5, SidebarSectionHeader, _collapsible$5); - __decorateElement$z(_init$z, 4, "open", _open_dec$7, SidebarSectionHeader, _open$7); - __decoratorMetadata$z(_init$z, SidebarSectionHeader); - __publicField$H(SidebarSectionHeader, "styles", styles$D); - customElements.define("sidebar-section-header", SidebarSectionHeader); - var __create$y = Object.create; - var __defProp$G = Object.defineProperty; - var __getOwnPropDesc$y = Object.getOwnPropertyDescriptor; - var __knownSymbol$y = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$y = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$G = (obj, key, value) => key in obj ? __defProp$G(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$y = (base) => [, , , __create$y(base?.[__knownSymbol$y("metadata")] ?? null)]; - var __decoratorStrings$y = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$y = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$y("Function expected") : fn; - var __decoratorContext$y = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$y[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$y("Already initialized") : fns.push(__expectFn$y(fn || null)) }); - var __decoratorMetadata$y = (array, target) => __defNormalProp$G(target, __knownSymbol$y("metadata"), array[3]); - var __runInitializers$y = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$y = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$y[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$y({ get [name]() { - return __privateGet$x(this, extra); - }, set [name](x2) { - return __privateSet$x(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$y(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$y(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$y("Object expected"); - else __expectFn$y(fn = it.get) && (desc.get = fn), __expectFn$y(fn = it.set) && (desc.set = fn), __expectFn$y(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$G(target, name, desc), target; - }; - var __publicField$G = (obj, key, value) => __defNormalProp$G(obj, key + "", value); - var __accessCheck$x = (obj, member, msg2) => member.has(obj) || __typeError$y("Cannot " + msg2); - var __privateGet$x = (obj, member, getter) => (__accessCheck$x(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$x = (obj, member, value) => member.has(obj) ? __typeError$y("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$x = (obj, member, value, setter) => (__accessCheck$x(obj, member, "write to private field"), member.set(obj, value), value); - var _open_dec$6, _collapsible_dec$4, _subtitle_dec, _title_dec, _a$y, _init$y, _title, _subtitle, _collapsible$4, _open$6; - class SidebarOptionsSection extends (_a$y = i$2, _title_dec = [n({ type: String })], _subtitle_dec = [n({ type: String })], _collapsible_dec$4 = [n({ type: Boolean })], _open_dec$6 = [n({ type: Boolean })], _a$y) { - constructor() { - super(...arguments); - __privateAdd$x(this, _title, __runInitializers$y(_init$y, 8, this, "")), __runInitializers$y(_init$y, 11, this); - __privateAdd$x(this, _subtitle, __runInitializers$y(_init$y, 12, this, "")), __runInitializers$y(_init$y, 15, this); - __privateAdd$x(this, _collapsible$4, __runInitializers$y(_init$y, 16, this, false)), __runInitializers$y(_init$y, 19, this); - __privateAdd$x(this, _open$6, __runInitializers$y(_init$y, 20, this, true)), __runInitializers$y(_init$y, 23, this); - } - _onToggle(e2) { - e2.stopPropagation(); - this.open = !this.open; - this.dispatchEvent( - new CustomEvent("dp-section-toggle", { - detail: { open: this.open }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b` + } + }; + _defineProperty(SidebarSectionHeader, "styles", styles$39); + __decorate([n$1({ type: String })], SidebarSectionHeader.prototype, "title", null); + __decorate([n$1({ type: String })], SidebarSectionHeader.prototype, "subtitle", null); + __decorate([n$1({ type: Boolean })], SidebarSectionHeader.prototype, "collapsible", null); + __decorate([n$1({ type: Boolean })], SidebarSectionHeader.prototype, "open", null); + customElements.define("sidebar-section-header", SidebarSectionHeader); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/display/sidebar-options-section/sidebar-options-section.ts + var _title_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _subtitle_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _collapsible_accessor_storage$4 = /* @__PURE__ */ new WeakMap(); + var _open_accessor_storage$6 = /* @__PURE__ */ new WeakMap(); + var SidebarOptionsSection = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _title_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _subtitle_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _collapsible_accessor_storage$4, false); + _classPrivateFieldInitSpec(this, _open_accessor_storage$6, true); + } + get title() { + return _classPrivateFieldGet2(_title_accessor_storage, this); + } + set title(value) { + _classPrivateFieldSet2(_title_accessor_storage, this, value); + } + get subtitle() { + return _classPrivateFieldGet2(_subtitle_accessor_storage, this); + } + set subtitle(value) { + _classPrivateFieldSet2(_subtitle_accessor_storage, this, value); + } + get collapsible() { + return _classPrivateFieldGet2(_collapsible_accessor_storage$4, this); + } + set collapsible(value) { + _classPrivateFieldSet2(_collapsible_accessor_storage$4, this, value); + } + get open() { + return _classPrivateFieldGet2(_open_accessor_storage$6, this); + } + set open(value) { + _classPrivateFieldSet2(_open_accessor_storage$6, this, value); + } + _onToggle(e) { + e.stopPropagation(); + this.open = !this.open; + this.dispatchEvent(new CustomEvent("dp-section-toggle", { + detail: { open: this.open }, + bubbles: true, + composed: true + })); + } + render() { + return b`
`}
`; - } - } - _init$y = __decoratorStart$y(_a$y); - _title = /* @__PURE__ */ new WeakMap(); - _subtitle = /* @__PURE__ */ new WeakMap(); - _collapsible$4 = /* @__PURE__ */ new WeakMap(); - _open$6 = /* @__PURE__ */ new WeakMap(); - __decorateElement$y(_init$y, 4, "title", _title_dec, SidebarOptionsSection, _title); - __decorateElement$y(_init$y, 4, "subtitle", _subtitle_dec, SidebarOptionsSection, _subtitle); - __decorateElement$y(_init$y, 4, "collapsible", _collapsible_dec$4, SidebarOptionsSection, _collapsible$4); - __decorateElement$y(_init$y, 4, "open", _open_dec$6, SidebarOptionsSection, _open$6); - __decoratorMetadata$y(_init$y, SidebarOptionsSection); - __publicField$G(SidebarOptionsSection, "styles", styles$E); - customElements.define("sidebar-options-section", SidebarOptionsSection); - const styles$C = i$5` + } + }; + _defineProperty(SidebarOptionsSection, "styles", styles$40); + __decorate([n$1({ type: String })], SidebarOptionsSection.prototype, "title", null); + __decorate([n$1({ type: String })], SidebarOptionsSection.prototype, "subtitle", null); + __decorate([n$1({ type: Boolean })], SidebarOptionsSection.prototype, "collapsible", null); + __decorate([n$1({ type: Boolean })], SidebarOptionsSection.prototype, "open", null); + customElements.define("sidebar-options-section", SidebarOptionsSection); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/form/radio-group/radio-group.styles.ts + var styles$38 = i$5` :host { display: block; } @@ -17874,75 +18076,49 @@ ${content.alert}` : "", cursor: pointer; } `; - var __create$x = Object.create; - var __defProp$F = Object.defineProperty; - var __getOwnPropDesc$x = Object.getOwnPropertyDescriptor; - var __knownSymbol$x = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$x = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$F = (obj, key, value) => key in obj ? __defProp$F(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$x = (base) => [, , , __create$x(base?.[__knownSymbol$x("metadata")] ?? null)]; - var __decoratorStrings$x = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$x = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$x("Function expected") : fn; - var __decoratorContext$x = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$x[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$x("Already initialized") : fns.push(__expectFn$x(fn || null)) }); - var __decoratorMetadata$x = (array, target) => __defNormalProp$F(target, __knownSymbol$x("metadata"), array[3]); - var __runInitializers$x = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$x = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$x[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$x({ get [name]() { - return __privateGet$w(this, extra); - }, set [name](x2) { - return __privateSet$w(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$x(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$x(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$x("Object expected"); - else __expectFn$x(fn = it.get) && (desc.get = fn), __expectFn$x(fn = it.set) && (desc.set = fn), __expectFn$x(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$F(target, name, desc), target; - }; - var __publicField$F = (obj, key, value) => __defNormalProp$F(obj, key + "", value); - var __accessCheck$w = (obj, member, msg2) => member.has(obj) || __typeError$x("Cannot " + msg2); - var __privateGet$w = (obj, member, getter) => (__accessCheck$w(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$w = (obj, member, value) => member.has(obj) ? __typeError$x("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$w = (obj, member, value, setter) => (__accessCheck$w(obj, member, "write to private field"), member.set(obj, value), value); - var _options_dec$1, _value_dec$5, _name_dec$2, _a$x, _init$x, _name$2, _value$5, _options$1; - class RadioGroup extends (_a$x = i$2, _name_dec$2 = [n({ type: String })], _value_dec$5 = [n({ type: String })], _options_dec$1 = [n({ type: Array })], _a$x) { - constructor() { - super(...arguments); - __privateAdd$w(this, _name$2, __runInitializers$x(_init$x, 8, this, "")), __runInitializers$x(_init$x, 11, this); - __privateAdd$w(this, _value$5, __runInitializers$x(_init$x, 12, this, "")), __runInitializers$x(_init$x, 15, this); - __privateAdd$w(this, _options$1, __runInitializers$x(_init$x, 16, this, [])), __runInitializers$x(_init$x, 19, this); - } - _onChange(e2) { - const input = e2.target; - this.dispatchEvent( - new CustomEvent("dp-radio-change", { - detail: { value: input.value }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/form/radio-group/radio-group.ts + var _name_accessor_storage$2 = /* @__PURE__ */ new WeakMap(); + var _value_accessor_storage$5 = /* @__PURE__ */ new WeakMap(); + var _options_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var RadioGroup = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _name_accessor_storage$2, ""); + _classPrivateFieldInitSpec(this, _value_accessor_storage$5, ""); + _classPrivateFieldInitSpec(this, _options_accessor_storage$1, []); + } + get name() { + return _classPrivateFieldGet2(_name_accessor_storage$2, this); + } + set name(value) { + _classPrivateFieldSet2(_name_accessor_storage$2, this, value); + } + get value() { + return _classPrivateFieldGet2(_value_accessor_storage$5, this); + } + set value(value) { + _classPrivateFieldSet2(_value_accessor_storage$5, this, value); + } + get options() { + return _classPrivateFieldGet2(_options_accessor_storage$1, this); + } + set options(value) { + _classPrivateFieldSet2(_options_accessor_storage$1, this, value); + } + _onChange(e) { + const input = e.target; + this.dispatchEvent(new CustomEvent("dp-radio-change", { + detail: { value: input.value }, + bubbles: true, + composed: true + })); + } + render() { + return b`
- ${this.options.map( - (opt) => b` + ${this.options.map((opt) => b` - ` - )} + `)}
`; - } - } - _init$x = __decoratorStart$x(_a$x); - _name$2 = /* @__PURE__ */ new WeakMap(); - _value$5 = /* @__PURE__ */ new WeakMap(); - _options$1 = /* @__PURE__ */ new WeakMap(); - __decorateElement$x(_init$x, 4, "name", _name_dec$2, RadioGroup, _name$2); - __decorateElement$x(_init$x, 4, "value", _value_dec$5, RadioGroup, _value$5); - __decorateElement$x(_init$x, 4, "options", _options_dec$1, RadioGroup, _options$1); - __decoratorMetadata$x(_init$x, RadioGroup); - __publicField$F(RadioGroup, "styles", styles$C); - customElements.define("radio-group", RadioGroup); - var __create$w = Object.create; - var __defProp$E = Object.defineProperty; - var __getOwnPropDesc$w = Object.getOwnPropertyDescriptor; - var __knownSymbol$w = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$w = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$E = (obj, key, value) => key in obj ? __defProp$E(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __name$c = (target, value) => __defProp$E(target, "name", { value, configurable: true }); - var __decoratorStart$w = (base) => [, , , __create$w(base?.[__knownSymbol$w("metadata")] ?? null)]; - var __decoratorStrings$w = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$w = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$w("Function expected") : fn; - var __decoratorContext$w = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$w[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$w("Already initialized") : fns.push(__expectFn$w(fn || null)) }); - var __decoratorMetadata$w = (array, target) => __defNormalProp$E(target, __knownSymbol$w("metadata"), array[3]); - var __runInitializers$w = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$w = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = !!(flags & 8), p2 = !!(flags & 16); - var j2 = k2 > 3 ? array.length + 1 : k2 ? s2 ? 1 : 2 : 0, key = __decoratorStrings$w[k2 + 5]; - var initializers = k2 > 3 && (array[j2 - 1] = []), extraInitializers = array[j2] || (array[j2] = []); - var desc = k2 && (!p2 && !s2 && (target = target.prototype), k2 < 5 && (k2 > 3 || !p2) && __getOwnPropDesc$w(k2 < 4 ? target : { get [name]() { - return __privateGet$v(this, extra); - }, set [name](x2) { - return __privateSet$v(this, extra, x2); - } }, name)); - k2 ? p2 && k2 < 4 && __name$c(extra, (k2 > 2 ? "set " : k2 > 1 ? "get " : "") + name) : __name$c(target, name); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$w(k2, name, done = {}, array[3], extraInitializers); - if (k2) { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: p2 ? (x2) => __privateIn$b(target, x2) : (x2) => name in x2 }; - if (k2 ^ 3) access.get = p2 ? (x2) => (k2 ^ 1 ? __privateGet$v : __privateMethod$b)(x2, target, k2 ^ 4 ? extra : desc.get) : (x2) => x2[name]; - if (k2 > 2) access.set = p2 ? (x2, y2) => __privateSet$v(x2, target, y2, k2 ^ 4 ? extra : desc.set) : (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])(k2 ? k2 < 4 ? p2 ? extra : desc[key] : k2 > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; - if (k2 ^ 4 || it === void 0) __expectFn$w(it) && (k2 > 4 ? initializers.unshift(it) : k2 ? p2 ? extra = it : desc[key] = it : target = it); - else if (typeof it !== "object" || it === null) __typeError$w("Object expected"); - else __expectFn$w(fn = it.get) && (desc.get = fn), __expectFn$w(fn = it.set) && (desc.set = fn), __expectFn$w(fn = it.init) && initializers.unshift(fn); - } - return k2 || __decoratorMetadata$w(array, target), desc && __defProp$E(target, name, desc), p2 ? k2 ^ 4 ? extra : desc : target; - }; - var __publicField$E = (obj, key, value) => __defNormalProp$E(obj, key + "", value); - var __accessCheck$v = (obj, member, msg2) => member.has(obj) || __typeError$w("Cannot " + msg2); - var __privateIn$b = (member, obj) => Object(obj) !== obj ? __typeError$w('Cannot use the "in" operator on this value') : member.has(obj); - var __privateGet$v = (obj, member, getter) => (__accessCheck$v(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); - var __privateAdd$v = (obj, member, value) => member.has(obj) ? __typeError$w("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$v = (obj, member, value, setter) => (__accessCheck$v(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); - var __privateMethod$b = (obj, member, method) => (__accessCheck$v(obj, member, "access private method"), method); - var _open_dec$5, _collapsible_dec$3, _datapointScope_dec$2, _a$w, _SidebarDatapointsSection_decorators, _init$w, _datapointScope$2, _collapsible$3, _open$5; - const DATAPOINT_SCOPE_OPTIONS = [ - { value: "linked", label: "Linked to selected targets" }, - { value: "all", label: "All datapoints" }, - { value: "hidden", label: "Hide datapoints" } - ]; - _SidebarDatapointsSection_decorators = [localized()]; - class SidebarDatapointsSection extends (_a$w = i$2, _datapointScope_dec$2 = [n({ type: String, attribute: "datapoint-scope" })], _collapsible_dec$3 = [n({ type: Boolean })], _open_dec$5 = [n({ type: Boolean })], _a$w) { - constructor() { - super(...arguments); - __privateAdd$v(this, _datapointScope$2, __runInitializers$w(_init$w, 8, this, "linked")), __runInitializers$w(_init$w, 11, this); - __privateAdd$v(this, _collapsible$3, __runInitializers$w(_init$w, 12, this, false)), __runInitializers$w(_init$w, 15, this); - __privateAdd$v(this, _open$5, __runInitializers$w(_init$w, 16, this, true)), __runInitializers$w(_init$w, 19, this); - } - _onScopeChange(e2) { - this.dispatchEvent( - new CustomEvent("dp-scope-change", { - detail: { value: e2.detail.value }, - bubbles: true, - composed: true - }) - ); - } - _localizedOptions(options) { - return options.map((opt) => ({ - ...opt, - label: msg(opt.label) - })); - } - render() { - return b` + } + }; + _defineProperty(RadioGroup, "styles", styles$38); + __decorate([n$1({ type: String })], RadioGroup.prototype, "name", null); + __decorate([n$1({ type: String })], RadioGroup.prototype, "value", null); + __decorate([n$1({ type: Array })], RadioGroup.prototype, "options", null); + customElements.define("radio-group", RadioGroup); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/sidebar-options/sections/sidebar-datapoints-section.ts + var _SidebarDatapointsSection, _datapointScope_accessor_storage$2, _collapsible_accessor_storage$3, _open_accessor_storage$5; + var DATAPOINT_SCOPE_OPTIONS = [ + { + value: "linked", + label: "Linked to selected targets" + }, + { + value: "all", + label: "All datapoints" + }, + { + value: "hidden", + label: "Hide datapoints" + } + ]; + var SidebarDatapointsSection = (_datapointScope_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _collapsible_accessor_storage$3 = /* @__PURE__ */ new WeakMap(), _open_accessor_storage$5 = /* @__PURE__ */ new WeakMap(), _SidebarDatapointsSection = class SidebarDatapointsSection extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _datapointScope_accessor_storage$2, "linked"); + _classPrivateFieldInitSpec(this, _collapsible_accessor_storage$3, false); + _classPrivateFieldInitSpec(this, _open_accessor_storage$5, true); + } + get datapointScope() { + return _classPrivateFieldGet2(_datapointScope_accessor_storage$2, this); + } + set datapointScope(value) { + _classPrivateFieldSet2(_datapointScope_accessor_storage$2, this, value); + } + get collapsible() { + return _classPrivateFieldGet2(_collapsible_accessor_storage$3, this); + } + set collapsible(value) { + _classPrivateFieldSet2(_collapsible_accessor_storage$3, this, value); + } + get open() { + return _classPrivateFieldGet2(_open_accessor_storage$5, this); + } + set open(value) { + _classPrivateFieldSet2(_open_accessor_storage$5, this, value); + } + _onScopeChange(e) { + this.dispatchEvent(new CustomEvent("dp-scope-change", { + detail: { value: e.detail.value }, + bubbles: true, + composed: true + })); + } + _localizedOptions(options) { + return options.map((opt) => ({ + ...opt, + label: msg(opt.label) + })); + } + render() { + return b` @@ -18066,25 +18211,26 @@ ${content.alert}` : "", > `; - } - } - _init$w = __decoratorStart$w(_a$w); - _datapointScope$2 = /* @__PURE__ */ new WeakMap(); - _collapsible$3 = /* @__PURE__ */ new WeakMap(); - _open$5 = /* @__PURE__ */ new WeakMap(); - __decorateElement$w(_init$w, 4, "datapointScope", _datapointScope_dec$2, SidebarDatapointsSection, _datapointScope$2); - __decorateElement$w(_init$w, 4, "collapsible", _collapsible_dec$3, SidebarDatapointsSection, _collapsible$3); - __decorateElement$w(_init$w, 4, "open", _open_dec$5, SidebarDatapointsSection, _open$5); - SidebarDatapointsSection = __decorateElement$w(_init$w, 0, "SidebarDatapointsSection", _SidebarDatapointsSection_decorators, SidebarDatapointsSection); - __publicField$E(SidebarDatapointsSection, "styles", styles$F); - __runInitializers$w(_init$w, 1, SidebarDatapointsSection); - customElements.define("sidebar-datapoints-section", SidebarDatapointsSection); - const styles$B = i$5` + } + }, _defineProperty(_SidebarDatapointsSection, "styles", styles$41), _SidebarDatapointsSection); + __decorate([n$1({ + type: String, + attribute: "datapoint-scope" + })], SidebarDatapointsSection.prototype, "datapointScope", null); + __decorate([n$1({ type: Boolean })], SidebarDatapointsSection.prototype, "collapsible", null); + __decorate([n$1({ type: Boolean })], SidebarDatapointsSection.prototype, "open", null); + SidebarDatapointsSection = __decorate([localized()], SidebarDatapointsSection); + customElements.define("sidebar-datapoints-section", SidebarDatapointsSection); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/sidebar-options/sections/sidebar-datapoint-display-section.styles.ts + var styles$37 = i$5` :host { display: block; } `; - const styles$A = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/form/checkbox-list/checkbox-list.styles.ts + var styles$36 = i$5` :host { display: block; } @@ -18104,72 +18250,35 @@ ${content.alert}` : "", cursor: pointer; } `; - var __create$v = Object.create; - var __defProp$D = Object.defineProperty; - var __getOwnPropDesc$v = Object.getOwnPropertyDescriptor; - var __knownSymbol$v = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$v = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$D = (obj, key, value) => key in obj ? __defProp$D(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$v = (base) => [, , , __create$v(base?.[__knownSymbol$v("metadata")] ?? null)]; - var __decoratorStrings$v = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$v = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$v("Function expected") : fn; - var __decoratorContext$v = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$v[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$v("Already initialized") : fns.push(__expectFn$v(fn || null)) }); - var __decoratorMetadata$v = (array, target) => __defNormalProp$D(target, __knownSymbol$v("metadata"), array[3]); - var __runInitializers$v = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$v = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$v[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$v({ get [name]() { - return __privateGet$u(this, extra); - }, set [name](x2) { - return __privateSet$u(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$v(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$v(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$v("Object expected"); - else __expectFn$v(fn = it.get) && (desc.get = fn), __expectFn$v(fn = it.set) && (desc.set = fn), __expectFn$v(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$D(target, name, desc), target; - }; - var __publicField$D = (obj, key, value) => __defNormalProp$D(obj, key + "", value); - var __accessCheck$u = (obj, member, msg2) => member.has(obj) || __typeError$v("Cannot " + msg2); - var __privateGet$u = (obj, member, getter) => (__accessCheck$u(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$u = (obj, member, value) => member.has(obj) ? __typeError$v("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$u = (obj, member, value, setter) => (__accessCheck$u(obj, member, "write to private field"), member.set(obj, value), value); - var _items_dec, _a$v, _init$v, _items; - class CheckboxList extends (_a$v = i$2, _items_dec = [n({ type: Array })], _a$v) { - constructor() { - super(...arguments); - __privateAdd$u(this, _items, __runInitializers$v(_init$v, 8, this, [])), __runInitializers$v(_init$v, 11, this); - } - _onChange(e2) { - const input = e2.target; - this.dispatchEvent( - new CustomEvent("dp-item-change", { - detail: { name: input.name, checked: input.checked }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/form/checkbox-list/checkbox-list.ts + var _items_accessor_storage = /* @__PURE__ */ new WeakMap(); + var CheckboxList = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _items_accessor_storage, []); + } + get items() { + return _classPrivateFieldGet2(_items_accessor_storage, this); + } + set items(value) { + _classPrivateFieldSet2(_items_accessor_storage, this, value); + } + _onChange(e) { + const input = e.target; + this.dispatchEvent(new CustomEvent("dp-item-change", { + detail: { + name: input.name, + checked: input.checked + }, + bubbles: true, + composed: true + })); + } + render() { + return b`
- ${this.items.map( - (item) => b` + ${this.items.map((item) => b` - ` - )} + `)}
`; - } - } - _init$v = __decoratorStart$v(_a$v); - _items = /* @__PURE__ */ new WeakMap(); - __decorateElement$v(_init$v, 4, "items", _items_dec, CheckboxList, _items); - __decoratorMetadata$v(_init$v, CheckboxList); - __publicField$D(CheckboxList, "styles", styles$A); - customElements.define("checkbox-list", CheckboxList); - var __create$u = Object.create; - var __defProp$C = Object.defineProperty; - var __getOwnPropDesc$u = Object.getOwnPropertyDescriptor; - var __knownSymbol$u = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$u = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$C = (obj, key, value) => key in obj ? __defProp$C(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __name$b = (target, value) => __defProp$C(target, "name", { value, configurable: true }); - var __decoratorStart$u = (base) => [, , , __create$u(base?.[__knownSymbol$u("metadata")] ?? null)]; - var __decoratorStrings$u = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$u = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$u("Function expected") : fn; - var __decoratorContext$u = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$u[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$u("Already initialized") : fns.push(__expectFn$u(fn || null)) }); - var __decoratorMetadata$u = (array, target) => __defNormalProp$C(target, __knownSymbol$u("metadata"), array[3]); - var __runInitializers$u = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$u = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = !!(flags & 8), p2 = !!(flags & 16); - var j2 = k2 > 3 ? array.length + 1 : k2 ? s2 ? 1 : 2 : 0, key = __decoratorStrings$u[k2 + 5]; - var initializers = k2 > 3 && (array[j2 - 1] = []), extraInitializers = array[j2] || (array[j2] = []); - var desc = k2 && (!p2 && !s2 && (target = target.prototype), k2 < 5 && (k2 > 3 || !p2) && __getOwnPropDesc$u(k2 < 4 ? target : { get [name]() { - return __privateGet$t(this, extra); - }, set [name](x2) { - return __privateSet$t(this, extra, x2); - } }, name)); - k2 ? p2 && k2 < 4 && __name$b(extra, (k2 > 2 ? "set " : k2 > 1 ? "get " : "") + name) : __name$b(target, name); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$u(k2, name, done = {}, array[3], extraInitializers); - if (k2) { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: p2 ? (x2) => __privateIn$a(target, x2) : (x2) => name in x2 }; - if (k2 ^ 3) access.get = p2 ? (x2) => (k2 ^ 1 ? __privateGet$t : __privateMethod$a)(x2, target, k2 ^ 4 ? extra : desc.get) : (x2) => x2[name]; - if (k2 > 2) access.set = p2 ? (x2, y2) => __privateSet$t(x2, target, y2, k2 ^ 4 ? extra : desc.set) : (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])(k2 ? k2 < 4 ? p2 ? extra : desc[key] : k2 > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; - if (k2 ^ 4 || it === void 0) __expectFn$u(it) && (k2 > 4 ? initializers.unshift(it) : k2 ? p2 ? extra = it : desc[key] = it : target = it); - else if (typeof it !== "object" || it === null) __typeError$u("Object expected"); - else __expectFn$u(fn = it.get) && (desc.get = fn), __expectFn$u(fn = it.set) && (desc.set = fn), __expectFn$u(fn = it.init) && initializers.unshift(fn); - } - return k2 || __decoratorMetadata$u(array, target), desc && __defProp$C(target, name, desc), p2 ? k2 ^ 4 ? extra : desc : target; - }; - var __publicField$C = (obj, key, value) => __defNormalProp$C(obj, key + "", value); - var __accessCheck$t = (obj, member, msg2) => member.has(obj) || __typeError$u("Cannot " + msg2); - var __privateIn$a = (member, obj) => Object(obj) !== obj ? __typeError$u('Cannot use the "in" operator on this value') : member.has(obj); - var __privateGet$t = (obj, member, getter) => (__accessCheck$t(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); - var __privateAdd$t = (obj, member, value) => member.has(obj) ? __typeError$u("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$t = (obj, member, value, setter) => (__accessCheck$t(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); - var __privateMethod$a = (obj, member, method) => (__accessCheck$t(obj, member, "access private method"), method); - var _open_dec$4, _collapsible_dec$2, _showLines_dec$2, _showIcons_dec$2, _a$u, _SidebarDatapointDisplaySection_decorators, _init$u, _showIcons$2, _showLines$2, _collapsible$2, _open$4; - _SidebarDatapointDisplaySection_decorators = [localized()]; - class SidebarDatapointDisplaySection extends (_a$u = i$2, _showIcons_dec$2 = [n({ type: Boolean, attribute: "show-icons" })], _showLines_dec$2 = [n({ type: Boolean, attribute: "show-lines" })], _collapsible_dec$2 = [n({ type: Boolean })], _open_dec$4 = [n({ type: Boolean })], _a$u) { - constructor() { - super(...arguments); - __privateAdd$t(this, _showIcons$2, __runInitializers$u(_init$u, 8, this, true)), __runInitializers$u(_init$u, 11, this); - __privateAdd$t(this, _showLines$2, __runInitializers$u(_init$u, 12, this, true)), __runInitializers$u(_init$u, 15, this); - __privateAdd$t(this, _collapsible$2, __runInitializers$u(_init$u, 16, this, false)), __runInitializers$u(_init$u, 19, this); - __privateAdd$t(this, _open$4, __runInitializers$u(_init$u, 20, this, true)), __runInitializers$u(_init$u, 23, this); - } - _onCheckboxChange(e2) { - const { name, checked } = e2.detail; - this.dispatchEvent( - new CustomEvent("dp-display-change", { - detail: { kind: name, value: checked }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b` + } + }; + _defineProperty(CheckboxList, "styles", styles$36); + __decorate([n$1({ type: Array })], CheckboxList.prototype, "items", null); + customElements.define("checkbox-list", CheckboxList); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/sidebar-options/sections/sidebar-datapoint-display-section.ts + var _SidebarDatapointDisplaySection, _showIcons_accessor_storage$2, _showLines_accessor_storage$2, _collapsible_accessor_storage$2, _open_accessor_storage$4; + var SidebarDatapointDisplaySection = (_showIcons_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _showLines_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _collapsible_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _open_accessor_storage$4 = /* @__PURE__ */ new WeakMap(), _SidebarDatapointDisplaySection = class SidebarDatapointDisplaySection extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _showIcons_accessor_storage$2, true); + _classPrivateFieldInitSpec(this, _showLines_accessor_storage$2, true); + _classPrivateFieldInitSpec(this, _collapsible_accessor_storage$2, false); + _classPrivateFieldInitSpec(this, _open_accessor_storage$4, true); + } + get showIcons() { + return _classPrivateFieldGet2(_showIcons_accessor_storage$2, this); + } + set showIcons(value) { + _classPrivateFieldSet2(_showIcons_accessor_storage$2, this, value); + } + get showLines() { + return _classPrivateFieldGet2(_showLines_accessor_storage$2, this); + } + set showLines(value) { + _classPrivateFieldSet2(_showLines_accessor_storage$2, this, value); + } + get collapsible() { + return _classPrivateFieldGet2(_collapsible_accessor_storage$2, this); + } + set collapsible(value) { + _classPrivateFieldSet2(_collapsible_accessor_storage$2, this, value); + } + get open() { + return _classPrivateFieldGet2(_open_accessor_storage$4, this); + } + set open(value) { + _classPrivateFieldSet2(_open_accessor_storage$4, this, value); + } + _onCheckboxChange(e) { + const { name, checked } = e.detail; + this.dispatchEvent(new CustomEvent("dp-display-change", { + detail: { + kind: name, + value: checked + }, + bubbles: true, + composed: true + })); + } + render() { + return b` `; - } - } - _init$u = __decoratorStart$u(_a$u); - _showIcons$2 = /* @__PURE__ */ new WeakMap(); - _showLines$2 = /* @__PURE__ */ new WeakMap(); - _collapsible$2 = /* @__PURE__ */ new WeakMap(); - _open$4 = /* @__PURE__ */ new WeakMap(); - __decorateElement$u(_init$u, 4, "showIcons", _showIcons_dec$2, SidebarDatapointDisplaySection, _showIcons$2); - __decorateElement$u(_init$u, 4, "showLines", _showLines_dec$2, SidebarDatapointDisplaySection, _showLines$2); - __decorateElement$u(_init$u, 4, "collapsible", _collapsible_dec$2, SidebarDatapointDisplaySection, _collapsible$2); - __decorateElement$u(_init$u, 4, "open", _open_dec$4, SidebarDatapointDisplaySection, _open$4); - SidebarDatapointDisplaySection = __decorateElement$u(_init$u, 0, "SidebarDatapointDisplaySection", _SidebarDatapointDisplaySection_decorators, SidebarDatapointDisplaySection); - __publicField$C(SidebarDatapointDisplaySection, "styles", styles$B); - __runInitializers$u(_init$u, 1, SidebarDatapointDisplaySection); - customElements.define( - "sidebar-datapoint-display-section", - SidebarDatapointDisplaySection - ); - const styles$z = i$5` + } + }, _defineProperty(_SidebarDatapointDisplaySection, "styles", styles$37), _SidebarDatapointDisplaySection); + __decorate([n$1({ + type: Boolean, + attribute: "show-icons" + })], SidebarDatapointDisplaySection.prototype, "showIcons", null); + __decorate([n$1({ + type: Boolean, + attribute: "show-lines" + })], SidebarDatapointDisplaySection.prototype, "showLines", null); + __decorate([n$1({ type: Boolean })], SidebarDatapointDisplaySection.prototype, "collapsible", null); + __decorate([n$1({ type: Boolean })], SidebarDatapointDisplaySection.prototype, "open", null); + SidebarDatapointDisplaySection = __decorate([localized()], SidebarDatapointDisplaySection); + customElements.define("sidebar-datapoint-display-section", SidebarDatapointDisplaySection); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/sidebar-options/sections/sidebar-analysis-section.styles.ts + var styles$35 = i$5` :host { display: block; } @@ -18331,154 +18406,136 @@ ${content.alert}` : "", font-weight: 600; } `; - var __create$t = Object.create; - var __defProp$B = Object.defineProperty; - var __getOwnPropDesc$t = Object.getOwnPropertyDescriptor; - var __knownSymbol$t = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$t = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$B = (obj, key, value) => key in obj ? __defProp$B(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __name$a = (target, value) => __defProp$B(target, "name", { value, configurable: true }); - var __decoratorStart$t = (base) => [, , , __create$t(base?.[__knownSymbol$t("metadata")] ?? null)]; - var __decoratorStrings$t = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$t = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$t("Function expected") : fn; - var __decoratorContext$t = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$t[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$t("Already initialized") : fns.push(__expectFn$t(fn || null)) }); - var __decoratorMetadata$t = (array, target) => __defNormalProp$B(target, __knownSymbol$t("metadata"), array[3]); - var __runInitializers$t = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$t = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = !!(flags & 8), p2 = !!(flags & 16); - var j2 = k2 > 3 ? array.length + 1 : k2 ? s2 ? 1 : 2 : 0, key = __decoratorStrings$t[k2 + 5]; - var initializers = k2 > 3 && (array[j2 - 1] = []), extraInitializers = array[j2] || (array[j2] = []); - var desc = k2 && (!p2 && !s2 && (target = target.prototype), k2 < 5 && (k2 > 3 || !p2) && __getOwnPropDesc$t(k2 < 4 ? target : { get [name]() { - return __privateGet$s(this, extra); - }, set [name](x2) { - return __privateSet$s(this, extra, x2); - } }, name)); - k2 ? p2 && k2 < 4 && __name$a(extra, (k2 > 2 ? "set " : k2 > 1 ? "get " : "") + name) : __name$a(target, name); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$t(k2, name, done = {}, array[3], extraInitializers); - if (k2) { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: p2 ? (x2) => __privateIn$9(target, x2) : (x2) => name in x2 }; - if (k2 ^ 3) access.get = p2 ? (x2) => (k2 ^ 1 ? __privateGet$s : __privateMethod$9)(x2, target, k2 ^ 4 ? extra : desc.get) : (x2) => x2[name]; - if (k2 > 2) access.set = p2 ? (x2, y2) => __privateSet$s(x2, target, y2, k2 ^ 4 ? extra : desc.set) : (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])(k2 ? k2 < 4 ? p2 ? extra : desc[key] : k2 > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; - if (k2 ^ 4 || it === void 0) __expectFn$t(it) && (k2 > 4 ? initializers.unshift(it) : k2 ? p2 ? extra = it : desc[key] = it : target = it); - else if (typeof it !== "object" || it === null) __typeError$t("Object expected"); - else __expectFn$t(fn = it.get) && (desc.get = fn), __expectFn$t(fn = it.set) && (desc.set = fn), __expectFn$t(fn = it.init) && initializers.unshift(fn); - } - return k2 || __decoratorMetadata$t(array, target), desc && __defProp$B(target, name, desc), p2 ? k2 ^ 4 ? extra : desc : target; - }; - var __publicField$B = (obj, key, value) => __defNormalProp$B(obj, key + "", value); - var __accessCheck$s = (obj, member, msg2) => member.has(obj) || __typeError$t("Cannot " + msg2); - var __privateIn$9 = (member, obj) => Object(obj) !== obj ? __typeError$t('Cannot use the "in" operator on this value') : member.has(obj); - var __privateGet$s = (obj, member, getter) => (__accessCheck$s(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); - var __privateAdd$s = (obj, member, value) => member.has(obj) ? __typeError$t("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$s = (obj, member, value, setter) => (__accessCheck$s(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); - var __privateMethod$9 = (obj, member, method) => (__accessCheck$s(obj, member, "access private method"), method); - var _open_dec$3, _collapsible_dec$1, _anyAnomaliesEnabled_dec$2, _showCorrelatedAnomalies_dec$2, _anomalyOverlapMode_dec$2, _a$t, _SidebarAnalysisSection_decorators, _init$t, _anomalyOverlapMode$2, _showCorrelatedAnomalies$2, _anyAnomaliesEnabled$2, _collapsible$1, _open$3; - const ANALYSIS_ANOMALY_OVERLAP_MODE_OPTIONS$1 = [ - { value: "all", label: "Show all anomalies" }, - { value: "only", label: "Overlaps only" } - ]; - _SidebarAnalysisSection_decorators = [localized()]; - class SidebarAnalysisSection extends (_a$t = i$2, _anomalyOverlapMode_dec$2 = [n({ type: String, attribute: "anomaly-overlap-mode" })], _showCorrelatedAnomalies_dec$2 = [n({ type: Boolean, attribute: "show-correlated-anomalies" })], _anyAnomaliesEnabled_dec$2 = [n({ type: Boolean, attribute: false })], _collapsible_dec$1 = [n({ type: Boolean })], _open_dec$3 = [n({ type: Boolean })], _a$t) { - constructor() { - super(...arguments); - __privateAdd$s(this, _anomalyOverlapMode$2, __runInitializers$t(_init$t, 8, this, "all")), __runInitializers$t(_init$t, 11, this); - __privateAdd$s(this, _showCorrelatedAnomalies$2, __runInitializers$t(_init$t, 12, this, false)), __runInitializers$t(_init$t, 15, this); - __privateAdd$s(this, _anyAnomaliesEnabled$2, __runInitializers$t(_init$t, 16, this, false)), __runInitializers$t(_init$t, 19, this); - __privateAdd$s(this, _collapsible$1, __runInitializers$t(_init$t, 20, this, false)), __runInitializers$t(_init$t, 23, this); - __privateAdd$s(this, _open$3, __runInitializers$t(_init$t, 24, this, true)), __runInitializers$t(_init$t, 27, this); - } - _localizedOptions(options) { - return options.map((opt) => ({ - ...opt, - label: msg(opt.label) - })); - } - _emitAnalysis(kind, value) { - this.dispatchEvent( - new CustomEvent("dp-analysis-change", { - detail: { kind, value }, - bubbles: true, - composed: true - }) - ); - } - _onAnomalyOverlapModeChange(e2) { - this._emitAnalysis("anomaly_overlap_mode", e2.detail.value); - } - _onCheckboxChange(e2) { - const { name, checked } = e2.detail; - this.dispatchEvent( - new CustomEvent("dp-display-change", { - detail: { kind: name, value: checked }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/molecules/sidebar-options/sections/sidebar-analysis-section.ts + var _SidebarAnalysisSection, _anomalyOverlapMode_accessor_storage$2, _showCorrelatedAnomal_accessor_storage$2, _anyAnomaliesEnabled_accessor_storage$2, _collapsible_accessor_storage$1, _open_accessor_storage$3; + var ANALYSIS_ANOMALY_OVERLAP_MODE_OPTIONS$1 = [{ + value: "all", + label: "Show all anomalies" + }, { + value: "only", + label: "Overlaps only" + }]; + var SidebarAnalysisSection = (_anomalyOverlapMode_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _showCorrelatedAnomal_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _anyAnomaliesEnabled_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _collapsible_accessor_storage$1 = /* @__PURE__ */ new WeakMap(), _open_accessor_storage$3 = /* @__PURE__ */ new WeakMap(), _SidebarAnalysisSection = class SidebarAnalysisSection extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _anomalyOverlapMode_accessor_storage$2, "all"); + _classPrivateFieldInitSpec(this, _showCorrelatedAnomal_accessor_storage$2, false); + _classPrivateFieldInitSpec(this, _anyAnomaliesEnabled_accessor_storage$2, false); + _classPrivateFieldInitSpec(this, _collapsible_accessor_storage$1, false); + _classPrivateFieldInitSpec(this, _open_accessor_storage$3, true); + } + get anomalyOverlapMode() { + return _classPrivateFieldGet2(_anomalyOverlapMode_accessor_storage$2, this); + } + set anomalyOverlapMode(value) { + _classPrivateFieldSet2(_anomalyOverlapMode_accessor_storage$2, this, value); + } + get showCorrelatedAnomalies() { + return _classPrivateFieldGet2(_showCorrelatedAnomal_accessor_storage$2, this); + } + set showCorrelatedAnomalies(value) { + _classPrivateFieldSet2(_showCorrelatedAnomal_accessor_storage$2, this, value); + } + get anyAnomaliesEnabled() { + return _classPrivateFieldGet2(_anyAnomaliesEnabled_accessor_storage$2, this); + } + set anyAnomaliesEnabled(value) { + _classPrivateFieldSet2(_anyAnomaliesEnabled_accessor_storage$2, this, value); + } + get collapsible() { + return _classPrivateFieldGet2(_collapsible_accessor_storage$1, this); + } + set collapsible(value) { + _classPrivateFieldSet2(_collapsible_accessor_storage$1, this, value); + } + get open() { + return _classPrivateFieldGet2(_open_accessor_storage$3, this); + } + set open(value) { + _classPrivateFieldSet2(_open_accessor_storage$3, this, value); + } + _localizedOptions(options) { + return options.map((opt) => ({ + ...opt, + label: msg(opt.label) + })); + } + _emitAnalysis(kind, value) { + this.dispatchEvent(new CustomEvent("dp-analysis-change", { + detail: { + kind, + value + }, + bubbles: true, + composed: true + })); + } + _onAnomalyOverlapModeChange(e) { + this._emitAnalysis("anomaly_overlap_mode", e.detail.value); + } + _onCheckboxChange(e) { + const { name, checked } = e.detail; + this.dispatchEvent(new CustomEvent("dp-display-change", { + detail: { + kind: name, + value: checked + }, + bubbles: true, + composed: true + })); + } + render() { + return b` ${this.anyAnomaliesEnabled ? b` ` : b`

- ${msg( - "Enable anomaly detection on a target first — open a target's settings and check Show anomalies." - )} + ${msg("Enable anomaly detection on a target first — open a target's settings and check Show anomalies.")}

`}
`; - } - } - _init$t = __decoratorStart$t(_a$t); - _anomalyOverlapMode$2 = /* @__PURE__ */ new WeakMap(); - _showCorrelatedAnomalies$2 = /* @__PURE__ */ new WeakMap(); - _anyAnomaliesEnabled$2 = /* @__PURE__ */ new WeakMap(); - _collapsible$1 = /* @__PURE__ */ new WeakMap(); - _open$3 = /* @__PURE__ */ new WeakMap(); - __decorateElement$t(_init$t, 4, "anomalyOverlapMode", _anomalyOverlapMode_dec$2, SidebarAnalysisSection, _anomalyOverlapMode$2); - __decorateElement$t(_init$t, 4, "showCorrelatedAnomalies", _showCorrelatedAnomalies_dec$2, SidebarAnalysisSection, _showCorrelatedAnomalies$2); - __decorateElement$t(_init$t, 4, "anyAnomaliesEnabled", _anyAnomaliesEnabled_dec$2, SidebarAnalysisSection, _anyAnomaliesEnabled$2); - __decorateElement$t(_init$t, 4, "collapsible", _collapsible_dec$1, SidebarAnalysisSection, _collapsible$1); - __decorateElement$t(_init$t, 4, "open", _open_dec$3, SidebarAnalysisSection, _open$3); - SidebarAnalysisSection = __decorateElement$t(_init$t, 0, "SidebarAnalysisSection", _SidebarAnalysisSection_decorators, SidebarAnalysisSection); - __publicField$B(SidebarAnalysisSection, "styles", styles$z); - __runInitializers$t(_init$t, 1, SidebarAnalysisSection); - customElements.define("sidebar-analysis-section", SidebarAnalysisSection); - const styles$y = i$5` + } + }, _defineProperty(_SidebarAnalysisSection, "styles", styles$35), _SidebarAnalysisSection); + __decorate([n$1({ + type: String, + attribute: "anomaly-overlap-mode" + })], SidebarAnalysisSection.prototype, "anomalyOverlapMode", null); + __decorate([n$1({ + type: Boolean, + attribute: "show-correlated-anomalies" + })], SidebarAnalysisSection.prototype, "showCorrelatedAnomalies", null); + __decorate([n$1({ + type: Boolean, + attribute: false + })], SidebarAnalysisSection.prototype, "anyAnomaliesEnabled", null); + __decorate([n$1({ type: Boolean })], SidebarAnalysisSection.prototype, "collapsible", null); + __decorate([n$1({ type: Boolean })], SidebarAnalysisSection.prototype, "open", null); + SidebarAnalysisSection = __decorate([localized()], SidebarAnalysisSection); + customElements.define("sidebar-analysis-section", SidebarAnalysisSection); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/sidebar-options/sections/sidebar-chart-display-section.styles.ts + var styles$34 = i$5` :host { display: block; --dp-spacing-sm: var(--spacing, 8px); @@ -18520,148 +18577,183 @@ ${content.alert}` : "", font-size: 0.84rem; } `; - var __create$s = Object.create; - var __defProp$A = Object.defineProperty; - var __getOwnPropDesc$s = Object.getOwnPropertyDescriptor; - var __knownSymbol$s = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$s = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$A = (obj, key, value) => key in obj ? __defProp$A(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __name$9 = (target, value) => __defProp$A(target, "name", { value, configurable: true }); - var __decoratorStart$s = (base) => [, , , __create$s(base?.[__knownSymbol$s("metadata")] ?? null)]; - var __decoratorStrings$s = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$s = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$s("Function expected") : fn; - var __decoratorContext$s = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$s[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$s("Already initialized") : fns.push(__expectFn$s(fn || null)) }); - var __decoratorMetadata$s = (array, target) => __defNormalProp$A(target, __knownSymbol$s("metadata"), array[3]); - var __runInitializers$s = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$s = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = !!(flags & 8), p2 = !!(flags & 16); - var j2 = k2 > 3 ? array.length + 1 : k2 ? s2 ? 1 : 2 : 0, key = __decoratorStrings$s[k2 + 5]; - var initializers = k2 > 3 && (array[j2 - 1] = []), extraInitializers = array[j2] || (array[j2] = []); - var desc = k2 && (!p2 && !s2 && (target = target.prototype), k2 < 5 && (k2 > 3 || !p2) && __getOwnPropDesc$s(k2 < 4 ? target : { get [name]() { - return __privateGet$r(this, extra); - }, set [name](x2) { - return __privateSet$r(this, extra, x2); - } }, name)); - k2 ? p2 && k2 < 4 && __name$9(extra, (k2 > 2 ? "set " : k2 > 1 ? "get " : "") + name) : __name$9(target, name); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$s(k2, name, done = {}, array[3], extraInitializers); - if (k2) { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: p2 ? (x2) => __privateIn$8(target, x2) : (x2) => name in x2 }; - if (k2 ^ 3) access.get = p2 ? (x2) => (k2 ^ 1 ? __privateGet$r : __privateMethod$8)(x2, target, k2 ^ 4 ? extra : desc.get) : (x2) => x2[name]; - if (k2 > 2) access.set = p2 ? (x2, y2) => __privateSet$r(x2, target, y2, k2 ^ 4 ? extra : desc.set) : (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])(k2 ? k2 < 4 ? p2 ? extra : desc[key] : k2 > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; - if (k2 ^ 4 || it === void 0) __expectFn$s(it) && (k2 > 4 ? initializers.unshift(it) : k2 ? p2 ? extra = it : desc[key] = it : target = it); - else if (typeof it !== "object" || it === null) __typeError$s("Object expected"); - else __expectFn$s(fn = it.get) && (desc.get = fn), __expectFn$s(fn = it.set) && (desc.set = fn), __expectFn$s(fn = it.init) && initializers.unshift(fn); - } - return k2 || __decoratorMetadata$s(array, target), desc && __defProp$A(target, name, desc), p2 ? k2 ^ 4 ? extra : desc : target; - }; - var __publicField$A = (obj, key, value) => __defNormalProp$A(obj, key + "", value); - var __accessCheck$r = (obj, member, msg2) => member.has(obj) || __typeError$s("Cannot " + msg2); - var __privateIn$8 = (member, obj) => Object(obj) !== obj ? __typeError$s('Cannot use the "in" operator on this value') : member.has(obj); - var __privateGet$r = (obj, member, getter) => (__accessCheck$r(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); - var __privateAdd$r = (obj, member, value) => member.has(obj) ? __typeError$s("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$r = (obj, member, value, setter) => (__accessCheck$r(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); - var __privateMethod$8 = (obj, member, method) => (__accessCheck$r(obj, member, "access private method"), method); - var _open_dec$2, _collapsible_dec, _hoverSnapMode_dec$2, _yAxisMode_dec$2, _dataGapThreshold_dec$2, _showDataGaps_dec$2, _showHoverGuides_dec$2, _showTooltips_dec$2, _a$s, _SidebarChartDisplaySection_decorators, _init$s, _showTooltips$2, _showHoverGuides$2, _showDataGaps$2, _dataGapThreshold$2, _yAxisMode$2, _hoverSnapMode$2, _collapsible, _open$2; - const DATA_GAP_THRESHOLD_OPTIONS$1 = [ - { value: "auto", label: "Auto-detect" }, - { value: "5m", label: "5 minutes" }, - { value: "15m", label: "15 minutes" }, - { value: "1h", label: "1 hour" }, - { value: "2h", label: "2 hours" }, - { value: "3h", label: "3 hours" }, - { value: "6h", label: "6 hours" }, - { value: "12h", label: "12 hours" }, - { value: "24h", label: "24 hours" } - ]; - const Y_AXIS_MODE_OPTIONS = [ - { value: "combined", label: "Combine y-axis by unit" }, - { value: "unique", label: "Unique y-axis per series" }, - { value: "split", label: "Split series into rows" } - ]; - const HOVER_SNAP_MODE_OPTIONS = [ - { value: "follow_series", label: "Follow the series" }, - { value: "snap_to_data_points", label: "Snap to data points" } - ]; - _SidebarChartDisplaySection_decorators = [localized()]; - class SidebarChartDisplaySection extends (_a$s = i$2, _showTooltips_dec$2 = [n({ type: Boolean, attribute: "show-tooltips" })], _showHoverGuides_dec$2 = [n({ type: Boolean, attribute: "show-hover-guides" })], _showDataGaps_dec$2 = [n({ type: Boolean, attribute: "show-data-gaps" })], _dataGapThreshold_dec$2 = [n({ type: String, attribute: "data-gap-threshold" })], _yAxisMode_dec$2 = [n({ type: String, attribute: "y-axis-mode" })], _hoverSnapMode_dec$2 = [n({ type: String, attribute: "hover-snap-mode" })], _collapsible_dec = [n({ type: Boolean })], _open_dec$2 = [n({ type: Boolean })], _a$s) { - constructor() { - super(...arguments); - __privateAdd$r(this, _showTooltips$2, __runInitializers$s(_init$s, 8, this, true)), __runInitializers$s(_init$s, 11, this); - __privateAdd$r(this, _showHoverGuides$2, __runInitializers$s(_init$s, 12, this, false)), __runInitializers$s(_init$s, 15, this); - __privateAdd$r(this, _showDataGaps$2, __runInitializers$s(_init$s, 16, this, true)), __runInitializers$s(_init$s, 19, this); - __privateAdd$r(this, _dataGapThreshold$2, __runInitializers$s(_init$s, 20, this, "2h")), __runInitializers$s(_init$s, 23, this); - __privateAdd$r(this, _yAxisMode$2, __runInitializers$s(_init$s, 24, this, "combined")), __runInitializers$s(_init$s, 27, this); - __privateAdd$r(this, _hoverSnapMode$2, __runInitializers$s(_init$s, 28, this, "follow_series")), __runInitializers$s(_init$s, 31, this); - __privateAdd$r(this, _collapsible, __runInitializers$s(_init$s, 32, this, false)), __runInitializers$s(_init$s, 35, this); - __privateAdd$r(this, _open$2, __runInitializers$s(_init$s, 36, this, true)), __runInitializers$s(_init$s, 39, this); - } - _localizedOptions(options) { - return options.map((opt) => ({ - ...opt, - label: msg(opt.label) - })); - } - _emitDisplay(kind, value) { - this.dispatchEvent( - new CustomEvent("dp-display-change", { - detail: { kind, value }, - bubbles: true, - composed: true - }) - ); - } - _onCheckboxChange(e2) { - const { name, checked } = e2.detail; - this._emitDisplay(name, checked); - } - _onGapThresholdChange(e2) { - this._emitDisplay( - "data_gap_threshold", - e2.target.value - ); - } - _onYAxisModeChange(e2) { - this._emitDisplay("y_axis_mode", e2.detail.value); - } - _onHoverSnapModeChange(e2) { - this._emitDisplay("hover_snap_mode", e2.detail.value); - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/molecules/sidebar-options/sections/sidebar-chart-display-section.ts + var _SidebarChartDisplaySection, _showTooltips_accessor_storage$2, _showHoverGuides_accessor_storage$2, _showDataGaps_accessor_storage$2, _dataGapThreshold_accessor_storage$2, _yAxisMode_accessor_storage$2, _hoverSnapMode_accessor_storage$2, _collapsible_accessor_storage, _open_accessor_storage$2; + var DATA_GAP_THRESHOLD_OPTIONS$1 = [ + { + value: "auto", + label: "Auto-detect" + }, + { + value: "5m", + label: "5 minutes" + }, + { + value: "15m", + label: "15 minutes" + }, + { + value: "1h", + label: "1 hour" + }, + { + value: "2h", + label: "2 hours" + }, + { + value: "3h", + label: "3 hours" + }, + { + value: "6h", + label: "6 hours" + }, + { + value: "12h", + label: "12 hours" + }, + { + value: "24h", + label: "24 hours" + } + ]; + var Y_AXIS_MODE_OPTIONS = [ + { + value: "combined", + label: "Combine y-axis by unit" + }, + { + value: "unique", + label: "Unique y-axis per series" + }, + { + value: "split", + label: "Split series into rows" + } + ]; + var HOVER_SNAP_MODE_OPTIONS = [{ + value: "follow_series", + label: "Follow the series" + }, { + value: "snap_to_data_points", + label: "Snap to data points" + }]; + var SidebarChartDisplaySection = (_showTooltips_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _showHoverGuides_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _showDataGaps_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _dataGapThreshold_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _yAxisMode_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _hoverSnapMode_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _collapsible_accessor_storage = /* @__PURE__ */ new WeakMap(), _open_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _SidebarChartDisplaySection = class SidebarChartDisplaySection extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _showTooltips_accessor_storage$2, true); + _classPrivateFieldInitSpec(this, _showHoverGuides_accessor_storage$2, false); + _classPrivateFieldInitSpec(this, _showDataGaps_accessor_storage$2, true); + _classPrivateFieldInitSpec(this, _dataGapThreshold_accessor_storage$2, "2h"); + _classPrivateFieldInitSpec(this, _yAxisMode_accessor_storage$2, "combined"); + _classPrivateFieldInitSpec(this, _hoverSnapMode_accessor_storage$2, "follow_series"); + _classPrivateFieldInitSpec(this, _collapsible_accessor_storage, false); + _classPrivateFieldInitSpec(this, _open_accessor_storage$2, true); + } + get showTooltips() { + return _classPrivateFieldGet2(_showTooltips_accessor_storage$2, this); + } + set showTooltips(value) { + _classPrivateFieldSet2(_showTooltips_accessor_storage$2, this, value); + } + get showHoverGuides() { + return _classPrivateFieldGet2(_showHoverGuides_accessor_storage$2, this); + } + set showHoverGuides(value) { + _classPrivateFieldSet2(_showHoverGuides_accessor_storage$2, this, value); + } + get showDataGaps() { + return _classPrivateFieldGet2(_showDataGaps_accessor_storage$2, this); + } + set showDataGaps(value) { + _classPrivateFieldSet2(_showDataGaps_accessor_storage$2, this, value); + } + get dataGapThreshold() { + return _classPrivateFieldGet2(_dataGapThreshold_accessor_storage$2, this); + } + set dataGapThreshold(value) { + _classPrivateFieldSet2(_dataGapThreshold_accessor_storage$2, this, value); + } + get yAxisMode() { + return _classPrivateFieldGet2(_yAxisMode_accessor_storage$2, this); + } + set yAxisMode(value) { + _classPrivateFieldSet2(_yAxisMode_accessor_storage$2, this, value); + } + get hoverSnapMode() { + return _classPrivateFieldGet2(_hoverSnapMode_accessor_storage$2, this); + } + set hoverSnapMode(value) { + _classPrivateFieldSet2(_hoverSnapMode_accessor_storage$2, this, value); + } + get collapsible() { + return _classPrivateFieldGet2(_collapsible_accessor_storage, this); + } + set collapsible(value) { + _classPrivateFieldSet2(_collapsible_accessor_storage, this, value); + } + get open() { + return _classPrivateFieldGet2(_open_accessor_storage$2, this); + } + set open(value) { + _classPrivateFieldSet2(_open_accessor_storage$2, this, value); + } + _localizedOptions(options) { + return options.map((opt) => ({ + ...opt, + label: msg(opt.label) + })); + } + _emitDisplay(kind, value) { + this.dispatchEvent(new CustomEvent("dp-display-change", { + detail: { + kind, + value + }, + bubbles: true, + composed: true + })); + } + _onCheckboxChange(e) { + const { name, checked } = e.detail; + this._emitDisplay(name, checked); + } + _onGapThresholdChange(e) { + this._emitDisplay("data_gap_threshold", e.target.value); + } + _onYAxisModeChange(e) { + this._emitDisplay("y_axis_mode", e.detail.value); + } + _onHoverSnapModeChange(e) { + this._emitDisplay("hover_snap_mode", e.detail.value); + } + render() { + return b`
@@ -18670,16 +18762,14 @@ ${content.alert}` : "", ?disabled=${!this.showDataGaps} @change=${this._onGapThresholdChange} > - ${this._localizedOptions(DATA_GAP_THRESHOLD_OPTIONS$1).map( - (opt) => b` + ${this._localizedOptions(DATA_GAP_THRESHOLD_OPTIONS$1).map((opt) => b` - ` - )} + `)} ${msg("Gap threshold")}
@@ -18701,308 +18791,259 @@ ${content.alert}` : "",

`; - } - } - _init$s = __decoratorStart$s(_a$s); - _showTooltips$2 = /* @__PURE__ */ new WeakMap(); - _showHoverGuides$2 = /* @__PURE__ */ new WeakMap(); - _showDataGaps$2 = /* @__PURE__ */ new WeakMap(); - _dataGapThreshold$2 = /* @__PURE__ */ new WeakMap(); - _yAxisMode$2 = /* @__PURE__ */ new WeakMap(); - _hoverSnapMode$2 = /* @__PURE__ */ new WeakMap(); - _collapsible = /* @__PURE__ */ new WeakMap(); - _open$2 = /* @__PURE__ */ new WeakMap(); - __decorateElement$s(_init$s, 4, "showTooltips", _showTooltips_dec$2, SidebarChartDisplaySection, _showTooltips$2); - __decorateElement$s(_init$s, 4, "showHoverGuides", _showHoverGuides_dec$2, SidebarChartDisplaySection, _showHoverGuides$2); - __decorateElement$s(_init$s, 4, "showDataGaps", _showDataGaps_dec$2, SidebarChartDisplaySection, _showDataGaps$2); - __decorateElement$s(_init$s, 4, "dataGapThreshold", _dataGapThreshold_dec$2, SidebarChartDisplaySection, _dataGapThreshold$2); - __decorateElement$s(_init$s, 4, "yAxisMode", _yAxisMode_dec$2, SidebarChartDisplaySection, _yAxisMode$2); - __decorateElement$s(_init$s, 4, "hoverSnapMode", _hoverSnapMode_dec$2, SidebarChartDisplaySection, _hoverSnapMode$2); - __decorateElement$s(_init$s, 4, "collapsible", _collapsible_dec, SidebarChartDisplaySection, _collapsible); - __decorateElement$s(_init$s, 4, "open", _open_dec$2, SidebarChartDisplaySection, _open$2); - SidebarChartDisplaySection = __decorateElement$s(_init$s, 0, "SidebarChartDisplaySection", _SidebarChartDisplaySection_decorators, SidebarChartDisplaySection); - __publicField$A(SidebarChartDisplaySection, "styles", styles$y); - __runInitializers$s(_init$s, 1, SidebarChartDisplaySection); - customElements.define( - "sidebar-chart-display-section", - SidebarChartDisplaySection - ); - var __defProp$z = Object.defineProperty; - var __defNormalProp$z = (obj, key, value) => key in obj ? __defProp$z(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$z = (obj, key, value) => __defNormalProp$z(obj, typeof key !== "symbol" ? key + "" : key, value); - class HassRecordsHistoryCardEditor extends EditorBase { - constructor() { - super(...arguments); - __publicField$z(this, "_onTargetChanged", (e2) => { - const val = e2.detail.value; - const isEmpty = !val || Object.values(val).every((value) => !value?.length); - this._updateTargetAndRows(isEmpty ? void 0 : val); - }); - __publicField$z(this, "_onAnalysisChange", (e2) => { - const { key, value } = e2.detail || {}; - if (!key) { - return; - } - this._updateGlobalAnalysis((analysis) => { - if (key.startsWith("anomaly_method_toggle_")) { - const method = key.slice("anomaly_method_toggle_".length); - const currentMethods = Array.isArray(analysis.anomaly_methods) ? analysis.anomaly_methods : []; - return { - ...analysis, - anomaly_methods: value === true ? [.../* @__PURE__ */ new Set([...currentMethods, method])] : currentMethods.filter((entry) => entry !== method) - }; - } - return { - ...analysis, - [key]: value - }; - }); - }); - __publicField$z(this, "_onScopeChange", (e2) => { - this._set("datapoint_scope", e2.detail.value); - }); - __publicField$z(this, "_onDisplayChange", (e2) => { - const { kind, value } = e2.detail || {}; - if (!kind) { - return; - } - if (kind === "icons") { - this._set("show_event_markers", value === true ? void 0 : false); - return; - } - if (kind === "lines") { - this._set("show_event_lines", value === true ? void 0 : false); - return; - } - if (kind === "tooltips") { - this._set("show_tooltips", value === true ? void 0 : false); - return; - } - if (kind === "hover_guides") { - this._set("emphasize_hover_guides", value === true ? true : void 0); - return; - } - if (kind === "correlated_anomalies") { - this._set("show_correlated_anomalies", value === true ? true : void 0); - return; - } - if (kind === "data_gaps") { - this._set("show_data_gaps", value === true ? void 0 : false); - return; - } - if (kind === "data_gap_threshold") { - this._set("data_gap_threshold", value); - return; - } - if (kind === "hover_snap_mode") { - this._set("hover_snap_mode", value); - return; - } - if (kind === "y_axis_mode") { - const cfg = { ...this._config }; - if (value === "split") { - cfg.split_view = true; - delete cfg.delink_y_axis; - } else if (value === "unique") { - delete cfg.split_view; - cfg.delink_y_axis = true; - } else { - delete cfg.split_view; - delete cfg.delink_y_axis; - } - this._emitConfig(cfg); - } - }); - __publicField$z(this, "_onChartAnalysisChange", (e2) => { - const { kind, value } = e2.detail || {}; - if (kind === "anomaly_overlap_mode" && typeof value === "string") { - this._set("anomaly_overlap_mode", value); - this._updateGlobalAnalysis((analysis) => ({ - ...analysis, - anomaly_overlap_mode: value === "only" ? "only" : "all" - })); - } - }); - } - _configTarget() { - if (this._config.target) { - return normalizeTargetValue(this._config.target) ?? {}; - } - const rowEntityIds = normalizeHistorySeriesRows( - this._config.series_settings - ).map((row) => row.entity_id); - let entityIdValue; - if (rowEntityIds.length > 0) { - entityIdValue = rowEntityIds; - } else if (Array.isArray(this._config.entities) && this._config.entities.length) { - entityIdValue = this._config.entities; - } else { - entityIdValue = this._config.entity; - } - return normalizeTargetValue({ - entity_id: entityIdValue - }) ?? {}; - } - _globalAnalysis() { - const normalizedRows = normalizeHistorySeriesRows( - this._config.series_settings - ); - if (normalizedRows.length > 0) { - return normalizeHistorySeriesAnalysis(normalizedRows[0].analysis); - } - return normalizeHistorySeriesAnalysis({ - show_trend_lines: this._config.show_trend_lines, - trend_method: this._config.trend_method, - trend_window: this._config.trend_window, - show_trend_crosshairs: this._config.show_trend_crosshairs, - show_summary_stats: this._config.show_summary_stats, - show_rate_of_change: this._config.show_rate_of_change, - rate_window: this._config.rate_window, - show_threshold_analysis: this._config.show_threshold_analysis, - show_threshold_shading: this._config.show_threshold_shading, - show_anomalies: this._config.show_anomalies, - anomaly_overlap_mode: this._config.anomaly_overlap_mode || this._config.chart_anomaly_overlap_mode, - anomaly_sensitivity: this._config.anomaly_sensitivity, - show_delta_analysis: this._config.show_delta_analysis, - show_delta_tooltip: this._config.show_delta_tooltip, - show_delta_lines: this._config.show_delta_lines, - hide_source_series: this._config.hide_delta_source_series, - stepped_series: this._config.stepped_series - }); - } - _syncTargetPicker() { - const tp = this.shadowRoot?.querySelector( - "#target-picker" - ); - if (!tp) { - return; - } - if (this.hass) { - tp.hass = this.hass; - } - tp.value = this._configTarget(); - } - _emitConfig(cfg) { - this._config = cfg; - this._fire(cfg); - } - _targetEntityIds(target) { - return resolveEntityIdsFromTarget(this.hass, target); - } - _compatConfigFromAnalysis(analysis) { - return { - show_trend_lines: analysis.show_trend_lines, - trend_method: analysis.trend_method, - trend_window: analysis.trend_window, - show_trend_crosshairs: analysis.show_trend_crosshairs, - show_summary_stats: analysis.show_summary_stats, - show_rate_of_change: analysis.show_rate_of_change, - rate_window: analysis.rate_window, - show_threshold_analysis: analysis.show_threshold_analysis, - show_threshold_shading: analysis.show_threshold_shading, - show_anomalies: analysis.show_anomalies, - anomaly_overlap_mode: analysis.anomaly_overlap_mode, - anomaly_sensitivity: analysis.anomaly_sensitivity, - show_delta_analysis: analysis.show_delta_analysis, - show_delta_tooltip: analysis.show_delta_tooltip, - show_delta_lines: analysis.show_delta_lines, - hide_delta_source_series: analysis.hide_source_series, - stepped_series: analysis.stepped_series - }; - } - _updateTargetAndRows(target) { - const currentRows = normalizeHistorySeriesRows( - this._config.series_settings - ); - const analysis = this._globalAnalysis(); - const entityIds = this._targetEntityIds(target); - const rows = buildHistorySeriesRows(entityIds, currentRows).map((row) => ({ - ...row, - analysis: normalizeHistorySeriesAnalysis(analysis) - })); - const cfg = { - ...this._config, - ...this._compatConfigFromAnalysis(analysis) - }; - delete cfg.entity; - delete cfg.entities; - if (!target || Object.values(target).every((value) => !value?.length)) { - delete cfg.target; - } else { - cfg.target = target; - } - if (rows.length === 0) { - delete cfg.series_settings; - } else { - cfg.series_settings = rows; - } - this._emitConfig(cfg); - } - _updateGlobalAnalysis(updater) { - const nextAnalysis = normalizeHistorySeriesAnalysis( - updater(this._globalAnalysis()) - ); - const target = this._configTarget(); - const currentRows = normalizeHistorySeriesRows( - this._config.series_settings - ); - const entityIds = this._targetEntityIds(target); - const rows = buildHistorySeriesRows(entityIds, currentRows).map((row) => ({ - ...row, - analysis: nextAnalysis - })); - const cfg = { - ...this._config, - ...this._compatConfigFromAnalysis(nextAnalysis), - target - }; - if (rows.length === 0) { - delete cfg.series_settings; - } else { - cfg.series_settings = rows; - } - this._emitConfig(cfg); - } - _yAxisMode() { - if (this._config.split_view === true) { - return "split"; - } - if (this._config.delink_y_axis === true) { - return "unique"; - } - return "combined"; - } - updated(changedProps) { - if (changedProps.has("hass") || changedProps.has("_config")) { - this._syncTargetPicker(); - } - } - render() { - const c2 = this._config; - const analysis = this._globalAnalysis(); - return b` + } + }, _defineProperty(_SidebarChartDisplaySection, "styles", styles$34), _SidebarChartDisplaySection); + __decorate([n$1({ + type: Boolean, + attribute: "show-tooltips" + })], SidebarChartDisplaySection.prototype, "showTooltips", null); + __decorate([n$1({ + type: Boolean, + attribute: "show-hover-guides" + })], SidebarChartDisplaySection.prototype, "showHoverGuides", null); + __decorate([n$1({ + type: Boolean, + attribute: "show-data-gaps" + })], SidebarChartDisplaySection.prototype, "showDataGaps", null); + __decorate([n$1({ + type: String, + attribute: "data-gap-threshold" + })], SidebarChartDisplaySection.prototype, "dataGapThreshold", null); + __decorate([n$1({ + type: String, + attribute: "y-axis-mode" + })], SidebarChartDisplaySection.prototype, "yAxisMode", null); + __decorate([n$1({ + type: String, + attribute: "hover-snap-mode" + })], SidebarChartDisplaySection.prototype, "hoverSnapMode", null); + __decorate([n$1({ type: Boolean })], SidebarChartDisplaySection.prototype, "collapsible", null); + __decorate([n$1({ type: Boolean })], SidebarChartDisplaySection.prototype, "open", null); + SidebarChartDisplaySection = __decorate([localized()], SidebarChartDisplaySection); + customElements.define("sidebar-chart-display-section", SidebarChartDisplaySection); + //#endregion + //#region custom_components/hass_datapoints/src/cards/history/editor.ts + var HassRecordsHistoryCardEditor = class extends EditorBase { + constructor(..._args) { + super(..._args); + _defineProperty(this, "_onTargetChanged", (e) => { + const val = e.detail.value; + const isEmpty = !val || Object.values(val).every((value) => !value?.length); + this._updateTargetAndRows(isEmpty ? void 0 : val); + }); + _defineProperty(this, "_onAnalysisChange", (e) => { + const { key, value } = e.detail || {}; + if (!key) return; + this._updateGlobalAnalysis((analysis) => { + if (key.startsWith("anomaly_method_toggle_")) { + const method = key.slice(22); + const currentMethods = Array.isArray(analysis.anomaly_methods) ? analysis.anomaly_methods : []; + return { + ...analysis, + anomaly_methods: value === true ? [...new Set([...currentMethods, method])] : currentMethods.filter((entry) => entry !== method) + }; + } + return { + ...analysis, + [key]: value + }; + }); + }); + _defineProperty(this, "_onScopeChange", (e) => { + this._set("datapoint_scope", e.detail.value); + }); + _defineProperty(this, "_onDisplayChange", (e) => { + const { kind, value } = e.detail || {}; + if (!kind) return; + if (kind === "icons") { + this._set("show_event_markers", value === true ? void 0 : false); + return; + } + if (kind === "lines") { + this._set("show_event_lines", value === true ? void 0 : false); + return; + } + if (kind === "tooltips") { + this._set("show_tooltips", value === true ? void 0 : false); + return; + } + if (kind === "hover_guides") { + this._set("emphasize_hover_guides", value === true ? true : void 0); + return; + } + if (kind === "correlated_anomalies") { + this._set("show_correlated_anomalies", value === true ? true : void 0); + return; + } + if (kind === "data_gaps") { + this._set("show_data_gaps", value === true ? void 0 : false); + return; + } + if (kind === "data_gap_threshold") { + this._set("data_gap_threshold", value); + return; + } + if (kind === "hover_snap_mode") { + this._set("hover_snap_mode", value); + return; + } + if (kind === "y_axis_mode") { + const cfg = { ...this._config }; + if (value === "split") { + cfg.split_view = true; + delete cfg.delink_y_axis; + } else if (value === "unique") { + delete cfg.split_view; + cfg.delink_y_axis = true; + } else { + delete cfg.split_view; + delete cfg.delink_y_axis; + } + this._emitConfig(cfg); + } + }); + _defineProperty(this, "_onChartAnalysisChange", (e) => { + const { kind, value } = e.detail || {}; + if (kind === "anomaly_overlap_mode" && typeof value === "string") { + this._set("anomaly_overlap_mode", value); + this._updateGlobalAnalysis((analysis) => ({ + ...analysis, + anomaly_overlap_mode: value === "only" ? "only" : "all" + })); + } + }); + } + _configTarget() { + if (this._config.target) return normalizeTargetValue(this._config.target) ?? {}; + const rowEntityIds = normalizeHistorySeriesRows(this._config.series_settings).map((row) => row.entity_id); + let entityIdValue; + if (rowEntityIds.length > 0) entityIdValue = rowEntityIds; + else if (Array.isArray(this._config.entities) && this._config.entities.length) entityIdValue = this._config.entities; + else entityIdValue = this._config.entity; + return normalizeTargetValue({ entity_id: entityIdValue }) ?? {}; + } + _globalAnalysis() { + const normalizedRows = normalizeHistorySeriesRows(this._config.series_settings); + if (normalizedRows.length > 0) return normalizeHistorySeriesAnalysis(normalizedRows[0].analysis); + return normalizeHistorySeriesAnalysis({ + show_trend_lines: this._config.show_trend_lines, + trend_method: this._config.trend_method, + trend_window: this._config.trend_window, + show_trend_crosshairs: this._config.show_trend_crosshairs, + show_summary_stats: this._config.show_summary_stats, + show_rate_of_change: this._config.show_rate_of_change, + rate_window: this._config.rate_window, + show_threshold_analysis: this._config.show_threshold_analysis, + show_threshold_shading: this._config.show_threshold_shading, + show_anomalies: this._config.show_anomalies, + anomaly_overlap_mode: this._config.anomaly_overlap_mode || this._config.chart_anomaly_overlap_mode, + anomaly_sensitivity: this._config.anomaly_sensitivity, + show_delta_analysis: this._config.show_delta_analysis, + show_delta_tooltip: this._config.show_delta_tooltip, + show_delta_lines: this._config.show_delta_lines, + hide_source_series: this._config.hide_delta_source_series, + stepped_series: this._config.stepped_series + }); + } + _syncTargetPicker() { + const tp = this.shadowRoot?.querySelector("#target-picker"); + if (!tp) return; + if (this.hass) tp.hass = this.hass; + tp.value = this._configTarget(); + } + _emitConfig(cfg) { + this._config = cfg; + this._fire(cfg); + } + _targetEntityIds(target) { + return resolveEntityIdsFromTarget(this.hass, target); + } + _compatConfigFromAnalysis(analysis) { + return { + show_trend_lines: analysis.show_trend_lines, + trend_method: analysis.trend_method, + trend_window: analysis.trend_window, + show_trend_crosshairs: analysis.show_trend_crosshairs, + show_summary_stats: analysis.show_summary_stats, + show_rate_of_change: analysis.show_rate_of_change, + rate_window: analysis.rate_window, + show_threshold_analysis: analysis.show_threshold_analysis, + show_threshold_shading: analysis.show_threshold_shading, + show_anomalies: analysis.show_anomalies, + anomaly_overlap_mode: analysis.anomaly_overlap_mode, + anomaly_sensitivity: analysis.anomaly_sensitivity, + show_delta_analysis: analysis.show_delta_analysis, + show_delta_tooltip: analysis.show_delta_tooltip, + show_delta_lines: analysis.show_delta_lines, + hide_delta_source_series: analysis.hide_source_series, + stepped_series: analysis.stepped_series + }; + } + _updateTargetAndRows(target) { + const currentRows = normalizeHistorySeriesRows(this._config.series_settings); + const analysis = this._globalAnalysis(); + const rows = buildHistorySeriesRows(this._targetEntityIds(target), currentRows).map((row) => ({ + ...row, + analysis: normalizeHistorySeriesAnalysis(analysis) + })); + const cfg = { + ...this._config, + ...this._compatConfigFromAnalysis(analysis) + }; + delete cfg.entity; + delete cfg.entities; + if (!target || Object.values(target).every((value) => !value?.length)) delete cfg.target; + else cfg.target = target; + if (rows.length === 0) delete cfg.series_settings; + else cfg.series_settings = rows; + this._emitConfig(cfg); + } + _updateGlobalAnalysis(updater) { + const nextAnalysis = normalizeHistorySeriesAnalysis(updater(this._globalAnalysis())); + const target = this._configTarget(); + const currentRows = normalizeHistorySeriesRows(this._config.series_settings); + const rows = buildHistorySeriesRows(this._targetEntityIds(target), currentRows).map((row) => ({ + ...row, + analysis: nextAnalysis + })); + const cfg = { + ...this._config, + ...this._compatConfigFromAnalysis(nextAnalysis), + target + }; + if (rows.length === 0) delete cfg.series_settings; + else cfg.series_settings = rows; + this._emitConfig(cfg); + } + _yAxisMode() { + if (this._config.split_view === true) return "split"; + if (this._config.delink_y_axis === true) return "unique"; + return "combined"; + } + updated(changedProps) { + if (changedProps.has("hass") || changedProps.has("_config")) this._syncTargetPicker(); + } + render() { + const c = this._config; + const analysis = this._globalAnalysis(); + return b`
this._set("title", e2.detail.value)} + .value=${c.title || ""} + @dp-field-change=${(e) => this._set("title", e.detail.value)} > this._set("hours_to_show", e2.detail.value)} + .value=${String(c.hours_to_show ?? 24)} + @dp-field-change=${(e) => this._set("hours_to_show", e.detail.value)} >
- ${msg( - "Choose the entities, devices, areas or labels to chart. Analysis settings below are applied to every selected target.", - { - id: "Choose the entities, devices, areas or labels to chart. Analysis settings below are applied to every selected target." - } - )} + ${msg("Choose the entities, devices, areas or labels to chart. Analysis settings below are applied to every selected target.", { id: "Choose the entities, devices, areas or labels to chart. Analysis settings below are applied to every selected target." })}
this._updateGlobalAnalysis((currentAnalysis) => ({ - ...currentAnalysis, - stepped_series: e2.detail.checked - }))} + @dp-switch-change=${(e) => this._updateGlobalAnalysis((currentAnalysis) => ({ + ...currentAnalysis, + stepped_series: e.detail.checked + }))} > this._set( - "show_add_annotation_button", - e2.detail.checked ? void 0 : false - )} + .checked=${c.show_add_annotation_button !== false} + @dp-switch-change=${(e) => this._set("show_add_annotation_button", e.detail.checked ? void 0 : false)} > this._set("hide_raw_data", e2.detail.checked ? true : void 0)} + .checked=${c.hide_raw_data === true} + @dp-switch-change=${(e) => this._set("hide_raw_data", e.detail.checked ? true : void 0)} >
`; - } - } - __publicField$z(HassRecordsHistoryCardEditor, "styles", [EditorBase.styles, styles$P]); - const PANEL_HISTORY_SAVED_PAGE_KEY = "hass_datapoints:saved_page_v1"; - async function fetchUserData(hass, key, defaultValue = null) { - try { - const result = await hass.connection.sendMessagePromise({ - type: "frontend/get_user_data", - key - }); - return result?.value ?? defaultValue; - } catch (err) { - logger.warn("[hass-datapoints] fetchUserData failed:", err); - return defaultValue; - } - } - async function saveUserData(hass, key, value) { - try { - await hass.connection.sendMessagePromise({ - type: "frontend/set_user_data", - key, - value - }); - } catch (err) { - logger.warn("[hass-datapoints] saveUserData failed:", err); - } - } - const SECOND_MS = 1e3; - const MINUTE_MS = 60 * SECOND_MS; - const HOUR_MS = 60 * MINUTE_MS; - const DAY_MS = 24 * HOUR_MS; - const RANGE_SLIDER_MIN_SPAN_MS = 15 * 60 * 1e3; - const RANGE_SLIDER_WINDOW_MS = 30 * DAY_MS; - const RANGE_AUTO_ZOOM_DEBOUNCE_MS = 3e3; - const RANGE_AUTO_ZOOM_SELECTION_PADDING_RATIO = 0.6; - const RANGE_FUTURE_BUFFER_YEARS = 1; - const RANGE_LABEL_MIN_GAP_PX = 10; - const RANGE_CONTEXT_LABEL_MIN_GAP_PX = 14; - const RANGE_HANDLE_EDGE_SCROLL_THRESHOLD_PX = 48; - const RANGE_HANDLE_EDGE_SCROLL_MAX_STEP_PX = 28; - const RANGE_ZOOM_OPTIONS = [ - { value: "auto", label: "Auto" }, - { value: "quarterly", label: "Quarterly" }, - { value: "month_compressed", label: "Month Compressed" }, - { value: "month_short", label: "Month Short" }, - { value: "month_expanded", label: "Month Expanded" }, - { value: "week_compressed", label: "Week Compressed" }, - { value: "week_expanded", label: "Week Expanded" }, - { value: "day", label: "Day" } - ]; - const RANGE_SNAP_OPTIONS = [ - { value: "auto", label: "Auto" }, - { value: "month", label: "Month" }, - { value: "week", label: "Week" }, - { value: "day", label: "Day" }, - { value: "hour", label: "Hour" }, - { value: "minute", label: "Minute" }, - { value: "second", label: "Second" } - ]; - const RANGE_ZOOM_CONFIGS = { - quarterly: { - baselineMs: 730 * DAY_MS, - boundsUnit: "month", - contextUnit: "year", - detailUnit: "month", - majorUnit: "quarter", - labelUnit: "quarter", - minorUnit: "month", - pixelsPerUnit: 96 - }, - month_compressed: { - baselineMs: 365 * DAY_MS, - boundsUnit: "month", - contextUnit: "year", - detailUnit: "week", - majorUnit: "month", - labelUnit: "month", - minorUnit: "month", - pixelsPerUnit: 76 - }, - month_short: { - baselineMs: 180 * DAY_MS, - boundsUnit: "week", - contextUnit: "month", - detailUnit: "day", - majorUnit: "week", - labelUnit: "week", - minorUnit: "week", - pixelsPerUnit: 54 - }, - month_expanded: { - baselineMs: 90 * DAY_MS, - boundsUnit: "week", - contextUnit: "month", - detailUnit: "day", - majorUnit: "week", - labelUnit: "week", - minorUnit: "week", - pixelsPerUnit: 72 - }, - week_compressed: { - baselineMs: 56 * DAY_MS, - boundsUnit: "week", - contextUnit: "month", - detailUnit: "day", - majorUnit: "week", - labelUnit: "week", - minorUnit: "week", - pixelsPerUnit: 120 - }, - week_expanded: { - baselineMs: 28 * DAY_MS, - boundsUnit: "day", - contextUnit: "month", - detailUnit: "hour", - detailStep: 12, - majorUnit: "day", - labelUnit: "day", - minorUnit: "day", - pixelsPerUnit: 30 - }, - day: { - baselineMs: 48 * HOUR_MS, - boundsUnit: "hour", - contextUnit: "day", - majorUnit: "hour", - labelUnit: "hour", - minorUnit: "hour", - pixelsPerUnit: 9 - } - }; - function extractRangeValue(source) { - if (!source) { - return { start: null, end: null }; - } - const detail = source.detail || {}; - const value = detail.value || source.value || source.target?.value || {}; - return { - start: parseDateValue( - detail.startDate || value.startDate || source.startDate || source.target?.startDate - ), - end: parseDateValue( - detail.endDate || value.endDate || source.endDate || source.target?.endDate - ) - }; - } - function formatRangeDateTime(value, locale) { - if (!(value instanceof Date) || Number.isNaN(value.getTime())) { - return "--"; - } - return value.toLocaleString(locale ? [locale] : [], { - month: "short", - day: "numeric", - hour: "2-digit", - minute: "2-digit" - }); - } - function formatRangeTick(value, locale) { - if (!(value instanceof Date) || Number.isNaN(value.getTime())) { - return "--"; - } - return value.toLocaleString(locale ? [locale] : [], { - month: "short", - day: "numeric", - hour: "2-digit", - minute: "2-digit" - }); - } - function clampNumber(value, min, max) { - return Math.min(max, Math.max(min, value)); - } - function startOfLocalDay(value) { - return new Date(value.getFullYear(), value.getMonth(), value.getDate()); - } - function startOfLocalHour(value) { - return new Date( - value.getFullYear(), - value.getMonth(), - value.getDate(), - value.getHours(), - 0, - 0, - 0 - ); - } - function startOfLocalMinute(value) { - return new Date( - value.getFullYear(), - value.getMonth(), - value.getDate(), - value.getHours(), - value.getMinutes(), - 0, - 0 - ); - } - function startOfLocalSecond(value) { - return new Date( - value.getFullYear(), - value.getMonth(), - value.getDate(), - value.getHours(), - value.getMinutes(), - value.getSeconds(), - 0 - ); - } - function startOfLocalMonth(value) { - return new Date(value.getFullYear(), value.getMonth(), 1); - } - function endOfLocalMonth(value) { - return new Date(value.getFullYear(), value.getMonth() + 1, 1); - } - function startOfLocalYear(value) { - return new Date(value.getFullYear(), 0, 1); - } - function startOfLocalWeek(value) { - const day = value.getDay(); - const mondayOffset = day === 0 ? -6 : 1 - day; - const result = startOfLocalDay(value); - result.setDate(result.getDate() + mondayOffset); - return result; - } - function startOfLocalQuarter(value) { - return new Date(value.getFullYear(), Math.floor(value.getMonth() / 3) * 3, 1); - } - function endOfLocalHour(value) { - const result = startOfLocalHour(value); - result.setHours(result.getHours() + 1); - return result; - } - function endOfLocalDay(value) { - const result = startOfLocalDay(value); - result.setDate(result.getDate() + 1); - return result; - } - function endOfLocalWeek(value) { - const result = startOfLocalWeek(value); - result.setDate(result.getDate() + 7); - return result; - } - function endOfLocalQuarter(value) { - const result = startOfLocalQuarter(value); - result.setMonth(result.getMonth() + 3); - return result; - } - function endOfLocalMinute(value) { - const result = startOfLocalMinute(value); - result.setMinutes(result.getMinutes() + 1); - return result; - } - function endOfLocalSecond(value) { - const result = startOfLocalSecond(value); - result.setSeconds(result.getSeconds() + 1); - return result; - } - function formatMonthLabel(value, locale) { - return value.toLocaleString(locale ? [locale] : [], { month: "short" }); - } - function formatYearLabel(value, locale) { - return value.toLocaleString(locale ? [locale] : [], { year: "numeric" }); - } - function getWeekOfYear(value) { - const date = new Date(value.getTime()); - date.setHours(0, 0, 0, 0); - const day = date.getDay() || 7; - date.setDate(date.getDate() + 4 - day); - const yearStart = new Date(date.getFullYear(), 0, 1); - return Math.ceil(((date.getTime() - yearStart.getTime()) / DAY_MS + 1) / 7); - } - function getWeekLabel(value, locale) { - return value.toLocaleString(locale ? [locale] : [], { - month: "short", - day: "numeric" - }); - } - function formatDayLabel(value, locale) { - return value.toLocaleString(locale ? [locale] : [], { day: "numeric" }); - } - function formatHourLabel(value, locale) { - return value.toLocaleTimeString(locale ? [locale] : [], { hour: "2-digit" }); - } - function formatQuarterLabel(value, zoomLevel = "", locale) { - return zoomLevel === "quarterly" ? `Q${Math.floor(value.getMonth() / 3) + 1}` : formatMonthLabel(value, locale); - } - function formatScaleLabel(value, unit, zoomLevel = "", locale) { - switch (unit) { - case "quarter": - return formatQuarterLabel(value, zoomLevel, locale); - case "month": - return formatMonthLabel(value, locale); - case "week": - return zoomLevel === "month_short" ? `${msg("Wk")} ${getWeekOfYear(value)}` : getWeekLabel(value, locale); - case "day": - return formatDayLabel(value, locale); - case "hour": - return formatHourLabel(value, locale); - default: - return formatRangeTick(value, locale); - } - } - function formatContextLabel(value, unit, locale) { - switch (unit) { - case "year": - return formatYearLabel(value, locale); - case "month": - return value.toLocaleString(locale ? [locale] : [], { - month: "short", - year: "numeric" - }); - case "day": - return value.toLocaleString(locale ? [locale] : [], { - month: "short", - day: "numeric" - }); - default: - return formatRangeTick(value, locale); - } - } - function formatPeriodSelectionLabel(value, unit, locale) { - switch (unit) { - case "year": - return formatYearLabel(value, locale); - case "quarter": - return `${formatQuarterLabel(value, "", locale)} ${formatYearLabel(value, locale)}`; - case "month": - return value.toLocaleString(locale ? [locale] : [], { - month: "long", - year: "numeric" - }); - case "week": - return `${msg("Week of")} ${value.toLocaleString(locale ? [locale] : [], { month: "short", day: "numeric", year: "numeric" })}`; - case "day": - return value.toLocaleString(locale ? [locale] : [], { - month: "short", - day: "numeric", - year: "numeric" - }); - case "hour": - return value.toLocaleString(locale ? [locale] : [], { - month: "short", - day: "numeric", - year: "numeric", - hour: "2-digit" - }); - default: - return formatRangeTick(value, locale); - } - } - function startOfUnit(value, unit) { - switch (unit) { - case "second": - return startOfLocalSecond(value); - case "minute": - return startOfLocalMinute(value); - case "hour": - return startOfLocalHour(value); - case "day": - return startOfLocalDay(value); - case "week": - return startOfLocalWeek(value); - case "month": - return startOfLocalMonth(value); - case "quarter": - return startOfLocalQuarter(value); - case "year": - return startOfLocalYear(value); - default: - return new Date(value); - } - } - function endOfUnit(value, unit) { - switch (unit) { - case "second": - return endOfLocalSecond(value); - case "minute": - return endOfLocalMinute(value); - case "hour": - return endOfLocalHour(value); - case "day": - return endOfLocalDay(value); - case "week": - return endOfLocalWeek(value); - case "month": - return endOfLocalMonth(value); - case "quarter": - return endOfLocalQuarter(value); - case "year": { - const result = startOfLocalYear(value); - result.setFullYear(result.getFullYear() + 1); - return result; - } - default: - return new Date(value); - } - } - function addUnit(value, unit, amount = 1) { - const result = new Date(value); - switch (unit) { - case "second": - result.setSeconds(result.getSeconds() + amount); - break; - case "minute": - result.setMinutes(result.getMinutes() + amount); - break; - case "hour": - result.setHours(result.getHours() + amount); - break; - case "day": - result.setDate(result.getDate() + amount); - break; - case "week": - result.setDate(result.getDate() + amount * 7); - break; - case "month": - result.setMonth(result.getMonth() + amount); - break; - case "quarter": - result.setMonth(result.getMonth() + amount * 3); - break; - case "year": - result.setFullYear(result.getFullYear() + amount); - break; - } - return result; - } - function snapDateToUnit(value, unit) { - const start = startOfUnit(value, unit); - const end = endOfUnit(value, unit); - return value.getTime() - start.getTime() < end.getTime() - value.getTime() ? start : end; - } - const styles$x = i$5` + } + }; + _defineProperty(HassRecordsHistoryCardEditor, "styles", [EditorBase.styles, styles$51]); + //#endregion + //#region custom_components/hass_datapoints/src/lib/data/preferences-api.ts + /** HA user-data key for the saved history page. Stored via frontend/set_user_data. */ + var PANEL_HISTORY_SAVED_PAGE_KEY = "hass_datapoints:saved_page_v1"; + async function fetchUserData(hass, key, defaultValue = null) { + try { + return (await hass.connection.sendMessagePromise({ + type: "frontend/get_user_data", + key + }))?.value ?? defaultValue; + } catch (err) { + logger.warn("[hass-datapoints] fetchUserData failed:", err); + return defaultValue; + } + } + async function saveUserData(hass, key, value) { + try { + await hass.connection.sendMessagePromise({ + type: "frontend/set_user_data", + key, + value + }); + } catch (err) { + logger.warn("[hass-datapoints] saveUserData failed:", err); + } + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/timeline/timeline-scale.ts + /** + * Timeline subsystem helpers: range math, labels, snapping, and scale config. + */ + var SECOND_MS = 1e3; + var MINUTE_MS = 60 * SECOND_MS; + var HOUR_MS = 60 * MINUTE_MS; + var DAY_MS = 24 * HOUR_MS; + 7 * DAY_MS; + var RANGE_SLIDER_MIN_SPAN_MS = 900 * 1e3; + 30 * DAY_MS; + var RANGE_AUTO_ZOOM_DEBOUNCE_MS = 3e3; + var RANGE_AUTO_ZOOM_SELECTION_PADDING_RATIO = .6; + var RANGE_ZOOM_OPTIONS = [ + { + value: "auto", + label: "Auto" + }, + { + value: "quarterly", + label: "Quarterly" + }, + { + value: "month_compressed", + label: "Month Compressed" + }, + { + value: "month_short", + label: "Month Short" + }, + { + value: "month_expanded", + label: "Month Expanded" + }, + { + value: "week_compressed", + label: "Week Compressed" + }, + { + value: "week_expanded", + label: "Week Expanded" + }, + { + value: "day", + label: "Day" + } + ]; + var RANGE_SNAP_OPTIONS = [ + { + value: "auto", + label: "Auto" + }, + { + value: "month", + label: "Month" + }, + { + value: "week", + label: "Week" + }, + { + value: "day", + label: "Day" + }, + { + value: "hour", + label: "Hour" + }, + { + value: "minute", + label: "Minute" + }, + { + value: "second", + label: "Second" + } + ]; + var RANGE_ZOOM_CONFIGS = { + quarterly: { + baselineMs: 730 * DAY_MS, + boundsUnit: "month", + contextUnit: "year", + detailUnit: "month", + majorUnit: "quarter", + labelUnit: "quarter", + minorUnit: "month", + pixelsPerUnit: 96 + }, + month_compressed: { + baselineMs: 365 * DAY_MS, + boundsUnit: "month", + contextUnit: "year", + detailUnit: "week", + majorUnit: "month", + labelUnit: "month", + minorUnit: "month", + pixelsPerUnit: 76 + }, + month_short: { + baselineMs: 180 * DAY_MS, + boundsUnit: "week", + contextUnit: "month", + detailUnit: "day", + majorUnit: "week", + labelUnit: "week", + minorUnit: "week", + pixelsPerUnit: 54 + }, + month_expanded: { + baselineMs: 90 * DAY_MS, + boundsUnit: "week", + contextUnit: "month", + detailUnit: "day", + majorUnit: "week", + labelUnit: "week", + minorUnit: "week", + pixelsPerUnit: 72 + }, + week_compressed: { + baselineMs: 56 * DAY_MS, + boundsUnit: "week", + contextUnit: "month", + detailUnit: "day", + majorUnit: "week", + labelUnit: "week", + minorUnit: "week", + pixelsPerUnit: 120 + }, + week_expanded: { + baselineMs: 28 * DAY_MS, + boundsUnit: "day", + contextUnit: "month", + detailUnit: "hour", + detailStep: 12, + majorUnit: "day", + labelUnit: "day", + minorUnit: "day", + pixelsPerUnit: 30 + }, + day: { + baselineMs: 48 * HOUR_MS, + boundsUnit: "hour", + contextUnit: "day", + majorUnit: "hour", + labelUnit: "hour", + minorUnit: "hour", + pixelsPerUnit: 9 + } + }; + function extractRangeValue(source) { + if (!source) return { + start: null, + end: null + }; + const detail = source.detail || {}; + const value = detail.value || source.value || source.target?.value || {}; + return { + start: parseDateValue(detail.startDate || value.startDate || source.startDate || source.target?.startDate), + end: parseDateValue(detail.endDate || value.endDate || source.endDate || source.target?.endDate) + }; + } + function formatRangeDateTime(value, locale) { + if (!(value instanceof Date) || Number.isNaN(value.getTime())) return "--"; + return value.toLocaleString(locale ? [locale] : [], { + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit" + }); + } + function formatRangeTick(value, locale) { + if (!(value instanceof Date) || Number.isNaN(value.getTime())) return "--"; + return value.toLocaleString(locale ? [locale] : [], { + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit" + }); + } + function clampNumber(value, min, max) { + return Math.min(max, Math.max(min, value)); + } + function startOfLocalDay(value) { + return new Date(value.getFullYear(), value.getMonth(), value.getDate()); + } + function startOfLocalHour(value) { + return new Date(value.getFullYear(), value.getMonth(), value.getDate(), value.getHours(), 0, 0, 0); + } + function startOfLocalMinute(value) { + return new Date(value.getFullYear(), value.getMonth(), value.getDate(), value.getHours(), value.getMinutes(), 0, 0); + } + function startOfLocalSecond(value) { + return new Date(value.getFullYear(), value.getMonth(), value.getDate(), value.getHours(), value.getMinutes(), value.getSeconds(), 0); + } + function startOfLocalMonth(value) { + return new Date(value.getFullYear(), value.getMonth(), 1); + } + function endOfLocalMonth(value) { + return new Date(value.getFullYear(), value.getMonth() + 1, 1); + } + function startOfLocalYear(value) { + return new Date(value.getFullYear(), 0, 1); + } + function startOfLocalWeek(value) { + const day = value.getDay(); + const mondayOffset = day === 0 ? -6 : 1 - day; + const result = startOfLocalDay(value); + result.setDate(result.getDate() + mondayOffset); + return result; + } + function startOfLocalQuarter(value) { + return new Date(value.getFullYear(), Math.floor(value.getMonth() / 3) * 3, 1); + } + function endOfLocalHour(value) { + const result = startOfLocalHour(value); + result.setHours(result.getHours() + 1); + return result; + } + function endOfLocalDay(value) { + const result = startOfLocalDay(value); + result.setDate(result.getDate() + 1); + return result; + } + function endOfLocalWeek(value) { + const result = startOfLocalWeek(value); + result.setDate(result.getDate() + 7); + return result; + } + function endOfLocalQuarter(value) { + const result = startOfLocalQuarter(value); + result.setMonth(result.getMonth() + 3); + return result; + } + function endOfLocalMinute(value) { + const result = startOfLocalMinute(value); + result.setMinutes(result.getMinutes() + 1); + return result; + } + function endOfLocalSecond(value) { + const result = startOfLocalSecond(value); + result.setSeconds(result.getSeconds() + 1); + return result; + } + function formatMonthLabel(value, locale) { + return value.toLocaleString(locale ? [locale] : [], { month: "short" }); + } + function formatYearLabel(value, locale) { + return value.toLocaleString(locale ? [locale] : [], { year: "numeric" }); + } + function getWeekOfYear(value) { + const date = new Date(value.getTime()); + date.setHours(0, 0, 0, 0); + const day = date.getDay() || 7; + date.setDate(date.getDate() + 4 - day); + const yearStart = new Date(date.getFullYear(), 0, 1); + return Math.ceil(((date.getTime() - yearStart.getTime()) / DAY_MS + 1) / 7); + } + function getWeekLabel(value, locale) { + return value.toLocaleString(locale ? [locale] : [], { + month: "short", + day: "numeric" + }); + } + function formatDayLabel(value, locale) { + return value.toLocaleString(locale ? [locale] : [], { day: "numeric" }); + } + function formatHourLabel(value, locale) { + return value.toLocaleTimeString(locale ? [locale] : [], { hour: "2-digit" }); + } + function formatQuarterLabel(value, zoomLevel = "", locale) { + return zoomLevel === "quarterly" ? `Q${Math.floor(value.getMonth() / 3) + 1}` : formatMonthLabel(value, locale); + } + function formatScaleLabel(value, unit, zoomLevel = "", locale) { + switch (unit) { + case "quarter": return formatQuarterLabel(value, zoomLevel, locale); + case "month": return formatMonthLabel(value, locale); + case "week": return zoomLevel === "month_short" ? `${msg("Wk")} ${getWeekOfYear(value)}` : getWeekLabel(value, locale); + case "day": return formatDayLabel(value, locale); + case "hour": return formatHourLabel(value, locale); + default: return formatRangeTick(value, locale); + } + } + function formatContextLabel(value, unit, locale) { + switch (unit) { + case "year": return formatYearLabel(value, locale); + case "month": return value.toLocaleString(locale ? [locale] : [], { + month: "short", + year: "numeric" + }); + case "day": return value.toLocaleString(locale ? [locale] : [], { + month: "short", + day: "numeric" + }); + default: return formatRangeTick(value, locale); + } + } + function formatPeriodSelectionLabel(value, unit, locale) { + switch (unit) { + case "year": return formatYearLabel(value, locale); + case "quarter": return `${formatQuarterLabel(value, "", locale)} ${formatYearLabel(value, locale)}`; + case "month": return value.toLocaleString(locale ? [locale] : [], { + month: "long", + year: "numeric" + }); + case "week": return `${msg("Week of")} ${value.toLocaleString(locale ? [locale] : [], { + month: "short", + day: "numeric", + year: "numeric" + })}`; + case "day": return value.toLocaleString(locale ? [locale] : [], { + month: "short", + day: "numeric", + year: "numeric" + }); + case "hour": return value.toLocaleString(locale ? [locale] : [], { + month: "short", + day: "numeric", + year: "numeric", + hour: "2-digit" + }); + default: return formatRangeTick(value, locale); + } + } + function startOfUnit(value, unit) { + switch (unit) { + case "second": return startOfLocalSecond(value); + case "minute": return startOfLocalMinute(value); + case "hour": return startOfLocalHour(value); + case "day": return startOfLocalDay(value); + case "week": return startOfLocalWeek(value); + case "month": return startOfLocalMonth(value); + case "quarter": return startOfLocalQuarter(value); + case "year": return startOfLocalYear(value); + default: return new Date(value); + } + } + function endOfUnit(value, unit) { + switch (unit) { + case "second": return endOfLocalSecond(value); + case "minute": return endOfLocalMinute(value); + case "hour": return endOfLocalHour(value); + case "day": return endOfLocalDay(value); + case "week": return endOfLocalWeek(value); + case "month": return endOfLocalMonth(value); + case "quarter": return endOfLocalQuarter(value); + case "year": { + const result = startOfLocalYear(value); + result.setFullYear(result.getFullYear() + 1); + return result; + } + default: return new Date(value); + } + } + function addUnit(value, unit, amount = 1) { + const result = new Date(value); + switch (unit) { + case "second": + result.setSeconds(result.getSeconds() + amount); + break; + case "minute": + result.setMinutes(result.getMinutes() + amount); + break; + case "hour": + result.setHours(result.getHours() + amount); + break; + case "day": + result.setDate(result.getDate() + amount); + break; + case "week": + result.setDate(result.getDate() + amount * 7); + break; + case "month": + result.setMonth(result.getMonth() + amount); + break; + case "quarter": + result.setMonth(result.getMonth() + amount * 3); + break; + case "year": + result.setFullYear(result.getFullYear() + amount); + break; + default: break; + } + return result; + } + function snapDateToUnit(value, unit) { + const start = startOfUnit(value, unit); + const end = endOfUnit(value, unit); + return value.getTime() - start.getTime() < end.getTime() - value.getTime() ? start : end; + } + //#endregion + //#region custom_components/hass_datapoints/src/molecules/target-row/target-row.styles.ts + var styles$33 = i$5` :host { display: block; --dp-spacing-xs: calc(var(--spacing, 8px) * 0.5); @@ -20028,169 +20054,207 @@ ${content.alert}` : "", border-radius: 4px; } `; - var __create$r = Object.create; - var __defProp$y = Object.defineProperty; - var __getOwnPropDesc$r = Object.getOwnPropertyDescriptor; - var __knownSymbol$r = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$r = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$y = (obj, key, value) => key in obj ? __defProp$y(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __name$8 = (target, value) => __defProp$y(target, "name", { value, configurable: true }); - var __decoratorStart$r = (base) => [, , , __create$r(base?.[__knownSymbol$r("metadata")] ?? null)]; - var __decoratorStrings$r = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$r = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$r("Function expected") : fn; - var __decoratorContext$r = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$r[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$r("Already initialized") : fns.push(__expectFn$r(fn || null)) }); - var __decoratorMetadata$r = (array, target) => __defNormalProp$y(target, __knownSymbol$r("metadata"), array[3]); - var __runInitializers$r = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$r = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = !!(flags & 8), p2 = !!(flags & 16); - var j2 = k2 > 3 ? array.length + 1 : k2 ? s2 ? 1 : 2 : 0, key = __decoratorStrings$r[k2 + 5]; - var initializers = k2 > 3 && (array[j2 - 1] = []), extraInitializers = array[j2] || (array[j2] = []); - var desc = k2 && (!p2 && !s2 && (target = target.prototype), k2 < 5 && (k2 > 3 || !p2) && __getOwnPropDesc$r(k2 < 4 ? target : { get [name]() { - return __privateGet$q(this, extra); - }, set [name](x2) { - return __privateSet$q(this, extra, x2); - } }, name)); - k2 ? p2 && k2 < 4 && __name$8(extra, (k2 > 2 ? "set " : k2 > 1 ? "get " : "") + name) : __name$8(target, name); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$r(k2, name, done = {}, array[3], extraInitializers); - if (k2) { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: p2 ? (x2) => __privateIn$7(target, x2) : (x2) => name in x2 }; - if (k2 ^ 3) access.get = p2 ? (x2) => (k2 ^ 1 ? __privateGet$q : __privateMethod$7)(x2, target, k2 ^ 4 ? extra : desc.get) : (x2) => x2[name]; - if (k2 > 2) access.set = p2 ? (x2, y2) => __privateSet$q(x2, target, y2, k2 ^ 4 ? extra : desc.set) : (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])(k2 ? k2 < 4 ? p2 ? extra : desc[key] : k2 > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; - if (k2 ^ 4 || it === void 0) __expectFn$r(it) && (k2 > 4 ? initializers.unshift(it) : k2 ? p2 ? extra = it : desc[key] = it : target = it); - else if (typeof it !== "object" || it === null) __typeError$r("Object expected"); - else __expectFn$r(fn = it.get) && (desc.get = fn), __expectFn$r(fn = it.set) && (desc.set = fn), __expectFn$r(fn = it.init) && initializers.unshift(fn); - } - return k2 || __decoratorMetadata$r(array, target), desc && __defProp$y(target, name, desc), p2 ? k2 ^ 4 ? extra : desc : target; - }; - var __publicField$y = (obj, key, value) => __defNormalProp$y(obj, key + "", value); - var __accessCheck$q = (obj, member, msg2) => member.has(obj) || __typeError$r("Cannot " + msg2); - var __privateIn$7 = (member, obj) => Object(obj) !== obj ? __typeError$r('Cannot use the "in" operator on this value') : member.has(obj); - var __privateGet$q = (obj, member, getter) => (__accessCheck$q(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); - var __privateAdd$q = (obj, member, value) => member.has(obj) ? __typeError$r("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$q = (obj, member, value, setter) => (__accessCheck$q(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); - var __privateMethod$7 = (obj, member, method) => (__accessCheck$q(obj, member, "access private method"), method); - var _hideDragHandle_dec, _allAnalysisSame_dec, _rowCount_dec, _computingMethods_dec, _computingProgress_dec, _computing_dec, _comparisonWindows_dec$2, _hass_dec$7, _stateObj_dec$1, _canShowDeltaAnalysis_dec$2, _entityId_dec, _index_dec, _analysis_dec, _visible_dec, _color_dec$1, _a$r, _TargetRow_decorators, _init$r, _color$1, _visible, _analysis, _index, _entityId, _canShowDeltaAnalysis$2, _stateObj$1, _hass$7, _comparisonWindows$2, _computing, _computingProgress, _computingMethods, _rowCount, _allAnalysisSame, _hideDragHandle; - function deriveSwatchIconColor(color) { - const hex = String(color || "").trim(); - const normalizedHex = /^#([0-9a-f]{6})$/i.test(hex) ? hex : null; - if (!normalizedHex) { - return "#ffffff"; - } - const channels = normalizedHex.slice(1).match(/.{2}/g)?.map((p2) => parseInt(p2, 16)); - if (!channels || channels.length !== 3) { - return "#ffffff"; - } - const [r2, g2, b2] = channels; - const luminance = (0.299 * r2 + 0.587 * g2 + 0.114 * b2) / 255; - const mixTarget = luminance > 0.62 ? 0 : 255; - const mixStrength = luminance > 0.62 ? Math.min(0.82, 0.35 + (luminance - 0.62) * 1.6) : Math.min(0.78, 0.4 + (0.62 - luminance) * 0.9); - const mixed = [r2, g2, b2].map( - (c2) => Math.max( - 0, - Math.min(255, Math.round(c2 * (1 - mixStrength) + mixTarget * mixStrength)) - ) - ); - return `rgb(${mixed[0]}, ${mixed[1]}, ${mixed[2]})`; - } - function _hasConfiguredAnalysis(a2) { - return a2.show_trend_lines || a2.show_summary_stats || a2.show_rate_of_change || a2.show_threshold_analysis || a2.show_anomalies || a2.show_delta_analysis || a2.stepped_series || a2.hide_source_series; - } - function _hasActiveAnalysis(a2, hasComparisonWindow) { - return a2.show_trend_lines || a2.show_summary_stats || a2.show_rate_of_change || a2.show_threshold_analysis || a2.show_anomalies || a2.show_delta_analysis && hasComparisonWindow; - } - _TargetRow_decorators = [localized()]; - class TargetRow extends (_a$r = i$2, _color_dec$1 = [n({ type: String })], _visible_dec = [n({ type: Boolean })], _analysis_dec = [n({ type: Object })], _index_dec = [n({ type: Number })], _entityId_dec = [n({ type: String, attribute: "entity-id" })], _canShowDeltaAnalysis_dec$2 = [n({ type: Boolean, attribute: "can-show-delta-analysis" })], _stateObj_dec$1 = [n({ type: Object, attribute: false })], _hass_dec$7 = [n({ type: Object, attribute: false })], _comparisonWindows_dec$2 = [n({ type: Array, attribute: "comparison-windows" })], _computing_dec = [n({ type: Boolean, attribute: false })], _computingProgress_dec = [n({ type: Number, attribute: false })], _computingMethods_dec = [n({ type: Object, attribute: false })], _rowCount_dec = [n({ type: Number, attribute: false })], _allAnalysisSame_dec = [n({ type: Boolean, attribute: false })], _hideDragHandle_dec = [n({ type: Boolean, attribute: "hide-drag-handle" })], _a$r) { - constructor() { - super(...arguments); - __privateAdd$q(this, _color$1, __runInitializers$r(_init$r, 8, this, "#03a9f4")), __runInitializers$r(_init$r, 11, this); - __privateAdd$q(this, _visible, __runInitializers$r(_init$r, 12, this, true)), __runInitializers$r(_init$r, 15, this); - __privateAdd$q(this, _analysis, __runInitializers$r(_init$r, 16, this, {})), __runInitializers$r(_init$r, 19, this); - __privateAdd$q(this, _index, __runInitializers$r(_init$r, 20, this, 0)), __runInitializers$r(_init$r, 23, this); - __privateAdd$q(this, _entityId, __runInitializers$r(_init$r, 24, this, "")), __runInitializers$r(_init$r, 27, this); - __privateAdd$q(this, _canShowDeltaAnalysis$2, __runInitializers$r(_init$r, 28, this, false)), __runInitializers$r(_init$r, 31, this); - __privateAdd$q(this, _stateObj$1, __runInitializers$r(_init$r, 32, this, null)), __runInitializers$r(_init$r, 35, this); - __privateAdd$q(this, _hass$7, __runInitializers$r(_init$r, 36, this, null)), __runInitializers$r(_init$r, 39, this); - __privateAdd$q(this, _comparisonWindows$2, __runInitializers$r(_init$r, 40, this, [])), __runInitializers$r(_init$r, 43, this); - __privateAdd$q(this, _computing, __runInitializers$r(_init$r, 44, this, false)), __runInitializers$r(_init$r, 47, this); - __privateAdd$q(this, _computingProgress, __runInitializers$r(_init$r, 48, this, 0)), __runInitializers$r(_init$r, 51, this); - __privateAdd$q(this, _computingMethods, __runInitializers$r(_init$r, 52, this, /* @__PURE__ */ new Set())), __runInitializers$r(_init$r, 55, this); - __privateAdd$q(this, _rowCount, __runInitializers$r(_init$r, 56, this, 1)), __runInitializers$r(_init$r, 59, this); - __privateAdd$q(this, _allAnalysisSame, __runInitializers$r(_init$r, 60, this, false)), __runInitializers$r(_init$r, 63, this); - __privateAdd$q(this, _hideDragHandle, __runInitializers$r(_init$r, 64, this, false)), __runInitializers$r(_init$r, 67, this); - } - /** Entity ID — from HA state when available, else from the config prop. */ - get _entityId() { - return this.stateObj?.entity_id ?? this.entityId ?? ""; - } - /** Display name derived from the HA state object, falling back to the entity ID. */ - get _entityName() { - return this.stateObj?.attributes?.friendly_name ?? this._entityId; - } - /** Unit of measurement derived from the HA state object. */ - get _unit() { - return this.stateObj?.attributes?.unit_of_measurement ?? ""; - } - get _supportsAnalysis() { - return Boolean(this._entityId) && !this._entityId.startsWith("binary_sensor."); - } - _emit(name, detail) { - this.dispatchEvent( - new CustomEvent(name, { detail, bubbles: true, composed: true }) - ); - } - _onColorChange(e2) { - this._emit("dp-row-color-change", { - index: this.index, - color: e2.target.value - }); - } - _onVisibilityChange(e2) { - this._emit("dp-row-visibility-change", { - entityId: this._entityId, - visible: e2.target.checked - }); - } - _onAnalysisToggle() { - this._emit("dp-row-toggle-analysis", { entityId: this._entityId }); - } - _onRemove() { - this._emit("dp-row-remove", { index: this.index }); - } - _onCheckbox(key, e2) { - this._emit("dp-row-analysis-change", { - entityId: this._entityId, - key, - value: e2.target.checked - }); - } - _onCopyAnalysisToAll() { - this._emit("dp-row-copy-analysis-to-all", { - entityId: this._entityId, - analysis: this.analysis - }); - } - _onGroupAnalysisChange(e2) { - this._emit("dp-row-analysis-change", e2.detail); - } - render() { - const a2 = this.analysis || {}; - const hasConfigured = _hasConfiguredAnalysis(a2); - const hasActive = _hasActiveAnalysis(a2, this.canShowDeltaAnalysis); - const rowClass = [ - "history-target-row", - this.visible === false ? "is-hidden" : "", - this.analysis?.expanded ? "analysis-open" : "" - ].filter(Boolean).join(" "); - return b` -
+ //#endregion + //#region custom_components/hass_datapoints/src/molecules/target-row/target-row.ts + var _TargetRow, _color_accessor_storage$1, _visible_accessor_storage, _analysis_accessor_storage, _index_accessor_storage, _entityId_accessor_storage, _canShowDeltaAnalysis_accessor_storage$2, _stateObj_accessor_storage$1, _hass_accessor_storage$7, _comparisonWindows_accessor_storage$2, _computing_accessor_storage, _computingProgress_accessor_storage, _computingMethods_accessor_storage, _rowCount_accessor_storage, _allAnalysisSame_accessor_storage, _hideDragHandle_accessor_storage; + function deriveSwatchIconColor(color) { + const hex = String(color || "").trim(); + const normalizedHex = /^#([0-9a-f]{6})$/i.test(hex) ? hex : null; + if (!normalizedHex) return "#ffffff"; + const channels = normalizedHex.slice(1).match(/.{2}/g)?.map((p) => parseInt(p, 16)); + if (!channels || channels.length !== 3) return "#ffffff"; + const [r, g, b] = channels; + const luminance = (.299 * r + .587 * g + .114 * b) / 255; + const mixTarget = luminance > .62 ? 0 : 255; + const mixStrength = luminance > .62 ? Math.min(.82, .35 + (luminance - .62) * 1.6) : Math.min(.78, .4 + (.62 - luminance) * .9); + const mixed = [ + r, + g, + b + ].map((c) => Math.max(0, Math.min(255, Math.round(c * (1 - mixStrength) + mixTarget * mixStrength)))); + return `rgb(${mixed[0]}, ${mixed[1]}, ${mixed[2]})`; + } + function _hasConfiguredAnalysis(a) { + return a.show_trend_lines || a.show_summary_stats || a.show_rate_of_change || a.show_threshold_analysis || a.show_anomalies || a.show_delta_analysis || a.stepped_series || a.hide_source_series; + } + function _hasActiveAnalysis(a, hasComparisonWindow) { + return a.show_trend_lines || a.show_summary_stats || a.show_rate_of_change || a.show_threshold_analysis || a.show_anomalies || a.show_delta_analysis && hasComparisonWindow; + } + var TargetRow = (_color_accessor_storage$1 = /* @__PURE__ */ new WeakMap(), _visible_accessor_storage = /* @__PURE__ */ new WeakMap(), _analysis_accessor_storage = /* @__PURE__ */ new WeakMap(), _index_accessor_storage = /* @__PURE__ */ new WeakMap(), _entityId_accessor_storage = /* @__PURE__ */ new WeakMap(), _canShowDeltaAnalysis_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _stateObj_accessor_storage$1 = /* @__PURE__ */ new WeakMap(), _hass_accessor_storage$7 = /* @__PURE__ */ new WeakMap(), _comparisonWindows_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _computing_accessor_storage = /* @__PURE__ */ new WeakMap(), _computingProgress_accessor_storage = /* @__PURE__ */ new WeakMap(), _computingMethods_accessor_storage = /* @__PURE__ */ new WeakMap(), _rowCount_accessor_storage = /* @__PURE__ */ new WeakMap(), _allAnalysisSame_accessor_storage = /* @__PURE__ */ new WeakMap(), _hideDragHandle_accessor_storage = /* @__PURE__ */ new WeakMap(), _TargetRow = class TargetRow extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _color_accessor_storage$1, "#03a9f4"); + _classPrivateFieldInitSpec(this, _visible_accessor_storage, true); + _classPrivateFieldInitSpec(this, _analysis_accessor_storage, {}); + _classPrivateFieldInitSpec(this, _index_accessor_storage, 0); + _classPrivateFieldInitSpec(this, _entityId_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _canShowDeltaAnalysis_accessor_storage$2, false); + _classPrivateFieldInitSpec(this, _stateObj_accessor_storage$1, null); + _classPrivateFieldInitSpec(this, _hass_accessor_storage$7, null); + _classPrivateFieldInitSpec(this, _comparisonWindows_accessor_storage$2, []); + _classPrivateFieldInitSpec(this, _computing_accessor_storage, false); + _classPrivateFieldInitSpec(this, _computingProgress_accessor_storage, 0); + _classPrivateFieldInitSpec(this, _computingMethods_accessor_storage, /* @__PURE__ */ new Set()); + _classPrivateFieldInitSpec(this, _rowCount_accessor_storage, 1); + _classPrivateFieldInitSpec(this, _allAnalysisSame_accessor_storage, false); + _classPrivateFieldInitSpec(this, _hideDragHandle_accessor_storage, false); + } + get color() { + return _classPrivateFieldGet2(_color_accessor_storage$1, this); + } + set color(value) { + _classPrivateFieldSet2(_color_accessor_storage$1, this, value); + } + get visible() { + return _classPrivateFieldGet2(_visible_accessor_storage, this); + } + set visible(value) { + _classPrivateFieldSet2(_visible_accessor_storage, this, value); + } + get analysis() { + return _classPrivateFieldGet2(_analysis_accessor_storage, this); + } + set analysis(value) { + _classPrivateFieldSet2(_analysis_accessor_storage, this, value); + } + get index() { + return _classPrivateFieldGet2(_index_accessor_storage, this); + } + set index(value) { + _classPrivateFieldSet2(_index_accessor_storage, this, value); + } + get entityId() { + return _classPrivateFieldGet2(_entityId_accessor_storage, this); + } + set entityId(value) { + _classPrivateFieldSet2(_entityId_accessor_storage, this, value); + } + get canShowDeltaAnalysis() { + return _classPrivateFieldGet2(_canShowDeltaAnalysis_accessor_storage$2, this); + } + set canShowDeltaAnalysis(value) { + _classPrivateFieldSet2(_canShowDeltaAnalysis_accessor_storage$2, this, value); + } + get stateObj() { + return _classPrivateFieldGet2(_stateObj_accessor_storage$1, this); + } + set stateObj(value) { + _classPrivateFieldSet2(_stateObj_accessor_storage$1, this, value); + } + get hass() { + return _classPrivateFieldGet2(_hass_accessor_storage$7, this); + } + set hass(value) { + _classPrivateFieldSet2(_hass_accessor_storage$7, this, value); + } + get comparisonWindows() { + return _classPrivateFieldGet2(_comparisonWindows_accessor_storage$2, this); + } + set comparisonWindows(value) { + _classPrivateFieldSet2(_comparisonWindows_accessor_storage$2, this, value); + } + get computing() { + return _classPrivateFieldGet2(_computing_accessor_storage, this); + } + set computing(value) { + _classPrivateFieldSet2(_computing_accessor_storage, this, value); + } + get computingProgress() { + return _classPrivateFieldGet2(_computingProgress_accessor_storage, this); + } + set computingProgress(value) { + _classPrivateFieldSet2(_computingProgress_accessor_storage, this, value); + } + get computingMethods() { + return _classPrivateFieldGet2(_computingMethods_accessor_storage, this); + } + set computingMethods(value) { + _classPrivateFieldSet2(_computingMethods_accessor_storage, this, value); + } + get rowCount() { + return _classPrivateFieldGet2(_rowCount_accessor_storage, this); + } + set rowCount(value) { + _classPrivateFieldSet2(_rowCount_accessor_storage, this, value); + } + get allAnalysisSame() { + return _classPrivateFieldGet2(_allAnalysisSame_accessor_storage, this); + } + set allAnalysisSame(value) { + _classPrivateFieldSet2(_allAnalysisSame_accessor_storage, this, value); + } + get hideDragHandle() { + return _classPrivateFieldGet2(_hideDragHandle_accessor_storage, this); + } + set hideDragHandle(value) { + _classPrivateFieldSet2(_hideDragHandle_accessor_storage, this, value); + } + /** Entity ID — from HA state when available, else from the config prop. */ + get _entityId() { + return this.stateObj?.entity_id ?? this.entityId ?? ""; + } + /** Display name derived from the HA state object, falling back to the entity ID. */ + get _entityName() { + return (this.stateObj?.attributes)?.friendly_name ?? this._entityId; + } + /** Unit of measurement derived from the HA state object. */ + get _unit() { + return (this.stateObj?.attributes)?.unit_of_measurement ?? ""; + } + get _supportsAnalysis() { + return Boolean(this._entityId) && !this._entityId.startsWith("binary_sensor."); + } + _emit(name, detail) { + this.dispatchEvent(new CustomEvent(name, { + detail, + bubbles: true, + composed: true + })); + } + _onColorChange(e) { + this._emit("dp-row-color-change", { + index: this.index, + color: e.target.value + }); + } + _onVisibilityChange(e) { + this._emit("dp-row-visibility-change", { + entityId: this._entityId, + visible: e.target.checked + }); + } + _onAnalysisToggle() { + this._emit("dp-row-toggle-analysis", { entityId: this._entityId }); + } + _onRemove() { + this._emit("dp-row-remove", { index: this.index }); + } + _onCheckbox(key, e) { + this._emit("dp-row-analysis-change", { + entityId: this._entityId, + key, + value: e.target.checked + }); + } + _onCopyAnalysisToAll() { + this._emit("dp-row-copy-analysis-to-all", { + entityId: this._entityId, + analysis: this.analysis + }); + } + _onGroupAnalysisChange(e) { + this._emit("dp-row-analysis-change", e.detail); + } + render() { + const a = this.analysis || {}; + const hasConfigured = _hasConfiguredAnalysis(a); + const hasActive = _hasActiveAnalysis(a, this.canShowDeltaAnalysis); + return b` +
${this.hideDragHandle ? A : b`
`; - } - } - _init$r = __decoratorStart$r(_a$r); - _color$1 = /* @__PURE__ */ new WeakMap(); - _visible = /* @__PURE__ */ new WeakMap(); - _analysis = /* @__PURE__ */ new WeakMap(); - _index = /* @__PURE__ */ new WeakMap(); - _entityId = /* @__PURE__ */ new WeakMap(); - _canShowDeltaAnalysis$2 = /* @__PURE__ */ new WeakMap(); - _stateObj$1 = /* @__PURE__ */ new WeakMap(); - _hass$7 = /* @__PURE__ */ new WeakMap(); - _comparisonWindows$2 = /* @__PURE__ */ new WeakMap(); - _computing = /* @__PURE__ */ new WeakMap(); - _computingProgress = /* @__PURE__ */ new WeakMap(); - _computingMethods = /* @__PURE__ */ new WeakMap(); - _rowCount = /* @__PURE__ */ new WeakMap(); - _allAnalysisSame = /* @__PURE__ */ new WeakMap(); - _hideDragHandle = /* @__PURE__ */ new WeakMap(); - __decorateElement$r(_init$r, 4, "color", _color_dec$1, TargetRow, _color$1); - __decorateElement$r(_init$r, 4, "visible", _visible_dec, TargetRow, _visible); - __decorateElement$r(_init$r, 4, "analysis", _analysis_dec, TargetRow, _analysis); - __decorateElement$r(_init$r, 4, "index", _index_dec, TargetRow, _index); - __decorateElement$r(_init$r, 4, "entityId", _entityId_dec, TargetRow, _entityId); - __decorateElement$r(_init$r, 4, "canShowDeltaAnalysis", _canShowDeltaAnalysis_dec$2, TargetRow, _canShowDeltaAnalysis$2); - __decorateElement$r(_init$r, 4, "stateObj", _stateObj_dec$1, TargetRow, _stateObj$1); - __decorateElement$r(_init$r, 4, "hass", _hass_dec$7, TargetRow, _hass$7); - __decorateElement$r(_init$r, 4, "comparisonWindows", _comparisonWindows_dec$2, TargetRow, _comparisonWindows$2); - __decorateElement$r(_init$r, 4, "computing", _computing_dec, TargetRow, _computing); - __decorateElement$r(_init$r, 4, "computingProgress", _computingProgress_dec, TargetRow, _computingProgress); - __decorateElement$r(_init$r, 4, "computingMethods", _computingMethods_dec, TargetRow, _computingMethods); - __decorateElement$r(_init$r, 4, "rowCount", _rowCount_dec, TargetRow, _rowCount); - __decorateElement$r(_init$r, 4, "allAnalysisSame", _allAnalysisSame_dec, TargetRow, _allAnalysisSame); - __decorateElement$r(_init$r, 4, "hideDragHandle", _hideDragHandle_dec, TargetRow, _hideDragHandle); - TargetRow = __decorateElement$r(_init$r, 0, "TargetRow", _TargetRow_decorators, TargetRow); - __publicField$y(TargetRow, "styles", styles$x); - __runInitializers$r(_init$r, 1, TargetRow); - customElements.define("target-row", TargetRow); - const styles$w = i$5` + } + }, _defineProperty(_TargetRow, "styles", styles$33), _TargetRow); + __decorate([n$1({ type: String })], TargetRow.prototype, "color", null); + __decorate([n$1({ type: Boolean })], TargetRow.prototype, "visible", null); + __decorate([n$1({ type: Object })], TargetRow.prototype, "analysis", null); + __decorate([n$1({ type: Number })], TargetRow.prototype, "index", null); + __decorate([n$1({ + type: String, + attribute: "entity-id" + })], TargetRow.prototype, "entityId", null); + __decorate([n$1({ + type: Boolean, + attribute: "can-show-delta-analysis" + })], TargetRow.prototype, "canShowDeltaAnalysis", null); + __decorate([n$1({ + type: Object, + attribute: false + })], TargetRow.prototype, "stateObj", null); + __decorate([n$1({ + type: Object, + attribute: false + })], TargetRow.prototype, "hass", null); + __decorate([n$1({ + type: Array, + attribute: "comparison-windows" + })], TargetRow.prototype, "comparisonWindows", null); + __decorate([n$1({ + type: Boolean, + attribute: false + })], TargetRow.prototype, "computing", null); + __decorate([n$1({ + type: Number, + attribute: false + })], TargetRow.prototype, "computingProgress", null); + __decorate([n$1({ + type: Object, + attribute: false + })], TargetRow.prototype, "computingMethods", null); + __decorate([n$1({ + type: Number, + attribute: false + })], TargetRow.prototype, "rowCount", null); + __decorate([n$1({ + type: Boolean, + attribute: false + })], TargetRow.prototype, "allAnalysisSame", null); + __decorate([n$1({ + type: Boolean, + attribute: "hide-drag-handle" + })], TargetRow.prototype, "hideDragHandle", null); + TargetRow = __decorate([localized()], TargetRow); + customElements.define("target-row", TargetRow); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/target-row-list/target-row-list.styles.ts + var styles$32 = i$5` :host { display: block; --dp-spacing-xs: calc(var(--spacing, 8px) * 0.5); @@ -20470,185 +20547,225 @@ ${content.alert}` : "", cursor: grab; } `; - var __create$q = Object.create; - var __defProp$x = Object.defineProperty; - var __getOwnPropDesc$q = Object.getOwnPropertyDescriptor; - var __knownSymbol$q = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$q = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$x = (obj, key, value) => key in obj ? __defProp$x(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __name$7 = (target, value) => __defProp$x(target, "name", { value, configurable: true }); - var __decoratorStart$q = (base) => [, , , __create$q(base?.[__knownSymbol$q("metadata")] ?? null)]; - var __decoratorStrings$q = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$q = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$q("Function expected") : fn; - var __decoratorContext$q = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$q[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$q("Already initialized") : fns.push(__expectFn$q(fn || null)) }); - var __decoratorMetadata$q = (array, target) => __defNormalProp$x(target, __knownSymbol$q("metadata"), array[3]); - var __runInitializers$q = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$q = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = !!(flags & 8), p2 = !!(flags & 16); - var j2 = k2 > 3 ? array.length + 1 : k2 ? s2 ? 1 : 2 : 0, key = __decoratorStrings$q[k2 + 5]; - var initializers = k2 > 3 && (array[j2 - 1] = []), extraInitializers = array[j2] || (array[j2] = []); - var desc = k2 && (!p2 && !s2 && (target = target.prototype), k2 < 5 && (k2 > 3 || !p2) && __getOwnPropDesc$q(k2 < 4 ? target : { get [name]() { - return __privateGet$p(this, extra); - }, set [name](x2) { - return __privateSet$p(this, extra, x2); - } }, name)); - k2 ? p2 && k2 < 4 && __name$7(extra, (k2 > 2 ? "set " : k2 > 1 ? "get " : "") + name) : __name$7(target, name); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$q(k2, name, done = {}, array[3], extraInitializers); - if (k2) { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: p2 ? (x2) => __privateIn$6(target, x2) : (x2) => name in x2 }; - if (k2 ^ 3) access.get = p2 ? (x2) => (k2 ^ 1 ? __privateGet$p : __privateMethod$6)(x2, target, k2 ^ 4 ? extra : desc.get) : (x2) => x2[name]; - if (k2 > 2) access.set = p2 ? (x2, y2) => __privateSet$p(x2, target, y2, k2 ^ 4 ? extra : desc.set) : (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])(k2 ? k2 < 4 ? p2 ? extra : desc[key] : k2 > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; - if (k2 ^ 4 || it === void 0) __expectFn$q(it) && (k2 > 4 ? initializers.unshift(it) : k2 ? p2 ? extra = it : desc[key] = it : target = it); - else if (typeof it !== "object" || it === null) __typeError$q("Object expected"); - else __expectFn$q(fn = it.get) && (desc.get = fn), __expectFn$q(fn = it.set) && (desc.set = fn), __expectFn$q(fn = it.init) && initializers.unshift(fn); - } - return k2 || __decoratorMetadata$q(array, target), desc && __defProp$x(target, name, desc), p2 ? k2 ^ 4 ? extra : desc : target; - }; - var __publicField$x = (obj, key, value) => __defNormalProp$x(obj, typeof key !== "symbol" ? key + "" : key, value); - var __accessCheck$p = (obj, member, msg2) => member.has(obj) || __typeError$q("Cannot " + msg2); - var __privateIn$6 = (member, obj) => Object(obj) !== obj ? __typeError$q('Cannot use the "in" operator on this value') : member.has(obj); - var __privateGet$p = (obj, member, getter) => (__accessCheck$p(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); - var __privateAdd$p = (obj, member, value) => member.has(obj) ? __typeError$q("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$p = (obj, member, value, setter) => (__accessCheck$p(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); - var __privateMethod$6 = (obj, member, method) => (__accessCheck$p(obj, member, "access private method"), method); - var _computingMethodsByEntity_dec, _analysisProgress_dec, _computingEntityIds_dec, _comparisonWindows_dec$1, _canShowDeltaAnalysis_dec$1, _hass_dec$6, _states_dec$1, _rows_dec$1, _a$q, _TargetRowList_decorators, _init$q, _rows$1, _states$1, _hass$6, _canShowDeltaAnalysis$1, _comparisonWindows$1, _computingEntityIds, _analysisProgress, _computingMethodsByEntity; - const _DURATION_SECONDS = { - raw: 0, - "5s": 5, - "10s": 10, - "15s": 15, - "30s": 30, - "1m": 60, - "2m": 120, - "5m": 300, - "10m": 600, - "15m": 900, - "30m": 1800, - "1h": 3600, - "2h": 7200, - "3h": 10800, - "4h": 14400, - "6h": 21600, - "12h": 43200, - "24h": 86400, - "7d": 604800, - "14d": 1209600, - "21d": 1814400, - "28d": 2419200 - }; - const _WINDOW_OPTIONS = { - trend_window: ["1h", "6h", "24h", "7d", "14d", "21d", "28d"], - rate_window: ["1h", "6h", "24h"], - anomaly_rate_window: ["1h", "6h", "24h"], - anomaly_zscore_window: ["1h", "6h", "24h", "7d"], - anomaly_persistence_window: ["30m", "1h", "3h", "6h", "12h", "24h"] - }; - function _clampWindowsToInterval(analysis, newInterval) { - const intervalSecs = _DURATION_SECONDS[newInterval] ?? 0; - if (intervalSecs === 0) return {}; - const updates = {}; - for (const [key, options] of Object.entries(_WINDOW_OPTIONS)) { - const current = analysis[key]; - if (!current || current === "point_to_point") continue; - const currentSecs = _DURATION_SECONDS[current] ?? 0; - if (currentSecs < intervalSecs) { - const next = options.find( - (opt) => (_DURATION_SECONDS[opt] ?? 0) >= intervalSecs - ); - if (next) { - updates[key] = next; - } - } - } - return updates; - } - _TargetRowList_decorators = [localized()]; - class TargetRowList extends (_a$q = i$2, _rows_dec$1 = [n({ type: Array })], _states_dec$1 = [n({ type: Object, attribute: false })], _hass_dec$6 = [n({ type: Object, attribute: false })], _canShowDeltaAnalysis_dec$1 = [n({ type: Boolean, attribute: "can-show-delta-analysis" })], _comparisonWindows_dec$1 = [n({ type: Array, attribute: false })], _computingEntityIds_dec = [n({ type: Object, attribute: false })], _analysisProgress_dec = [n({ type: Number, attribute: false })], _computingMethodsByEntity_dec = [n({ type: Object, attribute: false })], _a$q) { - constructor() { - super(...arguments); - __privateAdd$p(this, _rows$1, __runInitializers$q(_init$q, 8, this, [])), __runInitializers$q(_init$q, 11, this); - __privateAdd$p(this, _states$1, __runInitializers$q(_init$q, 12, this, {})), __runInitializers$q(_init$q, 15, this); - __privateAdd$p(this, _hass$6, __runInitializers$q(_init$q, 16, this, null)), __runInitializers$q(_init$q, 19, this); - __privateAdd$p(this, _canShowDeltaAnalysis$1, __runInitializers$q(_init$q, 20, this, false)), __runInitializers$q(_init$q, 23, this); - __privateAdd$p(this, _comparisonWindows$1, __runInitializers$q(_init$q, 24, this, [])), __runInitializers$q(_init$q, 27, this); - __privateAdd$p(this, _computingEntityIds, __runInitializers$q(_init$q, 28, this, /* @__PURE__ */ new Set())), __runInitializers$q(_init$q, 31, this); - __privateAdd$p(this, _analysisProgress, __runInitializers$q(_init$q, 32, this, 0)), __runInitializers$q(_init$q, 35, this); - __privateAdd$p(this, _computingMethodsByEntity, __runInitializers$q(_init$q, 36, this, /* @__PURE__ */ new Map())), __runInitializers$q(_init$q, 39, this); - __publicField$x(this, "_dragSourceIndex", null); - __publicField$x(this, "_onToggleAnalysisFast", (e2) => { - const entityId = String(e2?.detail?.entityId || "").trim(); - if (!entityId) { - return; - } - const index = this.rows?.findIndex((r2) => r2.entity_id === entityId) ?? -1; - if (index === -1) { - return; - } - this.rows = this.rows.map((row, i2) => { - if (i2 !== index) { - return row; - } - return { - ...row, - analysis: { - ...row.analysis, - expanded: !row.analysis?.expanded - } - }; - }); - }); - __publicField$x(this, "_onRowAnalysisChangeFast", (e2) => { - const { entityId, key, value } = e2.detail || {}; - if (!entityId || !key) { - return; - } - const index = this.rows?.findIndex((r2) => r2.entity_id === entityId) ?? -1; - if (index === -1) { - return; - } - const row = this.rows[index]; - const currentAnalysis = row.analysis || {}; - let nextAnalysis; - if (key.startsWith("anomaly_method_toggle_")) { - const method = key.slice("anomaly_method_toggle_".length); - const currentMethods = Array.isArray(currentAnalysis.anomaly_methods) ? currentAnalysis.anomaly_methods : []; - const nextMethods = value === true ? [.../* @__PURE__ */ new Set([...currentMethods, method])] : currentMethods.filter((m2) => m2 !== method); - nextAnalysis = { ...currentAnalysis, anomaly_methods: nextMethods }; - } else if (key === "sample_interval" && typeof value === "string") { - const windowUpdates = _clampWindowsToInterval(currentAnalysis, value); - nextAnalysis = { ...currentAnalysis, [key]: value, ...windowUpdates }; - } else { - nextAnalysis = { ...currentAnalysis, [key]: value }; - } - this.rows = this.rows.map( - (r2, i2) => i2 === index ? { ...r2, analysis: nextAnalysis } : r2 - ); - }); - __publicField$x(this, "_onDragEnd", (e2) => { - this._dragSourceIndex = null; - const target = e2.currentTarget; - target.classList.remove("is-dragging"); - this._clearDropIndicators(); - this._removeDragCursorStyle(); - }); - } - render() { - if (!this.rows.length) { - return b``; - } - const firstAnalysis = JSON.stringify(this.rows[0]?.analysis ?? {}); - const allAnalysisSame = this.rows.every( - (r2) => JSON.stringify(r2.analysis ?? {}) === firstAnalysis - ); - return b` + //#endregion + //#region custom_components/hass_datapoints/src/molecules/target-row-list/target-row-list.ts + var _TargetRowList, _rows_accessor_storage$1, _states_accessor_storage$1, _hass_accessor_storage$6, _canShowDeltaAnalysis_accessor_storage$1, _comparisonWindows_accessor_storage$1, _computingEntityIds_accessor_storage, _analysisProgress_accessor_storage, _computingMethodsByEn_accessor_storage; + var _DURATION_SECONDS = { + raw: 0, + "5s": 5, + "10s": 10, + "15s": 15, + "30s": 30, + "1m": 60, + "2m": 120, + "5m": 300, + "10m": 600, + "15m": 900, + "30m": 1800, + "1h": 3600, + "2h": 7200, + "3h": 10800, + "4h": 14400, + "6h": 21600, + "12h": 43200, + "24h": 86400, + "7d": 604800, + "14d": 1209600, + "21d": 1814400, + "28d": 2419200 + }; + var _WINDOW_OPTIONS = { + trend_window: [ + "1h", + "6h", + "24h", + "7d", + "14d", + "21d", + "28d" + ], + rate_window: [ + "1h", + "6h", + "24h" + ], + anomaly_rate_window: [ + "1h", + "6h", + "24h" + ], + anomaly_zscore_window: [ + "1h", + "6h", + "24h", + "7d" + ], + anomaly_persistence_window: [ + "30m", + "1h", + "3h", + "6h", + "12h", + "24h" + ] + }; + function _clampWindowsToInterval(analysis, newInterval) { + const intervalSecs = _DURATION_SECONDS[newInterval] ?? 0; + if (intervalSecs === 0) return {}; + const updates = {}; + for (const [key, options] of Object.entries(_WINDOW_OPTIONS)) { + const current = analysis[key]; + if (!current || current === "point_to_point") continue; + if ((_DURATION_SECONDS[current] ?? 0) < intervalSecs) { + const next = options.find((opt) => (_DURATION_SECONDS[opt] ?? 0) >= intervalSecs); + if (next) updates[key] = next; + } + } + return updates; + } + var TargetRowList = (_rows_accessor_storage$1 = /* @__PURE__ */ new WeakMap(), _states_accessor_storage$1 = /* @__PURE__ */ new WeakMap(), _hass_accessor_storage$6 = /* @__PURE__ */ new WeakMap(), _canShowDeltaAnalysis_accessor_storage$1 = /* @__PURE__ */ new WeakMap(), _comparisonWindows_accessor_storage$1 = /* @__PURE__ */ new WeakMap(), _computingEntityIds_accessor_storage = /* @__PURE__ */ new WeakMap(), _analysisProgress_accessor_storage = /* @__PURE__ */ new WeakMap(), _computingMethodsByEn_accessor_storage = /* @__PURE__ */ new WeakMap(), _TargetRowList = class TargetRowList extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _rows_accessor_storage$1, []); + _classPrivateFieldInitSpec(this, _states_accessor_storage$1, {}); + _classPrivateFieldInitSpec(this, _hass_accessor_storage$6, null); + _classPrivateFieldInitSpec(this, _canShowDeltaAnalysis_accessor_storage$1, false); + _classPrivateFieldInitSpec(this, _comparisonWindows_accessor_storage$1, []); + _classPrivateFieldInitSpec(this, _computingEntityIds_accessor_storage, /* @__PURE__ */ new Set()); + _classPrivateFieldInitSpec(this, _analysisProgress_accessor_storage, 0); + _classPrivateFieldInitSpec(this, _computingMethodsByEn_accessor_storage, /* @__PURE__ */ new Map()); + _defineProperty( + this, + /** Index of the row currently being dragged, or null when not dragging. */ + "_dragSourceIndex", + null + ); + _defineProperty( + this, + /** + * Optimistically toggle the expanded state of a row's analysis panel + * immediately (before the panel's round-trip mutation arrives). This gives + * instant visual feedback with no perceived delay. + */ + "_onToggleAnalysisFast", + (e) => { + const entityId = String(e?.detail?.entityId || "").trim(); + if (!entityId) return; + const index = this.rows?.findIndex((r) => r.entity_id === entityId) ?? -1; + if (index === -1) return; + this.rows = this.rows.map((row, i) => { + if (i !== index) return row; + return { + ...row, + analysis: { + ...row.analysis, + expanded: !row.analysis?.expanded + } + }; + }); + } + ); + _defineProperty( + this, + /** + * Optimistically apply analysis option changes immediately so sub-option + * groups (e.g. method-specific windows) appear without waiting for the + * panel round-trip. Handles both plain key/value changes and the special + * `anomaly_method_toggle_*` keys used by the anomaly group. + */ + "_onRowAnalysisChangeFast", + (e) => { + const { entityId, key, value } = e.detail || {}; + if (!entityId || !key) return; + const index = this.rows?.findIndex((r) => r.entity_id === entityId) ?? -1; + if (index === -1) return; + const currentAnalysis = this.rows[index].analysis || {}; + let nextAnalysis; + if (key.startsWith("anomaly_method_toggle_")) { + const method = key.slice(22); + const currentMethods = Array.isArray(currentAnalysis.anomaly_methods) ? currentAnalysis.anomaly_methods : []; + const nextMethods = value === true ? [...new Set([...currentMethods, method])] : currentMethods.filter((m) => m !== method); + nextAnalysis = { + ...currentAnalysis, + anomaly_methods: nextMethods + }; + } else if (key === "sample_interval" && typeof value === "string") { + const windowUpdates = _clampWindowsToInterval(currentAnalysis, value); + nextAnalysis = { + ...currentAnalysis, + [key]: value, + ...windowUpdates + }; + } else nextAnalysis = { + ...currentAnalysis, + [key]: value + }; + this.rows = this.rows.map((r, i) => i === index ? { + ...r, + analysis: nextAnalysis + } : r); + } + ); + _defineProperty(this, "_onDragEnd", (e) => { + this._dragSourceIndex = null; + e.currentTarget.classList.remove("is-dragging"); + this._clearDropIndicators(); + this._removeDragCursorStyle(); + }); + } + get rows() { + return _classPrivateFieldGet2(_rows_accessor_storage$1, this); + } + set rows(value) { + _classPrivateFieldSet2(_rows_accessor_storage$1, this, value); + } + get states() { + return _classPrivateFieldGet2(_states_accessor_storage$1, this); + } + set states(value) { + _classPrivateFieldSet2(_states_accessor_storage$1, this, value); + } + get hass() { + return _classPrivateFieldGet2(_hass_accessor_storage$6, this); + } + set hass(value) { + _classPrivateFieldSet2(_hass_accessor_storage$6, this, value); + } + get canShowDeltaAnalysis() { + return _classPrivateFieldGet2(_canShowDeltaAnalysis_accessor_storage$1, this); + } + set canShowDeltaAnalysis(value) { + _classPrivateFieldSet2(_canShowDeltaAnalysis_accessor_storage$1, this, value); + } + get comparisonWindows() { + return _classPrivateFieldGet2(_comparisonWindows_accessor_storage$1, this); + } + set comparisonWindows(value) { + _classPrivateFieldSet2(_comparisonWindows_accessor_storage$1, this, value); + } + get computingEntityIds() { + return _classPrivateFieldGet2(_computingEntityIds_accessor_storage, this); + } + set computingEntityIds(value) { + _classPrivateFieldSet2(_computingEntityIds_accessor_storage, this, value); + } + get analysisProgress() { + return _classPrivateFieldGet2(_analysisProgress_accessor_storage, this); + } + set analysisProgress(value) { + _classPrivateFieldSet2(_analysisProgress_accessor_storage, this, value); + } + get computingMethodsByEntity() { + return _classPrivateFieldGet2(_computingMethodsByEn_accessor_storage, this); + } + set computingMethodsByEntity(value) { + _classPrivateFieldSet2(_computingMethodsByEn_accessor_storage, this, value); + } + render() { + if (!this.rows.length) return b``; + const firstAnalysis = JSON.stringify(this.rows[0]?.analysis ?? {}); + const allAnalysisSame = this.rows.every((r) => JSON.stringify(r.analysis ?? {}) === firstAnalysis); + return b`
- ${this.rows.map( - (row, index) => b` + ${this.rows.map((row, index) => b` this._onDragStart(e2, index)} + @dragstart=${(e) => this._onDragStart(e, index)} @dragend=${this._onDragEnd} > - ` - )} + `)}
`; - } - // --------------------------------------------------------------------------- - // Drag-to-reorder handlers - // --------------------------------------------------------------------------- - _onDragStart(e2, index) { - this._dragSourceIndex = index; - if (e2.dataTransfer) { - e2.dataTransfer.effectAllowed = "move"; - e2.dataTransfer.setData("text/plain", String(index)); - const rowEl = e2.currentTarget; - const rect = rowEl.getBoundingClientRect(); - e2.dataTransfer.setDragImage( - rowEl, - e2.clientX - rect.left, - e2.clientY - rect.top - ); - } - const target = e2.currentTarget; - setTimeout(() => target.classList.add("is-dragging"), 0); - this._ensureDragCursorStyle(); - } - _ensureDragCursorStyle() { - const doc = this.ownerDocument; - if (!doc.getElementById("dp-drag-cursor-style")) { - const style = doc.createElement("style"); - style.id = "dp-drag-cursor-style"; - style.textContent = "*, *::before, *::after { cursor: grabbing !important; }"; - doc.head.appendChild(style); - } - } - _removeDragCursorStyle() { - this.ownerDocument.getElementById("dp-drag-cursor-style")?.remove(); - } - _onDragOver(e2) { - if (this._dragSourceIndex === null) return; - e2.preventDefault(); - if (e2.dataTransfer) e2.dataTransfer.dropEffect = "move"; - const rowEl = this._rowFromEvent(e2); - if (!rowEl) return; - const rect = rowEl.getBoundingClientRect(); - const isAbove = e2.clientY < rect.top + rect.height / 2; - this._clearDropIndicators(); - rowEl.classList.add(isAbove ? "is-drag-over-before" : "is-drag-over-after"); - } - _onDragLeave(e2) { - const rowEl = this._rowFromEvent(e2); - if (rowEl && !rowEl.contains(e2.relatedTarget)) { - rowEl.classList.remove("is-drag-over-before", "is-drag-over-after"); - } - } - _onDrop(e2) { - e2.preventDefault(); - const fromIndex = this._dragSourceIndex ?? parseInt(e2.dataTransfer?.getData("text/plain") ?? "", 10); - const rowEl = this._rowFromEvent(e2); - if (!rowEl || !Number.isFinite(fromIndex)) return; - const toIndexRaw = parseInt( - rowEl.dataset.rowIndex ?? "", - 10 - ); - if (!Number.isFinite(toIndexRaw)) return; - const rect = rowEl.getBoundingClientRect(); - const isAbove = e2.clientY < rect.top + rect.height / 2; - const insertBeforeIndex = isAbove ? toIndexRaw : toIndexRaw + 1; - const toIndex = fromIndex < insertBeforeIndex ? insertBeforeIndex - 1 : insertBeforeIndex; - rowEl.classList.remove("is-drag-over-before", "is-drag-over-after"); - if (fromIndex !== toIndex) { - const newRows = [...this.rows]; - const [moved] = newRows.splice(fromIndex, 1); - newRows.splice(toIndex, 0, moved); - this.dispatchEvent( - new CustomEvent("dp-rows-reorder", { - detail: { rows: newRows }, - bubbles: true, - composed: true - }) - ); - } - } - // --------------------------------------------------------------------------- - // Helpers - // --------------------------------------------------------------------------- - /** Walk the composed event path to find the nearest target-row element. */ - _rowFromEvent(e2) { - for (const node of e2.composedPath()) { - if (node instanceof Element && node.tagName?.toLowerCase() === "target-row") { - return node; - } - } - return null; - } - _clearDropIndicators() { - this.shadowRoot?.querySelectorAll("target-row").forEach((r2) => { - r2.classList.remove("is-drag-over-before", "is-drag-over-after"); - }); - } - } - _init$q = __decoratorStart$q(_a$q); - _rows$1 = /* @__PURE__ */ new WeakMap(); - _states$1 = /* @__PURE__ */ new WeakMap(); - _hass$6 = /* @__PURE__ */ new WeakMap(); - _canShowDeltaAnalysis$1 = /* @__PURE__ */ new WeakMap(); - _comparisonWindows$1 = /* @__PURE__ */ new WeakMap(); - _computingEntityIds = /* @__PURE__ */ new WeakMap(); - _analysisProgress = /* @__PURE__ */ new WeakMap(); - _computingMethodsByEntity = /* @__PURE__ */ new WeakMap(); - __decorateElement$q(_init$q, 4, "rows", _rows_dec$1, TargetRowList, _rows$1); - __decorateElement$q(_init$q, 4, "states", _states_dec$1, TargetRowList, _states$1); - __decorateElement$q(_init$q, 4, "hass", _hass_dec$6, TargetRowList, _hass$6); - __decorateElement$q(_init$q, 4, "canShowDeltaAnalysis", _canShowDeltaAnalysis_dec$1, TargetRowList, _canShowDeltaAnalysis$1); - __decorateElement$q(_init$q, 4, "comparisonWindows", _comparisonWindows_dec$1, TargetRowList, _comparisonWindows$1); - __decorateElement$q(_init$q, 4, "computingEntityIds", _computingEntityIds_dec, TargetRowList, _computingEntityIds); - __decorateElement$q(_init$q, 4, "analysisProgress", _analysisProgress_dec, TargetRowList, _analysisProgress); - __decorateElement$q(_init$q, 4, "computingMethodsByEntity", _computingMethodsByEntity_dec, TargetRowList, _computingMethodsByEntity); - TargetRowList = __decorateElement$q(_init$q, 0, "TargetRowList", _TargetRowList_decorators, TargetRowList); - __publicField$x(TargetRowList, "styles", styles$w); - __runInitializers$q(_init$q, 1, TargetRowList); - customElements.define("target-row-list", TargetRowList); - const styles$v = i$5` + } + _onDragStart(e, index) { + this._dragSourceIndex = index; + if (e.dataTransfer) { + e.dataTransfer.effectAllowed = "move"; + e.dataTransfer.setData("text/plain", String(index)); + const rowEl = e.currentTarget; + const rect = rowEl.getBoundingClientRect(); + e.dataTransfer.setDragImage(rowEl, e.clientX - rect.left, e.clientY - rect.top); + } + const target = e.currentTarget; + setTimeout(() => target.classList.add("is-dragging"), 0); + this._ensureDragCursorStyle(); + } + _ensureDragCursorStyle() { + const doc = this.ownerDocument; + if (!doc.getElementById("dp-drag-cursor-style")) { + const style = doc.createElement("style"); + style.id = "dp-drag-cursor-style"; + style.textContent = "*, *::before, *::after { cursor: grabbing !important; }"; + doc.head.appendChild(style); + } + } + _removeDragCursorStyle() { + this.ownerDocument.getElementById("dp-drag-cursor-style")?.remove(); + } + _onDragOver(e) { + if (this._dragSourceIndex === null) return; + e.preventDefault(); + if (e.dataTransfer) e.dataTransfer.dropEffect = "move"; + const rowEl = this._rowFromEvent(e); + if (!rowEl) return; + const rect = rowEl.getBoundingClientRect(); + const isAbove = e.clientY < rect.top + rect.height / 2; + this._clearDropIndicators(); + rowEl.classList.add(isAbove ? "is-drag-over-before" : "is-drag-over-after"); + } + _onDragLeave(e) { + const rowEl = this._rowFromEvent(e); + if (rowEl && !rowEl.contains(e.relatedTarget)) rowEl.classList.remove("is-drag-over-before", "is-drag-over-after"); + } + _onDrop(e) { + e.preventDefault(); + const fromIndex = this._dragSourceIndex ?? parseInt(e.dataTransfer?.getData("text/plain") ?? "", 10); + const rowEl = this._rowFromEvent(e); + if (!rowEl || !Number.isFinite(fromIndex)) return; + const toIndexRaw = parseInt(rowEl.dataset.rowIndex ?? "", 10); + if (!Number.isFinite(toIndexRaw)) return; + const rect = rowEl.getBoundingClientRect(); + const insertBeforeIndex = e.clientY < rect.top + rect.height / 2 ? toIndexRaw : toIndexRaw + 1; + const toIndex = fromIndex < insertBeforeIndex ? insertBeforeIndex - 1 : insertBeforeIndex; + rowEl.classList.remove("is-drag-over-before", "is-drag-over-after"); + if (fromIndex !== toIndex) { + const newRows = [...this.rows]; + const [moved] = newRows.splice(fromIndex, 1); + newRows.splice(toIndex, 0, moved); + this.dispatchEvent(new CustomEvent("dp-rows-reorder", { + detail: { rows: newRows }, + bubbles: true, + composed: true + })); + } + } + /** Walk the composed event path to find the nearest target-row element. */ + _rowFromEvent(e) { + for (const node of e.composedPath()) if (node instanceof Element && node.tagName?.toLowerCase() === "target-row") return node; + return null; + } + _clearDropIndicators() { + this.shadowRoot?.querySelectorAll("target-row").forEach((r) => { + r.classList.remove("is-drag-over-before", "is-drag-over-after"); + }); + } + }, _defineProperty(_TargetRowList, "styles", styles$32), _TargetRowList); + __decorate([n$1({ type: Array })], TargetRowList.prototype, "rows", null); + __decorate([n$1({ + type: Object, + attribute: false + })], TargetRowList.prototype, "states", null); + __decorate([n$1({ + type: Object, + attribute: false + })], TargetRowList.prototype, "hass", null); + __decorate([n$1({ + type: Boolean, + attribute: "can-show-delta-analysis" + })], TargetRowList.prototype, "canShowDeltaAnalysis", null); + __decorate([n$1({ + type: Array, + attribute: false + })], TargetRowList.prototype, "comparisonWindows", null); + __decorate([n$1({ + type: Object, + attribute: false + })], TargetRowList.prototype, "computingEntityIds", null); + __decorate([n$1({ + type: Number, + attribute: false + })], TargetRowList.prototype, "analysisProgress", null); + __decorate([n$1({ + type: Object, + attribute: false + })], TargetRowList.prototype, "computingMethodsByEntity", null); + TargetRowList = __decorate([localized()], TargetRowList); + customElements.define("target-row-list", TargetRowList); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/sidebar-options/sidebar-options.styles.ts + var styles$31 = i$5` :host { display: block; --dp-spacing-lg: calc(var(--spacing, 8px) * 2); @@ -20815,104 +20918,170 @@ ${content.alert}` : "", gap: var(--dp-spacing-lg); } `; - var __create$p = Object.create; - var __defProp$w = Object.defineProperty; - var __getOwnPropDesc$p = Object.getOwnPropertyDescriptor; - var __knownSymbol$p = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$p = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$w = (obj, key, value) => key in obj ? __defProp$w(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$p = (base) => [, , , __create$p(base?.[__knownSymbol$p("metadata")] ?? null)]; - var __decoratorStrings$p = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$p = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$p("Function expected") : fn; - var __decoratorContext$p = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$p[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$p("Already initialized") : fns.push(__expectFn$p(fn || null)) }); - var __decoratorMetadata$p = (array, target) => __defNormalProp$w(target, __knownSymbol$p("metadata"), array[3]); - var __runInitializers$p = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$p = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$p[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$p({ get [name]() { - return __privateGet$o(this, extra); - }, set [name](x2) { - return __privateSet$o(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$p(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$p(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$p("Object expected"); - else __expectFn$p(fn = it.get) && (desc.get = fn), __expectFn$p(fn = it.set) && (desc.set = fn), __expectFn$p(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$w(target, name, desc), target; - }; - var __publicField$w = (obj, key, value) => __defNormalProp$w(obj, key + "", value); - var __accessCheck$o = (obj, member, msg2) => member.has(obj) || __typeError$p("Cannot " + msg2); - var __privateGet$o = (obj, member, getter) => (__accessCheck$o(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$o = (obj, member, value) => member.has(obj) ? __typeError$p("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$o = (obj, member, value, setter) => (__accessCheck$o(obj, member, "write to private field"), member.set(obj, value), value); - var _chartOpen_dec, _analysisOpen_dec, _datapointsOpen_dec, _targetsOpen_dec, _anyAnomaliesEnabled_dec$1, _anomalyOverlapMode_dec$1, _hoverSnapMode_dec$1, _yAxisMode_dec$1, _dataGapThreshold_dec$1, _showDataGaps_dec$1, _showCorrelatedAnomalies_dec$1, _showHoverGuides_dec$1, _showTooltips_dec$1, _showLines_dec$1, _showIcons_dec$1, _datapointScope_dec$1, _a$p, _init$p, _datapointScope$1, _showIcons$1, _showLines$1, _showTooltips$1, _showHoverGuides$1, _showCorrelatedAnomalies$1, _showDataGaps$1, _dataGapThreshold$1, _yAxisMode$1, _hoverSnapMode$1, _anomalyOverlapMode$1, _anyAnomaliesEnabled$1, _targetsOpen, _datapointsOpen, _analysisOpen, _chartOpen; - class SidebarOptions extends (_a$p = i$2, _datapointScope_dec$1 = [n({ type: String, attribute: "datapoint-scope" })], _showIcons_dec$1 = [n({ type: Boolean, attribute: "show-icons" })], _showLines_dec$1 = [n({ type: Boolean, attribute: "show-lines" })], _showTooltips_dec$1 = [n({ type: Boolean, attribute: "show-tooltips" })], _showHoverGuides_dec$1 = [n({ type: Boolean, attribute: "show-hover-guides" })], _showCorrelatedAnomalies_dec$1 = [n({ type: Boolean, attribute: "show-correlated-anomalies" })], _showDataGaps_dec$1 = [n({ type: Boolean, attribute: "show-data-gaps" })], _dataGapThreshold_dec$1 = [n({ type: String, attribute: "data-gap-threshold" })], _yAxisMode_dec$1 = [n({ type: String, attribute: "y-axis-mode" })], _hoverSnapMode_dec$1 = [n({ type: String, attribute: "hover-snap-mode" })], _anomalyOverlapMode_dec$1 = [n({ type: String, attribute: "anomaly-overlap-mode" })], _anyAnomaliesEnabled_dec$1 = [n({ type: Boolean, attribute: false })], _targetsOpen_dec = [r()], _datapointsOpen_dec = [n({ type: Boolean, attribute: "datapoints-open" })], _analysisOpen_dec = [n({ type: Boolean, attribute: "analysis-open" })], _chartOpen_dec = [n({ type: Boolean, attribute: "chart-open" })], _a$p) { - constructor() { - super(...arguments); - __privateAdd$o(this, _datapointScope$1, __runInitializers$p(_init$p, 8, this, "linked")), __runInitializers$p(_init$p, 11, this); - __privateAdd$o(this, _showIcons$1, __runInitializers$p(_init$p, 12, this, true)), __runInitializers$p(_init$p, 15, this); - __privateAdd$o(this, _showLines$1, __runInitializers$p(_init$p, 16, this, true)), __runInitializers$p(_init$p, 19, this); - __privateAdd$o(this, _showTooltips$1, __runInitializers$p(_init$p, 20, this, true)), __runInitializers$p(_init$p, 23, this); - __privateAdd$o(this, _showHoverGuides$1, __runInitializers$p(_init$p, 24, this, false)), __runInitializers$p(_init$p, 27, this); - __privateAdd$o(this, _showCorrelatedAnomalies$1, __runInitializers$p(_init$p, 28, this, false)), __runInitializers$p(_init$p, 31, this); - __privateAdd$o(this, _showDataGaps$1, __runInitializers$p(_init$p, 32, this, true)), __runInitializers$p(_init$p, 35, this); - __privateAdd$o(this, _dataGapThreshold$1, __runInitializers$p(_init$p, 36, this, "2h")), __runInitializers$p(_init$p, 39, this); - __privateAdd$o(this, _yAxisMode$1, __runInitializers$p(_init$p, 40, this, "combined")), __runInitializers$p(_init$p, 43, this); - __privateAdd$o(this, _hoverSnapMode$1, __runInitializers$p(_init$p, 44, this, "follow_series")), __runInitializers$p(_init$p, 47, this); - __privateAdd$o(this, _anomalyOverlapMode$1, __runInitializers$p(_init$p, 48, this, "all")), __runInitializers$p(_init$p, 51, this); - __privateAdd$o(this, _anyAnomaliesEnabled$1, __runInitializers$p(_init$p, 52, this, false)), __runInitializers$p(_init$p, 55, this); - __privateAdd$o(this, _targetsOpen, __runInitializers$p(_init$p, 56, this, true)), __runInitializers$p(_init$p, 59, this); - __privateAdd$o(this, _datapointsOpen, __runInitializers$p(_init$p, 60, this, true)), __runInitializers$p(_init$p, 63, this); - __privateAdd$o(this, _analysisOpen, __runInitializers$p(_init$p, 64, this, true)), __runInitializers$p(_init$p, 67, this); - __privateAdd$o(this, _chartOpen, __runInitializers$p(_init$p, 68, this, true)), __runInitializers$p(_init$p, 71, this); - } - _onTargetsToggle(e2) { - this.targetsOpen = e2.detail.open; - this._emitAccordionChange(); - } - _onDatapointsToggle(e2) { - this.datapointsOpen = e2.detail.open; - this._emitAccordionChange(); - } - _onAnalysisToggle(e2) { - this.analysisOpen = e2.detail.open; - this._emitAccordionChange(); - } - _onChartToggle(e2) { - this.chartOpen = e2.detail.open; - this._emitAccordionChange(); - } - _emitAccordionChange() { - this.dispatchEvent( - new CustomEvent("dp-accordion-change", { - detail: { - targetsOpen: this.targetsOpen, - datapointsOpen: this.datapointsOpen, - analysisOpen: this.analysisOpen, - chartOpen: this.chartOpen - }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/molecules/sidebar-options/sidebar-options.ts + var _datapointScope_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _showIcons_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _showLines_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _showTooltips_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _showHoverGuides_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _showCorrelatedAnomal_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _showDataGaps_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _dataGapThreshold_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _yAxisMode_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _hoverSnapMode_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _anomalyOverlapMode_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _anyAnomaliesEnabled_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _targetsOpen_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _datapointsOpen_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _analysisOpen_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _chartOpen_accessor_storage = /* @__PURE__ */ new WeakMap(); + var SidebarOptions = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _datapointScope_accessor_storage$1, "linked"); + _classPrivateFieldInitSpec(this, _showIcons_accessor_storage$1, true); + _classPrivateFieldInitSpec(this, _showLines_accessor_storage$1, true); + _classPrivateFieldInitSpec(this, _showTooltips_accessor_storage$1, true); + _classPrivateFieldInitSpec(this, _showHoverGuides_accessor_storage$1, false); + _classPrivateFieldInitSpec(this, _showCorrelatedAnomal_accessor_storage$1, false); + _classPrivateFieldInitSpec(this, _showDataGaps_accessor_storage$1, true); + _classPrivateFieldInitSpec(this, _dataGapThreshold_accessor_storage$1, "2h"); + _classPrivateFieldInitSpec(this, _yAxisMode_accessor_storage$1, "combined"); + _classPrivateFieldInitSpec(this, _hoverSnapMode_accessor_storage$1, "follow_series"); + _classPrivateFieldInitSpec(this, _anomalyOverlapMode_accessor_storage$1, "all"); + _classPrivateFieldInitSpec(this, _anyAnomaliesEnabled_accessor_storage$1, false); + _classPrivateFieldInitSpec(this, _targetsOpen_accessor_storage, true); + _classPrivateFieldInitSpec(this, _datapointsOpen_accessor_storage, true); + _classPrivateFieldInitSpec(this, _analysisOpen_accessor_storage, true); + _classPrivateFieldInitSpec(this, _chartOpen_accessor_storage, true); + } + get datapointScope() { + return _classPrivateFieldGet2(_datapointScope_accessor_storage$1, this); + } + set datapointScope(value) { + _classPrivateFieldSet2(_datapointScope_accessor_storage$1, this, value); + } + get showIcons() { + return _classPrivateFieldGet2(_showIcons_accessor_storage$1, this); + } + set showIcons(value) { + _classPrivateFieldSet2(_showIcons_accessor_storage$1, this, value); + } + get showLines() { + return _classPrivateFieldGet2(_showLines_accessor_storage$1, this); + } + set showLines(value) { + _classPrivateFieldSet2(_showLines_accessor_storage$1, this, value); + } + get showTooltips() { + return _classPrivateFieldGet2(_showTooltips_accessor_storage$1, this); + } + set showTooltips(value) { + _classPrivateFieldSet2(_showTooltips_accessor_storage$1, this, value); + } + get showHoverGuides() { + return _classPrivateFieldGet2(_showHoverGuides_accessor_storage$1, this); + } + set showHoverGuides(value) { + _classPrivateFieldSet2(_showHoverGuides_accessor_storage$1, this, value); + } + get showCorrelatedAnomalies() { + return _classPrivateFieldGet2(_showCorrelatedAnomal_accessor_storage$1, this); + } + set showCorrelatedAnomalies(value) { + _classPrivateFieldSet2(_showCorrelatedAnomal_accessor_storage$1, this, value); + } + get showDataGaps() { + return _classPrivateFieldGet2(_showDataGaps_accessor_storage$1, this); + } + set showDataGaps(value) { + _classPrivateFieldSet2(_showDataGaps_accessor_storage$1, this, value); + } + get dataGapThreshold() { + return _classPrivateFieldGet2(_dataGapThreshold_accessor_storage$1, this); + } + set dataGapThreshold(value) { + _classPrivateFieldSet2(_dataGapThreshold_accessor_storage$1, this, value); + } + get yAxisMode() { + return _classPrivateFieldGet2(_yAxisMode_accessor_storage$1, this); + } + set yAxisMode(value) { + _classPrivateFieldSet2(_yAxisMode_accessor_storage$1, this, value); + } + get hoverSnapMode() { + return _classPrivateFieldGet2(_hoverSnapMode_accessor_storage$1, this); + } + set hoverSnapMode(value) { + _classPrivateFieldSet2(_hoverSnapMode_accessor_storage$1, this, value); + } + get anomalyOverlapMode() { + return _classPrivateFieldGet2(_anomalyOverlapMode_accessor_storage$1, this); + } + set anomalyOverlapMode(value) { + _classPrivateFieldSet2(_anomalyOverlapMode_accessor_storage$1, this, value); + } + get anyAnomaliesEnabled() { + return _classPrivateFieldGet2(_anyAnomaliesEnabled_accessor_storage$1, this); + } + set anyAnomaliesEnabled(value) { + _classPrivateFieldSet2(_anyAnomaliesEnabled_accessor_storage$1, this, value); + } + get targetsOpen() { + return _classPrivateFieldGet2(_targetsOpen_accessor_storage, this); + } + set targetsOpen(value) { + _classPrivateFieldSet2(_targetsOpen_accessor_storage, this, value); + } + get datapointsOpen() { + return _classPrivateFieldGet2(_datapointsOpen_accessor_storage, this); + } + set datapointsOpen(value) { + _classPrivateFieldSet2(_datapointsOpen_accessor_storage, this, value); + } + get analysisOpen() { + return _classPrivateFieldGet2(_analysisOpen_accessor_storage, this); + } + set analysisOpen(value) { + _classPrivateFieldSet2(_analysisOpen_accessor_storage, this, value); + } + get chartOpen() { + return _classPrivateFieldGet2(_chartOpen_accessor_storage, this); + } + set chartOpen(value) { + _classPrivateFieldSet2(_chartOpen_accessor_storage, this, value); + } + _onTargetsToggle(e) { + this.targetsOpen = e.detail.open; + this._emitAccordionChange(); + } + _onDatapointsToggle(e) { + this.datapointsOpen = e.detail.open; + this._emitAccordionChange(); + } + _onAnalysisToggle(e) { + this.analysisOpen = e.detail.open; + this._emitAccordionChange(); + } + _onChartToggle(e) { + this.chartOpen = e.detail.open; + this._emitAccordionChange(); + } + _emitAccordionChange() { + this.dispatchEvent(new CustomEvent("dp-accordion-change", { + detail: { + targetsOpen: this.targetsOpen, + datapointsOpen: this.datapointsOpen, + analysisOpen: this.analysisOpen, + chartOpen: this.chartOpen + }, + bubbles: true, + composed: true + })); + } + render() { + return b` `; - } - } - _init$p = __decoratorStart$p(_a$p); - _datapointScope$1 = /* @__PURE__ */ new WeakMap(); - _showIcons$1 = /* @__PURE__ */ new WeakMap(); - _showLines$1 = /* @__PURE__ */ new WeakMap(); - _showTooltips$1 = /* @__PURE__ */ new WeakMap(); - _showHoverGuides$1 = /* @__PURE__ */ new WeakMap(); - _showCorrelatedAnomalies$1 = /* @__PURE__ */ new WeakMap(); - _showDataGaps$1 = /* @__PURE__ */ new WeakMap(); - _dataGapThreshold$1 = /* @__PURE__ */ new WeakMap(); - _yAxisMode$1 = /* @__PURE__ */ new WeakMap(); - _hoverSnapMode$1 = /* @__PURE__ */ new WeakMap(); - _anomalyOverlapMode$1 = /* @__PURE__ */ new WeakMap(); - _anyAnomaliesEnabled$1 = /* @__PURE__ */ new WeakMap(); - _targetsOpen = /* @__PURE__ */ new WeakMap(); - _datapointsOpen = /* @__PURE__ */ new WeakMap(); - _analysisOpen = /* @__PURE__ */ new WeakMap(); - _chartOpen = /* @__PURE__ */ new WeakMap(); - __decorateElement$p(_init$p, 4, "datapointScope", _datapointScope_dec$1, SidebarOptions, _datapointScope$1); - __decorateElement$p(_init$p, 4, "showIcons", _showIcons_dec$1, SidebarOptions, _showIcons$1); - __decorateElement$p(_init$p, 4, "showLines", _showLines_dec$1, SidebarOptions, _showLines$1); - __decorateElement$p(_init$p, 4, "showTooltips", _showTooltips_dec$1, SidebarOptions, _showTooltips$1); - __decorateElement$p(_init$p, 4, "showHoverGuides", _showHoverGuides_dec$1, SidebarOptions, _showHoverGuides$1); - __decorateElement$p(_init$p, 4, "showCorrelatedAnomalies", _showCorrelatedAnomalies_dec$1, SidebarOptions, _showCorrelatedAnomalies$1); - __decorateElement$p(_init$p, 4, "showDataGaps", _showDataGaps_dec$1, SidebarOptions, _showDataGaps$1); - __decorateElement$p(_init$p, 4, "dataGapThreshold", _dataGapThreshold_dec$1, SidebarOptions, _dataGapThreshold$1); - __decorateElement$p(_init$p, 4, "yAxisMode", _yAxisMode_dec$1, SidebarOptions, _yAxisMode$1); - __decorateElement$p(_init$p, 4, "hoverSnapMode", _hoverSnapMode_dec$1, SidebarOptions, _hoverSnapMode$1); - __decorateElement$p(_init$p, 4, "anomalyOverlapMode", _anomalyOverlapMode_dec$1, SidebarOptions, _anomalyOverlapMode$1); - __decorateElement$p(_init$p, 4, "anyAnomaliesEnabled", _anyAnomaliesEnabled_dec$1, SidebarOptions, _anyAnomaliesEnabled$1); - __decorateElement$p(_init$p, 4, "targetsOpen", _targetsOpen_dec, SidebarOptions, _targetsOpen); - __decorateElement$p(_init$p, 4, "datapointsOpen", _datapointsOpen_dec, SidebarOptions, _datapointsOpen); - __decorateElement$p(_init$p, 4, "analysisOpen", _analysisOpen_dec, SidebarOptions, _analysisOpen); - __decorateElement$p(_init$p, 4, "chartOpen", _chartOpen_dec, SidebarOptions, _chartOpen); - __decoratorMetadata$p(_init$p, SidebarOptions); - __publicField$w(SidebarOptions, "styles", styles$v); - customElements.define("sidebar-options", SidebarOptions); - const styles$u = i$5` + } + }; + _defineProperty(SidebarOptions, "styles", styles$31); + __decorate([n$1({ + type: String, + attribute: "datapoint-scope" + })], SidebarOptions.prototype, "datapointScope", null); + __decorate([n$1({ + type: Boolean, + attribute: "show-icons" + })], SidebarOptions.prototype, "showIcons", null); + __decorate([n$1({ + type: Boolean, + attribute: "show-lines" + })], SidebarOptions.prototype, "showLines", null); + __decorate([n$1({ + type: Boolean, + attribute: "show-tooltips" + })], SidebarOptions.prototype, "showTooltips", null); + __decorate([n$1({ + type: Boolean, + attribute: "show-hover-guides" + })], SidebarOptions.prototype, "showHoverGuides", null); + __decorate([n$1({ + type: Boolean, + attribute: "show-correlated-anomalies" + })], SidebarOptions.prototype, "showCorrelatedAnomalies", null); + __decorate([n$1({ + type: Boolean, + attribute: "show-data-gaps" + })], SidebarOptions.prototype, "showDataGaps", null); + __decorate([n$1({ + type: String, + attribute: "data-gap-threshold" + })], SidebarOptions.prototype, "dataGapThreshold", null); + __decorate([n$1({ + type: String, + attribute: "y-axis-mode" + })], SidebarOptions.prototype, "yAxisMode", null); + __decorate([n$1({ + type: String, + attribute: "hover-snap-mode" + })], SidebarOptions.prototype, "hoverSnapMode", null); + __decorate([n$1({ + type: String, + attribute: "anomaly-overlap-mode" + })], SidebarOptions.prototype, "anomalyOverlapMode", null); + __decorate([n$1({ + type: Boolean, + attribute: false + })], SidebarOptions.prototype, "anyAnomaliesEnabled", null); + __decorate([r$1()], SidebarOptions.prototype, "targetsOpen", null); + __decorate([n$1({ + type: Boolean, + attribute: "datapoints-open" + })], SidebarOptions.prototype, "datapointsOpen", null); + __decorate([n$1({ + type: Boolean, + attribute: "analysis-open" + })], SidebarOptions.prototype, "analysisOpen", null); + __decorate([n$1({ + type: Boolean, + attribute: "chart-open" + })], SidebarOptions.prototype, "chartOpen", null); + customElements.define("sidebar-options", SidebarOptions); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/collapsed-options-menu/collapsed-options-menu.styles.ts + var styles$30 = i$5` :host { display: block; } @@ -21076,130 +21274,194 @@ ${content.alert}` : "", opacity: 0.7; } `; - var __create$o = Object.create; - var __defProp$v = Object.defineProperty; - var __getOwnPropDesc$o = Object.getOwnPropertyDescriptor; - var __knownSymbol$o = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$o = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$v = (obj, key, value) => key in obj ? __defProp$v(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$o = (base) => [, , , __create$o(base?.[__knownSymbol$o("metadata")] ?? null)]; - var __decoratorStrings$o = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$o = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$o("Function expected") : fn; - var __decoratorContext$o = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$o[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$o("Already initialized") : fns.push(__expectFn$o(fn || null)) }); - var __decoratorMetadata$o = (array, target) => __defNormalProp$v(target, __knownSymbol$o("metadata"), array[3]); - var __runInitializers$o = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$o = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$o[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$o({ get [name]() { - return __privateGet$n(this, extra); - }, set [name](x2) { - return __privateSet$n(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$o(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$o(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$o("Object expected"); - else __expectFn$o(fn = it.get) && (desc.get = fn), __expectFn$o(fn = it.set) && (desc.set = fn), __expectFn$o(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$v(target, name, desc), target; - }; - var __publicField$v = (obj, key, value) => __defNormalProp$v(obj, typeof key !== "symbol" ? key + "" : key, value); - var __accessCheck$n = (obj, member, msg2) => member.has(obj) || __typeError$o("Cannot " + msg2); - var __privateGet$n = (obj, member, getter) => (__accessCheck$n(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$n = (obj, member, value) => member.has(obj) ? __typeError$o("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$n = (obj, member, value, setter) => (__accessCheck$n(obj, member, "write to private field"), member.set(obj, value), value); - var _activeSection_dec, _anyAnomaliesEnabled_dec, _anomalyOverlapMode_dec, _hoverSnapMode_dec, _yAxisMode_dec, _dataGapThreshold_dec, _showDataGaps_dec, _showCorrelatedAnomalies_dec, _showHoverGuides_dec, _showTooltips_dec, _showLines_dec, _showIcons_dec, _datapointScope_dec, _a$o, _init$o, _datapointScope, _showIcons, _showLines, _showTooltips, _showHoverGuides, _showCorrelatedAnomalies, _showDataGaps, _dataGapThreshold, _yAxisMode, _hoverSnapMode, _anomalyOverlapMode, _anyAnomaliesEnabled, _activeSection; - const SECTIONS = [ - { key: "datapoints", label: "Datapoints" }, - { key: "datapoint-display", label: "Datapoint Display" }, - { key: "analysis", label: "Analysis" }, - { key: "chart-display", label: "Chart Display" } - ]; - class CollapsedOptionsMenu extends (_a$o = i$2, _datapointScope_dec = [n({ type: String })], _showIcons_dec = [n({ type: Boolean })], _showLines_dec = [n({ type: Boolean })], _showTooltips_dec = [n({ type: Boolean })], _showHoverGuides_dec = [n({ type: Boolean })], _showCorrelatedAnomalies_dec = [n({ type: Boolean })], _showDataGaps_dec = [n({ type: Boolean })], _dataGapThreshold_dec = [n({ type: String })], _yAxisMode_dec = [n({ type: String })], _hoverSnapMode_dec = [n({ type: String })], _anomalyOverlapMode_dec = [n({ type: String })], _anyAnomaliesEnabled_dec = [n({ type: Boolean })], _activeSection_dec = [r()], _a$o) { - constructor() { - super(...arguments); - __privateAdd$n(this, _datapointScope, __runInitializers$o(_init$o, 8, this, "linked")), __runInitializers$o(_init$o, 11, this); - __privateAdd$n(this, _showIcons, __runInitializers$o(_init$o, 12, this, true)), __runInitializers$o(_init$o, 15, this); - __privateAdd$n(this, _showLines, __runInitializers$o(_init$o, 16, this, true)), __runInitializers$o(_init$o, 19, this); - __privateAdd$n(this, _showTooltips, __runInitializers$o(_init$o, 20, this, true)), __runInitializers$o(_init$o, 23, this); - __privateAdd$n(this, _showHoverGuides, __runInitializers$o(_init$o, 24, this, false)), __runInitializers$o(_init$o, 27, this); - __privateAdd$n(this, _showCorrelatedAnomalies, __runInitializers$o(_init$o, 28, this, false)), __runInitializers$o(_init$o, 31, this); - __privateAdd$n(this, _showDataGaps, __runInitializers$o(_init$o, 32, this, true)), __runInitializers$o(_init$o, 35, this); - __privateAdd$n(this, _dataGapThreshold, __runInitializers$o(_init$o, 36, this, "2h")), __runInitializers$o(_init$o, 39, this); - __privateAdd$n(this, _yAxisMode, __runInitializers$o(_init$o, 40, this, "combined")), __runInitializers$o(_init$o, 43, this); - __privateAdd$n(this, _hoverSnapMode, __runInitializers$o(_init$o, 44, this, "follow_series")), __runInitializers$o(_init$o, 47, this); - __privateAdd$n(this, _anomalyOverlapMode, __runInitializers$o(_init$o, 48, this, "all")), __runInitializers$o(_init$o, 51, this); - __privateAdd$n(this, _anyAnomaliesEnabled, __runInitializers$o(_init$o, 52, this, false)), __runInitializers$o(_init$o, 55, this); - __privateAdd$n(this, _activeSection, __runInitializers$o(_init$o, 56, this, null)), __runInitializers$o(_init$o, 59, this); - __publicField$v(this, "_closeTimer", null); - } - disconnectedCallback() { - super.disconnectedCallback(); - if (this._closeTimer !== null) { - clearTimeout(this._closeTimer); - this._closeTimer = null; - } - } - _activateSection(key) { - if (this._closeTimer !== null) { - clearTimeout(this._closeTimer); - this._closeTimer = null; - } - this.activeSection = key; - } - _scheduleClose() { - if (this._closeTimer !== null) clearTimeout(this._closeTimer); - this._closeTimer = setTimeout(() => { - this.activeSection = null; - this._closeTimer = null; - }, 200); - } - _cancelClose() { - if (this._closeTimer !== null) { - clearTimeout(this._closeTimer); - this._closeTimer = null; - } - } - _renderSection() { - switch (this.activeSection) { - case "datapoints": - return b` + //#endregion + //#region custom_components/hass_datapoints/src/molecules/collapsed-options-menu/collapsed-options-menu.ts + var SECTIONS = [ + { + key: "datapoints", + label: "Datapoints" + }, + { + key: "datapoint-display", + label: "Datapoint Display" + }, + { + key: "analysis", + label: "Analysis" + }, + { + key: "chart-display", + label: "Chart Display" + } + ]; + var _datapointScope_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _showIcons_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _showLines_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _showTooltips_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _showHoverGuides_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _showCorrelatedAnomal_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _showDataGaps_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _dataGapThreshold_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _yAxisMode_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _hoverSnapMode_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _anomalyOverlapMode_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _anyAnomaliesEnabled_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _activeSection_accessor_storage = /* @__PURE__ */ new WeakMap(); + /** + * `collapsed-options-menu` renders a two-panel nested options menu for use + * in the collapsed sidebar. Level 1 shows section group names; hovering or + * clicking a name opens Level 2 to the right with that section's controls. + * + * All option-change events (`dp-scope-change`, `dp-display-change`, + * `dp-analysis-change`) bubble out via `composed: true` from the section + * children — no re-firing needed. + */ + var CollapsedOptionsMenu = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _datapointScope_accessor_storage, "linked"); + _classPrivateFieldInitSpec(this, _showIcons_accessor_storage, true); + _classPrivateFieldInitSpec(this, _showLines_accessor_storage, true); + _classPrivateFieldInitSpec(this, _showTooltips_accessor_storage, true); + _classPrivateFieldInitSpec(this, _showHoverGuides_accessor_storage, false); + _classPrivateFieldInitSpec(this, _showCorrelatedAnomal_accessor_storage, false); + _classPrivateFieldInitSpec(this, _showDataGaps_accessor_storage, true); + _classPrivateFieldInitSpec(this, _dataGapThreshold_accessor_storage, "2h"); + _classPrivateFieldInitSpec(this, _yAxisMode_accessor_storage, "combined"); + _classPrivateFieldInitSpec(this, _hoverSnapMode_accessor_storage, "follow_series"); + _classPrivateFieldInitSpec(this, _anomalyOverlapMode_accessor_storage, "all"); + _classPrivateFieldInitSpec(this, _anyAnomaliesEnabled_accessor_storage, false); + _classPrivateFieldInitSpec(this, _activeSection_accessor_storage, null); + _defineProperty(this, "_closeTimer", null); + } + get datapointScope() { + return _classPrivateFieldGet2(_datapointScope_accessor_storage, this); + } + set datapointScope(value) { + _classPrivateFieldSet2(_datapointScope_accessor_storage, this, value); + } + get showIcons() { + return _classPrivateFieldGet2(_showIcons_accessor_storage, this); + } + set showIcons(value) { + _classPrivateFieldSet2(_showIcons_accessor_storage, this, value); + } + get showLines() { + return _classPrivateFieldGet2(_showLines_accessor_storage, this); + } + set showLines(value) { + _classPrivateFieldSet2(_showLines_accessor_storage, this, value); + } + get showTooltips() { + return _classPrivateFieldGet2(_showTooltips_accessor_storage, this); + } + set showTooltips(value) { + _classPrivateFieldSet2(_showTooltips_accessor_storage, this, value); + } + get showHoverGuides() { + return _classPrivateFieldGet2(_showHoverGuides_accessor_storage, this); + } + set showHoverGuides(value) { + _classPrivateFieldSet2(_showHoverGuides_accessor_storage, this, value); + } + get showCorrelatedAnomalies() { + return _classPrivateFieldGet2(_showCorrelatedAnomal_accessor_storage, this); + } + set showCorrelatedAnomalies(value) { + _classPrivateFieldSet2(_showCorrelatedAnomal_accessor_storage, this, value); + } + get showDataGaps() { + return _classPrivateFieldGet2(_showDataGaps_accessor_storage, this); + } + set showDataGaps(value) { + _classPrivateFieldSet2(_showDataGaps_accessor_storage, this, value); + } + get dataGapThreshold() { + return _classPrivateFieldGet2(_dataGapThreshold_accessor_storage, this); + } + set dataGapThreshold(value) { + _classPrivateFieldSet2(_dataGapThreshold_accessor_storage, this, value); + } + get yAxisMode() { + return _classPrivateFieldGet2(_yAxisMode_accessor_storage, this); + } + set yAxisMode(value) { + _classPrivateFieldSet2(_yAxisMode_accessor_storage, this, value); + } + get hoverSnapMode() { + return _classPrivateFieldGet2(_hoverSnapMode_accessor_storage, this); + } + set hoverSnapMode(value) { + _classPrivateFieldSet2(_hoverSnapMode_accessor_storage, this, value); + } + get anomalyOverlapMode() { + return _classPrivateFieldGet2(_anomalyOverlapMode_accessor_storage, this); + } + set anomalyOverlapMode(value) { + _classPrivateFieldSet2(_anomalyOverlapMode_accessor_storage, this, value); + } + get anyAnomaliesEnabled() { + return _classPrivateFieldGet2(_anyAnomaliesEnabled_accessor_storage, this); + } + set anyAnomaliesEnabled(value) { + _classPrivateFieldSet2(_anyAnomaliesEnabled_accessor_storage, this, value); + } + get activeSection() { + return _classPrivateFieldGet2(_activeSection_accessor_storage, this); + } + set activeSection(value) { + _classPrivateFieldSet2(_activeSection_accessor_storage, this, value); + } + disconnectedCallback() { + super.disconnectedCallback(); + if (this._closeTimer !== null) { + clearTimeout(this._closeTimer); + this._closeTimer = null; + } + } + _activateSection(key) { + if (this._closeTimer !== null) { + clearTimeout(this._closeTimer); + this._closeTimer = null; + } + this.activeSection = key; + } + _scheduleClose() { + if (this._closeTimer !== null) clearTimeout(this._closeTimer); + this._closeTimer = setTimeout(() => { + this.activeSection = null; + this._closeTimer = null; + }, 200); + } + _cancelClose() { + if (this._closeTimer !== null) { + clearTimeout(this._closeTimer); + this._closeTimer = null; + } + } + _renderSection() { + switch (this.activeSection) { + case "datapoints": return b` `; - case "datapoint-display": - return b` + case "datapoint-display": return b` `; - case "analysis": - return b` + case "analysis": return b` `; - case "chart-display": - return b` + case "chart-display": return b` `; - default: - return A; - } - } - render() { - return b` + default: return A; + } + } + render() { + return b`
${this.activeSection ? b`
`; - } - } - _init$o = __decoratorStart$o(_a$o); - _datapointScope = /* @__PURE__ */ new WeakMap(); - _showIcons = /* @__PURE__ */ new WeakMap(); - _showLines = /* @__PURE__ */ new WeakMap(); - _showTooltips = /* @__PURE__ */ new WeakMap(); - _showHoverGuides = /* @__PURE__ */ new WeakMap(); - _showCorrelatedAnomalies = /* @__PURE__ */ new WeakMap(); - _showDataGaps = /* @__PURE__ */ new WeakMap(); - _dataGapThreshold = /* @__PURE__ */ new WeakMap(); - _yAxisMode = /* @__PURE__ */ new WeakMap(); - _hoverSnapMode = /* @__PURE__ */ new WeakMap(); - _anomalyOverlapMode = /* @__PURE__ */ new WeakMap(); - _anyAnomaliesEnabled = /* @__PURE__ */ new WeakMap(); - _activeSection = /* @__PURE__ */ new WeakMap(); - __decorateElement$o(_init$o, 4, "datapointScope", _datapointScope_dec, CollapsedOptionsMenu, _datapointScope); - __decorateElement$o(_init$o, 4, "showIcons", _showIcons_dec, CollapsedOptionsMenu, _showIcons); - __decorateElement$o(_init$o, 4, "showLines", _showLines_dec, CollapsedOptionsMenu, _showLines); - __decorateElement$o(_init$o, 4, "showTooltips", _showTooltips_dec, CollapsedOptionsMenu, _showTooltips); - __decorateElement$o(_init$o, 4, "showHoverGuides", _showHoverGuides_dec, CollapsedOptionsMenu, _showHoverGuides); - __decorateElement$o(_init$o, 4, "showCorrelatedAnomalies", _showCorrelatedAnomalies_dec, CollapsedOptionsMenu, _showCorrelatedAnomalies); - __decorateElement$o(_init$o, 4, "showDataGaps", _showDataGaps_dec, CollapsedOptionsMenu, _showDataGaps); - __decorateElement$o(_init$o, 4, "dataGapThreshold", _dataGapThreshold_dec, CollapsedOptionsMenu, _dataGapThreshold); - __decorateElement$o(_init$o, 4, "yAxisMode", _yAxisMode_dec, CollapsedOptionsMenu, _yAxisMode); - __decorateElement$o(_init$o, 4, "hoverSnapMode", _hoverSnapMode_dec, CollapsedOptionsMenu, _hoverSnapMode); - __decorateElement$o(_init$o, 4, "anomalyOverlapMode", _anomalyOverlapMode_dec, CollapsedOptionsMenu, _anomalyOverlapMode); - __decorateElement$o(_init$o, 4, "anyAnomaliesEnabled", _anyAnomaliesEnabled_dec, CollapsedOptionsMenu, _anyAnomaliesEnabled); - __decorateElement$o(_init$o, 4, "activeSection", _activeSection_dec, CollapsedOptionsMenu, _activeSection); - __decoratorMetadata$o(_init$o, CollapsedOptionsMenu); - __publicField$v(CollapsedOptionsMenu, "styles", styles$u); - customElements.define("collapsed-options-menu", CollapsedOptionsMenu); - const e = e$1(class extends i$1 { - constructor(t2) { - if (super(t2), t2.type !== t$1.ATTRIBUTE || "class" !== t2.name || t2.strings?.length > 2) throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute."); - } - render(t2) { - return " " + Object.keys(t2).filter((s2) => t2[s2]).join(" ") + " "; - } - update(s2, [i2]) { - if (void 0 === this.st) { - this.st = /* @__PURE__ */ new Set(), void 0 !== s2.strings && (this.nt = new Set(s2.strings.join(" ").split(/\s/).filter((t2) => "" !== t2))); - for (const t2 in i2) i2[t2] && !this.nt?.has(t2) && this.st.add(t2); - return this.render(i2); - } - const r2 = s2.element.classList; - for (const t2 of this.st) t2 in i2 || (r2.remove(t2), this.st.delete(t2)); - for (const t2 in i2) { - const s3 = !!i2[t2]; - s3 === this.st.has(t2) || this.nt?.has(t2) || (s3 ? (r2.add(t2), this.st.add(t2)) : (r2.remove(t2), this.st.delete(t2))); - } - return E; - } - }); - const styles$t = i$5` + } + }; + _defineProperty(CollapsedOptionsMenu, "styles", styles$30); + __decorate([n$1({ type: String })], CollapsedOptionsMenu.prototype, "datapointScope", null); + __decorate([n$1({ type: Boolean })], CollapsedOptionsMenu.prototype, "showIcons", null); + __decorate([n$1({ type: Boolean })], CollapsedOptionsMenu.prototype, "showLines", null); + __decorate([n$1({ type: Boolean })], CollapsedOptionsMenu.prototype, "showTooltips", null); + __decorate([n$1({ type: Boolean })], CollapsedOptionsMenu.prototype, "showHoverGuides", null); + __decorate([n$1({ type: Boolean })], CollapsedOptionsMenu.prototype, "showCorrelatedAnomalies", null); + __decorate([n$1({ type: Boolean })], CollapsedOptionsMenu.prototype, "showDataGaps", null); + __decorate([n$1({ type: String })], CollapsedOptionsMenu.prototype, "dataGapThreshold", null); + __decorate([n$1({ type: String })], CollapsedOptionsMenu.prototype, "yAxisMode", null); + __decorate([n$1({ type: String })], CollapsedOptionsMenu.prototype, "hoverSnapMode", null); + __decorate([n$1({ type: String })], CollapsedOptionsMenu.prototype, "anomalyOverlapMode", null); + __decorate([n$1({ type: Boolean })], CollapsedOptionsMenu.prototype, "anyAnomaliesEnabled", null); + __decorate([r$1()], CollapsedOptionsMenu.prototype, "activeSection", null); + customElements.define("collapsed-options-menu", CollapsedOptionsMenu); + //#endregion + //#region node_modules/.pnpm/lit-html@3.3.2/node_modules/lit-html/directives/class-map.js + /** + * @license + * Copyright 2018 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ var e = e$2(class extends i$1 { + constructor(t) { + if (super(t), t.type !== t$1.ATTRIBUTE || "class" !== t.name || t.strings?.length > 2) throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute."); + } + render(t) { + return " " + Object.keys(t).filter((s) => t[s]).join(" ") + " "; + } + update(s, [i]) { + if (void 0 === this.st) { + this.st = /* @__PURE__ */ new Set(), void 0 !== s.strings && (this.nt = new Set(s.strings.join(" ").split(/\s/).filter((t) => "" !== t))); + for (const t in i) i[t] && !this.nt?.has(t) && this.st.add(t); + return this.render(i); + } + const r = s.element.classList; + for (const t of this.st) t in i || (r.remove(t), this.st.delete(t)); + for (const t in i) { + const s = !!i[t]; + s === this.st.has(t) || this.nt?.has(t) || (s ? (r.add(t), this.st.add(t)) : (r.remove(t), this.st.delete(t))); + } + return E; + } + }); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/comparison-tab-rail/comparison-tab-rail.styles.ts + var styles$29 = i$5` :host { display: block; } @@ -21396,7 +21648,9 @@ ${content.alert}` : "", display: none; } `; - const styles$s = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/molecules/comparison-tab/comparison-tab.styles.ts + var styles$28 = i$5` :host { display: contents; } @@ -21601,113 +21855,125 @@ ${content.alert}` : "", .chart-tab-action.delete:focus-visible { background: color-mix( in srgb, - var(--error-color, #db4437) 14%, - transparent - ); - color: var(--error-color, #db4437); - } -`; - var __create$n = Object.create; - var __defProp$u = Object.defineProperty; - var __getOwnPropDesc$n = Object.getOwnPropertyDescriptor; - var __knownSymbol$n = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$n = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$u = (obj, key, value) => key in obj ? __defProp$u(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$n = (base) => [, , , __create$n(base?.[__knownSymbol$n("metadata")] ?? null)]; - var __decoratorStrings$n = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$n = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$n("Function expected") : fn; - var __decoratorContext$n = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$n[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$n("Already initialized") : fns.push(__expectFn$n(fn || null)) }); - var __decoratorMetadata$n = (array, target) => __defNormalProp$u(target, __knownSymbol$n("metadata"), array[3]); - var __runInitializers$n = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$n = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$n[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$n({ get [name]() { - return __privateGet$m(this, extra); - }, set [name](x2) { - return __privateSet$m(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$n(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$n(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$n("Object expected"); - else __expectFn$n(fn = it.get) && (desc.get = fn), __expectFn$n(fn = it.set) && (desc.set = fn), __expectFn$n(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$u(target, name, desc), target; - }; - var __publicField$u = (obj, key, value) => __defNormalProp$u(obj, key + "", value); - var __accessCheck$m = (obj, member, msg2) => member.has(obj) || __typeError$n("Cannot " + msg2); - var __privateGet$m = (obj, member, getter) => (__accessCheck$m(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$m = (obj, member, value) => member.has(obj) ? __typeError$n("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$m = (obj, member, value, setter) => (__accessCheck$m(obj, member, "write to private field"), member.set(obj, value), value); - var _editable_dec, _loading_dec, _previewing_dec, _active_dec, _detail_dec, _label_dec$7, _tabId_dec, _a$n, _init$n, _tabId, _label$7, _detail, _active, _previewing, _loading, _editable; - class ComparisonTab extends (_a$n = i$2, _tabId_dec = [n({ type: String, attribute: "tab-id" })], _label_dec$7 = [n({ type: String })], _detail_dec = [n({ type: String })], _active_dec = [n({ type: Boolean })], _previewing_dec = [n({ type: Boolean })], _loading_dec = [n({ type: Boolean })], _editable_dec = [n({ type: Boolean })], _a$n) { - constructor() { - super(...arguments); - __privateAdd$m(this, _tabId, __runInitializers$n(_init$n, 8, this, "")), __runInitializers$n(_init$n, 11, this); - __privateAdd$m(this, _label$7, __runInitializers$n(_init$n, 12, this, "")), __runInitializers$n(_init$n, 15, this); - __privateAdd$m(this, _detail, __runInitializers$n(_init$n, 16, this, "")), __runInitializers$n(_init$n, 19, this); - __privateAdd$m(this, _active, __runInitializers$n(_init$n, 20, this, false)), __runInitializers$n(_init$n, 23, this); - __privateAdd$m(this, _previewing, __runInitializers$n(_init$n, 24, this, false)), __runInitializers$n(_init$n, 27, this); - __privateAdd$m(this, _loading, __runInitializers$n(_init$n, 28, this, false)), __runInitializers$n(_init$n, 31, this); - __privateAdd$m(this, _editable, __runInitializers$n(_init$n, 32, this, false)), __runInitializers$n(_init$n, 35, this); - } - _emit(name) { - this.dispatchEvent( - new CustomEvent(name, { - detail: { tabId: this.tabId }, - bubbles: true, - composed: true - }) - ); - } - _onTriggerClick() { - this._emit("dp-tab-activate"); - } - _onMouseEnter() { - this._emit("dp-tab-hover"); - } - _onMouseLeave() { - this._emit("dp-tab-leave"); - } - _onTriggerFocus() { - this._emit("dp-tab-hover"); - } - _onTriggerBlur() { - this._emit("dp-tab-leave"); - } - _onEditClick(ev) { - ev.preventDefault(); - ev.stopPropagation(); - this._emit("dp-tab-edit"); - } - _onDeleteClick(ev) { - ev.preventDefault(); - ev.stopPropagation(); - this._emit("dp-tab-delete"); - } - render() { - const tabClasses = e({ - "chart-tab": true, - active: this.active, - previewing: this.previewing, - loading: this.loading - }); - return b` + var(--error-color, #db4437) 14%, + transparent + ); + color: var(--error-color, #db4437); + } +`; + //#endregion + //#region custom_components/hass_datapoints/src/molecules/comparison-tab/comparison-tab.ts + var _tabId_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _label_accessor_storage$7 = /* @__PURE__ */ new WeakMap(); + var _detail_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _active_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _previewing_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _loading_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _editable_accessor_storage = /* @__PURE__ */ new WeakMap(); + /** + * `comparison-tab` renders a single comparison date-window tab in the chart + * tab rail, handling active, previewing, loading, and editable states. + * + * @fires dp-tab-activate - `{ tabId: string }` fired when the trigger button is clicked + * @fires dp-tab-hover - `{ tabId: string }` fired on mouseenter / trigger focus + * @fires dp-tab-leave - `{ tabId: string }` fired on mouseleave / trigger blur + * @fires dp-tab-edit - `{ tabId: string }` fired when the edit action button is clicked + * @fires dp-tab-delete - `{ tabId: string }` fired when the delete action button is clicked + */ + var ComparisonTab = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _tabId_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _label_accessor_storage$7, ""); + _classPrivateFieldInitSpec(this, _detail_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _active_accessor_storage, false); + _classPrivateFieldInitSpec(this, _previewing_accessor_storage, false); + _classPrivateFieldInitSpec(this, _loading_accessor_storage, false); + _classPrivateFieldInitSpec(this, _editable_accessor_storage, false); + } + get tabId() { + return _classPrivateFieldGet2(_tabId_accessor_storage, this); + } + set tabId(value) { + _classPrivateFieldSet2(_tabId_accessor_storage, this, value); + } + get label() { + return _classPrivateFieldGet2(_label_accessor_storage$7, this); + } + set label(value) { + _classPrivateFieldSet2(_label_accessor_storage$7, this, value); + } + get detail() { + return _classPrivateFieldGet2(_detail_accessor_storage, this); + } + set detail(value) { + _classPrivateFieldSet2(_detail_accessor_storage, this, value); + } + get active() { + return _classPrivateFieldGet2(_active_accessor_storage, this); + } + set active(value) { + _classPrivateFieldSet2(_active_accessor_storage, this, value); + } + get previewing() { + return _classPrivateFieldGet2(_previewing_accessor_storage, this); + } + set previewing(value) { + _classPrivateFieldSet2(_previewing_accessor_storage, this, value); + } + get loading() { + return _classPrivateFieldGet2(_loading_accessor_storage, this); + } + set loading(value) { + _classPrivateFieldSet2(_loading_accessor_storage, this, value); + } + get editable() { + return _classPrivateFieldGet2(_editable_accessor_storage, this); + } + set editable(value) { + _classPrivateFieldSet2(_editable_accessor_storage, this, value); + } + _emit(name) { + this.dispatchEvent(new CustomEvent(name, { + detail: { tabId: this.tabId }, + bubbles: true, + composed: true + })); + } + _onTriggerClick() { + this._emit("dp-tab-activate"); + } + _onMouseEnter() { + this._emit("dp-tab-hover"); + } + _onMouseLeave() { + this._emit("dp-tab-leave"); + } + _onTriggerFocus() { + this._emit("dp-tab-hover"); + } + _onTriggerBlur() { + this._emit("dp-tab-leave"); + } + _onEditClick(ev) { + ev.preventDefault(); + ev.stopPropagation(); + this._emit("dp-tab-edit"); + } + _onDeleteClick(ev) { + ev.preventDefault(); + ev.stopPropagation(); + this._emit("dp-tab-delete"); + } + render() { + return b`
@@ -21754,146 +22020,94 @@ ${content.alert}` : "", ` : null}
`; - } - } - _init$n = __decoratorStart$n(_a$n); - _tabId = /* @__PURE__ */ new WeakMap(); - _label$7 = /* @__PURE__ */ new WeakMap(); - _detail = /* @__PURE__ */ new WeakMap(); - _active = /* @__PURE__ */ new WeakMap(); - _previewing = /* @__PURE__ */ new WeakMap(); - _loading = /* @__PURE__ */ new WeakMap(); - _editable = /* @__PURE__ */ new WeakMap(); - __decorateElement$n(_init$n, 4, "tabId", _tabId_dec, ComparisonTab, _tabId); - __decorateElement$n(_init$n, 4, "label", _label_dec$7, ComparisonTab, _label$7); - __decorateElement$n(_init$n, 4, "detail", _detail_dec, ComparisonTab, _detail); - __decorateElement$n(_init$n, 4, "active", _active_dec, ComparisonTab, _active); - __decorateElement$n(_init$n, 4, "previewing", _previewing_dec, ComparisonTab, _previewing); - __decorateElement$n(_init$n, 4, "loading", _loading_dec, ComparisonTab, _loading); - __decorateElement$n(_init$n, 4, "editable", _editable_dec, ComparisonTab, _editable); - __decoratorMetadata$n(_init$n, ComparisonTab); - __publicField$u(ComparisonTab, "styles", styles$s); - customElements.define("comparison-tab", ComparisonTab); - var __create$m = Object.create; - var __defProp$t = Object.defineProperty; - var __getOwnPropDesc$m = Object.getOwnPropertyDescriptor; - var __knownSymbol$m = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$m = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$t = (obj, key, value) => key in obj ? __defProp$t(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __name$6 = (target, value) => __defProp$t(target, "name", { value, configurable: true }); - var __decoratorStart$m = (base) => [, , , __create$m(base?.[__knownSymbol$m("metadata")] ?? null)]; - var __decoratorStrings$m = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$m = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$m("Function expected") : fn; - var __decoratorContext$m = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$m[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$m("Already initialized") : fns.push(__expectFn$m(fn || null)) }); - var __decoratorMetadata$m = (array, target) => __defNormalProp$t(target, __knownSymbol$m("metadata"), array[3]); - var __runInitializers$m = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$m = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = !!(flags & 8), p2 = !!(flags & 16); - var j2 = k2 > 3 ? array.length + 1 : k2 ? s2 ? 1 : 2 : 0, key = __decoratorStrings$m[k2 + 5]; - var initializers = k2 > 3 && (array[j2 - 1] = []), extraInitializers = array[j2] || (array[j2] = []); - var desc = k2 && (!p2 && !s2 && (target = target.prototype), k2 < 5 && (k2 > 3 || !p2) && __getOwnPropDesc$m(k2 < 4 ? target : { get [name]() { - return __privateGet$l(this, extra); - }, set [name](x2) { - return __privateSet$l(this, extra, x2); - } }, name)); - k2 ? p2 && k2 < 4 && __name$6(extra, (k2 > 2 ? "set " : k2 > 1 ? "get " : "") + name) : __name$6(target, name); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$m(k2, name, done = {}, array[3], extraInitializers); - if (k2) { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: p2 ? (x2) => __privateIn$5(target, x2) : (x2) => name in x2 }; - if (k2 ^ 3) access.get = p2 ? (x2) => (k2 ^ 1 ? __privateGet$l : __privateMethod$5)(x2, target, k2 ^ 4 ? extra : desc.get) : (x2) => x2[name]; - if (k2 > 2) access.set = p2 ? (x2, y2) => __privateSet$l(x2, target, y2, k2 ^ 4 ? extra : desc.set) : (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])(k2 ? k2 < 4 ? p2 ? extra : desc[key] : k2 > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; - if (k2 ^ 4 || it === void 0) __expectFn$m(it) && (k2 > 4 ? initializers.unshift(it) : k2 ? p2 ? extra = it : desc[key] = it : target = it); - else if (typeof it !== "object" || it === null) __typeError$m("Object expected"); - else __expectFn$m(fn = it.get) && (desc.get = fn), __expectFn$m(fn = it.set) && (desc.set = fn), __expectFn$m(fn = it.init) && initializers.unshift(fn); - } - return k2 || __decoratorMetadata$m(array, target), desc && __defProp$t(target, name, desc), p2 ? k2 ^ 4 ? extra : desc : target; - }; - var __publicField$t = (obj, key, value) => __defNormalProp$t(obj, typeof key !== "symbol" ? key + "" : key, value); - var __accessCheck$l = (obj, member, msg2) => member.has(obj) || __typeError$m("Cannot " + msg2); - var __privateIn$5 = (member, obj) => Object(obj) !== obj ? __typeError$m('Cannot use the "in" operator on this value') : member.has(obj); - var __privateGet$l = (obj, member, getter) => (__accessCheck$l(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); - var __privateAdd$l = (obj, member, value) => member.has(obj) ? __typeError$m("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$l = (obj, member, value, setter) => (__accessCheck$l(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); - var __privateMethod$5 = (obj, member, method) => (__accessCheck$l(obj, member, "access private method"), method); - var _overflowing_dec, _hoveredId_dec, _loadingIds_dec, _tabs_dec, _a$m, _ComparisonTabRail_decorators, _init$m, _tabs, _loadingIds, _hoveredId, _overflowing; - _ComparisonTabRail_decorators = [localized()]; - class ComparisonTabRail extends (_a$m = i$2, _tabs_dec = [n({ type: Array })], _loadingIds_dec = [n({ type: Array, attribute: false })], _hoveredId_dec = [n({ type: String, attribute: "hovered-id" })], _overflowing_dec = [n({ type: Boolean })], _a$m) { - constructor() { - super(...arguments); - __privateAdd$l(this, _tabs, __runInitializers$m(_init$m, 8, this, [])), __runInitializers$m(_init$m, 11, this); - __privateAdd$l(this, _loadingIds, __runInitializers$m(_init$m, 12, this, [])), __runInitializers$m(_init$m, 15, this); - __privateAdd$l(this, _hoveredId, __runInitializers$m(_init$m, 16, this, "")), __runInitializers$m(_init$m, 19, this); - __privateAdd$l(this, _overflowing, __runInitializers$m(_init$m, 20, this, false)), __runInitializers$m(_init$m, 23, this); - __publicField$t(this, "_resizeObserver"); - } - connectedCallback() { - super.connectedCallback(); - if (this._resizeObserver) { - return; - } - this._resizeObserver = new ResizeObserver(() => this._checkOverflow()); - this.updateComplete.then(() => { - if (!this.isConnected || !this._resizeObserver) { - return; - } - const shell = this.shadowRoot?.querySelector( - ".chart-tabs-shell" - ); - if (shell) { - this._resizeObserver.observe(shell); - } - }); - } - disconnectedCallback() { - super.disconnectedCallback(); - this._resizeObserver?.disconnect(); - this._resizeObserver = void 0; - } - _checkOverflow() { - const shell = this.shadowRoot?.querySelector( - ".chart-tabs-shell" - ); - if (!shell) { - return; - } - const rail = shell.querySelector( - ".chart-tabs-rail" - ); - if (!rail) { - return; - } - this.overflowing = rail.scrollWidth > rail.clientWidth; - } - _onAddClick() { - this.dispatchEvent( - new CustomEvent("dp-tab-add", { - detail: {}, - bubbles: true, - composed: true - }) - ); - } - render() { - const shellClasses = e({ - "chart-tabs-shell": true, - overflowing: this.overflowing - }); - return b` -
+ } + }; + _defineProperty(ComparisonTab, "styles", styles$28); + __decorate([n$1({ + type: String, + attribute: "tab-id" + })], ComparisonTab.prototype, "tabId", null); + __decorate([n$1({ type: String })], ComparisonTab.prototype, "label", null); + __decorate([n$1({ type: String })], ComparisonTab.prototype, "detail", null); + __decorate([n$1({ type: Boolean })], ComparisonTab.prototype, "active", null); + __decorate([n$1({ type: Boolean })], ComparisonTab.prototype, "previewing", null); + __decorate([n$1({ type: Boolean })], ComparisonTab.prototype, "loading", null); + __decorate([n$1({ type: Boolean })], ComparisonTab.prototype, "editable", null); + customElements.define("comparison-tab", ComparisonTab); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/comparison-tab-rail/comparison-tab-rail.ts + var _ComparisonTabRail, _tabs_accessor_storage, _loadingIds_accessor_storage, _hoveredId_accessor_storage, _overflowing_accessor_storage; + var ComparisonTabRail = (_tabs_accessor_storage = /* @__PURE__ */ new WeakMap(), _loadingIds_accessor_storage = /* @__PURE__ */ new WeakMap(), _hoveredId_accessor_storage = /* @__PURE__ */ new WeakMap(), _overflowing_accessor_storage = /* @__PURE__ */ new WeakMap(), _ComparisonTabRail = class ComparisonTabRail extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _tabs_accessor_storage, []); + _classPrivateFieldInitSpec(this, _loadingIds_accessor_storage, []); + _classPrivateFieldInitSpec(this, _hoveredId_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _overflowing_accessor_storage, false); + _defineProperty(this, "_resizeObserver", void 0); + } + get tabs() { + return _classPrivateFieldGet2(_tabs_accessor_storage, this); + } + set tabs(value) { + _classPrivateFieldSet2(_tabs_accessor_storage, this, value); + } + get loadingIds() { + return _classPrivateFieldGet2(_loadingIds_accessor_storage, this); + } + set loadingIds(value) { + _classPrivateFieldSet2(_loadingIds_accessor_storage, this, value); + } + get hoveredId() { + return _classPrivateFieldGet2(_hoveredId_accessor_storage, this); + } + set hoveredId(value) { + _classPrivateFieldSet2(_hoveredId_accessor_storage, this, value); + } + get overflowing() { + return _classPrivateFieldGet2(_overflowing_accessor_storage, this); + } + set overflowing(value) { + _classPrivateFieldSet2(_overflowing_accessor_storage, this, value); + } + connectedCallback() { + super.connectedCallback(); + if (this._resizeObserver) return; + this._resizeObserver = new ResizeObserver(() => this._checkOverflow()); + this.updateComplete.then(() => { + if (!this.isConnected || !this._resizeObserver) return; + const shell = this.shadowRoot?.querySelector(".chart-tabs-shell"); + if (shell) this._resizeObserver.observe(shell); + }); + } + disconnectedCallback() { + super.disconnectedCallback(); + this._resizeObserver?.disconnect(); + this._resizeObserver = void 0; + } + _checkOverflow() { + const shell = this.shadowRoot?.querySelector(".chart-tabs-shell"); + if (!shell) return; + const rail = shell.querySelector(".chart-tabs-rail"); + if (!rail) return; + this.overflowing = rail.scrollWidth > rail.clientWidth; + } + _onAddClick() { + this.dispatchEvent(new CustomEvent("dp-tab-add", { + detail: {}, + bubbles: true, + composed: true + })); + } + render() { + return b` +
- ${c( - this.tabs, - (tab) => tab.id, - (tab) => b` + ${c(this.tabs, (tab) => tab.id, (tab) => b` - ` - )} + `)}
`; - } - } - _init$m = __decoratorStart$m(_a$m); - _tabs = /* @__PURE__ */ new WeakMap(); - _loadingIds = /* @__PURE__ */ new WeakMap(); - _hoveredId = /* @__PURE__ */ new WeakMap(); - _overflowing = /* @__PURE__ */ new WeakMap(); - __decorateElement$m(_init$m, 4, "tabs", _tabs_dec, ComparisonTabRail, _tabs); - __decorateElement$m(_init$m, 4, "loadingIds", _loadingIds_dec, ComparisonTabRail, _loadingIds); - __decorateElement$m(_init$m, 4, "hoveredId", _hoveredId_dec, ComparisonTabRail, _hoveredId); - __decorateElement$m(_init$m, 4, "overflowing", _overflowing_dec, ComparisonTabRail, _overflowing); - ComparisonTabRail = __decorateElement$m(_init$m, 0, "ComparisonTabRail", _ComparisonTabRail_decorators, ComparisonTabRail); - __publicField$t(ComparisonTabRail, "styles", styles$t); - __runInitializers$m(_init$m, 1, ComparisonTabRail); - customElements.define("comparison-tab-rail", ComparisonTabRail); - const styles$r = i$5` + } + }, _defineProperty(_ComparisonTabRail, "styles", styles$29), _ComparisonTabRail); + __decorate([n$1({ type: Array })], ComparisonTabRail.prototype, "tabs", null); + __decorate([n$1({ + type: Array, + attribute: false + })], ComparisonTabRail.prototype, "loadingIds", null); + __decorate([n$1({ + type: String, + attribute: "hovered-id" + })], ComparisonTabRail.prototype, "hoveredId", null); + __decorate([n$1({ type: Boolean })], ComparisonTabRail.prototype, "overflowing", null); + ComparisonTabRail = __decorate([localized()], ComparisonTabRail); + customElements.define("comparison-tab-rail", ComparisonTabRail); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/date-window-dialog/date-window-dialog.styles.ts + var styles$27 = i$5` :host { --dp-spacing-xs: calc(var(--spacing, 8px) * 0.5); --dp-spacing-sm: var(--spacing, 8px); @@ -22082,7 +22296,9 @@ ${content.alert}` : "", } } `; - const styles$q = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/range-timeline/range-timeline.styles.ts + var styles$26 = i$5` :host { display: block; position: relative; @@ -22417,7 +22633,9 @@ ${content.alert}` : "", z-index: 9; } `; - const styles$p = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/range-handle/range-handle.styles.ts + var styles$25 = i$5` :host { position: absolute; top: 26px; @@ -22463,1545 +22681,1213 @@ ${content.alert}` : "", 0 0 0 5px rgba(239, 83, 80, 0.2); } } - - .handle.is-live { - background: #ef5350; - animation: dp-live-breathe 3s ease-in-out infinite; - } -`; - var __create$l = Object.create; - var __defProp$s = Object.defineProperty; - var __getOwnPropDesc$l = Object.getOwnPropertyDescriptor; - var __knownSymbol$l = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$l = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$s = (obj, key, value) => key in obj ? __defProp$s(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$l = (base) => [, , , __create$l(base?.[__knownSymbol$l("metadata")] ?? null)]; - var __decoratorStrings$l = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$l = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$l("Function expected") : fn; - var __decoratorContext$l = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$l[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$l("Already initialized") : fns.push(__expectFn$l(fn || null)) }); - var __decoratorMetadata$l = (array, target) => __defNormalProp$s(target, __knownSymbol$l("metadata"), array[3]); - var __runInitializers$l = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$l = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$l[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$l({ get [name]() { - return __privateGet$k(this, extra); - }, set [name](x2) { - return __privateSet$k(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$l(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$l(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$l("Object expected"); - else __expectFn$l(fn = it.get) && (desc.get = fn), __expectFn$l(fn = it.set) && (desc.set = fn), __expectFn$l(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$s(target, name, desc), target; - }; - var __publicField$s = (obj, key, value) => __defNormalProp$s(obj, key + "", value); - var __accessCheck$k = (obj, member, msg2) => member.has(obj) || __typeError$l("Cannot " + msg2); - var __privateGet$k = (obj, member, getter) => (__accessCheck$k(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$k = (obj, member, value) => member.has(obj) ? __typeError$l("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$k = (obj, member, value, setter) => (__accessCheck$k(obj, member, "write to private field"), member.set(obj, value), value); - var _live_dec, _label_dec$6, _position_dec, _a$l, _init$l, _position, _label$6, _live; - class RangeHandle extends (_a$l = i$2, _position_dec = [n({ type: Number })], _label_dec$6 = [n({ type: String })], _live_dec = [n({ type: Boolean })], _a$l) { - constructor() { - super(...arguments); - __privateAdd$k(this, _position, __runInitializers$l(_init$l, 8, this, 0)), __runInitializers$l(_init$l, 11, this); - __privateAdd$k(this, _label$6, __runInitializers$l(_init$l, 12, this, "")), __runInitializers$l(_init$l, 15, this); - __privateAdd$k(this, _live, __runInitializers$l(_init$l, 16, this, false)), __runInitializers$l(_init$l, 19, this); - } - updated(changed) { - if (changed.has("position")) { - this.style.left = `${this.position}%`; - } - } - _onPointerDown(e2) { - e2.preventDefault(); - this.dispatchEvent( - new CustomEvent("dp-handle-drag-start", { - detail: { pointerId: e2.pointerId, clientX: e2.clientX }, - bubbles: true, - composed: true - }) - ); - } - _onKeyDown(e2) { - const navKeys = [ - "ArrowLeft", - "ArrowRight", - "ArrowDown", - "ArrowUp", - "PageDown", - "PageUp", - "Home", - "End" - ]; - if (!navKeys.includes(e2.key)) return; - e2.preventDefault(); - this.dispatchEvent( - new CustomEvent("dp-handle-keydown", { - detail: { key: e2.key, shiftKey: e2.shiftKey }, - bubbles: true, - composed: true - }) - ); - } - _onPointerEnter() { - this.dispatchEvent( - new CustomEvent("dp-handle-hover", { bubbles: true, composed: true }) - ); - } - _onPointerLeave() { - this.dispatchEvent( - new CustomEvent("dp-handle-leave", { bubbles: true, composed: true }) - ); - } - _onFocus() { - this.dispatchEvent( - new CustomEvent("dp-handle-focus", { bubbles: true, composed: true }) - ); - } - _onBlur() { - this.dispatchEvent( - new CustomEvent("dp-handle-blur", { bubbles: true, composed: true }) - ); - } - render() { - return b` - - `; - } - } - _init$l = __decoratorStart$l(_a$l); - _position = /* @__PURE__ */ new WeakMap(); - _label$6 = /* @__PURE__ */ new WeakMap(); - _live = /* @__PURE__ */ new WeakMap(); - __decorateElement$l(_init$l, 4, "position", _position_dec, RangeHandle, _position); - __decorateElement$l(_init$l, 4, "label", _label_dec$6, RangeHandle, _label$6); - __decorateElement$l(_init$l, 4, "live", _live_dec, RangeHandle, _live); - __decoratorMetadata$l(_init$l, RangeHandle); - __publicField$s(RangeHandle, "styles", styles$p); - customElements.define("range-handle", RangeHandle); - var __create$k = Object.create; - var __defProp$r = Object.defineProperty; - var __getOwnPropDesc$k = Object.getOwnPropertyDescriptor; - var __knownSymbol$k = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$k = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$r = (obj, key, value) => key in obj ? __defProp$r(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __name$5 = (target, value) => __defProp$r(target, "name", { value, configurable: true }); - var __decoratorStart$k = (base) => [, , , __create$k(base?.[__knownSymbol$k("metadata")] ?? null)]; - var __decoratorStrings$k = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$k = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$k("Function expected") : fn; - var __decoratorContext$k = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$k[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$k("Already initialized") : fns.push(__expectFn$k(fn || null)) }); - var __decoratorMetadata$k = (array, target) => __defNormalProp$r(target, __knownSymbol$k("metadata"), array[3]); - var __runInitializers$k = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$k = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = !!(flags & 8), p2 = !!(flags & 16); - var j2 = k2 > 3 ? array.length + 1 : k2 ? s2 ? 1 : 2 : 0, key = __decoratorStrings$k[k2 + 5]; - var initializers = k2 > 3 && (array[j2 - 1] = []), extraInitializers = array[j2] || (array[j2] = []); - var desc = k2 && (!p2 && !s2 && (target = target.prototype), k2 < 5 && (k2 > 3 || !p2) && __getOwnPropDesc$k(k2 < 4 ? target : { get [name]() { - return __privateGet$j(this, extra); - }, set [name](x2) { - return __privateSet$j(this, extra, x2); - } }, name)); - k2 ? p2 && k2 < 4 && __name$5(extra, (k2 > 2 ? "set " : k2 > 1 ? "get " : "") + name) : __name$5(target, name); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$k(k2, name, done = {}, array[3], extraInitializers); - if (k2) { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: p2 ? (x2) => __privateIn$4(target, x2) : (x2) => name in x2 }; - if (k2 ^ 3) access.get = p2 ? (x2) => (k2 ^ 1 ? __privateGet$j : __privateMethod$4)(x2, target, k2 ^ 4 ? extra : desc.get) : (x2) => x2[name]; - if (k2 > 2) access.set = p2 ? (x2, y2) => __privateSet$j(x2, target, y2, k2 ^ 4 ? extra : desc.set) : (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])(k2 ? k2 < 4 ? p2 ? extra : desc[key] : k2 > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; - if (k2 ^ 4 || it === void 0) __expectFn$k(it) && (k2 > 4 ? initializers.unshift(it) : k2 ? p2 ? extra = it : desc[key] = it : target = it); - else if (typeof it !== "object" || it === null) __typeError$k("Object expected"); - else __expectFn$k(fn = it.get) && (desc.get = fn), __expectFn$k(fn = it.set) && (desc.set = fn), __expectFn$k(fn = it.init) && initializers.unshift(fn); - } - return k2 || __decoratorMetadata$k(array, target), desc && __defProp$r(target, name, desc), p2 ? k2 ^ 4 ? extra : desc : target; - }; - var __publicField$r = (obj, key, value) => __defNormalProp$r(obj, typeof key !== "symbol" ? key + "" : key, value); - var __accessCheck$j = (obj, member, msg2) => member.has(obj) || __typeError$k("Cannot " + msg2); - var __privateIn$4 = (member, obj) => Object(obj) !== obj ? __typeError$k('Cannot use the "in" operator on this value') : member.has(obj); - var __privateGet$j = (obj, member, getter) => (__accessCheck$j(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); - var __privateAdd$j = (obj, member, value) => member.has(obj) ? __typeError$k("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$j = (obj, member, value, setter) => (__accessCheck$j(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); - var __privateMethod$4 = (obj, member, method) => (__accessCheck$j(obj, member, "access private method"), method); - var _locale_dec$1, _isLiveEdge_dec$2, _dateSnapping_dec$3, _zoomLevel_dec$3, _rangeBounds_dec$3, _endTime_dec$2, _startTime_dec$2, _a$k, _RangeTimeline_decorators, _init$k, _startTime$2, _endTime$2, _rangeBounds$3, _zoomLevel$3, _dateSnapping$3, _isLiveEdge$2, _locale$1; - _RangeTimeline_decorators = [localized()]; - class RangeTimeline extends (_a$k = i$2, _startTime_dec$2 = [n({ type: Object })], _endTime_dec$2 = [n({ type: Object })], _rangeBounds_dec$3 = [n({ type: Object })], _zoomLevel_dec$3 = [n({ type: String })], _dateSnapping_dec$3 = [n({ type: String })], _isLiveEdge_dec$2 = [n({ type: Boolean })], _locale_dec$1 = [n({ type: String })], _a$k) { - constructor() { - super(); - __privateAdd$j(this, _startTime$2, __runInitializers$k(_init$k, 8, this, null)), __runInitializers$k(_init$k, 11, this); - __privateAdd$j(this, _endTime$2, __runInitializers$k(_init$k, 12, this, null)), __runInitializers$k(_init$k, 15, this); - __privateAdd$j(this, _rangeBounds$3, __runInitializers$k(_init$k, 16, this, null)), __runInitializers$k(_init$k, 19, this); - __privateAdd$j(this, _zoomLevel$3, __runInitializers$k(_init$k, 20, this, "day")), __runInitializers$k(_init$k, 23, this); - __privateAdd$j(this, _dateSnapping$3, __runInitializers$k(_init$k, 24, this, "auto")), __runInitializers$k(_init$k, 27, this); - __privateAdd$j(this, _isLiveEdge$2, __runInitializers$k(_init$k, 28, this, false)), __runInitializers$k(_init$k, 31, this); - __privateAdd$j(this, _locale$1, __runInitializers$k(_init$k, 32, this, "")), __runInitializers$k(_init$k, 35, this); - __publicField$r(this, "_draftStartTime", null); - __publicField$r(this, "_draftEndTime", null); - __publicField$r(this, "_activeRangeHandle", null); - __publicField$r(this, "_hoveredRangeHandle", null); - __publicField$r(this, "_focusedRangeHandle", null); - __publicField$r(this, "_hoveredPeriodRange", null); - __publicField$r(this, "_rangePointerId", null); - __publicField$r(this, "_rangeInteractionActive", false); - __publicField$r(this, "_rangeContentWidth", 0); - __publicField$r(this, "_rangeCommitTimer", null); - __publicField$r(this, "_isProgrammaticScroll", false); - __publicField$r(this, "_scrollbarHideTimer", null); - __publicField$r(this, "_timelinePointerId", null); - __publicField$r(this, "_timelinePointerStartX", 0); - __publicField$r(this, "_timelinePointerStartScrollLeft", 0); - __publicField$r(this, "_timelinePointerStartTimestamp", null); - __publicField$r(this, "_timelinePointerMode", null); - __publicField$r(this, "_timelineDragStartRangeMs", 0); - __publicField$r(this, "_timelineDragEndRangeMs", 0); - __publicField$r(this, "_timelineDragStartZoomRange", null); - __publicField$r(this, "_timelinePointerMoved", false); - __publicField$r(this, "_timelineTrackClickPending", false); - __publicField$r(this, "_rangeScrollViewportEl", null); - __publicField$r(this, "_rangeTimelineEl", null); - __publicField$r(this, "_rangeTrackEl", null); - __publicField$r(this, "_rangeTickLayerEl", null); - __publicField$r(this, "_rangeLabelLayerEl", null); - __publicField$r(this, "_rangeContextLayerEl", null); - __publicField$r(this, "_rangeSelectionEl", null); - __publicField$r(this, "_rangeStartHandleEl", null); - __publicField$r(this, "_rangeEndHandleEl", null); - __publicField$r(this, "_rangeStartTooltipEl", null); - __publicField$r(this, "_rangeEndTooltipEl", null); - __publicField$r(this, "_rangeJumpLeftEl", null); - __publicField$r(this, "_rangeJumpRightEl", null); - __publicField$r(this, "_resizeObserver", null); - __publicField$r(this, "_onRangeScroll"); - __publicField$r(this, "_onRangePointerMove"); - __publicField$r(this, "_onRangePointerUp"); - __publicField$r(this, "_onTimelinePointerMove"); - __publicField$r(this, "_onTimelinePointerUp"); - this._onRangePointerMove = (ev) => this._handleRangePointerMove(ev); - this._onRangePointerUp = (ev) => this._finishRangePointerInteraction(ev); - this._onTimelinePointerMove = (ev) => this._handleTimelinePointerMove(ev); - this._onTimelinePointerUp = (ev) => this._finishTimelinePointerInteraction(ev); - this._onRangeScroll = () => { - this._updateSelectionJumpControls(); - this._syncVisibleRangeLabels(); - this._updateRangeTooltip(); - this.dispatchEvent( - new CustomEvent("dp-range-scroll", { bubbles: true, composed: true }) - ); - if (!this._isProgrammaticScroll) { - this._showScrollbar(); - } - }; - } - disconnectedCallback() { - super.disconnectedCallback(); - if (this._rangeScrollViewportEl) { - this._rangeScrollViewportEl.removeEventListener( - "scroll", - this._onRangeScroll - ); - } - this._detachRangePointerListeners(); - this._detachTimelinePointerListeners(); - if (this._rangeCommitTimer) { - window.clearTimeout(this._rangeCommitTimer); - this._rangeCommitTimer = null; - } - if (this._scrollbarHideTimer) { - window.clearTimeout(this._scrollbarHideTimer); - this._scrollbarHideTimer = null; - } - if (this._resizeObserver) { - this._resizeObserver.disconnect(); - this._resizeObserver = null; - } - } - firstUpdated() { - const sr = this.shadowRoot; - this._rangeScrollViewportEl = sr.getElementById("range-scroll-viewport"); - this._rangeTimelineEl = sr.getElementById("range-timeline"); - this._rangeTrackEl = sr.getElementById("range-track"); - this._rangeTickLayerEl = sr.getElementById("range-tick-layer"); - this._rangeLabelLayerEl = sr.getElementById("range-label-layer"); - this._rangeContextLayerEl = sr.getElementById("range-context-layer"); - this._rangeSelectionEl = sr.getElementById("range-selection"); - this._rangeStartHandleEl = sr.getElementById("range-start-handle"); - this._rangeEndHandleEl = sr.getElementById("range-end-handle"); - this._rangeStartTooltipEl = sr.getElementById("range-tooltip-start"); - this._rangeEndTooltipEl = sr.getElementById("range-tooltip-end"); - this._rangeJumpLeftEl = sr.getElementById("range-jump-left"); - this._rangeJumpRightEl = sr.getElementById("range-jump-right"); - this._rangeScrollViewportEl?.addEventListener( - "scroll", - this._onRangeScroll - ); - if (typeof ResizeObserver !== "undefined") { - this._resizeObserver = new ResizeObserver(() => { - this._syncTimelineWidth(); - this._updateSelectionJumpControls(); - this._syncVisibleRangeLabels(); - this._revealSelectionInTimeline("auto"); - }); - if (this._rangeScrollViewportEl) { - this._resizeObserver.observe(this._rangeScrollViewportEl); - } - } - this._syncRangeControl(); - } - updated(changed) { - const rangeProps = [ - "startTime", - "endTime", - "rangeBounds", - "zoomLevel", - "dateSnapping" - ]; - if (rangeProps.some((p2) => changed.has(p2))) { - this._syncRangeControl(); - } - } - _pctForTime(time) { - if (!time || !this.rangeBounds) return 0; - const { min, max } = this.rangeBounds; - return Math.max( - 0, - Math.min(100, (time.getTime() - min) / (max - min) * 100) - ); - } - render() { - return b` - - -
-
- -
-
-
- -
-
-
- this._beginRangePointerInteraction( - "start", - e2.detail.pointerId, - e2.detail.clientX - )} - @dp-handle-keydown=${(e2) => this._handleRangeHandleKeyDown("start", e2.detail)} - @dp-handle-hover=${() => this._setRangeTooltipHoverHandle("start")} - @dp-handle-leave=${() => this._clearRangeTooltipHoverHandle("start")} - @dp-handle-focus=${() => this._setRangeTooltipFocusHandle("start")} - @dp-handle-blur=${() => this._clearRangeTooltipFocusHandle("start")} - > - this._beginRangePointerInteraction( - "end", - e2.detail.pointerId, - e2.detail.clientX - )} - @dp-handle-keydown=${(e2) => this._handleRangeHandleKeyDown("end", e2.detail)} - @dp-handle-hover=${() => this._setRangeTooltipHoverHandle("end")} - @dp-handle-leave=${() => this._clearRangeTooltipHoverHandle("end")} - @dp-handle-focus=${() => this._setRangeTooltipFocusHandle("end")} - @dp-handle-blur=${() => this._clearRangeTooltipFocusHandle("end")} - > -
-
- - - `; - } - // --------------------------------------------------------------------------- - // Zoom / snap helpers - // --------------------------------------------------------------------------- - _getZoomConfig() { - return RANGE_ZOOM_CONFIGS[this.zoomLevel] || RANGE_ZOOM_CONFIGS.month_short; - } - _getEffectiveSnapUnit() { - if (this.dateSnapping !== "auto") { - return this.dateSnapping; - } - switch (this.zoomLevel) { - case "quarterly": - case "month_compressed": - return "month"; - case "month_short": - case "month_expanded": - case "week_compressed": - return "week"; - case "week_expanded": - return "day"; - case "day": - return "hour"; - default: - return "day"; - } - } - _getScaleLabelZoomLevel() { - if (this.zoomLevel === "quarterly" || this.zoomLevel === "month_short") { - return this.zoomLevel; - } - return ""; - } - _getSnapSpanMs(reference = /* @__PURE__ */ new Date()) { - const snapUnit = this._getEffectiveSnapUnit(); - const start = startOfUnit(reference, snapUnit); - const end = endOfUnit(reference, snapUnit); - return Math.max(SECOND_MS, end.getTime() - start.getTime()); - } - _countUnitsInRange(startMs, endMs, unit) { - const totalMs = Math.max(0, endMs - startMs); - const perMs = { - second: SECOND_MS, - minute: 60 * SECOND_MS, - hour: 60 * 60 * SECOND_MS, - day: 24 * 60 * 60 * SECOND_MS, - week: 7 * 24 * 60 * 60 * SECOND_MS - }; - if (perMs[unit]) return Math.ceil(totalMs / perMs[unit]); - if (unit === "month") - return Math.ceil(totalMs / (30.44 * 24 * 60 * 60 * SECOND_MS)); - if (unit === "quarter") - return Math.ceil(totalMs / (91.3 * 24 * 60 * 60 * SECOND_MS)); - if (unit === "year") - return Math.ceil(totalMs / (365.25 * 24 * 60 * 60 * SECOND_MS)); - return Math.max(1, Math.ceil(totalMs / (24 * 60 * 60 * SECOND_MS))); - } - // --------------------------------------------------------------------------- - // Sync / render - // --------------------------------------------------------------------------- - _syncRangeControl() { - if (!this._rangeTrackEl || !this._rangeStartHandleEl || !this._rangeEndHandleEl) - return; - if (!this.rangeBounds) return; - this._draftStartTime = this.startTime ? new Date(this.startTime) : null; - this._draftEndTime = this.endTime ? new Date(this.endTime) : null; - this._syncTimelineWidth(); - this._updateHandleStacking(); - this._renderRangeScale(); - this._updateRangePreview(); - this._updateSelectionJumpControls(); - this._revealSelectionInTimeline("auto"); - } - _syncTimelineWidth() { - if (!this.rangeBounds || !this._rangeTimelineEl) return; - const { config } = this.rangeBounds; - const viewportWidth = Math.max( - this._rangeScrollViewportEl?.clientWidth || 0, - 320 - ); - const unitCount = this._countUnitsInRange( - this.rangeBounds.min, - this.rangeBounds.max, - config.majorUnit - ); - const contentWidth = Math.max( - viewportWidth, - unitCount * (config.pixelsPerUnit || 60) - ); - this._rangeContentWidth = contentWidth; - this._rangeTimelineEl.style.width = `${contentWidth}px`; - } - _renderScaleMarkers(fragment, unit, className, total, step = 1) { - if (!this.rangeBounds) return; - let markerTime = addUnit( - startOfUnit(new Date(this.rangeBounds.min), unit), - unit, - 0 - ); - if (markerTime.getTime() < this.rangeBounds.min) { - markerTime = addUnit(markerTime, unit, step); - } - while (markerTime.getTime() < this.rangeBounds.max) { - const tick = document.createElement("span"); - tick.className = `range-tick ${className}`; - tick.style.left = `${(markerTime.getTime() - this.rangeBounds.min) / total * 100}%`; - fragment.appendChild(tick); - markerTime = addUnit(markerTime, unit, step); - } - } - _buildRangePeriodButton(className, leftValue, total, text, unit, startTime) { - if (!this.rangeBounds) return document.createElement("button"); - const button = document.createElement("button"); - button.type = "button"; - button.className = `range-period-button ${className}`; - button.style.left = `${(leftValue - this.rangeBounds.min) / total * 100}%`; - button.textContent = text; - const selectionLabel = formatPeriodSelectionLabel( - startTime, - unit, - this.locale || void 0 - ); - const selectTitle = `${msg("Select")} ${selectionLabel}`; - button.title = selectTitle; - button.setAttribute("aria-label", selectTitle); - button.addEventListener( - "click", - (ev) => this._handleRangePeriodSelect(unit, startTime, ev) - ); - button.addEventListener( - "pointerenter", - () => this._setHoveredPeriodRange(unit, startTime) - ); - button.addEventListener( - "pointerleave", - () => this._clearHoveredPeriodRange(unit, startTime) - ); - button.addEventListener( - "focus", - () => this._setHoveredPeriodRange(unit, startTime) - ); - button.addEventListener( - "blur", - () => this._clearHoveredPeriodRange(unit, startTime) - ); - return button; - } - _getRangeUnitAnchorMs(startTime, unit, anchor = "auto") { - const unitStart = Math.max( - startOfUnit(new Date(startTime), unit).getTime(), - this.rangeBounds?.min ?? -Infinity - ); - const unitEnd = Math.min( - endOfUnit(new Date(startTime), unit).getTime(), - this.rangeBounds?.max ?? Infinity - ); - let resolvedAnchor = anchor; - if (resolvedAnchor === "auto") { - resolvedAnchor = unit === "day" || unit === "week" ? "center" : "start"; - } - if (resolvedAnchor === "center") { - return unitStart + Math.max(0, (unitEnd - unitStart) / 2); - } - return unitStart; - } - _estimateRangeLabelWidth(text, className, minGap) { - const basePadding = className === "range-context-label" ? 20 : 14; - const charWidth = className === "range-context-label" ? 8.2 : 7.2; - return String(text).length * charWidth + basePadding + minGap; - } - _computeRangeLabelStride(unit, formatter, className, minGap) { - if (!this.rangeBounds || !this._rangeContentWidth) return 1; - const total = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); - let current = startOfUnit(new Date(this.rangeBounds.min), unit); - let previousMs = null; - let minSpacingPx = Infinity; - let maxLabelWidthPx = 0; - let samples = 0; - while (current.getTime() < this.rangeBounds.max && samples < 24) { - const currentMs = Math.max(current.getTime(), this.rangeBounds.min); - const text = formatter(current); - maxLabelWidthPx = Math.max( - maxLabelWidthPx, - this._estimateRangeLabelWidth(text, className, minGap) - ); - if (previousMs != null) { - const spacingPx = (currentMs - previousMs) / total * this._rangeContentWidth; - if (spacingPx > 0) minSpacingPx = Math.min(minSpacingPx, spacingPx); - } - previousMs = currentMs; - current = addUnit(current, unit, 1); - samples += 1; - } - if (!Number.isFinite(minSpacingPx) || minSpacingPx <= 0) return 1; - return Math.max(1, Math.ceil(maxLabelWidthPx / minSpacingPx)); - } - _syncVisibleRangeLabels() { - } - _renderRangeScale() { - if (!this.rangeBounds || !this._rangeTickLayerEl || !this._rangeLabelLayerEl || !this._rangeContextLayerEl) - return; - this._rangeTickLayerEl.innerHTML = ""; - this._rangeLabelLayerEl.innerHTML = ""; - this._rangeContextLayerEl.innerHTML = ""; - const total = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); - const { config } = this.rangeBounds; - const tickFragment = document.createDocumentFragment(); - const labelFragment = document.createDocumentFragment(); - const contextFragment = document.createDocumentFragment(); - const scaleLabelZoomLevel = this._getScaleLabelZoomLevel(); - const scaleLabelStride = config.labelUnit === "month" || config.labelUnit === "day" ? 1 : this._computeRangeLabelStride( - config.labelUnit, - (value) => formatScaleLabel( - value, - config.labelUnit, - scaleLabelZoomLevel, - this.locale || void 0 - ), - "range-scale-label", - RANGE_LABEL_MIN_GAP_PX - ); - const contextLabelStride = config.contextUnit === "month" || config.contextUnit === "day" ? 1 : this._computeRangeLabelStride( - config.contextUnit, - (value) => formatContextLabel( - value, - config.contextUnit, - this.locale || void 0 - ), - "range-context-label", - RANGE_CONTEXT_LABEL_MIN_GAP_PX - ); - if (config.detailUnit && config.detailUnit !== config.minorUnit && config.detailUnit !== config.majorUnit) { - this._renderScaleMarkers( - tickFragment, - config.detailUnit, - "fine", - total, - config.detailStep || 1 - ); - } - if (config.minorUnit !== config.majorUnit) { - this._renderScaleMarkers(tickFragment, config.minorUnit, "", total); - } - this._renderScaleMarkers(tickFragment, config.majorUnit, "major", total); - let labelRef = startOfUnit( - new Date(this.rangeBounds.min), - config.labelUnit - ); - let labelIndex = 0; - while (labelRef.getTime() < this.rangeBounds.max) { - if (labelIndex % scaleLabelStride === 0) { - const leftValue = this._getRangeUnitAnchorMs( - labelRef, - config.labelUnit, - "auto" - ); - const label = this._buildRangePeriodButton( - "range-scale-label", - leftValue, - total, - formatScaleLabel( - labelRef, - config.labelUnit, - scaleLabelZoomLevel, - this.locale || void 0 - ), - config.labelUnit, - labelRef - ); - labelFragment.appendChild(label); - } - labelRef = addUnit(labelRef, config.labelUnit, 1); - labelIndex += 1; - } - let contextRef = startOfUnit( - new Date(this.rangeBounds.min), - config.contextUnit - ); - if (contextRef.getTime() < this.rangeBounds.min) { - contextRef = addUnit(contextRef, config.contextUnit, 1); - } - let contextIndex = 0; - while (contextRef.getTime() < this.rangeBounds.max) { - const left = `${(contextRef.getTime() - this.rangeBounds.min) / total * 100}%`; - const divider = document.createElement("span"); - divider.className = "range-divider"; - divider.style.left = left; - contextFragment.appendChild(divider); - if (contextIndex % contextLabelStride === 0) { - const label = this._buildRangePeriodButton( - "range-context-label", - contextRef.getTime(), - total, - formatContextLabel( - contextRef, - config.contextUnit, - this.locale || void 0 - ), - config.contextUnit, - contextRef - ); - contextFragment.appendChild(label); - } - contextRef = addUnit(contextRef, config.contextUnit, 1); - contextIndex += 1; - } - this._rangeTickLayerEl.appendChild(tickFragment); - this._rangeLabelLayerEl.appendChild(labelFragment); - this._rangeContextLayerEl.appendChild(contextFragment); - this._syncVisibleRangeLabels(); - } - // --------------------------------------------------------------------------- - // Handle position / tooltip - // --------------------------------------------------------------------------- - _updateHandleStacking(activeHandle = this._activeRangeHandle) { - if (!this._rangeStartHandleEl || !this._rangeEndHandleEl) return; - this._rangeStartHandleEl.style.zIndex = activeHandle === "start" ? "5" : "3"; - this._rangeEndHandleEl.style.zIndex = activeHandle === "end" ? "5" : "4"; - } - _updateRangePreview() { - if (!this.rangeBounds || !this._draftStartTime || !this._draftEndTime) - return; - const total = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); - const startPct = (this._draftStartTime.getTime() - this.rangeBounds.min) / total * 100; - const endPct = (this._draftEndTime.getTime() - this.rangeBounds.min) / total * 100; - if (this._rangeSelectionEl) { - this._rangeSelectionEl.style.left = `${startPct}%`; - this._rangeSelectionEl.style.width = `${Math.max(0, endPct - startPct)}%`; - } - if (this._rangeStartHandleEl) { - this._rangeStartHandleEl.style.left = `${startPct}%`; - this._rangeStartHandleEl.setAttribute( - "aria-valuetext", - formatRangeDateTime(this._draftStartTime, this.locale || void 0) - ); - } - if (this._rangeEndHandleEl) { - this._rangeEndHandleEl.style.left = `${endPct}%`; - this._rangeEndHandleEl.setAttribute( - "aria-valuetext", - formatRangeDateTime(this._draftEndTime, this.locale || void 0) - ); - } - this._updateRangeTooltip(); - } - _getVisibleRangeTooltipHandles() { - if (this._timelinePointerMode === "selection" || this._timelinePointerMode === "interval_select") { - return ["start", "end"]; - } - const handle = this._activeRangeHandle || this._focusedRangeHandle || this._hoveredRangeHandle || null; - return handle ? [handle] : []; - } - _setRangeTooltipHoverHandle(handle) { - this._hoveredRangeHandle = handle; - this._updateRangeTooltip(); - } - _clearRangeTooltipHoverHandle(handle) { - if (this._activeRangeHandle === handle) return; - if (this._hoveredRangeHandle === handle) this._hoveredRangeHandle = null; - this._updateRangeTooltip(); - } - _setRangeTooltipFocusHandle(handle) { - this._focusedRangeHandle = handle; - this._updateRangeTooltip(); - } - _clearRangeTooltipFocusHandle(handle) { - if (this._activeRangeHandle === handle) return; - if (this._focusedRangeHandle === handle) this._focusedRangeHandle = null; - this._updateRangeTooltip(); - } - _updateRangeTooltip() { - if (!this.rangeBounds || !this._rangeScrollViewportEl) return; - const visibleHandles = new Set(this._getVisibleRangeTooltipHandles()); - this._updateRangeTooltipForHandle("start", visibleHandles.has("start")); - this._updateRangeTooltipForHandle("end", visibleHandles.has("end")); - } - _updateRangeTooltipForHandle(handle, visible) { - const tooltip = handle === "start" ? this._rangeStartTooltipEl : this._rangeEndTooltipEl; - if (!tooltip) return; - if (!visible) { - tooltip.classList.remove("visible"); - tooltip.setAttribute("aria-hidden", "true"); - return; - } - const value = handle === "start" ? this._draftStartTime : this._draftEndTime; - if (!value || !this.rangeBounds || !this._rangeScrollViewportEl) { - tooltip.classList.remove("visible"); - tooltip.setAttribute("aria-hidden", "true"); - return; - } - const total = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); - const contentWidth = Math.max( - this._rangeContentWidth || 0, - this._rangeScrollViewportEl.clientWidth || 0, - 1 - ); - const valuePx = (value.getTime() - this.rangeBounds.min) / total * contentWidth; - const viewportX = valuePx - this._rangeScrollViewportEl.scrollLeft; - const clampedX = clampNumber( - viewportX, - 0, - this._rangeScrollViewportEl.clientWidth - ); - if (handle === "end" && this.isLiveEdge) { - const dateEl = document.createElement("span"); - dateEl.textContent = formatRangeDateTime(value, this.locale || void 0); - const hintEl = document.createElement("span"); - hintEl.className = "range-tooltip-live-hint"; - hintEl.textContent = msg("Updates with new data"); - tooltip.textContent = ""; - tooltip.append(dateEl, hintEl); - } else { - tooltip.textContent = formatRangeDateTime( - value, - this.locale || void 0 - ); - } - tooltip.style.left = `${clampedX}px`; - tooltip.classList.add("visible"); - tooltip.setAttribute("aria-hidden", "false"); - } - // --------------------------------------------------------------------------- - // Period hover - // --------------------------------------------------------------------------- - _handleRangePeriodSelect(unit, startTime, ev) { - ev.preventDefault(); - ev.stopPropagation(); - const periodStart = startOfUnit(new Date(startTime), unit); - const periodEnd = endOfUnit(new Date(startTime), unit); - if (this._rangeCommitTimer) { - window.clearTimeout(this._rangeCommitTimer); - this._rangeCommitTimer = null; - } - this._draftStartTime = new Date(periodStart); - this._draftEndTime = new Date(periodEnd); - this._updateRangePreview(); - this.dispatchEvent( - new CustomEvent("dp-range-period-select", { - detail: { unit, startTime: periodStart }, - bubbles: true, - composed: true - }) - ); - this._commitRangeSelection({ push: true }); - } - _setHoveredPeriodRange(unit, startTime) { - const start = startOfUnit(new Date(startTime), unit); - const end = endOfUnit(new Date(startTime), unit); - this._hoveredPeriodRange = { - unit, - start: start.getTime(), - end: end.getTime() - }; - this.dispatchEvent( - new CustomEvent("dp-range-period-hover", { - detail: { start, end }, - bubbles: true, - composed: true - }) - ); - } - _clearHoveredPeriodRange(unit, startTime) { - if (!this._hoveredPeriodRange) return; - const start = startOfUnit(new Date(startTime), unit).getTime(); - const end = endOfUnit(new Date(startTime), unit).getTime(); - if (this._hoveredPeriodRange.start === start && this._hoveredPeriodRange.end === end) { - this._hoveredPeriodRange = null; - this.dispatchEvent( - new CustomEvent("dp-range-period-leave", { - bubbles: true, - composed: true - }) - ); - } - } - // --------------------------------------------------------------------------- - // Jump controls / scroll - // --------------------------------------------------------------------------- - _updateSelectionJumpControls() { - if (!this._rangeScrollViewportEl || !this.rangeBounds || !this._rangeContentWidth || !this.startTime || !this.endTime) { - if (this._rangeJumpLeftEl) this._rangeJumpLeftEl.hidden = true; - if (this._rangeJumpRightEl) this._rangeJumpRightEl.hidden = true; - return; - } - const total = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); - const viewportWidth = this._rangeScrollViewportEl.clientWidth; - const currentLeft = this._rangeScrollViewportEl.scrollLeft; - const currentRight = currentLeft + viewportWidth; - const startPx = (this.startTime.getTime() - this.rangeBounds.min) / total * this._rangeContentWidth; - const endPx = (this.endTime.getTime() - this.rangeBounds.min) / total * this._rangeContentWidth; - if (this._rangeJumpLeftEl) - this._rangeJumpLeftEl.hidden = !(endPx < currentLeft); - if (this._rangeJumpRightEl) - this._rangeJumpRightEl.hidden = !(startPx > currentRight); - } - _scrollTimelineToRange(range, behavior = "auto", { center = false } = {}) { - if (!this._rangeScrollViewportEl || !this.rangeBounds || !this._rangeContentWidth || !range) - return; - const viewportWidth = this._rangeScrollViewportEl.clientWidth; - if (!viewportWidth || this._rangeContentWidth <= viewportWidth) return; - const totalMs = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); - const visibleSpanMs = totalMs * Math.min(1, viewportWidth / this._rangeContentWidth); - const maxScrollLeft = Math.max(0, this._rangeContentWidth - viewportWidth); - const viewportRangeMs = Math.max(0, totalMs - visibleSpanMs); - if (viewportRangeMs <= 0) return; - const targetStart = center ? clampNumber( - (range.start + range.end) / 2 - visibleSpanMs / 2, - this.rangeBounds.min, - this.rangeBounds.max - visibleSpanMs - ) : clampNumber( - range.start, - this.rangeBounds.min, - this.rangeBounds.max - visibleSpanMs - ); - const ratio = (targetStart - this.rangeBounds.min) / viewportRangeMs; - const nextLeft = clampNumber(ratio * maxScrollLeft, 0, maxScrollLeft); - this._rangeScrollViewportEl.scrollTo({ left: nextLeft, behavior }); - } - revealSelection() { - this._revealSelectionInTimeline("smooth"); - } - _revealSelectionInTimeline(behavior = "auto") { - if (!this.startTime || !this.endTime) return; - this._isProgrammaticScroll = true; - this._scrollTimelineToRange( - { start: this.startTime.getTime(), end: this.endTime.getTime() }, - behavior, - { center: true } - ); - window.setTimeout(() => { - this._isProgrammaticScroll = false; - }, 50); - } - _showScrollbar() { - if (!this._rangeScrollViewportEl) return; - this._rangeScrollViewportEl.classList.add("scrollbar-visible"); - if (this._scrollbarHideTimer) window.clearTimeout(this._scrollbarHideTimer); - this._scrollbarHideTimer = window.setTimeout(() => { - this._scrollbarHideTimer = null; - this._rangeScrollViewportEl?.classList.remove("scrollbar-visible"); - }, 1500); - } - // --------------------------------------------------------------------------- - // Coordinate math - // --------------------------------------------------------------------------- - _timestampFromClientX(clientX) { - if (!this.rangeBounds || !this._rangeTrackEl) return null; - const rect = this._rangeTrackEl.getBoundingClientRect(); - if (!rect.width) return null; - const ratio = clampNumber((clientX - rect.left) / rect.width, 0, 1); - return this.rangeBounds.min + ratio * (this.rangeBounds.max - this.rangeBounds.min); - } - _getTimelineSelectionDragDeltaMs(timestamp) { - if (timestamp == null || this._timelinePointerStartTimestamp == null) - return 0; - const snapUnit = this._getEffectiveSnapUnit(); - if (!snapUnit) return timestamp - this._timelinePointerStartTimestamp; - const snappedStart = snapDateToUnit( - new Date(this._timelinePointerStartTimestamp), - snapUnit - ).getTime(); - const snappedCurrent = snapDateToUnit( - new Date(timestamp), - snapUnit - ).getTime(); - return snappedCurrent - snappedStart; - } - // --------------------------------------------------------------------------- - // Draft range manipulation - // --------------------------------------------------------------------------- - _setDraftRangeFromTimestamp(handle, timestamp) { - if (!this.rangeBounds) return; - const snapUnit = this._getEffectiveSnapUnit(); - let startMs = this._draftStartTime?.getTime() ?? this.startTime?.getTime() ?? this.rangeBounds.min; - let endMs = this._draftEndTime?.getTime() ?? this.endTime?.getTime() ?? this.rangeBounds.max; - const snapped = clampNumber( - snapDateToUnit(new Date(timestamp), snapUnit).getTime(), - this.rangeBounds.min, - this.rangeBounds.max - ); - const minSpan = Math.max(this._getSnapSpanMs(new Date(snapped)), SECOND_MS); - if (handle === "start") { - startMs = clampNumber(snapped, this.rangeBounds.min, endMs - minSpan); - } else { - endMs = clampNumber(snapped, startMs + minSpan, this.rangeBounds.max); - } - this._draftStartTime = new Date(startMs); - this._draftEndTime = new Date(endMs); - this._updateHandleStacking(handle); - this._updateRangePreview(); - this._fireDraftEvent(); - this._scheduleRangeCommit(); - } - _shiftDraftRangeByDelta(deltaMs) { - if (!this.rangeBounds) return; - const startMs = this._timelineDragStartRangeMs; - const endMs = this._timelineDragEndRangeMs; - const minDelta = this.rangeBounds.min - startMs; - const maxDelta = this.rangeBounds.max - endMs; - const clampedDelta = clampNumber(deltaMs, minDelta, maxDelta); - this._draftStartTime = new Date(startMs + clampedDelta); - this._draftEndTime = new Date(endMs + clampedDelta); - this._updateRangePreview(); - this._fireDraftEvent(); - this._scheduleRangeCommit(); - } - _setDraftRangeFromIntervalSelection(startTimestamp, endTimestamp) { - if (!this.rangeBounds) return; - const unit = this.rangeBounds.config?.labelUnit || this._getEffectiveSnapUnit(); - const startValue = Math.min(startTimestamp, endTimestamp); - const endValue = Math.max(startTimestamp, endTimestamp); - const rangeStart = clampNumber( - startOfUnit(new Date(startValue), unit).getTime(), - this.rangeBounds.min, - this.rangeBounds.max - ); - const rangeEnd = clampNumber( - endOfUnit(new Date(endValue), unit).getTime(), - this.rangeBounds.min, - this.rangeBounds.max - ); - if (rangeStart >= rangeEnd) return; - this._draftStartTime = new Date(rangeStart); - this._draftEndTime = new Date(rangeEnd); - this._updateRangePreview(); - } - _fireDraftEvent() { - if (!this._draftStartTime || !this._draftEndTime) return; - this.dispatchEvent( - new CustomEvent("dp-range-draft", { - detail: { - start: new Date(this._draftStartTime), - end: new Date(this._draftEndTime) - }, - bubbles: true, - composed: true - }) - ); - } - _scheduleRangeCommit() { - if (this._rangeInteractionActive || this._timelinePointerMode === "selection" || this._timelinePointerMode === "interval_select") - return; - if (this._rangeCommitTimer) window.clearTimeout(this._rangeCommitTimer); - this._rangeCommitTimer = window.setTimeout(() => { - this._rangeCommitTimer = null; - this._commitRangeSelection({ push: false }); - }, 240); - } - _commitRangeSelection({ push = false } = {}) { - if (!this._draftStartTime || !this._draftEndTime) return; - this.dispatchEvent( - new CustomEvent("dp-range-commit", { - detail: { - start: new Date(this._draftStartTime), - end: new Date(this._draftEndTime), - push - }, - bubbles: true, - composed: true - }) - ); - } - // --------------------------------------------------------------------------- - // Handle drag interaction - // --------------------------------------------------------------------------- - _beginRangePointerInteraction(handle, pointerId, clientX) { - if (!this._rangeTrackEl) return; - this._rangeInteractionActive = true; - if (this._rangeCommitTimer) { - window.clearTimeout(this._rangeCommitTimer); - this._rangeCommitTimer = null; - } - this._activeRangeHandle = handle; - this._hoveredRangeHandle = handle; - this._rangePointerId = pointerId; - this._updateHandleStacking(handle); - this._updateRangeTooltip(); - this._attachRangePointerListeners(); - const target = handle === "start" ? this._rangeStartHandleEl : this._rangeEndHandleEl; - target?.focus?.(); - const timestamp = this._timestampFromClientX(clientX); - if (timestamp != null) { - this._setDraftRangeFromTimestamp(handle, timestamp); - } - } - _maybeAutoScrollTimelineDuringHandleDrag(clientX) { - if (!this._rangeScrollViewportEl) return; - const viewport = this._rangeScrollViewportEl; - const rect = viewport.getBoundingClientRect(); - if (!rect.width) return; - const maxScrollLeft = Math.max( - 0, - viewport.scrollWidth - viewport.clientWidth - ); - if (maxScrollLeft <= 0) return; - let delta = 0; - const leftDistance = clientX - rect.left; - const rightDistance = rect.right - clientX; - if (leftDistance < RANGE_HANDLE_EDGE_SCROLL_THRESHOLD_PX) { - const ratio = clampNumber( - (RANGE_HANDLE_EDGE_SCROLL_THRESHOLD_PX - leftDistance) / RANGE_HANDLE_EDGE_SCROLL_THRESHOLD_PX, - 0, - 1 - ); - delta = -Math.max( - 1, - Math.round(ratio * RANGE_HANDLE_EDGE_SCROLL_MAX_STEP_PX) - ); - } else if (rightDistance < RANGE_HANDLE_EDGE_SCROLL_THRESHOLD_PX) { - const ratio = clampNumber( - (RANGE_HANDLE_EDGE_SCROLL_THRESHOLD_PX - rightDistance) / RANGE_HANDLE_EDGE_SCROLL_THRESHOLD_PX, - 0, - 1 - ); - delta = Math.max( - 1, - Math.round(ratio * RANGE_HANDLE_EDGE_SCROLL_MAX_STEP_PX) - ); - } - if (!delta) return; - viewport.scrollLeft = clampNumber( - viewport.scrollLeft + delta, - 0, - maxScrollLeft - ); - } - _attachRangePointerListeners() { - window.addEventListener("pointermove", this._onRangePointerMove); - window.addEventListener("pointerup", this._onRangePointerUp); - window.addEventListener("pointercancel", this._onRangePointerUp); - } - _detachRangePointerListeners() { - window.removeEventListener("pointermove", this._onRangePointerMove); - window.removeEventListener("pointerup", this._onRangePointerUp); - window.removeEventListener("pointercancel", this._onRangePointerUp); - this._rangePointerId = null; - this._activeRangeHandle = null; - this._rangeInteractionActive = false; - this._updateHandleStacking(); - this._updateRangeTooltip(); - } - _handleRangePointerMove(ev) { - if (!this._activeRangeHandle) return; - if (this._rangePointerId != null && ev.pointerId !== this._rangePointerId) - return; - this._maybeAutoScrollTimelineDuringHandleDrag(ev.clientX); - const timestamp = this._timestampFromClientX(ev.clientX); - if (timestamp == null) return; - ev.preventDefault(); - this._setDraftRangeFromTimestamp(this._activeRangeHandle, timestamp); - } - _finishRangePointerInteraction(ev) { - if (!this._activeRangeHandle) return; - if (this._rangePointerId != null && ev.pointerId !== this._rangePointerId) - return; - this._detachRangePointerListeners(); - this._focusedRangeHandle = null; - this._hoveredRangeHandle = null; - this._updateRangeTooltip(); - this._commitRangeSelection({ push: true }); - } - _handleRangeHandleKeyDown(handle, detail) { - if (!this.rangeBounds) return; - const snapUnit = this._getEffectiveSnapUnit(); - const currentValue = handle === "start" ? this._draftStartTime?.getTime() ?? this.startTime?.getTime() : this._draftEndTime?.getTime() ?? this.endTime?.getTime(); - if (currentValue == null) return; - const config = this._getZoomConfig(); - let nextValue = null; - if (detail.key === "ArrowLeft" || detail.key === "ArrowDown") - nextValue = addUnit(new Date(currentValue), snapUnit, -1).getTime(); - if (detail.key === "ArrowRight" || detail.key === "ArrowUp") - nextValue = addUnit(new Date(currentValue), snapUnit, 1).getTime(); - if (detail.key === "PageDown") - nextValue = addUnit( - new Date(currentValue), - config.majorUnit, - -1 - ).getTime(); - if (detail.key === "PageUp") - nextValue = addUnit( - new Date(currentValue), - config.majorUnit, - 1 - ).getTime(); - if (detail.key === "Home") nextValue = this.rangeBounds.min; - if (detail.key === "End") nextValue = this.rangeBounds.max; - if (nextValue == null) return; - this._focusedRangeHandle = handle; - this._setDraftRangeFromTimestamp(handle, nextValue); - } - // --------------------------------------------------------------------------- - // Timeline pan / interval-select interactions - // --------------------------------------------------------------------------- - _handleTimelinePointerDown(ev) { - if (ev.button !== 0) return; - if (ev.composedPath().some( - (node) => node instanceof Element && (node.tagName === "RANGE-HANDLE" || node.closest?.("range-handle")) - )) { - return; - } - if (ev.target?.closest?.(".range-period-button")) return; - if (!this._rangeScrollViewportEl) return; - const isSelectionDrag = !!ev.target?.closest?.( - ".range-selection" - ); - const trackRect = this._rangeTrackEl?.getBoundingClientRect(); - const isTrackRegion = !!trackRect && ev.clientY >= trackRect.top - 6 && ev.clientY <= trackRect.bottom + 6; - const isIntervalSelect = !isSelectionDrag && !isTrackRegion; - this._detachTimelinePointerListeners(); - this._rangeInteractionActive = isSelectionDrag || isIntervalSelect; - if ((isSelectionDrag || isIntervalSelect) && this._rangeCommitTimer) { - window.clearTimeout(this._rangeCommitTimer); - this._rangeCommitTimer = null; - } - this._timelinePointerId = ev.pointerId; - this._timelinePointerStartX = ev.clientX; - this._timelinePointerStartScrollLeft = this._rangeScrollViewportEl.scrollLeft; - this._timelinePointerStartTimestamp = isSelectionDrag || isIntervalSelect ? this._timestampFromClientX(ev.clientX) : null; - if (isSelectionDrag) { - this._timelinePointerMode = "selection"; - } else if (isIntervalSelect) { - this._timelinePointerMode = "interval_select"; - } else { - this._timelinePointerMode = "pan"; - } - this._timelineDragStartRangeMs = this._draftStartTime?.getTime() ?? this.startTime?.getTime() ?? 0; - this._timelineDragEndRangeMs = this._draftEndTime?.getTime() ?? this.endTime?.getTime() ?? 0; - this._timelinePointerMoved = false; - this._timelineTrackClickPending = !isSelectionDrag && !isIntervalSelect && !!ev.target?.closest?.(".range-track"); - this._rangeScrollViewportEl.classList.remove("dragging"); - this._rangeSelectionEl?.classList.toggle("dragging", isSelectionDrag); - window.addEventListener("pointermove", this._onTimelinePointerMove); - window.addEventListener("pointerup", this._onTimelinePointerUp); - window.addEventListener("pointercancel", this._onTimelinePointerUp); - } - _detachTimelinePointerListeners() { - window.removeEventListener("pointermove", this._onTimelinePointerMove); - window.removeEventListener("pointerup", this._onTimelinePointerUp); - window.removeEventListener("pointercancel", this._onTimelinePointerUp); - if (this._rangeScrollViewportEl) - this._rangeScrollViewportEl.classList.remove("dragging"); - this._rangeSelectionEl?.classList.remove("dragging"); - this._timelinePointerId = null; - this._timelinePointerStartTimestamp = null; - this._timelinePointerMode = null; - this._rangeInteractionActive = false; - this._timelinePointerMoved = false; - this._timelineTrackClickPending = false; - } - _handleTimelinePointerMove(ev) { - if (this._timelinePointerId == null || ev.pointerId !== this._timelinePointerId || !this._rangeScrollViewportEl) - return; - if (this._timelinePointerMode === "selection") { - const timestamp = this._timestampFromClientX(ev.clientX); - if (timestamp == null || this._timelinePointerStartTimestamp == null) - return; - const deltaX2 = ev.clientX - this._timelinePointerStartX; - if (!this._timelinePointerMoved && Math.abs(deltaX2) < 4) return; - this._timelinePointerMoved = true; - this._shiftDraftRangeByDelta( - this._getTimelineSelectionDragDeltaMs(timestamp) - ); - ev.preventDefault(); - return; - } - if (this._timelinePointerMode === "interval_select") { - const timestamp = this._timestampFromClientX(ev.clientX); - if (timestamp == null || this._timelinePointerStartTimestamp == null) - return; - const deltaX2 = ev.clientX - this._timelinePointerStartX; - if (!this._timelinePointerMoved && Math.abs(deltaX2) < 4) return; - this._timelinePointerMoved = true; - this._setDraftRangeFromIntervalSelection( - this._timelinePointerStartTimestamp, - timestamp - ); - ev.preventDefault(); - return; - } - const deltaX = ev.clientX - this._timelinePointerStartX; - if (!this._timelinePointerMoved && Math.abs(deltaX) < 4) return; - this._timelinePointerMoved = true; - this._timelineTrackClickPending = false; - this._rangeScrollViewportEl.classList.add("dragging"); - const maxScrollLeft = Math.max( - 0, - this._rangeScrollViewportEl.scrollWidth - this._rangeScrollViewportEl.clientWidth - ); - this._rangeScrollViewportEl.scrollLeft = clampNumber( - this._timelinePointerStartScrollLeft - deltaX, - 0, - maxScrollLeft - ); - ev.preventDefault(); - } - _finishTimelinePointerInteraction(ev) { - if (this._timelinePointerId == null || ev.pointerId !== this._timelinePointerId) - return; - const mode = this._timelinePointerMode; - const didMove = this._timelinePointerMoved; - const shouldSelectTrack = this._timelineTrackClickPending && !didMove; - const clientX = ev.clientX; - this._detachTimelinePointerListeners(); - if (mode === "selection") { - this._focusedRangeHandle = null; - this._hoveredRangeHandle = null; - this._updateRangeTooltip(); - if (didMove) { - this._commitRangeSelection({ push: true }); - } - return; - } - if (mode === "interval_select") { - this._hoveredPeriodRange = null; - this._updateRangeTooltip(); - if (didMove) { - this._commitRangeSelection({ push: true }); - } - return; - } - if (shouldSelectTrack) { - this._handleTrackSelectionAtClientX(clientX); - } - } - _handleTrackSelectionAtClientX(clientX) { - const timestamp = this._timestampFromClientX(clientX); - if (timestamp == null) return; - const startMs = this._draftStartTime?.getTime() ?? this.startTime?.getTime() ?? this.rangeBounds?.min; - const endMs = this._draftEndTime?.getTime() ?? this.endTime?.getTime() ?? this.rangeBounds?.max; - if (startMs == null || endMs == null) return; - const handle = Math.abs(timestamp - startMs) <= Math.abs(timestamp - endMs) ? "start" : "end"; - this._setDraftRangeFromTimestamp(handle, timestamp); - } - _handleRangeViewportPointerMove(ev) { - if (this._timelinePointerId != null || this._rangePointerId != null) return; - if (ev.composedPath().some((el) => el.tagName === "DP-RANGE-HANDLE")) - return; - if (ev.target?.closest?.(".range-period-button")) return; - if (ev.target?.closest?.(".range-selection")) return; - const timestamp = this._timestampFromClientX(ev.clientX); - if (timestamp == null || !this.rangeBounds) return; - const unit = this.rangeBounds.config?.labelUnit || this._getEffectiveSnapUnit(); - if (!unit) return; - this._setHoveredPeriodRange(unit, new Date(timestamp)); - } - _handleRangeViewportPointerLeave() { - if (this._timelinePointerId != null || this._rangePointerId != null) return; - if (this._hoveredPeriodRange) { - this._hoveredPeriodRange = null; - this.dispatchEvent( - new CustomEvent("dp-range-period-leave", { - bubbles: true, - composed: true - }) - ); - } - } - } - _init$k = __decoratorStart$k(_a$k); - _startTime$2 = /* @__PURE__ */ new WeakMap(); - _endTime$2 = /* @__PURE__ */ new WeakMap(); - _rangeBounds$3 = /* @__PURE__ */ new WeakMap(); - _zoomLevel$3 = /* @__PURE__ */ new WeakMap(); - _dateSnapping$3 = /* @__PURE__ */ new WeakMap(); - _isLiveEdge$2 = /* @__PURE__ */ new WeakMap(); - _locale$1 = /* @__PURE__ */ new WeakMap(); - __decorateElement$k(_init$k, 4, "startTime", _startTime_dec$2, RangeTimeline, _startTime$2); - __decorateElement$k(_init$k, 4, "endTime", _endTime_dec$2, RangeTimeline, _endTime$2); - __decorateElement$k(_init$k, 4, "rangeBounds", _rangeBounds_dec$3, RangeTimeline, _rangeBounds$3); - __decorateElement$k(_init$k, 4, "zoomLevel", _zoomLevel_dec$3, RangeTimeline, _zoomLevel$3); - __decorateElement$k(_init$k, 4, "dateSnapping", _dateSnapping_dec$3, RangeTimeline, _dateSnapping$3); - __decorateElement$k(_init$k, 4, "isLiveEdge", _isLiveEdge_dec$2, RangeTimeline, _isLiveEdge$2); - __decorateElement$k(_init$k, 4, "locale", _locale_dec$1, RangeTimeline, _locale$1); - RangeTimeline = __decorateElement$k(_init$k, 0, "RangeTimeline", _RangeTimeline_decorators, RangeTimeline); - __publicField$r(RangeTimeline, "styles", styles$q); - __runInitializers$k(_init$k, 1, RangeTimeline); - customElements.define("range-timeline", RangeTimeline); - var __create$j = Object.create; - var __defProp$q = Object.defineProperty; - var __getOwnPropDesc$j = Object.getOwnPropertyDescriptor; - var __knownSymbol$j = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$j = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$q = (obj, key, value) => key in obj ? __defProp$q(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __name$4 = (target, value) => __defProp$q(target, "name", { value, configurable: true }); - var __decoratorStart$j = (base) => [, , , __create$j(base?.[__knownSymbol$j("metadata")] ?? null)]; - var __decoratorStrings$j = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$j = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$j("Function expected") : fn; - var __decoratorContext$j = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$j[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$j("Already initialized") : fns.push(__expectFn$j(fn || null)) }); - var __decoratorMetadata$j = (array, target) => __defNormalProp$q(target, __knownSymbol$j("metadata"), array[3]); - var __runInitializers$j = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$j = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = !!(flags & 8), p2 = !!(flags & 16); - var j2 = k2 > 3 ? array.length + 1 : k2 ? s2 ? 1 : 2 : 0, key = __decoratorStrings$j[k2 + 5]; - var initializers = k2 > 3 && (array[j2 - 1] = []), extraInitializers = array[j2] || (array[j2] = []); - var desc = k2 && (!p2 && !s2 && (target = target.prototype), k2 < 5 && (k2 > 3 || !p2) && __getOwnPropDesc$j(k2 < 4 ? target : { get [name]() { - return __privateGet$i(this, extra); - }, set [name](x2) { - return __privateSet$i(this, extra, x2); - } }, name)); - k2 ? p2 && k2 < 4 && __name$4(extra, (k2 > 2 ? "set " : k2 > 1 ? "get " : "") + name) : __name$4(target, name); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$j(k2, name, done = {}, array[3], extraInitializers); - if (k2) { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: p2 ? (x2) => __privateIn$3(target, x2) : (x2) => name in x2 }; - if (k2 ^ 3) access.get = p2 ? (x2) => (k2 ^ 1 ? __privateGet$i : __privateMethod$3)(x2, target, k2 ^ 4 ? extra : desc.get) : (x2) => x2[name]; - if (k2 > 2) access.set = p2 ? (x2, y2) => __privateSet$i(x2, target, y2, k2 ^ 4 ? extra : desc.set) : (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])(k2 ? k2 < 4 ? p2 ? extra : desc[key] : k2 > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; - if (k2 ^ 4 || it === void 0) __expectFn$j(it) && (k2 > 4 ? initializers.unshift(it) : k2 ? p2 ? extra = it : desc[key] = it : target = it); - else if (typeof it !== "object" || it === null) __typeError$j("Object expected"); - else __expectFn$j(fn = it.get) && (desc.get = fn), __expectFn$j(fn = it.set) && (desc.set = fn), __expectFn$j(fn = it.init) && initializers.unshift(fn); - } - return k2 || __decoratorMetadata$j(array, target), desc && __defProp$q(target, name, desc), p2 ? k2 ^ 4 ? extra : desc : target; - }; - var __publicField$q = (obj, key, value) => __defNormalProp$q(obj, key + "", value); - var __accessCheck$i = (obj, member, msg2) => member.has(obj) || __typeError$j("Cannot " + msg2); - var __privateIn$3 = (member, obj) => Object(obj) !== obj ? __typeError$j('Cannot use the "in" operator on this value') : member.has(obj); - var __privateGet$i = (obj, member, getter) => (__accessCheck$i(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); - var __privateAdd$i = (obj, member, value) => member.has(obj) ? __typeError$j("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$i = (obj, member, value, setter) => (__accessCheck$i(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); - var __privateMethod$3 = (obj, member, method) => (__accessCheck$i(obj, member, "access private method"), method); - var _dateSnapping_dec$2, _zoomLevel_dec$2, _rangeBounds_dec$2, _submitLabel_dec, _showShortcuts_dec, _showDelete_dec, _endValue_dec, _startValue_dec, _name_dec$1, _heading_dec, _open_dec$1, _a$j, _DateWindowDialog_decorators, _init$j, _open$1, _heading, _name$1, _startValue, _endValue, _showDelete, _showShortcuts, _submitLabel, _rangeBounds$2, _zoomLevel$2, _dateSnapping$2; - _DateWindowDialog_decorators = [localized()]; - class DateWindowDialog extends (_a$j = i$2, _open_dec$1 = [n({ type: Boolean })], _heading_dec = [n({ type: String })], _name_dec$1 = [n({ type: String })], _startValue_dec = [n({ type: String, attribute: "start-value" })], _endValue_dec = [n({ type: String, attribute: "end-value" })], _showDelete_dec = [n({ type: Boolean, attribute: "show-delete" })], _showShortcuts_dec = [n({ type: Boolean, attribute: "show-shortcuts" })], _submitLabel_dec = [n({ type: String, attribute: "submit-label" })], _rangeBounds_dec$2 = [n({ type: Object })], _zoomLevel_dec$2 = [n({ type: String, attribute: "zoom-level" })], _dateSnapping_dec$2 = [n({ type: String, attribute: "date-snapping" })], _a$j) { - constructor() { - super(...arguments); - __privateAdd$i(this, _open$1, __runInitializers$j(_init$j, 8, this, false)), __runInitializers$j(_init$j, 11, this); - __privateAdd$i(this, _heading, __runInitializers$j(_init$j, 12, this, "Add date window")), __runInitializers$j(_init$j, 15, this); - __privateAdd$i(this, _name$1, __runInitializers$j(_init$j, 16, this, "")), __runInitializers$j(_init$j, 19, this); - __privateAdd$i(this, _startValue, __runInitializers$j(_init$j, 20, this, "")), __runInitializers$j(_init$j, 23, this); - __privateAdd$i(this, _endValue, __runInitializers$j(_init$j, 24, this, "")), __runInitializers$j(_init$j, 27, this); - __privateAdd$i(this, _showDelete, __runInitializers$j(_init$j, 28, this, false)), __runInitializers$j(_init$j, 31, this); - __privateAdd$i(this, _showShortcuts, __runInitializers$j(_init$j, 32, this, false)), __runInitializers$j(_init$j, 35, this); - __privateAdd$i(this, _submitLabel, __runInitializers$j(_init$j, 36, this, "Create date window")), __runInitializers$j(_init$j, 39, this); - __privateAdd$i(this, _rangeBounds$2, __runInitializers$j(_init$j, 40, this, null)), __runInitializers$j(_init$j, 43, this); - __privateAdd$i(this, _zoomLevel$2, __runInitializers$j(_init$j, 44, this, "auto")), __runInitializers$j(_init$j, 47, this); - __privateAdd$i(this, _dateSnapping$2, __runInitializers$j(_init$j, 48, this, "hour")), __runInitializers$j(_init$j, 51, this); - } - /** Shake the dialog — call this when the parent detects a validation error. */ - shake() { - const dialog = this.shadowRoot?.querySelector( - "ha-dialog" - ); - if (!dialog) return; - dialog.classList.remove("dp-shaking"); - void dialog.offsetWidth; - dialog.classList.add("dp-shaking"); - dialog.addEventListener( - "animationend", - () => dialog.classList.remove("dp-shaking"), - { once: true } - ); - } - _emit(name, detail = {}) { - this.dispatchEvent( - new CustomEvent(name, { - detail, - bubbles: true, - composed: true - }) - ); - } - _onDialogClosed() { - this._emit("dp-window-close"); - } - _onCancel() { - this._emit("dp-window-close"); - } - _onSubmit() { - const nameInput = this.shadowRoot?.querySelector("#date-window-name"); - const startInput = this.shadowRoot?.querySelector("#date-window-start"); - const endInput = this.shadowRoot?.querySelector("#date-window-end"); - const nameVal = nameInput?.value ?? this.name; - this._emit("dp-window-submit", { - name: String(nameVal ?? "").trim(), - start: startInput?.value ?? this.startValue, - end: endInput?.value ?? this.endValue - }); - } - _onDelete() { - this._emit("dp-window-delete"); - } - _onPreviousShortcut() { - this._emit("dp-window-shortcut", { direction: -1 }); - } - _onNextShortcut() { - this._emit("dp-window-shortcut", { direction: 1 }); - } - _onDateChange() { - const startInput = this.shadowRoot?.querySelector("#date-window-start"); - const endInput = this.shadowRoot?.querySelector("#date-window-end"); - this._emit("dp-window-date-change", { - start: startInput?.value ?? "", - end: endInput?.value ?? "" - }); - } - _onRangeCommit(ev) { - const { start, end } = ev.detail ?? {}; - if (!start || !end) return; - const fmt = (ms) => { - const d2 = new Date(ms); - const pad = (n2) => String(n2).padStart(2, "0"); - return `${d2.getFullYear()}-${pad(d2.getMonth() + 1)}-${pad(d2.getDate())}T${pad(d2.getHours())}:${pad(d2.getMinutes())}`; - }; - const startStr = fmt(start); - const endStr = fmt(end); - const startInput = this.shadowRoot?.querySelector("#date-window-start"); - const endInput = this.shadowRoot?.querySelector("#date-window-end"); - if (startInput) startInput.value = startStr; - if (endInput) endInput.value = endStr; - this._emit("dp-window-date-change", { start: startStr, end: endStr }); - } - /** Parse the startValue / endValue strings to Date objects for the timeline. */ - _parseValueToDate(value) { - if (!value) return null; - const d2 = new Date(value); - return Number.isNaN(d2.getTime()) ? null : d2; - } - render() { - return b` + + .handle.is-live { + background: #ef5350; + animation: dp-live-breathe 3s ease-in-out infinite; + } +`; + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/range-handle/range-handle.ts + var _position_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _label_accessor_storage$6 = /* @__PURE__ */ new WeakMap(); + var _live_accessor_storage = /* @__PURE__ */ new WeakMap(); + /** + * `range-handle` is a circular drag-handle button for a timeline range slider. + * + * The parent positions the handle by setting the `position` prop (reflected to + * `style.left` as a percentage). The handle fires events for all pointer and keyboard + * interactions; the parent owns drag logic and tooltips. + * + * @fires dp-handle-drag-start - `{ pointerId, clientX }` — pointer down on handle + * @fires dp-handle-keydown - `{ key, shiftKey }` — navigation key pressed (arrows, Page, Home, End) + * @fires dp-handle-hover - `{}` — pointer entered the handle + * @fires dp-handle-leave - `{}` — pointer left the handle + * @fires dp-handle-focus - `{}` — handle received focus + * @fires dp-handle-blur - `{}` — handle lost focus + */ + var RangeHandle = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _position_accessor_storage, 0); + _classPrivateFieldInitSpec(this, _label_accessor_storage$6, ""); + _classPrivateFieldInitSpec(this, _live_accessor_storage, false); + } + get position() { + return _classPrivateFieldGet2(_position_accessor_storage, this); + } + set position(value) { + _classPrivateFieldSet2(_position_accessor_storage, this, value); + } + get label() { + return _classPrivateFieldGet2(_label_accessor_storage$6, this); + } + set label(value) { + _classPrivateFieldSet2(_label_accessor_storage$6, this, value); + } + get live() { + return _classPrivateFieldGet2(_live_accessor_storage, this); + } + set live(value) { + _classPrivateFieldSet2(_live_accessor_storage, this, value); + } + updated(changed) { + if (changed.has("position")) this.style.left = `${this.position}%`; + } + _onPointerDown(e) { + e.preventDefault(); + this.dispatchEvent(new CustomEvent("dp-handle-drag-start", { + detail: { + pointerId: e.pointerId, + clientX: e.clientX + }, + bubbles: true, + composed: true + })); + } + _onKeyDown(e) { + if (![ + "ArrowLeft", + "ArrowRight", + "ArrowDown", + "ArrowUp", + "PageDown", + "PageUp", + "Home", + "End" + ].includes(e.key)) return; + e.preventDefault(); + this.dispatchEvent(new CustomEvent("dp-handle-keydown", { + detail: { + key: e.key, + shiftKey: e.shiftKey + }, + bubbles: true, + composed: true + })); + } + _onPointerEnter() { + this.dispatchEvent(new CustomEvent("dp-handle-hover", { + bubbles: true, + composed: true + })); + } + _onPointerLeave() { + this.dispatchEvent(new CustomEvent("dp-handle-leave", { + bubbles: true, + composed: true + })); + } + _onFocus() { + this.dispatchEvent(new CustomEvent("dp-handle-focus", { + bubbles: true, + composed: true + })); + } + _onBlur() { + this.dispatchEvent(new CustomEvent("dp-handle-blur", { + bubbles: true, + composed: true + })); + } + render() { + return b` + + `; + } + }; + _defineProperty(RangeHandle, "styles", styles$25); + __decorate([n$1({ type: Number })], RangeHandle.prototype, "position", null); + __decorate([n$1({ type: String })], RangeHandle.prototype, "label", null); + __decorate([n$1({ type: Boolean })], RangeHandle.prototype, "live", null); + customElements.define("range-handle", RangeHandle); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/range-timeline/range-timeline.ts + var _RangeTimeline, _startTime_accessor_storage$2, _endTime_accessor_storage$2, _rangeBounds_accessor_storage$3, _zoomLevel_accessor_storage$3, _dateSnapping_accessor_storage$3, _isLiveEdge_accessor_storage$2, _locale_accessor_storage$1; + var RangeTimeline = (_startTime_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _endTime_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _rangeBounds_accessor_storage$3 = /* @__PURE__ */ new WeakMap(), _zoomLevel_accessor_storage$3 = /* @__PURE__ */ new WeakMap(), _dateSnapping_accessor_storage$3 = /* @__PURE__ */ new WeakMap(), _isLiveEdge_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _locale_accessor_storage$1 = /* @__PURE__ */ new WeakMap(), _RangeTimeline = class RangeTimeline extends i$2 { + get startTime() { + return _classPrivateFieldGet2(_startTime_accessor_storage$2, this); + } + set startTime(value) { + _classPrivateFieldSet2(_startTime_accessor_storage$2, this, value); + } + get endTime() { + return _classPrivateFieldGet2(_endTime_accessor_storage$2, this); + } + set endTime(value) { + _classPrivateFieldSet2(_endTime_accessor_storage$2, this, value); + } + get rangeBounds() { + return _classPrivateFieldGet2(_rangeBounds_accessor_storage$3, this); + } + set rangeBounds(value) { + _classPrivateFieldSet2(_rangeBounds_accessor_storage$3, this, value); + } + get zoomLevel() { + return _classPrivateFieldGet2(_zoomLevel_accessor_storage$3, this); + } + set zoomLevel(value) { + _classPrivateFieldSet2(_zoomLevel_accessor_storage$3, this, value); + } + get dateSnapping() { + return _classPrivateFieldGet2(_dateSnapping_accessor_storage$3, this); + } + set dateSnapping(value) { + _classPrivateFieldSet2(_dateSnapping_accessor_storage$3, this, value); + } + get isLiveEdge() { + return _classPrivateFieldGet2(_isLiveEdge_accessor_storage$2, this); + } + set isLiveEdge(value) { + _classPrivateFieldSet2(_isLiveEdge_accessor_storage$2, this, value); + } + get locale() { + return _classPrivateFieldGet2(_locale_accessor_storage$1, this); + } + set locale(value) { + _classPrivateFieldSet2(_locale_accessor_storage$1, this, value); + } + constructor() { + super(); + _classPrivateFieldInitSpec(this, _startTime_accessor_storage$2, null); + _classPrivateFieldInitSpec(this, _endTime_accessor_storage$2, null); + _classPrivateFieldInitSpec(this, _rangeBounds_accessor_storage$3, null); + _classPrivateFieldInitSpec(this, _zoomLevel_accessor_storage$3, "day"); + _classPrivateFieldInitSpec(this, _dateSnapping_accessor_storage$3, "auto"); + _classPrivateFieldInitSpec(this, _isLiveEdge_accessor_storage$2, false); + _classPrivateFieldInitSpec(this, _locale_accessor_storage$1, ""); + _defineProperty(this, "_draftStartTime", null); + _defineProperty(this, "_draftEndTime", null); + _defineProperty(this, "_activeRangeHandle", null); + _defineProperty(this, "_hoveredRangeHandle", null); + _defineProperty(this, "_focusedRangeHandle", null); + _defineProperty(this, "_hoveredPeriodRange", null); + _defineProperty(this, "_rangePointerId", null); + _defineProperty(this, "_rangeInteractionActive", false); + _defineProperty(this, "_rangeContentWidth", 0); + _defineProperty(this, "_rangeCommitTimer", null); + _defineProperty(this, "_isProgrammaticScroll", false); + _defineProperty(this, "_scrollbarHideTimer", null); + _defineProperty(this, "_timelinePointerId", null); + _defineProperty(this, "_timelinePointerStartX", 0); + _defineProperty(this, "_timelinePointerStartScrollLeft", 0); + _defineProperty(this, "_timelinePointerStartTimestamp", null); + _defineProperty(this, "_timelinePointerMode", null); + _defineProperty(this, "_timelineDragStartRangeMs", 0); + _defineProperty(this, "_timelineDragEndRangeMs", 0); + _defineProperty(this, "_timelineDragStartZoomRange", null); + _defineProperty(this, "_timelinePointerMoved", false); + _defineProperty(this, "_timelineTrackClickPending", false); + _defineProperty(this, "_rangeScrollViewportEl", null); + _defineProperty(this, "_rangeTimelineEl", null); + _defineProperty(this, "_rangeTrackEl", null); + _defineProperty(this, "_rangeTickLayerEl", null); + _defineProperty(this, "_rangeLabelLayerEl", null); + _defineProperty(this, "_rangeContextLayerEl", null); + _defineProperty(this, "_rangeSelectionEl", null); + _defineProperty(this, "_rangeStartHandleEl", null); + _defineProperty(this, "_rangeEndHandleEl", null); + _defineProperty(this, "_rangeStartTooltipEl", null); + _defineProperty(this, "_rangeEndTooltipEl", null); + _defineProperty(this, "_rangeJumpLeftEl", null); + _defineProperty(this, "_rangeJumpRightEl", null); + _defineProperty(this, "_resizeObserver", null); + _defineProperty(this, "_onRangeScroll", void 0); + _defineProperty(this, "_onRangePointerMove", void 0); + _defineProperty(this, "_onRangePointerUp", void 0); + _defineProperty(this, "_onTimelinePointerMove", void 0); + _defineProperty(this, "_onTimelinePointerUp", void 0); + this._onRangePointerMove = (ev) => this._handleRangePointerMove(ev); + this._onRangePointerUp = (ev) => this._finishRangePointerInteraction(ev); + this._onTimelinePointerMove = (ev) => this._handleTimelinePointerMove(ev); + this._onTimelinePointerUp = (ev) => this._finishTimelinePointerInteraction(ev); + this._onRangeScroll = () => { + this._updateSelectionJumpControls(); + this._syncVisibleRangeLabels(); + this._updateRangeTooltip(); + this.dispatchEvent(new CustomEvent("dp-range-scroll", { + bubbles: true, + composed: true + })); + if (!this._isProgrammaticScroll) this._showScrollbar(); + }; + } + disconnectedCallback() { + super.disconnectedCallback(); + if (this._rangeScrollViewportEl) this._rangeScrollViewportEl.removeEventListener("scroll", this._onRangeScroll); + this._detachRangePointerListeners(); + this._detachTimelinePointerListeners(); + if (this._rangeCommitTimer) { + window.clearTimeout(this._rangeCommitTimer); + this._rangeCommitTimer = null; + } + if (this._scrollbarHideTimer) { + window.clearTimeout(this._scrollbarHideTimer); + this._scrollbarHideTimer = null; + } + if (this._resizeObserver) { + this._resizeObserver.disconnect(); + this._resizeObserver = null; + } + } + firstUpdated() { + const sr = this.shadowRoot; + this._rangeScrollViewportEl = sr.getElementById("range-scroll-viewport"); + this._rangeTimelineEl = sr.getElementById("range-timeline"); + this._rangeTrackEl = sr.getElementById("range-track"); + this._rangeTickLayerEl = sr.getElementById("range-tick-layer"); + this._rangeLabelLayerEl = sr.getElementById("range-label-layer"); + this._rangeContextLayerEl = sr.getElementById("range-context-layer"); + this._rangeSelectionEl = sr.getElementById("range-selection"); + this._rangeStartHandleEl = sr.getElementById("range-start-handle"); + this._rangeEndHandleEl = sr.getElementById("range-end-handle"); + this._rangeStartTooltipEl = sr.getElementById("range-tooltip-start"); + this._rangeEndTooltipEl = sr.getElementById("range-tooltip-end"); + this._rangeJumpLeftEl = sr.getElementById("range-jump-left"); + this._rangeJumpRightEl = sr.getElementById("range-jump-right"); + this._rangeScrollViewportEl?.addEventListener("scroll", this._onRangeScroll); + if (typeof ResizeObserver !== "undefined") { + this._resizeObserver = new ResizeObserver(() => { + this._syncTimelineWidth(); + this._updateSelectionJumpControls(); + this._syncVisibleRangeLabels(); + this._revealSelectionInTimeline("auto"); + }); + if (this._rangeScrollViewportEl) this._resizeObserver.observe(this._rangeScrollViewportEl); + } + this._syncRangeControl(); + } + updated(changed) { + if ([ + "startTime", + "endTime", + "rangeBounds", + "zoomLevel", + "dateSnapping" + ].some((p) => changed.has(p))) this._syncRangeControl(); + } + _pctForTime(time) { + if (!time || !this.rangeBounds) return 0; + const { min, max } = this.rangeBounds; + return Math.max(0, Math.min(100, (time.getTime() - min) / (max - min) * 100)); + } + render() { + return b` + + +
+
+ +
+
+
+ +
+
+
+ this._beginRangePointerInteraction("start", e.detail.pointerId, e.detail.clientX)} + @dp-handle-keydown=${(e) => this._handleRangeHandleKeyDown("start", e.detail)} + @dp-handle-hover=${() => this._setRangeTooltipHoverHandle("start")} + @dp-handle-leave=${() => this._clearRangeTooltipHoverHandle("start")} + @dp-handle-focus=${() => this._setRangeTooltipFocusHandle("start")} + @dp-handle-blur=${() => this._clearRangeTooltipFocusHandle("start")} + > + this._beginRangePointerInteraction("end", e.detail.pointerId, e.detail.clientX)} + @dp-handle-keydown=${(e) => this._handleRangeHandleKeyDown("end", e.detail)} + @dp-handle-hover=${() => this._setRangeTooltipHoverHandle("end")} + @dp-handle-leave=${() => this._clearRangeTooltipHoverHandle("end")} + @dp-handle-focus=${() => this._setRangeTooltipFocusHandle("end")} + @dp-handle-blur=${() => this._clearRangeTooltipFocusHandle("end")} + > +
+
+ + + `; + } + _getZoomConfig() { + return RANGE_ZOOM_CONFIGS[this.zoomLevel] || RANGE_ZOOM_CONFIGS.month_short; + } + _getEffectiveSnapUnit() { + if (this.dateSnapping !== "auto") return this.dateSnapping; + switch (this.zoomLevel) { + case "quarterly": + case "month_compressed": return "month"; + case "month_short": + case "month_expanded": + case "week_compressed": return "week"; + case "week_expanded": return "day"; + case "day": return "hour"; + default: return "day"; + } + } + _getScaleLabelZoomLevel() { + if (this.zoomLevel === "quarterly" || this.zoomLevel === "month_short") return this.zoomLevel; + return ""; + } + _getSnapSpanMs(reference = /* @__PURE__ */ new Date()) { + const snapUnit = this._getEffectiveSnapUnit(); + const start = startOfUnit(reference, snapUnit); + const end = endOfUnit(reference, snapUnit); + return Math.max(SECOND_MS, end.getTime() - start.getTime()); + } + _countUnitsInRange(startMs, endMs, unit) { + const totalMs = Math.max(0, endMs - startMs); + const perMs = { + second: SECOND_MS, + minute: 60 * SECOND_MS, + hour: 3600 * SECOND_MS, + day: 1440 * 60 * SECOND_MS, + week: 10080 * 60 * SECOND_MS + }; + if (perMs[unit]) return Math.ceil(totalMs / perMs[unit]); + if (unit === "month") return Math.ceil(totalMs / (30.44 * 24 * 60 * 60 * SECOND_MS)); + if (unit === "quarter") return Math.ceil(totalMs / (91.3 * 24 * 60 * 60 * SECOND_MS)); + if (unit === "year") return Math.ceil(totalMs / (365.25 * 24 * 60 * 60 * SECOND_MS)); + return Math.max(1, Math.ceil(totalMs / (1440 * 60 * SECOND_MS))); + } + _syncRangeControl() { + if (!this._rangeTrackEl || !this._rangeStartHandleEl || !this._rangeEndHandleEl) return; + if (!this.rangeBounds) return; + this._draftStartTime = this.startTime ? new Date(this.startTime) : null; + this._draftEndTime = this.endTime ? new Date(this.endTime) : null; + this._syncTimelineWidth(); + this._updateHandleStacking(); + this._renderRangeScale(); + this._updateRangePreview(); + this._updateSelectionJumpControls(); + this._revealSelectionInTimeline("auto"); + } + _syncTimelineWidth() { + if (!this.rangeBounds || !this._rangeTimelineEl) return; + const { config } = this.rangeBounds; + const viewportWidth = Math.max(this._rangeScrollViewportEl?.clientWidth || 0, 320); + const unitCount = this._countUnitsInRange(this.rangeBounds.min, this.rangeBounds.max, config.majorUnit); + const contentWidth = Math.max(viewportWidth, unitCount * (config.pixelsPerUnit || 60)); + this._rangeContentWidth = contentWidth; + this._rangeTimelineEl.style.width = `${contentWidth}px`; + } + _renderScaleMarkers(fragment, unit, className, total, step = 1) { + if (!this.rangeBounds) return; + let markerTime = addUnit(startOfUnit(new Date(this.rangeBounds.min), unit), unit, 0); + if (markerTime.getTime() < this.rangeBounds.min) markerTime = addUnit(markerTime, unit, step); + while (markerTime.getTime() < this.rangeBounds.max) { + const tick = document.createElement("span"); + tick.className = `range-tick ${className}`; + tick.style.left = `${(markerTime.getTime() - this.rangeBounds.min) / total * 100}%`; + fragment.appendChild(tick); + markerTime = addUnit(markerTime, unit, step); + } + } + _buildRangePeriodButton(className, leftValue, total, text, unit, startTime) { + if (!this.rangeBounds) return document.createElement("button"); + const button = document.createElement("button"); + button.type = "button"; + button.className = `range-period-button ${className}`; + button.style.left = `${(leftValue - this.rangeBounds.min) / total * 100}%`; + button.textContent = text; + const selectionLabel = formatPeriodSelectionLabel(startTime, unit, this.locale || void 0); + const selectTitle = `${msg("Select")} ${selectionLabel}`; + button.title = selectTitle; + button.setAttribute("aria-label", selectTitle); + button.addEventListener("click", (ev) => this._handleRangePeriodSelect(unit, startTime, ev)); + button.addEventListener("pointerenter", () => this._setHoveredPeriodRange(unit, startTime)); + button.addEventListener("pointerleave", () => this._clearHoveredPeriodRange(unit, startTime)); + button.addEventListener("focus", () => this._setHoveredPeriodRange(unit, startTime)); + button.addEventListener("blur", () => this._clearHoveredPeriodRange(unit, startTime)); + return button; + } + _getRangeUnitAnchorMs(startTime, unit, anchor = "auto") { + const unitStart = Math.max(startOfUnit(new Date(startTime), unit).getTime(), this.rangeBounds?.min ?? -Infinity); + const unitEnd = Math.min(endOfUnit(new Date(startTime), unit).getTime(), this.rangeBounds?.max ?? Infinity); + let resolvedAnchor = anchor; + if (resolvedAnchor === "auto") resolvedAnchor = unit === "day" || unit === "week" ? "center" : "start"; + if (resolvedAnchor === "center") return unitStart + Math.max(0, (unitEnd - unitStart) / 2); + return unitStart; + } + _estimateRangeLabelWidth(text, className, minGap) { + const basePadding = className === "range-context-label" ? 20 : 14; + const charWidth = className === "range-context-label" ? 8.2 : 7.2; + return String(text).length * charWidth + basePadding + minGap; + } + _computeRangeLabelStride(unit, formatter, className, minGap) { + if (!this.rangeBounds || !this._rangeContentWidth) return 1; + const total = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); + let current = startOfUnit(new Date(this.rangeBounds.min), unit); + let previousMs = null; + let minSpacingPx = Infinity; + let maxLabelWidthPx = 0; + let samples = 0; + while (current.getTime() < this.rangeBounds.max && samples < 24) { + const currentMs = Math.max(current.getTime(), this.rangeBounds.min); + const text = formatter(current); + maxLabelWidthPx = Math.max(maxLabelWidthPx, this._estimateRangeLabelWidth(text, className, minGap)); + if (previousMs != null) { + const spacingPx = (currentMs - previousMs) / total * this._rangeContentWidth; + if (spacingPx > 0) minSpacingPx = Math.min(minSpacingPx, spacingPx); + } + previousMs = currentMs; + current = addUnit(current, unit, 1); + samples += 1; + } + if (!Number.isFinite(minSpacingPx) || minSpacingPx <= 0) return 1; + return Math.max(1, Math.ceil(maxLabelWidthPx / minSpacingPx)); + } + _syncVisibleRangeLabels() {} + _renderRangeScale() { + if (!this.rangeBounds || !this._rangeTickLayerEl || !this._rangeLabelLayerEl || !this._rangeContextLayerEl) return; + this._rangeTickLayerEl.innerHTML = ""; + this._rangeLabelLayerEl.innerHTML = ""; + this._rangeContextLayerEl.innerHTML = ""; + const total = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); + const { config } = this.rangeBounds; + const tickFragment = document.createDocumentFragment(); + const labelFragment = document.createDocumentFragment(); + const contextFragment = document.createDocumentFragment(); + const scaleLabelZoomLevel = this._getScaleLabelZoomLevel(); + const scaleLabelStride = config.labelUnit === "month" || config.labelUnit === "day" ? 1 : this._computeRangeLabelStride(config.labelUnit, (value) => formatScaleLabel(value, config.labelUnit, scaleLabelZoomLevel, this.locale || void 0), "range-scale-label", 10); + const contextLabelStride = config.contextUnit === "month" || config.contextUnit === "day" ? 1 : this._computeRangeLabelStride(config.contextUnit, (value) => formatContextLabel(value, config.contextUnit, this.locale || void 0), "range-context-label", 14); + if (config.detailUnit && config.detailUnit !== config.minorUnit && config.detailUnit !== config.majorUnit) this._renderScaleMarkers(tickFragment, config.detailUnit, "fine", total, config.detailStep || 1); + if (config.minorUnit !== config.majorUnit) this._renderScaleMarkers(tickFragment, config.minorUnit, "", total); + this._renderScaleMarkers(tickFragment, config.majorUnit, "major", total); + let labelRef = startOfUnit(new Date(this.rangeBounds.min), config.labelUnit); + let labelIndex = 0; + while (labelRef.getTime() < this.rangeBounds.max) { + if (labelIndex % scaleLabelStride === 0) { + const leftValue = this._getRangeUnitAnchorMs(labelRef, config.labelUnit, "auto"); + const label = this._buildRangePeriodButton("range-scale-label", leftValue, total, formatScaleLabel(labelRef, config.labelUnit, scaleLabelZoomLevel, this.locale || void 0), config.labelUnit, labelRef); + labelFragment.appendChild(label); + } + labelRef = addUnit(labelRef, config.labelUnit, 1); + labelIndex += 1; + } + let contextRef = startOfUnit(new Date(this.rangeBounds.min), config.contextUnit); + if (contextRef.getTime() < this.rangeBounds.min) contextRef = addUnit(contextRef, config.contextUnit, 1); + let contextIndex = 0; + while (contextRef.getTime() < this.rangeBounds.max) { + const left = `${(contextRef.getTime() - this.rangeBounds.min) / total * 100}%`; + const divider = document.createElement("span"); + divider.className = "range-divider"; + divider.style.left = left; + contextFragment.appendChild(divider); + if (contextIndex % contextLabelStride === 0) { + const label = this._buildRangePeriodButton("range-context-label", contextRef.getTime(), total, formatContextLabel(contextRef, config.contextUnit, this.locale || void 0), config.contextUnit, contextRef); + contextFragment.appendChild(label); + } + contextRef = addUnit(contextRef, config.contextUnit, 1); + contextIndex += 1; + } + this._rangeTickLayerEl.appendChild(tickFragment); + this._rangeLabelLayerEl.appendChild(labelFragment); + this._rangeContextLayerEl.appendChild(contextFragment); + this._syncVisibleRangeLabels(); + } + _updateHandleStacking(activeHandle = this._activeRangeHandle) { + if (!this._rangeStartHandleEl || !this._rangeEndHandleEl) return; + this._rangeStartHandleEl.style.zIndex = activeHandle === "start" ? "5" : "3"; + this._rangeEndHandleEl.style.zIndex = activeHandle === "end" ? "5" : "4"; + } + _updateRangePreview() { + if (!this.rangeBounds || !this._draftStartTime || !this._draftEndTime) return; + const total = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); + const startPct = (this._draftStartTime.getTime() - this.rangeBounds.min) / total * 100; + const endPct = (this._draftEndTime.getTime() - this.rangeBounds.min) / total * 100; + if (this._rangeSelectionEl) { + this._rangeSelectionEl.style.left = `${startPct}%`; + this._rangeSelectionEl.style.width = `${Math.max(0, endPct - startPct)}%`; + } + if (this._rangeStartHandleEl) { + this._rangeStartHandleEl.style.left = `${startPct}%`; + this._rangeStartHandleEl.setAttribute("aria-valuetext", formatRangeDateTime(this._draftStartTime, this.locale || void 0)); + } + if (this._rangeEndHandleEl) { + this._rangeEndHandleEl.style.left = `${endPct}%`; + this._rangeEndHandleEl.setAttribute("aria-valuetext", formatRangeDateTime(this._draftEndTime, this.locale || void 0)); + } + this._updateRangeTooltip(); + } + _getVisibleRangeTooltipHandles() { + if (this._timelinePointerMode === "selection" || this._timelinePointerMode === "interval_select") return ["start", "end"]; + const handle = this._activeRangeHandle || this._focusedRangeHandle || this._hoveredRangeHandle || null; + return handle ? [handle] : []; + } + _setRangeTooltipHoverHandle(handle) { + this._hoveredRangeHandle = handle; + this._updateRangeTooltip(); + } + _clearRangeTooltipHoverHandle(handle) { + if (this._activeRangeHandle === handle) return; + if (this._hoveredRangeHandle === handle) this._hoveredRangeHandle = null; + this._updateRangeTooltip(); + } + _setRangeTooltipFocusHandle(handle) { + this._focusedRangeHandle = handle; + this._updateRangeTooltip(); + } + _clearRangeTooltipFocusHandle(handle) { + if (this._activeRangeHandle === handle) return; + if (this._focusedRangeHandle === handle) this._focusedRangeHandle = null; + this._updateRangeTooltip(); + } + _updateRangeTooltip() { + if (!this.rangeBounds || !this._rangeScrollViewportEl) return; + const visibleHandles = new Set(this._getVisibleRangeTooltipHandles()); + this._updateRangeTooltipForHandle("start", visibleHandles.has("start")); + this._updateRangeTooltipForHandle("end", visibleHandles.has("end")); + } + _updateRangeTooltipForHandle(handle, visible) { + const tooltip = handle === "start" ? this._rangeStartTooltipEl : this._rangeEndTooltipEl; + if (!tooltip) return; + if (!visible) { + tooltip.classList.remove("visible"); + tooltip.setAttribute("aria-hidden", "true"); + return; + } + const value = handle === "start" ? this._draftStartTime : this._draftEndTime; + if (!value || !this.rangeBounds || !this._rangeScrollViewportEl) { + tooltip.classList.remove("visible"); + tooltip.setAttribute("aria-hidden", "true"); + return; + } + const total = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); + const contentWidth = Math.max(this._rangeContentWidth || 0, this._rangeScrollViewportEl.clientWidth || 0, 1); + const clampedX = clampNumber((value.getTime() - this.rangeBounds.min) / total * contentWidth - this._rangeScrollViewportEl.scrollLeft, 0, this._rangeScrollViewportEl.clientWidth); + if (handle === "end" && this.isLiveEdge) { + const dateEl = document.createElement("span"); + dateEl.textContent = formatRangeDateTime(value, this.locale || void 0); + const hintEl = document.createElement("span"); + hintEl.className = "range-tooltip-live-hint"; + hintEl.textContent = msg("Updates with new data"); + tooltip.textContent = ""; + tooltip.append(dateEl, hintEl); + } else tooltip.textContent = formatRangeDateTime(value, this.locale || void 0); + tooltip.style.left = `${clampedX}px`; + tooltip.classList.add("visible"); + tooltip.setAttribute("aria-hidden", "false"); + } + _handleRangePeriodSelect(unit, startTime, ev) { + ev.preventDefault(); + ev.stopPropagation(); + const periodStart = startOfUnit(new Date(startTime), unit); + const periodEnd = endOfUnit(new Date(startTime), unit); + if (this._rangeCommitTimer) { + window.clearTimeout(this._rangeCommitTimer); + this._rangeCommitTimer = null; + } + this._draftStartTime = new Date(periodStart); + this._draftEndTime = new Date(periodEnd); + this._updateRangePreview(); + this.dispatchEvent(new CustomEvent("dp-range-period-select", { + detail: { + unit, + startTime: periodStart + }, + bubbles: true, + composed: true + })); + this._commitRangeSelection({ push: true }); + } + _setHoveredPeriodRange(unit, startTime) { + const start = startOfUnit(new Date(startTime), unit); + const end = endOfUnit(new Date(startTime), unit); + this._hoveredPeriodRange = { + unit, + start: start.getTime(), + end: end.getTime() + }; + this.dispatchEvent(new CustomEvent("dp-range-period-hover", { + detail: { + start, + end + }, + bubbles: true, + composed: true + })); + } + _clearHoveredPeriodRange(unit, startTime) { + if (!this._hoveredPeriodRange) return; + const start = startOfUnit(new Date(startTime), unit).getTime(); + const end = endOfUnit(new Date(startTime), unit).getTime(); + if (this._hoveredPeriodRange.start === start && this._hoveredPeriodRange.end === end) { + this._hoveredPeriodRange = null; + this.dispatchEvent(new CustomEvent("dp-range-period-leave", { + bubbles: true, + composed: true + })); + } + } + _updateSelectionJumpControls() { + if (!this._rangeScrollViewportEl || !this.rangeBounds || !this._rangeContentWidth || !this.startTime || !this.endTime) { + if (this._rangeJumpLeftEl) this._rangeJumpLeftEl.hidden = true; + if (this._rangeJumpRightEl) this._rangeJumpRightEl.hidden = true; + return; + } + const total = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); + const viewportWidth = this._rangeScrollViewportEl.clientWidth; + const currentLeft = this._rangeScrollViewportEl.scrollLeft; + const currentRight = currentLeft + viewportWidth; + const startPx = (this.startTime.getTime() - this.rangeBounds.min) / total * this._rangeContentWidth; + const endPx = (this.endTime.getTime() - this.rangeBounds.min) / total * this._rangeContentWidth; + if (this._rangeJumpLeftEl) this._rangeJumpLeftEl.hidden = !(endPx < currentLeft); + if (this._rangeJumpRightEl) this._rangeJumpRightEl.hidden = !(startPx > currentRight); + } + _scrollTimelineToRange(range, behavior = "auto", { center = false } = {}) { + if (!this._rangeScrollViewportEl || !this.rangeBounds || !this._rangeContentWidth || !range) return; + const viewportWidth = this._rangeScrollViewportEl.clientWidth; + if (!viewportWidth || this._rangeContentWidth <= viewportWidth) return; + const totalMs = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); + const visibleSpanMs = totalMs * Math.min(1, viewportWidth / this._rangeContentWidth); + const maxScrollLeft = Math.max(0, this._rangeContentWidth - viewportWidth); + const viewportRangeMs = Math.max(0, totalMs - visibleSpanMs); + if (viewportRangeMs <= 0) return; + const nextLeft = clampNumber(((center ? clampNumber((range.start + range.end) / 2 - visibleSpanMs / 2, this.rangeBounds.min, this.rangeBounds.max - visibleSpanMs) : clampNumber(range.start, this.rangeBounds.min, this.rangeBounds.max - visibleSpanMs)) - this.rangeBounds.min) / viewportRangeMs * maxScrollLeft, 0, maxScrollLeft); + this._rangeScrollViewportEl.scrollTo({ + left: nextLeft, + behavior + }); + } + revealSelection() { + this._revealSelectionInTimeline("smooth"); + } + _revealSelectionInTimeline(behavior = "auto") { + if (!this.startTime || !this.endTime) return; + this._isProgrammaticScroll = true; + this._scrollTimelineToRange({ + start: this.startTime.getTime(), + end: this.endTime.getTime() + }, behavior, { center: true }); + window.setTimeout(() => { + this._isProgrammaticScroll = false; + }, 50); + } + _showScrollbar() { + if (!this._rangeScrollViewportEl) return; + this._rangeScrollViewportEl.classList.add("scrollbar-visible"); + if (this._scrollbarHideTimer) window.clearTimeout(this._scrollbarHideTimer); + this._scrollbarHideTimer = window.setTimeout(() => { + this._scrollbarHideTimer = null; + this._rangeScrollViewportEl?.classList.remove("scrollbar-visible"); + }, 1500); + } + _timestampFromClientX(clientX) { + if (!this.rangeBounds || !this._rangeTrackEl) return null; + const rect = this._rangeTrackEl.getBoundingClientRect(); + if (!rect.width) return null; + const ratio = clampNumber((clientX - rect.left) / rect.width, 0, 1); + return this.rangeBounds.min + ratio * (this.rangeBounds.max - this.rangeBounds.min); + } + _getTimelineSelectionDragDeltaMs(timestamp) { + if (timestamp == null || this._timelinePointerStartTimestamp == null) return 0; + const snapUnit = this._getEffectiveSnapUnit(); + if (!snapUnit) return timestamp - this._timelinePointerStartTimestamp; + const snappedStart = snapDateToUnit(new Date(this._timelinePointerStartTimestamp), snapUnit).getTime(); + return snapDateToUnit(new Date(timestamp), snapUnit).getTime() - snappedStart; + } + _setDraftRangeFromTimestamp(handle, timestamp) { + if (!this.rangeBounds) return; + const snapUnit = this._getEffectiveSnapUnit(); + let startMs = this._draftStartTime?.getTime() ?? this.startTime?.getTime() ?? this.rangeBounds.min; + let endMs = this._draftEndTime?.getTime() ?? this.endTime?.getTime() ?? this.rangeBounds.max; + const snapped = clampNumber(snapDateToUnit(new Date(timestamp), snapUnit).getTime(), this.rangeBounds.min, this.rangeBounds.max); + const minSpan = Math.max(this._getSnapSpanMs(new Date(snapped)), SECOND_MS); + if (handle === "start") startMs = clampNumber(snapped, this.rangeBounds.min, endMs - minSpan); + else endMs = clampNumber(snapped, startMs + minSpan, this.rangeBounds.max); + this._draftStartTime = new Date(startMs); + this._draftEndTime = new Date(endMs); + this._updateHandleStacking(handle); + this._updateRangePreview(); + this._fireDraftEvent(); + this._scheduleRangeCommit(); + } + _shiftDraftRangeByDelta(deltaMs) { + if (!this.rangeBounds) return; + const startMs = this._timelineDragStartRangeMs; + const endMs = this._timelineDragEndRangeMs; + const clampedDelta = clampNumber(deltaMs, this.rangeBounds.min - startMs, this.rangeBounds.max - endMs); + this._draftStartTime = new Date(startMs + clampedDelta); + this._draftEndTime = new Date(endMs + clampedDelta); + this._updateRangePreview(); + this._fireDraftEvent(); + this._scheduleRangeCommit(); + } + _setDraftRangeFromIntervalSelection(startTimestamp, endTimestamp) { + if (!this.rangeBounds) return; + const unit = this.rangeBounds.config?.labelUnit || this._getEffectiveSnapUnit(); + const startValue = Math.min(startTimestamp, endTimestamp); + const endValue = Math.max(startTimestamp, endTimestamp); + const rangeStart = clampNumber(startOfUnit(new Date(startValue), unit).getTime(), this.rangeBounds.min, this.rangeBounds.max); + const rangeEnd = clampNumber(endOfUnit(new Date(endValue), unit).getTime(), this.rangeBounds.min, this.rangeBounds.max); + if (rangeStart >= rangeEnd) return; + this._draftStartTime = new Date(rangeStart); + this._draftEndTime = new Date(rangeEnd); + this._updateRangePreview(); + } + _fireDraftEvent() { + if (!this._draftStartTime || !this._draftEndTime) return; + this.dispatchEvent(new CustomEvent("dp-range-draft", { + detail: { + start: new Date(this._draftStartTime), + end: new Date(this._draftEndTime) + }, + bubbles: true, + composed: true + })); + } + _scheduleRangeCommit() { + if (this._rangeInteractionActive || this._timelinePointerMode === "selection" || this._timelinePointerMode === "interval_select") return; + if (this._rangeCommitTimer) window.clearTimeout(this._rangeCommitTimer); + this._rangeCommitTimer = window.setTimeout(() => { + this._rangeCommitTimer = null; + this._commitRangeSelection({ push: false }); + }, 240); + } + _commitRangeSelection({ push = false } = {}) { + if (!this._draftStartTime || !this._draftEndTime) return; + this.dispatchEvent(new CustomEvent("dp-range-commit", { + detail: { + start: new Date(this._draftStartTime), + end: new Date(this._draftEndTime), + push + }, + bubbles: true, + composed: true + })); + } + _beginRangePointerInteraction(handle, pointerId, clientX) { + if (!this._rangeTrackEl) return; + this._rangeInteractionActive = true; + if (this._rangeCommitTimer) { + window.clearTimeout(this._rangeCommitTimer); + this._rangeCommitTimer = null; + } + this._activeRangeHandle = handle; + this._hoveredRangeHandle = handle; + this._rangePointerId = pointerId; + this._updateHandleStacking(handle); + this._updateRangeTooltip(); + this._attachRangePointerListeners(); + (handle === "start" ? this._rangeStartHandleEl : this._rangeEndHandleEl)?.focus?.(); + const timestamp = this._timestampFromClientX(clientX); + if (timestamp != null) this._setDraftRangeFromTimestamp(handle, timestamp); + } + _maybeAutoScrollTimelineDuringHandleDrag(clientX) { + if (!this._rangeScrollViewportEl) return; + const viewport = this._rangeScrollViewportEl; + const rect = viewport.getBoundingClientRect(); + if (!rect.width) return; + const maxScrollLeft = Math.max(0, viewport.scrollWidth - viewport.clientWidth); + if (maxScrollLeft <= 0) return; + let delta = 0; + const leftDistance = clientX - rect.left; + const rightDistance = rect.right - clientX; + if (leftDistance < 48) { + const ratio = clampNumber((48 - leftDistance) / 48, 0, 1); + delta = -Math.max(1, Math.round(ratio * 28)); + } else if (rightDistance < 48) { + const ratio = clampNumber((48 - rightDistance) / 48, 0, 1); + delta = Math.max(1, Math.round(ratio * 28)); + } + if (!delta) return; + viewport.scrollLeft = clampNumber(viewport.scrollLeft + delta, 0, maxScrollLeft); + } + _attachRangePointerListeners() { + window.addEventListener("pointermove", this._onRangePointerMove); + window.addEventListener("pointerup", this._onRangePointerUp); + window.addEventListener("pointercancel", this._onRangePointerUp); + } + _detachRangePointerListeners() { + window.removeEventListener("pointermove", this._onRangePointerMove); + window.removeEventListener("pointerup", this._onRangePointerUp); + window.removeEventListener("pointercancel", this._onRangePointerUp); + this._rangePointerId = null; + this._activeRangeHandle = null; + this._rangeInteractionActive = false; + this._updateHandleStacking(); + this._updateRangeTooltip(); + } + _handleRangePointerMove(ev) { + if (!this._activeRangeHandle) return; + if (this._rangePointerId != null && ev.pointerId !== this._rangePointerId) return; + this._maybeAutoScrollTimelineDuringHandleDrag(ev.clientX); + const timestamp = this._timestampFromClientX(ev.clientX); + if (timestamp == null) return; + ev.preventDefault(); + this._setDraftRangeFromTimestamp(this._activeRangeHandle, timestamp); + } + _finishRangePointerInteraction(ev) { + if (!this._activeRangeHandle) return; + if (this._rangePointerId != null && ev.pointerId !== this._rangePointerId) return; + this._detachRangePointerListeners(); + this._focusedRangeHandle = null; + this._hoveredRangeHandle = null; + this._updateRangeTooltip(); + this._commitRangeSelection({ push: true }); + } + _handleRangeHandleKeyDown(handle, detail) { + if (!this.rangeBounds) return; + const snapUnit = this._getEffectiveSnapUnit(); + const currentValue = handle === "start" ? this._draftStartTime?.getTime() ?? this.startTime?.getTime() : this._draftEndTime?.getTime() ?? this.endTime?.getTime(); + if (currentValue == null) return; + const config = this._getZoomConfig(); + let nextValue = null; + if (detail.key === "ArrowLeft" || detail.key === "ArrowDown") nextValue = addUnit(new Date(currentValue), snapUnit, -1).getTime(); + if (detail.key === "ArrowRight" || detail.key === "ArrowUp") nextValue = addUnit(new Date(currentValue), snapUnit, 1).getTime(); + if (detail.key === "PageDown") nextValue = addUnit(new Date(currentValue), config.majorUnit, -1).getTime(); + if (detail.key === "PageUp") nextValue = addUnit(new Date(currentValue), config.majorUnit, 1).getTime(); + if (detail.key === "Home") nextValue = this.rangeBounds.min; + if (detail.key === "End") nextValue = this.rangeBounds.max; + if (nextValue == null) return; + this._focusedRangeHandle = handle; + this._setDraftRangeFromTimestamp(handle, nextValue); + } + _handleTimelinePointerDown(ev) { + if (ev.button !== 0) return; + if (ev.composedPath().some((node) => node instanceof Element && (node.tagName === "RANGE-HANDLE" || node.closest?.("range-handle")))) return; + if (ev.target?.closest?.(".range-period-button")) return; + if (!this._rangeScrollViewportEl) return; + const isSelectionDrag = !!ev.target?.closest?.(".range-selection"); + const trackRect = this._rangeTrackEl?.getBoundingClientRect(); + const isTrackRegion = !!trackRect && ev.clientY >= trackRect.top - 6 && ev.clientY <= trackRect.bottom + 6; + const isIntervalSelect = !isSelectionDrag && !isTrackRegion; + this._detachTimelinePointerListeners(); + this._rangeInteractionActive = isSelectionDrag || isIntervalSelect; + if ((isSelectionDrag || isIntervalSelect) && this._rangeCommitTimer) { + window.clearTimeout(this._rangeCommitTimer); + this._rangeCommitTimer = null; + } + this._timelinePointerId = ev.pointerId; + this._timelinePointerStartX = ev.clientX; + this._timelinePointerStartScrollLeft = this._rangeScrollViewportEl.scrollLeft; + this._timelinePointerStartTimestamp = isSelectionDrag || isIntervalSelect ? this._timestampFromClientX(ev.clientX) : null; + if (isSelectionDrag) this._timelinePointerMode = "selection"; + else if (isIntervalSelect) this._timelinePointerMode = "interval_select"; + else this._timelinePointerMode = "pan"; + this._timelineDragStartRangeMs = this._draftStartTime?.getTime() ?? this.startTime?.getTime() ?? 0; + this._timelineDragEndRangeMs = this._draftEndTime?.getTime() ?? this.endTime?.getTime() ?? 0; + this._timelinePointerMoved = false; + this._timelineTrackClickPending = !isSelectionDrag && !isIntervalSelect && !!ev.target?.closest?.(".range-track"); + this._rangeScrollViewportEl.classList.remove("dragging"); + this._rangeSelectionEl?.classList.toggle("dragging", isSelectionDrag); + window.addEventListener("pointermove", this._onTimelinePointerMove); + window.addEventListener("pointerup", this._onTimelinePointerUp); + window.addEventListener("pointercancel", this._onTimelinePointerUp); + } + _detachTimelinePointerListeners() { + window.removeEventListener("pointermove", this._onTimelinePointerMove); + window.removeEventListener("pointerup", this._onTimelinePointerUp); + window.removeEventListener("pointercancel", this._onTimelinePointerUp); + if (this._rangeScrollViewportEl) this._rangeScrollViewportEl.classList.remove("dragging"); + this._rangeSelectionEl?.classList.remove("dragging"); + this._timelinePointerId = null; + this._timelinePointerStartTimestamp = null; + this._timelinePointerMode = null; + this._rangeInteractionActive = false; + this._timelinePointerMoved = false; + this._timelineTrackClickPending = false; + } + _handleTimelinePointerMove(ev) { + if (this._timelinePointerId == null || ev.pointerId !== this._timelinePointerId || !this._rangeScrollViewportEl) return; + if (this._timelinePointerMode === "selection") { + const timestamp = this._timestampFromClientX(ev.clientX); + if (timestamp == null || this._timelinePointerStartTimestamp == null) return; + const deltaX = ev.clientX - this._timelinePointerStartX; + if (!this._timelinePointerMoved && Math.abs(deltaX) < 4) return; + this._timelinePointerMoved = true; + this._shiftDraftRangeByDelta(this._getTimelineSelectionDragDeltaMs(timestamp)); + ev.preventDefault(); + return; + } + if (this._timelinePointerMode === "interval_select") { + const timestamp = this._timestampFromClientX(ev.clientX); + if (timestamp == null || this._timelinePointerStartTimestamp == null) return; + const deltaX = ev.clientX - this._timelinePointerStartX; + if (!this._timelinePointerMoved && Math.abs(deltaX) < 4) return; + this._timelinePointerMoved = true; + this._setDraftRangeFromIntervalSelection(this._timelinePointerStartTimestamp, timestamp); + ev.preventDefault(); + return; + } + const deltaX = ev.clientX - this._timelinePointerStartX; + if (!this._timelinePointerMoved && Math.abs(deltaX) < 4) return; + this._timelinePointerMoved = true; + this._timelineTrackClickPending = false; + this._rangeScrollViewportEl.classList.add("dragging"); + const maxScrollLeft = Math.max(0, this._rangeScrollViewportEl.scrollWidth - this._rangeScrollViewportEl.clientWidth); + this._rangeScrollViewportEl.scrollLeft = clampNumber(this._timelinePointerStartScrollLeft - deltaX, 0, maxScrollLeft); + ev.preventDefault(); + } + _finishTimelinePointerInteraction(ev) { + if (this._timelinePointerId == null || ev.pointerId !== this._timelinePointerId) return; + const mode = this._timelinePointerMode; + const didMove = this._timelinePointerMoved; + const shouldSelectTrack = this._timelineTrackClickPending && !didMove; + const clientX = ev.clientX; + this._detachTimelinePointerListeners(); + if (mode === "selection") { + this._focusedRangeHandle = null; + this._hoveredRangeHandle = null; + this._updateRangeTooltip(); + if (didMove) this._commitRangeSelection({ push: true }); + return; + } + if (mode === "interval_select") { + this._hoveredPeriodRange = null; + this._updateRangeTooltip(); + if (didMove) this._commitRangeSelection({ push: true }); + return; + } + if (shouldSelectTrack) this._handleTrackSelectionAtClientX(clientX); + } + _handleTrackSelectionAtClientX(clientX) { + const timestamp = this._timestampFromClientX(clientX); + if (timestamp == null) return; + const startMs = this._draftStartTime?.getTime() ?? this.startTime?.getTime() ?? this.rangeBounds?.min; + const endMs = this._draftEndTime?.getTime() ?? this.endTime?.getTime() ?? this.rangeBounds?.max; + if (startMs == null || endMs == null) return; + const handle = Math.abs(timestamp - startMs) <= Math.abs(timestamp - endMs) ? "start" : "end"; + this._setDraftRangeFromTimestamp(handle, timestamp); + } + _handleRangeViewportPointerMove(ev) { + if (this._timelinePointerId != null || this._rangePointerId != null) return; + if (ev.composedPath().some((el) => el.tagName === "DP-RANGE-HANDLE")) return; + if (ev.target?.closest?.(".range-period-button")) return; + if (ev.target?.closest?.(".range-selection")) return; + const timestamp = this._timestampFromClientX(ev.clientX); + if (timestamp == null || !this.rangeBounds) return; + const unit = this.rangeBounds.config?.labelUnit || this._getEffectiveSnapUnit(); + if (!unit) return; + this._setHoveredPeriodRange(unit, new Date(timestamp)); + } + _handleRangeViewportPointerLeave() { + if (this._timelinePointerId != null || this._rangePointerId != null) return; + if (this._hoveredPeriodRange) { + this._hoveredPeriodRange = null; + this.dispatchEvent(new CustomEvent("dp-range-period-leave", { + bubbles: true, + composed: true + })); + } + } + }, _defineProperty(_RangeTimeline, "styles", styles$26), _RangeTimeline); + __decorate([n$1({ type: Object })], RangeTimeline.prototype, "startTime", null); + __decorate([n$1({ type: Object })], RangeTimeline.prototype, "endTime", null); + __decorate([n$1({ type: Object })], RangeTimeline.prototype, "rangeBounds", null); + __decorate([n$1({ type: String })], RangeTimeline.prototype, "zoomLevel", null); + __decorate([n$1({ type: String })], RangeTimeline.prototype, "dateSnapping", null); + __decorate([n$1({ type: Boolean })], RangeTimeline.prototype, "isLiveEdge", null); + __decorate([n$1({ type: String })], RangeTimeline.prototype, "locale", null); + RangeTimeline = __decorate([localized()], RangeTimeline); + customElements.define("range-timeline", RangeTimeline); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/date-window-dialog/date-window-dialog.ts + var _DateWindowDialog, _open_accessor_storage$1, _heading_accessor_storage, _name_accessor_storage$1, _startValue_accessor_storage, _endValue_accessor_storage, _showDelete_accessor_storage, _showShortcuts_accessor_storage, _submitLabel_accessor_storage, _rangeBounds_accessor_storage$2, _zoomLevel_accessor_storage$2, _dateSnapping_accessor_storage$2; + var DateWindowDialog = (_open_accessor_storage$1 = /* @__PURE__ */ new WeakMap(), _heading_accessor_storage = /* @__PURE__ */ new WeakMap(), _name_accessor_storage$1 = /* @__PURE__ */ new WeakMap(), _startValue_accessor_storage = /* @__PURE__ */ new WeakMap(), _endValue_accessor_storage = /* @__PURE__ */ new WeakMap(), _showDelete_accessor_storage = /* @__PURE__ */ new WeakMap(), _showShortcuts_accessor_storage = /* @__PURE__ */ new WeakMap(), _submitLabel_accessor_storage = /* @__PURE__ */ new WeakMap(), _rangeBounds_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _zoomLevel_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _dateSnapping_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _DateWindowDialog = class DateWindowDialog extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _open_accessor_storage$1, false); + _classPrivateFieldInitSpec(this, _heading_accessor_storage, "Add date window"); + _classPrivateFieldInitSpec(this, _name_accessor_storage$1, ""); + _classPrivateFieldInitSpec(this, _startValue_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _endValue_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _showDelete_accessor_storage, false); + _classPrivateFieldInitSpec(this, _showShortcuts_accessor_storage, false); + _classPrivateFieldInitSpec(this, _submitLabel_accessor_storage, "Create date window"); + _classPrivateFieldInitSpec(this, _rangeBounds_accessor_storage$2, null); + _classPrivateFieldInitSpec(this, _zoomLevel_accessor_storage$2, "auto"); + _classPrivateFieldInitSpec(this, _dateSnapping_accessor_storage$2, "hour"); + } + get open() { + return _classPrivateFieldGet2(_open_accessor_storage$1, this); + } + set open(value) { + _classPrivateFieldSet2(_open_accessor_storage$1, this, value); + } + get heading() { + return _classPrivateFieldGet2(_heading_accessor_storage, this); + } + set heading(value) { + _classPrivateFieldSet2(_heading_accessor_storage, this, value); + } + get name() { + return _classPrivateFieldGet2(_name_accessor_storage$1, this); + } + set name(value) { + _classPrivateFieldSet2(_name_accessor_storage$1, this, value); + } + get startValue() { + return _classPrivateFieldGet2(_startValue_accessor_storage, this); + } + set startValue(value) { + _classPrivateFieldSet2(_startValue_accessor_storage, this, value); + } + get endValue() { + return _classPrivateFieldGet2(_endValue_accessor_storage, this); + } + set endValue(value) { + _classPrivateFieldSet2(_endValue_accessor_storage, this, value); + } + get showDelete() { + return _classPrivateFieldGet2(_showDelete_accessor_storage, this); + } + set showDelete(value) { + _classPrivateFieldSet2(_showDelete_accessor_storage, this, value); + } + get showShortcuts() { + return _classPrivateFieldGet2(_showShortcuts_accessor_storage, this); + } + set showShortcuts(value) { + _classPrivateFieldSet2(_showShortcuts_accessor_storage, this, value); + } + get submitLabel() { + return _classPrivateFieldGet2(_submitLabel_accessor_storage, this); + } + set submitLabel(value) { + _classPrivateFieldSet2(_submitLabel_accessor_storage, this, value); + } + get rangeBounds() { + return _classPrivateFieldGet2(_rangeBounds_accessor_storage$2, this); + } + set rangeBounds(value) { + _classPrivateFieldSet2(_rangeBounds_accessor_storage$2, this, value); + } + get zoomLevel() { + return _classPrivateFieldGet2(_zoomLevel_accessor_storage$2, this); + } + set zoomLevel(value) { + _classPrivateFieldSet2(_zoomLevel_accessor_storage$2, this, value); + } + get dateSnapping() { + return _classPrivateFieldGet2(_dateSnapping_accessor_storage$2, this); + } + set dateSnapping(value) { + _classPrivateFieldSet2(_dateSnapping_accessor_storage$2, this, value); + } + /** Shake the dialog — call this when the parent detects a validation error. */ + shake() { + const dialog = this.shadowRoot?.querySelector("ha-dialog"); + if (!dialog) return; + dialog.classList.remove("dp-shaking"); + dialog.offsetWidth; + dialog.classList.add("dp-shaking"); + dialog.addEventListener("animationend", () => dialog.classList.remove("dp-shaking"), { once: true }); + } + _emit(name, detail = {}) { + this.dispatchEvent(new CustomEvent(name, { + detail, + bubbles: true, + composed: true + })); + } + _onDialogClosed() { + this._emit("dp-window-close"); + } + _onCancel() { + this._emit("dp-window-close"); + } + _onSubmit() { + const nameInput = this.shadowRoot?.querySelector("#date-window-name"); + const startInput = this.shadowRoot?.querySelector("#date-window-start"); + const endInput = this.shadowRoot?.querySelector("#date-window-end"); + const nameVal = nameInput?.value ?? this.name; + this._emit("dp-window-submit", { + name: String(nameVal ?? "").trim(), + start: startInput?.value ?? this.startValue, + end: endInput?.value ?? this.endValue + }); + } + _onDelete() { + this._emit("dp-window-delete"); + } + _onPreviousShortcut() { + this._emit("dp-window-shortcut", { direction: -1 }); + } + _onNextShortcut() { + this._emit("dp-window-shortcut", { direction: 1 }); + } + _onDateChange() { + const startInput = this.shadowRoot?.querySelector("#date-window-start"); + const endInput = this.shadowRoot?.querySelector("#date-window-end"); + this._emit("dp-window-date-change", { + start: startInput?.value ?? "", + end: endInput?.value ?? "" + }); + } + _onRangeCommit(ev) { + const { start, end } = ev.detail ?? {}; + if (!start || !end) return; + const fmt = (ms) => { + const d = new Date(ms); + const pad = (n) => String(n).padStart(2, "0"); + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`; + }; + const startStr = fmt(start); + const endStr = fmt(end); + const startInput = this.shadowRoot?.querySelector("#date-window-start"); + const endInput = this.shadowRoot?.querySelector("#date-window-end"); + if (startInput) startInput.value = startStr; + if (endInput) endInput.value = endStr; + this._emit("dp-window-date-change", { + start: startStr, + end: endStr + }); + } + /** Parse the startValue / endValue strings to Date objects for the timeline. */ + _parseValueToDate(value) { + if (!value) return null; + const d = new Date(value); + return Number.isNaN(d.getTime()) ? null : d; + } + render() { + return b` ${this.heading}
- ${msg( - "A date window saves a named date range as a tab, so you can quickly preview it against the selected range or jump the chart back to it later." - )} + ${msg("A date window saves a named date range as a tab, so you can quickly preview it against the selected range or jump the chart back to it later.")}
@@ -24102,36 +23986,45 @@ ${content.alert}` : "",
`; - } - } - _init$j = __decoratorStart$j(_a$j); - _open$1 = /* @__PURE__ */ new WeakMap(); - _heading = /* @__PURE__ */ new WeakMap(); - _name$1 = /* @__PURE__ */ new WeakMap(); - _startValue = /* @__PURE__ */ new WeakMap(); - _endValue = /* @__PURE__ */ new WeakMap(); - _showDelete = /* @__PURE__ */ new WeakMap(); - _showShortcuts = /* @__PURE__ */ new WeakMap(); - _submitLabel = /* @__PURE__ */ new WeakMap(); - _rangeBounds$2 = /* @__PURE__ */ new WeakMap(); - _zoomLevel$2 = /* @__PURE__ */ new WeakMap(); - _dateSnapping$2 = /* @__PURE__ */ new WeakMap(); - __decorateElement$j(_init$j, 4, "open", _open_dec$1, DateWindowDialog, _open$1); - __decorateElement$j(_init$j, 4, "heading", _heading_dec, DateWindowDialog, _heading); - __decorateElement$j(_init$j, 4, "name", _name_dec$1, DateWindowDialog, _name$1); - __decorateElement$j(_init$j, 4, "startValue", _startValue_dec, DateWindowDialog, _startValue); - __decorateElement$j(_init$j, 4, "endValue", _endValue_dec, DateWindowDialog, _endValue); - __decorateElement$j(_init$j, 4, "showDelete", _showDelete_dec, DateWindowDialog, _showDelete); - __decorateElement$j(_init$j, 4, "showShortcuts", _showShortcuts_dec, DateWindowDialog, _showShortcuts); - __decorateElement$j(_init$j, 4, "submitLabel", _submitLabel_dec, DateWindowDialog, _submitLabel); - __decorateElement$j(_init$j, 4, "rangeBounds", _rangeBounds_dec$2, DateWindowDialog, _rangeBounds$2); - __decorateElement$j(_init$j, 4, "zoomLevel", _zoomLevel_dec$2, DateWindowDialog, _zoomLevel$2); - __decorateElement$j(_init$j, 4, "dateSnapping", _dateSnapping_dec$2, DateWindowDialog, _dateSnapping$2); - DateWindowDialog = __decorateElement$j(_init$j, 0, "DateWindowDialog", _DateWindowDialog_decorators, DateWindowDialog); - __publicField$q(DateWindowDialog, "styles", styles$r); - __runInitializers$j(_init$j, 1, DateWindowDialog); - customElements.define("date-window-dialog", DateWindowDialog); - const styles$o = i$5` + } + }, _defineProperty(_DateWindowDialog, "styles", styles$27), _DateWindowDialog); + __decorate([n$1({ type: Boolean })], DateWindowDialog.prototype, "open", null); + __decorate([n$1({ type: String })], DateWindowDialog.prototype, "heading", null); + __decorate([n$1({ type: String })], DateWindowDialog.prototype, "name", null); + __decorate([n$1({ + type: String, + attribute: "start-value" + })], DateWindowDialog.prototype, "startValue", null); + __decorate([n$1({ + type: String, + attribute: "end-value" + })], DateWindowDialog.prototype, "endValue", null); + __decorate([n$1({ + type: Boolean, + attribute: "show-delete" + })], DateWindowDialog.prototype, "showDelete", null); + __decorate([n$1({ + type: Boolean, + attribute: "show-shortcuts" + })], DateWindowDialog.prototype, "showShortcuts", null); + __decorate([n$1({ + type: String, + attribute: "submit-label" + })], DateWindowDialog.prototype, "submitLabel", null); + __decorate([n$1({ type: Object })], DateWindowDialog.prototype, "rangeBounds", null); + __decorate([n$1({ + type: String, + attribute: "zoom-level" + })], DateWindowDialog.prototype, "zoomLevel", null); + __decorate([n$1({ + type: String, + attribute: "date-snapping" + })], DateWindowDialog.prototype, "dateSnapping", null); + DateWindowDialog = __decorate([localized()], DateWindowDialog); + customElements.define("date-window-dialog", DateWindowDialog); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/resizable-panes/resizable-panes.styles.ts + var styles$24 = i$5` :host { display: grid; overflow: hidden; @@ -24259,138 +24152,130 @@ ${content.alert}` : "", outline: none; } `; - var __create$i = Object.create; - var __defProp$p = Object.defineProperty; - var __getOwnPropDesc$i = Object.getOwnPropertyDescriptor; - var __knownSymbol$i = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$i = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$p = (obj, key, value) => key in obj ? __defProp$p(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$i = (base) => [, , , __create$i(base?.[__knownSymbol$i("metadata")] ?? null)]; - var __decoratorStrings$i = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$i = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$i("Function expected") : fn; - var __decoratorContext$i = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$i[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$i("Already initialized") : fns.push(__expectFn$i(fn || null)) }); - var __decoratorMetadata$i = (array, target) => __defNormalProp$p(target, __knownSymbol$i("metadata"), array[3]); - var __runInitializers$i = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$i = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$i[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$i({ get [name]() { - return __privateGet$h(this, extra); - }, set [name](x2) { - return __privateSet$h(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$i(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$i(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$i("Object expected"); - else __expectFn$i(fn = it.get) && (desc.get = fn), __expectFn$i(fn = it.set) && (desc.set = fn), __expectFn$i(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$p(target, name, desc), target; - }; - var __publicField$p = (obj, key, value) => __defNormalProp$p(obj, typeof key !== "symbol" ? key + "" : key, value); - var __accessCheck$h = (obj, member, msg2) => member.has(obj) || __typeError$i("Cannot " + msg2); - var __privateGet$h = (obj, member, getter) => (__accessCheck$h(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$h = (obj, member, value) => member.has(obj) ? __typeError$i("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$h = (obj, member, value, setter) => (__accessCheck$h(obj, member, "write to private field"), member.set(obj, value), value); - var _secondHidden_dec, _max_dec, _min_dec, _ratio_dec, _direction_dec, _a$i, _init$i, _direction, _ratio, _min, _max, _secondHidden; - class ResizablePanes extends (_a$i = i$2, _direction_dec = [n({ type: String, reflect: true })], _ratio_dec = [n({ type: Number })], _min_dec = [n({ type: Number })], _max_dec = [n({ type: Number })], _secondHidden_dec = [n({ type: Boolean, attribute: "second-hidden", reflect: true })], _a$i) { - constructor() { - super(...arguments); - __privateAdd$h(this, _direction, __runInitializers$i(_init$i, 8, this, "vertical")), __runInitializers$i(_init$i, 11, this); - __privateAdd$h(this, _ratio, __runInitializers$i(_init$i, 12, this, 0.5)), __runInitializers$i(_init$i, 15, this); - __privateAdd$h(this, _min, __runInitializers$i(_init$i, 16, this, 0.25)), __runInitializers$i(_init$i, 19, this); - __privateAdd$h(this, _max, __runInitializers$i(_init$i, 20, this, 0.75)), __runInitializers$i(_init$i, 23, this); - __privateAdd$h(this, _secondHidden, __runInitializers$i(_init$i, 24, this, false)), __runInitializers$i(_init$i, 27, this); - __publicField$p(this, "_pointerId", null); - __publicField$p(this, "_splitterEl", null); - __publicField$p(this, "_onPointerDown", (ev) => { - if (ev.button !== 0) return; - ev.preventDefault(); - this._pointerId = ev.pointerId; - this._splitterEl?.classList.add("dragging"); - window.addEventListener("pointermove", this._onPointerMove); - window.addEventListener("pointerup", this._onPointerUp); - window.addEventListener("pointercancel", this._onPointerUp); - }); - __publicField$p(this, "_onPointerMove", (ev) => { - if (this._pointerId == null || ev.pointerId !== this._pointerId) return; - ev.preventDefault(); - const rect = this.getBoundingClientRect(); - const totalSize = this.direction === "horizontal" ? rect.width : rect.height; - if (!totalSize) return; - const pointerOffset = this.direction === "horizontal" ? ev.clientX - rect.left : ev.clientY - rect.top; - const clamped = Math.min( - Math.max(this.min, pointerOffset / totalSize), - this.max - ); - this.ratio = clamped; - this._applyRatio(); - this.dispatchEvent( - new CustomEvent("dp-panes-resize", { - detail: { ratio: clamped }, - bubbles: true, - composed: true - }) - ); - }); - __publicField$p(this, "_onPointerUp", (ev) => { - if (this._pointerId == null || ev.pointerId !== this._pointerId) return; - this._pointerId = null; - this._splitterEl?.classList.remove("dragging"); - window.removeEventListener("pointermove", this._onPointerMove); - window.removeEventListener("pointerup", this._onPointerUp); - window.removeEventListener("pointercancel", this._onPointerUp); - this.dispatchEvent( - new CustomEvent("dp-panes-resize", { - detail: { ratio: this.ratio, committed: true }, - bubbles: true, - composed: true - }) - ); - }); - } - // ── Lifecycle ────────────────────────────────────────────────────────────── - firstUpdated() { - this._splitterEl = this.shadowRoot?.querySelector(".pane-splitter") ?? null; - this._applyRatio(); - } - disconnectedCallback() { - super.disconnectedCallback(); - if (this._pointerId != null) { - window.removeEventListener("pointermove", this._onPointerMove); - window.removeEventListener("pointerup", this._onPointerUp); - window.removeEventListener("pointercancel", this._onPointerUp); - this._splitterEl?.classList.remove("dragging"); - this._pointerId = null; - } - } - updated(changed) { - if (changed.has("ratio") || changed.has("direction") || changed.has("secondHidden")) { - this._applyRatio(); - } - } - // ── Layout ───────────────────────────────────────────────────────────────── - _applyRatio() { - this.style.setProperty( - "--dp-panes-top-size", - `${Math.round(this.ratio * 1e3) / 10}%` - ); - } - // ── Render ───────────────────────────────────────────────────────────────── - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/resizable-panes/resizable-panes.ts + var _direction_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _ratio_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _min_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _max_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _secondHidden_accessor_storage = /* @__PURE__ */ new WeakMap(); + /** + * `resizable-panes` is a two-pane layout atom with a draggable splitter. + * + * The ratio (0..1) controls how much space the first pane takes. It can be + * clamped with `min` / `max` (also 0..1), or via pixel-based CSS custom + * properties `--dp-panes-min-first` / `--dp-panes-min-second` on the host. + * + * @fires dp-panes-resize - `{ ratio: number }` fired on each pointer-move frame + * and once more on pointer-up so the parent can persist. + * + * @slot first - Content for the first (top or left) pane + * @slot second - Content for the second (bottom or right) pane + */ + var ResizablePanes = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _direction_accessor_storage, "vertical"); + _classPrivateFieldInitSpec(this, _ratio_accessor_storage, .5); + _classPrivateFieldInitSpec(this, _min_accessor_storage, .25); + _classPrivateFieldInitSpec(this, _max_accessor_storage, .75); + _classPrivateFieldInitSpec(this, _secondHidden_accessor_storage, false); + _defineProperty(this, "_pointerId", null); + _defineProperty(this, "_splitterEl", null); + _defineProperty(this, "_onPointerDown", (ev) => { + if (ev.button !== 0) return; + ev.preventDefault(); + this._pointerId = ev.pointerId; + this._splitterEl?.classList.add("dragging"); + window.addEventListener("pointermove", this._onPointerMove); + window.addEventListener("pointerup", this._onPointerUp); + window.addEventListener("pointercancel", this._onPointerUp); + }); + _defineProperty(this, "_onPointerMove", (ev) => { + if (this._pointerId == null || ev.pointerId !== this._pointerId) return; + ev.preventDefault(); + const rect = this.getBoundingClientRect(); + const totalSize = this.direction === "horizontal" ? rect.width : rect.height; + if (!totalSize) return; + const pointerOffset = this.direction === "horizontal" ? ev.clientX - rect.left : ev.clientY - rect.top; + const clamped = Math.min(Math.max(this.min, pointerOffset / totalSize), this.max); + this.ratio = clamped; + this._applyRatio(); + this.dispatchEvent(new CustomEvent("dp-panes-resize", { + detail: { ratio: clamped }, + bubbles: true, + composed: true + })); + }); + _defineProperty(this, "_onPointerUp", (ev) => { + if (this._pointerId == null || ev.pointerId !== this._pointerId) return; + this._pointerId = null; + this._splitterEl?.classList.remove("dragging"); + window.removeEventListener("pointermove", this._onPointerMove); + window.removeEventListener("pointerup", this._onPointerUp); + window.removeEventListener("pointercancel", this._onPointerUp); + this.dispatchEvent(new CustomEvent("dp-panes-resize", { + detail: { + ratio: this.ratio, + committed: true + }, + bubbles: true, + composed: true + })); + }); + } + get direction() { + return _classPrivateFieldGet2(_direction_accessor_storage, this); + } + set direction(value) { + _classPrivateFieldSet2(_direction_accessor_storage, this, value); + } + get ratio() { + return _classPrivateFieldGet2(_ratio_accessor_storage, this); + } + set ratio(value) { + _classPrivateFieldSet2(_ratio_accessor_storage, this, value); + } + get min() { + return _classPrivateFieldGet2(_min_accessor_storage, this); + } + set min(value) { + _classPrivateFieldSet2(_min_accessor_storage, this, value); + } + get max() { + return _classPrivateFieldGet2(_max_accessor_storage, this); + } + set max(value) { + _classPrivateFieldSet2(_max_accessor_storage, this, value); + } + get secondHidden() { + return _classPrivateFieldGet2(_secondHidden_accessor_storage, this); + } + set secondHidden(value) { + _classPrivateFieldSet2(_secondHidden_accessor_storage, this, value); + } + firstUpdated() { + this._splitterEl = this.shadowRoot?.querySelector(".pane-splitter") ?? null; + this._applyRatio(); + } + disconnectedCallback() { + super.disconnectedCallback(); + if (this._pointerId != null) { + window.removeEventListener("pointermove", this._onPointerMove); + window.removeEventListener("pointerup", this._onPointerUp); + window.removeEventListener("pointercancel", this._onPointerUp); + this._splitterEl?.classList.remove("dragging"); + this._pointerId = null; + } + } + updated(changed) { + if (changed.has("ratio") || changed.has("direction") || changed.has("secondHidden")) this._applyRatio(); + } + _applyRatio() { + this.style.setProperty("--dp-panes-top-size", `${Math.round(this.ratio * 1e3) / 10}%`); + } + render() { + return b`
${!this.secondHidden ? b`
` : null} `; - } - } - _init$i = __decoratorStart$i(_a$i); - _direction = /* @__PURE__ */ new WeakMap(); - _ratio = /* @__PURE__ */ new WeakMap(); - _min = /* @__PURE__ */ new WeakMap(); - _max = /* @__PURE__ */ new WeakMap(); - _secondHidden = /* @__PURE__ */ new WeakMap(); - __decorateElement$i(_init$i, 4, "direction", _direction_dec, ResizablePanes, _direction); - __decorateElement$i(_init$i, 4, "ratio", _ratio_dec, ResizablePanes, _ratio); - __decorateElement$i(_init$i, 4, "min", _min_dec, ResizablePanes, _min); - __decorateElement$i(_init$i, 4, "max", _max_dec, ResizablePanes, _max); - __decorateElement$i(_init$i, 4, "secondHidden", _secondHidden_dec, ResizablePanes, _secondHidden); - __decoratorMetadata$i(_init$i, ResizablePanes); - __publicField$p(ResizablePanes, "styles", styles$o); - customElements.define("resizable-panes", ResizablePanes); - var __defProp$o = Object.defineProperty; - var __defNormalProp$o = (obj, key, value) => key in obj ? __defProp$o(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$o = (obj, key, value) => __defNormalProp$o(obj, typeof key !== "symbol" ? key + "" : key, value); - class HistoryChart extends HTMLElement { - constructor() { - super(...arguments); - __publicField$o(this, "_configKey", ""); - __publicField$o(this, "_config", null); - __publicField$o(this, "_hass", null); - __publicField$o(this, "_chartEl", null); - } - // ── Construction ─────────────────────────────────────────────────────────── - connectedCallback() { - if (!this._chartEl) { - const card = document.createElement( - "hass-datapoints-history-card" - ); - card.style.cssText = "flex:1 1 auto;min-width:0;min-height:0;width:100%;height:100%;"; - this.appendChild(card); - this._chartEl = card; - this._applyConfig(); - if (this._hass !== null && this._chartEl) { - this._chartEl.hass = this._hass; - } - } - } - // ── Public API ───────────────────────────────────────────────────────────── - /** Direct reference to the inner `hass-datapoints-history-card` element. */ - get chartEl() { - return this._chartEl; - } - get config() { - return this._config; - } - set config(value) { - this._config = value; - this._applyConfig(); - } - get hass() { - return this._hass; - } - set hass(value) { - this._hass = value; - if (this._chartEl) { - this._chartEl.hass = value; - } - } - /** - * Passes an external committed zoom range to the inner card. - */ - setExternalZoomRange(range) { - this._chartEl?.setExternalZoomRange?.(range); - } - // ── Internal helpers ─────────────────────────────────────────────────────── - _applyConfig() { - if (!this._chartEl || !this._config) return; - const nextKey = JSON.stringify(this._config); - if (nextKey !== this._configKey) { - this._chartEl.setConfig(this._config); - this._configKey = nextKey; - } - } - } - customElements.define("history-chart", HistoryChart); - const styles$n = i$5` + } + }; + _defineProperty(ResizablePanes, "styles", styles$24); + __decorate([n$1({ + type: String, + reflect: true + })], ResizablePanes.prototype, "direction", null); + __decorate([n$1({ type: Number })], ResizablePanes.prototype, "ratio", null); + __decorate([n$1({ type: Number })], ResizablePanes.prototype, "min", null); + __decorate([n$1({ type: Number })], ResizablePanes.prototype, "max", null); + __decorate([n$1({ + type: Boolean, + attribute: "second-hidden", + reflect: true + })], ResizablePanes.prototype, "secondHidden", null); + customElements.define("resizable-panes", ResizablePanes); + //#endregion + //#region custom_components/hass_datapoints/src/molecules/history-chart/history-chart.ts + var HistoryChart = class extends HTMLElement { + constructor(..._args) { + super(..._args); + _defineProperty(this, "_configKey", ""); + _defineProperty(this, "_config", null); + _defineProperty(this, "_hass", null); + _defineProperty(this, "_chartEl", null); + } + connectedCallback() { + if (!this._chartEl) { + const card = document.createElement("hass-datapoints-history-card"); + card.style.cssText = "flex:1 1 auto;min-width:0;min-height:0;width:100%;height:100%;"; + this.appendChild(card); + this._chartEl = card; + this._applyConfig(); + if (this._hass !== null && this._chartEl) this._chartEl.hass = this._hass; + } + } + /** Direct reference to the inner `hass-datapoints-history-card` element. */ + get chartEl() { + return this._chartEl; + } + get config() { + return this._config; + } + set config(value) { + this._config = value; + this._applyConfig(); + } + get hass() { + return this._hass; + } + set hass(value) { + this._hass = value; + if (this._chartEl) this._chartEl.hass = value; + } + /** + * Passes an external committed zoom range to the inner card. + */ + setExternalZoomRange(range) { + this._chartEl?.setExternalZoomRange?.(range); + } + _applyConfig() { + if (!this._chartEl || !this._config) return; + const nextKey = JSON.stringify(this._config); + if (nextKey !== this._configKey) { + this._chartEl.setConfig(this._config); + this._configKey = nextKey; + } + } + }; + customElements.define("history-chart", HistoryChart); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/panel-shell/panel-shell.styles.ts + var styles$23 = i$5` :host { display: block; height: 100%; @@ -24824,6 +24701,7 @@ ${content.alert}` : "", position: absolute; top: 0; left: 0; + min-width: min(380px, 85vw); width: min(380px, 85vw); height: 100%; z-index: 10; @@ -24889,8 +24767,16 @@ ${content.alert}` : "", display: none; } } + + @media (max-width: 545px) { + .page-sidebar { + width: min(380px, 95vw); + } + } `; - const styles$m = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/molecules/floating-menu/floating-menu.styles.ts + var styles$22 = i$5` :host { display: contents; } @@ -24922,97 +24808,65 @@ ${content.alert}` : "", display: none; } `; - var __create$h = Object.create; - var __defProp$n = Object.defineProperty; - var __getOwnPropDesc$h = Object.getOwnPropertyDescriptor; - var __knownSymbol$h = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$h = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$n = (obj, key, value) => key in obj ? __defProp$n(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$h = (base) => [, , , __create$h(base?.[__knownSymbol$h("metadata")] ?? null)]; - var __decoratorStrings$h = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$h = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$h("Function expected") : fn; - var __decoratorContext$h = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$h[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$h("Already initialized") : fns.push(__expectFn$h(fn || null)) }); - var __decoratorMetadata$h = (array, target) => __defNormalProp$n(target, __knownSymbol$h("metadata"), array[3]); - var __runInitializers$h = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$h = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$h[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$h({ get [name]() { - return __privateGet$g(this, extra); - }, set [name](x2) { - return __privateSet$g(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$h(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$h(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$h("Object expected"); - else __expectFn$h(fn = it.get) && (desc.get = fn), __expectFn$h(fn = it.set) && (desc.set = fn), __expectFn$h(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$n(target, name, desc), target; - }; - var __publicField$n = (obj, key, value) => __defNormalProp$n(obj, key + "", value); - var __accessCheck$g = (obj, member, msg2) => member.has(obj) || __typeError$h("Cannot " + msg2); - var __privateGet$g = (obj, member, getter) => (__accessCheck$g(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$g = (obj, member, value) => member.has(obj) ? __typeError$h("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$g = (obj, member, value, setter) => (__accessCheck$g(obj, member, "write to private field"), member.set(obj, value), value); - var _open_dec, _a$h, _init$h, _open; - class FloatingMenu extends (_a$h = i$2, _open_dec = [n({ type: Boolean, reflect: true })], _a$h) { - constructor() { - super(...arguments); - __privateAdd$g(this, _open, __runInitializers$h(_init$h, 8, this, false)), __runInitializers$h(_init$h, 11, this); - } - connectedCallback() { - super.connectedCallback(); - this._onPointerDown = this._onPointerDown.bind(this); - window.addEventListener("pointerdown", this._onPointerDown, true); - } - disconnectedCallback() { - super.disconnectedCallback(); - window.removeEventListener("pointerdown", this._onPointerDown, true); - } - _onPointerDown(e2) { - if (!this.open) { - return; - } - const path = e2.composedPath(); - const clickedInside = path.some((node) => node === this); - if (!clickedInside) { - this.dispatchEvent( - new CustomEvent("dp-menu-close", { - detail: {}, - bubbles: true, - composed: true - }) - ); - } - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/molecules/floating-menu/floating-menu.ts + var _open_accessor_storage = /* @__PURE__ */ new WeakMap(); + /** + * `floating-menu` renders a positioned floating overlay panel. + * + * The parent is responsible for positioning the menu by setting the CSS custom + * properties `--floating-menu-top` and `--floating-menu-left` on this element, + * and for toggling `open` in response to the trigger button. + * + * Content is projected via the default ``. + * + * @fires dp-menu-close - `{}` fired when the user clicks outside the menu while it is open + */ + var FloatingMenu = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _open_accessor_storage, false); + } + get open() { + return _classPrivateFieldGet2(_open_accessor_storage, this); + } + set open(value) { + _classPrivateFieldSet2(_open_accessor_storage, this, value); + } + connectedCallback() { + super.connectedCallback(); + this._onPointerDown = this._onPointerDown.bind(this); + window.addEventListener("pointerdown", this._onPointerDown, true); + } + disconnectedCallback() { + super.disconnectedCallback(); + window.removeEventListener("pointerdown", this._onPointerDown, true); + } + _onPointerDown(e) { + if (!this.open) return; + if (!e.composedPath().some((node) => node === this)) this.dispatchEvent(new CustomEvent("dp-menu-close", { + detail: {}, + bubbles: true, + composed: true + })); + } + render() { + return b` `; - } - } - _init$h = __decoratorStart$h(_a$h); - _open = /* @__PURE__ */ new WeakMap(); - __decorateElement$h(_init$h, 4, "open", _open_dec, FloatingMenu, _open); - __decoratorMetadata$h(_init$h, FloatingMenu); - __publicField$n(FloatingMenu, "styles", styles$m); - customElements.define("floating-menu", FloatingMenu); - const styles$l = i$5` + } + }; + _defineProperty(FloatingMenu, "styles", styles$22); + __decorate([n$1({ + type: Boolean, + reflect: true + })], FloatingMenu.prototype, "open", null); + customElements.define("floating-menu", FloatingMenu); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/page-menu-item/page-menu-item.styles.ts + var styles$21 = i$5` :host { display: block; } @@ -25053,260 +24907,199 @@ ${content.alert}` : "", flex: 0 0 auto; } `; - var __create$g = Object.create; - var __defProp$m = Object.defineProperty; - var __getOwnPropDesc$g = Object.getOwnPropertyDescriptor; - var __knownSymbol$g = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$g = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$m = (obj, key, value) => key in obj ? __defProp$m(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$g = (base) => [, , , __create$g(base?.[__knownSymbol$g("metadata")] ?? null)]; - var __decoratorStrings$g = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$g = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$g("Function expected") : fn; - var __decoratorContext$g = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$g[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$g("Already initialized") : fns.push(__expectFn$g(fn || null)) }); - var __decoratorMetadata$g = (array, target) => __defNormalProp$m(target, __knownSymbol$g("metadata"), array[3]); - var __runInitializers$g = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$g = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$g[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$g({ get [name]() { - return __privateGet$f(this, extra); - }, set [name](x2) { - return __privateSet$f(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$g(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$g(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$g("Object expected"); - else __expectFn$g(fn = it.get) && (desc.get = fn), __expectFn$g(fn = it.set) && (desc.set = fn), __expectFn$g(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$m(target, name, desc), target; - }; - var __publicField$m = (obj, key, value) => __defNormalProp$m(obj, key + "", value); - var __accessCheck$f = (obj, member, msg2) => member.has(obj) || __typeError$g("Cannot " + msg2); - var __privateGet$f = (obj, member, getter) => (__accessCheck$f(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$f = (obj, member, value) => member.has(obj) ? __typeError$g("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$f = (obj, member, value, setter) => (__accessCheck$f(obj, member, "write to private field"), member.set(obj, value), value); - var _disabled_dec, _label_dec$5, _icon_dec, _a$g, _init$g, _icon, _label$5, _disabled; - class PageMenuItem extends (_a$g = i$2, _icon_dec = [n({ type: String })], _label_dec$5 = [n({ type: String })], _disabled_dec = [n({ type: Boolean })], _a$g) { - constructor() { - super(...arguments); - __privateAdd$f(this, _icon, __runInitializers$g(_init$g, 8, this, "")), __runInitializers$g(_init$g, 11, this); - __privateAdd$f(this, _label$5, __runInitializers$g(_init$g, 12, this, "")), __runInitializers$g(_init$g, 15, this); - __privateAdd$f(this, _disabled, __runInitializers$g(_init$g, 16, this, false)), __runInitializers$g(_init$g, 19, this); - } - _onClick() { - if (this.disabled) { - return; - } - this.dispatchEvent( - new CustomEvent("dp-menu-action", { - bubbles: true, - composed: true - }) - ); - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/page-menu-item/page-menu-item.ts + var _icon_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _label_accessor_storage$5 = /* @__PURE__ */ new WeakMap(); + var _disabled_accessor_storage = /* @__PURE__ */ new WeakMap(); + var PageMenuItem = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _icon_accessor_storage$1, ""); + _classPrivateFieldInitSpec(this, _label_accessor_storage$5, ""); + _classPrivateFieldInitSpec(this, _disabled_accessor_storage, false); + } + get icon() { + return _classPrivateFieldGet2(_icon_accessor_storage$1, this); + } + set icon(value) { + _classPrivateFieldSet2(_icon_accessor_storage$1, this, value); + } + get label() { + return _classPrivateFieldGet2(_label_accessor_storage$5, this); + } + set label(value) { + _classPrivateFieldSet2(_label_accessor_storage$5, this, value); + } + get disabled() { + return _classPrivateFieldGet2(_disabled_accessor_storage, this); + } + set disabled(value) { + _classPrivateFieldSet2(_disabled_accessor_storage, this, value); + } + _onClick() { + if (this.disabled) return; + this.dispatchEvent(new CustomEvent("dp-menu-action", { + bubbles: true, + composed: true + })); + } + render() { + return b` `; - } - } - _init$g = __decoratorStart$g(_a$g); - _icon = /* @__PURE__ */ new WeakMap(); - _label$5 = /* @__PURE__ */ new WeakMap(); - _disabled = /* @__PURE__ */ new WeakMap(); - __decorateElement$g(_init$g, 4, "icon", _icon_dec, PageMenuItem, _icon); - __decorateElement$g(_init$g, 4, "label", _label_dec$5, PageMenuItem, _label$5); - __decorateElement$g(_init$g, 4, "disabled", _disabled_dec, PageMenuItem, _disabled); - __decoratorMetadata$g(_init$g, PageMenuItem); - __publicField$m(PageMenuItem, "styles", styles$l); - customElements.define("page-menu-item", PageMenuItem); - var __create$f = Object.create; - var __defProp$l = Object.defineProperty; - var __getOwnPropDesc$f = Object.getOwnPropertyDescriptor; - var __knownSymbol$f = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$f = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$l = (obj, key, value) => key in obj ? __defProp$l(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __name$3 = (target, value) => __defProp$l(target, "name", { value, configurable: true }); - var __decoratorStart$f = (base) => [, , , __create$f(base?.[__knownSymbol$f("metadata")] ?? null)]; - var __decoratorStrings$f = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$f = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$f("Function expected") : fn; - var __decoratorContext$f = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$f[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$f("Already initialized") : fns.push(__expectFn$f(fn || null)) }); - var __decoratorMetadata$f = (array, target) => __defNormalProp$l(target, __knownSymbol$f("metadata"), array[3]); - var __runInitializers$f = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$f = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = !!(flags & 8), p2 = !!(flags & 16); - var j2 = k2 > 3 ? array.length + 1 : k2 ? s2 ? 1 : 2 : 0, key = __decoratorStrings$f[k2 + 5]; - var initializers = k2 > 3 && (array[j2 - 1] = []), extraInitializers = array[j2] || (array[j2] = []); - var desc = k2 && (!p2 && !s2 && (target = target.prototype), k2 < 5 && (k2 > 3 || !p2) && __getOwnPropDesc$f(k2 < 4 ? target : { get [name]() { - return __privateGet$e(this, extra); - }, set [name](x2) { - return __privateSet$e(this, extra, x2); - } }, name)); - k2 ? p2 && k2 < 4 && __name$3(extra, (k2 > 2 ? "set " : k2 > 1 ? "get " : "") + name) : __name$3(target, name); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$f(k2, name, done = {}, array[3], extraInitializers); - if (k2) { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: p2 ? (x2) => __privateIn$2(target, x2) : (x2) => name in x2 }; - if (k2 ^ 3) access.get = p2 ? (x2) => (k2 ^ 1 ? __privateGet$e : __privateMethod$2)(x2, target, k2 ^ 4 ? extra : desc.get) : (x2) => x2[name]; - if (k2 > 2) access.set = p2 ? (x2, y2) => __privateSet$e(x2, target, y2, k2 ^ 4 ? extra : desc.set) : (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])(k2 ? k2 < 4 ? p2 ? extra : desc[key] : k2 > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; - if (k2 ^ 4 || it === void 0) __expectFn$f(it) && (k2 > 4 ? initializers.unshift(it) : k2 ? p2 ? extra = it : desc[key] = it : target = it); - else if (typeof it !== "object" || it === null) __typeError$f("Object expected"); - else __expectFn$f(fn = it.get) && (desc.get = fn), __expectFn$f(fn = it.set) && (desc.set = fn), __expectFn$f(fn = it.init) && initializers.unshift(fn); - } - return k2 || __decoratorMetadata$f(array, target), desc && __defProp$l(target, name, desc), p2 ? k2 ^ 4 ? extra : desc : target; - }; - var __publicField$l = (obj, key, value) => __defNormalProp$l(obj, key + "", value); - var __accessCheck$e = (obj, member, msg2) => member.has(obj) || __typeError$f("Cannot " + msg2); - var __privateIn$2 = (member, obj) => Object(obj) !== obj ? __typeError$f('Cannot use the "in" operator on this value') : member.has(obj); - var __privateGet$e = (obj, member, getter) => (__accessCheck$e(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); - var __privateAdd$e = (obj, member, value) => member.has(obj) ? __typeError$f("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$e = (obj, member, value, setter) => (__accessCheck$e(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); - var __privateMethod$2 = (obj, member, method) => (__accessCheck$e(obj, member, "access private method"), method); - var __pageMenuOpen_dec, _layoutMode_dec, _hasSavedState_dec, _sidebarCollapsed_dec$2, _narrow_dec, _hass_dec$5, _a$f, _PanelShell_decorators, _init$f, _hass$5, _narrow, _sidebarCollapsed$2, _hasSavedState, _layoutMode, __pageMenuOpen; - _PanelShell_decorators = [localized()]; - class PanelShell extends (_a$f = i$2, _hass_dec$5 = [n({ type: Object })], _narrow_dec = [n({ type: Boolean })], _sidebarCollapsed_dec$2 = [n({ type: Boolean, attribute: "sidebar-collapsed" })], _hasSavedState_dec = [n({ type: Boolean, attribute: "has-saved-state" })], _layoutMode_dec = [n({ type: String, attribute: "layout-mode" })], __pageMenuOpen_dec = [r()], _a$f) { - constructor() { - super(...arguments); - __privateAdd$e(this, _hass$5, __runInitializers$f(_init$f, 8, this, null)), __runInitializers$f(_init$f, 11, this); - __privateAdd$e(this, _narrow, __runInitializers$f(_init$f, 12, this, false)), __runInitializers$f(_init$f, 15, this); - __privateAdd$e(this, _sidebarCollapsed$2, __runInitializers$f(_init$f, 16, this, false)), __runInitializers$f(_init$f, 19, this); - __privateAdd$e(this, _hasSavedState, __runInitializers$f(_init$f, 20, this, false)), __runInitializers$f(_init$f, 23, this); - __privateAdd$e(this, _layoutMode, __runInitializers$f(_init$f, 24, this, "desktop")), __runInitializers$f(_init$f, 27, this); - __privateAdd$e(this, __pageMenuOpen, __runInitializers$f(_init$f, 28, this, false)), __runInitializers$f(_init$f, 31, this); - } - // ── Public API ───────────────────────────────────────────────────────────── - /** Returns the `#page-content` element for layout height calculations. */ - getPageContentEl() { - return this.shadowRoot?.querySelector("#page-content") ?? null; - } - /** Returns the main `#content` element. */ - getContentEl() { - return this.shadowRoot?.querySelector("#content") ?? null; - } - /** Returns the `#collapsed-target-popup` element for imperative positioning. */ - getTargetPopupEl() { - return this.shadowRoot?.querySelector("#collapsed-target-popup") ?? null; - } - /** Returns the `#collapsed-options-popup` element for imperative positioning. */ - getOptionsPopupEl() { - return this.shadowRoot?.querySelector("#collapsed-options-popup") ?? null; - } - /** - * Recalculates and applies the `--history-page-content-height` CSS property so - * the page-content area fills the remaining viewport height below the top bar. - */ - syncLayoutHeight() { - const pageContentEl = this.getPageContentEl(); - if (!pageContentEl) return; - const pageRect = pageContentEl.getBoundingClientRect(); - const hostRect = this.getBoundingClientRect(); - const availableHeight = Math.max(0, hostRect.bottom - pageRect.top); - if (availableHeight > 0) { - pageContentEl.style.setProperty( - "--history-page-content-height", - `${availableHeight}px` - ); - } - } - /** Closes the page options menu without emitting an event. */ - closePageMenu() { - if (this._pageMenuOpen) { - this._pageMenuOpen = false; - } - } - // ── Private helpers ──────────────────────────────────────────────────────── - _emit(name, detail = {}) { - this.dispatchEvent( - new CustomEvent(name, { detail, bubbles: true, composed: true }) - ); - } - _computeMenuPosition(anchorEl, menuWidth) { - const viewportPadding = 8; - const anchorRect = anchorEl.getBoundingClientRect(); - const left = Math.max( - viewportPadding, - Math.min( - anchorRect.right - menuWidth, - window.innerWidth - menuWidth - viewportPadding - ) - ); - const top = Math.max(viewportPadding, anchorRect.bottom + 8); - return { left, top }; - } - _togglePageMenu(force) { - const next = force !== void 0 ? force : !this._pageMenuOpen; - this._pageMenuOpen = next; - if (next) { - const menuEl = this.shadowRoot?.querySelector("#page-menu"); - const buttonEl = this.shadowRoot?.querySelector("#page-menu-button"); - if (menuEl && buttonEl) { - const { left, top } = this._computeMenuPosition( - buttonEl, - Math.max(220, menuEl.offsetWidth || 220) - ); - menuEl.style.setProperty("--floating-menu-left", `${left}px`); - menuEl.style.setProperty("--floating-menu-top", `${top}px`); - } - } - } - // ── Event handlers ───────────────────────────────────────────────────────── - _onPageMenuButtonClick() { - this._togglePageMenu(); - } - _onPageMenuClose() { - this._togglePageMenu(false); - } - _onMenuDownload() { - this._togglePageMenu(false); - this._emit("dp-shell-menu-download"); - } - _onMenuSave() { - this._togglePageMenu(false); - this._emit("dp-shell-menu-save"); - } - _onMenuRestore() { - this._togglePageMenu(false); - this._emit("dp-shell-menu-restore"); - } - _onMenuClear() { - this._togglePageMenu(false); - this._emit("dp-shell-menu-clear"); - } - _onSidebarToggle() { - this._emit("dp-shell-sidebar-toggle"); - } - _onScrimClick() { - this._emit("dp-shell-scrim-click"); - } - // ── Render ───────────────────────────────────────────────────────────────── - render() { - const isOverlay = this.layoutMode !== "desktop"; - const sidebarIcon = this.sidebarCollapsed ? "mdi:chevron-right" : "mdi:chevron-left"; - const sidebarLabel = this.sidebarCollapsed ? msg("Expand targets sidebar") : msg("Collapse targets sidebar"); - return b` + } + }; + _defineProperty(PageMenuItem, "styles", styles$21); + __decorate([n$1({ type: String })], PageMenuItem.prototype, "icon", null); + __decorate([n$1({ type: String })], PageMenuItem.prototype, "label", null); + __decorate([n$1({ type: Boolean })], PageMenuItem.prototype, "disabled", null); + customElements.define("page-menu-item", PageMenuItem); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/panel-shell/panel-shell.ts + var _PanelShell, _hass_accessor_storage$5, _narrow_accessor_storage, _sidebarCollapsed_accessor_storage$2, _hasSavedState_accessor_storage, _layoutMode_accessor_storage, _pageMenuOpen_accessor_storage; + var PanelShell = (_hass_accessor_storage$5 = /* @__PURE__ */ new WeakMap(), _narrow_accessor_storage = /* @__PURE__ */ new WeakMap(), _sidebarCollapsed_accessor_storage$2 = /* @__PURE__ */ new WeakMap(), _hasSavedState_accessor_storage = /* @__PURE__ */ new WeakMap(), _layoutMode_accessor_storage = /* @__PURE__ */ new WeakMap(), _pageMenuOpen_accessor_storage = /* @__PURE__ */ new WeakMap(), _PanelShell = class PanelShell extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _hass_accessor_storage$5, null); + _classPrivateFieldInitSpec(this, _narrow_accessor_storage, false); + _classPrivateFieldInitSpec(this, _sidebarCollapsed_accessor_storage$2, false); + _classPrivateFieldInitSpec(this, _hasSavedState_accessor_storage, false); + _classPrivateFieldInitSpec(this, _layoutMode_accessor_storage, "desktop"); + _classPrivateFieldInitSpec(this, _pageMenuOpen_accessor_storage, false); + } + get hass() { + return _classPrivateFieldGet2(_hass_accessor_storage$5, this); + } + set hass(value) { + _classPrivateFieldSet2(_hass_accessor_storage$5, this, value); + } + get narrow() { + return _classPrivateFieldGet2(_narrow_accessor_storage, this); + } + set narrow(value) { + _classPrivateFieldSet2(_narrow_accessor_storage, this, value); + } + get sidebarCollapsed() { + return _classPrivateFieldGet2(_sidebarCollapsed_accessor_storage$2, this); + } + set sidebarCollapsed(value) { + _classPrivateFieldSet2(_sidebarCollapsed_accessor_storage$2, this, value); + } + get hasSavedState() { + return _classPrivateFieldGet2(_hasSavedState_accessor_storage, this); + } + set hasSavedState(value) { + _classPrivateFieldSet2(_hasSavedState_accessor_storage, this, value); + } + get layoutMode() { + return _classPrivateFieldGet2(_layoutMode_accessor_storage, this); + } + set layoutMode(value) { + _classPrivateFieldSet2(_layoutMode_accessor_storage, this, value); + } + get _pageMenuOpen() { + return _classPrivateFieldGet2(_pageMenuOpen_accessor_storage, this); + } + set _pageMenuOpen(value) { + _classPrivateFieldSet2(_pageMenuOpen_accessor_storage, this, value); + } + /** Returns the `#page-content` element for layout height calculations. */ + getPageContentEl() { + return this.shadowRoot?.querySelector("#page-content") ?? null; + } + /** Returns the main `#content` element. */ + getContentEl() { + return this.shadowRoot?.querySelector("#content") ?? null; + } + /** Returns the `#collapsed-target-popup` element for imperative positioning. */ + getTargetPopupEl() { + return this.shadowRoot?.querySelector("#collapsed-target-popup") ?? null; + } + /** Returns the `#collapsed-options-popup` element for imperative positioning. */ + getOptionsPopupEl() { + return this.shadowRoot?.querySelector("#collapsed-options-popup") ?? null; + } + /** + * Recalculates and applies the `--history-page-content-height` CSS property so + * the page-content area fills the remaining viewport height below the top bar. + */ + syncLayoutHeight() { + const pageContentEl = this.getPageContentEl(); + if (!pageContentEl) return; + const pageRect = pageContentEl.getBoundingClientRect(); + const hostRect = this.getBoundingClientRect(); + const availableHeight = Math.max(0, hostRect.bottom - pageRect.top); + if (availableHeight > 0) pageContentEl.style.setProperty("--history-page-content-height", `${availableHeight}px`); + } + /** Closes the page options menu without emitting an event. */ + closePageMenu() { + if (this._pageMenuOpen) this._pageMenuOpen = false; + } + _emit(name, detail = {}) { + this.dispatchEvent(new CustomEvent(name, { + detail, + bubbles: true, + composed: true + })); + } + _computeMenuPosition(anchorEl, menuWidth) { + const viewportPadding = 8; + const anchorRect = anchorEl.getBoundingClientRect(); + return { + left: Math.max(viewportPadding, Math.min(anchorRect.right - menuWidth, window.innerWidth - menuWidth - viewportPadding)), + top: Math.max(viewportPadding, anchorRect.bottom + 8) + }; + } + _togglePageMenu(force) { + const next = force !== void 0 ? force : !this._pageMenuOpen; + this._pageMenuOpen = next; + if (next) { + const menuEl = this.shadowRoot?.querySelector("#page-menu"); + const buttonEl = this.shadowRoot?.querySelector("#page-menu-button"); + if (menuEl && buttonEl) { + const { left, top } = this._computeMenuPosition(buttonEl, Math.max(220, menuEl.offsetWidth || 220)); + menuEl.style.setProperty("--floating-menu-left", `${left}px`); + menuEl.style.setProperty("--floating-menu-top", `${top}px`); + } + } + } + _onPageMenuButtonClick() { + this._togglePageMenu(); + } + _onPageMenuClose() { + this._togglePageMenu(false); + } + _onMenuDownload() { + this._togglePageMenu(false); + this._emit("dp-shell-menu-download"); + } + _onMenuSave() { + this._togglePageMenu(false); + this._emit("dp-shell-menu-save"); + } + _onMenuRestore() { + this._togglePageMenu(false); + this._emit("dp-shell-menu-restore"); + } + _onMenuClear() { + this._togglePageMenu(false); + this._emit("dp-shell-menu-clear"); + } + _onSidebarToggle() { + this._emit("dp-shell-sidebar-toggle"); + } + _onScrimClick() { + this._emit("dp-shell-scrim-click"); + } + render() { + const isOverlay = this.layoutMode !== "desktop"; + const sidebarIcon = this.sidebarCollapsed ? "mdi:chevron-right" : "mdi:chevron-left"; + const sidebarLabel = this.sidebarCollapsed ? msg("Expand targets sidebar") : msg("Collapse targets sidebar"); + return b`
`; - } - } - _init$f = __decoratorStart$f(_a$f); - _hass$5 = /* @__PURE__ */ new WeakMap(); - _narrow = /* @__PURE__ */ new WeakMap(); - _sidebarCollapsed$2 = /* @__PURE__ */ new WeakMap(); - _hasSavedState = /* @__PURE__ */ new WeakMap(); - _layoutMode = /* @__PURE__ */ new WeakMap(); - __pageMenuOpen = /* @__PURE__ */ new WeakMap(); - __decorateElement$f(_init$f, 4, "hass", _hass_dec$5, PanelShell, _hass$5); - __decorateElement$f(_init$f, 4, "narrow", _narrow_dec, PanelShell, _narrow); - __decorateElement$f(_init$f, 4, "sidebarCollapsed", _sidebarCollapsed_dec$2, PanelShell, _sidebarCollapsed$2); - __decorateElement$f(_init$f, 4, "hasSavedState", _hasSavedState_dec, PanelShell, _hasSavedState); - __decorateElement$f(_init$f, 4, "layoutMode", _layoutMode_dec, PanelShell, _layoutMode); - __decorateElement$f(_init$f, 4, "_pageMenuOpen", __pageMenuOpen_dec, PanelShell, __pageMenuOpen); - PanelShell = __decorateElement$f(_init$f, 0, "PanelShell", _PanelShell_decorators, PanelShell); - __publicField$l(PanelShell, "styles", styles$n); - __runInitializers$f(_init$f, 1, PanelShell); - customElements.define("panel-shell", PanelShell); - const styles$k = i$5` + } + }, _defineProperty(_PanelShell, "styles", styles$23), _PanelShell); + __decorate([n$1({ type: Object })], PanelShell.prototype, "hass", null); + __decorate([n$1({ type: Boolean })], PanelShell.prototype, "narrow", null); + __decorate([n$1({ + type: Boolean, + attribute: "sidebar-collapsed" + })], PanelShell.prototype, "sidebarCollapsed", null); + __decorate([n$1({ + type: Boolean, + attribute: "has-saved-state" + })], PanelShell.prototype, "hasSavedState", null); + __decorate([n$1({ + type: String, + attribute: "layout-mode" + })], PanelShell.prototype, "layoutMode", null); + __decorate([r$1()], PanelShell.prototype, "_pageMenuOpen", null); + PanelShell = __decorate([localized()], PanelShell); + customElements.define("panel-shell", PanelShell); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/history-targets/history-targets.styles.ts + var styles$20 = i$5` :host { display: block; height: 100%; @@ -25466,7 +25261,7 @@ ${content.alert}` : "", } .history-target-rows { - width: calc(var(--sidebar-width-expanded) - var(--dp-spacing-lg) * 2); + width: 100%; } .sidebar-section-header { @@ -25653,132 +25448,117 @@ ${content.alert}` : "", gap: calc(var(--spacing, 8px) * 1.25); } `; - var __create$e = Object.create; - var __defProp$k = Object.defineProperty; - var __getOwnPropDesc$e = Object.getOwnPropertyDescriptor; - var __knownSymbol$e = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$e = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$k = (obj, key, value) => key in obj ? __defProp$k(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __name$2 = (target, value) => __defProp$k(target, "name", { value, configurable: true }); - var __decoratorStart$e = (base) => [, , , __create$e(base?.[__knownSymbol$e("metadata")] ?? null)]; - var __decoratorStrings$e = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$e = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$e("Function expected") : fn; - var __decoratorContext$e = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$e[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$e("Already initialized") : fns.push(__expectFn$e(fn || null)) }); - var __decoratorMetadata$e = (array, target) => __defNormalProp$k(target, __knownSymbol$e("metadata"), array[3]); - var __runInitializers$e = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$e = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = !!(flags & 8), p2 = !!(flags & 16); - var j2 = k2 > 3 ? array.length + 1 : k2 ? s2 ? 1 : 2 : 0, key = __decoratorStrings$e[k2 + 5]; - var initializers = k2 > 3 && (array[j2 - 1] = []), extraInitializers = array[j2] || (array[j2] = []); - var desc = k2 && (!p2 && !s2 && (target = target.prototype), k2 < 5 && (k2 > 3 || !p2) && __getOwnPropDesc$e(k2 < 4 ? target : { get [name]() { - return __privateGet$d(this, extra); - }, set [name](x2) { - return __privateSet$d(this, extra, x2); - } }, name)); - k2 ? p2 && k2 < 4 && __name$2(extra, (k2 > 2 ? "set " : k2 > 1 ? "get " : "") + name) : __name$2(target, name); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$e(k2, name, done = {}, array[3], extraInitializers); - if (k2) { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: p2 ? (x2) => __privateIn$1(target, x2) : (x2) => name in x2 }; - if (k2 ^ 3) access.get = p2 ? (x2) => (k2 ^ 1 ? __privateGet$d : __privateMethod$1)(x2, target, k2 ^ 4 ? extra : desc.get) : (x2) => x2[name]; - if (k2 > 2) access.set = p2 ? (x2, y2) => __privateSet$d(x2, target, y2, k2 ^ 4 ? extra : desc.set) : (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])(k2 ? k2 < 4 ? p2 ? extra : desc[key] : k2 > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; - if (k2 ^ 4 || it === void 0) __expectFn$e(it) && (k2 > 4 ? initializers.unshift(it) : k2 ? p2 ? extra = it : desc[key] = it : target = it); - else if (typeof it !== "object" || it === null) __typeError$e("Object expected"); - else __expectFn$e(fn = it.get) && (desc.get = fn), __expectFn$e(fn = it.set) && (desc.set = fn), __expectFn$e(fn = it.init) && initializers.unshift(fn); - } - return k2 || __decoratorMetadata$e(array, target), desc && __defProp$k(target, name, desc), p2 ? k2 ^ 4 ? extra : desc : target; - }; - var __publicField$k = (obj, key, value) => __defNormalProp$k(obj, key + "", value); - var __accessCheck$d = (obj, member, msg2) => member.has(obj) || __typeError$e("Cannot " + msg2); - var __privateIn$1 = (member, obj) => Object(obj) !== obj ? __typeError$e('Cannot use the "in" operator on this value') : member.has(obj); - var __privateGet$d = (obj, member, getter) => (__accessCheck$d(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); - var __privateAdd$d = (obj, member, value) => member.has(obj) ? __typeError$e("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$d = (obj, member, value, setter) => (__accessCheck$d(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); - var __privateMethod$1 = (obj, member, method) => (__accessCheck$d(obj, member, "access private method"), method); - var __collapsedSummaryKey_dec, _sidebarCollapsed_dec$1, _canShowDeltaAnalysis_dec, _comparisonWindows_dec, _hass_dec$4, _states_dec, _rows_dec, _a$e, _HistoryTargets_decorators, _init$e, _rows, _states, _hass$4, _comparisonWindows, _canShowDeltaAnalysis, _sidebarCollapsed$1, __collapsedSummaryKey; - _HistoryTargets_decorators = [localized()]; - class HistoryTargets extends (_a$e = i$2, _rows_dec = [n({ type: Array })], _states_dec = [n({ type: Object })], _hass_dec$4 = [n({ type: Object })], _comparisonWindows_dec = [n({ type: Array })], _canShowDeltaAnalysis_dec = [n({ type: Boolean, attribute: "can-show-delta-analysis" })], _sidebarCollapsed_dec$1 = [n({ type: Boolean, attribute: "sidebar-collapsed", reflect: true })], __collapsedSummaryKey_dec = [r()], _a$e) { - constructor() { - super(...arguments); - __privateAdd$d(this, _rows, __runInitializers$e(_init$e, 8, this, [])), __runInitializers$e(_init$e, 11, this); - __privateAdd$d(this, _states, __runInitializers$e(_init$e, 12, this, {})), __runInitializers$e(_init$e, 15, this); - __privateAdd$d(this, _hass$4, __runInitializers$e(_init$e, 16, this, null)), __runInitializers$e(_init$e, 19, this); - __privateAdd$d(this, _comparisonWindows, __runInitializers$e(_init$e, 20, this, [])), __runInitializers$e(_init$e, 23, this); - __privateAdd$d(this, _canShowDeltaAnalysis, __runInitializers$e(_init$e, 24, this, false)), __runInitializers$e(_init$e, 27, this); - __privateAdd$d(this, _sidebarCollapsed$1, __runInitializers$e(_init$e, 28, this, false)), __runInitializers$e(_init$e, 31, this); - __privateAdd$d(this, __collapsedSummaryKey, __runInitializers$e(_init$e, 32, this, "")), __runInitializers$e(_init$e, 35, this); - } - // ── Private helpers ──────────────────────────────────────────────────────── - _emit(name, detail = {}) { - this.dispatchEvent( - new CustomEvent(name, { detail, bubbles: true, composed: true }) - ); - } - /** Returns the `target-row-list` element for direct property access by the parent. */ - getRowListEl() { - return this.shadowRoot?.querySelector( - "target-row-list" - ) ?? null; - } - /** Returns the `ha-target-picker` element for direct property access by the parent. */ - getTargetPickerEl() { - return this.shadowRoot?.querySelector( - "ha-target-picker" - ) ?? null; - } - // ── Event handlers ───────────────────────────────────────────────────────── - _onPrefsClick(ev) { - ev.stopPropagation(); - this._emit("dp-targets-prefs-click"); - } - _onAddTargetClick(ev) { - ev.stopPropagation(); - this._emit("dp-targets-add-click", { - buttonEl: ev.currentTarget - }); - } - _onCollapsedEntityClick(ev, entityId) { - ev.stopPropagation(); - this._emit("dp-collapsed-entity-click", { - entityId, - buttonEl: ev.currentTarget - }); - } - // ── Render ───────────────────────────────────────────────────────────────── - _renderCollapsedSummary() { - if (!this.rows.length) { - return A; - } - return this.rows.map((row) => { - const label = entityName(this.hass, row.entity_id) || row.entity_id; - return b` + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/history-targets/history-targets.ts + var _HistoryTargets, _rows_accessor_storage, _states_accessor_storage, _hass_accessor_storage$4, _comparisonWindows_accessor_storage, _canShowDeltaAnalysis_accessor_storage, _sidebarCollapsed_accessor_storage$1, _collapsedSummaryKey_accessor_storage; + var HistoryTargets = (_rows_accessor_storage = /* @__PURE__ */ new WeakMap(), _states_accessor_storage = /* @__PURE__ */ new WeakMap(), _hass_accessor_storage$4 = /* @__PURE__ */ new WeakMap(), _comparisonWindows_accessor_storage = /* @__PURE__ */ new WeakMap(), _canShowDeltaAnalysis_accessor_storage = /* @__PURE__ */ new WeakMap(), _sidebarCollapsed_accessor_storage$1 = /* @__PURE__ */ new WeakMap(), _collapsedSummaryKey_accessor_storage = /* @__PURE__ */ new WeakMap(), _HistoryTargets = class HistoryTargets extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _rows_accessor_storage, []); + _classPrivateFieldInitSpec(this, _states_accessor_storage, {}); + _classPrivateFieldInitSpec(this, _hass_accessor_storage$4, null); + _classPrivateFieldInitSpec(this, _comparisonWindows_accessor_storage, []); + _classPrivateFieldInitSpec(this, _canShowDeltaAnalysis_accessor_storage, false); + _classPrivateFieldInitSpec(this, _sidebarCollapsed_accessor_storage$1, false); + _classPrivateFieldInitSpec(this, _collapsedSummaryKey_accessor_storage, ""); + } + get rows() { + return _classPrivateFieldGet2(_rows_accessor_storage, this); + } + set rows(value) { + _classPrivateFieldSet2(_rows_accessor_storage, this, value); + } + get states() { + return _classPrivateFieldGet2(_states_accessor_storage, this); + } + set states(value) { + _classPrivateFieldSet2(_states_accessor_storage, this, value); + } + get hass() { + return _classPrivateFieldGet2(_hass_accessor_storage$4, this); + } + set hass(value) { + _classPrivateFieldSet2(_hass_accessor_storage$4, this, value); + } + get comparisonWindows() { + return _classPrivateFieldGet2(_comparisonWindows_accessor_storage, this); + } + set comparisonWindows(value) { + _classPrivateFieldSet2(_comparisonWindows_accessor_storage, this, value); + } + get canShowDeltaAnalysis() { + return _classPrivateFieldGet2(_canShowDeltaAnalysis_accessor_storage, this); + } + set canShowDeltaAnalysis(value) { + _classPrivateFieldSet2(_canShowDeltaAnalysis_accessor_storage, this, value); + } + get sidebarCollapsed() { + return _classPrivateFieldGet2(_sidebarCollapsed_accessor_storage$1, this); + } + set sidebarCollapsed(value) { + _classPrivateFieldSet2(_sidebarCollapsed_accessor_storage$1, this, value); + } + get _collapsedSummaryKey() { + return _classPrivateFieldGet2(_collapsedSummaryKey_accessor_storage, this); + } + set _collapsedSummaryKey(value) { + _classPrivateFieldSet2(_collapsedSummaryKey_accessor_storage, this, value); + } + _emit(name, detail = {}) { + this.dispatchEvent(new CustomEvent(name, { + detail, + bubbles: true, + composed: true + })); + } + /** Returns the `target-row-list` element for direct property access by the parent. */ + getRowListEl() { + return this.shadowRoot?.querySelector("target-row-list") ?? null; + } + /** Returns the `ha-target-picker` element for direct property access by the parent. */ + getTargetPickerEl() { + return this.shadowRoot?.querySelector("ha-target-picker") ?? null; + } + _onPrefsClick(ev) { + ev.stopPropagation(); + this._emit("dp-targets-prefs-click"); + } + _onAddTargetClick(ev) { + ev.stopPropagation(); + this._emit("dp-targets-add-click", { buttonEl: ev.currentTarget }); + } + _onCollapsedEntityClick(ev, entityId) { + ev.stopPropagation(); + this._emit("dp-collapsed-entity-click", { + entityId, + buttonEl: ev.currentTarget + }); + } + _renderCollapsedSummary() { + if (!this.rows.length) return A; + return this.rows.map((row) => { + const label = entityName(this.hass, row.entity_id) || row.entity_id; + return b` `; - }); - } - render() { - return b` + }); + } + render() { + return b`
`; - } - } - _init$e = __decoratorStart$e(_a$e); - _rows = /* @__PURE__ */ new WeakMap(); - _states = /* @__PURE__ */ new WeakMap(); - _hass$4 = /* @__PURE__ */ new WeakMap(); - _comparisonWindows = /* @__PURE__ */ new WeakMap(); - _canShowDeltaAnalysis = /* @__PURE__ */ new WeakMap(); - _sidebarCollapsed$1 = /* @__PURE__ */ new WeakMap(); - __collapsedSummaryKey = /* @__PURE__ */ new WeakMap(); - __decorateElement$e(_init$e, 4, "rows", _rows_dec, HistoryTargets, _rows); - __decorateElement$e(_init$e, 4, "states", _states_dec, HistoryTargets, _states); - __decorateElement$e(_init$e, 4, "hass", _hass_dec$4, HistoryTargets, _hass$4); - __decorateElement$e(_init$e, 4, "comparisonWindows", _comparisonWindows_dec, HistoryTargets, _comparisonWindows); - __decorateElement$e(_init$e, 4, "canShowDeltaAnalysis", _canShowDeltaAnalysis_dec, HistoryTargets, _canShowDeltaAnalysis); - __decorateElement$e(_init$e, 4, "sidebarCollapsed", _sidebarCollapsed_dec$1, HistoryTargets, _sidebarCollapsed$1); - __decorateElement$e(_init$e, 4, "_collapsedSummaryKey", __collapsedSummaryKey_dec, HistoryTargets, __collapsedSummaryKey); - HistoryTargets = __decorateElement$e(_init$e, 0, "HistoryTargets", _HistoryTargets_decorators, HistoryTargets); - __publicField$k(HistoryTargets, "styles", styles$k); - __runInitializers$e(_init$e, 1, HistoryTargets); - customElements.define("history-targets", HistoryTargets); - const styles$j = i$5` + } + }, _defineProperty(_HistoryTargets, "styles", styles$20), _HistoryTargets); + __decorate([n$1({ type: Array })], HistoryTargets.prototype, "rows", null); + __decorate([n$1({ type: Object })], HistoryTargets.prototype, "states", null); + __decorate([n$1({ type: Object })], HistoryTargets.prototype, "hass", null); + __decorate([n$1({ type: Array })], HistoryTargets.prototype, "comparisonWindows", null); + __decorate([n$1({ + type: Boolean, + attribute: "can-show-delta-analysis" + })], HistoryTargets.prototype, "canShowDeltaAnalysis", null); + __decorate([n$1({ + type: Boolean, + attribute: "sidebar-collapsed", + reflect: true + })], HistoryTargets.prototype, "sidebarCollapsed", null); + __decorate([r$1()], HistoryTargets.prototype, "_collapsedSummaryKey", null); + HistoryTargets = __decorate([localized()], HistoryTargets); + customElements.define("history-targets", HistoryTargets); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/range-toolbar/range-toolbar.styles.ts + var styles$19 = i$5` :host { display: block; position: relative; @@ -26143,7 +25922,9 @@ ${content.alert}` : "", } } `; - const styles$i = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/molecules/panel-timeline/panel-timeline.styles.ts + var styles$18 = i$5` :host { display: contents; } @@ -26291,125 +26072,183 @@ ${content.alert}` : "", opacity: 0.45; } `; - var __create$d = Object.create; - var __defProp$j = Object.defineProperty; - var __getOwnPropDesc$d = Object.getOwnPropertyDescriptor; - var __knownSymbol$d = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$d = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$j = (obj, key, value) => key in obj ? __defProp$j(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$d = (base) => [, , , __create$d(base?.[__knownSymbol$d("metadata")] ?? null)]; - var __decoratorStrings$d = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$d = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$d("Function expected") : fn; - var __decoratorContext$d = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$d[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$d("Already initialized") : fns.push(__expectFn$d(fn || null)) }); - var __decoratorMetadata$d = (array, target) => __defNormalProp$j(target, __knownSymbol$d("metadata"), array[3]); - var __runInitializers$d = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$d = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$d[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$d({ get [name]() { - return __privateGet$c(this, extra); - }, set [name](x2) { - return __privateSet$c(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$d(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$d(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$d("Object expected"); - else __expectFn$d(fn = it.get) && (desc.get = fn), __expectFn$d(fn = it.set) && (desc.set = fn), __expectFn$d(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$j(target, name, desc), target; - }; - var __publicField$j = (obj, key, value) => __defNormalProp$j(obj, typeof key !== "symbol" ? key + "" : key, value); - var __accessCheck$c = (obj, member, msg2) => member.has(obj) || __typeError$d("Cannot " + msg2); - var __privateGet$c = (obj, member, getter) => (__accessCheck$c(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$c = (obj, member, value) => member.has(obj) ? __typeError$d("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$c = (obj, member, value, setter) => (__accessCheck$c(obj, member, "write to private field"), member.set(obj, value), value); - var _events_dec$1, _chartHoverWindowTimeMs_dec$1, _chartHoverTimeMs_dec$1, _zoomWindowRange_dec$1, _zoomRange_dec$1, _comparisonPreview_dec$1, _hoveredPeriodRange_dec, _locale_dec, _isLiveEdge_dec$1, _dateSnapping_dec$1, _zoomLevel_dec$1, _rangeBounds_dec$1, _endTime_dec$1, _startTime_dec$1, _a$d, _init$d, _startTime$1, _endTime$1, _rangeBounds$1, _zoomLevel$1, _dateSnapping$1, _isLiveEdge$1, _locale, _hoveredPeriodRange, _comparisonPreview$1, _zoomRange$1, _zoomWindowRange$1, _chartHoverTimeMs$1, _chartHoverWindowTimeMs$1, _events$1; - class PanelTimeline extends (_a$d = i$2, _startTime_dec$1 = [n({ type: Object })], _endTime_dec$1 = [n({ type: Object })], _rangeBounds_dec$1 = [n({ type: Object })], _zoomLevel_dec$1 = [n({ type: String })], _dateSnapping_dec$1 = [n({ type: String })], _isLiveEdge_dec$1 = [n({ type: Boolean })], _locale_dec = [n({ type: String })], _hoveredPeriodRange_dec = [r()], _comparisonPreview_dec$1 = [n({ type: Object })], _zoomRange_dec$1 = [n({ type: Object })], _zoomWindowRange_dec$1 = [n({ type: Object })], _chartHoverTimeMs_dec$1 = [n({ type: Number })], _chartHoverWindowTimeMs_dec$1 = [n({ type: Number })], _events_dec$1 = [n({ type: Array })], _a$d) { - constructor() { - super(...arguments); - __privateAdd$c(this, _startTime$1, __runInitializers$d(_init$d, 8, this, null)), __runInitializers$d(_init$d, 11, this); - __privateAdd$c(this, _endTime$1, __runInitializers$d(_init$d, 12, this, null)), __runInitializers$d(_init$d, 15, this); - __privateAdd$c(this, _rangeBounds$1, __runInitializers$d(_init$d, 16, this, null)), __runInitializers$d(_init$d, 19, this); - __privateAdd$c(this, _zoomLevel$1, __runInitializers$d(_init$d, 20, this, "day")), __runInitializers$d(_init$d, 23, this); - __privateAdd$c(this, _dateSnapping$1, __runInitializers$d(_init$d, 24, this, "auto")), __runInitializers$d(_init$d, 27, this); - __privateAdd$c(this, _isLiveEdge$1, __runInitializers$d(_init$d, 28, this, false)), __runInitializers$d(_init$d, 31, this); - __privateAdd$c(this, _locale, __runInitializers$d(_init$d, 32, this, "")), __runInitializers$d(_init$d, 35, this); - __privateAdd$c(this, _hoveredPeriodRange, __runInitializers$d(_init$d, 36, this, null)), __runInitializers$d(_init$d, 39, this); - __privateAdd$c(this, _comparisonPreview$1, __runInitializers$d(_init$d, 40, this, null)), __runInitializers$d(_init$d, 43, this); - __privateAdd$c(this, _zoomRange$1, __runInitializers$d(_init$d, 44, this, null)), __runInitializers$d(_init$d, 47, this); - __privateAdd$c(this, _zoomWindowRange$1, __runInitializers$d(_init$d, 48, this, null)), __runInitializers$d(_init$d, 51, this); - __privateAdd$c(this, _chartHoverTimeMs$1, __runInitializers$d(_init$d, 52, this, null)), __runInitializers$d(_init$d, 55, this); - __privateAdd$c(this, _chartHoverWindowTimeMs$1, __runInitializers$d(_init$d, 56, this, null)), __runInitializers$d(_init$d, 59, this); - __privateAdd$c(this, _events$1, __runInitializers$d(_init$d, 60, this, [])), __runInitializers$d(_init$d, 63, this); - __publicField$j(this, "_rangeHoverPreviewEl", null); - __publicField$j(this, "_rangeComparisonPreviewEl", null); - __publicField$j(this, "_rangeZoomHighlightEl", null); - __publicField$j(this, "_rangeZoomWindowHighlightEl", null); - __publicField$j(this, "_rangeChartHoverLineEl", null); - __publicField$j(this, "_rangeChartHoverWindowLineEl", null); - __publicField$j(this, "_rangeEventLayerEl", null); - __publicField$j(this, "_liveZoomRange"); - __publicField$j(this, "_liveZoomWindowRange"); - } - firstUpdated() { - const sr = this.shadowRoot; - this._rangeHoverPreviewEl = sr.getElementById("range-hover-preview"); - this._rangeComparisonPreviewEl = sr.getElementById( - "range-comparison-preview" - ); - this._rangeZoomHighlightEl = sr.getElementById("range-zoom-highlight"); - this._rangeZoomWindowHighlightEl = sr.getElementById( - "range-zoom-window-highlight" - ); - this._rangeChartHoverLineEl = sr.getElementById("range-chart-hover-line"); - this._rangeChartHoverWindowLineEl = sr.getElementById( - "range-chart-hover-window-line" - ); - this._rangeEventLayerEl = sr.getElementById("range-event-layer"); - this._syncAllOverlays(); - } - updated(changed) { - const trackProps = [ - "hoveredPeriodRange", - "comparisonPreview", - "zoomRange", - "zoomWindowRange", - "rangeBounds" - ]; - const timelineProps = [ - "chartHoverTimeMs", - "chartHoverWindowTimeMs", - "events", - "rangeBounds" - ]; - if (trackProps.some((p2) => changed.has(p2))) { - if (changed.has("zoomRange")) { - this._liveZoomRange = this.zoomRange ?? null; - } - if (changed.has("zoomWindowRange")) { - this._liveZoomWindowRange = this.zoomWindowRange ?? null; - } - this._syncTrackOverlays(); - } - if (timelineProps.some((p2) => changed.has(p2))) { - this._syncTimelineOverlays(); - } - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/molecules/panel-timeline/panel-timeline.ts + var _startTime_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _endTime_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _rangeBounds_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _zoomLevel_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _dateSnapping_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _isLiveEdge_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _locale_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _hoveredPeriodRange_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _comparisonPreview_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _zoomRange_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _zoomWindowRange_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _chartHoverTimeMs_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _chartHoverWindowTime_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _events_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + /** + * `panel-timeline` is a panel-history-specific wrapper around `range-timeline`. + * + * It adds the overlay layers (hover preview, comparison preview, zoom highlights, + * chart hover lines, event dots) via named slots, keeping the core slider atom + * free of panel-specific state. + * + * All `dp-range-*` events from the inner atom bubble through naturally. + * + * @fires dp-range-draft - Bubbled from inner range-timeline + * @fires dp-range-commit - Bubbled from inner range-timeline + * @fires dp-range-period-select - Bubbled from inner range-timeline + * @fires dp-range-period-hover - Bubbled from inner range-timeline + * @fires dp-range-period-leave - Bubbled from inner range-timeline + * @fires dp-range-scroll - Bubbled from inner range-timeline + */ + var PanelTimeline = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _startTime_accessor_storage$1, null); + _classPrivateFieldInitSpec(this, _endTime_accessor_storage$1, null); + _classPrivateFieldInitSpec(this, _rangeBounds_accessor_storage$1, null); + _classPrivateFieldInitSpec(this, _zoomLevel_accessor_storage$1, "day"); + _classPrivateFieldInitSpec(this, _dateSnapping_accessor_storage$1, "auto"); + _classPrivateFieldInitSpec(this, _isLiveEdge_accessor_storage$1, false); + _classPrivateFieldInitSpec(this, _locale_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _hoveredPeriodRange_accessor_storage, null); + _classPrivateFieldInitSpec(this, _comparisonPreview_accessor_storage$1, null); + _classPrivateFieldInitSpec(this, _zoomRange_accessor_storage$1, null); + _classPrivateFieldInitSpec(this, _zoomWindowRange_accessor_storage$1, null); + _classPrivateFieldInitSpec(this, _chartHoverTimeMs_accessor_storage$1, null); + _classPrivateFieldInitSpec(this, _chartHoverWindowTime_accessor_storage$1, null); + _classPrivateFieldInitSpec(this, _events_accessor_storage$1, []); + _defineProperty(this, "_rangeHoverPreviewEl", null); + _defineProperty(this, "_rangeComparisonPreviewEl", null); + _defineProperty(this, "_rangeZoomHighlightEl", null); + _defineProperty(this, "_rangeZoomWindowHighlightEl", null); + _defineProperty(this, "_rangeChartHoverLineEl", null); + _defineProperty(this, "_rangeChartHoverWindowLineEl", null); + _defineProperty(this, "_rangeEventLayerEl", null); + _defineProperty(this, "_liveZoomRange", void 0); + _defineProperty(this, "_liveZoomWindowRange", void 0); + } + get startTime() { + return _classPrivateFieldGet2(_startTime_accessor_storage$1, this); + } + set startTime(value) { + _classPrivateFieldSet2(_startTime_accessor_storage$1, this, value); + } + get endTime() { + return _classPrivateFieldGet2(_endTime_accessor_storage$1, this); + } + set endTime(value) { + _classPrivateFieldSet2(_endTime_accessor_storage$1, this, value); + } + get rangeBounds() { + return _classPrivateFieldGet2(_rangeBounds_accessor_storage$1, this); + } + set rangeBounds(value) { + _classPrivateFieldSet2(_rangeBounds_accessor_storage$1, this, value); + } + get zoomLevel() { + return _classPrivateFieldGet2(_zoomLevel_accessor_storage$1, this); + } + set zoomLevel(value) { + _classPrivateFieldSet2(_zoomLevel_accessor_storage$1, this, value); + } + get dateSnapping() { + return _classPrivateFieldGet2(_dateSnapping_accessor_storage$1, this); + } + set dateSnapping(value) { + _classPrivateFieldSet2(_dateSnapping_accessor_storage$1, this, value); + } + get isLiveEdge() { + return _classPrivateFieldGet2(_isLiveEdge_accessor_storage$1, this); + } + set isLiveEdge(value) { + _classPrivateFieldSet2(_isLiveEdge_accessor_storage$1, this, value); + } + get locale() { + return _classPrivateFieldGet2(_locale_accessor_storage, this); + } + set locale(value) { + _classPrivateFieldSet2(_locale_accessor_storage, this, value); + } + get hoveredPeriodRange() { + return _classPrivateFieldGet2(_hoveredPeriodRange_accessor_storage, this); + } + set hoveredPeriodRange(value) { + _classPrivateFieldSet2(_hoveredPeriodRange_accessor_storage, this, value); + } + get comparisonPreview() { + return _classPrivateFieldGet2(_comparisonPreview_accessor_storage$1, this); + } + set comparisonPreview(value) { + _classPrivateFieldSet2(_comparisonPreview_accessor_storage$1, this, value); + } + get zoomRange() { + return _classPrivateFieldGet2(_zoomRange_accessor_storage$1, this); + } + set zoomRange(value) { + _classPrivateFieldSet2(_zoomRange_accessor_storage$1, this, value); + } + get zoomWindowRange() { + return _classPrivateFieldGet2(_zoomWindowRange_accessor_storage$1, this); + } + set zoomWindowRange(value) { + _classPrivateFieldSet2(_zoomWindowRange_accessor_storage$1, this, value); + } + get chartHoverTimeMs() { + return _classPrivateFieldGet2(_chartHoverTimeMs_accessor_storage$1, this); + } + set chartHoverTimeMs(value) { + _classPrivateFieldSet2(_chartHoverTimeMs_accessor_storage$1, this, value); + } + get chartHoverWindowTimeMs() { + return _classPrivateFieldGet2(_chartHoverWindowTime_accessor_storage$1, this); + } + set chartHoverWindowTimeMs(value) { + _classPrivateFieldSet2(_chartHoverWindowTime_accessor_storage$1, this, value); + } + get events() { + return _classPrivateFieldGet2(_events_accessor_storage$1, this); + } + set events(value) { + _classPrivateFieldSet2(_events_accessor_storage$1, this, value); + } + firstUpdated() { + const sr = this.shadowRoot; + this._rangeHoverPreviewEl = sr.getElementById("range-hover-preview"); + this._rangeComparisonPreviewEl = sr.getElementById("range-comparison-preview"); + this._rangeZoomHighlightEl = sr.getElementById("range-zoom-highlight"); + this._rangeZoomWindowHighlightEl = sr.getElementById("range-zoom-window-highlight"); + this._rangeChartHoverLineEl = sr.getElementById("range-chart-hover-line"); + this._rangeChartHoverWindowLineEl = sr.getElementById("range-chart-hover-window-line"); + this._rangeEventLayerEl = sr.getElementById("range-event-layer"); + this._syncAllOverlays(); + } + updated(changed) { + const trackProps = [ + "hoveredPeriodRange", + "comparisonPreview", + "zoomRange", + "zoomWindowRange", + "rangeBounds" + ]; + const timelineProps = [ + "chartHoverTimeMs", + "chartHoverWindowTimeMs", + "events", + "rangeBounds" + ]; + if (trackProps.some((p) => changed.has(p))) { + if (changed.has("zoomRange")) this._liveZoomRange = this.zoomRange ?? null; + if (changed.has("zoomWindowRange")) this._liveZoomWindowRange = this.zoomWindowRange ?? null; + this._syncTrackOverlays(); + } + if (timelineProps.some((p) => changed.has(p))) this._syncTimelineOverlays(); + } + render() { + return b`
`; - } - // --------------------------------------------------------------------------- - // Coordinate helper - // --------------------------------------------------------------------------- - _pct(ms) { - if (!this.rangeBounds) return 0; - const total = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); - return (ms - this.rangeBounds.min) / total * 100; - } - // --------------------------------------------------------------------------- - // Internal period hover handling (from dp-range-period-hover/leave events - // bubbling up from the inner range-timeline atom) - // --------------------------------------------------------------------------- - revealSelection() { - const timeline = this.shadowRoot?.querySelector( - "range-timeline" - ); - timeline?.revealSelection?.(); - } - syncZoomHighlights(zoomRange, zoomWindowRange) { - this._liveZoomRange = zoomRange ? { ...zoomRange } : null; - this._liveZoomWindowRange = zoomWindowRange ? { ...zoomWindowRange } : null; - this._setRangeOverlay(this._rangeZoomHighlightEl, this._liveZoomRange); - this._setRangeOverlay( - this._rangeZoomWindowHighlightEl, - this._liveZoomWindowRange - ); - } - _onPeriodHoverInternal(ev) { - const { start, end } = ev.detail; - this.hoveredPeriodRange = { start: start.getTime(), end: end.getTime() }; - } - _onPeriodLeaveInternal() { - this.hoveredPeriodRange = null; - } - // --------------------------------------------------------------------------- - // Overlay sync - // --------------------------------------------------------------------------- - _syncAllOverlays() { - this._syncTrackOverlays(); - this._syncTimelineOverlays(); - } - _setRangeOverlay(el, range) { - if (!el) return; - if (!range || !this.rangeBounds) { - el.classList.remove("visible"); - return; - } - const total = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); - const startClamped = clampNumber( - range.start, - this.rangeBounds.min, - this.rangeBounds.max - ); - const endClamped = clampNumber( - range.end, - this.rangeBounds.min, - this.rangeBounds.max - ); - const startPct = (startClamped - this.rangeBounds.min) / total * 100; - const endPct = (endClamped - this.rangeBounds.min) / total * 100; - el.style.left = `${startPct}%`; - el.style.width = `${Math.max(0, endPct - startPct)}%`; - el.classList.add("visible"); - } - _setHoverLine(el, timeMs) { - if (!el) return; - if (timeMs == null || !this.rangeBounds) { - el.classList.remove("visible"); - return; - } - const clamped = clampNumber( - timeMs, - this.rangeBounds.min, - this.rangeBounds.max - ); - el.style.left = `${this._pct(clamped)}%`; - el.classList.add("visible"); - } - _syncTrackOverlays() { - const zoomRange = this._liveZoomRange !== void 0 ? this._liveZoomRange : this.zoomRange ?? null; - const zoomWindowRange = this._liveZoomWindowRange !== void 0 ? this._liveZoomWindowRange : this.zoomWindowRange ?? null; - this._setRangeOverlay( - this._rangeHoverPreviewEl, - this.hoveredPeriodRange ?? null - ); - this._setRangeOverlay( - this._rangeComparisonPreviewEl, - this.comparisonPreview ?? null - ); - this._setRangeOverlay(this._rangeZoomHighlightEl, zoomRange); - this._setRangeOverlay(this._rangeZoomWindowHighlightEl, zoomWindowRange); - } - _syncTimelineOverlays() { - this._setHoverLine( - this._rangeChartHoverLineEl, - this.chartHoverTimeMs ?? null - ); - this._setHoverLine( - this._rangeChartHoverWindowLineEl, - this.chartHoverWindowTimeMs ?? null - ); - this._syncEventLayer(); - } - _syncEventLayer() { - if (!this._rangeEventLayerEl || !this.rangeBounds) return; - this._rangeEventLayerEl.innerHTML = ""; - const fragment = document.createDocumentFragment(); - const total = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); - for (const event of this.events) { - const timestamp = new Date(event.timestamp).getTime(); - if (!Number.isFinite(timestamp) || timestamp < this.rangeBounds.min || timestamp > this.rangeBounds.max) - continue; - const dot = document.createElement("span"); - dot.className = "range-event-dot"; - dot.style.left = `${(timestamp - this.rangeBounds.min) / total * 100}%`; - dot.style.background = event.color ?? "#03a9f4"; - fragment.appendChild(dot); - } - this._rangeEventLayerEl.appendChild(fragment); - } - } - _init$d = __decoratorStart$d(_a$d); - _startTime$1 = /* @__PURE__ */ new WeakMap(); - _endTime$1 = /* @__PURE__ */ new WeakMap(); - _rangeBounds$1 = /* @__PURE__ */ new WeakMap(); - _zoomLevel$1 = /* @__PURE__ */ new WeakMap(); - _dateSnapping$1 = /* @__PURE__ */ new WeakMap(); - _isLiveEdge$1 = /* @__PURE__ */ new WeakMap(); - _locale = /* @__PURE__ */ new WeakMap(); - _hoveredPeriodRange = /* @__PURE__ */ new WeakMap(); - _comparisonPreview$1 = /* @__PURE__ */ new WeakMap(); - _zoomRange$1 = /* @__PURE__ */ new WeakMap(); - _zoomWindowRange$1 = /* @__PURE__ */ new WeakMap(); - _chartHoverTimeMs$1 = /* @__PURE__ */ new WeakMap(); - _chartHoverWindowTimeMs$1 = /* @__PURE__ */ new WeakMap(); - _events$1 = /* @__PURE__ */ new WeakMap(); - __decorateElement$d(_init$d, 4, "startTime", _startTime_dec$1, PanelTimeline, _startTime$1); - __decorateElement$d(_init$d, 4, "endTime", _endTime_dec$1, PanelTimeline, _endTime$1); - __decorateElement$d(_init$d, 4, "rangeBounds", _rangeBounds_dec$1, PanelTimeline, _rangeBounds$1); - __decorateElement$d(_init$d, 4, "zoomLevel", _zoomLevel_dec$1, PanelTimeline, _zoomLevel$1); - __decorateElement$d(_init$d, 4, "dateSnapping", _dateSnapping_dec$1, PanelTimeline, _dateSnapping$1); - __decorateElement$d(_init$d, 4, "isLiveEdge", _isLiveEdge_dec$1, PanelTimeline, _isLiveEdge$1); - __decorateElement$d(_init$d, 4, "locale", _locale_dec, PanelTimeline, _locale); - __decorateElement$d(_init$d, 4, "hoveredPeriodRange", _hoveredPeriodRange_dec, PanelTimeline, _hoveredPeriodRange); - __decorateElement$d(_init$d, 4, "comparisonPreview", _comparisonPreview_dec$1, PanelTimeline, _comparisonPreview$1); - __decorateElement$d(_init$d, 4, "zoomRange", _zoomRange_dec$1, PanelTimeline, _zoomRange$1); - __decorateElement$d(_init$d, 4, "zoomWindowRange", _zoomWindowRange_dec$1, PanelTimeline, _zoomWindowRange$1); - __decorateElement$d(_init$d, 4, "chartHoverTimeMs", _chartHoverTimeMs_dec$1, PanelTimeline, _chartHoverTimeMs$1); - __decorateElement$d(_init$d, 4, "chartHoverWindowTimeMs", _chartHoverWindowTimeMs_dec$1, PanelTimeline, _chartHoverWindowTimeMs$1); - __decorateElement$d(_init$d, 4, "events", _events_dec$1, PanelTimeline, _events$1); - __decoratorMetadata$d(_init$d, PanelTimeline); - __publicField$j(PanelTimeline, "styles", styles$i); - customElements.define("panel-timeline", PanelTimeline); - const styles$h = i$5` + } + _pct(ms) { + if (!this.rangeBounds) return 0; + const total = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); + return (ms - this.rangeBounds.min) / total * 100; + } + revealSelection() { + (this.shadowRoot?.querySelector("range-timeline"))?.revealSelection?.(); + } + syncZoomHighlights(zoomRange, zoomWindowRange) { + this._liveZoomRange = zoomRange ? { ...zoomRange } : null; + this._liveZoomWindowRange = zoomWindowRange ? { ...zoomWindowRange } : null; + this._setRangeOverlay(this._rangeZoomHighlightEl, this._liveZoomRange); + this._setRangeOverlay(this._rangeZoomWindowHighlightEl, this._liveZoomWindowRange); + } + _onPeriodHoverInternal(ev) { + const { start, end } = ev.detail; + this.hoveredPeriodRange = { + start: start.getTime(), + end: end.getTime() + }; + } + _onPeriodLeaveInternal() { + this.hoveredPeriodRange = null; + } + _syncAllOverlays() { + this._syncTrackOverlays(); + this._syncTimelineOverlays(); + } + _setRangeOverlay(el, range) { + if (!el) return; + if (!range || !this.rangeBounds) { + el.classList.remove("visible"); + return; + } + const total = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); + const startClamped = clampNumber(range.start, this.rangeBounds.min, this.rangeBounds.max); + const endClamped = clampNumber(range.end, this.rangeBounds.min, this.rangeBounds.max); + const startPct = (startClamped - this.rangeBounds.min) / total * 100; + const endPct = (endClamped - this.rangeBounds.min) / total * 100; + el.style.left = `${startPct}%`; + el.style.width = `${Math.max(0, endPct - startPct)}%`; + el.classList.add("visible"); + } + _setHoverLine(el, timeMs) { + if (!el) return; + if (timeMs == null || !this.rangeBounds) { + el.classList.remove("visible"); + return; + } + const clamped = clampNumber(timeMs, this.rangeBounds.min, this.rangeBounds.max); + el.style.left = `${this._pct(clamped)}%`; + el.classList.add("visible"); + } + _syncTrackOverlays() { + const zoomRange = this._liveZoomRange !== void 0 ? this._liveZoomRange : this.zoomRange ?? null; + const zoomWindowRange = this._liveZoomWindowRange !== void 0 ? this._liveZoomWindowRange : this.zoomWindowRange ?? null; + this._setRangeOverlay(this._rangeHoverPreviewEl, this.hoveredPeriodRange ?? null); + this._setRangeOverlay(this._rangeComparisonPreviewEl, this.comparisonPreview ?? null); + this._setRangeOverlay(this._rangeZoomHighlightEl, zoomRange); + this._setRangeOverlay(this._rangeZoomWindowHighlightEl, zoomWindowRange); + } + _syncTimelineOverlays() { + this._setHoverLine(this._rangeChartHoverLineEl, this.chartHoverTimeMs ?? null); + this._setHoverLine(this._rangeChartHoverWindowLineEl, this.chartHoverWindowTimeMs ?? null); + this._syncEventLayer(); + } + _syncEventLayer() { + if (!this._rangeEventLayerEl || !this.rangeBounds) return; + this._rangeEventLayerEl.innerHTML = ""; + const fragment = document.createDocumentFragment(); + const total = Math.max(1, this.rangeBounds.max - this.rangeBounds.min); + for (const event of this.events) { + const timestamp = new Date(event.timestamp).getTime(); + if (!Number.isFinite(timestamp) || timestamp < this.rangeBounds.min || timestamp > this.rangeBounds.max) continue; + const dot = document.createElement("span"); + dot.className = "range-event-dot"; + dot.style.left = `${(timestamp - this.rangeBounds.min) / total * 100}%`; + dot.style.background = event.color ?? "#03a9f4"; + fragment.appendChild(dot); + } + this._rangeEventLayerEl.appendChild(fragment); + } + }; + _defineProperty(PanelTimeline, "styles", styles$18); + __decorate([n$1({ type: Object })], PanelTimeline.prototype, "startTime", null); + __decorate([n$1({ type: Object })], PanelTimeline.prototype, "endTime", null); + __decorate([n$1({ type: Object })], PanelTimeline.prototype, "rangeBounds", null); + __decorate([n$1({ type: String })], PanelTimeline.prototype, "zoomLevel", null); + __decorate([n$1({ type: String })], PanelTimeline.prototype, "dateSnapping", null); + __decorate([n$1({ type: Boolean })], PanelTimeline.prototype, "isLiveEdge", null); + __decorate([n$1({ type: String })], PanelTimeline.prototype, "locale", null); + __decorate([r$1()], PanelTimeline.prototype, "hoveredPeriodRange", null); + __decorate([n$1({ type: Object })], PanelTimeline.prototype, "comparisonPreview", null); + __decorate([n$1({ type: Object })], PanelTimeline.prototype, "zoomRange", null); + __decorate([n$1({ type: Object })], PanelTimeline.prototype, "zoomWindowRange", null); + __decorate([n$1({ type: Number })], PanelTimeline.prototype, "chartHoverTimeMs", null); + __decorate([n$1({ type: Number })], PanelTimeline.prototype, "chartHoverWindowTimeMs", null); + __decorate([n$1({ type: Array })], PanelTimeline.prototype, "events", null); + customElements.define("panel-timeline", PanelTimeline); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/form/date-time-input/date-time-input.styles.ts + var styles$17 = i$5` :host { display: block; } @@ -26634,374 +26421,357 @@ ${content.alert}` : "", border: 1px solid var(--divider-color, #444); border-radius: 6px; background: transparent; - color: var(--primary-text-color); - font-size: 0.85rem; - font-family: inherit; - } - input:focus { - border-color: var(--primary-color, #03a9f4); - outline: none; - } -`; - var __create$c = Object.create; - var __defProp$i = Object.defineProperty; - var __getOwnPropDesc$c = Object.getOwnPropertyDescriptor; - var __knownSymbol$c = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$c = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$i = (obj, key, value) => key in obj ? __defProp$i(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$c = (base) => [, , , __create$c(base?.[__knownSymbol$c("metadata")] ?? null)]; - var __decoratorStrings$c = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$c = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$c("Function expected") : fn; - var __decoratorContext$c = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$c[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$c("Already initialized") : fns.push(__expectFn$c(fn || null)) }); - var __decoratorMetadata$c = (array, target) => __defNormalProp$i(target, __knownSymbol$c("metadata"), array[3]); - var __runInitializers$c = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$c = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$c[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$c({ get [name]() { - return __privateGet$b(this, extra); - }, set [name](x2) { - return __privateSet$b(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$c(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$c(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$c("Object expected"); - else __expectFn$c(fn = it.get) && (desc.get = fn), __expectFn$c(fn = it.set) && (desc.set = fn), __expectFn$c(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$i(target, name, desc), target; - }; - var __publicField$i = (obj, key, value) => __defNormalProp$i(obj, key + "", value); - var __accessCheck$b = (obj, member, msg2) => member.has(obj) || __typeError$c("Cannot " + msg2); - var __privateGet$b = (obj, member, getter) => (__accessCheck$b(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$b = (obj, member, value) => member.has(obj) ? __typeError$c("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$b = (obj, member, value, setter) => (__accessCheck$b(obj, member, "write to private field"), member.set(obj, value), value); - var _label_dec$4, _value_dec$4, _a$c, _init$c, _value$4, _label$4; - class DateTimeInput extends (_a$c = i$2, _value_dec$4 = [n({ type: String })], _label_dec$4 = [n({ type: String })], _a$c) { - constructor() { - super(...arguments); - __privateAdd$b(this, _value$4, __runInitializers$c(_init$c, 8, this, "")), __runInitializers$c(_init$c, 11, this); - __privateAdd$b(this, _label$4, __runInitializers$c(_init$c, 12, this, "")), __runInitializers$c(_init$c, 15, this); - } - _onChange(e2) { - this.dispatchEvent( - new CustomEvent("dp-datetime-change", { - detail: { value: e2.target.value }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b` - ${this.label ? b`` : ""} - - `; - } - } - _init$c = __decoratorStart$c(_a$c); - _value$4 = /* @__PURE__ */ new WeakMap(); - _label$4 = /* @__PURE__ */ new WeakMap(); - __decorateElement$c(_init$c, 4, "value", _value_dec$4, DateTimeInput, _value$4); - __decorateElement$c(_init$c, 4, "label", _label_dec$4, DateTimeInput, _label$4); - __decoratorMetadata$c(_init$c, DateTimeInput); - __publicField$i(DateTimeInput, "styles", styles$h); - customElements.define("date-time-input", DateTimeInput); - var __create$b = Object.create; - var __defProp$h = Object.defineProperty; - var __getOwnPropDesc$b = Object.getOwnPropertyDescriptor; - var __knownSymbol$b = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$b = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$h = (obj, key, value) => key in obj ? __defProp$h(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __name$1 = (target, value) => __defProp$h(target, "name", { value, configurable: true }); - var __decoratorStart$b = (base) => [, , , __create$b(base?.[__knownSymbol$b("metadata")] ?? null)]; - var __decoratorStrings$b = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$b = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$b("Function expected") : fn; - var __decoratorContext$b = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$b[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$b("Already initialized") : fns.push(__expectFn$b(fn || null)) }); - var __decoratorMetadata$b = (array, target) => __defNormalProp$h(target, __knownSymbol$b("metadata"), array[3]); - var __runInitializers$b = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$b = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = !!(flags & 8), p2 = !!(flags & 16); - var j2 = k2 > 3 ? array.length + 1 : k2 ? s2 ? 1 : 2 : 0, key = __decoratorStrings$b[k2 + 5]; - var initializers = k2 > 3 && (array[j2 - 1] = []), extraInitializers = array[j2] || (array[j2] = []); - var desc = k2 && (!p2 && !s2 && (target = target.prototype), k2 < 5 && (k2 > 3 || !p2) && __getOwnPropDesc$b(k2 < 4 ? target : { get [name]() { - return __privateGet$a(this, extra); - }, set [name](x2) { - return __privateSet$a(this, extra, x2); - } }, name)); - k2 ? p2 && k2 < 4 && __name$1(extra, (k2 > 2 ? "set " : k2 > 1 ? "get " : "") + name) : __name$1(target, name); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$b(k2, name, done = {}, array[3], extraInitializers); - if (k2) { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: p2 ? (x2) => __privateIn(target, x2) : (x2) => name in x2 }; - if (k2 ^ 3) access.get = p2 ? (x2) => (k2 ^ 1 ? __privateGet$a : __privateMethod)(x2, target, k2 ^ 4 ? extra : desc.get) : (x2) => x2[name]; - if (k2 > 2) access.set = p2 ? (x2, y2) => __privateSet$a(x2, target, y2, k2 ^ 4 ? extra : desc.set) : (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])(k2 ? k2 < 4 ? p2 ? extra : desc[key] : k2 > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; - if (k2 ^ 4 || it === void 0) __expectFn$b(it) && (k2 > 4 ? initializers.unshift(it) : k2 ? p2 ? extra = it : desc[key] = it : target = it); - else if (typeof it !== "object" || it === null) __typeError$b("Object expected"); - else __expectFn$b(fn = it.get) && (desc.get = fn), __expectFn$b(fn = it.set) && (desc.set = fn), __expectFn$b(fn = it.init) && initializers.unshift(fn); - } - return k2 || __decoratorMetadata$b(array, target), desc && __defProp$h(target, name, desc), p2 ? k2 ^ 4 ? extra : desc : target; - }; - var __publicField$h = (obj, key, value) => __defNormalProp$h(obj, key + "", value); - var __accessCheck$a = (obj, member, msg2) => member.has(obj) || __typeError$b("Cannot " + msg2); - var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError$b('Cannot use the "in" operator on this value') : member.has(obj); - var __privateGet$a = (obj, member, getter) => (__accessCheck$a(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); - var __privateAdd$a = (obj, member, value) => member.has(obj) ? __typeError$b("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$a = (obj, member, value, setter) => (__accessCheck$a(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); - var __privateMethod = (obj, member, method) => (__accessCheck$a(obj, member, "access private method"), method); - var __pickerOpen_dec, __optionsOpen_dec, __optionsView_dec, _chartHoverWindowTimeMs_dec, _chartHoverTimeMs_dec, _zoomWindowRange_dec, _zoomRange_dec, _comparisonPreview_dec, _timelineEvents_dec, _isLiveEdge_dec, _sidebarCollapsed_dec, _dateSnapping_dec, _zoomLevel_dec, _rangeBounds_dec, _endTime_dec, _startTime_dec, _hass_dec$3, _a$b, _RangeToolbar_decorators, _init$b, _hass$3, _startTime, _endTime, _rangeBounds, _zoomLevel, _dateSnapping, _sidebarCollapsed, _isLiveEdge, _timelineEvents, _comparisonPreview, _zoomRange, _zoomWindowRange, _chartHoverTimeMs, _chartHoverWindowTimeMs, __optionsView, __optionsOpen, __pickerOpen; - _RangeToolbar_decorators = [localized()]; - class RangeToolbar extends (_a$b = i$2, _hass_dec$3 = [n({ attribute: false })], _startTime_dec = [n({ type: Object })], _endTime_dec = [n({ type: Object })], _rangeBounds_dec = [n({ type: Object })], _zoomLevel_dec = [n({ type: String, attribute: "zoom-level" })], _dateSnapping_dec = [n({ type: String, attribute: "date-snapping" })], _sidebarCollapsed_dec = [n({ type: Boolean, attribute: "sidebar-collapsed" })], _isLiveEdge_dec = [n({ type: Boolean, attribute: "is-live-edge" })], _timelineEvents_dec = [n({ type: Array, attribute: false })], _comparisonPreview_dec = [n({ type: Object, attribute: false })], _zoomRange_dec = [n({ type: Object, attribute: false })], _zoomWindowRange_dec = [n({ type: Object, attribute: false })], _chartHoverTimeMs_dec = [n({ type: Number, attribute: false })], _chartHoverWindowTimeMs_dec = [n({ type: Number, attribute: false })], __optionsView_dec = [r()], __optionsOpen_dec = [r()], __pickerOpen_dec = [r()], _a$b) { - constructor() { - super(...arguments); - __privateAdd$a(this, _hass$3, __runInitializers$b(_init$b, 8, this, null)), __runInitializers$b(_init$b, 11, this); - __privateAdd$a(this, _startTime, __runInitializers$b(_init$b, 12, this, null)), __runInitializers$b(_init$b, 15, this); - __privateAdd$a(this, _endTime, __runInitializers$b(_init$b, 16, this, null)), __runInitializers$b(_init$b, 19, this); - __privateAdd$a(this, _rangeBounds, __runInitializers$b(_init$b, 20, this, null)), __runInitializers$b(_init$b, 23, this); - __privateAdd$a(this, _zoomLevel, __runInitializers$b(_init$b, 24, this, "auto")), __runInitializers$b(_init$b, 27, this); - __privateAdd$a(this, _dateSnapping, __runInitializers$b(_init$b, 28, this, "hour")), __runInitializers$b(_init$b, 31, this); - __privateAdd$a(this, _sidebarCollapsed, __runInitializers$b(_init$b, 32, this, false)), __runInitializers$b(_init$b, 35, this); - __privateAdd$a(this, _isLiveEdge, __runInitializers$b(_init$b, 36, this, false)), __runInitializers$b(_init$b, 39, this); - __privateAdd$a(this, _timelineEvents, __runInitializers$b(_init$b, 40, this, [])), __runInitializers$b(_init$b, 43, this); - __privateAdd$a(this, _comparisonPreview, __runInitializers$b(_init$b, 44, this, null)), __runInitializers$b(_init$b, 47, this); - __privateAdd$a(this, _zoomRange, __runInitializers$b(_init$b, 48, this, null)), __runInitializers$b(_init$b, 51, this); - __privateAdd$a(this, _zoomWindowRange, __runInitializers$b(_init$b, 52, this, null)), __runInitializers$b(_init$b, 55, this); - __privateAdd$a(this, _chartHoverTimeMs, __runInitializers$b(_init$b, 56, this, null)), __runInitializers$b(_init$b, 59, this); - __privateAdd$a(this, _chartHoverWindowTimeMs, __runInitializers$b(_init$b, 60, this, null)), __runInitializers$b(_init$b, 63, this); - __privateAdd$a(this, __optionsView, __runInitializers$b(_init$b, 64, this, "root")), __runInitializers$b(_init$b, 67, this); - __privateAdd$a(this, __optionsOpen, __runInitializers$b(_init$b, 68, this, false)), __runInitializers$b(_init$b, 71, this); - __privateAdd$a(this, __pickerOpen, __runInitializers$b(_init$b, 72, this, false)), __runInitializers$b(_init$b, 75, this); - } - // ── Public API ───────────────────────────────────────────────────────────── - /** Sync mobile date inputs to the given start/end values. */ - syncMobileDates(start, end) { - const fmtInput = (d2) => { - if (!d2) return ""; - const pad = (n2) => String(n2).padStart(2, "0"); - return `${d2.getFullYear()}-${pad(d2.getMonth() + 1)}-${pad(d2.getDate())}T${pad(d2.getHours())}:${pad(d2.getMinutes())}`; - }; - const startEl = this.shadowRoot?.querySelector("#range-mobile-start"); - const endEl = this.shadowRoot?.querySelector("#range-mobile-end"); - if (startEl) startEl.value = fmtInput(start); - if (endEl) endEl.value = fmtInput(end); - } - /** Sync the options menu current-value labels. */ - syncOptionsLabels() { - const zoomLabel = RANGE_ZOOM_OPTIONS.find((o2) => o2.value === this.zoomLevel)?.label ?? "Auto"; - const snapLabel = RANGE_SNAP_OPTIONS.find((o2) => o2.value === this.dateSnapping)?.label ?? "Hour"; - const zoomCurrent = this.shadowRoot?.querySelector( - "[data-options-current='zoom']" - ); - const snapCurrent = this.shadowRoot?.querySelector( - "[data-options-current='snap']" - ); - if (zoomCurrent) { - zoomCurrent.textContent = msg(zoomLabel); - } - if (snapCurrent) { - snapCurrent.textContent = msg(snapLabel); - } - this.shadowRoot?.querySelectorAll("[data-option-group='zoom']").forEach((btn) => { - btn.classList.toggle( - "selected", - btn.dataset.optionValue === this.zoomLevel - ); - }); - this.shadowRoot?.querySelectorAll("[data-option-group='snap']").forEach((btn) => { - btn.classList.toggle( - "selected", - btn.dataset.optionValue === this.dateSnapping - ); - }); - } - /** Close all open floating menus. */ - closeMenus() { - if (this._pickerOpen) this._pickerOpen = false; - if (this._optionsOpen) { - this._optionsOpen = false; - this._optionsView = "root"; - } - } - /** Forward revealSelection to the inner timeline without exposing shadow DOM internals. */ - revealSelection() { - const panelTimeline = this.shadowRoot?.querySelector("#range-panel-timeline"); - if (!panelTimeline) { - return; - } - panelTimeline.revealSelection?.(); - } - syncZoomHighlights(zoomRange, zoomWindowRange) { - const panelTimeline = this.shadowRoot?.querySelector("#range-panel-timeline"); - if (!panelTimeline) { - return; - } - panelTimeline.syncZoomHighlights?.(zoomRange, zoomWindowRange); - } - // ── Private helpers ──────────────────────────────────────────────────────── - _emit(name, detail = {}) { - this.dispatchEvent( - new CustomEvent(name, { detail, bubbles: true, composed: true }) - ); - } - _computeMenuPosition(anchorEl, menuWidth) { - const viewportPadding = 8; - const anchorRect = anchorEl.getBoundingClientRect(); - const left = Math.max( - viewportPadding, - Math.min( - anchorRect.right - menuWidth, - window.innerWidth - menuWidth - viewportPadding - ) - ); - const top = Math.max(viewportPadding, anchorRect.bottom + 8); - return { left, top }; - } - _toggleOptions(force) { - const next = force !== void 0 ? force : !this._optionsOpen; - if (next) this._pickerOpen = false; - this._optionsOpen = next; - if (!next) this._optionsView = "root"; - if (next) { - const menuEl = this.shadowRoot?.querySelector( - "#range-options-menu" - ); - const btnEl = this.shadowRoot?.querySelector( - "#range-options-button" - ); - if (menuEl && btnEl) { - const { left, top } = this._computeMenuPosition( - btnEl, - Math.max(280, menuEl.offsetWidth || 280) - ); - menuEl.style.setProperty("--floating-menu-left", `${left}px`); - menuEl.style.setProperty("--floating-menu-top", `${top}px`); - } - } - this.updateComplete.then(() => this.syncOptionsLabels()); - } - _togglePicker(force) { - const next = force !== void 0 ? force : !this._pickerOpen; - if (next) this._optionsOpen = false; - this._pickerOpen = next; - if (next) { - const menuEl = this.shadowRoot?.querySelector("#range-picker-menu"); - const btnEl = this.shadowRoot?.querySelector( - "#range-picker-button" - ); - if (menuEl && btnEl) { - const { left, top } = this._computeMenuPosition( - btnEl, - Math.max(320, menuEl.offsetWidth || 320) - ); - menuEl.style.setProperty("--floating-menu-left", `${left}px`); - menuEl.style.setProperty("--floating-menu-top", `${top}px`); - } - } - } - // ── Event handlers ───────────────────────────────────────────────────────── - _onSidebarToggle() { - this._emit("dp-toolbar-sidebar-toggle"); - } - _onTimelineRangeCommit(ev) { - ev.stopPropagation(); - this._emit("dp-range-commit", { - start: ev.detail.start, - end: ev.detail.end, - push: ev.detail.push ?? false - }); - } - _onTimelineRangeDraft(ev) { - ev.stopPropagation(); - this._emit("dp-range-draft", { - start: ev.detail.start, - end: ev.detail.end - }); - } - _onPickerButtonClick() { - this._togglePicker(); - } - _onPickerMenuClose() { - this._togglePicker(false); - } - _onDatePickerChange(ev) { - const value = ev.detail?.value ?? ev.detail; - if (!value) return; - const start = value.start instanceof Date ? value.start : value.start; - const end = value.end instanceof Date ? value.end : value.end; - if (start && end) { - this._emit("dp-date-picker-change", { start, end }); - this._togglePicker(false); - } - } - _onOptionsButtonClick() { - this._toggleOptions(); - } - _onOptionsMenuClose() { - this._toggleOptions(false); - } - _onOptionsBack() { - this._optionsView = "root"; - } - _onOptionsSubmenu(submenu) { - this._optionsView = submenu; - } - _onOptionSelect(group, value) { - if (group === "zoom") { - this._emit("dp-zoom-level-change", { value }); - } else if (group === "snap") { - this._emit("dp-snap-change", { value }); - } - this._toggleOptions(false); - } - _onMobileStartChange(ev) { - const startEl = this.shadowRoot?.querySelector("#range-mobile-start"); - if (startEl) startEl.value = ev.detail.value; - this._commitMobileDates(); - } - _onMobileEndChange(ev) { - const endEl = this.shadowRoot?.querySelector("#range-mobile-end"); - if (endEl) endEl.value = ev.detail.value; - this._commitMobileDates(); - } - _commitMobileDates() { - const startEl = this.shadowRoot?.querySelector("#range-mobile-start"); - const endEl = this.shadowRoot?.querySelector("#range-mobile-end"); - const startVal = startEl?.value; - const endVal = endEl?.value; - if (!startVal || !endVal) return; - const start = new Date(startVal); - const end = new Date(endVal); - if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime()) || start >= end) - return; - this._emit("dp-range-commit", { start, end, push: true }); - } - // ── Render ───────────────────────────────────────────────────────────────── - _renderZoomOptions() { - return RANGE_ZOOM_OPTIONS.map( - (option) => b` + color: var(--primary-text-color); + font-size: 0.85rem; + font-family: inherit; + } + input:focus { + border-color: var(--primary-color, #03a9f4); + outline: none; + } +`; + //#endregion + //#region custom_components/hass_datapoints/src/atoms/form/date-time-input/date-time-input.ts + var _value_accessor_storage$4 = /* @__PURE__ */ new WeakMap(); + var _label_accessor_storage$4 = /* @__PURE__ */ new WeakMap(); + var DateTimeInput = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _value_accessor_storage$4, ""); + _classPrivateFieldInitSpec(this, _label_accessor_storage$4, ""); + } + get value() { + return _classPrivateFieldGet2(_value_accessor_storage$4, this); + } + set value(value) { + _classPrivateFieldSet2(_value_accessor_storage$4, this, value); + } + get label() { + return _classPrivateFieldGet2(_label_accessor_storage$4, this); + } + set label(value) { + _classPrivateFieldSet2(_label_accessor_storage$4, this, value); + } + _onChange(e) { + this.dispatchEvent(new CustomEvent("dp-datetime-change", { + detail: { value: e.target.value }, + bubbles: true, + composed: true + })); + } + render() { + return b` + ${this.label ? b`` : ""} + + `; + } + }; + _defineProperty(DateTimeInput, "styles", styles$17); + __decorate([n$1({ type: String })], DateTimeInput.prototype, "value", null); + __decorate([n$1({ type: String })], DateTimeInput.prototype, "label", null); + customElements.define("date-time-input", DateTimeInput); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/components/range-toolbar/range-toolbar.ts + var _RangeToolbar, _hass_accessor_storage$3, _startTime_accessor_storage, _endTime_accessor_storage, _rangeBounds_accessor_storage, _zoomLevel_accessor_storage, _dateSnapping_accessor_storage, _sidebarCollapsed_accessor_storage, _isLiveEdge_accessor_storage, _timelineEvents_accessor_storage, _comparisonPreview_accessor_storage, _zoomRange_accessor_storage, _zoomWindowRange_accessor_storage, _chartHoverTimeMs_accessor_storage, _chartHoverWindowTime_accessor_storage, _optionsView_accessor_storage, _optionsOpen_accessor_storage, _pickerOpen_accessor_storage; + var RangeToolbar = (_hass_accessor_storage$3 = /* @__PURE__ */ new WeakMap(), _startTime_accessor_storage = /* @__PURE__ */ new WeakMap(), _endTime_accessor_storage = /* @__PURE__ */ new WeakMap(), _rangeBounds_accessor_storage = /* @__PURE__ */ new WeakMap(), _zoomLevel_accessor_storage = /* @__PURE__ */ new WeakMap(), _dateSnapping_accessor_storage = /* @__PURE__ */ new WeakMap(), _sidebarCollapsed_accessor_storage = /* @__PURE__ */ new WeakMap(), _isLiveEdge_accessor_storage = /* @__PURE__ */ new WeakMap(), _timelineEvents_accessor_storage = /* @__PURE__ */ new WeakMap(), _comparisonPreview_accessor_storage = /* @__PURE__ */ new WeakMap(), _zoomRange_accessor_storage = /* @__PURE__ */ new WeakMap(), _zoomWindowRange_accessor_storage = /* @__PURE__ */ new WeakMap(), _chartHoverTimeMs_accessor_storage = /* @__PURE__ */ new WeakMap(), _chartHoverWindowTime_accessor_storage = /* @__PURE__ */ new WeakMap(), _optionsView_accessor_storage = /* @__PURE__ */ new WeakMap(), _optionsOpen_accessor_storage = /* @__PURE__ */ new WeakMap(), _pickerOpen_accessor_storage = /* @__PURE__ */ new WeakMap(), _RangeToolbar = class RangeToolbar extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _hass_accessor_storage$3, null); + _classPrivateFieldInitSpec(this, _startTime_accessor_storage, null); + _classPrivateFieldInitSpec(this, _endTime_accessor_storage, null); + _classPrivateFieldInitSpec(this, _rangeBounds_accessor_storage, null); + _classPrivateFieldInitSpec(this, _zoomLevel_accessor_storage, "auto"); + _classPrivateFieldInitSpec(this, _dateSnapping_accessor_storage, "hour"); + _classPrivateFieldInitSpec(this, _sidebarCollapsed_accessor_storage, false); + _classPrivateFieldInitSpec(this, _isLiveEdge_accessor_storage, false); + _classPrivateFieldInitSpec(this, _timelineEvents_accessor_storage, []); + _classPrivateFieldInitSpec(this, _comparisonPreview_accessor_storage, null); + _classPrivateFieldInitSpec(this, _zoomRange_accessor_storage, null); + _classPrivateFieldInitSpec(this, _zoomWindowRange_accessor_storage, null); + _classPrivateFieldInitSpec(this, _chartHoverTimeMs_accessor_storage, null); + _classPrivateFieldInitSpec(this, _chartHoverWindowTime_accessor_storage, null); + _classPrivateFieldInitSpec(this, _optionsView_accessor_storage, "root"); + _classPrivateFieldInitSpec(this, _optionsOpen_accessor_storage, false); + _classPrivateFieldInitSpec(this, _pickerOpen_accessor_storage, false); + } + get hass() { + return _classPrivateFieldGet2(_hass_accessor_storage$3, this); + } + set hass(value) { + _classPrivateFieldSet2(_hass_accessor_storage$3, this, value); + } + get startTime() { + return _classPrivateFieldGet2(_startTime_accessor_storage, this); + } + set startTime(value) { + _classPrivateFieldSet2(_startTime_accessor_storage, this, value); + } + get endTime() { + return _classPrivateFieldGet2(_endTime_accessor_storage, this); + } + set endTime(value) { + _classPrivateFieldSet2(_endTime_accessor_storage, this, value); + } + get rangeBounds() { + return _classPrivateFieldGet2(_rangeBounds_accessor_storage, this); + } + set rangeBounds(value) { + _classPrivateFieldSet2(_rangeBounds_accessor_storage, this, value); + } + get zoomLevel() { + return _classPrivateFieldGet2(_zoomLevel_accessor_storage, this); + } + set zoomLevel(value) { + _classPrivateFieldSet2(_zoomLevel_accessor_storage, this, value); + } + get dateSnapping() { + return _classPrivateFieldGet2(_dateSnapping_accessor_storage, this); + } + set dateSnapping(value) { + _classPrivateFieldSet2(_dateSnapping_accessor_storage, this, value); + } + get sidebarCollapsed() { + return _classPrivateFieldGet2(_sidebarCollapsed_accessor_storage, this); + } + set sidebarCollapsed(value) { + _classPrivateFieldSet2(_sidebarCollapsed_accessor_storage, this, value); + } + get isLiveEdge() { + return _classPrivateFieldGet2(_isLiveEdge_accessor_storage, this); + } + set isLiveEdge(value) { + _classPrivateFieldSet2(_isLiveEdge_accessor_storage, this, value); + } + get timelineEvents() { + return _classPrivateFieldGet2(_timelineEvents_accessor_storage, this); + } + set timelineEvents(value) { + _classPrivateFieldSet2(_timelineEvents_accessor_storage, this, value); + } + get comparisonPreview() { + return _classPrivateFieldGet2(_comparisonPreview_accessor_storage, this); + } + set comparisonPreview(value) { + _classPrivateFieldSet2(_comparisonPreview_accessor_storage, this, value); + } + get zoomRange() { + return _classPrivateFieldGet2(_zoomRange_accessor_storage, this); + } + set zoomRange(value) { + _classPrivateFieldSet2(_zoomRange_accessor_storage, this, value); + } + get zoomWindowRange() { + return _classPrivateFieldGet2(_zoomWindowRange_accessor_storage, this); + } + set zoomWindowRange(value) { + _classPrivateFieldSet2(_zoomWindowRange_accessor_storage, this, value); + } + get chartHoverTimeMs() { + return _classPrivateFieldGet2(_chartHoverTimeMs_accessor_storage, this); + } + set chartHoverTimeMs(value) { + _classPrivateFieldSet2(_chartHoverTimeMs_accessor_storage, this, value); + } + get chartHoverWindowTimeMs() { + return _classPrivateFieldGet2(_chartHoverWindowTime_accessor_storage, this); + } + set chartHoverWindowTimeMs(value) { + _classPrivateFieldSet2(_chartHoverWindowTime_accessor_storage, this, value); + } + get _optionsView() { + return _classPrivateFieldGet2(_optionsView_accessor_storage, this); + } + set _optionsView(value) { + _classPrivateFieldSet2(_optionsView_accessor_storage, this, value); + } + get _optionsOpen() { + return _classPrivateFieldGet2(_optionsOpen_accessor_storage, this); + } + set _optionsOpen(value) { + _classPrivateFieldSet2(_optionsOpen_accessor_storage, this, value); + } + get _pickerOpen() { + return _classPrivateFieldGet2(_pickerOpen_accessor_storage, this); + } + set _pickerOpen(value) { + _classPrivateFieldSet2(_pickerOpen_accessor_storage, this, value); + } + /** Sync mobile date inputs to the given start/end values. */ + syncMobileDates(start, end) { + const fmtInput = (d) => { + if (!d) return ""; + const pad = (n) => String(n).padStart(2, "0"); + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`; + }; + const startEl = this.shadowRoot?.querySelector("#range-mobile-start"); + const endEl = this.shadowRoot?.querySelector("#range-mobile-end"); + if (startEl) startEl.value = fmtInput(start); + if (endEl) endEl.value = fmtInput(end); + } + /** Sync the options menu current-value labels. */ + syncOptionsLabels() { + const zoomLabel = RANGE_ZOOM_OPTIONS.find((o) => o.value === this.zoomLevel)?.label ?? "Auto"; + const snapLabel = RANGE_SNAP_OPTIONS.find((o) => o.value === this.dateSnapping)?.label ?? "Hour"; + const zoomCurrent = this.shadowRoot?.querySelector("[data-options-current='zoom']"); + const snapCurrent = this.shadowRoot?.querySelector("[data-options-current='snap']"); + if (zoomCurrent) zoomCurrent.textContent = msg(zoomLabel); + if (snapCurrent) snapCurrent.textContent = msg(snapLabel); + this.shadowRoot?.querySelectorAll("[data-option-group='zoom']").forEach((btn) => { + btn.classList.toggle("selected", btn.dataset.optionValue === this.zoomLevel); + }); + this.shadowRoot?.querySelectorAll("[data-option-group='snap']").forEach((btn) => { + btn.classList.toggle("selected", btn.dataset.optionValue === this.dateSnapping); + }); + } + /** Close all open floating menus. */ + closeMenus() { + if (this._pickerOpen) this._pickerOpen = false; + if (this._optionsOpen) { + this._optionsOpen = false; + this._optionsView = "root"; + } + } + /** Forward revealSelection to the inner timeline without exposing shadow DOM internals. */ + revealSelection() { + const panelTimeline = this.shadowRoot?.querySelector("#range-panel-timeline"); + if (!panelTimeline) return; + panelTimeline.revealSelection?.(); + } + syncZoomHighlights(zoomRange, zoomWindowRange) { + const panelTimeline = this.shadowRoot?.querySelector("#range-panel-timeline"); + if (!panelTimeline) return; + panelTimeline.syncZoomHighlights?.(zoomRange, zoomWindowRange); + } + _emit(name, detail = {}) { + this.dispatchEvent(new CustomEvent(name, { + detail, + bubbles: true, + composed: true + })); + } + _computeMenuPosition(anchorEl, menuWidth) { + const viewportPadding = 8; + const anchorRect = anchorEl.getBoundingClientRect(); + return { + left: Math.max(viewportPadding, Math.min(anchorRect.right - menuWidth, window.innerWidth - menuWidth - viewportPadding)), + top: Math.max(viewportPadding, anchorRect.bottom + 8) + }; + } + _toggleOptions(force) { + const next = force !== void 0 ? force : !this._optionsOpen; + if (next) this._pickerOpen = false; + this._optionsOpen = next; + if (!next) this._optionsView = "root"; + if (next) { + const menuEl = this.shadowRoot?.querySelector("#range-options-menu"); + const btnEl = this.shadowRoot?.querySelector("#range-options-button"); + if (menuEl && btnEl) { + const { left, top } = this._computeMenuPosition(btnEl, Math.max(280, menuEl.offsetWidth || 280)); + menuEl.style.setProperty("--floating-menu-left", `${left}px`); + menuEl.style.setProperty("--floating-menu-top", `${top}px`); + } + } + this.updateComplete.then(() => this.syncOptionsLabels()); + } + _togglePicker(force) { + const next = force !== void 0 ? force : !this._pickerOpen; + if (next) this._optionsOpen = false; + this._pickerOpen = next; + if (next) { + const menuEl = this.shadowRoot?.querySelector("#range-picker-menu"); + const btnEl = this.shadowRoot?.querySelector("#range-picker-button"); + if (menuEl && btnEl) { + const { left, top } = this._computeMenuPosition(btnEl, Math.max(320, menuEl.offsetWidth || 320)); + menuEl.style.setProperty("--floating-menu-left", `${left}px`); + menuEl.style.setProperty("--floating-menu-top", `${top}px`); + } + } + } + _onSidebarToggle() { + this._emit("dp-toolbar-sidebar-toggle"); + } + _onTimelineRangeCommit(ev) { + ev.stopPropagation(); + this._emit("dp-range-commit", { + start: ev.detail.start, + end: ev.detail.end, + push: ev.detail.push ?? false + }); + } + _onTimelineRangeDraft(ev) { + ev.stopPropagation(); + this._emit("dp-range-draft", { + start: ev.detail.start, + end: ev.detail.end + }); + } + _onPickerButtonClick() { + this._togglePicker(); + } + _onPickerMenuClose() { + this._togglePicker(false); + } + _onDatePickerChange(ev) { + const value = ev.detail?.value ?? ev.detail; + if (!value) return; + const start = value.start instanceof Date ? value.start : value.start; + const end = value.end instanceof Date ? value.end : value.end; + if (start && end) { + this._emit("dp-date-picker-change", { + start, + end + }); + this._togglePicker(false); + } + } + _onOptionsButtonClick() { + this._toggleOptions(); + } + _onOptionsMenuClose() { + this._toggleOptions(false); + } + _onOptionsBack() { + this._optionsView = "root"; + } + _onOptionsSubmenu(submenu) { + this._optionsView = submenu; + } + _onOptionSelect(group, value) { + if (group === "zoom") this._emit("dp-zoom-level-change", { value }); + else if (group === "snap") this._emit("dp-snap-change", { value }); + this._toggleOptions(false); + } + _onMobileStartChange(ev) { + const startEl = this.shadowRoot?.querySelector("#range-mobile-start"); + if (startEl) startEl.value = ev.detail.value; + this._commitMobileDates(); + } + _onMobileEndChange(ev) { + const endEl = this.shadowRoot?.querySelector("#range-mobile-end"); + if (endEl) endEl.value = ev.detail.value; + this._commitMobileDates(); + } + _commitMobileDates() { + const startEl = this.shadowRoot?.querySelector("#range-mobile-start"); + const endEl = this.shadowRoot?.querySelector("#range-mobile-end"); + const startVal = startEl?.value; + const endVal = endEl?.value; + if (!startVal || !endVal) return; + const start = new Date(startVal); + const end = new Date(endVal); + if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime()) || start >= end) return; + this._emit("dp-range-commit", { + start, + end, + push: true + }); + } + _renderZoomOptions() { + return RANGE_ZOOM_OPTIONS.map((option) => b` - ` - ); - } - _renderSnapOptions() { - return RANGE_SNAP_OPTIONS.map( - (option) => b` + `); + } + _renderSnapOptions() { + return RANGE_SNAP_OPTIONS.map((option) => b` - ` - ); - } - render() { - const sidebarToggleIcon = this.sidebarCollapsed ? "mdi:chevron-right" : "mdi:chevron-left"; - return b` + `); + } + render() { + const sidebarToggleIcon = this.sidebarCollapsed ? "mdi:chevron-right" : "mdi:chevron-left"; + return b`
@@ -27197,724 +26964,563 @@ ${content.alert}` : "",
`; - } - updated() { - this.syncOptionsLabels(); - } - } - _init$b = __decoratorStart$b(_a$b); - _hass$3 = /* @__PURE__ */ new WeakMap(); - _startTime = /* @__PURE__ */ new WeakMap(); - _endTime = /* @__PURE__ */ new WeakMap(); - _rangeBounds = /* @__PURE__ */ new WeakMap(); - _zoomLevel = /* @__PURE__ */ new WeakMap(); - _dateSnapping = /* @__PURE__ */ new WeakMap(); - _sidebarCollapsed = /* @__PURE__ */ new WeakMap(); - _isLiveEdge = /* @__PURE__ */ new WeakMap(); - _timelineEvents = /* @__PURE__ */ new WeakMap(); - _comparisonPreview = /* @__PURE__ */ new WeakMap(); - _zoomRange = /* @__PURE__ */ new WeakMap(); - _zoomWindowRange = /* @__PURE__ */ new WeakMap(); - _chartHoverTimeMs = /* @__PURE__ */ new WeakMap(); - _chartHoverWindowTimeMs = /* @__PURE__ */ new WeakMap(); - __optionsView = /* @__PURE__ */ new WeakMap(); - __optionsOpen = /* @__PURE__ */ new WeakMap(); - __pickerOpen = /* @__PURE__ */ new WeakMap(); - __decorateElement$b(_init$b, 4, "hass", _hass_dec$3, RangeToolbar, _hass$3); - __decorateElement$b(_init$b, 4, "startTime", _startTime_dec, RangeToolbar, _startTime); - __decorateElement$b(_init$b, 4, "endTime", _endTime_dec, RangeToolbar, _endTime); - __decorateElement$b(_init$b, 4, "rangeBounds", _rangeBounds_dec, RangeToolbar, _rangeBounds); - __decorateElement$b(_init$b, 4, "zoomLevel", _zoomLevel_dec, RangeToolbar, _zoomLevel); - __decorateElement$b(_init$b, 4, "dateSnapping", _dateSnapping_dec, RangeToolbar, _dateSnapping); - __decorateElement$b(_init$b, 4, "sidebarCollapsed", _sidebarCollapsed_dec, RangeToolbar, _sidebarCollapsed); - __decorateElement$b(_init$b, 4, "isLiveEdge", _isLiveEdge_dec, RangeToolbar, _isLiveEdge); - __decorateElement$b(_init$b, 4, "timelineEvents", _timelineEvents_dec, RangeToolbar, _timelineEvents); - __decorateElement$b(_init$b, 4, "comparisonPreview", _comparisonPreview_dec, RangeToolbar, _comparisonPreview); - __decorateElement$b(_init$b, 4, "zoomRange", _zoomRange_dec, RangeToolbar, _zoomRange); - __decorateElement$b(_init$b, 4, "zoomWindowRange", _zoomWindowRange_dec, RangeToolbar, _zoomWindowRange); - __decorateElement$b(_init$b, 4, "chartHoverTimeMs", _chartHoverTimeMs_dec, RangeToolbar, _chartHoverTimeMs); - __decorateElement$b(_init$b, 4, "chartHoverWindowTimeMs", _chartHoverWindowTimeMs_dec, RangeToolbar, _chartHoverWindowTimeMs); - __decorateElement$b(_init$b, 4, "_optionsView", __optionsView_dec, RangeToolbar, __optionsView); - __decorateElement$b(_init$b, 4, "_optionsOpen", __optionsOpen_dec, RangeToolbar, __optionsOpen); - __decorateElement$b(_init$b, 4, "_pickerOpen", __pickerOpen_dec, RangeToolbar, __pickerOpen); - RangeToolbar = __decorateElement$b(_init$b, 0, "RangeToolbar", _RangeToolbar_decorators, RangeToolbar); - __publicField$h(RangeToolbar, "styles", styles$j); - __runInitializers$b(_init$b, 1, RangeToolbar); - customElements.define("range-toolbar", RangeToolbar); - function createInitialHistoryPageState() { - return { - display: { - sidebarCollapsed: false - }, - range: { - startTime: null, - endTime: null, - previewZoomRange: null, - committedZoomRange: null - }, - comparison: { - windows: [], - selectedWindowId: null, - hoveredWindowId: null - }, - targets: { - selection: {}, - rawSelection: {}, - rows: [] - } - }; - } - function createHistoryPageAppStateContext() { - const state = createInitialHistoryPageState(); - return { - state, - setSidebarCollapsed(value) { - state.display.sidebarCollapsed = value; - }, - setRange(startTime, endTime) { - state.range.startTime = startTime; - state.range.endTime = endTime; - }, - setPreviewZoomRange(value) { - state.range.previewZoomRange = value ? { ...value } : null; - }, - setCommittedZoomRange(value) { - state.range.committedZoomRange = value ? { ...value } : null; - }, - setTargetSelection(value) { - state.targets.selection = { ...value || {} }; - }, - setTargetSelectionRaw(value) { - state.targets.rawSelection = { ...value || {} }; - }, - setSeriesRows(rows) { - state.targets.rows = Array.isArray(rows) ? rows.map((row) => ({ ...row })) : []; - }, - setComparisonWindows(windows) { - state.comparison.windows = Array.isArray(windows) ? windows.map((window2) => ({ ...window2 })) : []; - }, - setSelectedComparisonWindowId(value) { - state.comparison.selectedWindowId = value || null; - }, - setHoveredComparisonWindowId(value) { - state.comparison.hoveredWindowId = value || null; - } - }; - } - function createHistoryPageFetchContext(getHass) { - const state = { - historyBoundsLoaded: false, - historyBoundsLoading: false, - preferencesLoaded: false, - preferencesLoading: false, - savedPageLoaded: false, - hasSavedPage: false, - timelineEventsLoading: false, - timelineEventsKey: "" - }; - let historyBoundsPromise = null; - let preferencesPromise = null; - let savedPagePromise = null; - let timelineEventsPromise = null; - return { - state, - async ensureHistoryBounds(options) { - const hass = getHass(); - if (!hass || state.historyBoundsLoaded || historyBoundsPromise) { - return historyBoundsPromise ?? Promise.resolve(); - } - state.historyBoundsLoading = true; - historyBoundsPromise = fetchEventBounds(hass).then(({ start, end }) => { - options.onSuccess({ start, end }); - state.historyBoundsLoaded = true; - }).catch((error) => { - state.historyBoundsLoaded = true; - options.onError?.(error); - }).finally(() => { - state.historyBoundsLoading = false; - historyBoundsPromise = null; - }); - return historyBoundsPromise; - }, - async ensureUserPreferences(options) { - const hass = getHass(); - if (!hass || state.preferencesLoaded || preferencesPromise) { - return preferencesPromise ?? Promise.resolve(); - } - state.preferencesLoading = true; - preferencesPromise = fetchUserData( - hass, - options.preferencesKey, - options.fallbackValue - ).then((preferences) => { - options.onSuccess(preferences); - state.preferencesLoaded = true; - }).catch((error) => { - state.preferencesLoaded = true; - options.onError?.(error); - }).finally(() => { - state.preferencesLoading = false; - preferencesPromise = null; - }); - return preferencesPromise; - }, - async loadSavedPageIndicator(options) { - const hass = getHass(); - if (!hass || state.savedPageLoaded || savedPagePromise) { - return savedPagePromise ?? Promise.resolve(); - } - state.savedPageLoaded = true; - savedPagePromise = fetchUserData( - hass, - options.savedPageKey, - options.fallbackValue - ).then((saved) => { - state.hasSavedPage = !!saved; - options.onSuccess(saved ?? null); - }).catch((error) => { - options.onError?.(error); - }).finally(() => { - savedPagePromise = null; - }); - return savedPagePromise; - }, - resetTimelineEvents() { - state.timelineEventsKey = ""; - }, - async loadTimelineEvents(options) { - const hass = getHass(); - const key = `${options.startIso}|${options.endIso}|${options.datapointScope}|${options.entityIds.join(",")}`; - if (!hass || state.timelineEventsKey === key || timelineEventsPromise) { - return timelineEventsPromise ?? Promise.resolve(); - } - if (options.datapointScope === "linked" && options.entityIds.length === 0) { - state.timelineEventsKey = key; - options.onSuccess([], key); - return Promise.resolve(); - } - state.timelineEventsLoading = true; - timelineEventsPromise = fetchEvents( - hass, - options.startIso, - options.endIso, - options.datapointScope === "linked" ? options.entityIds : void 0 - ).then((events) => { - const normalized = Array.isArray(events) ? events : []; - state.timelineEventsKey = key; - options.onSuccess(normalized, key); - }).catch((error) => { - options.onError?.(error); - }).finally(() => { - state.timelineEventsLoading = false; - timelineEventsPromise = null; - }); - return timelineEventsPromise; - } - }; - } - function createHistoryPageNavigationContext() { - return { - readStateFromLocation() { - const url = new URL(window.location.href); - const entityFromUrl = url.searchParams.get("entity_id"); - const deviceFromUrl = url.searchParams.get("device_id"); - const areaFromUrl = url.searchParams.get("area_id"); - const labelFromUrl = url.searchParams.get("label_id"); - const datapointsScopeFromUrl = url.searchParams.get("datapoints_scope"); - const startFromUrl = url.searchParams.get("start_time"); - const endFromUrl = url.searchParams.get("end_time"); - const zoomStartFromUrl = url.searchParams.get("zoom_start_time"); - const zoomEndFromUrl = url.searchParams.get("zoom_end_time"); - const seriesColorsFromUrl = parseSeriesColorsParam( - url.searchParams.get("series_colors") - ); - const dateWindowsFromUrl = parseDateWindowsParam( - url.searchParams.get("date_windows") - ); - const pageStateFromUrl = parseHistoryPageStateParam( - url.searchParams.get("page_state") - ); - const hoursToShowRaw = Number.parseInt( - url.searchParams.get("hours_to_show") || "", - 10 - ); - const hasTargetInUrl = !!(entityFromUrl || deviceFromUrl || areaFromUrl || labelFromUrl); - const hasRangeInUrl = !!startFromUrl && !!endFromUrl; - return { - entityFromUrl, - deviceFromUrl, - areaFromUrl, - labelFromUrl, - datapointsScopeFromUrl, - startFromUrl, - endFromUrl, - zoomStartFromUrl, - zoomEndFromUrl, - seriesColorsFromUrl, - dateWindowsFromUrl, - pageStateFromUrl, - hoursFromUrl: Number.isFinite(hoursToShowRaw) ? hoursToShowRaw : NaN, - hasTargetInUrl, - hasRangeInUrl, - sessionState: readHistoryPageSessionState() - }; - }, - readSessionState() { - return readHistoryPageSessionState(); - }, - saveSessionState(source) { - writeHistoryPageSessionState( - source - ); - }, - updateUrl(options) { - const url = new URL(window.location.href); - const target = options.entities.length > 0 ? { entity_id: [...options.entities] } : {}; - if (target.entity_id?.length) { - url.searchParams.set("entity_id", target.entity_id.join(",")); - } else { - url.searchParams.delete("entity_id"); - } - url.searchParams.delete("device_id"); - url.searchParams.delete("area_id"); - url.searchParams.delete("label_id"); - if (options.datapointScope === "all") { - url.searchParams.set("datapoints_scope", "all"); - } else if (options.datapointScope === "hidden") { - url.searchParams.set("datapoints_scope", "hidden"); - } else { - url.searchParams.delete("datapoints_scope"); - } - if (options.startTime && options.endTime) { - url.searchParams.set("start_time", options.startTime.toISOString()); - url.searchParams.set("end_time", options.endTime.toISOString()); - url.searchParams.set("hours_to_show", String(options.hours)); - } else { - url.searchParams.delete("start_time"); - url.searchParams.delete("end_time"); - url.searchParams.delete("hours_to_show"); - } - if (options.committedZoomRange) { - url.searchParams.set( - "zoom_start_time", - new Date(options.committedZoomRange.start).toISOString() - ); - url.searchParams.set( - "zoom_end_time", - new Date(options.committedZoomRange.end).toISOString() - ); - } else { - url.searchParams.delete("zoom_start_time"); - url.searchParams.delete("zoom_end_time"); - } - const dateWindowsParam = serializeDateWindowsParam( - options.comparisonWindows - ); - if (dateWindowsParam) { - url.searchParams.set("date_windows", dateWindowsParam); - } else { - url.searchParams.delete("date_windows"); - } - const pageStateParam = serializeHistoryPageStateParam(options.pageState); - if (pageStateParam) { - url.searchParams.set("page_state", pageStateParam); - } else { - url.searchParams.delete("page_state"); - } - const seriesColorEntries = options.seriesRows.map((row) => { - const key = options.seriesColorQueryKey(row.entity_id); - return key && /^#[0-9a-f]{6}$/i.test(row.color || "") ? `${encodeURIComponent(key)}:${row.color.toLowerCase()}` : null; - }).filter(Boolean); - if (seriesColorEntries.length) { - url.searchParams.set("series_colors", seriesColorEntries.join(",")); - } else { - url.searchParams.delete("series_colors"); - } - const nextUrl = `${url.pathname}${url.search}`; - const currentUrl = `${window.location.pathname}${window.location.search}`; - if (nextUrl === currentUrl) { - return; - } - if (options.push) { - window.history.pushState(null, "", nextUrl); - } else { - window.history.replaceState(null, "", nextUrl); - } - } - }; - } - function getInnerHistoryChart(chartEl) { - if (!chartEl?.shadowRoot) { - return null; - } - return chartEl.shadowRoot.querySelector?.("hass-datapoints-history-chart") ?? chartEl.shadowRoot.querySelector?.("dp-history-chart") ?? chartEl.shadowRoot.querySelector?.("history-chart") ?? null; - } - function getComparisonTabsHost(chartEl) { - const innerChart = getInnerHistoryChart(chartEl); - return innerChart?.querySelector?.("#chart-top-slot") ?? null; - } - function ensureCollapsedPickerAnchor(targetControl, anchorEl) { - const assignedSlot = targetControl.assignedSlot ?? null; - if (!assignedSlot) { - return; - } - const slotContainer = assignedSlot.parentElement instanceof HTMLElement ? assignedSlot.parentElement : null; - let hiddenAnchorHost = null; - if (slotContainer && window.getComputedStyle(slotContainer).display === "none") { - hiddenAnchorHost = slotContainer; - } else if (window.getComputedStyle(assignedSlot).display === "none") { - hiddenAnchorHost = assignedSlot; - } - if (!hiddenAnchorHost) { - return; - } - const anchorRect = anchorEl?.getBoundingClientRect(); - const popupWidth = Math.max(320, Math.round(anchorRect?.width ?? 1)); - const viewportPadding = 8; - const left = Math.max( - viewportPadding, - Math.min( - Math.round(anchorRect?.left ?? 0), - window.innerWidth - popupWidth - viewportPadding - ) - ); - const top = Math.round(anchorRect?.top ?? 0); - const width = popupWidth; - const height = Math.max(1, Math.round(anchorRect?.height ?? 1)); - hiddenAnchorHost.style.display = "block"; - hiddenAnchorHost.style.position = "fixed"; - hiddenAnchorHost.style.left = `${left}px`; - hiddenAnchorHost.style.top = `${top}px`; - hiddenAnchorHost.style.width = `${width}px`; - hiddenAnchorHost.style.height = `${height}px`; - hiddenAnchorHost.style.margin = "0"; - hiddenAnchorHost.style.padding = "0"; - hiddenAnchorHost.style.opacity = "0"; - hiddenAnchorHost.style.pointerEvents = "none"; - hiddenAnchorHost.style.overflow = "visible"; - hiddenAnchorHost.style.zIndex = "2147483647"; - targetControl.style.display = "block"; - targetControl.style.width = `${width}px`; - targetControl.style.height = `${height}px`; - targetControl.style.opacity = "0"; - targetControl.style.pointerEvents = "none"; - } - function createHistoryPageOrchestrationContext() { - let chartResizeRaf = null; - return { - requestChartResizeRedraw(chartEl) { - if (chartResizeRaf != null) { - return; - } - let ranSynchronously = false; - const rafId = window.requestAnimationFrame(() => { - ranSynchronously = true; - chartResizeRaf = null; - if (!chartEl) { - return; - } - if (Array.isArray( - chartEl._lastDrawArgs - ) && chartEl._lastDrawArgs.length > 0 && typeof chartEl._drawChart === "function") { - chartEl._drawChart( - ...chartEl._lastDrawArgs - ); - return; - } - const innerChart = getInnerHistoryChart(chartEl); - if (innerChart && Array.isArray( - innerChart._lastDrawArgs - ) && innerChart._lastDrawArgs.length > 0) { - if (typeof innerChart._queueDrawChart === "function") { - innerChart._queueDrawChart( - ...innerChart._lastDrawArgs - ); - return; - } - if (typeof innerChart._drawChart === "function") { - innerChart._drawChart( - ...innerChart._lastDrawArgs - ); - } - } - }); - chartResizeRaf = ranSynchronously ? null : rafId; - }, - cancelChartResizeRedraw() { - if (chartResizeRaf != null) { - window.cancelAnimationFrame(chartResizeRaf); - chartResizeRaf = null; - } - }, - openTargetPicker(targetControl, anchorEl) { - if (!targetControl) { - return; - } - ensureCollapsedPickerAnchor(targetControl, anchorEl); - const genericPicker = targetControl.shadowRoot?.querySelector?.( - "ha-generic-picker" - ) ?? null; - if (genericPicker && typeof genericPicker.open === "function") { - genericPicker.open(); - return; - } - if (typeof targetControl.focus === "function") { - targetControl.focus(); - } - if (typeof targetControl.click === "function") { - targetControl.click(); - } - }, - renderComparisonTabs(options) { - const tabsEl = getComparisonTabsHost(options.chartEl); - if (!tabsEl || !options.startTime || !options.endTime) { - return { - comparisonTabRailComp: options.comparisonTabRailComp, - comparisonTabsHostEl: options.comparisonTabsHostEl - }; - } - const activeComparisonWindowId = options.selectedComparisonWindowId || null; - const currentTab = options.startTime && options.endTime ? { - id: "current-range", - label: "Selected range", - detail: options.formatComparisonLabel( - options.startTime, - options.endTime - ), - active: activeComparisonWindowId == null, - editable: false - } : null; - const tabs = [ - ...currentTab ? [currentTab] : [], - ...options.comparisonWindows.map((window2) => ({ - ...window2, - detail: options.formatComparisonLabel( - new Date(window2.start_time), - new Date(window2.end_time) - ), - active: window2.id === activeComparisonWindowId, - editable: true - })) - ]; - tabsEl.hidden = false; - let comparisonTabRailComp = options.comparisonTabRailComp; - let comparisonTabsHostEl = options.comparisonTabsHostEl; - if (!comparisonTabRailComp || comparisonTabsHostEl !== tabsEl) { - tabsEl.innerHTML = ""; - const rail = document.createElement("comparison-tab-rail"); - rail.addEventListener( - "dp-tab-activate", - (ev) => options.onActivate(ev.detail.tabId) - ); - rail.addEventListener( - "dp-tab-hover", - (ev) => options.onHover(ev.detail.tabId) - ); - rail.addEventListener( - "dp-tab-leave", - (ev) => options.onLeave(ev.detail.tabId) - ); - rail.addEventListener( - "dp-tab-edit", - (ev) => options.onEdit(ev.detail.tabId) - ); - rail.addEventListener( - "dp-tab-delete", - (ev) => options.onDelete(ev.detail.tabId) - ); - rail.addEventListener("dp-tab-add", () => options.onAdd()); - tabsEl.appendChild(rail); - comparisonTabRailComp = rail; - comparisonTabsHostEl = tabsEl; - } - comparisonTabRailComp.tabs = tabs; - comparisonTabRailComp.loadingIds = [...options.loadingComparisonWindowIds]; - comparisonTabRailComp.hoveredId = options.hoveredComparisonWindowId || ""; - return { - comparisonTabRailComp, - comparisonTabsHostEl - }; - }, - updateComparisonTabsOverflow(chartEl) { - window.requestAnimationFrame(() => { - const innerChart = getInnerHistoryChart(chartEl); - const shell = innerChart?.querySelector?.("#chart-tabs-shell") ?? null; - const rail = innerChart?.querySelector?.("#chart-tabs-rail") ?? null; - if (!shell || !rail) { - return; - } - shell.classList.toggle( - "overflowing", - rail.scrollWidth > rail.clientWidth + 4 - ); - }); - }, - handleComparisonTabHover(options) { - if (!options.id || options.hoveredComparisonWindowId === options.id) { - return; - } - options.setHoveredComparisonWindowId(options.id); - options.updateComparisonRangePreview(); - options.updateChartHoverIndicator(); - options.renderContent(); - }, - handleComparisonTabLeave(options) { - if (!options.id || options.hoveredComparisonWindowId !== options.id) { - return; - } - options.setHoveredComparisonWindowId(null); - options.updateComparisonRangePreview(); - options.updateChartHoverIndicator(); - options.renderContent(); - }, - handleComparisonTabActivate(options) { - if (!options.id || options.id === "current-range") { - options.setSelectedComparisonWindowId(null); - options.setHoveredComparisonWindowId(null); - options.clearDeltaAnalysisSelectionState(); - options.updateComparisonRangePreview(); - options.renderComparisonTabs(); - options.setAdjustComparisonAxisScale(false); - options.renderContent(); - return; - } - const targetWindow = options.comparisonWindows.find( - (window2) => window2.id === options.id - ); - if (!targetWindow) { - return; - } - const nextSelectedWindowId = options.selectedComparisonWindowId === options.id ? null : options.id; - options.setSelectedComparisonWindowId(nextSelectedWindowId); - if (!nextSelectedWindowId) { - options.clearDeltaAnalysisSelectionState(); - } - options.updateComparisonRangePreview(); - options.updateChartHoverIndicator(); - options.renderContent(); - } - }; - } - function escapeXml(value) { - return String(value ?? "").replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); - } - function sanitizeWorksheetName(name) { - const cleaned = String(name || "Sheet").replace(/[\\/*?:[\]]/g, " ").trim(); - return cleaned.slice(0, 31) || "Sheet"; - } - function columnNumberToName(index) { - let value = index + 1; - let name = ""; - while (value > 0) { - const remainder = (value - 1) % 26; - name = String.fromCharCode(65 + remainder) + name; - value = Math.floor((value - 1) / 26); - } - return name; - } - function toIsoString(value) { - if (!value) { - return ""; - } - const date = value instanceof Date ? value : new Date(value); - if (Number.isNaN(date.getTime())) { - return ""; - } - return date.toISOString(); - } - function normalizeHistoryTimestamp(rawTimestamp) { - if (typeof rawTimestamp === "number") { - if (rawTimestamp > 1e11) { - return rawTimestamp; - } - return rawTimestamp * 1e3; - } - const timestamp = new Date( - rawTimestamp ?? 0 - ).getTime(); - if (!Number.isFinite(timestamp)) { - return null; - } - return timestamp; - } - function getHistoryStatesForEntity(entityId, histResult, entityIds) { - if (!histResult) { - return []; - } - const asRecord = histResult; - if (typeof histResult === "object" && !Array.isArray(histResult) && Array.isArray(asRecord?.[entityId])) { - return asRecord[entityId]; - } - if (Array.isArray(histResult)) { - const entityIndex = entityIds.indexOf(entityId); - if (entityIndex >= 0 && Array.isArray(histResult[entityIndex])) { - return histResult[entityIndex]; - } - if (histResult.every( - (entry) => entry && typeof entry === "object" && !Array.isArray(entry) - )) { - return histResult.filter( - (entry) => entry.entity_id === entityId - ); - } - } - if (histResult && typeof histResult === "object") { - const withResult = histResult; - if (Array.isArray( - withResult.result?.[entityId] - )) { - return withResult.result[entityId]; - } - if (Array.isArray(withResult.result)) { - const entityIndex = entityIds.indexOf(entityId); - if (entityIndex >= 0 && Array.isArray(withResult.result[entityIndex])) { - return withResult.result[entityIndex]; - } - } - } - return []; - } - function createWorksheetXml(rows) { - const rowXml = rows.map((row, rowIndex) => { - const cellXml = row.map((cell, cellIndex) => { - const cellRef = `${columnNumberToName(cellIndex)}${rowIndex + 1}`; - const styleAttribute = rowIndex === 0 ? ' s="1"' : ""; - return `${escapeXml(cell)}`; - }).join(""); - return `${cellXml}`; - }).join(""); - return ` + } + updated() { + this.syncOptionsLabels(); + } + }, _defineProperty(_RangeToolbar, "styles", styles$19), _RangeToolbar); + __decorate([n$1({ attribute: false })], RangeToolbar.prototype, "hass", null); + __decorate([n$1({ type: Object })], RangeToolbar.prototype, "startTime", null); + __decorate([n$1({ type: Object })], RangeToolbar.prototype, "endTime", null); + __decorate([n$1({ type: Object })], RangeToolbar.prototype, "rangeBounds", null); + __decorate([n$1({ + type: String, + attribute: "zoom-level" + })], RangeToolbar.prototype, "zoomLevel", null); + __decorate([n$1({ + type: String, + attribute: "date-snapping" + })], RangeToolbar.prototype, "dateSnapping", null); + __decorate([n$1({ + type: Boolean, + attribute: "sidebar-collapsed" + })], RangeToolbar.prototype, "sidebarCollapsed", null); + __decorate([n$1({ + type: Boolean, + attribute: "is-live-edge" + })], RangeToolbar.prototype, "isLiveEdge", null); + __decorate([n$1({ + type: Array, + attribute: false + })], RangeToolbar.prototype, "timelineEvents", null); + __decorate([n$1({ + type: Object, + attribute: false + })], RangeToolbar.prototype, "comparisonPreview", null); + __decorate([n$1({ + type: Object, + attribute: false + })], RangeToolbar.prototype, "zoomRange", null); + __decorate([n$1({ + type: Object, + attribute: false + })], RangeToolbar.prototype, "zoomWindowRange", null); + __decorate([n$1({ + type: Number, + attribute: false + })], RangeToolbar.prototype, "chartHoverTimeMs", null); + __decorate([n$1({ + type: Number, + attribute: false + })], RangeToolbar.prototype, "chartHoverWindowTimeMs", null); + __decorate([r$1()], RangeToolbar.prototype, "_optionsView", null); + __decorate([r$1()], RangeToolbar.prototype, "_optionsOpen", null); + __decorate([r$1()], RangeToolbar.prototype, "_pickerOpen", null); + RangeToolbar = __decorate([localized()], RangeToolbar); + customElements.define("range-toolbar", RangeToolbar); + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/context/app-state-context.ts + function createInitialHistoryPageState() { + return { + display: { sidebarCollapsed: false }, + range: { + startTime: null, + endTime: null, + previewZoomRange: null, + committedZoomRange: null + }, + comparison: { + windows: [], + selectedWindowId: null, + hoveredWindowId: null + }, + targets: { + selection: {}, + rawSelection: {}, + rows: [] + } + }; + } + function createHistoryPageAppStateContext() { + const state = createInitialHistoryPageState(); + return { + state, + setSidebarCollapsed(value) { + state.display.sidebarCollapsed = value; + }, + setRange(startTime, endTime) { + state.range.startTime = startTime; + state.range.endTime = endTime; + }, + setPreviewZoomRange(value) { + state.range.previewZoomRange = value ? { ...value } : null; + }, + setCommittedZoomRange(value) { + state.range.committedZoomRange = value ? { ...value } : null; + }, + setTargetSelection(value) { + state.targets.selection = { ...value || {} }; + }, + setTargetSelectionRaw(value) { + state.targets.rawSelection = { ...value || {} }; + }, + setSeriesRows(rows) { + state.targets.rows = Array.isArray(rows) ? rows.map((row) => ({ ...row })) : []; + }, + setComparisonWindows(windows) { + state.comparison.windows = Array.isArray(windows) ? windows.map((window) => ({ ...window })) : []; + }, + setSelectedComparisonWindowId(value) { + state.comparison.selectedWindowId = value || null; + }, + setHoveredComparisonWindowId(value) { + state.comparison.hoveredWindowId = value || null; + } + }; + } + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/context/fetch-context.ts + function createHistoryPageFetchContext(getHass) { + const state = { + historyBoundsLoaded: false, + historyBoundsLoading: false, + preferencesLoaded: false, + preferencesLoading: false, + savedPageLoaded: false, + hasSavedPage: false, + timelineEventsLoading: false, + timelineEventsKey: "" + }; + let historyBoundsPromise = null; + let preferencesPromise = null; + let savedPagePromise = null; + let timelineEventsPromise = null; + return { + state, + async ensureHistoryBounds(options) { + const hass = getHass(); + if (!hass || state.historyBoundsLoaded || historyBoundsPromise) return historyBoundsPromise ?? Promise.resolve(); + state.historyBoundsLoading = true; + historyBoundsPromise = fetchEventBounds(hass).then(({ start, end }) => { + options.onSuccess({ + start, + end + }); + state.historyBoundsLoaded = true; + }).catch((error) => { + state.historyBoundsLoaded = true; + options.onError?.(error); + }).finally(() => { + state.historyBoundsLoading = false; + historyBoundsPromise = null; + }); + return historyBoundsPromise; + }, + async ensureUserPreferences(options) { + const hass = getHass(); + if (!hass || state.preferencesLoaded || preferencesPromise) return preferencesPromise ?? Promise.resolve(); + state.preferencesLoading = true; + preferencesPromise = fetchUserData(hass, options.preferencesKey, options.fallbackValue).then((preferences) => { + options.onSuccess(preferences); + state.preferencesLoaded = true; + }).catch((error) => { + state.preferencesLoaded = true; + options.onError?.(error); + }).finally(() => { + state.preferencesLoading = false; + preferencesPromise = null; + }); + return preferencesPromise; + }, + async loadSavedPageIndicator(options) { + const hass = getHass(); + if (!hass || state.savedPageLoaded || savedPagePromise) return savedPagePromise ?? Promise.resolve(); + state.savedPageLoaded = true; + savedPagePromise = fetchUserData(hass, options.savedPageKey, options.fallbackValue).then((saved) => { + state.hasSavedPage = !!saved; + options.onSuccess(saved ?? null); + }).catch((error) => { + options.onError?.(error); + }).finally(() => { + savedPagePromise = null; + }); + return savedPagePromise; + }, + resetTimelineEvents() { + state.timelineEventsKey = ""; + }, + async loadTimelineEvents(options) { + const hass = getHass(); + const key = `${options.startIso}|${options.endIso}|${options.datapointScope}|${options.entityIds.join(",")}`; + if (!hass || state.timelineEventsKey === key || timelineEventsPromise) return timelineEventsPromise ?? Promise.resolve(); + if (options.datapointScope === "linked" && options.entityIds.length === 0) { + state.timelineEventsKey = key; + options.onSuccess([], key); + return Promise.resolve(); + } + state.timelineEventsLoading = true; + timelineEventsPromise = fetchEvents(hass, options.startIso, options.endIso, options.datapointScope === "linked" ? options.entityIds : void 0).then((events) => { + const normalized = Array.isArray(events) ? events : []; + state.timelineEventsKey = key; + options.onSuccess(normalized, key); + }).catch((error) => { + options.onError?.(error); + }).finally(() => { + state.timelineEventsLoading = false; + timelineEventsPromise = null; + }); + return timelineEventsPromise; + } + }; + } + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/context/navigation-context.ts + function createHistoryPageNavigationContext() { + return { + readStateFromLocation() { + const url = new URL(window.location.href); + const entityFromUrl = url.searchParams.get("entity_id"); + const deviceFromUrl = url.searchParams.get("device_id"); + const areaFromUrl = url.searchParams.get("area_id"); + const labelFromUrl = url.searchParams.get("label_id"); + const datapointsScopeFromUrl = url.searchParams.get("datapoints_scope"); + const startFromUrl = url.searchParams.get("start_time"); + const endFromUrl = url.searchParams.get("end_time"); + const zoomStartFromUrl = url.searchParams.get("zoom_start_time"); + const zoomEndFromUrl = url.searchParams.get("zoom_end_time"); + const seriesColorsFromUrl = parseSeriesColorsParam(url.searchParams.get("series_colors")); + const dateWindowsFromUrl = parseDateWindowsParam(url.searchParams.get("date_windows")); + const pageStateFromUrl = parseHistoryPageStateParam(url.searchParams.get("page_state")); + const hoursToShowRaw = Number.parseInt(url.searchParams.get("hours_to_show") || "", 10); + const hasTargetInUrl = !!(entityFromUrl || deviceFromUrl || areaFromUrl || labelFromUrl); + const hasRangeInUrl = !!startFromUrl && !!endFromUrl; + return { + entityFromUrl, + deviceFromUrl, + areaFromUrl, + labelFromUrl, + datapointsScopeFromUrl, + startFromUrl, + endFromUrl, + zoomStartFromUrl, + zoomEndFromUrl, + seriesColorsFromUrl, + dateWindowsFromUrl, + pageStateFromUrl, + hoursFromUrl: Number.isFinite(hoursToShowRaw) ? hoursToShowRaw : NaN, + hasTargetInUrl, + hasRangeInUrl, + sessionState: readHistoryPageSessionState() + }; + }, + readSessionState() { + return readHistoryPageSessionState(); + }, + saveSessionState(source) { + writeHistoryPageSessionState(source); + }, + updateUrl(options) { + const url = new URL(window.location.href); + const target = options.entities.length > 0 ? { entity_id: [...options.entities] } : {}; + if (target.entity_id?.length) url.searchParams.set("entity_id", target.entity_id.join(",")); + else url.searchParams.delete("entity_id"); + url.searchParams.delete("device_id"); + url.searchParams.delete("area_id"); + url.searchParams.delete("label_id"); + if (options.datapointScope === "all") url.searchParams.set("datapoints_scope", "all"); + else if (options.datapointScope === "hidden") url.searchParams.set("datapoints_scope", "hidden"); + else url.searchParams.delete("datapoints_scope"); + if (options.startTime && options.endTime) { + url.searchParams.set("start_time", options.startTime.toISOString()); + url.searchParams.set("end_time", options.endTime.toISOString()); + url.searchParams.set("hours_to_show", String(options.hours)); + } else { + url.searchParams.delete("start_time"); + url.searchParams.delete("end_time"); + url.searchParams.delete("hours_to_show"); + } + if (options.committedZoomRange) { + url.searchParams.set("zoom_start_time", new Date(options.committedZoomRange.start).toISOString()); + url.searchParams.set("zoom_end_time", new Date(options.committedZoomRange.end).toISOString()); + } else { + url.searchParams.delete("zoom_start_time"); + url.searchParams.delete("zoom_end_time"); + } + const dateWindowsParam = serializeDateWindowsParam(options.comparisonWindows); + if (dateWindowsParam) url.searchParams.set("date_windows", dateWindowsParam); + else url.searchParams.delete("date_windows"); + const pageStateParam = serializeHistoryPageStateParam(options.pageState); + if (pageStateParam) url.searchParams.set("page_state", pageStateParam); + else url.searchParams.delete("page_state"); + const seriesColorEntries = options.seriesRows.map((row) => { + const key = options.seriesColorQueryKey(row.entity_id); + return key && /^#[0-9a-f]{6}$/i.test(row.color || "") ? `${encodeURIComponent(key)}:${row.color.toLowerCase()}` : null; + }).filter(Boolean); + if (seriesColorEntries.length) url.searchParams.set("series_colors", seriesColorEntries.join(",")); + else url.searchParams.delete("series_colors"); + const nextUrl = `${url.pathname}${url.search}`; + if (nextUrl === `${window.location.pathname}${window.location.search}`) return; + if (options.push) window.history.pushState(null, "", nextUrl); + else window.history.replaceState(null, "", nextUrl); + } + }; + } + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/context/orchestration-context.ts + function getInnerHistoryChart(chartEl) { + if (!chartEl?.shadowRoot) return null; + return chartEl.shadowRoot.querySelector?.("hass-datapoints-history-chart") ?? chartEl.shadowRoot.querySelector?.("dp-history-chart") ?? chartEl.shadowRoot.querySelector?.("history-chart") ?? null; + } + function getComparisonTabsHost(chartEl) { + return getInnerHistoryChart(chartEl)?.querySelector?.("#chart-top-slot") ?? null; + } + function ensureCollapsedPickerAnchor(targetControl, anchorEl) { + const assignedSlot = targetControl.assignedSlot ?? null; + if (!assignedSlot) return; + const slotContainer = assignedSlot.parentElement instanceof HTMLElement ? assignedSlot.parentElement : null; + let hiddenAnchorHost = null; + if (slotContainer && window.getComputedStyle(slotContainer).display === "none") hiddenAnchorHost = slotContainer; + else if (window.getComputedStyle(assignedSlot).display === "none") hiddenAnchorHost = assignedSlot; + if (!hiddenAnchorHost) return; + const anchorRect = anchorEl?.getBoundingClientRect(); + const popupWidth = Math.max(320, Math.round(anchorRect?.width ?? 1)); + const viewportPadding = 8; + const left = Math.max(viewportPadding, Math.min(Math.round(anchorRect?.left ?? 0), window.innerWidth - popupWidth - viewportPadding)); + const top = Math.round(anchorRect?.top ?? 0); + const width = popupWidth; + const height = Math.max(1, Math.round(anchorRect?.height ?? 1)); + hiddenAnchorHost.style.display = "block"; + hiddenAnchorHost.style.position = "fixed"; + hiddenAnchorHost.style.left = `${left}px`; + hiddenAnchorHost.style.top = `${top}px`; + hiddenAnchorHost.style.width = `${width}px`; + hiddenAnchorHost.style.height = `${height}px`; + hiddenAnchorHost.style.margin = "0"; + hiddenAnchorHost.style.padding = "0"; + hiddenAnchorHost.style.opacity = "0"; + hiddenAnchorHost.style.pointerEvents = "none"; + hiddenAnchorHost.style.overflow = "visible"; + hiddenAnchorHost.style.zIndex = "2147483647"; + targetControl.style.display = "block"; + targetControl.style.width = `${width}px`; + targetControl.style.height = `${height}px`; + targetControl.style.opacity = "0"; + targetControl.style.pointerEvents = "none"; + } + function createHistoryPageOrchestrationContext() { + let chartResizeRaf = null; + return { + requestChartResizeRedraw(chartEl) { + if (chartResizeRaf != null) return; + let ranSynchronously = false; + const rafId = window.requestAnimationFrame(() => { + ranSynchronously = true; + chartResizeRaf = null; + if (!chartEl) return; + if (Array.isArray(chartEl._lastDrawArgs) && chartEl._lastDrawArgs.length > 0 && typeof chartEl._drawChart === "function") { + chartEl._drawChart(...chartEl._lastDrawArgs); + return; + } + const innerChart = getInnerHistoryChart(chartEl); + if (innerChart && Array.isArray(innerChart._lastDrawArgs) && innerChart._lastDrawArgs.length > 0) { + if (typeof innerChart._queueDrawChart === "function") { + innerChart._queueDrawChart(...innerChart._lastDrawArgs); + return; + } + if (typeof innerChart._drawChart === "function") innerChart._drawChart(...innerChart._lastDrawArgs); + } + }); + chartResizeRaf = ranSynchronously ? null : rafId; + }, + cancelChartResizeRedraw() { + if (chartResizeRaf != null) { + window.cancelAnimationFrame(chartResizeRaf); + chartResizeRaf = null; + } + }, + openTargetPicker(targetControl, anchorEl) { + if (!targetControl) return; + ensureCollapsedPickerAnchor(targetControl, anchorEl); + const genericPicker = targetControl.shadowRoot?.querySelector?.("ha-generic-picker") ?? null; + if (genericPicker && typeof genericPicker.open === "function") { + genericPicker.open(); + return; + } + if (typeof targetControl.focus === "function") targetControl.focus(); + if (typeof targetControl.click === "function") targetControl.click(); + }, + renderComparisonTabs(options) { + const tabsEl = getComparisonTabsHost(options.chartEl); + if (!tabsEl || !options.startTime || !options.endTime) return { + comparisonTabRailComp: options.comparisonTabRailComp, + comparisonTabsHostEl: options.comparisonTabsHostEl + }; + const activeComparisonWindowId = options.selectedComparisonWindowId || null; + const currentTab = options.startTime && options.endTime ? { + id: "current-range", + label: "Selected range", + detail: options.formatComparisonLabel(options.startTime, options.endTime), + active: activeComparisonWindowId == null, + editable: false + } : null; + const tabs = [...currentTab ? [currentTab] : [], ...options.comparisonWindows.map((window) => ({ + ...window, + detail: options.formatComparisonLabel(new Date(window.start_time), new Date(window.end_time)), + active: window.id === activeComparisonWindowId, + editable: true + }))]; + tabsEl.hidden = false; + let comparisonTabRailComp = options.comparisonTabRailComp; + let comparisonTabsHostEl = options.comparisonTabsHostEl; + if (!comparisonTabRailComp || comparisonTabsHostEl !== tabsEl) { + tabsEl.innerHTML = ""; + const rail = document.createElement("comparison-tab-rail"); + rail.addEventListener("dp-tab-activate", (ev) => options.onActivate(ev.detail.tabId)); + rail.addEventListener("dp-tab-hover", (ev) => options.onHover(ev.detail.tabId)); + rail.addEventListener("dp-tab-leave", (ev) => options.onLeave(ev.detail.tabId)); + rail.addEventListener("dp-tab-edit", (ev) => options.onEdit(ev.detail.tabId)); + rail.addEventListener("dp-tab-delete", (ev) => options.onDelete(ev.detail.tabId)); + rail.addEventListener("dp-tab-add", () => options.onAdd()); + tabsEl.appendChild(rail); + comparisonTabRailComp = rail; + comparisonTabsHostEl = tabsEl; + } + comparisonTabRailComp.tabs = tabs; + comparisonTabRailComp.loadingIds = [...options.loadingComparisonWindowIds]; + comparisonTabRailComp.hoveredId = options.hoveredComparisonWindowId || ""; + return { + comparisonTabRailComp, + comparisonTabsHostEl + }; + }, + updateComparisonTabsOverflow(chartEl) { + window.requestAnimationFrame(() => { + const innerChart = getInnerHistoryChart(chartEl); + const shell = innerChart?.querySelector?.("#chart-tabs-shell") ?? null; + const rail = innerChart?.querySelector?.("#chart-tabs-rail") ?? null; + if (!shell || !rail) return; + shell.classList.toggle("overflowing", rail.scrollWidth > rail.clientWidth + 4); + }); + }, + handleComparisonTabHover(options) { + if (!options.id || options.hoveredComparisonWindowId === options.id) return; + options.setHoveredComparisonWindowId(options.id); + options.updateComparisonRangePreview(); + options.updateChartHoverIndicator(); + options.renderContent(); + }, + handleComparisonTabLeave(options) { + if (!options.id || options.hoveredComparisonWindowId !== options.id) return; + options.setHoveredComparisonWindowId(null); + options.updateComparisonRangePreview(); + options.updateChartHoverIndicator(); + options.renderContent(); + }, + handleComparisonTabActivate(options) { + if (!options.id || options.id === "current-range") { + options.setSelectedComparisonWindowId(null); + options.setHoveredComparisonWindowId(null); + options.clearDeltaAnalysisSelectionState(); + options.updateComparisonRangePreview(); + options.renderComparisonTabs(); + options.setAdjustComparisonAxisScale(false); + options.renderContent(); + return; + } + if (!options.comparisonWindows.find((window) => window.id === options.id)) return; + const nextSelectedWindowId = options.selectedComparisonWindowId === options.id ? null : options.id; + options.setSelectedComparisonWindowId(nextSelectedWindowId); + if (!nextSelectedWindowId) options.clearDeltaAnalysisSelectionState(); + options.updateComparisonRangePreview(); + options.updateChartHoverIndicator(); + options.renderContent(); + } + }; + } + //#endregion + //#region custom_components/hass_datapoints/src/lib/export-spreadsheet.ts + function escapeXml(value) { + return String(value ?? "").replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); + } + function sanitizeWorksheetName(name) { + return String(name || "Sheet").replace(/[\\/*?:[\]]/g, " ").trim().slice(0, 31) || "Sheet"; + } + function columnNumberToName(index) { + let value = index + 1; + let name = ""; + while (value > 0) { + const remainder = (value - 1) % 26; + name = String.fromCharCode(65 + remainder) + name; + value = Math.floor((value - 1) / 26); + } + return name; + } + function toIsoString(value) { + if (!value) return ""; + const date = value instanceof Date ? value : new Date(value); + if (Number.isNaN(date.getTime())) return ""; + return date.toISOString(); + } + function normalizeHistoryTimestamp(rawTimestamp) { + if (typeof rawTimestamp === "number") { + if (rawTimestamp > 1e11) return rawTimestamp; + return rawTimestamp * 1e3; + } + const timestamp = new Date(rawTimestamp ?? 0).getTime(); + if (!Number.isFinite(timestamp)) return null; + return timestamp; + } + function getHistoryStatesForEntity(entityId, histResult, entityIds) { + if (!histResult) return []; + const asRecord = histResult; + if (typeof histResult === "object" && !Array.isArray(histResult) && Array.isArray(asRecord?.[entityId])) return asRecord[entityId]; + if (Array.isArray(histResult)) { + const entityIndex = entityIds.indexOf(entityId); + if (entityIndex >= 0 && Array.isArray(histResult[entityIndex])) return histResult[entityIndex]; + if (histResult.every((entry) => entry && typeof entry === "object" && !Array.isArray(entry))) return histResult.filter((entry) => entry.entity_id === entityId); + } + if (histResult && typeof histResult === "object") { + const withResult = histResult; + if (Array.isArray(withResult.result?.[entityId])) return withResult.result[entityId]; + if (Array.isArray(withResult.result)) { + const entityIndex = entityIds.indexOf(entityId); + if (entityIndex >= 0 && Array.isArray(withResult.result[entityIndex])) return withResult.result[entityIndex]; + } + } + return []; + } + function createWorksheetXml(rows) { + return ` - ${rowXml} + ${rows.map((row, rowIndex) => { + const cellXml = row.map((cell, cellIndex) => { + return `${escapeXml(cell)}`; + }).join(""); + return `${cellXml}`; + }).join("")} `; - } - function createWorkbookXml(sheets) { - const sheetXml = sheets.map( - (sheet, index) => `` - ).join(""); - return ` + } + function createWorkbookXml(sheets) { + return ` - ${sheetXml} + ${sheets.map((sheet, index) => ``).join("")} `; - } - function createWorkbookRelsXml(sheets) { - const relXml = sheets.map( - (_2, index) => `` - ).join(""); - return ` + } + function createWorkbookRelsXml(sheets) { + return ` - ${relXml} + ${sheets.map((_, index) => ``).join("")} `; - } - function createRootRelsXml() { - return ` + } + function createRootRelsXml() { + return ` `; - } - function createStylesXml() { - return ` + } + function createStylesXml() { + return ` @@ -27942,396 +27548,309 @@ ${content.alert}` : "", `; - } - function createContentTypesXml(sheets) { - const overrides = sheets.map( - (_2, index) => `` - ).join(""); - return ` + } + function createContentTypesXml(sheets) { + return ` - ${overrides} + ${sheets.map((_, index) => ``).join("")} `; - } - function createCrc32Table() { - const table = new Uint32Array(256); - for (let index = 0; index < 256; index += 1) { - let value = index; - for (let bit = 0; bit < 8; bit += 1) { - if ((value & 1) === 1) { - value = 3988292384 ^ value >>> 1; - } else { - value >>>= 1; - } - } - table[index] = value >>> 0; - } - return table; - } - const CRC32_TABLE = createCrc32Table(); - function crc32(bytes) { - let crc = 4294967295; - for (const byte of bytes) { - crc = CRC32_TABLE[(crc ^ byte) & 255] ^ crc >>> 8; - } - return (crc ^ 4294967295) >>> 0; - } - function createZip(entries) { - const encoder = new TextEncoder(); - const localParts = []; - const centralParts = []; - let offset = 0; - for (const entry of entries) { - const nameBytes = encoder.encode(entry.name); - const dataBytes = encoder.encode(entry.content); - const crc = crc32(dataBytes); - const localHeader = new Uint8Array(30 + nameBytes.length); - const localView = new DataView(localHeader.buffer); - localView.setUint32(0, 67324752, true); - localView.setUint16(4, 20, true); - localView.setUint16(6, 0, true); - localView.setUint16(8, 0, true); - localView.setUint16(10, 0, true); - localView.setUint16(12, 0, true); - localView.setUint32(14, crc, true); - localView.setUint32(18, dataBytes.length, true); - localView.setUint32(22, dataBytes.length, true); - localView.setUint16(26, nameBytes.length, true); - localView.setUint16(28, 0, true); - localHeader.set(nameBytes, 30); - localParts.push(localHeader, dataBytes); - const centralHeader = new Uint8Array(46 + nameBytes.length); - const centralView = new DataView(centralHeader.buffer); - centralView.setUint32(0, 33639248, true); - centralView.setUint16(4, 20, true); - centralView.setUint16(6, 20, true); - centralView.setUint16(8, 0, true); - centralView.setUint16(10, 0, true); - centralView.setUint16(12, 0, true); - centralView.setUint16(14, 0, true); - centralView.setUint32(16, crc, true); - centralView.setUint32(20, dataBytes.length, true); - centralView.setUint32(24, dataBytes.length, true); - centralView.setUint16(28, nameBytes.length, true); - centralView.setUint16(30, 0, true); - centralView.setUint16(32, 0, true); - centralView.setUint16(34, 0, true); - centralView.setUint16(36, 0, true); - centralView.setUint32(38, 0, true); - centralView.setUint32(42, offset, true); - centralHeader.set(nameBytes, 46); - centralParts.push(centralHeader); - offset += localHeader.length + dataBytes.length; - } - const centralDirectorySize = centralParts.reduce( - (sum, part) => sum + part.length, - 0 - ); - const endRecord = new Uint8Array(22); - const endView = new DataView(endRecord.buffer); - endView.setUint32(0, 101010256, true); - endView.setUint16(4, 0, true); - endView.setUint16(6, 0, true); - endView.setUint16(8, entries.length, true); - endView.setUint16(10, entries.length, true); - endView.setUint32(12, centralDirectorySize, true); - endView.setUint32(16, offset, true); - endView.setUint16(20, 0, true); - return new Blob( - [ - ...localParts, - ...centralParts, - endRecord - ], - { - type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - } - ); - } - function downloadWorkbook(filename, blob2) { - const url = URL.createObjectURL(blob2); - const anchor = document.createElement("a"); - anchor.href = url; - anchor.download = filename; - document.body.appendChild(anchor); - anchor.click(); - anchor.remove(); - window.setTimeout(() => { - URL.revokeObjectURL(url); - }, 0); - } - function createCombinedRows(hass, entityIds, histResult, events) { - const entityColumns = entityIds.map((entityId) => { - const stateObj = hass?.states?.[entityId]; - const unit = stateObj?.attributes?.unit_of_measurement || ""; - const name = entityName(hass, entityId) || entityId; - return { - entityId, - unit, - header: `${name} (${entityId})` - }; - }); - const rows = [ - [ - "Timestamp", - ...entityColumns.map((column) => column.header), - "Datapoint Message", - "Datapoint Annotation", - "Datapoint Icon", - "Datapoint Color", - "Datapoint Entity IDs", - "Datapoint Device IDs", - "Datapoint Area IDs", - "Datapoint Label IDs" - ] - ]; - const timestampMap = /* @__PURE__ */ new Map(); - for (const column of entityColumns) { - const states = getHistoryStatesForEntity( - column.entityId, - histResult, - entityIds - ); - for (const state of states) { - const timestamp = normalizeHistoryTimestamp( - state?.lu ?? state?.lc ?? state?.last_changed ?? state?.last_updated - ); - if (timestamp == null || !Number.isFinite(timestamp)) { - continue; - } - const rawValue = state?.s ?? state?.state ?? ""; - const displayValue = column.unit ? `${rawValue} ${column.unit}` : `${rawValue}`; - if (!timestampMap.has(timestamp)) { - timestampMap.set(timestamp, /* @__PURE__ */ new Map()); - } - timestampMap.get(timestamp)?.set(column.entityId, displayValue); - } - } - for (const event of events || []) { - const timestamp = normalizeHistoryTimestamp(event?.timestamp); - if (timestamp == null || !Number.isFinite(timestamp)) { - continue; - } - if (!timestampMap.has(timestamp)) { - timestampMap.set(timestamp, /* @__PURE__ */ new Map()); - } - const rowMap = timestampMap.get(timestamp); - rowMap?.set("__datapoints__", [ - ...rowMap?.get("__datapoints__") || [], - event - ]); - } - const sortedTimestamps = [...timestampMap.keys()].sort( - (left, right) => left - right - ); - for (const timestamp of sortedTimestamps) { - const rowValues = timestampMap.get(timestamp); - const datapointEvents = rowValues?.get("__datapoints__") || []; - rows.push([ - toIsoString(timestamp), - ...entityColumns.map( - (column) => String(rowValues?.get(column.entityId) || "") - ), - datapointEvents.map((event) => event?.message || "").filter(Boolean).join("\n"), - datapointEvents.map((event) => event?.annotation || "").filter(Boolean).join("\n"), - datapointEvents.map((event) => event?.icon || "").filter(Boolean).join("\n"), - datapointEvents.map((event) => event?.color || "").filter(Boolean).join("\n"), - datapointEvents.map( - (event) => Array.isArray(event?.entity_ids) ? event.entity_ids.join(", ") : "" - ).filter(Boolean).join("\n"), - datapointEvents.map( - (event) => Array.isArray(event?.device_ids) ? event.device_ids.join(", ") : "" - ).filter(Boolean).join("\n"), - datapointEvents.map( - (event) => Array.isArray(event?.area_ids) ? event.area_ids.join(", ") : "" - ).filter(Boolean).join("\n"), - datapointEvents.map( - (event) => Array.isArray(event?.label_ids) ? event.label_ids.join(", ") : "" - ).filter(Boolean).join("\n") - ]); - } - return rows; - } - function buildFilename(prefix, startTime, endTime) { - const start = toIsoString(startTime).replace(/[:]/g, "-"); - const end = toIsoString(endTime).replace(/[:]/g, "-"); - return `${prefix}-${start}-to-${end}.xlsx`; - } - async function downloadHistorySpreadsheet({ - hass, - entityIds, - startTime, - endTime, - datapointScope, - filenamePrefix = "data-points-history" - }) { - const startIso = toIsoString(startTime); - const endIso = toIsoString(endTime); - const normalizedEntityIds = Array.isArray(entityIds) ? entityIds.filter(Boolean) : []; - const eventEntityFilter = datapointScope === "all" ? void 0 : normalizedEntityIds; - const [histResult, events] = await Promise.all([ - fetchHistoryDuringPeriod( - hass, - startIso, - endIso, - normalizedEntityIds, - { - include_start_time_state: true, - significant_changes_only: false, - no_attributes: true - } - ), - fetchEvents(hass, startIso, endIso, eventEntityFilter) - ]); - const sheets = [ - { - name: "History Export", - rows: createCombinedRows(hass, normalizedEntityIds, histResult, events) - } - ]; - const workbookBlob = createZip([ - { - name: "[Content_Types].xml", - content: createContentTypesXml(sheets) - }, - { - name: "_rels/.rels", - content: createRootRelsXml() - }, - { - name: "xl/workbook.xml", - content: createWorkbookXml(sheets) - }, - { - name: "xl/_rels/workbook.xml.rels", - content: createWorkbookRelsXml(sheets) - }, - { - name: "xl/styles.xml", - content: createStylesXml() - }, - ...sheets.map((sheet, index) => ({ - name: `xl/worksheets/sheet${index + 1}.xml`, - content: createWorksheetXml(sheet.rows) - })) - ]); - downloadWorkbook( - buildFilename(filenamePrefix, startTime, endTime), - workbookBlob - ); - } - function createHistoryPagePersistenceContext(getHass) { - const state = { - savingPreferences: false, - savePageBusy: false, - exportBusy: false - }; - return { - state, - async saveUserPreferences(options) { - const hass = getHass(); - if (!hass || state.savingPreferences) { - return; - } - state.savingPreferences = true; - try { - await saveUserData( - hass, - options.preferencesKey, - options.payload - ); - options.onSuccess?.(); - } catch (error) { - options.onError?.(error); - } finally { - state.savingPreferences = false; - } - }, - async downloadSpreadsheet(options) { - const hass = getHass(); - if (!hass || state.exportBusy) { - return; - } - state.exportBusy = true; - try { - await downloadHistorySpreadsheet({ - hass, - entityIds: options.entityIds, - startTime: options.startTime, - endTime: options.endTime, - datapointScope: options.datapointScope - }); - options.onSuccess?.(); - } catch (error) { - options.onError?.(error); - } finally { - state.exportBusy = false; - } - }, - async savePageState(options) { - const hass = getHass(); - if (!hass || state.savePageBusy) { - return; - } - state.savePageBusy = true; - try { - await saveUserData(hass, options.savedPageKey, options.state); - options.onSuccess?.(); - } catch (error) { - options.onError?.(error); - } finally { - state.savePageBusy = false; - } - }, - async restorePageState(options) { - const hass = getHass(); - if (!hass) { - return; - } - try { - const saved = await fetchUserData( - hass, - options.savedPageKey, - options.fallbackValue - ); - if (!saved || typeof saved !== "object") { - options.onMissing?.(); - return; - } - options.onSuccess?.(saved); - } catch (error) { - options.onError?.(error); - } - }, - async clearSavedPageState(options) { - const hass = getHass(); - if (!hass) { - return; - } - try { - await saveUserData(hass, options.savedPageKey, null); - options.onSuccess?.(); - } catch (error) { - options.onError?.(error); - } - } - }; - } - function createHistoryPageContext(hass = null) { - const context = { - hass, - app: createHistoryPageAppStateContext(), - fetch: createHistoryPageFetchContext(() => context.hass), - persistence: createHistoryPagePersistenceContext(() => context.hass), - orchestration: createHistoryPageOrchestrationContext(), - navigation: createHistoryPageNavigationContext() - }; - return context; - } - const PANEL_HISTORY_STYLE = ` + } + function createCrc32Table() { + const table = new Uint32Array(256); + for (let index = 0; index < 256; index += 1) { + let value = index; + for (let bit = 0; bit < 8; bit += 1) if ((value & 1) === 1) value = 3988292384 ^ value >>> 1; + else value >>>= 1; + table[index] = value >>> 0; + } + return table; + } + var CRC32_TABLE = createCrc32Table(); + function crc32(bytes) { + let crc = 4294967295; + for (const byte of bytes) crc = CRC32_TABLE[(crc ^ byte) & 255] ^ crc >>> 8; + return (crc ^ 4294967295) >>> 0; + } + function createZip(entries) { + const encoder = new TextEncoder(); + const localParts = []; + const centralParts = []; + let offset = 0; + for (const entry of entries) { + const nameBytes = encoder.encode(entry.name); + const dataBytes = encoder.encode(entry.content); + const crc = crc32(dataBytes); + const localHeader = new Uint8Array(30 + nameBytes.length); + const localView = new DataView(localHeader.buffer); + localView.setUint32(0, 67324752, true); + localView.setUint16(4, 20, true); + localView.setUint16(6, 0, true); + localView.setUint16(8, 0, true); + localView.setUint16(10, 0, true); + localView.setUint16(12, 0, true); + localView.setUint32(14, crc, true); + localView.setUint32(18, dataBytes.length, true); + localView.setUint32(22, dataBytes.length, true); + localView.setUint16(26, nameBytes.length, true); + localView.setUint16(28, 0, true); + localHeader.set(nameBytes, 30); + localParts.push(localHeader, dataBytes); + const centralHeader = new Uint8Array(46 + nameBytes.length); + const centralView = new DataView(centralHeader.buffer); + centralView.setUint32(0, 33639248, true); + centralView.setUint16(4, 20, true); + centralView.setUint16(6, 20, true); + centralView.setUint16(8, 0, true); + centralView.setUint16(10, 0, true); + centralView.setUint16(12, 0, true); + centralView.setUint16(14, 0, true); + centralView.setUint32(16, crc, true); + centralView.setUint32(20, dataBytes.length, true); + centralView.setUint32(24, dataBytes.length, true); + centralView.setUint16(28, nameBytes.length, true); + centralView.setUint16(30, 0, true); + centralView.setUint16(32, 0, true); + centralView.setUint16(34, 0, true); + centralView.setUint16(36, 0, true); + centralView.setUint32(38, 0, true); + centralView.setUint32(42, offset, true); + centralHeader.set(nameBytes, 46); + centralParts.push(centralHeader); + offset += localHeader.length + dataBytes.length; + } + const centralDirectorySize = centralParts.reduce((sum, part) => sum + part.length, 0); + const endRecord = new Uint8Array(22); + const endView = new DataView(endRecord.buffer); + endView.setUint32(0, 101010256, true); + endView.setUint16(4, 0, true); + endView.setUint16(6, 0, true); + endView.setUint16(8, entries.length, true); + endView.setUint16(10, entries.length, true); + endView.setUint32(12, centralDirectorySize, true); + endView.setUint32(16, offset, true); + endView.setUint16(20, 0, true); + return new Blob([ + ...localParts, + ...centralParts, + endRecord + ], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }); + } + function downloadWorkbook(filename, blob) { + const url = URL.createObjectURL(blob); + const anchor = document.createElement("a"); + anchor.href = url; + anchor.download = filename; + document.body.appendChild(anchor); + anchor.click(); + anchor.remove(); + window.setTimeout(() => { + URL.revokeObjectURL(url); + }, 0); + } + function createCombinedRows(hass, entityIds, histResult, events) { + const entityColumns = entityIds.map((entityId) => { + return { + entityId, + unit: (hass?.states?.[entityId])?.attributes?.unit_of_measurement || "", + header: `${entityName(hass, entityId) || entityId} (${entityId})` + }; + }); + const rows = [[ + "Timestamp", + ...entityColumns.map((column) => column.header), + "Datapoint Message", + "Datapoint Annotation", + "Datapoint Icon", + "Datapoint Color", + "Datapoint Entity IDs", + "Datapoint Device IDs", + "Datapoint Area IDs", + "Datapoint Label IDs" + ]]; + const timestampMap = /* @__PURE__ */ new Map(); + for (const column of entityColumns) { + const states = getHistoryStatesForEntity(column.entityId, histResult, entityIds); + for (const state of states) { + const timestamp = normalizeHistoryTimestamp(state?.lu ?? state?.lc ?? state?.last_changed ?? state?.last_updated); + if (timestamp == null || !Number.isFinite(timestamp)) continue; + const rawValue = state?.s ?? state?.state ?? ""; + const displayValue = column.unit ? `${rawValue} ${column.unit}` : `${rawValue}`; + if (!timestampMap.has(timestamp)) timestampMap.set(timestamp, /* @__PURE__ */ new Map()); + timestampMap.get(timestamp)?.set(column.entityId, displayValue); + } + } + for (const event of events || []) { + const timestamp = normalizeHistoryTimestamp(event?.timestamp); + if (timestamp == null || !Number.isFinite(timestamp)) continue; + if (!timestampMap.has(timestamp)) timestampMap.set(timestamp, /* @__PURE__ */ new Map()); + const rowMap = timestampMap.get(timestamp); + rowMap?.set("__datapoints__", [...rowMap?.get("__datapoints__") || [], event]); + } + const sortedTimestamps = [...timestampMap.keys()].sort((left, right) => left - right); + for (const timestamp of sortedTimestamps) { + const rowValues = timestampMap.get(timestamp); + const datapointEvents = rowValues?.get("__datapoints__") || []; + rows.push([ + toIsoString(timestamp), + ...entityColumns.map((column) => String(rowValues?.get(column.entityId) || "")), + datapointEvents.map((event) => event?.message || "").filter(Boolean).join("\n"), + datapointEvents.map((event) => event?.annotation || "").filter(Boolean).join("\n"), + datapointEvents.map((event) => event?.icon || "").filter(Boolean).join("\n"), + datapointEvents.map((event) => event?.color || "").filter(Boolean).join("\n"), + datapointEvents.map((event) => Array.isArray(event?.entity_ids) ? event.entity_ids.join(", ") : "").filter(Boolean).join("\n"), + datapointEvents.map((event) => Array.isArray(event?.device_ids) ? event.device_ids.join(", ") : "").filter(Boolean).join("\n"), + datapointEvents.map((event) => Array.isArray(event?.area_ids) ? event.area_ids.join(", ") : "").filter(Boolean).join("\n"), + datapointEvents.map((event) => Array.isArray(event?.label_ids) ? event.label_ids.join(", ") : "").filter(Boolean).join("\n") + ]); + } + return rows; + } + function buildFilename(prefix, startTime, endTime) { + return `${prefix}-${toIsoString(startTime).replace(/[:]/g, "-")}-to-${toIsoString(endTime).replace(/[:]/g, "-")}.xlsx`; + } + async function downloadHistorySpreadsheet({ hass, entityIds, startTime, endTime, datapointScope, filenamePrefix = "data-points-history" }) { + const startIso = toIsoString(startTime); + const endIso = toIsoString(endTime); + const normalizedEntityIds = Array.isArray(entityIds) ? entityIds.filter(Boolean) : []; + const eventEntityFilter = datapointScope === "all" ? void 0 : normalizedEntityIds; + const [histResult, events] = await Promise.all([fetchHistoryDuringPeriod(hass, startIso, endIso, normalizedEntityIds, { + include_start_time_state: true, + significant_changes_only: false, + no_attributes: true + }), fetchEvents(hass, startIso, endIso, eventEntityFilter)]); + const sheets = [{ + name: "History Export", + rows: createCombinedRows(hass, normalizedEntityIds, histResult, events) + }]; + const workbookBlob = createZip([ + { + name: "[Content_Types].xml", + content: createContentTypesXml(sheets) + }, + { + name: "_rels/.rels", + content: createRootRelsXml() + }, + { + name: "xl/workbook.xml", + content: createWorkbookXml(sheets) + }, + { + name: "xl/_rels/workbook.xml.rels", + content: createWorkbookRelsXml(sheets) + }, + { + name: "xl/styles.xml", + content: createStylesXml() + }, + ...sheets.map((sheet, index) => ({ + name: `xl/worksheets/sheet${index + 1}.xml`, + content: createWorksheetXml(sheet.rows) + })) + ]); + downloadWorkbook(buildFilename(filenamePrefix, startTime, endTime), workbookBlob); + } + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/context/persistence-context.ts + function createHistoryPagePersistenceContext(getHass) { + const state = { + savingPreferences: false, + savePageBusy: false, + exportBusy: false + }; + return { + state, + async saveUserPreferences(options) { + const hass = getHass(); + if (!hass || state.savingPreferences) return; + state.savingPreferences = true; + try { + await saveUserData(hass, options.preferencesKey, options.payload); + options.onSuccess?.(); + } catch (error) { + options.onError?.(error); + } finally { + state.savingPreferences = false; + } + }, + async downloadSpreadsheet(options) { + const hass = getHass(); + if (!hass || state.exportBusy) return; + state.exportBusy = true; + try { + await downloadHistorySpreadsheet({ + hass, + entityIds: options.entityIds, + startTime: options.startTime, + endTime: options.endTime, + datapointScope: options.datapointScope + }); + options.onSuccess?.(); + } catch (error) { + options.onError?.(error); + } finally { + state.exportBusy = false; + } + }, + async savePageState(options) { + const hass = getHass(); + if (!hass || state.savePageBusy) return; + state.savePageBusy = true; + try { + await saveUserData(hass, options.savedPageKey, options.state); + options.onSuccess?.(); + } catch (error) { + options.onError?.(error); + } finally { + state.savePageBusy = false; + } + }, + async restorePageState(options) { + const hass = getHass(); + if (!hass) return; + try { + const saved = await fetchUserData(hass, options.savedPageKey, options.fallbackValue); + if (!saved || typeof saved !== "object") { + options.onMissing?.(); + return; + } + options.onSuccess?.(saved); + } catch (error) { + options.onError?.(error); + } + }, + async clearSavedPageState(options) { + const hass = getHass(); + if (!hass) return; + try { + await saveUserData(hass, options.savedPageKey, null); + options.onSuccess?.(); + } catch (error) { + options.onError?.(error); + } + } + }; + } + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/context/create-history-page-context.ts + function createHistoryPageContext(hass = null) { + const context = { + hass, + app: createHistoryPageAppStateContext(), + fetch: createHistoryPageFetchContext(() => context.hass), + persistence: createHistoryPagePersistenceContext(() => context.hass), + orchestration: createHistoryPageOrchestrationContext(), + navigation: createHistoryPageNavigationContext() + }; + return context; + } + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/datapoints.styles.ts + var PANEL_HISTORY_STYLE = ` :host { display: block; height: 100%; @@ -30447,6 +29966,7 @@ ${content.alert}` : "", position: absolute; top: 0; left: 0; + min-width: min(380px, 85vw); width: min(380px, 85vw); height: 100%; z-index: 10; @@ -30528,8 +30048,14 @@ ${content.alert}` : "", display: flex; } } + + @media (max-width: 545px) { + .page-sidebar { + width: min(380px, 95vw); + } + } `; - const PANEL_HISTORY_LOADING_STYLE = ` + var PANEL_HISTORY_LOADING_STYLE = ` :host { display: block; height: 100%; @@ -30581,734 +30107,597 @@ ${content.alert}` : "", } } `; - const DATA_GAP_THRESHOLD_OPTIONS = [ - { value: "auto", label: "Auto-detect" }, - { value: "5m", label: "5 minutes" }, - { value: "15m", label: "15 minutes" }, - { value: "1h", label: "1 hour" }, - { value: "2h", label: "2 hours" }, - { value: "3h", label: "3 hours" }, - { value: "6h", label: "6 hours" }, - { value: "12h", label: "12 hours" }, - { value: "24h", label: "24 hours" } - ]; - const ANALYSIS_ANOMALY_OVERLAP_MODE_OPTIONS = [ - { value: "all", label: "Show all anomalies" }, - { value: "only", label: "Overlaps only" } - ]; - class HassRecordsHistoryPanel extends HTMLElement { - constructor() { - super(); - this.attachShadow({ mode: "open" }); - this._context = createHistoryPageContext(); - this._rendered = false; - this._shellBuilt = false; - this._entities = []; - this._seriesRows = []; - this._targetSelection = {}; - this._targetSelectionRaw = {}; - this._hours = 24; - this._startTime = null; - this._endTime = null; - this._panel = null; - this._narrow = false; - this._contentKey = ""; - this._contentSplitRatio = 0.44; - this._sidebarCollapsed = false; - this._layoutMode = "desktop"; - this._mqTablet = window.matchMedia("(max-width: 900px)"); - this._mqMobile = window.matchMedia("(max-width: 720px)"); - this._onLayoutChange = () => this._updateLayoutMode(); - this._collapsedPopupEntityId = null; - this._collapsedPopupAnchorEl = null; - this._collapsedPopupOutsideClickHandler = null; - this._collapsedPopupKeyHandler = null; - this._lastSyncedLocale = ""; - this._datapointScope = "linked"; - this._showChartDatapointIcons = true; - this._showChartDatapointLines = true; - this._showChartTooltips = true; - this._showChartEmphasizedHoverGuides = false; - this._chartHoverSnapMode = "follow_series"; - this._delinkChartYAxis = false; - this._showCorrelatedAnomalies = false; - this._chartAnomalyOverlapMode = "all"; - this._showDataGaps = true; - this._dataGapThreshold = "2h"; - this._historyStartTime = null; - this._historyEndTime = null; - this._historyBoundsLoaded = false; - this._timelineEvents = []; - this._timelineEventsKey = ""; - this._preferredSeriesColors = {}; - this._preferencesLoaded = false; - this._comparisonWindows = []; - this._selectedComparisonWindowId = null; - this._hoveredComparisonWindowId = null; - this._loadingComparisonWindowIds = []; - this._comparisonTabsHostEl = null; - this._comparisonTabRailComp = null; - this._pendingAnomalyComparisonWindowEntityId = null; - this._dateWindowDialogOpen = false; - this._editingDateWindowId = null; - this._dateWindowDialogComp = null; - this._splitChartView = false; - this._dateWindowDialogNameEl = null; - this._dateWindowDialogStartEl = null; - this._dateWindowDialogEndEl = null; - this._dateWindowDialogShortcutsEl = null; - this._dateWindowDialogDraftRange = null; - this._uiReadyPromise = null; - this._uiReadyApplied = false; - this._chartEl = null; - this._historyChartMol = null; - this._listEl = null; - this._chartConfigKey = ""; - this._listConfigKey = ""; - this._shellEl = null; - this._contentHostEl = null; - this._contentSplitterEl = null; - this._targetControl = null; - this._targetRowsEl = null; - this._rowListEl = null; - this._targetRowsRenderKey = ""; - this._sidebarOptionsEl = null; - this._sidebarOptionsComp = null; - this._sidebarAccordionTargetsOpen = true; - this._sidebarAccordionDatapointsOpen = true; - this._sidebarAccordionAnalysisOpen = true; - this._sidebarAccordionChartOpen = true; - this._dateControl = null; - this._dateRangePickerEl = null; - this._panelTimelineEl = null; - this._rangeBounds = null; - this._autoZoomTimer = null; - this._resolvedAutoZoomLevel = null; - this._hoveredPeriodRange = null; - this._chartHoverTimeMs = null; - this._chartZoomRange = null; - this._chartZoomCommittedRange = null; - this._chartZoomStateCommitTimer = null; - this._zoomLevel = "auto"; - this._dateSnapping = "auto"; - this._hasTargetInUrl = false; - this._hasRangeInUrl = false; - this._hasPageStateInUrl = false; - this._localPageStateDirty = false; - this._pendingPreferencesSaveTimer = null; - this._recordsSearchQuery = ""; - this._hiddenEventIds = []; - this._hoveredEventIds = []; - this._restoredFromSession = false; - this._savedPageLoaded = false; - this._hasSavedPage = false; - this._pageMenuOpen = false; - this._onChartHover = (ev) => this._handleChartHover(ev); - this._onChartZoom = (ev) => this._handleChartZoom(ev); - this._onRecordsSearch = (ev) => this._handleRecordsSearch(ev); - this._onToggleEventVisibility = (ev) => this._handleToggleEventVisibility(ev); - this._onHoverEventRecord = (ev) => this._handleHoverEventRecord(ev); - this._onToggleSeriesVisibility = (ev) => this._handleToggleSeriesVisibility(ev); - this._onComparisonLoading = (ev) => this._handleComparisonLoading(ev); - this._computingEntityIds = /* @__PURE__ */ new Set(); - this._analysisProgress = 0; - this._computingMethods = /* @__PURE__ */ new Map(); - this._onAnalysisComputing = (ev) => this._handleAnalysisComputing(ev); - this._onAnalysisMethodResult = (ev) => this._handleAnalysisMethodResult(ev); - this._onWindowPointerDown = (_ev) => this._handleWindowPointerDown(); - this._onWindowResize = () => { - if (this._rendered) { - this._syncPageLayoutHeight(); - this._applyContentSplitLayout(); - this._requestChartResizeRedraw(); - this._syncRangeControl(); - } - }; - this._onCollapsedSidebarClick = (_ev) => this._handleCollapsedSidebarClick(); - this._onEventRecorded = () => this._handleEventRecorded(); - this._haEventUnsubscribe = null; - this._onPopState = () => { - this._initFromContext(); - if (this._rendered) { - this._syncControls(); - this._renderContent(); - } - }; - this._onLocationChanged = () => { - this._initFromContext(); - if (this._rendered) { - this._syncControls(); - this._renderContent(); - } - }; - } - _appState() { - return this._context.app; - } - get _targetSelection() { - return this._appState().state.targets.selection; - } - set _targetSelection(value) { - this._appState().setTargetSelection(value || {}); - } - get _targetSelectionRaw() { - return this._appState().state.targets.rawSelection; - } - set _targetSelectionRaw(value) { - this._appState().setTargetSelectionRaw(value || {}); - } - get _seriesRows() { - return this._appState().state.targets.rows; - } - set _seriesRows(value) { - this._appState().setSeriesRows(Array.isArray(value) ? value : []); - } - get _startTime() { - return this._appState().state.range.startTime; - } - set _startTime(value) { - this._appState().setRange(value || null, this._endTime || null); - } - get _endTime() { - return this._appState().state.range.endTime; - } - set _endTime(value) { - this._appState().setRange(this._startTime || null, value || null); - } - get _sidebarCollapsed() { - return this._appState().state.display.sidebarCollapsed; - } - set _sidebarCollapsed(value) { - this._appState().setSidebarCollapsed(!!value); - } - get _comparisonWindows() { - return this._appState().state.comparison.windows; - } - set _comparisonWindows(value) { - this._appState().setComparisonWindows(Array.isArray(value) ? value : []); - } - get _selectedComparisonWindowId() { - return this._appState().state.comparison.selectedWindowId; - } - set _selectedComparisonWindowId(value) { - this._appState().setSelectedComparisonWindowId(value || null); - } - get _hoveredComparisonWindowId() { - return this._appState().state.comparison.hoveredWindowId; - } - set _hoveredComparisonWindowId(value) { - this._appState().setHoveredComparisonWindowId(value || null); - } - get _chartZoomRange() { - return this._appState().state.range.previewZoomRange; - } - set _chartZoomRange(value) { - this._appState().setPreviewZoomRange(value || null); - } - get _chartZoomCommittedRange() { - return this._appState().state.range.committedZoomRange; - } - set _chartZoomCommittedRange(value) { - this._appState().setCommittedZoomRange(value || null); - } - get _historyBoundsLoaded() { - return this._context.fetch.state.historyBoundsLoaded; - } - set _historyBoundsLoaded(value) { - this._context.fetch.state.historyBoundsLoaded = !!value; - } - get _preferencesLoaded() { - return this._context.fetch.state.preferencesLoaded; - } - set _preferencesLoaded(value) { - this._context.fetch.state.preferencesLoaded = !!value; - } - get _savedPageLoaded() { - return this._context.fetch.state.savedPageLoaded; - } - set _savedPageLoaded(value) { - this._context.fetch.state.savedPageLoaded = !!value; - } - get _hasSavedPage() { - return this._context.fetch.state.hasSavedPage; - } - set _hasSavedPage(value) { - this._context.fetch.state.hasSavedPage = !!value; - } - get _timelineEventsKey() { - return this._context.fetch.state.timelineEventsKey; - } - set _timelineEventsKey(value) { - this._context.fetch.state.timelineEventsKey = String(value || ""); - } - get _savePageBusy() { - return this._context.persistence.state.savePageBusy; - } - set _savePageBusy(value) { - this._context.persistence.state.savePageBusy = !!value; - } - get _exportBusy() { - return this._context.persistence.state.exportBusy; - } - set _exportBusy(value) { - this._context.persistence.state.exportBusy = !!value; - } - set hass(hass) { - this._hass = hass; - this._context.hass = hass; - syncFrontendLocale(this._hass).then((locale) => { - if (!this.isConnected) { - return; - } - if (!this._shellBuilt && this._rendered) { - this._buildLoadingShell(); - return; - } - if (!this._rendered) { - return; - } - if (locale !== this._lastSyncedLocale) { - this._lastSyncedLocale = locale; - this._renderContent(); - } else { - if (this._shellEl && this._hass) { - this._shellEl.hass = this._hass; - } - if (this._chartEl) { - this._chartEl.hass = this._hass; - } - if (this._listEl) { - this._listEl.hass = this._hass; - } - if (this._targetControl && this._hass) { - this._targetControl.hass = this._hass; - } - if (this._historyTargetsComp) { - this._historyTargetsComp.hass = this._hass ?? null; - this._historyTargetsComp.states = this._hass?.states ?? {}; - } - if (this._rowListEl) { - this._rowListEl.hass = this._hass ?? null; - this._rowListEl.states = this._hass?.states ?? {}; - } - if (this._rangeToolbarComp) { - this._rangeToolbarComp.hass = this._hass ?? null; - } - this.shadowRoot?.querySelectorAll( - "[data-series-icon-entity-id], [data-series-collapsed-icon-entity-id]" - ).forEach((iconEl) => { - const icon = iconEl; - const entityId = icon.dataset.seriesIconEntityId || icon.dataset.seriesCollapsedIconEntityId; - if (!entityId) return; - icon.stateObj = this._hass?.states?.[entityId]; - icon.hass = this._hass; - }); - } - }); - if (!this._haEventUnsubscribe && this._hass?.connection) { - this._hass.connection.subscribeEvents( - () => this._handleEventRecorded(), - `${DOMAIN}_event_recorded` - ).then((unsub) => { - this._haEventUnsubscribe = unsub; - }).catch(() => { - }); - } - if (!this._rendered) { - this._rendered = true; - this._initFromContext(); - if (this.isConnected) { - this._buildLoadingShell(); - } - } - if (!this._seriesRows.length && Object.keys(this._targetSelection || {}).length) { - this._seriesRows = buildHistorySeriesRows( - resolveEntityIdsFromTarget(this._hass, this._targetSelection) - ); - } - this._syncSeriesState(); - if (!this._shellBuilt) { - return; - } - this._ensureHistoryBounds(); - this._ensureUserPreferences(); - this._loadSavedPageIndicator(); - } - set panel(panel) { - this._panel = panel; - this._initFromContext(); - if (this._rendered) { - this._syncControls(); - this._renderContent(); - } - } - set narrow(value) { - this._narrow = value; - } - connectedCallback() { - this._mqTablet.addEventListener("change", this._onLayoutChange); - this._mqMobile.addEventListener("change", this._onLayoutChange); - this._updateLayoutMode(); - this._onOverlayKeydown = (ev) => { - if (ev.key === "Escape" && !this._sidebarCollapsed && this._layoutMode !== "desktop") { - this._toggleSidebarCollapsed(); - } - }; - window.addEventListener("keydown", this._onOverlayKeydown); - window.addEventListener("popstate", this._onPopState); - window.addEventListener("location-changed", this._onLocationChanged); - window.addEventListener("pointerdown", this._onWindowPointerDown, true); - window.addEventListener("resize", this._onWindowResize); - window.addEventListener( - "hass-datapoints-event-recorded", - this._onEventRecorded - ); - this.addEventListener("hass-datapoints-chart-hover", this._onChartHover); - this.addEventListener("hass-datapoints-chart-zoom", this._onChartZoom); - this.addEventListener( - "hass-datapoints-records-search", - this._onRecordsSearch - ); - this.addEventListener( - "hass-datapoints-toggle-event-visibility", - this._onToggleEventVisibility - ); - this.addEventListener( - "hass-datapoints-hover-event-record", - this._onHoverEventRecord - ); - this.addEventListener( - "hass-datapoints-toggle-series-visibility", - this._onToggleSeriesVisibility - ); - this.addEventListener( - "hass-datapoints-comparison-loading", - this._onComparisonLoading - ); - this.addEventListener( - "hass-datapoints-analysis-computing", - this._onAnalysisComputing - ); - this.addEventListener( - "hass-datapoints-analysis-method-result", - this._onAnalysisMethodResult - ); - if (this._rendered && !this._shellBuilt) { - this._buildLoadingShell(); - } - this._ensureUiComponentsReady(); - if (this._rendered && this._shellBuilt) { - window.requestAnimationFrame(() => { - if (!this.isConnected) { - return; - } - this._syncControls(); - this._renderContent(); - if (this._restoredFromSession) { - this._restoredFromSession = false; - this._updateUrl({ push: false }); - } - }); - } - } - disconnectedCallback() { - this._mqTablet.removeEventListener("change", this._onLayoutChange); - this._mqMobile.removeEventListener("change", this._onLayoutChange); - if (this._onOverlayKeydown) { - window.removeEventListener("keydown", this._onOverlayKeydown); - } - window.removeEventListener("popstate", this._onPopState); - window.removeEventListener("location-changed", this._onLocationChanged); - window.removeEventListener("pointerdown", this._onWindowPointerDown, true); - window.removeEventListener("resize", this._onWindowResize); - window.removeEventListener( - "hass-datapoints-event-recorded", - this._onEventRecorded - ); - if (this._haEventUnsubscribe) { - this._haEventUnsubscribe(); - this._haEventUnsubscribe = null; - } - this.removeEventListener("hass-datapoints-chart-hover", this._onChartHover); - this.removeEventListener("hass-datapoints-chart-zoom", this._onChartZoom); - this.removeEventListener( - "hass-datapoints-records-search", - this._onRecordsSearch - ); - this.removeEventListener( - "hass-datapoints-toggle-event-visibility", - this._onToggleEventVisibility - ); - this.removeEventListener( - "hass-datapoints-hover-event-record", - this._onHoverEventRecord - ); - this.removeEventListener( - "hass-datapoints-toggle-series-visibility", - this._onToggleSeriesVisibility - ); - this.removeEventListener( - "hass-datapoints-comparison-loading", - this._onComparisonLoading - ); - this.removeEventListener( - "hass-datapoints-analysis-computing", - this._onAnalysisComputing - ); - this.removeEventListener( - "hass-datapoints-analysis-method-result", - this._onAnalysisMethodResult - ); - if (this._rangeCommitTimer) { - window.clearTimeout(this._rangeCommitTimer); - this._rangeCommitTimer = null; - } - if (this._autoZoomTimer) { - window.clearTimeout(this._autoZoomTimer); - this._autoZoomTimer = null; - } - if (this._pendingPreferencesSaveTimer) { - window.clearTimeout(this._pendingPreferencesSaveTimer); - this._pendingPreferencesSaveTimer = null; - } - if (this._chartZoomStateCommitTimer) { - window.clearTimeout(this._chartZoomStateCommitTimer); - this._chartZoomStateCommitTimer = null; - } - this._hideCollapsedTargetPopup(); - this._hideCollapsedOptionsPopup(); - this._uiReadyPromise = null; - this._uiReadyApplied = false; - this._context.orchestration.cancelChartResizeRedraw(); - } - _initFromContext() { - const { - entityFromUrl, - deviceFromUrl, - areaFromUrl, - labelFromUrl, - datapointsScopeFromUrl, - startFromUrl, - endFromUrl, - zoomStartFromUrl, - zoomEndFromUrl, - seriesColorsFromUrl, - dateWindowsFromUrl, - hoursFromUrl, - hasTargetInUrl, - hasRangeInUrl, - pageStateFromUrl, - sessionState - } = this._context.navigation.readStateFromLocation(); - const persistedState = pageStateFromUrl && typeof pageStateFromUrl === "object" ? { ...sessionState || {}, ...pageStateFromUrl } : sessionState; - const panelCfg = this._panel?.config ?? {}; - this._hasTargetInUrl = hasTargetInUrl; - this._hasRangeInUrl = hasRangeInUrl; - this._hasPageStateInUrl = !!pageStateFromUrl; - this._localPageStateDirty = false; - this._restoredFromSession = !hasTargetInUrl && !hasRangeInUrl && !!persistedState; - this._sidebarCollapsed = !!persistedState?.sidebar_collapsed; - this._sidebarAccordionTargetsOpen = persistedState?.sidebar_accordion_targets_open !== false; - this._sidebarAccordionDatapointsOpen = persistedState?.sidebar_accordion_datapoints_open !== false; - this._sidebarAccordionAnalysisOpen = persistedState?.sidebar_accordion_analysis_open !== false; - this._sidebarAccordionChartOpen = persistedState?.sidebar_accordion_chart_open !== false; - if (persistedState && Number.isFinite(persistedState.content_split_ratio)) { - this._contentSplitRatio = clampNumber( - persistedState.content_split_ratio, - 0.25, - 0.75 - ); - } - let resolvedDatapointScope; - if (datapointsScopeFromUrl === "all") { - resolvedDatapointScope = "all"; - } else if (datapointsScopeFromUrl === "hidden") { - resolvedDatapointScope = "hidden"; - } else if (!datapointsScopeFromUrl && persistedState?.datapoint_scope === "all") { - resolvedDatapointScope = "all"; - } else if (!datapointsScopeFromUrl && persistedState?.datapoint_scope === "hidden") { - resolvedDatapointScope = "hidden"; - } else { - resolvedDatapointScope = "linked"; - } - this._datapointScope = resolvedDatapointScope; - this._showChartDatapointIcons = persistedState?.show_chart_datapoint_icons !== false; - this._showChartDatapointLines = persistedState?.show_chart_datapoint_lines !== false; - this._showChartTooltips = persistedState?.show_chart_tooltips !== false; - this._showChartEmphasizedHoverGuides = persistedState?.show_chart_emphasized_hover_guides === true; - this._chartHoverSnapMode = persistedState?.chart_hover_snap_mode === "snap_to_data_points" ? "snap_to_data_points" : "follow_series"; - this._delinkChartYAxis = persistedState?.delink_chart_y_axis === true; - this._splitChartView = persistedState?.split_chart_view === true; - this._showCorrelatedAnomalies = persistedState?.show_chart_correlated_anomalies === true; - this._chartAnomalyOverlapMode = ANALYSIS_ANOMALY_OVERLAP_MODE_OPTIONS.some( - (o2) => o2.value === persistedState?.chart_anomaly_overlap_mode - ) ? persistedState.chart_anomaly_overlap_mode : "all"; - this._showDataGaps = persistedState?.show_data_gaps !== false; - this._dataGapThreshold = DATA_GAP_THRESHOLD_OPTIONS.some( - (option) => option.value === persistedState?.data_gap_threshold - ) ? persistedState.data_gap_threshold : "2h"; - this._comparisonWindows = dateWindowsFromUrl.length ? dateWindowsFromUrl : normalizeDateWindows( - persistedState?.date_windows - ); - const targetFromUrl = normalizeTargetValue({ - entity_id: entityFromUrl ? entityFromUrl.split(",") : [], - device_id: deviceFromUrl ? deviceFromUrl.split(",") : [], - area_id: areaFromUrl ? areaFromUrl.split(",") : [], - label_id: labelFromUrl ? labelFromUrl.split(",") : [] - }); - const panelTarget = panelConfigTarget(panelCfg); - let nextTargetSelection; - if (Object.keys(targetFromUrl).length) { - nextTargetSelection = targetFromUrl; - } else if (!hasTargetInUrl && persistedState?.entities?.length) { - nextTargetSelection = normalizeTargetValue( - persistedState.target_selection_raw || persistedState.target_selection || { - entity_id: persistedState.entities - } - ); - } else { - nextTargetSelection = panelTarget; - } - this._targetSelection = nextTargetSelection; - this._targetSelectionRaw = !hasTargetInUrl && persistedState?.target_selection_raw ? persistedState.target_selection_raw : nextTargetSelection; - this._seriesRows = !hasTargetInUrl && Array.isArray(persistedState?.series_rows) ? normalizeHistorySeriesRows(persistedState.series_rows) : buildHistorySeriesRows( - resolveEntityIdsFromTarget(this._hass, this._targetSelection) - ); - if (Array.isArray(persistedState?.series_rows)) { - this._seriesRows = this._mergeSavedSeriesRows( - this._seriesRows, - persistedState.series_rows - ); - } - this._seriesRows = this._applyPreferredSeriesColors( - this._seriesRows, - seriesColorsFromUrl - ); - this._syncSeriesState(); - const start = parseDateValue(startFromUrl) || (!hasRangeInUrl ? parseDateValue(persistedState?.start_time) : null) || parseDateValue(panelCfg.start_time); - const end = parseDateValue(endFromUrl) || (!hasRangeInUrl ? parseDateValue(persistedState?.end_time) : null) || parseDateValue(panelCfg.end_time); - const zoomStart = parseDateValue(zoomStartFromUrl) || (!zoomStartFromUrl && !zoomEndFromUrl ? parseDateValue(persistedState?.zoom_start_time) : null); - const zoomEnd = parseDateValue(zoomEndFromUrl) || (!zoomStartFromUrl && !zoomEndFromUrl ? parseDateValue(persistedState?.zoom_end_time) : null); - this._chartZoomRange = null; - this._chartZoomCommittedRange = zoomStart && zoomEnd && zoomStart < zoomEnd ? { start: zoomStart.getTime(), end: zoomEnd.getTime() } : null; - if (start && end && start < end) { - this._startTime = start; - this._endTime = end; - this._hours = Math.max( - 1, - Math.round((end.getTime() - start.getTime()) / 36e5) - ); - return; - } - if (Number.isFinite(hoursFromUrl) && hoursFromUrl > 0) { - this._hours = hoursFromUrl; - } else if (!hasRangeInUrl && persistedState && Number.isFinite(persistedState.hours) && persistedState.hours > 0) { - this._hours = persistedState.hours; - } else if (panelCfg.hours_to_show) { - this._hours = panelCfg.hours_to_show; - } - const now = /* @__PURE__ */ new Date(); - this._startTime = startOfUnit(now, "week"); - this._endTime = endOfUnit(now, "week"); - this._hours = Math.max( - 1, - Math.round( - (this._endTime.getTime() - this._startTime.getTime()) / 36e5 - ) - ); - } - _saveSessionState() { - this._localPageStateDirty = true; - this._context.navigation.saveSessionState(this); - this._scheduleUserPreferencesSave(); - } - _scheduleUserPreferencesSave() { - if (this._pendingPreferencesSaveTimer) { - window.clearTimeout(this._pendingPreferencesSaveTimer); - } - this._pendingPreferencesSaveTimer = window.setTimeout(() => { - this._pendingPreferencesSaveTimer = null; - this._saveUserPreferences(); - }, 160); - } - _applyPreferencePageState(state) { - if (!state || typeof state !== "object") { - return; - } - const s2 = state; - if (!this._hasPageStateInUrl) { - this._sidebarCollapsed = !!s2.sidebar_collapsed; - this._sidebarAccordionTargetsOpen = s2.sidebar_accordion_targets_open !== false; - this._sidebarAccordionDatapointsOpen = s2.sidebar_accordion_datapoints_open !== false; - this._sidebarAccordionAnalysisOpen = s2.sidebar_accordion_analysis_open !== false; - this._sidebarAccordionChartOpen = s2.sidebar_accordion_chart_open !== false; - if (Number.isFinite(s2.content_split_ratio)) { - this._contentSplitRatio = clampNumber( - s2.content_split_ratio, - 0.25, - 0.75 - ); - } - this._showChartDatapointIcons = s2.show_chart_datapoint_icons !== false; - this._showChartDatapointLines = s2.show_chart_datapoint_lines !== false; - this._showChartTooltips = s2.show_chart_tooltips !== false; - this._showChartEmphasizedHoverGuides = s2.show_chart_emphasized_hover_guides === true; - this._chartHoverSnapMode = s2.chart_hover_snap_mode === "snap_to_data_points" ? "snap_to_data_points" : "follow_series"; - this._delinkChartYAxis = s2.delink_chart_y_axis === true; - this._splitChartView = s2.split_chart_view === true; - this._showCorrelatedAnomalies = s2.show_chart_correlated_anomalies === true; - this._chartAnomalyOverlapMode = ANALYSIS_ANOMALY_OVERLAP_MODE_OPTIONS.some( - (option) => option.value === s2.chart_anomaly_overlap_mode - ) ? s2.chart_anomaly_overlap_mode : "all"; - this._showDataGaps = s2.show_data_gaps !== false; - this._dataGapThreshold = DATA_GAP_THRESHOLD_OPTIONS.some( - (option) => option.value === s2.data_gap_threshold - ) ? s2.data_gap_threshold : this._dataGapThreshold; - this._datapointScope = s2.datapoint_scope === "all" || s2.datapoint_scope === "hidden" ? s2.datapoint_scope : "linked"; - } - if (!this._hasTargetInUrl) { - if (s2.target_selection) { - this._targetSelection = normalizeTargetValue( - s2.target_selection - ); - } - if (s2.target_selection_raw) { - this._targetSelectionRaw = s2.target_selection_raw; - } - } - if (Array.isArray(s2.series_rows)) { - const nextRows = !this._hasTargetInUrl ? normalizeHistorySeriesRows(s2.series_rows) : this._mergeSavedSeriesRows(this._seriesRows, s2.series_rows); - this._seriesRows = this._applyPreferredSeriesColors(nextRows); - this._syncSeriesState(); - } - if (!this._hasRangeInUrl) { - const start = parseDateValue(s2.start_time); - const end = parseDateValue(s2.end_time); - if (start && end && start < end) { - this._startTime = start; - this._endTime = end; - } - const zoomStart = parseDateValue(s2.zoom_start_time); - const zoomEnd = parseDateValue(s2.zoom_end_time); - this._chartZoomCommittedRange = zoomStart && zoomEnd && zoomStart < zoomEnd ? { start: zoomStart.getTime(), end: zoomEnd.getTime() } : this._chartZoomCommittedRange; - if (s2.hours && Number.isFinite(s2.hours) && s2.hours > 0) { - this._hours = s2.hours; - } - if (Array.isArray(s2.date_windows) && !this._hasPageStateInUrl) { - this._comparisonWindows = normalizeDateWindows( - s2.date_windows - ); - } - } - } - _buildLoadingShell() { - this._shellBuilt = false; - const loadingLabel = msg("Loading Datapoints…"); - const root = this.shadowRoot; - if (!root) { - return; - } - root.innerHTML = ` + //#endregion + //#region custom_components/hass_datapoints/src/panels/datapoints/datapoints.ts + var DATA_GAP_THRESHOLD_OPTIONS = [ + { + value: "auto", + label: "Auto-detect" + }, + { + value: "5m", + label: "5 minutes" + }, + { + value: "15m", + label: "15 minutes" + }, + { + value: "1h", + label: "1 hour" + }, + { + value: "2h", + label: "2 hours" + }, + { + value: "3h", + label: "3 hours" + }, + { + value: "6h", + label: "6 hours" + }, + { + value: "12h", + label: "12 hours" + }, + { + value: "24h", + label: "24 hours" + } + ]; + var ANALYSIS_ANOMALY_OVERLAP_MODE_OPTIONS = [{ + value: "all", + label: "Show all anomalies" + }, { + value: "only", + label: "Overlaps only" + }]; + /** + * hass-datapoints-history-panel – Sidebar panel for annotated history exploration. + */ + var HassRecordsHistoryPanel = class extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open" }); + this._context = createHistoryPageContext(); + this._rendered = false; + this._shellBuilt = false; + this._entities = []; + this._seriesRows = []; + this._targetSelection = {}; + this._targetSelectionRaw = {}; + this._hours = 24; + this._startTime = null; + this._endTime = null; + this._panel = null; + this._narrow = false; + this._contentKey = ""; + this._contentSplitRatio = .44; + this._sidebarCollapsed = false; + this._layoutMode = "desktop"; + this._mqTablet = window.matchMedia("(max-width: 900px)"); + this._mqMobile = window.matchMedia("(max-width: 720px)"); + this._onLayoutChange = () => this._updateLayoutMode(); + this._collapsedPopupEntityId = null; + this._collapsedPopupAnchorEl = null; + this._collapsedPopupOutsideClickHandler = null; + this._collapsedPopupKeyHandler = null; + this._lastSyncedLocale = ""; + this._datapointScope = "linked"; + this._showChartDatapointIcons = true; + this._showChartDatapointLines = true; + this._showChartTooltips = true; + this._showChartEmphasizedHoverGuides = false; + this._chartHoverSnapMode = "follow_series"; + this._delinkChartYAxis = false; + this._showCorrelatedAnomalies = false; + this._chartAnomalyOverlapMode = "all"; + this._showDataGaps = true; + this._dataGapThreshold = "2h"; + this._historyStartTime = null; + this._historyEndTime = null; + this._historyBoundsLoaded = false; + this._timelineEvents = []; + this._timelineEventsKey = ""; + this._preferredSeriesColors = {}; + this._preferencesLoaded = false; + this._comparisonWindows = []; + this._selectedComparisonWindowId = null; + this._hoveredComparisonWindowId = null; + this._loadingComparisonWindowIds = []; + this._comparisonTabsHostEl = null; + this._comparisonTabRailComp = null; + this._pendingAnomalyComparisonWindowEntityId = null; + this._dateWindowDialogOpen = false; + this._editingDateWindowId = null; + this._dateWindowDialogComp = null; + this._splitChartView = false; + this._dateWindowDialogNameEl = null; + this._dateWindowDialogStartEl = null; + this._dateWindowDialogEndEl = null; + this._dateWindowDialogShortcutsEl = null; + this._dateWindowDialogDraftRange = null; + this._uiReadyPromise = null; + this._uiReadyApplied = false; + this._chartEl = null; + this._historyChartMol = null; + this._listEl = null; + this._chartConfigKey = ""; + this._listConfigKey = ""; + this._shellEl = null; + this._contentHostEl = null; + this._contentSplitterEl = null; + this._targetControl = null; + this._targetRowsEl = null; + this._rowListEl = null; + this._targetRowsRenderKey = ""; + this._sidebarOptionsEl = null; + this._sidebarOptionsComp = null; + this._sidebarAccordionTargetsOpen = true; + this._sidebarAccordionDatapointsOpen = true; + this._sidebarAccordionAnalysisOpen = true; + this._sidebarAccordionChartOpen = true; + this._dateControl = null; + this._dateRangePickerEl = null; + this._panelTimelineEl = null; + this._rangeBounds = null; + this._autoZoomTimer = null; + this._resolvedAutoZoomLevel = null; + this._hoveredPeriodRange = null; + this._chartHoverTimeMs = null; + this._chartZoomRange = null; + this._chartZoomCommittedRange = null; + this._chartZoomStateCommitTimer = null; + this._zoomLevel = "auto"; + this._dateSnapping = "auto"; + this._hasTargetInUrl = false; + this._hasRangeInUrl = false; + this._hasPageStateInUrl = false; + this._localPageStateDirty = false; + this._pendingPreferencesSaveTimer = null; + this._recordsSearchQuery = ""; + this._hiddenEventIds = []; + this._hoveredEventIds = []; + this._restoredFromSession = false; + this._savedPageLoaded = false; + this._hasSavedPage = false; + this._pageMenuOpen = false; + this._onChartHover = (ev) => this._handleChartHover(ev); + this._onChartZoom = (ev) => this._handleChartZoom(ev); + this._onRecordsSearch = (ev) => this._handleRecordsSearch(ev); + this._onToggleEventVisibility = (ev) => this._handleToggleEventVisibility(ev); + this._onHoverEventRecord = (ev) => this._handleHoverEventRecord(ev); + this._onToggleSeriesVisibility = (ev) => this._handleToggleSeriesVisibility(ev); + this._onComparisonLoading = (ev) => this._handleComparisonLoading(ev); + this._computingEntityIds = /* @__PURE__ */ new Set(); + this._analysisProgress = 0; + this._computingMethods = /* @__PURE__ */ new Map(); + this._onAnalysisComputing = (ev) => this._handleAnalysisComputing(ev); + this._onAnalysisMethodResult = (ev) => this._handleAnalysisMethodResult(ev); + this._onWindowPointerDown = (_ev) => this._handleWindowPointerDown(); + this._onWindowResize = () => { + if (this._rendered) { + this._syncPageLayoutHeight(); + this._applyContentSplitLayout(); + this._requestChartResizeRedraw(); + this._syncRangeControl(); + } + }; + this._onCollapsedSidebarClick = (_ev) => this._handleCollapsedSidebarClick(); + this._onEventRecorded = () => this._handleEventRecorded(); + this._haEventUnsubscribe = null; + this._onPopState = () => { + this._initFromContext(); + if (this._rendered) { + this._syncControls(); + this._renderContent(); + } + }; + this._onLocationChanged = () => { + this._initFromContext(); + if (this._rendered) { + this._syncControls(); + this._renderContent(); + } + }; + } + _appState() { + return this._context.app; + } + get _targetSelection() { + return this._appState().state.targets.selection; + } + set _targetSelection(value) { + this._appState().setTargetSelection(value || {}); + } + get _targetSelectionRaw() { + return this._appState().state.targets.rawSelection; + } + set _targetSelectionRaw(value) { + this._appState().setTargetSelectionRaw(value || {}); + } + get _seriesRows() { + return this._appState().state.targets.rows; + } + set _seriesRows(value) { + this._appState().setSeriesRows(Array.isArray(value) ? value : []); + } + get _startTime() { + return this._appState().state.range.startTime; + } + set _startTime(value) { + this._appState().setRange(value || null, this._endTime || null); + } + get _endTime() { + return this._appState().state.range.endTime; + } + set _endTime(value) { + this._appState().setRange(this._startTime || null, value || null); + } + get _sidebarCollapsed() { + return this._appState().state.display.sidebarCollapsed; + } + set _sidebarCollapsed(value) { + this._appState().setSidebarCollapsed(!!value); + } + get _comparisonWindows() { + return this._appState().state.comparison.windows; + } + set _comparisonWindows(value) { + this._appState().setComparisonWindows(Array.isArray(value) ? value : []); + } + get _selectedComparisonWindowId() { + return this._appState().state.comparison.selectedWindowId; + } + set _selectedComparisonWindowId(value) { + this._appState().setSelectedComparisonWindowId(value || null); + } + get _hoveredComparisonWindowId() { + return this._appState().state.comparison.hoveredWindowId; + } + set _hoveredComparisonWindowId(value) { + this._appState().setHoveredComparisonWindowId(value || null); + } + get _chartZoomRange() { + return this._appState().state.range.previewZoomRange; + } + set _chartZoomRange(value) { + this._appState().setPreviewZoomRange(value || null); + } + get _chartZoomCommittedRange() { + return this._appState().state.range.committedZoomRange; + } + set _chartZoomCommittedRange(value) { + this._appState().setCommittedZoomRange(value || null); + } + get _historyBoundsLoaded() { + return this._context.fetch.state.historyBoundsLoaded; + } + set _historyBoundsLoaded(value) { + this._context.fetch.state.historyBoundsLoaded = !!value; + } + get _preferencesLoaded() { + return this._context.fetch.state.preferencesLoaded; + } + set _preferencesLoaded(value) { + this._context.fetch.state.preferencesLoaded = !!value; + } + get _savedPageLoaded() { + return this._context.fetch.state.savedPageLoaded; + } + set _savedPageLoaded(value) { + this._context.fetch.state.savedPageLoaded = !!value; + } + get _hasSavedPage() { + return this._context.fetch.state.hasSavedPage; + } + set _hasSavedPage(value) { + this._context.fetch.state.hasSavedPage = !!value; + } + get _timelineEventsKey() { + return this._context.fetch.state.timelineEventsKey; + } + set _timelineEventsKey(value) { + this._context.fetch.state.timelineEventsKey = String(value || ""); + } + get _savePageBusy() { + return this._context.persistence.state.savePageBusy; + } + set _savePageBusy(value) { + this._context.persistence.state.savePageBusy = !!value; + } + get _exportBusy() { + return this._context.persistence.state.exportBusy; + } + set _exportBusy(value) { + this._context.persistence.state.exportBusy = !!value; + } + set hass(hass) { + this._hass = hass; + this._context.hass = hass; + syncFrontendLocale(this._hass).then((locale) => { + if (!this.isConnected) return; + if (!this._shellBuilt && this._rendered) { + this._buildLoadingShell(); + return; + } + if (!this._rendered) return; + if (locale !== this._lastSyncedLocale) { + this._lastSyncedLocale = locale; + this._renderContent(); + } else { + if (this._shellEl && this._hass) this._shellEl.hass = this._hass; + if (this._chartEl) this._chartEl.hass = this._hass; + if (this._listEl) this._listEl.hass = this._hass; + if (this._targetControl && this._hass) this._targetControl.hass = this._hass; + if (this._historyTargetsComp) { + this._historyTargetsComp.hass = this._hass ?? null; + this._historyTargetsComp.states = this._hass?.states ?? {}; + } + if (this._rowListEl) { + this._rowListEl.hass = this._hass ?? null; + this._rowListEl.states = this._hass?.states ?? {}; + } + if (this._rangeToolbarComp) this._rangeToolbarComp.hass = this._hass ?? null; + this.shadowRoot?.querySelectorAll("[data-series-icon-entity-id], [data-series-collapsed-icon-entity-id]").forEach((iconEl) => { + const icon = iconEl; + const entityId = icon.dataset.seriesIconEntityId || icon.dataset.seriesCollapsedIconEntityId; + if (!entityId) return; + icon.stateObj = this._hass?.states?.[entityId]; + icon.hass = this._hass; + }); + } + }); + if (!this._haEventUnsubscribe && this._hass?.connection) this._hass.connection.subscribeEvents(() => this._handleEventRecorded(), `${DOMAIN}_event_recorded`).then((unsub) => { + this._haEventUnsubscribe = unsub; + }).catch(() => {}); + if (!this._rendered) { + this._rendered = true; + this._initFromContext(); + if (this.isConnected) this._buildLoadingShell(); + } + if (!this._seriesRows.length && Object.keys(this._targetSelection || {}).length) this._seriesRows = buildHistorySeriesRows(resolveEntityIdsFromTarget(this._hass, this._targetSelection)); + this._syncSeriesState(); + if (!this._shellBuilt) return; + this._ensureHistoryBounds(); + this._ensureUserPreferences(); + this._loadSavedPageIndicator(); + } + set panel(panel) { + this._panel = panel; + this._initFromContext(); + if (this._rendered) { + this._syncControls(); + this._renderContent(); + } + } + set narrow(value) { + this._narrow = value; + } + connectedCallback() { + this._mqTablet.addEventListener("change", this._onLayoutChange); + this._mqMobile.addEventListener("change", this._onLayoutChange); + this._updateLayoutMode(); + this._onOverlayKeydown = (ev) => { + if (ev.key === "Escape" && !this._sidebarCollapsed && this._layoutMode !== "desktop") this._toggleSidebarCollapsed(); + }; + window.addEventListener("keydown", this._onOverlayKeydown); + window.addEventListener("popstate", this._onPopState); + window.addEventListener("location-changed", this._onLocationChanged); + window.addEventListener("pointerdown", this._onWindowPointerDown, true); + window.addEventListener("resize", this._onWindowResize); + window.addEventListener("hass-datapoints-event-recorded", this._onEventRecorded); + this.addEventListener("hass-datapoints-chart-hover", this._onChartHover); + this.addEventListener("hass-datapoints-chart-zoom", this._onChartZoom); + this.addEventListener("hass-datapoints-records-search", this._onRecordsSearch); + this.addEventListener("hass-datapoints-toggle-event-visibility", this._onToggleEventVisibility); + this.addEventListener("hass-datapoints-hover-event-record", this._onHoverEventRecord); + this.addEventListener("hass-datapoints-toggle-series-visibility", this._onToggleSeriesVisibility); + this.addEventListener("hass-datapoints-comparison-loading", this._onComparisonLoading); + this.addEventListener("hass-datapoints-analysis-computing", this._onAnalysisComputing); + this.addEventListener("hass-datapoints-analysis-method-result", this._onAnalysisMethodResult); + if (this._rendered && !this._shellBuilt) this._buildLoadingShell(); + this._ensureUiComponentsReady(); + if (this._rendered && this._shellBuilt) window.requestAnimationFrame(() => { + if (!this.isConnected) return; + this._syncControls(); + this._renderContent(); + if (this._restoredFromSession) { + this._restoredFromSession = false; + this._updateUrl({ push: false }); + } + }); + } + disconnectedCallback() { + this._mqTablet.removeEventListener("change", this._onLayoutChange); + this._mqMobile.removeEventListener("change", this._onLayoutChange); + if (this._onOverlayKeydown) window.removeEventListener("keydown", this._onOverlayKeydown); + window.removeEventListener("popstate", this._onPopState); + window.removeEventListener("location-changed", this._onLocationChanged); + window.removeEventListener("pointerdown", this._onWindowPointerDown, true); + window.removeEventListener("resize", this._onWindowResize); + window.removeEventListener("hass-datapoints-event-recorded", this._onEventRecorded); + if (this._haEventUnsubscribe) { + this._haEventUnsubscribe(); + this._haEventUnsubscribe = null; + } + this.removeEventListener("hass-datapoints-chart-hover", this._onChartHover); + this.removeEventListener("hass-datapoints-chart-zoom", this._onChartZoom); + this.removeEventListener("hass-datapoints-records-search", this._onRecordsSearch); + this.removeEventListener("hass-datapoints-toggle-event-visibility", this._onToggleEventVisibility); + this.removeEventListener("hass-datapoints-hover-event-record", this._onHoverEventRecord); + this.removeEventListener("hass-datapoints-toggle-series-visibility", this._onToggleSeriesVisibility); + this.removeEventListener("hass-datapoints-comparison-loading", this._onComparisonLoading); + this.removeEventListener("hass-datapoints-analysis-computing", this._onAnalysisComputing); + this.removeEventListener("hass-datapoints-analysis-method-result", this._onAnalysisMethodResult); + if (this._rangeCommitTimer) { + window.clearTimeout(this._rangeCommitTimer); + this._rangeCommitTimer = null; + } + if (this._autoZoomTimer) { + window.clearTimeout(this._autoZoomTimer); + this._autoZoomTimer = null; + } + if (this._pendingPreferencesSaveTimer) { + window.clearTimeout(this._pendingPreferencesSaveTimer); + this._pendingPreferencesSaveTimer = null; + } + if (this._chartZoomStateCommitTimer) { + window.clearTimeout(this._chartZoomStateCommitTimer); + this._chartZoomStateCommitTimer = null; + } + this._hideCollapsedTargetPopup(); + this._hideCollapsedOptionsPopup(); + this._uiReadyPromise = null; + this._uiReadyApplied = false; + this._context.orchestration.cancelChartResizeRedraw(); + } + _initFromContext() { + const { entityFromUrl, deviceFromUrl, areaFromUrl, labelFromUrl, datapointsScopeFromUrl, startFromUrl, endFromUrl, zoomStartFromUrl, zoomEndFromUrl, seriesColorsFromUrl, dateWindowsFromUrl, hoursFromUrl, hasTargetInUrl, hasRangeInUrl, pageStateFromUrl, sessionState } = this._context.navigation.readStateFromLocation(); + const persistedState = pageStateFromUrl && typeof pageStateFromUrl === "object" ? { + ...sessionState || {}, + ...pageStateFromUrl + } : sessionState; + const panelCfg = this._panel?.config ?? {}; + this._hasTargetInUrl = hasTargetInUrl; + this._hasRangeInUrl = hasRangeInUrl; + this._hasPageStateInUrl = !!pageStateFromUrl; + this._localPageStateDirty = false; + this._restoredFromSession = !hasTargetInUrl && !hasRangeInUrl && !!persistedState; + this._sidebarCollapsed = !!persistedState?.sidebar_collapsed; + this._sidebarAccordionTargetsOpen = persistedState?.sidebar_accordion_targets_open !== false; + this._sidebarAccordionDatapointsOpen = persistedState?.sidebar_accordion_datapoints_open !== false; + this._sidebarAccordionAnalysisOpen = persistedState?.sidebar_accordion_analysis_open !== false; + this._sidebarAccordionChartOpen = persistedState?.sidebar_accordion_chart_open !== false; + if (persistedState && Number.isFinite(persistedState.content_split_ratio)) this._contentSplitRatio = clampNumber(persistedState.content_split_ratio, .25, .75); + let resolvedDatapointScope; + if (datapointsScopeFromUrl === "all") resolvedDatapointScope = "all"; + else if (datapointsScopeFromUrl === "hidden") resolvedDatapointScope = "hidden"; + else if (!datapointsScopeFromUrl && persistedState?.datapoint_scope === "all") resolvedDatapointScope = "all"; + else if (!datapointsScopeFromUrl && persistedState?.datapoint_scope === "hidden") resolvedDatapointScope = "hidden"; + else resolvedDatapointScope = "linked"; + this._datapointScope = resolvedDatapointScope; + this._showChartDatapointIcons = persistedState?.show_chart_datapoint_icons !== false; + this._showChartDatapointLines = persistedState?.show_chart_datapoint_lines !== false; + this._showChartTooltips = persistedState?.show_chart_tooltips !== false; + this._showChartEmphasizedHoverGuides = persistedState?.show_chart_emphasized_hover_guides === true; + this._chartHoverSnapMode = persistedState?.chart_hover_snap_mode === "snap_to_data_points" ? "snap_to_data_points" : "follow_series"; + this._delinkChartYAxis = persistedState?.delink_chart_y_axis === true; + this._splitChartView = persistedState?.split_chart_view === true; + this._showCorrelatedAnomalies = persistedState?.show_chart_correlated_anomalies === true; + this._chartAnomalyOverlapMode = ANALYSIS_ANOMALY_OVERLAP_MODE_OPTIONS.some((o) => o.value === persistedState?.chart_anomaly_overlap_mode) ? persistedState.chart_anomaly_overlap_mode : "all"; + this._showDataGaps = persistedState?.show_data_gaps !== false; + this._dataGapThreshold = DATA_GAP_THRESHOLD_OPTIONS.some((option) => option.value === persistedState?.data_gap_threshold) ? persistedState.data_gap_threshold : "2h"; + this._comparisonWindows = dateWindowsFromUrl.length ? dateWindowsFromUrl : normalizeDateWindows(persistedState?.date_windows); + const targetFromUrl = normalizeTargetValue({ + entity_id: entityFromUrl ? entityFromUrl.split(",") : [], + device_id: deviceFromUrl ? deviceFromUrl.split(",") : [], + area_id: areaFromUrl ? areaFromUrl.split(",") : [], + label_id: labelFromUrl ? labelFromUrl.split(",") : [] + }); + const panelTarget = panelConfigTarget(panelCfg); + let nextTargetSelection; + if (Object.keys(targetFromUrl).length) nextTargetSelection = targetFromUrl; + else if (!hasTargetInUrl && persistedState?.entities?.length) nextTargetSelection = normalizeTargetValue(persistedState.target_selection_raw || persistedState.target_selection || { entity_id: persistedState.entities }); + else nextTargetSelection = panelTarget; + this._targetSelection = nextTargetSelection; + this._targetSelectionRaw = !hasTargetInUrl && persistedState?.target_selection_raw ? persistedState.target_selection_raw : nextTargetSelection; + this._seriesRows = !hasTargetInUrl && Array.isArray(persistedState?.series_rows) ? normalizeHistorySeriesRows(persistedState.series_rows) : buildHistorySeriesRows(resolveEntityIdsFromTarget(this._hass, this._targetSelection)); + if (Array.isArray(persistedState?.series_rows)) this._seriesRows = this._mergeSavedSeriesRows(this._seriesRows, persistedState.series_rows); + this._seriesRows = this._applyPreferredSeriesColors(this._seriesRows, seriesColorsFromUrl); + this._syncSeriesState(); + const start = parseDateValue(startFromUrl) || (!hasRangeInUrl ? parseDateValue(persistedState?.start_time) : null) || parseDateValue(panelCfg.start_time); + const end = parseDateValue(endFromUrl) || (!hasRangeInUrl ? parseDateValue(persistedState?.end_time) : null) || parseDateValue(panelCfg.end_time); + const zoomStart = parseDateValue(zoomStartFromUrl) || (!zoomStartFromUrl && !zoomEndFromUrl ? parseDateValue(persistedState?.zoom_start_time) : null); + const zoomEnd = parseDateValue(zoomEndFromUrl) || (!zoomStartFromUrl && !zoomEndFromUrl ? parseDateValue(persistedState?.zoom_end_time) : null); + this._chartZoomRange = null; + this._chartZoomCommittedRange = zoomStart && zoomEnd && zoomStart < zoomEnd ? { + start: zoomStart.getTime(), + end: zoomEnd.getTime() + } : null; + if (start && end && start < end) { + this._startTime = start; + this._endTime = end; + this._hours = Math.max(1, Math.round((end.getTime() - start.getTime()) / 36e5)); + return; + } + if (Number.isFinite(hoursFromUrl) && hoursFromUrl > 0) this._hours = hoursFromUrl; + else if (!hasRangeInUrl && persistedState && Number.isFinite(persistedState.hours) && persistedState.hours > 0) this._hours = persistedState.hours; + else if (panelCfg.hours_to_show) this._hours = panelCfg.hours_to_show; + const now = /* @__PURE__ */ new Date(); + this._startTime = startOfUnit(now, "week"); + this._endTime = endOfUnit(now, "week"); + this._hours = Math.max(1, Math.round((this._endTime.getTime() - this._startTime.getTime()) / 36e5)); + } + _saveSessionState() { + this._localPageStateDirty = true; + this._context.navigation.saveSessionState(this); + this._scheduleUserPreferencesSave(); + } + _scheduleUserPreferencesSave() { + if (this._pendingPreferencesSaveTimer) window.clearTimeout(this._pendingPreferencesSaveTimer); + this._pendingPreferencesSaveTimer = window.setTimeout(() => { + this._pendingPreferencesSaveTimer = null; + this._saveUserPreferences(); + }, 160); + } + _applyPreferencePageState(state) { + if (!state || typeof state !== "object") return; + const s = state; + if (!this._hasPageStateInUrl) { + this._sidebarCollapsed = !!s.sidebar_collapsed; + this._sidebarAccordionTargetsOpen = s.sidebar_accordion_targets_open !== false; + this._sidebarAccordionDatapointsOpen = s.sidebar_accordion_datapoints_open !== false; + this._sidebarAccordionAnalysisOpen = s.sidebar_accordion_analysis_open !== false; + this._sidebarAccordionChartOpen = s.sidebar_accordion_chart_open !== false; + if (Number.isFinite(s.content_split_ratio)) this._contentSplitRatio = clampNumber(s.content_split_ratio, .25, .75); + this._showChartDatapointIcons = s.show_chart_datapoint_icons !== false; + this._showChartDatapointLines = s.show_chart_datapoint_lines !== false; + this._showChartTooltips = s.show_chart_tooltips !== false; + this._showChartEmphasizedHoverGuides = s.show_chart_emphasized_hover_guides === true; + this._chartHoverSnapMode = s.chart_hover_snap_mode === "snap_to_data_points" ? "snap_to_data_points" : "follow_series"; + this._delinkChartYAxis = s.delink_chart_y_axis === true; + this._splitChartView = s.split_chart_view === true; + this._showCorrelatedAnomalies = s.show_chart_correlated_anomalies === true; + this._chartAnomalyOverlapMode = ANALYSIS_ANOMALY_OVERLAP_MODE_OPTIONS.some((option) => option.value === s.chart_anomaly_overlap_mode) ? s.chart_anomaly_overlap_mode : "all"; + this._showDataGaps = s.show_data_gaps !== false; + this._dataGapThreshold = DATA_GAP_THRESHOLD_OPTIONS.some((option) => option.value === s.data_gap_threshold) ? s.data_gap_threshold : this._dataGapThreshold; + this._datapointScope = s.datapoint_scope === "all" || s.datapoint_scope === "hidden" ? s.datapoint_scope : "linked"; + } + if (!this._hasTargetInUrl) { + if (s.target_selection) this._targetSelection = normalizeTargetValue(s.target_selection); + if (s.target_selection_raw) this._targetSelectionRaw = s.target_selection_raw; + } + if (Array.isArray(s.series_rows)) { + const nextRows = !this._hasTargetInUrl ? normalizeHistorySeriesRows(s.series_rows) : this._mergeSavedSeriesRows(this._seriesRows, s.series_rows); + this._seriesRows = this._applyPreferredSeriesColors(nextRows); + this._syncSeriesState(); + } + if (!this._hasRangeInUrl) { + const start = parseDateValue(s.start_time); + const end = parseDateValue(s.end_time); + if (start && end && start < end) { + this._startTime = start; + this._endTime = end; + } + const zoomStart = parseDateValue(s.zoom_start_time); + const zoomEnd = parseDateValue(s.zoom_end_time); + this._chartZoomCommittedRange = zoomStart && zoomEnd && zoomStart < zoomEnd ? { + start: zoomStart.getTime(), + end: zoomEnd.getTime() + } : this._chartZoomCommittedRange; + if (s.hours && Number.isFinite(s.hours) && s.hours > 0) this._hours = s.hours; + if (Array.isArray(s.date_windows) && !this._hasPageStateInUrl) this._comparisonWindows = normalizeDateWindows(s.date_windows); + } + } + _buildLoadingShell() { + this._shellBuilt = false; + const loadingLabel = msg("Loading Datapoints…"); + const root = this.shadowRoot; + if (!root) return; + root.innerHTML = `
@@ -31317,478 +30706,338 @@ ${content.alert}` : "",
`; - } - _buildShell() { - this._shellBuilt = true; - const root = this.shadowRoot; - if (!root) { - return; - } - root.innerHTML = ``; - const shell = document.createElement("panel-shell"); - if (this._hass) { - shell.hass = this._hass; - } - shell.narrow = this._narrow; - shell.sidebarCollapsed = this._sidebarCollapsed; - shell.hasSavedState = this._hasSavedPage; - shell.layoutMode = this._layoutMode; - root.appendChild(shell); - this._shellEl = shell; - const contentHost = document.createElement("div"); - contentHost.id = "content"; - shell.appendChild(contentHost); - this._contentHostEl = contentHost; - shell.addEventListener( - "dp-shell-menu-download", - () => this._downloadSpreadsheet() - ); - shell.addEventListener("dp-shell-menu-save", () => this._savePageState()); - shell.addEventListener( - "dp-shell-menu-restore", - () => this._restorePageState() - ); - shell.addEventListener( - "dp-shell-menu-clear", - () => this._clearSavedPageState() - ); - shell.addEventListener( - "dp-shell-sidebar-toggle", - () => this._toggleSidebarCollapsed() - ); - shell.addEventListener("dp-shell-scrim-click", () => { - if (!this._sidebarCollapsed) { - this._toggleSidebarCollapsed(); - } - }); - shell.addEventListener("click", this._onCollapsedSidebarClick); - shell.updateComplete.then(() => { - if (!this.isConnected) { - return; - } - this._sidebarOptionsEl = shell.shadowRoot?.querySelector("#sidebar-options") ?? null; - shell.syncLayoutHeight(); - this._applyContentSplitLayout(); - this._mountControls(); - this._renderSidebarOptions(); - this._ensureUiComponentsReady(); - }); - } - _syncPageLayoutHeight() { - this._shellEl?.syncLayoutHeight(); - } - _bootstrapAfterShellBuilt() { - if (!this._shellBuilt) { - return; - } - this._ensureHistoryBounds(); - this._ensureUserPreferences(); - this._loadSavedPageIndicator(); - this._syncHassBindings(); - this._renderContent(); - if (this._restoredFromSession) { - this._restoredFromSession = false; - this._updateUrl({ push: false }); - } - } - _ensureUiComponentsReady() { - if (this._uiReadyPromise) { - return this._uiReadyPromise; - } - const componentTags = [ - "ha-top-app-bar-fixed", - "ha-menu-button", - "ha-icon-button", - "ha-dialog", - "ha-tooltip", - "ha-target-picker", - "ha-date-range-picker" - ]; - this._uiReadyPromise = ensureHaComponents(componentTags).then((results) => results).then(() => { - if (!this.isConnected || !this._rendered) { - return; - } - window.requestAnimationFrame(() => { - window.requestAnimationFrame(() => { - if (!this.isConnected || !this._rendered) { - return; - } - this._uiReadyApplied = true; - if (!this._shellBuilt) { - this._buildShell(); - } - this._syncControls(); - this._bootstrapAfterShellBuilt(); - }); - }); - }).catch((error) => { - logger$1.warn( - "[hass-datapoints panel] ensure UI components ready failed", - { - message: error?.message || String(error) - } - ); - }); - return this._uiReadyPromise; - } - _syncControls() { - this._syncPageLayoutHeight(); - this._syncHassBindings(); - this._syncRangeUi(); - this._renderSidebarOptions(); - } - _syncSeriesState() { - this._seriesRows = normalizeHistorySeriesRows(this._seriesRows); - this._entities = this._seriesRows.map( - (row) => row.entity_id - ); - this._targetSelection = this._entities.length ? { entity_id: [...this._entities] } : {}; - this._targetSelectionRaw = this._targetSelection; - } - _seriesColorQueryKey(entityId) { - return slugifySeriesName(entityName(this._hass, entityId) || entityId); - } - _applyPreferredSeriesColors(rows, urlColorMap = null) { - const queryColors = urlColorMap && typeof urlColorMap === "object" ? urlColorMap : {}; - return normalizeHistorySeriesRows(rows).map((row) => { - const queryColor = queryColors[this._seriesColorQueryKey(row.entity_id)]; - const preferredColor = this._preferredSeriesColors?.[row.entity_id]; - let nextColor; - if (/^#[0-9a-f]{6}$/i.test(queryColor || "")) { - nextColor = queryColor; - } else if (/^#[0-9a-f]{6}$/i.test(preferredColor || "")) { - nextColor = preferredColor; - } else { - nextColor = row.color; - } - return nextColor === row.color ? row : { ...row, color: nextColor }; - }); - } - _mergeSavedSeriesRows(rows, savedRows) { - const normalizedRows = normalizeHistorySeriesRows(rows); - const normalizedSavedRows = normalizeHistorySeriesRows(savedRows); - if (!normalizedSavedRows.length) { - return normalizedRows; - } - const savedRowMap = new Map( - normalizedSavedRows.map((row) => [row.entity_id, row]) - ); - return normalizedRows.map((row) => { - const savedRow = savedRowMap.get(row.entity_id); - if (!savedRow) { - return row; - } - return { - ...row, - color: savedRow.color, - visible: savedRow.visible, - analysis: savedRow.analysis - }; - }); - } - _syncHassBindings() { - if (this._shellEl) { - if (this._hass) { - this._shellEl.hass = this._hass; - } - this._shellEl.narrow = this._narrow; - } - this._syncSidebarUi(); - if (this._targetControl) { - if (this._hass) { - this._targetControl.hass = this._hass; - } - this._targetControl.value = {}; - } - this._renderTargetRows(); - this.shadowRoot?.querySelectorAll( - "[data-series-icon-entity-id], [data-series-collapsed-icon-entity-id]" - ).forEach((iconEl) => { - const icon = iconEl; - const entityId = icon.dataset.seriesIconEntityId || icon.dataset.seriesCollapsedIconEntityId; - if (!entityId) { - return; - } - icon.stateObj = this._hass?.states?.[entityId]; - icon.hass = this._hass; - }); - if (this._rangeToolbarComp) { - this._rangeToolbarComp.hass = this._hass ?? null; - } - } - _syncRangeUi() { - if (!this._dateControl) { - return; - } - this._syncOptionsMenu(); - this._syncRangeControl(); - } - _renderSidebarOptions() { - if (!this._sidebarOptionsComp) { - return; - } - let yAxisMode; - if (this._splitChartView) { - yAxisMode = "split"; - } else if (this._delinkChartYAxis) { - yAxisMode = "unique"; - } else { - yAxisMode = "combined"; - } - this._sidebarOptionsComp.datapointScope = this._datapointScope; - this._sidebarOptionsComp.showIcons = this._showChartDatapointIcons; - this._sidebarOptionsComp.showLines = this._showChartDatapointLines; - this._sidebarOptionsComp.showTooltips = this._showChartTooltips; - this._sidebarOptionsComp.showHoverGuides = this._showChartEmphasizedHoverGuides; - this._sidebarOptionsComp.hoverSnapMode = this._chartHoverSnapMode; - this._sidebarOptionsComp.showCorrelatedAnomalies = this._showCorrelatedAnomalies; - this._sidebarOptionsComp.showDataGaps = this._showDataGaps; - this._sidebarOptionsComp.dataGapThreshold = this._dataGapThreshold; - this._sidebarOptionsComp.yAxisMode = yAxisMode; - this._sidebarOptionsComp.anomalyOverlapMode = this._chartAnomalyOverlapMode; - this._sidebarOptionsComp.anyAnomaliesEnabled = (this._seriesRows ?? []).some( - (r2) => r2.analysis?.show_anomalies === true - ); - this._sidebarOptionsComp.targetsOpen = this._sidebarAccordionTargetsOpen; - this._sidebarOptionsComp.datapointsOpen = this._sidebarAccordionDatapointsOpen; - this._sidebarOptionsComp.analysisOpen = this._sidebarAccordionAnalysisOpen; - this._sidebarOptionsComp.chartOpen = this._sidebarAccordionChartOpen; - this._refreshCollapsedOptionsPopup(); - } - _formatComparisonLabel(start, end) { - const fmt = (d2) => d2.toLocaleDateString(void 0, { month: "short", day: "numeric" }); - const fmtYear = (d2) => d2.toLocaleDateString(void 0, { - month: "short", - day: "numeric", - year: "numeric" - }); - const sameYear = start.getFullYear() === end.getFullYear(); - return sameYear ? `${fmt(start)} – ${fmt(end)}` : `${fmtYear(start)} – ${fmtYear(end)}`; - } - _getComparisonPreviewOverlay() { - const comparisonWindow = this._getActiveComparisonWindow(); - if (!comparisonWindow || !this._startTime || !this._endTime) { - return null; - } - const windowStart = parseDateValue(comparisonWindow.start_time); - const windowEnd = parseDateValue(comparisonWindow.end_time); - if (!windowStart || !windowEnd) { - return null; - } - const actualSpanMs = this._endTime.getTime() - this._startTime.getTime(); - if (!Number.isFinite(actualSpanMs) || actualSpanMs <= 0) { - return null; - } - const actualStart = new Date(windowStart.getTime()); - const actualEnd = new Date(windowStart.getTime() + actualSpanMs); - const windowRangeLabel = this._formatComparisonLabel( - windowStart, - windowEnd - ); - const actualRangeLabel = this._formatComparisonLabel( - actualStart, - actualEnd - ); - if (windowRangeLabel === actualRangeLabel) { - return null; - } - return { - label: comparisonWindow.label || "Preview", - window_range_label: windowRangeLabel, - actual_range_label: actualRangeLabel - }; - } - _getPreviewComparisonWindows() { - const comparisonIds = []; - if (this._selectedComparisonWindowId) { - comparisonIds.push(this._selectedComparisonWindowId); - } - if (this._hoveredComparisonWindowId && !comparisonIds.includes(this._hoveredComparisonWindowId)) { - comparisonIds.push(this._hoveredComparisonWindowId); - } - if (!comparisonIds.length) { - return []; - } - if (!this._startTime || !this._endTime) { - return []; - } - const startTime = this._startTime; - const previewWindows = comparisonIds.map( - (id) => this._comparisonWindows.find((w) => w.id === id) ?? null - ).filter((w) => w !== null).map((window2) => ({ - ...window2, - time_offset_ms: new Date(window2.start_time).getTime() - startTime.getTime() - })); - return previewWindows; - } - _getPreloadComparisonWindows() { - if (!this._startTime || !this._endTime) { - return []; - } - const startTime = this._startTime; - const preloadWindows = this._comparisonWindows.map((window2) => ({ - ...window2, - time_offset_ms: new Date(window2.start_time).getTime() - startTime.getTime() - })).filter((window2) => Number.isFinite(window2.time_offset_ms)); - return preloadWindows; - } - _getActiveComparisonWindow() { - if (this._hoveredComparisonWindowId) { - return this._comparisonWindows.find( - (window2) => window2.id === this._hoveredComparisonWindowId - ) || null; - } - if (this._selectedComparisonWindowId) { - return this._comparisonWindows.find( - (window2) => window2.id === this._selectedComparisonWindowId - ) || null; - } - return null; - } - _formatDateWindowInputValue(date) { - if (!(date instanceof Date) || Number.isNaN(date.getTime())) { - return ""; - } - const year = date.getFullYear(); - const month = String(date.getMonth() + 1).padStart(2, "0"); - const day = String(date.getDate()).padStart(2, "0"); - const hours = String(date.getHours()).padStart(2, "0"); - const minutes = String(date.getMinutes()).padStart(2, "0"); - return `${year}-${month}-${day}T${hours}:${minutes}`; - } - _parseDateWindowInputValue(value) { - if (!value || typeof value !== "string") { - return null; - } - const match = value.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})$/); - if (!match) { - return null; - } - const [, year, month, day, hour, minute] = match; - const parsed = new Date( - Number(year), - Number(month) - 1, - Number(day), - Number(hour), - Number(minute), - 0, - 0 - ); - if (Number.isNaN(parsed.getTime())) { - return null; - } - return parsed; - } - _shiftDateWindowByUnit(date, unit, amount) { - const shifted = new Date(date); - if (unit === "day") { - shifted.setDate(shifted.getDate() + amount); - return shifted; - } - if (unit === "week") { - shifted.setDate(shifted.getDate() + amount * 7); - return shifted; - } - if (unit === "month") { - shifted.setMonth(shifted.getMonth() + amount); - return shifted; - } - if (unit === "year") { - shifted.setFullYear(shifted.getFullYear() + amount); - return shifted; - } - return shifted; - } - _getRoundedDateWindowUnit(start, end) { - if (!(start instanceof Date) || !(end instanceof Date) || !(start < end)) { - return null; - } - const supportedUnits = ["day", "week", "month", "year"]; - for (const unit of supportedUnits) { - const roundedStart = startOfUnit(start, unit); - const roundedEnd = endOfUnit(start, unit); - if (roundedStart?.getTime?.() === start.getTime() && roundedEnd?.getTime?.() === end.getTime()) { - return unit; - } - } - return null; - } - _syncDateWindowDialogInputs() { - const startVal = this._formatDateWindowInputValue( - this._dateWindowDialogDraftRange?.start || null - ); - const endVal = this._formatDateWindowInputValue( - this._dateWindowDialogDraftRange?.end || null - ); - if (this._dateWindowDialogComp) { - this._dateWindowDialogComp.startValue = startVal; - this._dateWindowDialogComp.endValue = endVal; - return; - } - if (this._dateWindowDialogStartEl) { - this._dateWindowDialogStartEl.value = startVal; - } - if (this._dateWindowDialogEndEl) { - this._dateWindowDialogEndEl.value = endVal; - } - } - _handleDateWindowDialogInputChange() { - const start = this._parseDateWindowInputValue( - this._dateWindowDialogStartEl?.value || "" - ); - const end = this._parseDateWindowInputValue( - this._dateWindowDialogEndEl?.value || "" - ); - if (start && end && start < end) { - this._dateWindowDialogDraftRange = { start, end }; - return; - } - this._dateWindowDialogDraftRange = null; - } - _applyDateWindowShortcut(direction) { - if (this._editingDateWindowId) { - return; - } - const start = this._dateWindowDialogDraftRange?.start; - const end = this._dateWindowDialogDraftRange?.end; - if (!(start instanceof Date) || !(end instanceof Date) || !(start < end)) { - return; - } - const roundedUnit = this._getRoundedDateWindowUnit(start, end); - let nextStart; - let nextEnd; - if (roundedUnit) { - nextStart = startOfUnit( - this._shiftDateWindowByUnit(start, roundedUnit, direction), - roundedUnit - ); - nextEnd = endOfUnit(nextStart, roundedUnit); - } else { - const spanMs = end.getTime() - start.getTime(); - nextStart = new Date(start.getTime() + direction * spanMs); - nextEnd = new Date(end.getTime() + direction * spanMs); - } - this._dateWindowDialogDraftRange = { - start: nextStart, - end: nextEnd - }; - this._syncDateWindowDialogInputs(); - } - _ensureDateWindowDialog() { - if (this._dateWindowDialogComp || this._dateWindowDialogEl || !this.shadowRoot) - return; - const dialog = document.createElement("ha-dialog"); - dialog.id = "date-window-dialog"; - dialog.setAttribute("hideActions", ""); - dialog.scrimClickAction = true; - dialog.escapeKeyAction = true; - dialog.open = false; - dialog.headerTitle = "Add date window"; - dialog.style.setProperty( - "--dialog-content-padding", - `0 var(--dp-spacing-lg) var(--dp-spacing-lg)` - ); - dialog.innerHTML = ` + } + _buildShell() { + this._shellBuilt = true; + const root = this.shadowRoot; + if (!root) return; + root.innerHTML = ``; + const shell = document.createElement("panel-shell"); + if (this._hass) shell.hass = this._hass; + shell.narrow = this._narrow; + shell.sidebarCollapsed = this._sidebarCollapsed; + shell.hasSavedState = this._hasSavedPage; + shell.layoutMode = this._layoutMode; + root.appendChild(shell); + this._shellEl = shell; + const contentHost = document.createElement("div"); + contentHost.id = "content"; + shell.appendChild(contentHost); + this._contentHostEl = contentHost; + shell.addEventListener("dp-shell-menu-download", () => this._downloadSpreadsheet()); + shell.addEventListener("dp-shell-menu-save", () => this._savePageState()); + shell.addEventListener("dp-shell-menu-restore", () => this._restorePageState()); + shell.addEventListener("dp-shell-menu-clear", () => this._clearSavedPageState()); + shell.addEventListener("dp-shell-sidebar-toggle", () => this._toggleSidebarCollapsed()); + shell.addEventListener("dp-shell-scrim-click", () => { + if (!this._sidebarCollapsed) this._toggleSidebarCollapsed(); + }); + shell.addEventListener("click", this._onCollapsedSidebarClick); + shell.updateComplete.then(() => { + if (!this.isConnected) return; + this._sidebarOptionsEl = shell.shadowRoot?.querySelector("#sidebar-options") ?? null; + shell.syncLayoutHeight(); + this._applyContentSplitLayout(); + this._mountControls(); + this._renderSidebarOptions(); + this._ensureUiComponentsReady(); + }); + } + _syncPageLayoutHeight() { + this._shellEl?.syncLayoutHeight(); + } + _bootstrapAfterShellBuilt() { + if (!this._shellBuilt) return; + this._ensureHistoryBounds(); + this._ensureUserPreferences(); + this._loadSavedPageIndicator(); + this._syncHassBindings(); + this._renderContent(); + if (this._restoredFromSession) { + this._restoredFromSession = false; + this._updateUrl({ push: false }); + } + } + _ensureUiComponentsReady() { + if (this._uiReadyPromise) return this._uiReadyPromise; + this._uiReadyPromise = ensureHaComponents([ + "ha-top-app-bar-fixed", + "ha-menu-button", + "ha-icon-button", + "ha-dialog", + "ha-tooltip", + "ha-target-picker", + "ha-date-range-picker" + ]).then((results) => results).then(() => { + if (!this.isConnected || !this._rendered) return; + window.requestAnimationFrame(() => { + window.requestAnimationFrame(() => { + if (!this.isConnected || !this._rendered) return; + this._uiReadyApplied = true; + if (!this._shellBuilt) this._buildShell(); + this._syncControls(); + this._bootstrapAfterShellBuilt(); + }); + }); + }).catch((error) => { + logger$1.warn("[hass-datapoints panel] ensure UI components ready failed", { message: error?.message || String(error) }); + }); + return this._uiReadyPromise; + } + _syncControls() { + this._syncPageLayoutHeight(); + this._syncHassBindings(); + this._syncRangeUi(); + this._renderSidebarOptions(); + } + _syncSeriesState() { + this._seriesRows = normalizeHistorySeriesRows(this._seriesRows); + this._entities = this._seriesRows.map((row) => row.entity_id); + this._targetSelection = this._entities.length ? { entity_id: [...this._entities] } : {}; + this._targetSelectionRaw = this._targetSelection; + } + _seriesColorQueryKey(entityId) { + return slugifySeriesName(entityName(this._hass, entityId) || entityId); + } + _applyPreferredSeriesColors(rows, urlColorMap = null) { + const queryColors = urlColorMap && typeof urlColorMap === "object" ? urlColorMap : {}; + return normalizeHistorySeriesRows(rows).map((row) => { + const queryColor = queryColors[this._seriesColorQueryKey(row.entity_id)]; + const preferredColor = this._preferredSeriesColors?.[row.entity_id]; + let nextColor; + if (/^#[0-9a-f]{6}$/i.test(queryColor || "")) nextColor = queryColor; + else if (/^#[0-9a-f]{6}$/i.test(preferredColor || "")) nextColor = preferredColor; + else nextColor = row.color; + return nextColor === row.color ? row : { + ...row, + color: nextColor + }; + }); + } + _mergeSavedSeriesRows(rows, savedRows) { + const normalizedRows = normalizeHistorySeriesRows(rows); + const normalizedSavedRows = normalizeHistorySeriesRows(savedRows); + if (!normalizedSavedRows.length) return normalizedRows; + const savedRowMap = new Map(normalizedSavedRows.map((row) => [row.entity_id, row])); + return normalizedRows.map((row) => { + const savedRow = savedRowMap.get(row.entity_id); + if (!savedRow) return row; + return { + ...row, + color: savedRow.color, + visible: savedRow.visible, + analysis: savedRow.analysis + }; + }); + } + _syncHassBindings() { + if (this._shellEl) { + if (this._hass) this._shellEl.hass = this._hass; + this._shellEl.narrow = this._narrow; + } + this._syncSidebarUi(); + if (this._targetControl) { + if (this._hass) this._targetControl.hass = this._hass; + this._targetControl.value = {}; + } + this._renderTargetRows(); + this.shadowRoot?.querySelectorAll("[data-series-icon-entity-id], [data-series-collapsed-icon-entity-id]").forEach((iconEl) => { + const icon = iconEl; + const entityId = icon.dataset.seriesIconEntityId || icon.dataset.seriesCollapsedIconEntityId; + if (!entityId) return; + icon.stateObj = this._hass?.states?.[entityId]; + icon.hass = this._hass; + }); + if (this._rangeToolbarComp) this._rangeToolbarComp.hass = this._hass ?? null; + } + _syncRangeUi() { + if (!this._dateControl) return; + this._syncOptionsMenu(); + this._syncRangeControl(); + } + _renderSidebarOptions() { + if (!this._sidebarOptionsComp) return; + let yAxisMode; + if (this._splitChartView) yAxisMode = "split"; + else if (this._delinkChartYAxis) yAxisMode = "unique"; + else yAxisMode = "combined"; + this._sidebarOptionsComp.datapointScope = this._datapointScope; + this._sidebarOptionsComp.showIcons = this._showChartDatapointIcons; + this._sidebarOptionsComp.showLines = this._showChartDatapointLines; + this._sidebarOptionsComp.showTooltips = this._showChartTooltips; + this._sidebarOptionsComp.showHoverGuides = this._showChartEmphasizedHoverGuides; + this._sidebarOptionsComp.hoverSnapMode = this._chartHoverSnapMode; + this._sidebarOptionsComp.showCorrelatedAnomalies = this._showCorrelatedAnomalies; + this._sidebarOptionsComp.showDataGaps = this._showDataGaps; + this._sidebarOptionsComp.dataGapThreshold = this._dataGapThreshold; + this._sidebarOptionsComp.yAxisMode = yAxisMode; + this._sidebarOptionsComp.anomalyOverlapMode = this._chartAnomalyOverlapMode; + this._sidebarOptionsComp.anyAnomaliesEnabled = (this._seriesRows ?? []).some((r) => r.analysis?.show_anomalies === true); + this._sidebarOptionsComp.targetsOpen = this._sidebarAccordionTargetsOpen; + this._sidebarOptionsComp.datapointsOpen = this._sidebarAccordionDatapointsOpen; + this._sidebarOptionsComp.analysisOpen = this._sidebarAccordionAnalysisOpen; + this._sidebarOptionsComp.chartOpen = this._sidebarAccordionChartOpen; + this._refreshCollapsedOptionsPopup(); + } + _formatComparisonLabel(start, end) { + const fmt = (d) => d.toLocaleDateString(void 0, { + month: "short", + day: "numeric" + }); + const fmtYear = (d) => d.toLocaleDateString(void 0, { + month: "short", + day: "numeric", + year: "numeric" + }); + return start.getFullYear() === end.getFullYear() ? `${fmt(start)} – ${fmt(end)}` : `${fmtYear(start)} – ${fmtYear(end)}`; + } + _getComparisonPreviewOverlay() { + const comparisonWindow = this._getActiveComparisonWindow(); + if (!comparisonWindow || !this._startTime || !this._endTime) return null; + const windowStart = parseDateValue(comparisonWindow.start_time); + const windowEnd = parseDateValue(comparisonWindow.end_time); + if (!windowStart || !windowEnd) return null; + const actualSpanMs = this._endTime.getTime() - this._startTime.getTime(); + if (!Number.isFinite(actualSpanMs) || actualSpanMs <= 0) return null; + const actualStart = new Date(windowStart.getTime()); + const actualEnd = new Date(windowStart.getTime() + actualSpanMs); + const windowRangeLabel = this._formatComparisonLabel(windowStart, windowEnd); + const actualRangeLabel = this._formatComparisonLabel(actualStart, actualEnd); + if (windowRangeLabel === actualRangeLabel) return null; + return { + label: comparisonWindow.label || "Preview", + window_range_label: windowRangeLabel, + actual_range_label: actualRangeLabel + }; + } + _getPreviewComparisonWindows() { + const comparisonIds = []; + if (this._selectedComparisonWindowId) comparisonIds.push(this._selectedComparisonWindowId); + if (this._hoveredComparisonWindowId && !comparisonIds.includes(this._hoveredComparisonWindowId)) comparisonIds.push(this._hoveredComparisonWindowId); + if (!comparisonIds.length) return []; + if (!this._startTime || !this._endTime) return []; + const startTime = this._startTime; + return comparisonIds.map((id) => this._comparisonWindows.find((w) => w.id === id) ?? null).filter((w) => w !== null).map((window) => ({ + ...window, + time_offset_ms: new Date(window.start_time).getTime() - startTime.getTime() + })); + } + _getPreloadComparisonWindows() { + if (!this._startTime || !this._endTime) return []; + const startTime = this._startTime; + return this._comparisonWindows.map((window) => ({ + ...window, + time_offset_ms: new Date(window.start_time).getTime() - startTime.getTime() + })).filter((window) => Number.isFinite(window.time_offset_ms)); + } + _getActiveComparisonWindow() { + if (this._hoveredComparisonWindowId) return this._comparisonWindows.find((window) => window.id === this._hoveredComparisonWindowId) || null; + if (this._selectedComparisonWindowId) return this._comparisonWindows.find((window) => window.id === this._selectedComparisonWindowId) || null; + return null; + } + _formatDateWindowInputValue(date) { + if (!(date instanceof Date) || Number.isNaN(date.getTime())) return ""; + return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}T${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`; + } + _parseDateWindowInputValue(value) { + if (!value || typeof value !== "string") return null; + const match = value.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})$/); + if (!match) return null; + const [, year, month, day, hour, minute] = match; + const parsed = new Date(Number(year), Number(month) - 1, Number(day), Number(hour), Number(minute), 0, 0); + if (Number.isNaN(parsed.getTime())) return null; + return parsed; + } + _shiftDateWindowByUnit(date, unit, amount) { + const shifted = new Date(date); + if (unit === "day") { + shifted.setDate(shifted.getDate() + amount); + return shifted; + } + if (unit === "week") { + shifted.setDate(shifted.getDate() + amount * 7); + return shifted; + } + if (unit === "month") { + shifted.setMonth(shifted.getMonth() + amount); + return shifted; + } + if (unit === "year") { + shifted.setFullYear(shifted.getFullYear() + amount); + return shifted; + } + return shifted; + } + _getRoundedDateWindowUnit(start, end) { + if (!(start instanceof Date) || !(end instanceof Date) || !(start < end)) return null; + for (const unit of [ + "day", + "week", + "month", + "year" + ]) { + const roundedStart = startOfUnit(start, unit); + const roundedEnd = endOfUnit(start, unit); + if (roundedStart?.getTime?.() === start.getTime() && roundedEnd?.getTime?.() === end.getTime()) return unit; + } + return null; + } + _syncDateWindowDialogInputs() { + const startVal = this._formatDateWindowInputValue(this._dateWindowDialogDraftRange?.start || null); + const endVal = this._formatDateWindowInputValue(this._dateWindowDialogDraftRange?.end || null); + if (this._dateWindowDialogComp) { + this._dateWindowDialogComp.startValue = startVal; + this._dateWindowDialogComp.endValue = endVal; + return; + } + if (this._dateWindowDialogStartEl) this._dateWindowDialogStartEl.value = startVal; + if (this._dateWindowDialogEndEl) this._dateWindowDialogEndEl.value = endVal; + } + _handleDateWindowDialogInputChange() { + const start = this._parseDateWindowInputValue(this._dateWindowDialogStartEl?.value || ""); + const end = this._parseDateWindowInputValue(this._dateWindowDialogEndEl?.value || ""); + if (start && end && start < end) { + this._dateWindowDialogDraftRange = { + start, + end + }; + return; + } + this._dateWindowDialogDraftRange = null; + } + _applyDateWindowShortcut(direction) { + if (this._editingDateWindowId) return; + const start = this._dateWindowDialogDraftRange?.start; + const end = this._dateWindowDialogDraftRange?.end; + if (!(start instanceof Date) || !(end instanceof Date) || !(start < end)) return; + const roundedUnit = this._getRoundedDateWindowUnit(start, end); + let nextStart; + let nextEnd; + if (roundedUnit) { + nextStart = startOfUnit(this._shiftDateWindowByUnit(start, roundedUnit, direction), roundedUnit); + nextEnd = endOfUnit(nextStart, roundedUnit); + } else { + const spanMs = end.getTime() - start.getTime(); + nextStart = new Date(start.getTime() + direction * spanMs); + nextEnd = new Date(end.getTime() + direction * spanMs); + } + this._dateWindowDialogDraftRange = { + start: nextStart, + end: nextEnd + }; + this._syncDateWindowDialogInputs(); + } + _ensureDateWindowDialog() { + if (this._dateWindowDialogComp || this._dateWindowDialogEl || !this.shadowRoot) return; + const dialog = document.createElement("ha-dialog"); + dialog.id = "date-window-dialog"; + dialog.setAttribute("hideActions", ""); + dialog.scrimClickAction = true; + dialog.escapeKeyAction = true; + dialog.open = false; + dialog.headerTitle = "Add date window"; + dialog.style.setProperty("--dialog-content-padding", `0 var(--dp-spacing-lg) var(--dp-spacing-lg)`); + dialog.innerHTML = `
A date window saves a named date range as a tab, so you can quickly preview it against the selected range or jump the chart back to it later. @@ -31817,2188 +31066,1540 @@ ${content.alert}` : "",
Cancel - Create date window -
-
-
- `; - dialog.addEventListener("closed", () => this._closeDateWindowDialog(true)); - this.shadowRoot.appendChild(dialog); - this._dateWindowDialogEl = dialog; - this._dateWindowDialogNameEl = dialog.querySelector("#date-window-name"); - this._dateWindowDialogStartEl = dialog.querySelector("#date-window-start"); - this._dateWindowDialogEndEl = dialog.querySelector("#date-window-end"); - this._dateWindowDialogShortcutsEl = dialog.querySelector( - "#date-window-shortcuts" - ); - if (this._hass && this._dateWindowDialogNameEl) { - this._dateWindowDialogNameEl.hass = this._hass; - } - dialog.querySelector("#date-window-cancel")?.addEventListener("click", () => this._closeDateWindowDialog()); - dialog.querySelector("#date-window-submit")?.addEventListener("click", () => this._createDateWindowFromDialog()); - dialog.querySelector("#date-window-delete")?.addEventListener("click", () => this._deleteEditingDateWindow()); - this._dateWindowDialogStartEl?.addEventListener( - "change", - () => this._handleDateWindowDialogInputChange() - ); - this._dateWindowDialogEndEl?.addEventListener( - "change", - () => this._handleDateWindowDialogInputChange() - ); - dialog.querySelector("#date-window-previous")?.addEventListener("click", () => this._applyDateWindowShortcut(-1)); - dialog.querySelector("#date-window-next")?.addEventListener("click", () => this._applyDateWindowShortcut(1)); - } - _openDateWindowDialog(targetWindow = null) { - this._ensureDateWindowDialog(); - this._dateWindowDialogOpen = true; - this._editingDateWindowId = targetWindow?.id || null; - const dialogStart = targetWindow ? parseDateValue(targetWindow.start_time) : this._startTime; - const dialogEnd = targetWindow ? parseDateValue(targetWindow.end_time) : this._endTime; - this._dateWindowDialogDraftRange = dialogStart && dialogEnd && dialogStart < dialogEnd ? { start: new Date(dialogStart), end: new Date(dialogEnd) } : null; - if (this._dateWindowDialogComp) { - this._dateWindowDialogComp.heading = targetWindow ? msg("Edit date window") : msg("Add date window"); - this._dateWindowDialogComp.submitLabel = targetWindow ? msg("Save date window") : msg("Create date window"); - this._dateWindowDialogComp.showDelete = !!targetWindow; - this._dateWindowDialogComp.showShortcuts = !targetWindow; - this._dateWindowDialogComp.name = targetWindow?.label || ""; - this._dateWindowDialogComp.startValue = this._formatDateWindowInputValue( - this._dateWindowDialogDraftRange?.start || null - ); - this._dateWindowDialogComp.endValue = this._formatDateWindowInputValue( - this._dateWindowDialogDraftRange?.end || null - ); - this._dateWindowDialogComp.rangeBounds = this._rangeBounds ?? null; - this._dateWindowDialogComp.zoomLevel = this._zoomLevel ?? "auto"; - this._dateWindowDialogComp.dateSnapping = this._dateSnapping ?? "hour"; - this._dateWindowDialogComp.open = true; - return; - } - if (this._dateWindowDialogEl) { - this._dateWindowDialogEl.open = true; - this._dateWindowDialogEl.headerTitle = targetWindow ? msg("Edit date window") : msg("Add date window"); - } - const submitButton = this._dateWindowDialogEl?.querySelector( - "#date-window-submit" - ); - if (submitButton) { - submitButton.textContent = targetWindow ? msg("Save date window") : msg("Create date window"); - } - const deleteButton = this._dateWindowDialogEl?.querySelector( - "#date-window-delete" - ); - if (deleteButton) { - deleteButton.hidden = !targetWindow; - deleteButton.style.display = targetWindow ? "" : "none"; - } - if (this._dateWindowDialogShortcutsEl) { - this._dateWindowDialogShortcutsEl.hidden = !!targetWindow; - } - if (this._dateWindowDialogNameEl) { - this._dateWindowDialogNameEl.value = targetWindow?.label || ""; - } - this._syncDateWindowDialogInputs(); - window.requestAnimationFrame(() => this._dateWindowDialogNameEl?.focus()); - } - _closeDateWindowDialog(fromClosedEvent = false) { - this._dateWindowDialogOpen = false; - this._editingDateWindowId = null; - this._dateWindowDialogDraftRange = null; - this._pendingAnomalyComparisonWindowEntityId = null; - if (!fromClosedEvent) { - if (this._dateWindowDialogComp) { - this._dateWindowDialogComp.open = false; - } else if (this._dateWindowDialogEl) { - this._dateWindowDialogEl.open = false; - } - } - } - _createDateWindowFromDialog(overrides = {}) { - const rawName = overrides.name != null ? overrides.name : this._dateWindowDialogNameEl?.value || ""; - const label = String(rawName).trim(); - const parsedStart = overrides.start ? this._parseDateWindowInputValue(String(overrides.start)) : null; - const parsedEnd = overrides.end ? this._parseDateWindowInputValue(String(overrides.end)) : null; - const start = parsedStart || this._dateWindowDialogDraftRange?.start || null; - const end = parsedEnd || this._dateWindowDialogDraftRange?.end || null; - if (!label || !start || !end || start >= end) { - return; - } - const existingIds = new Set( - this._comparisonWindows.map( - (window2) => window2.id - ) - ); - const nextWindow = { - id: this._editingDateWindowId || makeDateWindowId(label, existingIds), - label, - start_time: start.toISOString(), - end_time: end.toISOString() - }; - this._comparisonWindows = normalizeDateWindows( - this._editingDateWindowId ? this._comparisonWindows.map( - (window2) => window2.id === this._editingDateWindowId ? nextWindow : window2 - ) : [...this._comparisonWindows, nextWindow] - ); - this._saveUserPreferences(); - this._saveSessionState(); - this._updateUrl({ push: false }); - const pendingEntityId = this._pendingAnomalyComparisonWindowEntityId; - const wasCreatingNew = !this._editingDateWindowId; - this._pendingAnomalyComparisonWindowEntityId = null; - this._closeDateWindowDialog(); - if (pendingEntityId && wasCreatingNew) { - this._setSeriesAnalysisOption( - pendingEntityId, - "anomaly_comparison_window_id", - nextWindow.id - ); - } - this._renderContent(); - } - async _deleteDateWindow(id) { - if (!id) { - return; - } - const windowToDelete = this._comparisonWindows.find( - (window2) => window2.id === id - ); - const confirmed = await confirmDestructiveAction(this, { - title: msg("Delete date window"), - message: `${msg("Delete")} "${windowToDelete?.label || msg("this date window")}"?`, - confirmLabel: msg("Delete date window") - }); - if (!confirmed) { - return false; - } - const nextWindows = this._comparisonWindows.filter( - (window2) => window2.id !== id - ); - if (nextWindows.length === this._comparisonWindows.length) { - return false; - } - if (this._hoveredComparisonWindowId === id) { - this._hoveredComparisonWindowId = null; - } - if (this._selectedComparisonWindowId === id) { - this._selectedComparisonWindowId = null; - this._clearDeltaAnalysisSelectionState(); - } - if (this._hoveredComparisonWindowId == null) { - this._updateComparisonRangePreview(); - } - this._comparisonWindows = nextWindows; - this._saveUserPreferences(); - this._saveSessionState(); - this._updateUrl({ push: false }); - this._renderContent(); - return true; - } - async _deleteEditingDateWindow() { - const id = this._editingDateWindowId; - if (!id) { - return; - } - const deleted = await this._deleteDateWindow(id); - if (deleted) { - this._closeDateWindowDialog(); - } - } - _handleComparisonTabHover(id) { - this._context.orchestration.handleComparisonTabHover({ - id, - hoveredComparisonWindowId: this._hoveredComparisonWindowId, - setHoveredComparisonWindowId: (value) => { - this._hoveredComparisonWindowId = value; - }, - updateComparisonRangePreview: () => { - this._updateComparisonRangePreview(); - }, - updateChartHoverIndicator: () => { - this._updateChartHoverIndicator(); - }, - renderContent: () => { - this._renderContent(); - } - }); - } - _handleComparisonTabLeave(id) { - this._context.orchestration.handleComparisonTabLeave({ - id, - hoveredComparisonWindowId: this._hoveredComparisonWindowId, - setHoveredComparisonWindowId: (value) => { - this._hoveredComparisonWindowId = value; - }, - updateComparisonRangePreview: () => { - this._updateComparisonRangePreview(); - }, - updateChartHoverIndicator: () => { - this._updateChartHoverIndicator(); - }, - renderContent: () => { - this._renderContent(); - } - }); - } - _handleComparisonLoading(ev) { - const ids = Array.isArray(ev?.detail?.ids) ? ev.detail.ids.filter(Boolean) : []; - const loading2 = ev?.detail?.loading === true; - this._loadingComparisonWindowIds = loading2 ? [.../* @__PURE__ */ new Set([...this._loadingComparisonWindowIds, ...ids])] : this._loadingComparisonWindowIds.filter( - (id) => !ids.includes(id) - ); - this._renderComparisonTabs(); - } - _handleAnalysisComputing(ev) { - const computing = ev?.detail?.computing === true; - const entityIds = Array.isArray(ev?.detail?.entityIds) ? ev.detail.entityIds : []; - const computingProgress = computing ? 0 : 100; - const progress = typeof ev?.detail?.progress === "number" ? ev.detail.progress : computingProgress; - if (computing) { - for (const id of entityIds) { - this._computingEntityIds.add(id); - } - if (progress === 0) { - for (const id of entityIds) { - const row = this._seriesRows?.find((r2) => r2.entity_id === id); - const methods = row?.analysis?.show_anomalies === true && Array.isArray(row.analysis.anomaly_methods) ? row.analysis.anomaly_methods : []; - this._computingMethods.set(id, new Set(methods)); - logger$1.log( - `[datapoints] analysis started for ${id} — methods: [${methods.join(", ")}]` - ); - } - } else { - logger$1.log(`[datapoints] analysis progress ${progress}%`); - } - } else { - for (const id of entityIds) { - this._computingEntityIds.delete(id); - this._computingMethods.delete(id); - } - logger$1.log(`[datapoints] analysis complete (${entityIds.join(", ")})`); - } - this._analysisProgress = progress; - this._pushComputingStateToRowList(); - } - _handleAnalysisMethodResult(ev) { - logger$1.log("[datapoints] _handleAnalysisMethodResult received", ev?.detail); - const entityId = ev?.detail?.entityId; - const method = ev?.detail?.method; - if (!entityId || !method) { - logger$1.log( - "[datapoints] _handleAnalysisMethodResult: missing entityId or method, ignoring" - ); - return; - } - const current = this._computingMethods.get(entityId); - if (current) { - const next = new Set(current); - next.delete(method); - this._computingMethods.set(entityId, next); - } - const remaining = [...this._computingMethods.get(entityId) ?? []]; - logger$1.log( - `[datapoints] method done: ${method} for ${entityId} — remaining: [${remaining.join(", ") || "none"}]` - ); - this._pushComputingStateToRowList(); - } - _pushComputingStateToRowList() { - if (this._rowListEl) { - this._rowListEl.computingEntityIds = new Set(this._computingEntityIds); - this._rowListEl.analysisProgress = this._analysisProgress; - this._rowListEl.computingMethodsByEntity = new Map( - this._computingMethods - ); - } - } - _clearDeltaAnalysisSelectionState() { - } - _handleComparisonTabActivate(id) { - this._context.orchestration.handleComparisonTabActivate({ - id, - comparisonWindows: this._comparisonWindows, - selectedComparisonWindowId: this._selectedComparisonWindowId, - setSelectedComparisonWindowId: (value) => { - this._selectedComparisonWindowId = value; - }, - setHoveredComparisonWindowId: (value) => { - this._hoveredComparisonWindowId = value; - }, - clearDeltaAnalysisSelectionState: () => { - this._clearDeltaAnalysisSelectionState(); - }, - updateComparisonRangePreview: () => { - this._updateComparisonRangePreview(); - }, - updateChartHoverIndicator: () => { - this._updateChartHoverIndicator(); - }, - renderComparisonTabs: () => { - this._renderComparisonTabs(); - }, - renderContent: () => { - this._renderContent(); - }, - setAdjustComparisonAxisScale: (value) => { - if (this._chartEl) { - this._chartEl._adjustComparisonAxisScale = value; - } - } - }); - } - _syncSidebarUi() { - if (this._shellEl) { - this._shellEl.sidebarCollapsed = this._sidebarCollapsed; - } - if (this._historyTargetsComp) { - this._historyTargetsComp.sidebarCollapsed = this._sidebarCollapsed; - } - if (this._rangeToolbarComp) { - this._rangeToolbarComp.sidebarCollapsed = this._sidebarCollapsed; - } - } - _updateLayoutMode() { - const prev = this._layoutMode; - if (this._mqMobile.matches) { - this._layoutMode = "mobile"; - } else if (this._mqTablet.matches) { - this._layoutMode = "tablet"; - } else { - this._layoutMode = "desktop"; - } - if (prev !== this._layoutMode) { - if (this._shellEl) { - this._shellEl.layoutMode = this._layoutMode; - } - this._syncSidebarUi(); - this._syncMobileDateInputs(); - } - } - _syncMobileDateInputs() { - this._rangeToolbarComp?.syncMobileDates(this._startTime, this._endTime); - } - _applyContentSplitLayout() { - const content = this._contentHostEl; - if (!content) { - return; - } - const resizablePanes = content.querySelector( - "#content-resizable-panes" - ); - if (resizablePanes) { - resizablePanes.ratio = this._contentSplitRatio; - } else { - content.style.setProperty( - "--content-top-size", - `${Math.round(this._contentSplitRatio * 1e3) / 10}%` - ); - } - this._updateComparisonTabsOverflow(); - } - _requestChartResizeRedraw() { - this._context.orchestration.requestChartResizeRedraw(this._chartEl); - } - _toggleSidebarCollapsed() { - this._sidebarCollapsed = !this._sidebarCollapsed; - if (!this._sidebarCollapsed) { - this._hideCollapsedTargetPopup(); - } - this._saveSessionState(); - this._updateUrl({ push: false }); - this._syncSidebarUi(); - window.requestAnimationFrame(() => { - if (!this.isConnected) { - return; - } - this._syncRangeControl(); - }); - } - _handleCollapsedSidebarClick() { - if (!this._sidebarCollapsed) ; - } - _openTargetPicker(anchorEl = null) { - this._context.orchestration.openTargetPicker(this._targetControl, anchorEl); - } - _setDatapointScope(scope) { - let nextScope; - if (scope === "all") { - nextScope = "all"; - } else if (scope === "hidden") { - nextScope = "hidden"; - } else { - nextScope = "linked"; - } - if (nextScope === this._datapointScope) { - return; - } - this._datapointScope = nextScope; - this._timelineEvents = []; - this._timelineEventsKey = ""; - this._saveSessionState(); - this._renderSidebarOptions(); - this._updateUrl({ push: false }); - this._ensureTimelineEvents(); - this._renderContent(); - } - _setChartYAxisMode(mode) { - const nextDelink = mode === "unique"; - const nextSplit = mode === "split"; - if (this._delinkChartYAxis === nextDelink && this._splitChartView === nextSplit) { - return; - } - this._delinkChartYAxis = nextDelink; - this._splitChartView = nextSplit; - this._saveSessionState(); - this._updateUrl({ push: false }); - this._renderSidebarOptions(); - this._renderContent(); - } - _setChartDatapointDisplayOption(kind, enabled) { - const normalized = !!enabled; - if (kind === "icons") { - if (this._showChartDatapointIcons === normalized) { - return; - } - this._showChartDatapointIcons = normalized; - } else if (kind === "lines") { - if (this._showChartDatapointLines === normalized) { - return; - } - this._showChartDatapointLines = normalized; - } else if (kind === "tooltips") { - if (this._showChartTooltips === normalized) { - return; - } - this._showChartTooltips = normalized; - } else if (kind === "hover_guides") { - if (this._showChartEmphasizedHoverGuides === normalized) { - return; - } - this._showChartEmphasizedHoverGuides = normalized; - } else if (kind === "hover_snap_mode") { - const value = String(enabled) === "snap_to_data_points" ? "snap_to_data_points" : "follow_series"; - if (this._chartHoverSnapMode === value) { - return; - } - this._chartHoverSnapMode = value; - } else if (kind === "correlated_anomalies") { - if (this._showCorrelatedAnomalies === normalized) { - return; - } - this._showCorrelatedAnomalies = normalized; - } else if (kind === "delink_y_axis") { - if (this._delinkChartYAxis === normalized) { - return; - } - this._delinkChartYAxis = normalized; - if (normalized) { - this._splitChartView = false; - } - } else if (kind === "split_chart_view") { - if (this._splitChartView === normalized) { - return; - } - this._splitChartView = normalized; - if (normalized) { - this._delinkChartYAxis = false; - } - } else if (kind === "data_gaps") { - if (this._showDataGaps === normalized) { - return; - } - this._showDataGaps = normalized; - } else if (kind === "data_gap_threshold") { - const value = String(enabled); - if (this._dataGapThreshold === value) { - return; - } - this._dataGapThreshold = value; - } else { - return; - } - this._saveSessionState(); - this._updateUrl({ push: false }); - this._renderSidebarOptions(); - this._renderContent(); - } - async _ensureHistoryBounds() { - await this._context.fetch.ensureHistoryBounds({ - onSuccess: ({ start, end }) => { - this._historyStartTime = parseDateValue( - start - ); - this._historyEndTime = parseDateValue( - end - ); - if (this._zoomLevel === "auto") { - this._resolvedAutoZoomLevel = null; - } - if (this._rendered) { - this._syncControls(); - } - }, - onError: (err) => { - logger$1.warn("[hass-datapoints] failed to load event bounds:", err); - } - }); - } - async _ensureTimelineEvents() { - if (!this._hass || !this._rangeBounds) { - return; - } - if (this._datapointScope === "hidden" || this._datapointScope === "linked" && this._entities.length === 0) { - this._timelineEvents = []; - this._context.fetch.resetTimelineEvents(); - if (this._rendered && this._rangeToolbarComp) { - this._rangeToolbarComp.timelineEvents = []; - } - return; - } - const startIso = new Date(this._rangeBounds.min).toISOString(); - const endIso = new Date(this._rangeBounds.max).toISOString(); - await this._context.fetch.loadTimelineEvents({ - startIso, - endIso, - datapointScope: this._datapointScope, - entityIds: this._entities, - onSuccess: (events, key) => { - this._timelineEvents = events; - this._timelineEventsKey = key; - if (this._rendered && this._rangeToolbarComp) { - this._rangeToolbarComp.timelineEvents = this._timelineEvents; - } - }, - onError: (err) => { - logger$1.warn("[hass-datapoints] failed to load timeline events:", err); - } - }); - } - async _ensureUserPreferences() { - await this._context.fetch.ensureUserPreferences({ - preferencesKey: PANEL_HISTORY_PREFERENCES_KEY, - fallbackValue: null, - onSuccess: (preferences) => { - const normalized = normalizeHistoryPagePreferences( - preferences, - { - zoomOptions: RANGE_ZOOM_OPTIONS, - snapOptions: RANGE_SNAP_OPTIONS - } - ); - this._zoomLevel = normalized.zoomLevel; - this._dateSnapping = normalized.dateSnapping; - this._resolvedAutoZoomLevel = normalized.zoomLevel === "auto" ? null : this._resolvedAutoZoomLevel; - this._preferredSeriesColors = normalized.preferredSeriesColors; - this._comparisonWindows = this._comparisonWindows.length ? this._comparisonWindows : normalized.comparisonWindows; - this._seriesRows = this._applyPreferredSeriesColors(this._seriesRows); - if (!this._localPageStateDirty) { - this._applyPreferencePageState(normalized.pageState); - } - if (normalized.shouldPersistDefaults) { - this._saveUserPreferences(); - } - if (this._rendered) { - this._renderTargetRows(); - this._syncControls(); - this._updateUrl({ push: false }); - this._renderContent(); - } - }, - onError: (err) => { - logger$1.warn("[hass-datapoints] failed to load panel preferences:", err); - } - }); - } - _saveUserPreferences() { - if (!this._hass) { - return; - } - const payload = buildHistoryPagePreferencesPayload( - this - ); - this._preferredSeriesColors = payload.series_colors ?? {}; - this._context.persistence.saveUserPreferences({ - preferencesKey: PANEL_HISTORY_PREFERENCES_KEY, - payload - }); - } - _mountControls() { - if (!this._shellEl) { - return; - } - const histTargets = this._mountHistoryTargetsControl(); - this._mountTargetPickerControl(histTargets); - this._mountRangeToolbarControl(); - this._mountSidebarOptionsControl(); - this._mountDateWindowDialogControl(); - this._syncControls(); - } - _mountHistoryTargetsControl() { - const histTargets = document.createElement( - "history-targets" - ); - histTargets.slot = "sidebar"; - histTargets.rows = []; - histTargets.states = {}; - histTargets.hass = this._hass ?? null; - histTargets.comparisonWindows = this._comparisonWindows; - histTargets.canShowDeltaAnalysis = false; - histTargets.sidebarCollapsed = this._sidebarCollapsed; - histTargets.addEventListener( - "dp-row-color-change", - (ev) => { - const { index, color } = ev.detail || {}; - this._updateSeriesRowColor(index, color); - } - ); - histTargets.addEventListener( - "dp-row-visibility-change", - (ev) => { - const { entityId, visible } = ev.detail || {}; - this._updateSeriesRowVisibilityByEntityId(entityId, visible); - } - ); - histTargets.addEventListener( - "dp-row-remove", - (ev) => { - const { index } = ev.detail || {}; - this._removeSeriesRow(index); - } - ); - histTargets.addEventListener( - "dp-row-toggle-analysis", - (ev) => { - const { entityId } = ev.detail || {}; - this._toggleSeriesAnalysisExpanded(entityId); - } - ); - histTargets.addEventListener( - "dp-row-analysis-change", - (ev) => { - const { entityId, key, value } = ev.detail || {}; - this._setSeriesAnalysisOption(entityId, key, value); - } - ); - histTargets.addEventListener( - "dp-row-copy-analysis-to-all", - (ev) => { - const { entityId, analysis } = ev.detail || {}; - this._copyAnalysisToAll(entityId, analysis); - } - ); - histTargets.addEventListener( - "dp-rows-reorder", - (ev) => { - const { rows } = ev.detail || {}; - if (!Array.isArray(rows)) { - return; - } - this._seriesRows = rows; - this._syncSeriesState(); - this._saveSessionState(); - this._renderTargetRows(); - this._syncControls(); - this._updateUrl({ push: true }); - this._renderContent(); - } - ); - histTargets.addEventListener("dp-targets-prefs-click", (ev) => { - ev.stopPropagation(); - const anchor = ev.composedPath()[0] || ev.target; - if (!(anchor instanceof HTMLElement)) { - return; - } - if (this._collapsedOptionsPopupOpen) { - this._hideCollapsedOptionsPopup(); - } else { - this._showCollapsedOptionsPopup(anchor); - } - }); - histTargets.addEventListener( - "dp-targets-add-click", - (ev) => { - const { buttonEl } = ev.detail || {}; - this._openTargetPicker(buttonEl ?? void 0); - } - ); - histTargets.addEventListener( - "dp-collapsed-entity-click", - (ev) => { - const { entityId, buttonEl } = ev.detail || {}; - if (!entityId) { - return; - } - if (this._collapsedPopupEntityId === entityId) { - this._hideCollapsedTargetPopup(); - } else { - this._showCollapsedTargetPopup(entityId, buttonEl ?? void 0); - } - } - ); - this._shellEl.appendChild(histTargets); - this._historyTargetsComp = histTargets; - this._rowListEl = null; - return histTargets; - } - _mountTargetPickerControl(histTargets) { - const targetControl = document.createElement( - "ha-target-picker" - ); - targetControl.slot = "picker"; - targetControl.style.display = "block"; - targetControl.style.width = "100%"; - if (this._hass) { - targetControl.hass = this._hass; - } - targetControl.addEventListener( - "value-changed", - (ev) => { - const hasValue = ev.detail && Object.prototype.hasOwnProperty.call(ev.detail, "value"); - if (!hasValue) { - return; - } - const rawValue = normalizeTargetValue( - ev.detail?.value ?? null - ); - const nextEntityIds = resolveEntityIdsFromTarget(this._hass, rawValue); - if (!nextEntityIds.length) { - return; - } - this._addSeriesRows(nextEntityIds); - targetControl.value = {}; - this._saveSessionState(); - this._syncControls(); - this._updateUrl({ push: true }); - this._renderContent(); - } - ); - histTargets.appendChild(targetControl); - this._targetControl = targetControl; - ensureHaComponents(["ha-target-picker"]).then(() => { - if (!this.isConnected || this._targetControl !== targetControl) { - return; - } - if (this._hass) { - targetControl.hass = this._hass; - } - targetControl.value = {}; - }); - } - _mountRangeToolbarControl() { - const rangeToolbar = document.createElement( - "range-toolbar" - ); - rangeToolbar.slot = "controls"; - rangeToolbar.startTime = this._startTime; - rangeToolbar.endTime = this._endTime; - rangeToolbar.rangeBounds = this._rangeBounds; - rangeToolbar.zoomLevel = this._zoomLevel; - rangeToolbar.dateSnapping = this._dateSnapping; - rangeToolbar.sidebarCollapsed = this._sidebarCollapsed; - rangeToolbar.hass = this._hass ?? null; - rangeToolbar.isLiveEdge = this._isOnLiveEdge(); - rangeToolbar.timelineEvents = this._timelineEvents || []; - rangeToolbar.comparisonPreview = null; - rangeToolbar.zoomRange = this._chartZoomCommittedRange ? { - start: +this._chartZoomCommittedRange.start, - end: +this._chartZoomCommittedRange.end - } : null; - rangeToolbar.zoomWindowRange = null; - rangeToolbar.chartHoverTimeMs = null; - rangeToolbar.chartHoverWindowTimeMs = null; - rangeToolbar.addEventListener( - "dp-range-commit", - (ev) => { - this._applyCommittedRange(ev.detail?.start, ev.detail?.end, { - push: ev.detail?.push ?? false - }); - } - ); - rangeToolbar.addEventListener( - "dp-range-draft", - (ev) => { - this._scheduleAutoZoomUpdate(ev.detail?.start, ev.detail?.end); - } - ); - rangeToolbar.addEventListener( - "dp-toolbar-sidebar-toggle", - () => this._toggleSidebarCollapsed() - ); - rangeToolbar.addEventListener( - "dp-zoom-level-change", - (ev) => { - const { value } = ev.detail || {}; - if (value && value !== this._zoomLevel) { - this._zoomLevel = value; - this._clearAutoZoomTimer(); - this._resolvedAutoZoomLevel = value === "auto" ? null : this._resolvedAutoZoomLevel; - this._saveSessionState(); - this._updateUrl({ push: false }); - this._syncRangeControl(); - this._saveUserPreferences(); - } - } - ); - rangeToolbar.addEventListener( - "dp-snap-change", - (ev) => { - const { value } = ev.detail || {}; - if (value && value !== this._dateSnapping) { - this._dateSnapping = value; - this._saveSessionState(); - this._updateUrl({ push: false }); - this._syncRangeControl(); - this._saveUserPreferences(); - } - } - ); - rangeToolbar.addEventListener("dp-date-picker-change", (ev) => { - this._handleDatePickerChange(ev); - }); - this._shellEl.appendChild(rangeToolbar); - this._rangeToolbarComp = rangeToolbar; - this._dateControl = rangeToolbar; - rangeToolbar.updateComplete.then(() => { - if (!this.isConnected || this._rangeToolbarComp !== rangeToolbar) { - return; - } - this._syncControls(); - this._renderContent(); - }); - this._syncSidebarUi(); - } - _mountSidebarOptionsControl() { - if (this._sidebarOptionsEl) { - const sidebarComp = document.createElement( - "sidebar-options" - ); - sidebarComp.addEventListener( - "dp-scope-change", - (ev) => { - const { value } = ev.detail || {}; - if (value) { - this._setDatapointScope(value); - } - } - ); - sidebarComp.addEventListener( - "dp-display-change", - (ev) => { - const { kind, value } = ev.detail || {}; - if (!kind) { - return; - } - if (kind === "y_axis_mode") { - this._setChartYAxisMode(String(value || "")); - } else { - this._setChartDatapointDisplayOption(kind, value); - } - } - ); - sidebarComp.addEventListener( - "dp-analysis-change", - (ev) => { - const { kind, value } = ev.detail || {}; - if (kind === "anomaly_overlap_mode" && ANALYSIS_ANOMALY_OVERLAP_MODE_OPTIONS.some((o2) => o2.value === value)) { - if (this._chartAnomalyOverlapMode === value) { - return; - } - this._chartAnomalyOverlapMode = value; - this._saveSessionState(); - this._updateUrl({ push: false }); - this._renderSidebarOptions(); - this._renderContent(); - } - } - ); - sidebarComp.addEventListener( - "dp-accordion-change", - (ev) => { - const { targetsOpen, datapointsOpen, analysisOpen, chartOpen } = ev.detail || {}; - if (typeof targetsOpen === "boolean") { - this._sidebarAccordionTargetsOpen = targetsOpen; - } - if (typeof datapointsOpen === "boolean") { - this._sidebarAccordionDatapointsOpen = datapointsOpen; - } - if (typeof analysisOpen === "boolean") { - this._sidebarAccordionAnalysisOpen = analysisOpen; - } - if (typeof chartOpen === "boolean") { - this._sidebarAccordionChartOpen = chartOpen; - } - this._saveSessionState(); - this._updateUrl({ push: false }); - } - ); - this._sidebarOptionsEl.appendChild(sidebarComp); - this._sidebarOptionsComp = sidebarComp; - } - } - _mountDateWindowDialogControl() { - if (this.shadowRoot) { - const dialogComp = document.createElement( - "date-window-dialog" - ); - dialogComp.addEventListener( - "dp-window-close", - () => this._closeDateWindowDialog() - ); - dialogComp.addEventListener( - "dp-window-submit", - (ev) => { - this._createDateWindowFromDialog(ev.detail || {}); - } - ); - dialogComp.addEventListener( - "dp-window-delete", - () => this._deleteEditingDateWindow() - ); - dialogComp.addEventListener( - "dp-window-shortcut", - (ev) => { - if (typeof ev.detail?.direction === "number") { - this._applyDateWindowShortcut(ev.detail.direction); - } - } - ); - dialogComp.addEventListener( - "dp-window-date-change", - (ev) => { - const start = this._parseDateWindowInputValue(ev.detail?.start || ""); - const end = this._parseDateWindowInputValue(ev.detail?.end || ""); - if (start && end && start < end) { - this._dateWindowDialogDraftRange = { start, end }; - } else { - this._dateWindowDialogDraftRange = null; - } - } - ); - this.shadowRoot.appendChild(dialogComp); - this._dateWindowDialogComp = dialogComp; - } - } - _renderTargetRows() { - if (!this._historyTargetsComp) { - return; - } - this._historyTargetsComp.rows = this._seriesRows; - this._historyTargetsComp.states = this._hass?.states ?? {}; - this._historyTargetsComp.hass = this._hass ?? null; - this._historyTargetsComp.canShowDeltaAnalysis = !!this._selectedComparisonWindowId; - this._historyTargetsComp.comparisonWindows = this._comparisonWindows; - if (!this._rowListEl) { - this._rowListEl = this._historyTargetsComp.getRowListEl(); - } else { - this._rowListEl.rows = this._seriesRows; - this._rowListEl.states = this._hass?.states ?? {}; - this._rowListEl.hass = this._hass ?? null; - this._rowListEl.canShowDeltaAnalysis = !!this._selectedComparisonWindowId; - this._rowListEl.comparisonWindows = this._comparisonWindows; - } - this._refreshCollapsedTargetPopup(); - } - _addSeriesRows(entityIds) { - const merged2 = new Map( - this._seriesRows.map((row) => [row.entity_id, row]) - ); - normalizeEntityIds(entityIds).forEach((entityId, index) => { - if (merged2.has(entityId)) { - return; - } - merged2.set(entityId, { - entity_id: entityId, - color: this._preferredSeriesColors?.[entityId] && /^#[0-9a-f]{6}$/i.test(this._preferredSeriesColors[entityId]) ? this._preferredSeriesColors[entityId] : COLORS[(merged2.size + index) % COLORS.length], - visible: true, - analysis: normalizeHistorySeriesAnalysis(null) - }); - }); - this._seriesRows = [...merged2.values()]; - this._syncSeriesState(); - this._renderTargetRows(); - } - _updateSeriesRowColor(index, color) { - if (!Number.isInteger(index) || index === void 0 || index < 0 || index >= this._seriesRows.length) { - return; - } - if (!/^#[0-9a-f]{6}$/i.test(color || "")) { - return; - } - if (this._seriesRows[index].color === color) { - return; - } - this._seriesRows[index] = { - ...this._seriesRows[index], - color - }; - this._saveUserPreferences(); - this._saveSessionState(); - this._updateUrl({ push: false }); - this._renderTargetRows(); - this._renderContent(); - } - _updateSeriesRowVisibility(index, visible) { - if (!Number.isInteger(index) || index === void 0 || index < 0 || index >= this._seriesRows.length) { - return; - } - if (this._seriesRows[index].visible === !!visible) { - return; - } - this._seriesRows[index] = { - ...this._seriesRows[index], - visible: !!visible - }; - this._saveSessionState(); - this._updateUrl({ push: false }); - this._renderTargetRows(); - this._renderContent(); - } - /** Open (or re-render) the collapsed-sidebar target popup for *entityId*, - * anchored to *anchorEl*. Wires all the same controls as the full sidebar row. */ - _showCollapsedTargetPopup(entityId, anchorEl) { - const popup = this._shellEl?.getTargetPopupEl(); - if (!popup) { - return; - } - const index = this._seriesRows.findIndex( - (r2) => r2.entity_id === entityId - ); - if (index < 0) { - this._hideCollapsedTargetPopup(); - return; - } - const row = this._seriesRows[index]; - this._collapsedPopupEntityId = entityId; - this._collapsedPopupAnchorEl = anchorEl ?? null; - popup.innerHTML = ""; - const targetRow = document.createElement("target-row"); - targetRow.hideDragHandle = true; - targetRow.color = row.color; - targetRow.visible = row.visible !== false; - targetRow.analysis = row.analysis || {}; - targetRow.index = index; - targetRow.entityId = row.entity_id; - targetRow.stateObj = this._hass?.states?.[row.entity_id] ?? null; - targetRow.hass = this._hass ?? null; - targetRow.canShowDeltaAnalysis = !!this._selectedComparisonWindowId; - targetRow.comparisonWindows = this._comparisonWindows || []; - targetRow.addEventListener( - "dp-row-color-change", - (ev) => { - this._updateSeriesRowColor(ev.detail?.index, ev.detail?.color); - } - ); - targetRow.addEventListener( - "dp-row-visibility-change", - (ev) => { - this._updateSeriesRowVisibilityByEntityId( - ev.detail?.entityId, - ev.detail?.visible - ); - } - ); - targetRow.addEventListener( - "dp-row-toggle-analysis", - (ev) => { - this._toggleSeriesAnalysisExpanded(ev.detail?.entityId); - } - ); - targetRow.addEventListener( - "dp-row-analysis-change", - (ev) => { - this._setSeriesAnalysisOption( - ev.detail?.entityId, - ev.detail?.key, - ev.detail?.value - ); - } - ); - targetRow.addEventListener( - "dp-row-copy-analysis-to-all", - (ev) => { - const { entityId: id, analysis } = ev.detail || {}; - this._copyAnalysisToAll(id, analysis); - } - ); - targetRow.addEventListener( - "dp-row-remove", - (ev) => { - this._hideCollapsedTargetPopup(); - this._removeSeriesRow(ev.detail?.index); - } - ); - popup.appendChild(targetRow); - popup.removeAttribute("hidden"); - if (!anchorEl) { - return; - } - const anchorRect = anchorEl.getBoundingClientRect(); - const popupHeight = popup.offsetHeight; - const top = Math.max( - 8, - Math.min(anchorRect.top, window.innerHeight - popupHeight - 16) - ); - popup.style.top = `${top}px`; - popup.style.left = `${anchorRect.right + 8}px`; - popup.style.maxHeight = `${window.innerHeight - top - 16}px`; - if (this._collapsedPopupOutsideClickHandler) { - document.removeEventListener( - "click", - this._collapsedPopupOutsideClickHandler, - true - ); - } - this._collapsedPopupOutsideClickHandler = (ev) => { - const path = ev.composedPath(); - if (!path.includes(popup) && !path.includes(anchorEl)) { - this._hideCollapsedTargetPopup(); - } - }; - document.addEventListener( - "click", - this._collapsedPopupOutsideClickHandler, - true - ); - if (this._collapsedPopupKeyHandler) { - document.removeEventListener("keydown", this._collapsedPopupKeyHandler); - } - this._collapsedPopupKeyHandler = (ev) => { - if (ev.key === "Escape") { - this._hideCollapsedTargetPopup(); - anchorEl.focus(); - } - }; - document.addEventListener("keydown", this._collapsedPopupKeyHandler); - } - /** Close the collapsed-sidebar target popup and clean up all listeners. */ - _hideCollapsedTargetPopup() { - const popup = this._shellEl?.getTargetPopupEl(); - if (popup) { - popup.setAttribute("hidden", ""); - popup.innerHTML = ""; - } - if (this._collapsedPopupOutsideClickHandler) { - document.removeEventListener( - "click", - this._collapsedPopupOutsideClickHandler, - true - ); - this._collapsedPopupOutsideClickHandler = null; - } - if (this._collapsedPopupKeyHandler) { - document.removeEventListener("keydown", this._collapsedPopupKeyHandler); - this._collapsedPopupKeyHandler = null; - } - this._collapsedPopupEntityId = null; - this._collapsedPopupAnchorEl = null; - } - /** Re-render the popup in-place after a state change (e.g. analysis toggle). - * Called at the end of _renderTargetRows so the popup stays in sync. */ - _refreshCollapsedTargetPopup() { - if (!this._collapsedPopupEntityId) { - return; - } - const row = this._seriesRows.find( - (r2) => r2.entity_id === this._collapsedPopupEntityId - ); - if (!row) { - this._hideCollapsedTargetPopup(); - return; - } - const popup = this._shellEl?.getTargetPopupEl(); - const targetRow = popup?.querySelector( - "target-row" - ); - if (targetRow) { - targetRow.analysis = row.analysis; - targetRow.color = row.color; - targetRow.visible = row.visible !== false; - targetRow.stateObj = this._hass?.states?.[row.entity_id] ?? null; - targetRow.hass = this._hass ?? null; - } - } - // ── Collapsed-sidebar options popup ────────────────────────────────────── - /** Open the collapsed-sidebar options popup, anchored to *anchorEl*. */ - _showCollapsedOptionsPopup(anchorEl) { - const popup = this._shellEl?.getOptionsPopupEl(); - if (!popup) { - return; - } - let menu = popup.querySelector( - "collapsed-options-menu" - ); - if (!menu) { - menu = document.createElement( - "collapsed-options-menu" - ); - menu.addEventListener( - "dp-scope-change", - (ev) => { - const { value } = ev.detail || {}; - if (value) { - this._setDatapointScope(value); - } - } - ); - menu.addEventListener( - "dp-display-change", - (ev) => { - const { kind, value } = ev.detail || {}; - if (!kind) { - return; - } - if (kind === "y_axis_mode") { - this._setChartYAxisMode(String(value || "")); - } else { - this._setChartDatapointDisplayOption(kind, value); - } - } - ); - menu.addEventListener( - "dp-analysis-change", - (ev) => { - const { kind, value } = ev.detail || {}; - if (kind === "anomaly_overlap_mode" && value !== this._chartAnomalyOverlapMode) { - this._chartAnomalyOverlapMode = value; - this._saveSessionState(); - this._updateUrl({ push: false }); - this._renderSidebarOptions(); - this._renderContent(); - } - } - ); - popup.appendChild(menu); - } - this._syncCollapsedOptionsMenu(menu); - this._collapsedOptionsPopupOpen = true; - this._collapsedOptionsAnchorEl = anchorEl; - popup.removeAttribute("hidden"); - const anchorRect = anchorEl.getBoundingClientRect(); - const popupHeight = popup.offsetHeight; - const top = Math.max( - 8, - Math.min(anchorRect.top, window.innerHeight - popupHeight - 16) - ); - popup.style.top = `${top}px`; - popup.style.left = `${anchorRect.right + 8}px`; - if (this._collapsedOptionsOutsideClickHandler) { - document.removeEventListener( - "click", - this._collapsedOptionsOutsideClickHandler, - true - ); - } - this._collapsedOptionsOutsideClickHandler = (ev) => { - const path = ev.composedPath(); - if (!path.includes(popup) && !path.includes(anchorEl)) { - this._hideCollapsedOptionsPopup(); - } - }; - document.addEventListener( - "click", - this._collapsedOptionsOutsideClickHandler, - true - ); - if (this._collapsedOptionsKeyHandler) { - document.removeEventListener("keydown", this._collapsedOptionsKeyHandler); - } - this._collapsedOptionsKeyHandler = (ev) => { - if (ev.key === "Escape") { - this._hideCollapsedOptionsPopup(); - anchorEl.focus(); - } - }; - document.addEventListener("keydown", this._collapsedOptionsKeyHandler); - } - /** Close the collapsed-sidebar options popup and clean up all listeners. */ - _hideCollapsedOptionsPopup() { - const popup = this._shellEl?.getOptionsPopupEl(); - if (popup) { - popup.setAttribute("hidden", ""); - } - if (this._collapsedOptionsOutsideClickHandler) { - document.removeEventListener( - "click", - this._collapsedOptionsOutsideClickHandler, - true - ); - this._collapsedOptionsOutsideClickHandler = null; - } - if (this._collapsedOptionsKeyHandler) { - document.removeEventListener("keydown", this._collapsedOptionsKeyHandler); - this._collapsedOptionsKeyHandler = null; - } - this._collapsedOptionsPopupOpen = false; - this._collapsedOptionsAnchorEl = null; - } - /** Sync option props on the menu element — called from _renderSidebarOptions. */ - _refreshCollapsedOptionsPopup() { - if (!this._collapsedOptionsPopupOpen) { - return; - } - const popup = this._shellEl?.getOptionsPopupEl(); - const menu = popup?.querySelector( - "collapsed-options-menu" - ); - if (menu) { - this._syncCollapsedOptionsMenu(menu); - } - } - /** Write all current option values onto a collapsed-options-menu element. */ - _syncCollapsedOptionsMenu(menu) { - let yAxisMode; - if (this._splitChartView) { - yAxisMode = "split"; - } else if (this._delinkChartYAxis) { - yAxisMode = "unique"; - } else { - yAxisMode = "combined"; - } - menu.datapointScope = this._datapointScope; - menu.showIcons = this._showChartDatapointIcons; - menu.showLines = this._showChartDatapointLines; - menu.showTooltips = this._showChartTooltips; - menu.showHoverGuides = this._showChartEmphasizedHoverGuides; - menu.hoverSnapMode = this._chartHoverSnapMode; - menu.showCorrelatedAnomalies = this._showCorrelatedAnomalies; - menu.showDataGaps = this._showDataGaps; - menu.dataGapThreshold = this._dataGapThreshold; - menu.yAxisMode = yAxisMode; - menu.anomalyOverlapMode = this._chartAnomalyOverlapMode; - menu.anyAnomaliesEnabled = (this._seriesRows ?? []).some( - (r2) => r2.analysis?.show_anomalies === true - ); - } - _updateSeriesRowVisibilityByEntityId(entityId, visible) { - const normalizedEntityId = String(entityId || "").trim(); - if (!normalizedEntityId) { - return; - } - const index = this._seriesRows.findIndex( - (row) => row.entity_id === normalizedEntityId - ); - if (index === -1) { - return; - } - this._updateSeriesRowVisibility(index, visible); - } - _toggleSeriesAnalysisExpanded(entityId) { - const normalizedEntityId = String(entityId || "").trim(); - if (!normalizedEntityId) { - return; - } - const index = this._seriesRows.findIndex( - (row2) => row2.entity_id === normalizedEntityId - ); - if (index === -1) { - return; - } - const row = this._seriesRows[index]; - const currentAnalysis = normalizeHistorySeriesAnalysis(row.analysis); - const nextAnalysis = normalizeHistorySeriesAnalysis({ - ...row.analysis, - expanded: !currentAnalysis.expanded - }); - this._seriesRows[index] = { - ...row, - analysis: nextAnalysis - }; - this._saveSessionState(); - this._updateUrl({ push: false }); - this._renderTargetRows(); - } - _setSeriesAnalysisOption(entityId, key, value) { - const normalizedEntityId = String(entityId || "").trim(); - const normalizedKey = String(key || "").trim(); - if (!normalizedEntityId || !normalizedKey) { - return; - } - if (normalizedKey === "anomaly_comparison_window_id" && value === "__add_new__") { - this._pendingAnomalyComparisonWindowEntityId = normalizedEntityId; - this._openDateWindowDialog(); - return; - } - const index = this._seriesRows.findIndex( - (row2) => row2.entity_id === normalizedEntityId - ); - if (index === -1) { - return; - } - const row = this._seriesRows[index]; - const analysis = normalizeHistorySeriesAnalysis(row.analysis); - let nextKey = normalizedKey; - let nextValue = value; - if (nextKey.startsWith("anomaly_method_toggle_")) { - const method = nextKey.slice("anomaly_method_toggle_".length); - const currentMethods = analysis.anomaly_methods; - const nextMethods = nextValue === true ? [.../* @__PURE__ */ new Set([...currentMethods, method])] : currentMethods.filter((m2) => m2 !== method); - nextKey = "anomaly_methods"; - nextValue = nextMethods; - } - const nextSource = { - ...analysis, - [nextKey]: nextValue - }; - if (nextKey === "show_trend_lines" && nextValue !== true) { - nextSource.show_trend_crosshairs = false; - } - if (nextKey === "show_threshold_analysis" && nextValue !== true) { - nextSource.show_threshold_shading = false; - } - if (nextKey === "show_delta_analysis" && nextValue !== true) { - nextSource.show_delta_tooltip = true; - nextSource.show_delta_lines = false; - } - if (nextKey === "show_anomalies" && nextValue === true && (!Array.isArray(analysis.anomaly_methods) || analysis.anomaly_methods.length === 0)) { - nextSource.anomaly_methods = ["trend_residual"]; - } - const nextAnalysis = normalizeHistorySeriesAnalysis({ - ...nextSource, - expanded: true - }); - const unchanged = JSON.stringify(nextAnalysis) === JSON.stringify(analysis); - if (unchanged) { - return; - } - this._seriesRows[index] = { - ...row, - analysis: nextAnalysis - }; - this._saveSessionState(); - this._updateUrl({ push: false }); - this._renderTargetRows(); - this._renderSidebarOptions(); - this._renderContent(); - } - _copyAnalysisToAll(sourceEntityId, sourceAnalysis) { - const normalizedEntityId = String(sourceEntityId || "").trim(); - if (!normalizedEntityId || !sourceAnalysis) { - return; - } - let changed = false; - this._seriesRows = this._seriesRows.map((row) => { - if (row.entity_id === normalizedEntityId) { - return row; - } - const currentAnalysis = normalizeHistorySeriesAnalysis(row.analysis); - const nextAnalysis = normalizeHistorySeriesAnalysis({ - ...sourceAnalysis, - // Preserve per-row state that shouldn't be overwritten - expanded: currentAnalysis.expanded, - // Don't copy anomaly_comparison_window_id — it's entity-specific - anomaly_comparison_window_id: currentAnalysis.anomaly_comparison_window_id - }); - if (JSON.stringify(nextAnalysis) === JSON.stringify(currentAnalysis)) { - return row; - } - changed = true; - return { ...row, analysis: nextAnalysis }; - }); - if (!changed) { - return; - } - this._saveSessionState(); - this._updateUrl({ push: false }); - this._renderTargetRows(); - this._renderContent(); - } - _removeSeriesRow(index) { - if (!Number.isInteger(index) || index === void 0 || index < 0 || index >= this._seriesRows.length) { - return; - } - this._seriesRows = this._seriesRows.filter( - (_row, rowIndex) => rowIndex !== index - ); - this._syncSeriesState(); - this._saveSessionState(); - this._renderTargetRows(); - this._syncControls(); - this._updateUrl({ push: true }); - this._renderContent(); - } - _clearAutoZoomTimer() { - if (this._autoZoomTimer) { - window.clearTimeout(this._autoZoomTimer); - this._autoZoomTimer = null; - } - } - _toggleDatePickerMenu(force = false) { - if (!force) { - this._rangeToolbarComp?.closeMenus(); - } - } - _togglePageMenu(force = !this._pageMenuOpen) { - this._pageMenuOpen = !!force; - if (!force) { - this._shellEl?.closePageMenu(); - } - } - _handleWindowPointerDown() { - } - _syncOptionsMenu() { - this._rangeToolbarComp?.syncOptionsLabels(); - } - _handleDatePickerChange(ev) { - const { start, end } = extractRangeValue( - ev - ); - if (!start || !end || start >= end) { - return; - } - if (ev.type === "change") { - this._toggleDatePickerMenu(false); - } - this._applyCommittedRange(start, end, { push: true }); - } - async _downloadSpreadsheet() { - if (this._exportBusy || !this._hass || !this._startTime || !this._endTime) { - return; - } - this._togglePageMenu(false); - await this._context.persistence.downloadSpreadsheet({ - entityIds: this._entities, - startTime: this._startTime, - endTime: this._endTime, - datapointScope: this._datapointScope, - onError: (error) => { - logger$1.error( - "[hass-datapoints panel] spreadsheet export:failed", - error - ); - } - }); - } - // --------------------------------------------------------------------------- - // Saved page state (persistent via HA frontend user data) - // --------------------------------------------------------------------------- - async _loadSavedPageIndicator() { - await this._context.fetch.loadSavedPageIndicator({ - savedPageKey: PANEL_HISTORY_SAVED_PAGE_KEY, - fallbackValue: null, - onSuccess: () => { - this._syncSavedPageMenuItems(); - }, - onError: () => { - } - }); - } - _syncSavedPageMenuItems() { - if (this._shellEl) { - this._shellEl.hasSavedState = this._hasSavedPage; - } - } - async _savePageState() { - if (this._savePageBusy || !this._hass) { - return; - } - this._togglePageMenu(false); - await this._context.persistence.savePageState({ - savedPageKey: PANEL_HISTORY_SAVED_PAGE_KEY, - state: buildHistoryPageSessionState(this), - onSuccess: () => { - this._hasSavedPage = true; - this._syncSavedPageMenuItems(); - }, - onError: (err) => { - logger$1.error("[hass-datapoints panel] save page state failed:", err); - } - }); - } - async _restorePageState() { - if (!this._hass) { - return; - } - this._togglePageMenu(false); - await this._context.persistence.restorePageState({ - savedPageKey: PANEL_HISTORY_SAVED_PAGE_KEY, - fallbackValue: null, - onSuccess: (saved) => { - try { - window.sessionStorage.setItem( - `${DOMAIN}:panel_history_session`, - JSON.stringify(saved) - ); - } catch { - } - const baseUrl = window.location.pathname; - window.history.replaceState(null, "", baseUrl); - window.location.reload(); - }, - onError: (err) => { - logger$1.error("[hass-datapoints panel] restore page state failed:", err); - } - }); - } - async _clearSavedPageState() { - if (!this._hass) { - return; - } - this._togglePageMenu(false); - await this._context.persistence.clearSavedPageState({ - savedPageKey: PANEL_HISTORY_SAVED_PAGE_KEY, - onSuccess: () => { - this._hasSavedPage = false; - this._syncSavedPageMenuItems(); - }, - onError: (err) => { - logger$1.error( - "[hass-datapoints panel] clear saved page state failed:", - err - ); - } - }); - } - _getEffectiveZoomLevel() { - if (this._zoomLevel !== "auto") { - return this._zoomLevel; - } - if (!this._resolvedAutoZoomLevel) { - const referenceSpanMs = Math.max( - (this._endTime?.getTime() || Date.now()) - (this._startTime?.getTime() || Date.now() - RANGE_SLIDER_WINDOW_MS), - RANGE_SLIDER_MIN_SPAN_MS - ); - this._resolvedAutoZoomLevel = this._computeZoomLevelForSpan(referenceSpanMs); - } - return this._resolvedAutoZoomLevel; - } - _getZoomConfig() { - return RANGE_ZOOM_CONFIGS[this._getEffectiveZoomLevel()] || RANGE_ZOOM_CONFIGS.month_short; - } - _computeZoomLevelForSpan(spanMs) { - const normalizedSpanMs = Math.max(spanMs, RANGE_SLIDER_MIN_SPAN_MS); - if (normalizedSpanMs >= 180 * DAY_MS) { - return "quarterly"; - } - if (normalizedSpanMs >= 120 * DAY_MS) { - return "month_compressed"; - } - if (normalizedSpanMs >= 60 * DAY_MS) { - return "month_short"; - } - if (normalizedSpanMs >= 21 * DAY_MS) { - return "month_expanded"; - } - if (normalizedSpanMs >= 7 * DAY_MS) { - return "week_compressed"; - } - if (normalizedSpanMs >= 2 * DAY_MS) { - return "week_expanded"; - } - return "day"; - } - _getEffectiveSnapUnit() { - if (this._dateSnapping !== "auto") { - return this._dateSnapping; - } - switch (this._getEffectiveZoomLevel()) { - case "quarterly": - case "month_compressed": - return "month"; - case "month_short": - case "month_expanded": - case "week_compressed": - return "week"; - case "week_expanded": - return "day"; - case "day": - return "hour"; - default: - return "day"; - } - } - _getSnapSpanMs(reference = this._startTime || /* @__PURE__ */ new Date()) { - const snapUnit = this._getEffectiveSnapUnit(); - const start = startOfUnit(reference, snapUnit); - const end = endOfUnit(reference, snapUnit); - return Math.max(SECOND_MS, end.getTime() - start.getTime()); - } - _deriveRangeBounds() { - const config = this._getZoomConfig(); - const startMs = this._startTime?.getTime() || Date.now() - 24 * HOUR_MS; - const endMs = this._endTime?.getTime() || Date.now(); - const historyStartMs = this._historyStartTime?.getTime(); - const historyEndMs = this._historyEndTime?.getTime(); - const maxLookAheadMs = addUnit(/* @__PURE__ */ new Date(), "month", 3).getTime(); - const anchorMs = historyStartMs ?? startMs; - const naturalMin = startOfUnit( - new Date(anchorMs), - config.boundsUnit - ).getTime(); - const paddedMin = startOfUnit( - new Date(startMs - config.baselineMs * 0.3), - config.boundsUnit - ).getTime(); - const min = Math.min(naturalMin, paddedMin); - const futureReference = addUnit( - new Date(historyEndMs ?? endMs), - "year", - RANGE_FUTURE_BUFFER_YEARS - ).getTime(); - const maxReference = Math.min( - maxLookAheadMs, - Math.max( - futureReference, - endMs, - startMs + this._getSnapSpanMs(this._startTime || /* @__PURE__ */ new Date()) - ) - ); - const max = endOfUnit(new Date(maxReference), config.boundsUnit).getTime(); - return { min, max: Math.max(max, min + SECOND_MS), config }; - } - _syncRangeControl() { - if (!this._rangeToolbarComp) { - return; - } - this._rangeBounds = this._deriveRangeBounds(); - this._ensureTimelineEvents(); - this._rangeToolbarComp.startTime = this._startTime ? new Date(this._startTime) : null; - this._rangeToolbarComp.endTime = this._endTime ? new Date(this._endTime) : null; - this._rangeToolbarComp.rangeBounds = this._rangeBounds; - this._rangeToolbarComp.zoomLevel = this._getEffectiveZoomLevel(); - this._rangeToolbarComp.dateSnapping = this._dateSnapping; - this._rangeToolbarComp.isLiveEdge = this._isOnLiveEdge(); - this._rangeToolbarComp.timelineEvents = this._timelineEvents || []; - this._updateComparisonRangePreview(); - this._updateChartHoverIndicator(); - this._updateChartZoomHighlight(); - this._syncMobileDateInputs(); - } - _updateComparisonRangePreview() { - if (!this._rangeToolbarComp) { - return; - } - const comparisonWindow = this._getActiveComparisonWindow(); - if (!this._rangeBounds || !comparisonWindow) { - this._rangeToolbarComp.comparisonPreview = null; - this._updateZoomWindowHighlight(); - return; - } - const startMs = new Date(comparisonWindow.start_time).getTime(); - const endMs = new Date(comparisonWindow.end_time).getTime(); - if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || startMs >= endMs) { - this._rangeToolbarComp.comparisonPreview = null; - this._updateZoomWindowHighlight(); - return; - } - this._rangeToolbarComp.comparisonPreview = { start: startMs, end: endMs }; - this._updateZoomWindowHighlight(); - } - _handleChartHover(ev) { - this._chartHoverTimeMs = ev?.detail?.timeMs ?? null; - this._updateChartHoverIndicator(); - } - _handleChartZoom(ev) { - const detail = ev.detail; - const start = Number.isFinite(detail?.startTime) ? detail?.startTime ?? null : null; - const end = Number.isFinite(detail?.endTime) ? detail?.endTime ?? null : null; - const isPreview = !!detail?.preview; - const source = detail?.source || "select"; - const nextRange = start != null && end != null && start < end ? { start, end } : null; - if (isPreview) { - this._chartZoomRange = nextRange; - } else { - this._chartZoomRange = nextRange; - this._chartZoomCommittedRange = nextRange ? { ...nextRange } : null; - if (this._hoveredComparisonWindowId) { - this._hoveredComparisonWindowId = null; - } - if (source === "scroll") { - this._scheduleChartZoomStateCommit(); - } else { - this._saveSessionState(); - this._updateUrl({ push: false }); - this._syncListZoomState(); - } - } - this._updateChartZoomHighlight(); - if (!isPreview && source !== "scroll") { - this._renderComparisonTabs(); - } - if (!nextRange) { - this._rangeToolbarComp?.revealSelection?.(); - } - } - _scheduleChartZoomStateCommit() { - if (this._chartZoomStateCommitTimer) { - window.clearTimeout(this._chartZoomStateCommitTimer); - } - this._chartZoomStateCommitTimer = window.setTimeout(() => { - this._chartZoomStateCommitTimer = null; - this._saveSessionState(); - this._updateUrl({ push: false }); - this._syncListZoomState(); - }, 180); - } - _syncListZoomState() { - if (!this._listEl) { - return; - } - const listConfig = { - entities: this._entities, - datapoint_scope: this._datapointScope, - hours_to_show: this._hours, - start_time: this._startTime?.toISOString(), - end_time: this._endTime?.toISOString(), - zoom_start_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.start).toISOString() : null, - zoom_end_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.end).toISOString() : null, - page_size: 15, - show_entities: true, - show_actions: true, - show_search: true, - hidden_event_ids: this._hiddenEventIds - }; - const nextListConfigKey = JSON.stringify(listConfig); - if (this._listConfigKey !== nextListConfigKey) { - this._listEl.setConfig(listConfig); - this._listConfigKey = nextListConfigKey; - } - this._listEl.hass = this._hass; - } - _handleRecordsSearch(ev) { - const nextQuery = String(ev?.detail?.query || "").trim().toLowerCase(); - if (nextQuery === this._recordsSearchQuery) { - return; - } - this._recordsSearchQuery = nextQuery; - this._renderContent(); - } - _handleToggleEventVisibility(ev) { - const eventId = ev?.detail?.eventId; - if (!eventId) { - return; - } - const wasPreviouslyHidden = this._hiddenEventIds.includes(eventId); - if (wasPreviouslyHidden) { - this._hiddenEventIds = this._hiddenEventIds.filter( - (id) => id !== eventId - ); - } else { - this._hiddenEventIds = [...this._hiddenEventIds, eventId]; - } - this._renderContent(); - } - _handleHoverEventRecord(ev) { - const eventId = String(ev?.detail?.eventId || "").trim(); - if (!eventId) { - return; - } - const hovered = ev?.detail?.hovered === true; - const alreadyHovered = this._hoveredEventIds.includes(eventId); - if (hovered && alreadyHovered) { - return; - } - if (!hovered && !alreadyHovered) { - return; - } - this._hoveredEventIds = hovered ? [eventId] : []; - this._renderContent(); - } - _handleToggleSeriesVisibility(ev) { - const entityId = String(ev?.detail?.entityId || "").trim(); - const visible = ev?.detail?.visible; - if (!entityId || typeof visible !== "boolean") { - return; - } - const index = this._seriesRows.findIndex( - (row) => row.entity_id === entityId - ); - if (index === -1 || this._seriesRows[index].visible === visible) { - return; - } - this._seriesRows[index] = { ...this._seriesRows[index], visible }; - this._saveSessionState(); - this._updateUrl({ push: false }); - this._renderTargetRows(); - this._renderContent(); - } - _updateChartHoverIndicator() { - if (!this._rangeToolbarComp) { - return; - } - if (!this._rangeBounds || this._chartHoverTimeMs == null) { - this._rangeToolbarComp.chartHoverTimeMs = null; - this._rangeToolbarComp.chartHoverWindowTimeMs = null; - return; - } - this._rangeToolbarComp.chartHoverTimeMs = this._chartHoverTimeMs; - const activeWindow = this._getActiveComparisonWindow(); - if (activeWindow && this._startTime) { - const timeOffsetMs = new Date(activeWindow.start_time).getTime() - this._startTime.getTime(); - this._rangeToolbarComp.chartHoverWindowTimeMs = this._chartHoverTimeMs + timeOffsetMs; - } else { - this._rangeToolbarComp.chartHoverWindowTimeMs = null; - } - } - _updateChartZoomHighlight() { - if (!this._rangeToolbarComp) { - return; - } - const highlightRange = this._chartZoomRange || this._chartZoomCommittedRange; - const nextZoomRange = this._rangeBounds && highlightRange ? { - start: +highlightRange.start, - end: +highlightRange.end - } : null; - const nextZoomWindowRange = this._getZoomWindowHighlightRange(); - this._rangeToolbarComp.syncZoomHighlights( - nextZoomRange, - nextZoomWindowRange - ); - } - _updateZoomWindowHighlight() { - if (!this._rangeToolbarComp) { - return; - } - this._rangeToolbarComp.syncZoomHighlights( - this._rangeBounds && (this._chartZoomRange || this._chartZoomCommittedRange) ? { - start: +(this._chartZoomRange || this._chartZoomCommittedRange).start, - end: +(this._chartZoomRange || this._chartZoomCommittedRange).end - } : null, - this._getZoomWindowHighlightRange() - ); - } - _getZoomWindowHighlightRange() { - const activeWindow = this._getActiveComparisonWindow(); - const zoomRange = this._chartZoomRange || this._chartZoomCommittedRange; - if (!this._rangeBounds || !activeWindow || !zoomRange || !this._startTime) { - return null; - } - const windowStartMs = new Date(activeWindow.start_time).getTime(); - const windowEndMs = new Date(activeWindow.end_time).getTime(); - if (!Number.isFinite(windowStartMs) || !Number.isFinite(windowEndMs) || windowStartMs >= windowEndMs) { - return null; - } - const timeOffsetMs = windowStartMs - this._startTime.getTime(); - const zoomStartMs = +zoomRange.start + timeOffsetMs; - const zoomEndMs = +zoomRange.end + timeOffsetMs; - const intersectStart = Math.max(windowStartMs, zoomStartMs); - const intersectEnd = Math.min(windowEndMs, zoomEndMs); - if (intersectStart >= intersectEnd) { - return null; - } - return { - start: intersectStart, - end: intersectEnd - }; - } - _scheduleAutoZoomUpdate(draftStart, draftEnd) { - if (this._zoomLevel !== "auto" || !this._rangeBounds) { - return; - } - const start = draftStart || this._startTime; - const end = draftEnd || this._endTime; - if (!start || !end || start >= end) { - return; - } - const currentLevel = this._getEffectiveZoomLevel(); - const selectionSpanMs = Math.max( - end.getTime() - start.getTime(), - RANGE_SLIDER_MIN_SPAN_MS - ); - const paddedSelectionSpanMs = Math.max( - selectionSpanMs * (1 + RANGE_AUTO_ZOOM_SELECTION_PADDING_RATIO), - RANGE_SLIDER_MIN_SPAN_MS - ); - const candidateLevel = this._computeZoomLevelForSpan(paddedSelectionSpanMs); - if (candidateLevel === currentLevel) { - this._clearAutoZoomTimer(); - return; - } - this._clearAutoZoomTimer(); - this._autoZoomTimer = window.setTimeout(() => { - this._autoZoomTimer = null; - const latestStart = draftStart || this._startTime; - const latestEnd = draftEnd || this._endTime; - if (!latestStart || !latestEnd || latestStart >= latestEnd || this._zoomLevel !== "auto" || !this._rangeBounds) { - return; - } - const latestLevel = this._getEffectiveZoomLevel(); - const latestSelectionSpanMs = Math.max( - latestEnd.getTime() - latestStart.getTime(), - RANGE_SLIDER_MIN_SPAN_MS - ); - const latestPaddedSelectionSpanMs = Math.max( - latestSelectionSpanMs * (1 + RANGE_AUTO_ZOOM_SELECTION_PADDING_RATIO), - RANGE_SLIDER_MIN_SPAN_MS - ); - const latestCandidateLevel = this._computeZoomLevelForSpan( - latestPaddedSelectionSpanMs - ); - if (latestCandidateLevel === latestLevel) { - return; - } - this._resolvedAutoZoomLevel = latestCandidateLevel; - this._syncRangeControl(); - }, RANGE_AUTO_ZOOM_DEBOUNCE_MS); - } - // --------------------------------------------------------------------------- - // Live-edge detection and handle indicator - // --------------------------------------------------------------------------- - /** Returns true when the committed end time is at or very near "now", - * meaning new annotations should cause the visible range to advance. */ - _isOnLiveEdge() { - if (!this._endTime) { - return false; - } - return this._endTime.getTime() >= Date.now() - 2 * MINUTE_MS; - } - /** Toggle the live-edge indicator on the end handle. */ - _syncLiveEdgeHandle() { - if (!this._rangeToolbarComp) { - return; - } - this._rangeToolbarComp.isLiveEdge = this._isOnLiveEdge(); - } - /** Called whenever a new annotation is recorded (HA event or window event). - * If the current range is on the live edge, advance the end time to now - * so the chart immediately shows the new data point. */ - _handleEventRecorded() { - if (!this._isOnLiveEdge() || !this._startTime) { - return; - } - this._applyCommittedRange(this._startTime, /* @__PURE__ */ new Date(), { push: false }); - } - _applyCommittedRange(start, end, { push = false } = {}) { - if (!start || !end || start >= end) { - return; - } - const nextStart = new Date(start); - const nextEnd = new Date(end); - const didChange = !this._startTime || !this._endTime || this._startTime.getTime() !== nextStart.getTime() || this._endTime.getTime() !== nextEnd.getTime(); - this._startTime = nextStart; - this._endTime = nextEnd; - this._hours = Math.max( - 1, - Math.round((nextEnd.getTime() - nextStart.getTime()) / HOUR_MS) - ); - this._syncLiveEdgeHandle(); - this._scheduleAutoZoomUpdate(void 0, void 0); - this._syncControls(); - this._chartEl?.setExternalZoomRange?.(this._chartZoomCommittedRange); - if (!didChange) { - return; - } - this._saveSessionState(); - this._updateUrl({ push }); - this._renderContent(); - } - _commitRangeSelection({ push = false } = {}) { - if (this._rangeCommitTimer) { - window.clearTimeout(this._rangeCommitTimer); - this._rangeCommitTimer = null; - } - if (!this._draftStartTime || !this._draftEndTime || this._draftStartTime >= this._draftEndTime) { - return; - } - this._applyCommittedRange(this._draftStartTime, this._draftEndTime, { - push - }); - } - _updateUrl({ push = false } = {}) { - this._context.navigation.updateUrl({ - entities: this._entities, - datapointScope: this._datapointScope, - startTime: this._startTime, - endTime: this._endTime, - hours: this._hours, - committedZoomRange: this._chartZoomCommittedRange, - comparisonWindows: this._comparisonWindows, - pageState: buildHistoryPageSessionState( - this - ), - seriesRows: this._seriesRows, - seriesColorQueryKey: (entityId) => this._seriesColorQueryKey(entityId), - push - }); - } - _renderComparisonTabs() { - const result = this._context.orchestration.renderComparisonTabs({ - chartEl: this._chartEl, - comparisonWindows: Array.isArray(this._comparisonWindows) ? this._comparisonWindows : [], - selectedComparisonWindowId: this._selectedComparisonWindowId, - hoveredComparisonWindowId: this._hoveredComparisonWindowId, - startTime: this._startTime, - endTime: this._endTime, - loadingComparisonWindowIds: [...this._loadingComparisonWindowIds], - comparisonTabRailComp: this._comparisonTabRailComp, - comparisonTabsHostEl: this._comparisonTabsHostEl, - formatComparisonLabel: (startTime, endTime) => this._formatComparisonLabel(startTime, endTime), - onActivate: (tabId) => { - this._handleComparisonTabActivate(tabId); - }, - onHover: (tabId) => { - this._handleComparisonTabHover(tabId); - }, - onLeave: (tabId) => { - this._handleComparisonTabLeave(tabId); - }, - onEdit: (tabId) => { - const win = this._comparisonWindows.find( - (entry) => entry.id === tabId - ); - if (win) { - this._openDateWindowDialog(win); - } - }, - onDelete: (tabId) => { - if (tabId) { - this._deleteDateWindow(tabId); - } - }, - onAdd: () => { - this._openDateWindowDialog(); - } - }); - this._comparisonTabRailComp = result.comparisonTabRailComp; - this._comparisonTabsHostEl = result.comparisonTabsHostEl; - } - _updateComparisonTabsOverflow() { - this._context.orchestration.updateComparisonTabsOverflow(this._chartEl); - } - _renderContent() { - const content = this._contentHostEl; - if (!content) { - return; - } - if (!this._entities.length) { - if (this._contentKey === "__empty__") { - return; - } - this._chartHoverTimeMs = null; - this._updateChartHoverIndicator(); - this._chartZoomRange = null; - this._chartZoomCommittedRange = null; - this._updateChartZoomHighlight(); - content.innerHTML = ` + Create date window +
+
+
+ `; + dialog.addEventListener("closed", () => this._closeDateWindowDialog(true)); + this.shadowRoot.appendChild(dialog); + this._dateWindowDialogEl = dialog; + this._dateWindowDialogNameEl = dialog.querySelector("#date-window-name"); + this._dateWindowDialogStartEl = dialog.querySelector("#date-window-start"); + this._dateWindowDialogEndEl = dialog.querySelector("#date-window-end"); + this._dateWindowDialogShortcutsEl = dialog.querySelector("#date-window-shortcuts"); + if (this._hass && this._dateWindowDialogNameEl) this._dateWindowDialogNameEl.hass = this._hass; + dialog.querySelector("#date-window-cancel")?.addEventListener("click", () => this._closeDateWindowDialog()); + dialog.querySelector("#date-window-submit")?.addEventListener("click", () => this._createDateWindowFromDialog()); + dialog.querySelector("#date-window-delete")?.addEventListener("click", () => this._deleteEditingDateWindow()); + this._dateWindowDialogStartEl?.addEventListener("change", () => this._handleDateWindowDialogInputChange()); + this._dateWindowDialogEndEl?.addEventListener("change", () => this._handleDateWindowDialogInputChange()); + dialog.querySelector("#date-window-previous")?.addEventListener("click", () => this._applyDateWindowShortcut(-1)); + dialog.querySelector("#date-window-next")?.addEventListener("click", () => this._applyDateWindowShortcut(1)); + } + _openDateWindowDialog(targetWindow = null) { + this._ensureDateWindowDialog(); + this._dateWindowDialogOpen = true; + this._editingDateWindowId = targetWindow?.id || null; + const dialogStart = targetWindow ? parseDateValue(targetWindow.start_time) : this._startTime; + const dialogEnd = targetWindow ? parseDateValue(targetWindow.end_time) : this._endTime; + this._dateWindowDialogDraftRange = dialogStart && dialogEnd && dialogStart < dialogEnd ? { + start: new Date(dialogStart), + end: new Date(dialogEnd) + } : null; + if (this._dateWindowDialogComp) { + this._dateWindowDialogComp.heading = targetWindow ? msg("Edit date window") : msg("Add date window"); + this._dateWindowDialogComp.submitLabel = targetWindow ? msg("Save date window") : msg("Create date window"); + this._dateWindowDialogComp.showDelete = !!targetWindow; + this._dateWindowDialogComp.showShortcuts = !targetWindow; + this._dateWindowDialogComp.name = targetWindow?.label || ""; + this._dateWindowDialogComp.startValue = this._formatDateWindowInputValue(this._dateWindowDialogDraftRange?.start || null); + this._dateWindowDialogComp.endValue = this._formatDateWindowInputValue(this._dateWindowDialogDraftRange?.end || null); + this._dateWindowDialogComp.rangeBounds = this._rangeBounds ?? null; + this._dateWindowDialogComp.zoomLevel = this._zoomLevel ?? "auto"; + this._dateWindowDialogComp.dateSnapping = this._dateSnapping ?? "hour"; + this._dateWindowDialogComp.open = true; + return; + } + if (this._dateWindowDialogEl) { + this._dateWindowDialogEl.open = true; + this._dateWindowDialogEl.headerTitle = targetWindow ? msg("Edit date window") : msg("Add date window"); + } + const submitButton = this._dateWindowDialogEl?.querySelector("#date-window-submit"); + if (submitButton) submitButton.textContent = targetWindow ? msg("Save date window") : msg("Create date window"); + const deleteButton = this._dateWindowDialogEl?.querySelector("#date-window-delete"); + if (deleteButton) { + deleteButton.hidden = !targetWindow; + deleteButton.style.display = targetWindow ? "" : "none"; + } + if (this._dateWindowDialogShortcutsEl) this._dateWindowDialogShortcutsEl.hidden = !!targetWindow; + if (this._dateWindowDialogNameEl) this._dateWindowDialogNameEl.value = targetWindow?.label || ""; + this._syncDateWindowDialogInputs(); + window.requestAnimationFrame(() => this._dateWindowDialogNameEl?.focus()); + } + _closeDateWindowDialog(fromClosedEvent = false) { + this._dateWindowDialogOpen = false; + this._editingDateWindowId = null; + this._dateWindowDialogDraftRange = null; + this._pendingAnomalyComparisonWindowEntityId = null; + if (!fromClosedEvent) { + if (this._dateWindowDialogComp) this._dateWindowDialogComp.open = false; + else if (this._dateWindowDialogEl) this._dateWindowDialogEl.open = false; + } + } + _createDateWindowFromDialog(overrides = {}) { + const rawName = overrides.name != null ? overrides.name : this._dateWindowDialogNameEl?.value || ""; + const label = String(rawName).trim(); + const parsedStart = overrides.start ? this._parseDateWindowInputValue(String(overrides.start)) : null; + const parsedEnd = overrides.end ? this._parseDateWindowInputValue(String(overrides.end)) : null; + const start = parsedStart || this._dateWindowDialogDraftRange?.start || null; + const end = parsedEnd || this._dateWindowDialogDraftRange?.end || null; + if (!label || !start || !end || start >= end) return; + const existingIds = new Set(this._comparisonWindows.map((window) => window.id)); + const nextWindow = { + id: this._editingDateWindowId || makeDateWindowId(label, existingIds), + label, + start_time: start.toISOString(), + end_time: end.toISOString() + }; + this._comparisonWindows = normalizeDateWindows(this._editingDateWindowId ? this._comparisonWindows.map((window) => window.id === this._editingDateWindowId ? nextWindow : window) : [...this._comparisonWindows, nextWindow]); + this._saveUserPreferences(); + this._saveSessionState(); + this._updateUrl({ push: false }); + const pendingEntityId = this._pendingAnomalyComparisonWindowEntityId; + const wasCreatingNew = !this._editingDateWindowId; + this._pendingAnomalyComparisonWindowEntityId = null; + this._closeDateWindowDialog(); + if (pendingEntityId && wasCreatingNew) this._setSeriesAnalysisOption(pendingEntityId, "anomaly_comparison_window_id", nextWindow.id); + this._renderContent(); + } + async _deleteDateWindow(id) { + if (!id) return; + const windowToDelete = this._comparisonWindows.find((window) => window.id === id); + if (!await confirmDestructiveAction(this, { + title: msg("Delete date window"), + message: `${msg("Delete")} "${windowToDelete?.label || msg("this date window")}"?`, + confirmLabel: msg("Delete date window") + })) return false; + const nextWindows = this._comparisonWindows.filter((window) => window.id !== id); + if (nextWindows.length === this._comparisonWindows.length) return false; + if (this._hoveredComparisonWindowId === id) this._hoveredComparisonWindowId = null; + if (this._selectedComparisonWindowId === id) { + this._selectedComparisonWindowId = null; + this._clearDeltaAnalysisSelectionState(); + } + if (this._hoveredComparisonWindowId == null) this._updateComparisonRangePreview(); + this._comparisonWindows = nextWindows; + this._saveUserPreferences(); + this._saveSessionState(); + this._updateUrl({ push: false }); + this._renderContent(); + return true; + } + async _deleteEditingDateWindow() { + const id = this._editingDateWindowId; + if (!id) return; + if (await this._deleteDateWindow(id)) this._closeDateWindowDialog(); + } + _handleComparisonTabHover(id) { + this._context.orchestration.handleComparisonTabHover({ + id, + hoveredComparisonWindowId: this._hoveredComparisonWindowId, + setHoveredComparisonWindowId: (value) => { + this._hoveredComparisonWindowId = value; + }, + updateComparisonRangePreview: () => { + this._updateComparisonRangePreview(); + }, + updateChartHoverIndicator: () => { + this._updateChartHoverIndicator(); + }, + renderContent: () => { + this._renderContent(); + } + }); + } + _handleComparisonTabLeave(id) { + this._context.orchestration.handleComparisonTabLeave({ + id, + hoveredComparisonWindowId: this._hoveredComparisonWindowId, + setHoveredComparisonWindowId: (value) => { + this._hoveredComparisonWindowId = value; + }, + updateComparisonRangePreview: () => { + this._updateComparisonRangePreview(); + }, + updateChartHoverIndicator: () => { + this._updateChartHoverIndicator(); + }, + renderContent: () => { + this._renderContent(); + } + }); + } + _handleComparisonLoading(ev) { + const ids = Array.isArray(ev?.detail?.ids) ? ev.detail.ids.filter(Boolean) : []; + this._loadingComparisonWindowIds = ev?.detail?.loading === true ? [...new Set([...this._loadingComparisonWindowIds, ...ids])] : this._loadingComparisonWindowIds.filter((id) => !ids.includes(id)); + this._renderComparisonTabs(); + } + _handleAnalysisComputing(ev) { + const computing = ev?.detail?.computing === true; + const entityIds = Array.isArray(ev?.detail?.entityIds) ? ev.detail.entityIds : []; + const computingProgress = computing ? 0 : 100; + const progress = typeof ev?.detail?.progress === "number" ? ev.detail.progress : computingProgress; + if (computing) { + for (const id of entityIds) this._computingEntityIds.add(id); + if (progress === 0) for (const id of entityIds) { + const row = this._seriesRows?.find((r) => r.entity_id === id); + const methods = row?.analysis?.show_anomalies === true && Array.isArray(row.analysis.anomaly_methods) ? row.analysis.anomaly_methods : []; + this._computingMethods.set(id, new Set(methods)); + logger$1.log(`[datapoints] analysis started for ${id} — methods: [${methods.join(", ")}]`); + } + else logger$1.log(`[datapoints] analysis progress ${progress}%`); + } else { + for (const id of entityIds) { + this._computingEntityIds.delete(id); + this._computingMethods.delete(id); + } + logger$1.log(`[datapoints] analysis complete (${entityIds.join(", ")})`); + } + this._analysisProgress = progress; + this._pushComputingStateToRowList(); + } + _handleAnalysisMethodResult(ev) { + logger$1.log("[datapoints] _handleAnalysisMethodResult received", ev?.detail); + const entityId = ev?.detail?.entityId; + const method = ev?.detail?.method; + if (!entityId || !method) { + logger$1.log("[datapoints] _handleAnalysisMethodResult: missing entityId or method, ignoring"); + return; + } + const current = this._computingMethods.get(entityId); + if (current) { + const next = new Set(current); + next.delete(method); + this._computingMethods.set(entityId, next); + } + const remaining = [...this._computingMethods.get(entityId) ?? []]; + logger$1.log(`[datapoints] method done: ${method} for ${entityId} — remaining: [${remaining.join(", ") || "none"}]`); + this._pushComputingStateToRowList(); + } + _pushComputingStateToRowList() { + if (this._rowListEl) { + this._rowListEl.computingEntityIds = new Set(this._computingEntityIds); + this._rowListEl.analysisProgress = this._analysisProgress; + this._rowListEl.computingMethodsByEntity = new Map(this._computingMethods); + } + } + _clearDeltaAnalysisSelectionState() {} + _handleComparisonTabActivate(id) { + this._context.orchestration.handleComparisonTabActivate({ + id, + comparisonWindows: this._comparisonWindows, + selectedComparisonWindowId: this._selectedComparisonWindowId, + setSelectedComparisonWindowId: (value) => { + this._selectedComparisonWindowId = value; + }, + setHoveredComparisonWindowId: (value) => { + this._hoveredComparisonWindowId = value; + }, + clearDeltaAnalysisSelectionState: () => { + this._clearDeltaAnalysisSelectionState(); + }, + updateComparisonRangePreview: () => { + this._updateComparisonRangePreview(); + }, + updateChartHoverIndicator: () => { + this._updateChartHoverIndicator(); + }, + renderComparisonTabs: () => { + this._renderComparisonTabs(); + }, + renderContent: () => { + this._renderContent(); + }, + setAdjustComparisonAxisScale: (value) => { + if (this._chartEl) this._chartEl._adjustComparisonAxisScale = value; + } + }); + } + _syncSidebarUi() { + if (this._shellEl) this._shellEl.sidebarCollapsed = this._sidebarCollapsed; + if (this._historyTargetsComp) this._historyTargetsComp.sidebarCollapsed = this._sidebarCollapsed; + if (this._rangeToolbarComp) this._rangeToolbarComp.sidebarCollapsed = this._sidebarCollapsed; + } + _updateLayoutMode() { + const prev = this._layoutMode; + if (this._mqMobile.matches) this._layoutMode = "mobile"; + else if (this._mqTablet.matches) this._layoutMode = "tablet"; + else this._layoutMode = "desktop"; + if (prev !== this._layoutMode) { + if (this._shellEl) this._shellEl.layoutMode = this._layoutMode; + this._syncSidebarUi(); + this._syncMobileDateInputs(); + } + } + _syncMobileDateInputs() { + this._rangeToolbarComp?.syncMobileDates(this._startTime, this._endTime); + } + _applyContentSplitLayout() { + const content = this._contentHostEl; + if (!content) return; + const resizablePanes = content.querySelector("#content-resizable-panes"); + if (resizablePanes) resizablePanes.ratio = this._contentSplitRatio; + else content.style.setProperty("--content-top-size", `${Math.round(this._contentSplitRatio * 1e3) / 10}%`); + this._updateComparisonTabsOverflow(); + } + _requestChartResizeRedraw() { + this._context.orchestration.requestChartResizeRedraw(this._chartEl); + } + _toggleSidebarCollapsed() { + this._sidebarCollapsed = !this._sidebarCollapsed; + if (!this._sidebarCollapsed) this._hideCollapsedTargetPopup(); + this._saveSessionState(); + this._updateUrl({ push: false }); + this._syncSidebarUi(); + window.requestAnimationFrame(() => { + if (!this.isConnected) return; + this._syncRangeControl(); + }); + } + _handleCollapsedSidebarClick() { + if (!this._sidebarCollapsed) {} + } + _openTargetPicker(anchorEl = null) { + this._context.orchestration.openTargetPicker(this._targetControl, anchorEl); + } + _setDatapointScope(scope) { + let nextScope; + if (scope === "all") nextScope = "all"; + else if (scope === "hidden") nextScope = "hidden"; + else nextScope = "linked"; + if (nextScope === this._datapointScope) return; + this._datapointScope = nextScope; + this._timelineEvents = []; + this._timelineEventsKey = ""; + this._saveSessionState(); + this._renderSidebarOptions(); + this._updateUrl({ push: false }); + this._ensureTimelineEvents(); + this._renderContent(); + } + _setChartYAxisMode(mode) { + const nextDelink = mode === "unique"; + const nextSplit = mode === "split"; + if (this._delinkChartYAxis === nextDelink && this._splitChartView === nextSplit) return; + this._delinkChartYAxis = nextDelink; + this._splitChartView = nextSplit; + this._saveSessionState(); + this._updateUrl({ push: false }); + this._renderSidebarOptions(); + this._renderContent(); + } + _setChartDatapointDisplayOption(kind, enabled) { + const normalized = !!enabled; + if (kind === "icons") { + if (this._showChartDatapointIcons === normalized) return; + this._showChartDatapointIcons = normalized; + } else if (kind === "lines") { + if (this._showChartDatapointLines === normalized) return; + this._showChartDatapointLines = normalized; + } else if (kind === "tooltips") { + if (this._showChartTooltips === normalized) return; + this._showChartTooltips = normalized; + } else if (kind === "hover_guides") { + if (this._showChartEmphasizedHoverGuides === normalized) return; + this._showChartEmphasizedHoverGuides = normalized; + } else if (kind === "hover_snap_mode") { + const value = String(enabled) === "snap_to_data_points" ? "snap_to_data_points" : "follow_series"; + if (this._chartHoverSnapMode === value) return; + this._chartHoverSnapMode = value; + } else if (kind === "correlated_anomalies") { + if (this._showCorrelatedAnomalies === normalized) return; + this._showCorrelatedAnomalies = normalized; + } else if (kind === "delink_y_axis") { + if (this._delinkChartYAxis === normalized) return; + this._delinkChartYAxis = normalized; + if (normalized) this._splitChartView = false; + } else if (kind === "split_chart_view") { + if (this._splitChartView === normalized) return; + this._splitChartView = normalized; + if (normalized) this._delinkChartYAxis = false; + } else if (kind === "data_gaps") { + if (this._showDataGaps === normalized) return; + this._showDataGaps = normalized; + } else if (kind === "data_gap_threshold") { + const value = String(enabled); + if (this._dataGapThreshold === value) return; + this._dataGapThreshold = value; + } else return; + this._saveSessionState(); + this._updateUrl({ push: false }); + this._renderSidebarOptions(); + this._renderContent(); + } + async _ensureHistoryBounds() { + await this._context.fetch.ensureHistoryBounds({ + onSuccess: ({ start, end }) => { + this._historyStartTime = parseDateValue(start); + this._historyEndTime = parseDateValue(end); + if (this._zoomLevel === "auto") this._resolvedAutoZoomLevel = null; + if (this._rendered) this._syncControls(); + }, + onError: (err) => { + logger$1.warn("[hass-datapoints] failed to load event bounds:", err); + } + }); + } + async _ensureTimelineEvents() { + if (!this._hass || !this._rangeBounds) return; + if (this._datapointScope === "hidden" || this._datapointScope === "linked" && this._entities.length === 0) { + this._timelineEvents = []; + this._context.fetch.resetTimelineEvents(); + if (this._rendered && this._rangeToolbarComp) this._rangeToolbarComp.timelineEvents = []; + return; + } + const startIso = new Date(this._rangeBounds.min).toISOString(); + const endIso = new Date(this._rangeBounds.max).toISOString(); + await this._context.fetch.loadTimelineEvents({ + startIso, + endIso, + datapointScope: this._datapointScope, + entityIds: this._entities, + onSuccess: (events, key) => { + this._timelineEvents = events; + this._timelineEventsKey = key; + if (this._rendered && this._rangeToolbarComp) this._rangeToolbarComp.timelineEvents = this._timelineEvents; + }, + onError: (err) => { + logger$1.warn("[hass-datapoints] failed to load timeline events:", err); + } + }); + } + async _ensureUserPreferences() { + await this._context.fetch.ensureUserPreferences({ + preferencesKey: PANEL_HISTORY_PREFERENCES_KEY, + fallbackValue: null, + onSuccess: (preferences) => { + const normalized = normalizeHistoryPagePreferences(preferences, { + zoomOptions: RANGE_ZOOM_OPTIONS, + snapOptions: RANGE_SNAP_OPTIONS + }); + this._zoomLevel = normalized.zoomLevel; + this._dateSnapping = normalized.dateSnapping; + this._resolvedAutoZoomLevel = normalized.zoomLevel === "auto" ? null : this._resolvedAutoZoomLevel; + this._preferredSeriesColors = normalized.preferredSeriesColors; + this._comparisonWindows = this._comparisonWindows.length ? this._comparisonWindows : normalized.comparisonWindows; + this._seriesRows = this._applyPreferredSeriesColors(this._seriesRows); + if (!this._localPageStateDirty) this._applyPreferencePageState(normalized.pageState); + if (normalized.shouldPersistDefaults) this._saveUserPreferences(); + if (this._rendered) { + this._renderTargetRows(); + this._syncControls(); + this._updateUrl({ push: false }); + this._renderContent(); + } + }, + onError: (err) => { + logger$1.warn("[hass-datapoints] failed to load panel preferences:", err); + } + }); + } + _saveUserPreferences() { + if (!this._hass) return; + const payload = buildHistoryPagePreferencesPayload(this); + this._preferredSeriesColors = payload.series_colors ?? {}; + this._context.persistence.saveUserPreferences({ + preferencesKey: PANEL_HISTORY_PREFERENCES_KEY, + payload + }); + } + _mountControls() { + if (!this._shellEl) return; + const histTargets = this._mountHistoryTargetsControl(); + this._mountTargetPickerControl(histTargets); + this._mountRangeToolbarControl(); + this._mountSidebarOptionsControl(); + this._mountDateWindowDialogControl(); + this._syncControls(); + } + _mountHistoryTargetsControl() { + const histTargets = document.createElement("history-targets"); + histTargets.slot = "sidebar"; + histTargets.rows = []; + histTargets.states = {}; + histTargets.hass = this._hass ?? null; + histTargets.comparisonWindows = this._comparisonWindows; + histTargets.canShowDeltaAnalysis = false; + histTargets.sidebarCollapsed = this._sidebarCollapsed; + histTargets.addEventListener("dp-row-color-change", (ev) => { + const { index, color } = ev.detail || {}; + this._updateSeriesRowColor(index, color); + }); + histTargets.addEventListener("dp-row-visibility-change", (ev) => { + const { entityId, visible } = ev.detail || {}; + this._updateSeriesRowVisibilityByEntityId(entityId, visible); + }); + histTargets.addEventListener("dp-row-remove", (ev) => { + const { index } = ev.detail || {}; + this._removeSeriesRow(index); + }); + histTargets.addEventListener("dp-row-toggle-analysis", (ev) => { + const { entityId } = ev.detail || {}; + this._toggleSeriesAnalysisExpanded(entityId); + }); + histTargets.addEventListener("dp-row-analysis-change", (ev) => { + const { entityId, key, value } = ev.detail || {}; + this._setSeriesAnalysisOption(entityId, key, value); + }); + histTargets.addEventListener("dp-row-copy-analysis-to-all", (ev) => { + const { entityId, analysis } = ev.detail || {}; + this._copyAnalysisToAll(entityId, analysis); + }); + histTargets.addEventListener("dp-rows-reorder", (ev) => { + const { rows } = ev.detail || {}; + if (!Array.isArray(rows)) return; + this._seriesRows = rows; + this._syncSeriesState(); + this._saveSessionState(); + this._renderTargetRows(); + this._syncControls(); + this._updateUrl({ push: true }); + this._renderContent(); + }); + histTargets.addEventListener("dp-targets-prefs-click", (ev) => { + ev.stopPropagation(); + const anchor = ev.composedPath()[0] || ev.target; + if (!(anchor instanceof HTMLElement)) return; + if (this._collapsedOptionsPopupOpen) this._hideCollapsedOptionsPopup(); + else this._showCollapsedOptionsPopup(anchor); + }); + histTargets.addEventListener("dp-targets-add-click", (ev) => { + const { buttonEl } = ev.detail || {}; + this._openTargetPicker(buttonEl ?? void 0); + }); + histTargets.addEventListener("dp-collapsed-entity-click", (ev) => { + const { entityId, buttonEl } = ev.detail || {}; + if (!entityId) return; + if (this._collapsedPopupEntityId === entityId) this._hideCollapsedTargetPopup(); + else this._showCollapsedTargetPopup(entityId, buttonEl ?? void 0); + }); + this._shellEl.appendChild(histTargets); + this._historyTargetsComp = histTargets; + this._rowListEl = null; + return histTargets; + } + _mountTargetPickerControl(histTargets) { + const targetControl = document.createElement("ha-target-picker"); + targetControl.slot = "picker"; + targetControl.style.display = "block"; + targetControl.style.width = "100%"; + if (this._hass) targetControl.hass = this._hass; + targetControl.addEventListener("value-changed", (ev) => { + if (!(ev.detail && Object.hasOwn(ev.detail, "value"))) return; + const rawValue = normalizeTargetValue(ev.detail?.value ?? null); + const nextEntityIds = resolveEntityIdsFromTarget(this._hass, rawValue); + if (!nextEntityIds.length) return; + this._addSeriesRows(nextEntityIds); + targetControl.value = {}; + this._saveSessionState(); + this._syncControls(); + this._updateUrl({ push: true }); + this._renderContent(); + }); + histTargets.appendChild(targetControl); + this._targetControl = targetControl; + ensureHaComponents(["ha-target-picker"]).then(() => { + if (!this.isConnected || this._targetControl !== targetControl) return; + if (this._hass) targetControl.hass = this._hass; + targetControl.value = {}; + }); + } + _mountRangeToolbarControl() { + const rangeToolbar = document.createElement("range-toolbar"); + rangeToolbar.slot = "controls"; + rangeToolbar.startTime = this._startTime; + rangeToolbar.endTime = this._endTime; + rangeToolbar.rangeBounds = this._rangeBounds; + rangeToolbar.zoomLevel = this._zoomLevel; + rangeToolbar.dateSnapping = this._dateSnapping; + rangeToolbar.sidebarCollapsed = this._sidebarCollapsed; + rangeToolbar.hass = this._hass ?? null; + rangeToolbar.isLiveEdge = this._isOnLiveEdge(); + rangeToolbar.timelineEvents = this._timelineEvents || []; + rangeToolbar.comparisonPreview = null; + rangeToolbar.zoomRange = this._chartZoomCommittedRange ? { + start: +this._chartZoomCommittedRange.start, + end: +this._chartZoomCommittedRange.end + } : null; + rangeToolbar.zoomWindowRange = null; + rangeToolbar.chartHoverTimeMs = null; + rangeToolbar.chartHoverWindowTimeMs = null; + rangeToolbar.addEventListener("dp-range-commit", (ev) => { + this._applyCommittedRange(ev.detail?.start, ev.detail?.end, { push: ev.detail?.push ?? false }); + }); + rangeToolbar.addEventListener("dp-range-draft", (ev) => { + this._scheduleAutoZoomUpdate(ev.detail?.start, ev.detail?.end); + }); + rangeToolbar.addEventListener("dp-toolbar-sidebar-toggle", () => this._toggleSidebarCollapsed()); + rangeToolbar.addEventListener("dp-zoom-level-change", (ev) => { + const { value } = ev.detail || {}; + if (value && value !== this._zoomLevel) { + this._zoomLevel = value; + this._clearAutoZoomTimer(); + this._resolvedAutoZoomLevel = value === "auto" ? null : this._resolvedAutoZoomLevel; + this._saveSessionState(); + this._updateUrl({ push: false }); + this._syncRangeControl(); + this._saveUserPreferences(); + } + }); + rangeToolbar.addEventListener("dp-snap-change", (ev) => { + const { value } = ev.detail || {}; + if (value && value !== this._dateSnapping) { + this._dateSnapping = value; + this._saveSessionState(); + this._updateUrl({ push: false }); + this._syncRangeControl(); + this._saveUserPreferences(); + } + }); + rangeToolbar.addEventListener("dp-date-picker-change", (ev) => { + this._handleDatePickerChange(ev); + }); + this._shellEl.appendChild(rangeToolbar); + this._rangeToolbarComp = rangeToolbar; + this._dateControl = rangeToolbar; + rangeToolbar.updateComplete.then(() => { + if (!this.isConnected || this._rangeToolbarComp !== rangeToolbar) return; + this._syncControls(); + this._renderContent(); + }); + this._syncSidebarUi(); + } + _mountSidebarOptionsControl() { + if (this._sidebarOptionsEl) { + const sidebarComp = document.createElement("sidebar-options"); + sidebarComp.addEventListener("dp-scope-change", (ev) => { + const { value } = ev.detail || {}; + if (value) this._setDatapointScope(value); + }); + sidebarComp.addEventListener("dp-display-change", (ev) => { + const { kind, value } = ev.detail || {}; + if (!kind) return; + if (kind === "y_axis_mode") this._setChartYAxisMode(String(value || "")); + else this._setChartDatapointDisplayOption(kind, value); + }); + sidebarComp.addEventListener("dp-analysis-change", (ev) => { + const { kind, value } = ev.detail || {}; + if (kind === "anomaly_overlap_mode" && ANALYSIS_ANOMALY_OVERLAP_MODE_OPTIONS.some((o) => o.value === value)) { + if (this._chartAnomalyOverlapMode === value) return; + this._chartAnomalyOverlapMode = value; + this._saveSessionState(); + this._updateUrl({ push: false }); + this._renderSidebarOptions(); + this._renderContent(); + } + }); + sidebarComp.addEventListener("dp-accordion-change", (ev) => { + const { targetsOpen, datapointsOpen, analysisOpen, chartOpen } = ev.detail || {}; + if (typeof targetsOpen === "boolean") this._sidebarAccordionTargetsOpen = targetsOpen; + if (typeof datapointsOpen === "boolean") this._sidebarAccordionDatapointsOpen = datapointsOpen; + if (typeof analysisOpen === "boolean") this._sidebarAccordionAnalysisOpen = analysisOpen; + if (typeof chartOpen === "boolean") this._sidebarAccordionChartOpen = chartOpen; + this._saveSessionState(); + this._updateUrl({ push: false }); + }); + this._sidebarOptionsEl.appendChild(sidebarComp); + this._sidebarOptionsComp = sidebarComp; + } + } + _mountDateWindowDialogControl() { + if (this.shadowRoot) { + const dialogComp = document.createElement("date-window-dialog"); + dialogComp.addEventListener("dp-window-close", () => this._closeDateWindowDialog()); + dialogComp.addEventListener("dp-window-submit", (ev) => { + this._createDateWindowFromDialog(ev.detail || {}); + }); + dialogComp.addEventListener("dp-window-delete", () => this._deleteEditingDateWindow()); + dialogComp.addEventListener("dp-window-shortcut", (ev) => { + if (typeof ev.detail?.direction === "number") this._applyDateWindowShortcut(ev.detail.direction); + }); + dialogComp.addEventListener("dp-window-date-change", (ev) => { + const start = this._parseDateWindowInputValue(ev.detail?.start || ""); + const end = this._parseDateWindowInputValue(ev.detail?.end || ""); + if (start && end && start < end) this._dateWindowDialogDraftRange = { + start, + end + }; + else this._dateWindowDialogDraftRange = null; + }); + this.shadowRoot.appendChild(dialogComp); + this._dateWindowDialogComp = dialogComp; + } + } + _renderTargetRows() { + if (!this._historyTargetsComp) return; + this._historyTargetsComp.rows = this._seriesRows; + this._historyTargetsComp.states = this._hass?.states ?? {}; + this._historyTargetsComp.hass = this._hass ?? null; + this._historyTargetsComp.canShowDeltaAnalysis = !!this._selectedComparisonWindowId; + this._historyTargetsComp.comparisonWindows = this._comparisonWindows; + if (!this._rowListEl) this._rowListEl = this._historyTargetsComp.getRowListEl(); + else { + this._rowListEl.rows = this._seriesRows; + this._rowListEl.states = this._hass?.states ?? {}; + this._rowListEl.hass = this._hass ?? null; + this._rowListEl.canShowDeltaAnalysis = !!this._selectedComparisonWindowId; + this._rowListEl.comparisonWindows = this._comparisonWindows; + } + this._refreshCollapsedTargetPopup(); + } + _addSeriesRows(entityIds) { + const merged = new Map(this._seriesRows.map((row) => [row.entity_id, row])); + normalizeEntityIds(entityIds).forEach((entityId, index) => { + if (merged.has(entityId)) return; + merged.set(entityId, { + entity_id: entityId, + color: this._preferredSeriesColors?.[entityId] && /^#[0-9a-f]{6}$/i.test(this._preferredSeriesColors[entityId]) ? this._preferredSeriesColors[entityId] : COLORS[(merged.size + index) % COLORS.length], + visible: true, + analysis: normalizeHistorySeriesAnalysis(null) + }); + }); + this._seriesRows = [...merged.values()]; + this._syncSeriesState(); + this._renderTargetRows(); + } + _updateSeriesRowColor(index, color) { + if (!Number.isInteger(index) || index === void 0 || index < 0 || index >= this._seriesRows.length) return; + if (!/^#[0-9a-f]{6}$/i.test(color || "")) return; + if (this._seriesRows[index].color === color) return; + this._seriesRows[index] = { + ...this._seriesRows[index], + color + }; + this._saveUserPreferences(); + this._saveSessionState(); + this._updateUrl({ push: false }); + this._renderTargetRows(); + this._renderContent(); + } + _updateSeriesRowVisibility(index, visible) { + if (!Number.isInteger(index) || index === void 0 || index < 0 || index >= this._seriesRows.length) return; + if (this._seriesRows[index].visible === !!visible) return; + this._seriesRows[index] = { + ...this._seriesRows[index], + visible: !!visible + }; + this._saveSessionState(); + this._updateUrl({ push: false }); + this._renderTargetRows(); + this._renderContent(); + } + /** Open (or re-render) the collapsed-sidebar target popup for *entityId*, + * anchored to *anchorEl*. Wires all the same controls as the full sidebar row. */ + _showCollapsedTargetPopup(entityId, anchorEl) { + const popup = this._shellEl?.getTargetPopupEl(); + if (!popup) return; + const index = this._seriesRows.findIndex((r) => r.entity_id === entityId); + if (index < 0) { + this._hideCollapsedTargetPopup(); + return; + } + const row = this._seriesRows[index]; + this._collapsedPopupEntityId = entityId; + this._collapsedPopupAnchorEl = anchorEl ?? null; + popup.innerHTML = ""; + const targetRow = document.createElement("target-row"); + targetRow.hideDragHandle = true; + targetRow.color = row.color; + targetRow.visible = row.visible !== false; + targetRow.analysis = row.analysis || {}; + targetRow.index = index; + targetRow.entityId = row.entity_id; + targetRow.stateObj = this._hass?.states?.[row.entity_id] ?? null; + targetRow.hass = this._hass ?? null; + targetRow.canShowDeltaAnalysis = !!this._selectedComparisonWindowId; + targetRow.comparisonWindows = this._comparisonWindows || []; + targetRow.addEventListener("dp-row-color-change", (ev) => { + this._updateSeriesRowColor(ev.detail?.index, ev.detail?.color); + }); + targetRow.addEventListener("dp-row-visibility-change", (ev) => { + this._updateSeriesRowVisibilityByEntityId(ev.detail?.entityId, ev.detail?.visible); + }); + targetRow.addEventListener("dp-row-toggle-analysis", (ev) => { + this._toggleSeriesAnalysisExpanded(ev.detail?.entityId); + }); + targetRow.addEventListener("dp-row-analysis-change", (ev) => { + this._setSeriesAnalysisOption(ev.detail?.entityId, ev.detail?.key, ev.detail?.value); + }); + targetRow.addEventListener("dp-row-copy-analysis-to-all", (ev) => { + const { entityId: id, analysis } = ev.detail || {}; + this._copyAnalysisToAll(id, analysis); + }); + targetRow.addEventListener("dp-row-remove", (ev) => { + this._hideCollapsedTargetPopup(); + this._removeSeriesRow(ev.detail?.index); + }); + popup.appendChild(targetRow); + popup.removeAttribute("hidden"); + if (!anchorEl) return; + const anchorRect = anchorEl.getBoundingClientRect(); + const popupHeight = popup.offsetHeight; + const top = Math.max(8, Math.min(anchorRect.top, window.innerHeight - popupHeight - 16)); + popup.style.top = `${top}px`; + popup.style.left = `${anchorRect.right + 8}px`; + popup.style.maxHeight = `${window.innerHeight - top - 16}px`; + if (this._collapsedPopupOutsideClickHandler) document.removeEventListener("click", this._collapsedPopupOutsideClickHandler, true); + this._collapsedPopupOutsideClickHandler = (ev) => { + const path = ev.composedPath(); + if (!path.includes(popup) && !path.includes(anchorEl)) this._hideCollapsedTargetPopup(); + }; + document.addEventListener("click", this._collapsedPopupOutsideClickHandler, true); + if (this._collapsedPopupKeyHandler) document.removeEventListener("keydown", this._collapsedPopupKeyHandler); + this._collapsedPopupKeyHandler = (ev) => { + if (ev.key === "Escape") { + this._hideCollapsedTargetPopup(); + anchorEl.focus(); + } + }; + document.addEventListener("keydown", this._collapsedPopupKeyHandler); + } + /** Close the collapsed-sidebar target popup and clean up all listeners. */ + _hideCollapsedTargetPopup() { + const popup = this._shellEl?.getTargetPopupEl(); + if (popup) { + popup.setAttribute("hidden", ""); + popup.innerHTML = ""; + } + if (this._collapsedPopupOutsideClickHandler) { + document.removeEventListener("click", this._collapsedPopupOutsideClickHandler, true); + this._collapsedPopupOutsideClickHandler = null; + } + if (this._collapsedPopupKeyHandler) { + document.removeEventListener("keydown", this._collapsedPopupKeyHandler); + this._collapsedPopupKeyHandler = null; + } + this._collapsedPopupEntityId = null; + this._collapsedPopupAnchorEl = null; + } + /** Re-render the popup in-place after a state change (e.g. analysis toggle). + * Called at the end of _renderTargetRows so the popup stays in sync. */ + _refreshCollapsedTargetPopup() { + if (!this._collapsedPopupEntityId) return; + const row = this._seriesRows.find((r) => r.entity_id === this._collapsedPopupEntityId); + if (!row) { + this._hideCollapsedTargetPopup(); + return; + } + const targetRow = (this._shellEl?.getTargetPopupEl())?.querySelector("target-row"); + if (targetRow) { + targetRow.analysis = row.analysis; + targetRow.color = row.color; + targetRow.visible = row.visible !== false; + targetRow.stateObj = this._hass?.states?.[row.entity_id] ?? null; + targetRow.hass = this._hass ?? null; + } + } + /** Open the collapsed-sidebar options popup, anchored to *anchorEl*. */ + _showCollapsedOptionsPopup(anchorEl) { + const popup = this._shellEl?.getOptionsPopupEl(); + if (!popup) return; + let menu = popup.querySelector("collapsed-options-menu"); + if (!menu) { + menu = document.createElement("collapsed-options-menu"); + menu.addEventListener("dp-scope-change", (ev) => { + const { value } = ev.detail || {}; + if (value) this._setDatapointScope(value); + }); + menu.addEventListener("dp-display-change", (ev) => { + const { kind, value } = ev.detail || {}; + if (!kind) return; + if (kind === "y_axis_mode") this._setChartYAxisMode(String(value || "")); + else this._setChartDatapointDisplayOption(kind, value); + }); + menu.addEventListener("dp-analysis-change", (ev) => { + const { kind, value } = ev.detail || {}; + if (kind === "anomaly_overlap_mode" && value !== this._chartAnomalyOverlapMode) { + this._chartAnomalyOverlapMode = value; + this._saveSessionState(); + this._updateUrl({ push: false }); + this._renderSidebarOptions(); + this._renderContent(); + } + }); + popup.appendChild(menu); + } + this._syncCollapsedOptionsMenu(menu); + this._collapsedOptionsPopupOpen = true; + this._collapsedOptionsAnchorEl = anchorEl; + popup.removeAttribute("hidden"); + const anchorRect = anchorEl.getBoundingClientRect(); + const popupHeight = popup.offsetHeight; + const top = Math.max(8, Math.min(anchorRect.top, window.innerHeight - popupHeight - 16)); + popup.style.top = `${top}px`; + popup.style.left = `${anchorRect.right + 8}px`; + if (this._collapsedOptionsOutsideClickHandler) document.removeEventListener("click", this._collapsedOptionsOutsideClickHandler, true); + this._collapsedOptionsOutsideClickHandler = (ev) => { + const path = ev.composedPath(); + if (!path.includes(popup) && !path.includes(anchorEl)) this._hideCollapsedOptionsPopup(); + }; + document.addEventListener("click", this._collapsedOptionsOutsideClickHandler, true); + if (this._collapsedOptionsKeyHandler) document.removeEventListener("keydown", this._collapsedOptionsKeyHandler); + this._collapsedOptionsKeyHandler = (ev) => { + if (ev.key === "Escape") { + this._hideCollapsedOptionsPopup(); + anchorEl.focus(); + } + }; + document.addEventListener("keydown", this._collapsedOptionsKeyHandler); + } + /** Close the collapsed-sidebar options popup and clean up all listeners. */ + _hideCollapsedOptionsPopup() { + const popup = this._shellEl?.getOptionsPopupEl(); + if (popup) popup.setAttribute("hidden", ""); + if (this._collapsedOptionsOutsideClickHandler) { + document.removeEventListener("click", this._collapsedOptionsOutsideClickHandler, true); + this._collapsedOptionsOutsideClickHandler = null; + } + if (this._collapsedOptionsKeyHandler) { + document.removeEventListener("keydown", this._collapsedOptionsKeyHandler); + this._collapsedOptionsKeyHandler = null; + } + this._collapsedOptionsPopupOpen = false; + this._collapsedOptionsAnchorEl = null; + } + /** Sync option props on the menu element — called from _renderSidebarOptions. */ + _refreshCollapsedOptionsPopup() { + if (!this._collapsedOptionsPopupOpen) return; + const menu = (this._shellEl?.getOptionsPopupEl())?.querySelector("collapsed-options-menu"); + if (menu) this._syncCollapsedOptionsMenu(menu); + } + /** Write all current option values onto a collapsed-options-menu element. */ + _syncCollapsedOptionsMenu(menu) { + let yAxisMode; + if (this._splitChartView) yAxisMode = "split"; + else if (this._delinkChartYAxis) yAxisMode = "unique"; + else yAxisMode = "combined"; + menu.datapointScope = this._datapointScope; + menu.showIcons = this._showChartDatapointIcons; + menu.showLines = this._showChartDatapointLines; + menu.showTooltips = this._showChartTooltips; + menu.showHoverGuides = this._showChartEmphasizedHoverGuides; + menu.hoverSnapMode = this._chartHoverSnapMode; + menu.showCorrelatedAnomalies = this._showCorrelatedAnomalies; + menu.showDataGaps = this._showDataGaps; + menu.dataGapThreshold = this._dataGapThreshold; + menu.yAxisMode = yAxisMode; + menu.anomalyOverlapMode = this._chartAnomalyOverlapMode; + menu.anyAnomaliesEnabled = (this._seriesRows ?? []).some((r) => r.analysis?.show_anomalies === true); + } + _updateSeriesRowVisibilityByEntityId(entityId, visible) { + const normalizedEntityId = String(entityId || "").trim(); + if (!normalizedEntityId) return; + const index = this._seriesRows.findIndex((row) => row.entity_id === normalizedEntityId); + if (index === -1) return; + this._updateSeriesRowVisibility(index, visible); + } + _toggleSeriesAnalysisExpanded(entityId) { + const normalizedEntityId = String(entityId || "").trim(); + if (!normalizedEntityId) return; + const index = this._seriesRows.findIndex((row) => row.entity_id === normalizedEntityId); + if (index === -1) return; + const row = this._seriesRows[index]; + const currentAnalysis = normalizeHistorySeriesAnalysis(row.analysis); + const nextAnalysis = normalizeHistorySeriesAnalysis({ + ...row.analysis, + expanded: !currentAnalysis.expanded + }); + this._seriesRows[index] = { + ...row, + analysis: nextAnalysis + }; + this._saveSessionState(); + this._updateUrl({ push: false }); + this._renderTargetRows(); + } + _setSeriesAnalysisOption(entityId, key, value) { + const normalizedEntityId = String(entityId || "").trim(); + const normalizedKey = String(key || "").trim(); + if (!normalizedEntityId || !normalizedKey) return; + if (normalizedKey === "anomaly_comparison_window_id" && value === "__add_new__") { + this._pendingAnomalyComparisonWindowEntityId = normalizedEntityId; + this._openDateWindowDialog(); + return; + } + const index = this._seriesRows.findIndex((row) => row.entity_id === normalizedEntityId); + if (index === -1) return; + const row = this._seriesRows[index]; + const analysis = normalizeHistorySeriesAnalysis(row.analysis); + let nextKey = normalizedKey; + let nextValue = value; + if (nextKey.startsWith("anomaly_method_toggle_")) { + const method = nextKey.slice(22); + const currentMethods = analysis.anomaly_methods; + const nextMethods = nextValue === true ? [...new Set([...currentMethods, method])] : currentMethods.filter((m) => m !== method); + nextKey = "anomaly_methods"; + nextValue = nextMethods; + } + const nextSource = { + ...analysis, + [nextKey]: nextValue + }; + if (nextKey === "show_trend_lines" && nextValue !== true) nextSource.show_trend_crosshairs = false; + if (nextKey === "show_threshold_analysis" && nextValue !== true) nextSource.show_threshold_shading = false; + if (nextKey === "show_delta_analysis" && nextValue !== true) { + nextSource.show_delta_tooltip = true; + nextSource.show_delta_lines = false; + } + if (nextKey === "show_anomalies" && nextValue === true && (!Array.isArray(analysis.anomaly_methods) || analysis.anomaly_methods.length === 0)) nextSource.anomaly_methods = ["trend_residual"]; + const nextAnalysis = normalizeHistorySeriesAnalysis({ + ...nextSource, + expanded: true + }); + if (JSON.stringify(nextAnalysis) === JSON.stringify(analysis)) return; + this._seriesRows[index] = { + ...row, + analysis: nextAnalysis + }; + this._saveSessionState(); + this._updateUrl({ push: false }); + this._renderTargetRows(); + this._renderSidebarOptions(); + this._renderContent(); + } + _copyAnalysisToAll(sourceEntityId, sourceAnalysis) { + const normalizedEntityId = String(sourceEntityId || "").trim(); + if (!normalizedEntityId || !sourceAnalysis) return; + let changed = false; + this._seriesRows = this._seriesRows.map((row) => { + if (row.entity_id === normalizedEntityId) return row; + const currentAnalysis = normalizeHistorySeriesAnalysis(row.analysis); + const nextAnalysis = normalizeHistorySeriesAnalysis({ + ...sourceAnalysis, + expanded: currentAnalysis.expanded, + anomaly_comparison_window_id: currentAnalysis.anomaly_comparison_window_id + }); + if (JSON.stringify(nextAnalysis) === JSON.stringify(currentAnalysis)) return row; + changed = true; + return { + ...row, + analysis: nextAnalysis + }; + }); + if (!changed) return; + this._saveSessionState(); + this._updateUrl({ push: false }); + this._renderTargetRows(); + this._renderContent(); + } + _removeSeriesRow(index) { + if (!Number.isInteger(index) || index === void 0 || index < 0 || index >= this._seriesRows.length) return; + this._seriesRows = this._seriesRows.filter((_row, rowIndex) => rowIndex !== index); + this._syncSeriesState(); + this._saveSessionState(); + this._renderTargetRows(); + this._syncControls(); + this._updateUrl({ push: true }); + this._renderContent(); + } + _clearAutoZoomTimer() { + if (this._autoZoomTimer) { + window.clearTimeout(this._autoZoomTimer); + this._autoZoomTimer = null; + } + } + _toggleDatePickerMenu(force = false) { + if (!force) this._rangeToolbarComp?.closeMenus(); + } + _togglePageMenu(force = !this._pageMenuOpen) { + this._pageMenuOpen = !!force; + if (!force) this._shellEl?.closePageMenu(); + } + _handleWindowPointerDown() {} + _syncOptionsMenu() { + this._rangeToolbarComp?.syncOptionsLabels(); + } + _handleDatePickerChange(ev) { + const { start, end } = extractRangeValue(ev); + if (!start || !end || start >= end) return; + if (ev.type === "change") this._toggleDatePickerMenu(false); + this._applyCommittedRange(start, end, { push: true }); + } + async _downloadSpreadsheet() { + if (this._exportBusy || !this._hass || !this._startTime || !this._endTime) return; + this._togglePageMenu(false); + await this._context.persistence.downloadSpreadsheet({ + entityIds: this._entities, + startTime: this._startTime, + endTime: this._endTime, + datapointScope: this._datapointScope, + onError: (error) => { + logger$1.error("[hass-datapoints panel] spreadsheet export:failed", error); + } + }); + } + async _loadSavedPageIndicator() { + await this._context.fetch.loadSavedPageIndicator({ + savedPageKey: PANEL_HISTORY_SAVED_PAGE_KEY, + fallbackValue: null, + onSuccess: () => { + this._syncSavedPageMenuItems(); + }, + onError: () => {} + }); + } + _syncSavedPageMenuItems() { + if (this._shellEl) this._shellEl.hasSavedState = this._hasSavedPage; + } + async _savePageState() { + if (this._savePageBusy || !this._hass) return; + this._togglePageMenu(false); + await this._context.persistence.savePageState({ + savedPageKey: PANEL_HISTORY_SAVED_PAGE_KEY, + state: buildHistoryPageSessionState(this), + onSuccess: () => { + this._hasSavedPage = true; + this._syncSavedPageMenuItems(); + }, + onError: (err) => { + logger$1.error("[hass-datapoints panel] save page state failed:", err); + } + }); + } + async _restorePageState() { + if (!this._hass) return; + this._togglePageMenu(false); + await this._context.persistence.restorePageState({ + savedPageKey: PANEL_HISTORY_SAVED_PAGE_KEY, + fallbackValue: null, + onSuccess: (saved) => { + try { + window.sessionStorage.setItem(`${DOMAIN}:panel_history_session`, JSON.stringify(saved)); + } catch {} + const baseUrl = window.location.pathname; + window.history.replaceState(null, "", baseUrl); + window.location.reload(); + }, + onError: (err) => { + logger$1.error("[hass-datapoints panel] restore page state failed:", err); + } + }); + } + async _clearSavedPageState() { + if (!this._hass) return; + this._togglePageMenu(false); + await this._context.persistence.clearSavedPageState({ + savedPageKey: PANEL_HISTORY_SAVED_PAGE_KEY, + onSuccess: () => { + this._hasSavedPage = false; + this._syncSavedPageMenuItems(); + }, + onError: (err) => { + logger$1.error("[hass-datapoints panel] clear saved page state failed:", err); + } + }); + } + _getEffectiveZoomLevel() { + if (this._zoomLevel !== "auto") return this._zoomLevel; + if (!this._resolvedAutoZoomLevel) { + const referenceSpanMs = Math.max((this._endTime?.getTime() || Date.now()) - (this._startTime?.getTime() || Date.now() - 2592e6), RANGE_SLIDER_MIN_SPAN_MS); + this._resolvedAutoZoomLevel = this._computeZoomLevelForSpan(referenceSpanMs); + } + return this._resolvedAutoZoomLevel; + } + _getZoomConfig() { + return RANGE_ZOOM_CONFIGS[this._getEffectiveZoomLevel()] || RANGE_ZOOM_CONFIGS.month_short; + } + _computeZoomLevelForSpan(spanMs) { + const normalizedSpanMs = Math.max(spanMs, RANGE_SLIDER_MIN_SPAN_MS); + if (normalizedSpanMs >= 180 * 864e5) return "quarterly"; + if (normalizedSpanMs >= 120 * 864e5) return "month_compressed"; + if (normalizedSpanMs >= 60 * 864e5) return "month_short"; + if (normalizedSpanMs >= 21 * 864e5) return "month_expanded"; + if (normalizedSpanMs >= 7 * 864e5) return "week_compressed"; + if (normalizedSpanMs >= 2 * 864e5) return "week_expanded"; + return "day"; + } + _getEffectiveSnapUnit() { + if (this._dateSnapping !== "auto") return this._dateSnapping; + switch (this._getEffectiveZoomLevel()) { + case "quarterly": + case "month_compressed": return "month"; + case "month_short": + case "month_expanded": + case "week_compressed": return "week"; + case "week_expanded": return "day"; + case "day": return "hour"; + default: return "day"; + } + } + _getSnapSpanMs(reference = this._startTime || /* @__PURE__ */ new Date()) { + const snapUnit = this._getEffectiveSnapUnit(); + const start = startOfUnit(reference, snapUnit); + const end = endOfUnit(reference, snapUnit); + return Math.max(SECOND_MS, end.getTime() - start.getTime()); + } + _deriveRangeBounds() { + const config = this._getZoomConfig(); + const startMs = this._startTime?.getTime() || Date.now() - 24 * 36e5; + const endMs = this._endTime?.getTime() || Date.now(); + const historyStartMs = this._historyStartTime?.getTime(); + const historyEndMs = this._historyEndTime?.getTime(); + const maxLookAheadMs = addUnit(/* @__PURE__ */ new Date(), "month", 3).getTime(); + const naturalMin = startOfUnit(new Date(historyStartMs ?? startMs), config.boundsUnit).getTime(); + const paddedMin = startOfUnit(/* @__PURE__ */ new Date(startMs - config.baselineMs * .3), config.boundsUnit).getTime(); + const min = Math.min(naturalMin, paddedMin); + const futureReference = addUnit(new Date(historyEndMs ?? endMs), "year", 1).getTime(); + const maxReference = Math.min(maxLookAheadMs, Math.max(futureReference, endMs, startMs + this._getSnapSpanMs(this._startTime || /* @__PURE__ */ new Date()))); + const max = endOfUnit(new Date(maxReference), config.boundsUnit).getTime(); + return { + min, + max: Math.max(max, min + SECOND_MS), + config + }; + } + _syncRangeControl() { + if (!this._rangeToolbarComp) return; + this._rangeBounds = this._deriveRangeBounds(); + this._ensureTimelineEvents(); + this._rangeToolbarComp.startTime = this._startTime ? new Date(this._startTime) : null; + this._rangeToolbarComp.endTime = this._endTime ? new Date(this._endTime) : null; + this._rangeToolbarComp.rangeBounds = this._rangeBounds; + this._rangeToolbarComp.zoomLevel = this._getEffectiveZoomLevel(); + this._rangeToolbarComp.dateSnapping = this._dateSnapping; + this._rangeToolbarComp.isLiveEdge = this._isOnLiveEdge(); + this._rangeToolbarComp.timelineEvents = this._timelineEvents || []; + this._updateComparisonRangePreview(); + this._updateChartHoverIndicator(); + this._updateChartZoomHighlight(); + this._syncMobileDateInputs(); + } + _updateComparisonRangePreview() { + if (!this._rangeToolbarComp) return; + const comparisonWindow = this._getActiveComparisonWindow(); + if (!this._rangeBounds || !comparisonWindow) { + this._rangeToolbarComp.comparisonPreview = null; + this._updateZoomWindowHighlight(); + return; + } + const startMs = new Date(comparisonWindow.start_time).getTime(); + const endMs = new Date(comparisonWindow.end_time).getTime(); + if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || startMs >= endMs) { + this._rangeToolbarComp.comparisonPreview = null; + this._updateZoomWindowHighlight(); + return; + } + this._rangeToolbarComp.comparisonPreview = { + start: startMs, + end: endMs + }; + this._updateZoomWindowHighlight(); + } + _handleChartHover(ev) { + this._chartHoverTimeMs = ev?.detail?.timeMs ?? null; + this._updateChartHoverIndicator(); + } + _handleChartZoom(ev) { + const detail = ev.detail; + const start = Number.isFinite(detail?.startTime) ? detail?.startTime ?? null : null; + const end = Number.isFinite(detail?.endTime) ? detail?.endTime ?? null : null; + const isPreview = !!detail?.preview; + const source = detail?.source || "select"; + const nextRange = start != null && end != null && start < end ? { + start, + end + } : null; + if (isPreview) this._chartZoomRange = nextRange; + else { + this._chartZoomRange = nextRange; + this._chartZoomCommittedRange = nextRange ? { ...nextRange } : null; + if (this._hoveredComparisonWindowId) this._hoveredComparisonWindowId = null; + if (source === "scroll") this._scheduleChartZoomStateCommit(); + else { + this._saveSessionState(); + this._updateUrl({ push: false }); + this._syncListZoomState(); + } + } + this._updateChartZoomHighlight(); + if (!isPreview && source !== "scroll") this._renderComparisonTabs(); + if (!nextRange) this._rangeToolbarComp?.revealSelection?.(); + } + _scheduleChartZoomStateCommit() { + if (this._chartZoomStateCommitTimer) window.clearTimeout(this._chartZoomStateCommitTimer); + this._chartZoomStateCommitTimer = window.setTimeout(() => { + this._chartZoomStateCommitTimer = null; + this._saveSessionState(); + this._updateUrl({ push: false }); + this._syncListZoomState(); + }, 180); + } + _syncListZoomState() { + if (!this._listEl) return; + const listConfig = { + entities: this._entities, + datapoint_scope: this._datapointScope, + hours_to_show: this._hours, + start_time: this._startTime?.toISOString(), + end_time: this._endTime?.toISOString(), + zoom_start_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.start).toISOString() : null, + zoom_end_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.end).toISOString() : null, + page_size: 15, + show_entities: true, + show_actions: true, + show_search: true, + hidden_event_ids: this._hiddenEventIds + }; + const nextListConfigKey = JSON.stringify(listConfig); + if (this._listConfigKey !== nextListConfigKey) { + this._listEl.setConfig(listConfig); + this._listConfigKey = nextListConfigKey; + } + this._listEl.hass = this._hass; + } + _handleRecordsSearch(ev) { + const nextQuery = String(ev?.detail?.query || "").trim().toLowerCase(); + if (nextQuery === this._recordsSearchQuery) return; + this._recordsSearchQuery = nextQuery; + this._renderContent(); + } + _handleToggleEventVisibility(ev) { + const eventId = ev?.detail?.eventId; + if (!eventId) return; + if (this._hiddenEventIds.includes(eventId)) this._hiddenEventIds = this._hiddenEventIds.filter((id) => id !== eventId); + else this._hiddenEventIds = [...this._hiddenEventIds, eventId]; + this._renderContent(); + } + _handleHoverEventRecord(ev) { + const eventId = String(ev?.detail?.eventId || "").trim(); + if (!eventId) return; + const hovered = ev?.detail?.hovered === true; + const alreadyHovered = this._hoveredEventIds.includes(eventId); + if (hovered && alreadyHovered) return; + if (!hovered && !alreadyHovered) return; + this._hoveredEventIds = hovered ? [eventId] : []; + this._renderContent(); + } + _handleToggleSeriesVisibility(ev) { + const entityId = String(ev?.detail?.entityId || "").trim(); + const visible = ev?.detail?.visible; + if (!entityId || typeof visible !== "boolean") return; + const index = this._seriesRows.findIndex((row) => row.entity_id === entityId); + if (index === -1 || this._seriesRows[index].visible === visible) return; + this._seriesRows[index] = { + ...this._seriesRows[index], + visible + }; + this._saveSessionState(); + this._updateUrl({ push: false }); + this._renderTargetRows(); + this._renderContent(); + } + _updateChartHoverIndicator() { + if (!this._rangeToolbarComp) return; + if (!this._rangeBounds || this._chartHoverTimeMs == null) { + this._rangeToolbarComp.chartHoverTimeMs = null; + this._rangeToolbarComp.chartHoverWindowTimeMs = null; + return; + } + this._rangeToolbarComp.chartHoverTimeMs = this._chartHoverTimeMs; + const activeWindow = this._getActiveComparisonWindow(); + if (activeWindow && this._startTime) { + const timeOffsetMs = new Date(activeWindow.start_time).getTime() - this._startTime.getTime(); + this._rangeToolbarComp.chartHoverWindowTimeMs = this._chartHoverTimeMs + timeOffsetMs; + } else this._rangeToolbarComp.chartHoverWindowTimeMs = null; + } + _updateChartZoomHighlight() { + if (!this._rangeToolbarComp) return; + const highlightRange = this._chartZoomRange || this._chartZoomCommittedRange; + const nextZoomRange = this._rangeBounds && highlightRange ? { + start: +highlightRange.start, + end: +highlightRange.end + } : null; + const nextZoomWindowRange = this._getZoomWindowHighlightRange(); + this._rangeToolbarComp.syncZoomHighlights(nextZoomRange, nextZoomWindowRange); + } + _updateZoomWindowHighlight() { + if (!this._rangeToolbarComp) return; + this._rangeToolbarComp.syncZoomHighlights(this._rangeBounds && (this._chartZoomRange || this._chartZoomCommittedRange) ? { + start: +(this._chartZoomRange || this._chartZoomCommittedRange).start, + end: +(this._chartZoomRange || this._chartZoomCommittedRange).end + } : null, this._getZoomWindowHighlightRange()); + } + _getZoomWindowHighlightRange() { + const activeWindow = this._getActiveComparisonWindow(); + const zoomRange = this._chartZoomRange || this._chartZoomCommittedRange; + if (!this._rangeBounds || !activeWindow || !zoomRange || !this._startTime) return null; + const windowStartMs = new Date(activeWindow.start_time).getTime(); + const windowEndMs = new Date(activeWindow.end_time).getTime(); + if (!Number.isFinite(windowStartMs) || !Number.isFinite(windowEndMs) || windowStartMs >= windowEndMs) return null; + const timeOffsetMs = windowStartMs - this._startTime.getTime(); + const zoomStartMs = +zoomRange.start + timeOffsetMs; + const zoomEndMs = +zoomRange.end + timeOffsetMs; + const intersectStart = Math.max(windowStartMs, zoomStartMs); + const intersectEnd = Math.min(windowEndMs, zoomEndMs); + if (intersectStart >= intersectEnd) return null; + return { + start: intersectStart, + end: intersectEnd + }; + } + _scheduleAutoZoomUpdate(draftStart, draftEnd) { + if (this._zoomLevel !== "auto" || !this._rangeBounds) return; + const start = draftStart || this._startTime; + const end = draftEnd || this._endTime; + if (!start || !end || start >= end) return; + const currentLevel = this._getEffectiveZoomLevel(); + const selectionSpanMs = Math.max(end.getTime() - start.getTime(), RANGE_SLIDER_MIN_SPAN_MS); + const paddedSelectionSpanMs = Math.max(selectionSpanMs * (1 + RANGE_AUTO_ZOOM_SELECTION_PADDING_RATIO), RANGE_SLIDER_MIN_SPAN_MS); + if (this._computeZoomLevelForSpan(paddedSelectionSpanMs) === currentLevel) { + this._clearAutoZoomTimer(); + return; + } + this._clearAutoZoomTimer(); + this._autoZoomTimer = window.setTimeout(() => { + this._autoZoomTimer = null; + const latestStart = draftStart || this._startTime; + const latestEnd = draftEnd || this._endTime; + if (!latestStart || !latestEnd || latestStart >= latestEnd || this._zoomLevel !== "auto" || !this._rangeBounds) return; + const latestLevel = this._getEffectiveZoomLevel(); + const latestSelectionSpanMs = Math.max(latestEnd.getTime() - latestStart.getTime(), RANGE_SLIDER_MIN_SPAN_MS); + const latestPaddedSelectionSpanMs = Math.max(latestSelectionSpanMs * (1 + RANGE_AUTO_ZOOM_SELECTION_PADDING_RATIO), RANGE_SLIDER_MIN_SPAN_MS); + const latestCandidateLevel = this._computeZoomLevelForSpan(latestPaddedSelectionSpanMs); + if (latestCandidateLevel === latestLevel) return; + this._resolvedAutoZoomLevel = latestCandidateLevel; + this._syncRangeControl(); + }, RANGE_AUTO_ZOOM_DEBOUNCE_MS); + } + /** Returns true when the committed end time is at or very near "now", + * meaning new annotations should cause the visible range to advance. */ + _isOnLiveEdge() { + if (!this._endTime) return false; + return this._endTime.getTime() >= Date.now() - 2 * MINUTE_MS; + } + /** Toggle the live-edge indicator on the end handle. */ + _syncLiveEdgeHandle() { + if (!this._rangeToolbarComp) return; + this._rangeToolbarComp.isLiveEdge = this._isOnLiveEdge(); + } + /** Called whenever a new annotation is recorded (HA event or window event). + * If the current range is on the live edge, advance the end time to now + * so the chart immediately shows the new data point. */ + _handleEventRecorded() { + if (!this._isOnLiveEdge() || !this._startTime) return; + this._applyCommittedRange(this._startTime, /* @__PURE__ */ new Date(), { push: false }); + } + _applyCommittedRange(start, end, { push = false } = {}) { + if (!start || !end || start >= end) return; + const nextStart = new Date(start); + const nextEnd = new Date(end); + const didChange = !this._startTime || !this._endTime || this._startTime.getTime() !== nextStart.getTime() || this._endTime.getTime() !== nextEnd.getTime(); + this._startTime = nextStart; + this._endTime = nextEnd; + this._hours = Math.max(1, Math.round((nextEnd.getTime() - nextStart.getTime()) / HOUR_MS)); + this._syncLiveEdgeHandle(); + this._scheduleAutoZoomUpdate(void 0, void 0); + this._syncControls(); + this._chartEl?.setExternalZoomRange?.(this._chartZoomCommittedRange); + if (!didChange) return; + this._saveSessionState(); + this._updateUrl({ push }); + this._renderContent(); + } + _commitRangeSelection({ push = false } = {}) { + if (this._rangeCommitTimer) { + window.clearTimeout(this._rangeCommitTimer); + this._rangeCommitTimer = null; + } + if (!this._draftStartTime || !this._draftEndTime || this._draftStartTime >= this._draftEndTime) return; + this._applyCommittedRange(this._draftStartTime, this._draftEndTime, { push }); + } + _updateUrl({ push = false } = {}) { + this._context.navigation.updateUrl({ + entities: this._entities, + datapointScope: this._datapointScope, + startTime: this._startTime, + endTime: this._endTime, + hours: this._hours, + committedZoomRange: this._chartZoomCommittedRange, + comparisonWindows: this._comparisonWindows, + pageState: buildHistoryPageSessionState(this), + seriesRows: this._seriesRows, + seriesColorQueryKey: (entityId) => this._seriesColorQueryKey(entityId), + push + }); + } + _renderComparisonTabs() { + const result = this._context.orchestration.renderComparisonTabs({ + chartEl: this._chartEl, + comparisonWindows: Array.isArray(this._comparisonWindows) ? this._comparisonWindows : [], + selectedComparisonWindowId: this._selectedComparisonWindowId, + hoveredComparisonWindowId: this._hoveredComparisonWindowId, + startTime: this._startTime, + endTime: this._endTime, + loadingComparisonWindowIds: [...this._loadingComparisonWindowIds], + comparisonTabRailComp: this._comparisonTabRailComp, + comparisonTabsHostEl: this._comparisonTabsHostEl, + formatComparisonLabel: (startTime, endTime) => this._formatComparisonLabel(startTime, endTime), + onActivate: (tabId) => { + this._handleComparisonTabActivate(tabId); + }, + onHover: (tabId) => { + this._handleComparisonTabHover(tabId); + }, + onLeave: (tabId) => { + this._handleComparisonTabLeave(tabId); + }, + onEdit: (tabId) => { + const win = this._comparisonWindows.find((entry) => entry.id === tabId); + if (win) this._openDateWindowDialog(win); + }, + onDelete: (tabId) => { + if (tabId) this._deleteDateWindow(tabId); + }, + onAdd: () => { + this._openDateWindowDialog(); + } + }); + this._comparisonTabRailComp = result.comparisonTabRailComp; + this._comparisonTabsHostEl = result.comparisonTabsHostEl; + } + _updateComparisonTabsOverflow() { + this._context.orchestration.updateComparisonTabsOverflow(this._chartEl); + } + _renderContent() { + const content = this._contentHostEl; + if (!content) return; + if (!this._entities.length) { + if (this._contentKey === "__empty__") return; + this._chartHoverTimeMs = null; + this._updateChartHoverIndicator(); + this._chartZoomRange = null; + this._chartZoomCommittedRange = null; + this._updateChartZoomHighlight(); + content.innerHTML = ` Select one or more entities to inspect annotated history. `; - this._contentKey = "__empty__"; - this._chartEl = null; - this._historyChartMol = null; - this._listEl = null; - this._chartConfigKey = ""; - this._listConfigKey = ""; - return; - } - const contentKey = JSON.stringify({ - entities: this._entities, - series_entity_ids: this._seriesRows.map( - (row) => row.entity_id - ), - datapoint_scope: this._datapointScope, - start: this._startTime?.toISOString() || null, - end: this._endTime?.toISOString() || null, - hours: this._hours - }); - const showRecordsPanel = this._datapointScope !== "hidden"; - const chartMounted = !!(this._chartEl && this._chartEl.isConnected && content.contains(this._chartEl)); - const listMounted = !showRecordsPanel || !!(this._listEl && this._listEl.isConnected && content.contains(this._listEl)); - if (this._contentKey !== contentKey || !chartMounted || !listMounted) { - this._chartHoverTimeMs = null; - this._updateChartHoverIndicator(); - this._chartZoomRange = null; - this._updateChartZoomHighlight(); - this._hoveredEventIds = []; - this._recordsSearchQuery = ""; - content.innerHTML = ` + this._contentKey = "__empty__"; + this._chartEl = null; + this._historyChartMol = null; + this._listEl = null; + this._chartConfigKey = ""; + this._listConfigKey = ""; + return; + } + const contentKey = JSON.stringify({ + entities: this._entities, + series_entity_ids: this._seriesRows.map((row) => row.entity_id), + datapoint_scope: this._datapointScope, + start: this._startTime?.toISOString() || null, + end: this._endTime?.toISOString() || null, + hours: this._hours + }); + const showRecordsPanel = this._datapointScope !== "hidden"; + const chartMounted = !!(this._chartEl && this._chartEl.isConnected && content.contains(this._chartEl)); + const listMounted = !showRecordsPanel || !!(this._listEl && this._listEl.isConnected && content.contains(this._listEl)); + if (this._contentKey !== contentKey || !chartMounted || !listMounted) { + this._chartHoverTimeMs = null; + this._updateChartHoverIndicator(); + this._chartZoomRange = null; + this._updateChartZoomHighlight(); + this._hoveredEventIds = []; + this._recordsSearchQuery = ""; + content.innerHTML = `
`; - const chartConfig2 = { - entities: this._entities, - series_settings: this._seriesRows.map((row) => ({ - ...row, - analysis: { - ...row.analysis || {}, - anomaly_overlap_mode: this._chartAnomalyOverlapMode - } - })), - datapoint_scope: this._datapointScope, - show_event_markers: this._showChartDatapointIcons, - show_event_lines: this._showChartDatapointLines, - show_tooltips: this._showChartTooltips, - emphasize_hover_guides: this._showChartEmphasizedHoverGuides, - hover_snap_mode: this._chartHoverSnapMode, - show_correlated_anomalies: this._showCorrelatedAnomalies, - anomaly_overlap_mode: this._chartAnomalyOverlapMode, - delink_y_axis: this._delinkChartYAxis, - split_view: this._splitChartView, - show_data_gaps: this._showDataGaps, - data_gap_threshold: this._dataGapThreshold, - hours_to_show: this._hours, - start_time: this._startTime?.toISOString(), - end_time: this._endTime?.toISOString(), - zoom_start_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.start).toISOString() : null, - zoom_end_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.end).toISOString() : null, - message_filter: this._recordsSearchQuery || "", - hidden_event_ids: this._hiddenEventIds, - hovered_event_ids: this._hoveredEventIds, - comparison_windows: this._getPreviewComparisonWindows(), - preload_comparison_windows: this._getPreloadComparisonWindows(), - comparison_preview_overlay: this._getComparisonPreviewOverlay(), - comparison_hover_active: !!this._hoveredComparisonWindowId, - selected_comparison_window_id: this._selectedComparisonWindowId, - hovered_comparison_window_id: this._hoveredComparisonWindowId - }; - const chart = document.createElement( - "hass-datapoints-history-card" - ); - chart.setConfig(chartConfig2); - content.querySelector("#chart-card-host").appendChild( - chart - ); - const historyChartMol = { - _configKey: JSON.stringify(chartConfig2), - chartEl: chart - }; - this._historyChartMol = historyChartMol; - if (showRecordsPanel) { - const listConfig = { - entities: this._entities, - datapoint_scope: this._datapointScope, - hours_to_show: this._hours, - start_time: this._startTime?.toISOString(), - end_time: this._endTime?.toISOString(), - zoom_start_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.start).toISOString() : null, - zoom_end_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.end).toISOString() : null, - page_size: 15, - show_entities: true, - show_actions: true, - show_search: true, - hidden_event_ids: this._hiddenEventIds - }; - const list = document.createElement( - "hass-datapoints-list-card" - ); - list.setConfig(listConfig); - content.querySelector("#list-host").appendChild(list); - this._listEl = list; - } else { - this._listEl = null; - } - const resizablePanes = content.querySelector( - "#content-resizable-panes" - ); - this._contentSplitterEl = resizablePanes; - if (resizablePanes) { - resizablePanes.ratio = this._contentSplitRatio; - resizablePanes.min = 0.2; - resizablePanes.max = 0.8; - resizablePanes.addEventListener( - "dp-panes-resize", - (ev) => { - this._contentSplitRatio = ev.detail?.ratio ?? this._contentSplitRatio; - this._requestChartResizeRedraw(); - if (ev.detail?.committed) { - this._saveSessionState(); - window.requestAnimationFrame(() => this._syncRangeControl()); - } - } - ); - } - this._chartEl = chart; - this._historyChartMol = historyChartMol; - this._contentKey = contentKey; - this._chartConfigKey = ""; - this._listConfigKey = ""; - } - content.classList.toggle("datapoints-hidden", !showRecordsPanel); - const resizablePanesEl = content.querySelector( - "#content-resizable-panes" - ); - if (resizablePanesEl) { - resizablePanesEl.secondHidden = !showRecordsPanel; - } - this._applyContentSplitLayout(); - this._renderComparisonTabs(); - const chartConfig = { - entities: this._entities, - series_settings: this._seriesRows.map((row) => ({ - ...row, - analysis: { - ...row.analysis || {}, - anomaly_overlap_mode: this._chartAnomalyOverlapMode - } - })), - datapoint_scope: this._datapointScope, - show_event_markers: this._showChartDatapointIcons, - show_event_lines: this._showChartDatapointLines, - show_tooltips: this._showChartTooltips, - emphasize_hover_guides: this._showChartEmphasizedHoverGuides, - hover_snap_mode: this._chartHoverSnapMode, - show_correlated_anomalies: this._showCorrelatedAnomalies, - anomaly_overlap_mode: this._chartAnomalyOverlapMode, - delink_y_axis: this._delinkChartYAxis, - split_view: this._splitChartView, - show_data_gaps: this._showDataGaps, - data_gap_threshold: this._dataGapThreshold, - hours_to_show: this._hours, - start_time: this._startTime?.toISOString(), - end_time: this._endTime?.toISOString(), - zoom_start_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.start).toISOString() : null, - zoom_end_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.end).toISOString() : null, - message_filter: this._recordsSearchQuery || "", - hidden_event_ids: this._hiddenEventIds, - hovered_event_ids: this._hoveredEventIds, - comparison_windows: this._getPreviewComparisonWindows(), - preload_comparison_windows: this._getPreloadComparisonWindows(), - comparison_preview_overlay: this._getComparisonPreviewOverlay(), - comparison_hover_active: !!this._hoveredComparisonWindowId, - selected_comparison_window_id: this._selectedComparisonWindowId, - hovered_comparison_window_id: this._hoveredComparisonWindowId - }; - if (this._chartEl) { - const nextChartConfigKey = JSON.stringify(chartConfig); - const molKey = this._historyChartMol?._configKey; - const prevKey = molKey !== void 0 ? molKey : this._chartConfigKey; - if (prevKey !== nextChartConfigKey) { - this._chartEl.setConfig(chartConfig); - if (this._historyChartMol) { - this._historyChartMol._configKey = nextChartConfigKey; - } else { - this._chartConfigKey = nextChartConfigKey; - } - } - } - if (showRecordsPanel) { - const listConfig = { - entities: this._entities, - datapoint_scope: this._datapointScope, - hours_to_show: this._hours, - start_time: this._startTime?.toISOString(), - end_time: this._endTime?.toISOString(), - zoom_start_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.start).toISOString() : null, - zoom_end_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.end).toISOString() : null, - page_size: 15, - show_entities: true, - show_actions: true, - show_search: true, - hidden_event_ids: this._hiddenEventIds - }; - const nextListConfigKey = JSON.stringify(listConfig); - if (this._listEl && this._listConfigKey !== nextListConfigKey) { - this._listEl.setConfig(listConfig); - this._listConfigKey = nextListConfigKey; - } - if (this._listEl) { - this._listEl.hass = this._hass; - } - } else { - this._listConfigKey = ""; - } - if (this._chartEl) { - this._chartEl.hass = this._hass; - } - this._chartEl?.setExternalZoomRange?.(this._chartZoomCommittedRange); - } - } - const styles$g = i$5` + const chartConfig = { + entities: this._entities, + series_settings: this._seriesRows.map((row) => ({ + ...row, + analysis: { + ...row.analysis || {}, + anomaly_overlap_mode: this._chartAnomalyOverlapMode + } + })), + datapoint_scope: this._datapointScope, + show_event_markers: this._showChartDatapointIcons, + show_event_lines: this._showChartDatapointLines, + show_tooltips: this._showChartTooltips, + emphasize_hover_guides: this._showChartEmphasizedHoverGuides, + hover_snap_mode: this._chartHoverSnapMode, + show_correlated_anomalies: this._showCorrelatedAnomalies, + anomaly_overlap_mode: this._chartAnomalyOverlapMode, + delink_y_axis: this._delinkChartYAxis, + split_view: this._splitChartView, + show_data_gaps: this._showDataGaps, + data_gap_threshold: this._dataGapThreshold, + hours_to_show: this._hours, + start_time: this._startTime?.toISOString(), + end_time: this._endTime?.toISOString(), + zoom_start_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.start).toISOString() : null, + zoom_end_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.end).toISOString() : null, + message_filter: this._recordsSearchQuery || "", + hidden_event_ids: this._hiddenEventIds, + hovered_event_ids: this._hoveredEventIds, + comparison_windows: this._getPreviewComparisonWindows(), + preload_comparison_windows: this._getPreloadComparisonWindows(), + comparison_preview_overlay: this._getComparisonPreviewOverlay(), + comparison_hover_active: !!this._hoveredComparisonWindowId, + selected_comparison_window_id: this._selectedComparisonWindowId, + hovered_comparison_window_id: this._hoveredComparisonWindowId + }; + const chart = document.createElement("hass-datapoints-history-card"); + chart.setConfig(chartConfig); + content.querySelector("#chart-card-host").appendChild(chart); + const historyChartMol = { + _configKey: JSON.stringify(chartConfig), + chartEl: chart + }; + this._historyChartMol = historyChartMol; + if (showRecordsPanel) { + const listConfig = { + entities: this._entities, + datapoint_scope: this._datapointScope, + hours_to_show: this._hours, + start_time: this._startTime?.toISOString(), + end_time: this._endTime?.toISOString(), + zoom_start_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.start).toISOString() : null, + zoom_end_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.end).toISOString() : null, + page_size: 15, + show_entities: true, + show_actions: true, + show_search: true, + hidden_event_ids: this._hiddenEventIds + }; + const list = document.createElement("hass-datapoints-list-card"); + list.setConfig(listConfig); + content.querySelector("#list-host").appendChild(list); + this._listEl = list; + } else this._listEl = null; + const resizablePanes = content.querySelector("#content-resizable-panes"); + this._contentSplitterEl = resizablePanes; + if (resizablePanes) { + resizablePanes.ratio = this._contentSplitRatio; + resizablePanes.min = .2; + resizablePanes.max = .8; + resizablePanes.addEventListener("dp-panes-resize", (ev) => { + this._contentSplitRatio = ev.detail?.ratio ?? this._contentSplitRatio; + this._requestChartResizeRedraw(); + if (ev.detail?.committed) { + this._saveSessionState(); + window.requestAnimationFrame(() => this._syncRangeControl()); + } + }); + } + this._chartEl = chart; + this._historyChartMol = historyChartMol; + this._contentKey = contentKey; + this._chartConfigKey = ""; + this._listConfigKey = ""; + } + content.classList.toggle("datapoints-hidden", !showRecordsPanel); + const resizablePanesEl = content.querySelector("#content-resizable-panes"); + if (resizablePanesEl) resizablePanesEl.secondHidden = !showRecordsPanel; + this._applyContentSplitLayout(); + this._renderComparisonTabs(); + const chartConfig = { + entities: this._entities, + series_settings: this._seriesRows.map((row) => ({ + ...row, + analysis: { + ...row.analysis || {}, + anomaly_overlap_mode: this._chartAnomalyOverlapMode + } + })), + datapoint_scope: this._datapointScope, + show_event_markers: this._showChartDatapointIcons, + show_event_lines: this._showChartDatapointLines, + show_tooltips: this._showChartTooltips, + emphasize_hover_guides: this._showChartEmphasizedHoverGuides, + hover_snap_mode: this._chartHoverSnapMode, + show_correlated_anomalies: this._showCorrelatedAnomalies, + anomaly_overlap_mode: this._chartAnomalyOverlapMode, + delink_y_axis: this._delinkChartYAxis, + split_view: this._splitChartView, + show_data_gaps: this._showDataGaps, + data_gap_threshold: this._dataGapThreshold, + hours_to_show: this._hours, + start_time: this._startTime?.toISOString(), + end_time: this._endTime?.toISOString(), + zoom_start_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.start).toISOString() : null, + zoom_end_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.end).toISOString() : null, + message_filter: this._recordsSearchQuery || "", + hidden_event_ids: this._hiddenEventIds, + hovered_event_ids: this._hoveredEventIds, + comparison_windows: this._getPreviewComparisonWindows(), + preload_comparison_windows: this._getPreloadComparisonWindows(), + comparison_preview_overlay: this._getComparisonPreviewOverlay(), + comparison_hover_active: !!this._hoveredComparisonWindowId, + selected_comparison_window_id: this._selectedComparisonWindowId, + hovered_comparison_window_id: this._hoveredComparisonWindowId + }; + if (this._chartEl) { + const nextChartConfigKey = JSON.stringify(chartConfig); + const molKey = this._historyChartMol?._configKey; + if ((molKey !== void 0 ? molKey : this._chartConfigKey) !== nextChartConfigKey) { + this._chartEl.setConfig(chartConfig); + if (this._historyChartMol) this._historyChartMol._configKey = nextChartConfigKey; + else this._chartConfigKey = nextChartConfigKey; + } + } + if (showRecordsPanel) { + const listConfig = { + entities: this._entities, + datapoint_scope: this._datapointScope, + hours_to_show: this._hours, + start_time: this._startTime?.toISOString(), + end_time: this._endTime?.toISOString(), + zoom_start_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.start).toISOString() : null, + zoom_end_time: this._chartZoomCommittedRange ? new Date(this._chartZoomCommittedRange.end).toISOString() : null, + page_size: 15, + show_entities: true, + show_actions: true, + show_search: true, + hidden_event_ids: this._hiddenEventIds + }; + const nextListConfigKey = JSON.stringify(listConfig); + if (this._listEl && this._listConfigKey !== nextListConfigKey) { + this._listEl.setConfig(listConfig); + this._listConfigKey = nextListConfigKey; + } + if (this._listEl) this._listEl.hass = this._hass; + } else this._listConfigKey = ""; + if (this._chartEl) this._chartEl.hass = this._hass; + this._chartEl?.setExternalZoomRange?.(this._chartZoomCommittedRange); + } + }; + //#endregion + //#region custom_components/hass_datapoints/src/cards/list/list.styles.ts + var styles$16 = i$5` :host { display: block; height: 100%; @@ -34260,7 +32836,9 @@ ${content.alert}` : "", opacity: 0.5; } `; - const styles$f = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/search-bar/search-bar.styles.ts + var styles$15 = i$5` :host { display: block; } @@ -34291,69 +32869,37 @@ ${content.alert}` : "", opacity: 0.6; } `; - var __create$a = Object.create; - var __defProp$g = Object.defineProperty; - var __getOwnPropDesc$a = Object.getOwnPropertyDescriptor; - var __knownSymbol$a = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$a = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$g = (obj, key, value) => key in obj ? __defProp$g(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$a = (base) => [, , , __create$a(base?.[__knownSymbol$a("metadata")] ?? null)]; - var __decoratorStrings$a = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$a = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$a("Function expected") : fn; - var __decoratorContext$a = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$a[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$a("Already initialized") : fns.push(__expectFn$a(fn || null)) }); - var __decoratorMetadata$a = (array, target) => __defNormalProp$g(target, __knownSymbol$a("metadata"), array[3]); - var __runInitializers$a = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$a = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$a[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$a({ get [name]() { - return __privateGet$9(this, extra); - }, set [name](x2) { - return __privateSet$9(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$a(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$a(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$a("Object expected"); - else __expectFn$a(fn = it.get) && (desc.get = fn), __expectFn$a(fn = it.set) && (desc.set = fn), __expectFn$a(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$g(target, name, desc), target; - }; - var __publicField$g = (obj, key, value) => __defNormalProp$g(obj, key + "", value); - var __accessCheck$9 = (obj, member, msg2) => member.has(obj) || __typeError$a("Cannot " + msg2); - var __privateGet$9 = (obj, member, getter) => (__accessCheck$9(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$9 = (obj, member, value) => member.has(obj) ? __typeError$a("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$9 = (obj, member, value, setter) => (__accessCheck$9(obj, member, "write to private field"), member.set(obj, value), value); - var _placeholder_dec$1, _query_dec, _a$a, _init$a, _query, _placeholder$1; - class SearchBar extends (_a$a = i$2, _query_dec = [n({ type: String })], _placeholder_dec$1 = [n({ type: String })], _a$a) { - constructor() { - super(...arguments); - __privateAdd$9(this, _query, __runInitializers$a(_init$a, 8, this, "")), __runInitializers$a(_init$a, 11, this); - __privateAdd$9(this, _placeholder$1, __runInitializers$a(_init$a, 12, this, "Search...")), __runInitializers$a(_init$a, 15, this); - } - _onInput(e2) { - this.dispatchEvent( - new CustomEvent("dp-search", { - detail: { query: e2.target.value }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/search-bar/search-bar.ts + var _query_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _placeholder_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var SearchBar = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _query_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _placeholder_accessor_storage$1, "Search..."); + } + get query() { + return _classPrivateFieldGet2(_query_accessor_storage, this); + } + set query(value) { + _classPrivateFieldSet2(_query_accessor_storage, this, value); + } + get placeholder() { + return _classPrivateFieldGet2(_placeholder_accessor_storage$1, this); + } + set placeholder(value) { + _classPrivateFieldSet2(_placeholder_accessor_storage$1, this, value); + } + _onInput(e) { + this.dispatchEvent(new CustomEvent("dp-search", { + detail: { query: e.target.value }, + bubbles: true, + composed: true + })); + } + render() { + return b`
`; - } - } - _init$a = __decoratorStart$a(_a$a); - _query = /* @__PURE__ */ new WeakMap(); - _placeholder$1 = /* @__PURE__ */ new WeakMap(); - __decorateElement$a(_init$a, 4, "query", _query_dec, SearchBar, _query); - __decorateElement$a(_init$a, 4, "placeholder", _placeholder_dec$1, SearchBar, _placeholder$1); - __decoratorMetadata$a(_init$a, SearchBar); - __publicField$g(SearchBar, "styles", styles$f); - customElements.define("search-bar", SearchBar); - const styles$e = i$5` + } + }; + _defineProperty(SearchBar, "styles", styles$15); + __decorate([n$1({ type: String })], SearchBar.prototype, "query", null); + __decorate([n$1({ type: String })], SearchBar.prototype, "placeholder", null); + customElements.define("search-bar", SearchBar); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/pagination/pagination.styles.ts + var styles$14 = i$5` :host { display: flex; align-items: center; @@ -34411,84 +32955,60 @@ ${content.alert}` : "", opacity: 0.55; } `; - var __create$9 = Object.create; - var __defProp$f = Object.defineProperty; - var __getOwnPropDesc$9 = Object.getOwnPropertyDescriptor; - var __knownSymbol$9 = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$9 = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$f = (obj, key, value) => key in obj ? __defProp$f(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$9 = (base) => [, , , __create$9(base?.[__knownSymbol$9("metadata")] ?? null)]; - var __decoratorStrings$9 = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$9 = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$9("Function expected") : fn; - var __decoratorContext$9 = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$9[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$9("Already initialized") : fns.push(__expectFn$9(fn || null)) }); - var __decoratorMetadata$9 = (array, target) => __defNormalProp$f(target, __knownSymbol$9("metadata"), array[3]); - var __runInitializers$9 = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$9 = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$9[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$9({ get [name]() { - return __privateGet$8(this, extra); - }, set [name](x2) { - return __privateSet$8(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$9(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$9(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$9("Object expected"); - else __expectFn$9(fn = it.get) && (desc.get = fn), __expectFn$9(fn = it.set) && (desc.set = fn), __expectFn$9(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$f(target, name, desc), target; - }; - var __publicField$f = (obj, key, value) => __defNormalProp$f(obj, key + "", value); - var __accessCheck$8 = (obj, member, msg2) => member.has(obj) || __typeError$9("Cannot " + msg2); - var __privateGet$8 = (obj, member, getter) => (__accessCheck$8(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$8 = (obj, member, value) => member.has(obj) ? __typeError$9("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$8 = (obj, member, value, setter) => (__accessCheck$8(obj, member, "write to private field"), member.set(obj, value), value); - var _label_dec$3, _totalItems_dec, _totalPages_dec, _page_dec, _a$9, _init$9, _page, _totalPages, _totalItems, _label$3; - class Pagination extends (_a$9 = i$2, _page_dec = [n({ type: Number })], _totalPages_dec = [n({ type: Number })], _totalItems_dec = [n({ type: Number })], _label_dec$3 = [n({ type: String })], _a$9) { - constructor() { - super(...arguments); - __privateAdd$8(this, _page, __runInitializers$9(_init$9, 8, this, 0)), __runInitializers$9(_init$9, 11, this); - __privateAdd$8(this, _totalPages, __runInitializers$9(_init$9, 12, this, 1)), __runInitializers$9(_init$9, 15, this); - __privateAdd$8(this, _totalItems, __runInitializers$9(_init$9, 16, this, 0)), __runInitializers$9(_init$9, 19, this); - __privateAdd$8(this, _label$3, __runInitializers$9(_init$9, 20, this, "records")), __runInitializers$9(_init$9, 23, this); - } - _onPrev() { - if (this.page > 0) { - this.dispatchEvent( - new CustomEvent("dp-page-change", { - detail: { page: this.page - 1 }, - bubbles: true, - composed: true - }) - ); - } - } - _onNext() { - if (this.page < this.totalPages - 1) { - this.dispatchEvent( - new CustomEvent("dp-page-change", { - detail: { page: this.page + 1 }, - bubbles: true, - composed: true - }) - ); - } - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/atoms/interactive/pagination/pagination.ts + var _page_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _totalPages_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _totalItems_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _label_accessor_storage$3 = /* @__PURE__ */ new WeakMap(); + var Pagination = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _page_accessor_storage$1, 0); + _classPrivateFieldInitSpec(this, _totalPages_accessor_storage, 1); + _classPrivateFieldInitSpec(this, _totalItems_accessor_storage, 0); + _classPrivateFieldInitSpec(this, _label_accessor_storage$3, "records"); + } + get page() { + return _classPrivateFieldGet2(_page_accessor_storage$1, this); + } + set page(value) { + _classPrivateFieldSet2(_page_accessor_storage$1, this, value); + } + get totalPages() { + return _classPrivateFieldGet2(_totalPages_accessor_storage, this); + } + set totalPages(value) { + _classPrivateFieldSet2(_totalPages_accessor_storage, this, value); + } + get totalItems() { + return _classPrivateFieldGet2(_totalItems_accessor_storage, this); + } + set totalItems(value) { + _classPrivateFieldSet2(_totalItems_accessor_storage, this, value); + } + get label() { + return _classPrivateFieldGet2(_label_accessor_storage$3, this); + } + set label(value) { + _classPrivateFieldSet2(_label_accessor_storage$3, this, value); + } + _onPrev() { + if (this.page > 0) this.dispatchEvent(new CustomEvent("dp-page-change", { + detail: { page: this.page - 1 }, + bubbles: true, + composed: true + })); + } + _onNext() { + if (this.page < this.totalPages - 1) this.dispatchEvent(new CustomEvent("dp-page-change", { + detail: { page: this.page + 1 }, + bubbles: true, + composed: true + })); + } + render() { + return b`
@@ -35014,162 +33533,120 @@ ${content.alert}` : "",
`; - } - } - _init$8 = __decoratorStart$8(_a$8); - _eventRecord$1 = /* @__PURE__ */ new WeakMap(); - _hass$2 = /* @__PURE__ */ new WeakMap(); - _color = /* @__PURE__ */ new WeakMap(); - _language = /* @__PURE__ */ new WeakMap(); - __message = /* @__PURE__ */ new WeakMap(); - __annotation = /* @__PURE__ */ new WeakMap(); - __icon = /* @__PURE__ */ new WeakMap(); - __decorateElement$8(_init$8, 4, "eventRecord", _eventRecord_dec$1, CardListEditForm, _eventRecord$1); - __decorateElement$8(_init$8, 4, "hass", _hass_dec$2, CardListEditForm, _hass$2); - __decorateElement$8(_init$8, 4, "color", _color_dec, CardListEditForm, _color); - __decorateElement$8(_init$8, 4, "language", _language_dec, CardListEditForm, _language); - __decorateElement$8(_init$8, 4, "_message", __message_dec, CardListEditForm, __message); - __decorateElement$8(_init$8, 4, "_annotation", __annotation_dec, CardListEditForm, __annotation); - __decorateElement$8(_init$8, 4, "_icon", __icon_dec, CardListEditForm, __icon); - __decoratorMetadata$8(_init$8, CardListEditForm); - __publicField$e(CardListEditForm, "styles", styles$c); - customElements.define("list-edit-form", CardListEditForm); - var __create$7 = Object.create; - var __defProp$d = Object.defineProperty; - var __getOwnPropDesc$7 = Object.getOwnPropertyDescriptor; - var __knownSymbol$7 = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$7 = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$d = (obj, key, value) => key in obj ? __defProp$d(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$7 = (base) => [, , , __create$7(base?.[__knownSymbol$7("metadata")] ?? null)]; - var __decoratorStrings$7 = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$7 = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$7("Function expected") : fn; - var __decoratorContext$7 = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$7[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$7("Already initialized") : fns.push(__expectFn$7(fn || null)) }); - var __decoratorMetadata$7 = (array, target) => __defNormalProp$d(target, __knownSymbol$7("metadata"), array[3]); - var __runInitializers$7 = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$7 = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$7[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$7({ get [name]() { - return __privateGet$6(this, extra); - }, set [name](x2) { - return __privateSet$6(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$7(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$7(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$7("Object expected"); - else __expectFn$7(fn = it.get) && (desc.get = fn), __expectFn$7(fn = it.set) && (desc.set = fn), __expectFn$7(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$d(target, name, desc), target; - }; - var __publicField$d = (obj, key, value) => __defNormalProp$d(obj, key + "", value); - var __accessCheck$6 = (obj, member, msg2) => member.has(obj) || __typeError$7("Cannot " + msg2); - var __privateGet$6 = (obj, member, getter) => (__accessCheck$6(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$6 = (obj, member, value) => member.has(obj) ? __typeError$7("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$6 = (obj, member, value, setter) => (__accessCheck$6(obj, member, "write to private field"), member.set(obj, value), value); - var __annotationExpanded_dec, _context_dec, _eventRecord_dec, _a$7, _init$7, _eventRecord, _context, __annotationExpanded; - const DEFAULT_LANGUAGE = { - showAnnotation: "Show annotation", - openHistory: "Open related data point history", - editRecord: "Edit record", - deleteRecord: "Delete record", - showChartMarker: "Show chart marker", - hideChartMarker: "Hide chart marker", - chooseColor: "Choose colour", - save: "Save", - cancel: "Cancel", - message: "Message", - annotationFullMessage: "Annotation / full message" - }; - class CardListEventItem extends (_a$7 = i$2, _eventRecord_dec = [n({ attribute: false })], _context_dec = [n({ attribute: false })], __annotationExpanded_dec = [r()], _a$7) { - constructor() { - super(...arguments); - __privateAdd$6(this, _eventRecord, __runInitializers$7(_init$7, 8, this, null)), __runInitializers$7(_init$7, 11, this); - __privateAdd$6(this, _context, __runInitializers$7(_init$7, 12, this, { - hass: null, - showActions: true, - canEdit: true, - showEntities: true, - showFullMessage: true, - hidden: false, - editing: false, - editColor: "#03a9f4", - language: DEFAULT_LANGUAGE - })), __runInitializers$7(_init$7, 15, this); - __privateAdd$6(this, __annotationExpanded, __runInitializers$7(_init$7, 16, this, false)), __runInitializers$7(_init$7, 19, this); - } - _dispatch(name, detail = {}) { - this.dispatchEvent( - new CustomEvent(name, { - detail, - bubbles: true, - composed: true - }) - ); - } - render() { - if (!this.eventRecord) { - return b``; - } - const ev = this.eventRecord; - const showActions = this.context.showActions; - const canEdit = this.context.canEdit; - const showEntities = this.context.showEntities; - const showFullMessage = this.context.showFullMessage; - const annText = ev.annotation && ev.annotation !== ev.message ? ev.annotation.trim() : ""; - const color = ev.color || "#03a9f4"; - const icon = ev.icon || "mdi:bookmark"; - const iconColor = contrastColor(color); - const entities = ev.entity_ids || []; - const devices = ev.device_ids || []; - const areas = ev.area_ids || []; - const labels = ev.label_ids || []; - const hasRelated = entities.length || devices.length || areas.length || labels.length; - const isExpandable = !showFullMessage && !!annText; - const isHidden = this.context.hidden; - const visibilityIcon = isHidden ? "mdi:eye" : "mdi:eye-off"; - const visibilityLabel = isHidden ? this.context.language.showChartMarker : this.context.language.hideChartMarker; - const isEditing = this.context.editing; - const isSimple = !annText && !hasRelated; - return b` + } + }; + _defineProperty(CardListEditForm, "styles", styles$12); + __decorate([n$1({ attribute: false })], CardListEditForm.prototype, "eventRecord", null); + __decorate([n$1({ attribute: false })], CardListEditForm.prototype, "hass", null); + __decorate([n$1({ type: String })], CardListEditForm.prototype, "color", null); + __decorate([n$1({ attribute: false })], CardListEditForm.prototype, "language", null); + __decorate([r$1()], CardListEditForm.prototype, "_message", null); + __decorate([r$1()], CardListEditForm.prototype, "_annotation", null); + __decorate([r$1()], CardListEditForm.prototype, "_icon", null); + customElements.define("list-edit-form", CardListEditForm); + //#endregion + //#region custom_components/hass_datapoints/src/cards/list/list-event-item/list-event-item.ts + var DEFAULT_LANGUAGE = { + showAnnotation: "Show annotation", + openHistory: "Open related data point history", + editRecord: "Edit record", + deleteRecord: "Delete record", + showChartMarker: "Show chart marker", + hideChartMarker: "Hide chart marker", + chooseColor: "Choose colour", + save: "Save", + cancel: "Cancel", + message: "Message", + annotationFullMessage: "Annotation / full message" + }; + var _eventRecord_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _context_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _annotationExpanded_accessor_storage = /* @__PURE__ */ new WeakMap(); + var CardListEventItem = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _eventRecord_accessor_storage, null); + _classPrivateFieldInitSpec(this, _context_accessor_storage, { + hass: null, + showActions: true, + canEdit: true, + showEntities: true, + showFullMessage: true, + hidden: false, + editing: false, + editColor: "#03a9f4", + language: DEFAULT_LANGUAGE + }); + _classPrivateFieldInitSpec(this, _annotationExpanded_accessor_storage, false); + } + get eventRecord() { + return _classPrivateFieldGet2(_eventRecord_accessor_storage, this); + } + set eventRecord(value) { + _classPrivateFieldSet2(_eventRecord_accessor_storage, this, value); + } + get context() { + return _classPrivateFieldGet2(_context_accessor_storage, this); + } + set context(value) { + _classPrivateFieldSet2(_context_accessor_storage, this, value); + } + get _annotationExpanded() { + return _classPrivateFieldGet2(_annotationExpanded_accessor_storage, this); + } + set _annotationExpanded(value) { + _classPrivateFieldSet2(_annotationExpanded_accessor_storage, this, value); + } + _dispatch(name, detail = {}) { + this.dispatchEvent(new CustomEvent(name, { + detail, + bubbles: true, + composed: true + })); + } + render() { + if (!this.eventRecord) return b``; + const ev = this.eventRecord; + const showActions = this.context.showActions; + const canEdit = this.context.canEdit; + const showEntities = this.context.showEntities; + const showFullMessage = this.context.showFullMessage; + const annText = ev.annotation && ev.annotation !== ev.message ? ev.annotation.trim() : ""; + const color = ev.color || "#03a9f4"; + const icon = ev.icon || "mdi:bookmark"; + const iconColor = contrastColor(color); + const entities = ev.entity_ids || []; + const devices = ev.device_ids || []; + const areas = ev.area_ids || []; + const labels = ev.label_ids || []; + const hasRelated = entities.length || devices.length || areas.length || labels.length; + const isExpandable = !showFullMessage && !!annText; + const isHidden = this.context.hidden; + const visibilityIcon = isHidden ? "mdi:eye" : "mdi:eye-off"; + const visibilityLabel = isHidden ? this.context.language.showChartMarker : this.context.language.hideChartMarker; + const isEditing = this.context.editing; + return b`
{ - this._dispatch("dp-hover-event-record", { - eventId: ev.id, - hovered: true, - eventRecord: ev - }); - }} + this._dispatch("dp-hover-event-record", { + eventId: ev.id, + hovered: true, + eventRecord: ev + }); + }} @mouseleave=${() => { - this._dispatch("dp-hover-event-record", { - eventId: ev.id, - hovered: false, - eventRecord: ev - }); - }} + this._dispatch("dp-hover-event-record", { + eventId: ev.id, + hovered: false, + eventRecord: ev + }); + }} @click=${isExpandable ? (event) => { - const target = event.target; - if (target.closest( - ".ev-actions, .ev-entity-chip, .edit-form, ha-icon-button, ha-button" - )) { - return; - } - this._annotationExpanded = !this._annotationExpanded; - } : void 0} + if (event.target.closest(".ev-actions, .ev-entity-chip, .edit-form, ha-icon-button, ha-button")) return; + this._annotationExpanded = !this._annotationExpanded; + } : void 0} >
{ - event.preventDefault(); - event.stopPropagation(); - this._dispatch("dp-open-history", { eventRecord: ev }); - }} + event.preventDefault(); + event.stopPropagation(); + this._dispatch("dp-open-history", { eventRecord: ev }); + }} > { - event.stopPropagation(); - this._dispatch("dp-toggle-visibility", { - eventId: ev.id - }); - }} + event.stopPropagation(); + this._dispatch("dp-toggle-visibility", { eventId: ev.id }); + }} > @@ -35229,11 +33704,9 @@ ${content.alert}` : "", { - event.stopPropagation(); - this._dispatch("dp-edit-event", { - eventRecord: ev - }); - }} + event.stopPropagation(); + this._dispatch("dp-edit-event", { eventRecord: ev }); + }} > @@ -35241,11 +33714,9 @@ ${content.alert}` : "", label=${this.context.language.deleteRecord} style="--icon-primary-color:var(--error-color,#f44336)" @click=${(event) => { - event.stopPropagation(); - this._dispatch("dp-delete-event", { - eventRecord: ev - }); - }} + event.stopPropagation(); + this._dispatch("dp-delete-event", { eventRecord: ev }); + }} > @@ -35260,31 +33731,24 @@ ${content.alert}` : "",
` : ""} ${showEntities && hasRelated ? b`
- ${entities.map( - (entityId) => b` + ${entities.map((entityId) => b` ${(() => { - const entityLabel = entityName( - this.context.hass, - entityId - ); - const showSecondary = entityLabel !== entityId; - return b` + const entityLabel = entityName(this.context.hass, entityId); + const showSecondary = entityLabel !== entityId; + return b` `; - })()} - ` - )} - ${devices.map( - (id) => b` + })()} + `)} + ${devices.map((id) => b` ${deviceName(this.context.hass, id)} - ` - )} - ${areas.map( - (id) => b` + `)} + ${areas.map((id) => b` ${areaName(this.context.hass, id)} - ` - )} - ${labels.map( - (id) => b` + `)} + ${labels.map((id) => b` ${labelName(this.context.hass, id)} - ` - )} + `)}
` : ""} ${showActions && canEdit && isEditing ? b` @@ -35338,357 +33795,277 @@ ${content.alert}` : "", .color=${this.context.editColor} .language=${this.context.language} @dp-save-edit=${(event) => { - this._dispatch("dp-save-edit", { - eventRecord: ev, - values: event.detail - }); - }} + this._dispatch("dp-save-edit", { + eventRecord: ev, + values: event.detail + }); + }} @dp-cancel-edit=${() => { - this._dispatch("dp-cancel-edit", { eventRecord: ev }); - }} + this._dispatch("dp-cancel-edit", { eventRecord: ev }); + }} > ` : ""}
`; - } - } - _init$7 = __decoratorStart$7(_a$7); - _eventRecord = /* @__PURE__ */ new WeakMap(); - _context = /* @__PURE__ */ new WeakMap(); - __annotationExpanded = /* @__PURE__ */ new WeakMap(); - __decorateElement$7(_init$7, 4, "eventRecord", _eventRecord_dec, CardListEventItem, _eventRecord); - __decorateElement$7(_init$7, 4, "context", _context_dec, CardListEventItem, _context); - __decorateElement$7(_init$7, 4, "_annotationExpanded", __annotationExpanded_dec, CardListEventItem, __annotationExpanded); - __decoratorMetadata$7(_init$7, CardListEventItem); - __publicField$d(CardListEventItem, "styles", styles$d); - customElements.define("list-event-item", CardListEventItem); - var __create$6 = Object.create; - var __defProp$c = Object.defineProperty; - var __getOwnPropDesc$6 = Object.getOwnPropertyDescriptor; - var __knownSymbol$6 = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$6 = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$c = (obj, key, value) => key in obj ? __defProp$c(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __name = (target, value) => __defProp$c(target, "name", { value, configurable: true }); - var __decoratorStart$6 = (base) => [, , , __create$6(base?.[__knownSymbol$6("metadata")] ?? null)]; - var __decoratorStrings$6 = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$6 = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$6("Function expected") : fn; - var __decoratorContext$6 = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$6[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$6("Already initialized") : fns.push(__expectFn$6(fn || null)) }); - var __decoratorMetadata$6 = (array, target) => __defNormalProp$c(target, __knownSymbol$6("metadata"), array[3]); - var __runInitializers$6 = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) fns[i2].call(self2); - return value; - }; - var __decorateElement$6 = (array, flags, name, decorators, target, extra) => { - var it, done, ctx, k2 = flags & 7, p2 = false; - var j2 = 0; - var extraInitializers = array[j2] || (array[j2] = []); - var desc = k2 && (target = target.prototype, k2 < 5 && (k2 > 3 || !p2) && __getOwnPropDesc$6(target, name)); - __name(target, name); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$6(k2, name, done = {}, array[3], extraInitializers); - it = (0, decorators[i2])(target, ctx), done._ = 1; - __expectFn$6(it) && (target = it); - } - return __decoratorMetadata$6(array, target), desc && __defProp$c(target, name, desc), p2 ? k2 ^ 4 ? extra : desc : target; - }; - var __publicField$c = (obj, key, value) => __defNormalProp$c(obj, typeof key !== "symbol" ? key + "" : key, value); - var _HassRecordsListCard_decorators, _init$6, _a$6; - _HassRecordsListCard_decorators = [localized()]; - class HassRecordsListCard extends (_a$6 = i$2) { - constructor() { - super(); - __publicField$c(this, "_pageSize", 15); - __publicField$c(this, "_configKey", ""); - __publicField$c(this, "_unsubscribe", null); - __publicField$c(this, "_windowListener", null); - __publicField$c(this, "_initialized", false); - this._config = {}; - this._hass = null; - this._allEvents = []; - this._searchQuery = ""; - this._page = 0; - this._editingId = null; - this._editColor = "#03a9f4"; - } - setConfig(config) { - const nextKey = JSON.stringify(config); - if (this._configKey === nextKey) { - return; - } - this._configKey = nextKey; - this._config = config || {}; - if (config.page_size) this._pageSize = config.page_size; - if (this._hass) this._load(); - } - set hass(hass) { - const isFirst = !this._hass; - this._hass = hass; - if (isFirst) { - this._load(); - this._setupAutoRefresh(); - } - } - get hass() { - return this._hass; - } - connectedCallback() { - super.connectedCallback(); - this._windowListener = () => this._load(); - window.addEventListener( - "hass-datapoints-event-recorded", - this._windowListener - ); - } - disconnectedCallback() { - super.disconnectedCallback(); - if (this._unsubscribe) { - this._unsubscribe(); - this._unsubscribe = null; - } - if (this._windowListener) { - window.removeEventListener( - "hass-datapoints-event-recorded", - this._windowListener - ); - this._windowListener = null; - } - } - _setupAutoRefresh() { - if (!this._hass) return; - this._hass.connection.subscribeEvents(() => this._load(), `${DOMAIN}_event_recorded`).then((unsub) => { - this._unsubscribe = unsub; - }).catch(() => { - }); - } - async _load() { - if (!this._hass || !this._config) return; - const cfg = this._config; - const endTime = cfg.zoom_end_time || cfg.end_time || void 0; - let startTime = cfg.zoom_start_time || cfg.start_time || void 0; - if (!startTime && cfg.hours_to_show) { - const end = endTime ? new Date(endTime) : /* @__PURE__ */ new Date(); - startTime = new Date( - end.getTime() - cfg.hours_to_show * 3600 * 1e3 - ).toISOString(); - } - if (!startTime) { - startTime = (/* @__PURE__ */ new Date(0)).toISOString(); - } - const effectiveEndTime = endTime || (/* @__PURE__ */ new Date()).toISOString(); - let entityIds; - if (cfg.target) { - const resolved = resolveEntityIdsFromTarget(this._hass, cfg.target); - entityIds = resolved.length ? resolved : void 0; - } else if (cfg.entity) { - entityIds = [cfg.entity]; - } else if (cfg.entities) { - entityIds = cfg.entities.map( - (e2) => typeof e2 === "string" ? e2 : e2.entity - ); - } else { - entityIds = void 0; - } - const hass = this._hass; - if (!hass) { - this._allEvents = []; - return; - } - const events = await fetchEvents( - hass, - startTime, - effectiveEndTime, - cfg.datapoint_scope === "all" ? void 0 : entityIds - ); - this._allEvents = [...events].reverse(); - } - _filtered() { - const msgFilter = (this._config.message_filter || "").toLowerCase().trim(); - const searchQ = this._searchQuery.toLowerCase().trim(); - return this._allEvents.filter((e2) => { - const haystack = [ - e2.message.toLowerCase(), - (e2.annotation || "").toLowerCase(), - ...(e2.entity_ids || []).map((id) => id.toLowerCase()) - ]; - if (msgFilter && !haystack.some((h2) => h2.includes(msgFilter))) - return false; - if (searchQ && !haystack.some((h2) => h2.includes(searchQ))) return false; - return true; - }); - } - _onSearch(e2) { - this._searchQuery = e2.detail.query; - this._page = 0; - } - _onPageChange(e2) { - this._page = e2.detail.page; - this.shadowRoot?.querySelector(".list-scroll")?.scrollTo(0, 0); - } - _navigateToEventHistory(ev) { - const range = this._getNavigationContextForEvent(ev); - navigateToDataPointsHistory( - this, - { - entity_id: ev?.entity_ids || [], - device_id: ev?.device_ids || [], - area_id: ev?.area_ids || [], - label_id: ev?.label_ids || [] - }, - { - start_time: range?.start_time, - end_time: range?.end_time, - zoom_start_time: range?.zoom_start_time, - zoom_end_time: range?.zoom_end_time, - datapoint_scope: typeof this._config?.datapoint_scope === "string" ? this._config.datapoint_scope : void 0 - } - ); - } - _getNavigationContextForEvent(ev) { - const cfg = this._config || {}; - const startTime = cfg.start_time || null; - const endTime = cfg.end_time || null; - const zoomStartTime = cfg.zoom_start_time || null; - const zoomEndTime = cfg.zoom_end_time || null; - if (startTime && endTime) { - return { - start_time: startTime, - end_time: endTime, - zoom_start_time: zoomStartTime, - zoom_end_time: zoomEndTime - }; - } - const eventTime = ev?.timestamp ? new Date(ev.timestamp) : null; - if (!eventTime || !Number.isFinite(eventTime.getTime())) return null; - const start = new Date(eventTime.getTime() - 12 * 3600 * 1e3); - const end = new Date(eventTime.getTime() + 12 * 3600 * 1e3); - return { start_time: start.toISOString(), end_time: end.toISOString() }; - } - _openEdit(ev) { - this._editingId = ev.id; - this._editColor = ev.color || "#03a9f4"; - } - _closeEdit() { - this._editingId = null; - } - async _saveEdit(ev, values) { - const message = values.message.trim(); - const ann = values.annotation.trim(); - const icon = values.icon || "mdi:bookmark"; - const color = values.color || this._editColor; - if (!message) { - return; - } - const hass = this._hass; - if (!hass) { - return; - } - try { - await updateEvent(hass, ev.id, { - message, - annotation: ann || message, - icon, - color - }); - this._closeEdit(); - await this._load(); - } catch (err) { - logger$1.error("[hass-datapoints list-card] update failed", err); - } - } - async _deleteEvent(ev) { - const message = ev.message || "this record"; - const confirmed = await confirmDestructiveAction(this, { - title: msg("Delete record"), - message: `${msg("Delete")} ${message}?`, - confirmLabel: msg("Delete record") - }); - if (!confirmed) return; - const hass = this._hass; - if (!hass) { - return; - } - try { - await deleteEvent(hass, ev.id); - await this._load(); - } catch (err) { - logger$1.error("[hass-datapoints list-card] delete failed", err); - } - } - _toggleVisibility(ev) { - this.dispatchEvent( - new CustomEvent("hass-datapoints-toggle-event-visibility", { - bubbles: true, - composed: true, - detail: { eventId: ev.id } - }) - ); - } - _fireMoreInfo(entityId) { - this.dispatchEvent( - new CustomEvent("hass-more-info", { - bubbles: true, - composed: true, - detail: { entityId } - }) - ); - } - _handleHoverEventRecord(event) { - this.dispatchEvent( - new CustomEvent("hass-datapoints-hover-event-record", { - bubbles: true, - composed: true, - detail: { - eventId: event.detail.eventId, - hovered: event.detail.hovered === true, - eventRecord: event.detail.eventRecord - } - }) - ); - } - _itemContext(ev) { - const cfg = this._config; - const hidden = (cfg.hidden_event_ids || []).includes(ev.id); - if (hidden !== ev._lastHidden) { - ev._lastHidden = hidden; - } - return { - hass: this._hass, - showActions: cfg.show_actions !== false, - canEdit: this._hass?.user?.is_admin === true, - showEntities: cfg.show_entities !== false, - showFullMessage: cfg.show_full_message !== false, - hidden, - editing: this._editingId === ev.id, - editColor: this._editColor, - language: { - showAnnotation: msg("Show annotation"), - openHistory: msg("Open related data point history"), - editRecord: msg("Edit record"), - deleteRecord: msg("Delete record"), - showChartMarker: msg("Show chart marker"), - hideChartMarker: msg("Hide chart marker"), - chooseColor: msg("Choose colour"), - save: msg("Save"), - cancel: msg("Cancel"), - message: msg("Message"), - annotationFullMessage: msg("Annotation / full message") - } - }; - } - render() { - const cfg = this._config; - const showSearch = cfg.show_search !== false; - const filtered = this._filtered(); - const total = filtered.length; - const pageSize = this._pageSize; - const totalPages = Math.max(1, Math.ceil(total / pageSize)); - const page = Math.min(this._page, totalPages - 1); - const slice = filtered.slice(page * pageSize, (page + 1) * pageSize); - const showPagination = totalPages > 1; - return b` + } + }; + _defineProperty(CardListEventItem, "styles", styles$13); + __decorate([n$1({ attribute: false })], CardListEventItem.prototype, "eventRecord", null); + __decorate([n$1({ attribute: false })], CardListEventItem.prototype, "context", null); + __decorate([r$1()], CardListEventItem.prototype, "_annotationExpanded", null); + customElements.define("list-event-item", CardListEventItem); + //#endregion + //#region custom_components/hass_datapoints/src/cards/list/list.ts + var _HassRecordsListCard; + var HassRecordsListCard = (_HassRecordsListCard = class HassRecordsListCard extends i$2 { + constructor() { + super(); + _defineProperty(this, "_pageSize", 15); + _defineProperty(this, "_configKey", ""); + _defineProperty(this, "_unsubscribe", null); + _defineProperty(this, "_windowListener", null); + _defineProperty(this, "_initialized", false); + this._config = {}; + this._hass = null; + this._allEvents = []; + this._searchQuery = ""; + this._page = 0; + this._editingId = null; + this._editColor = "#03a9f4"; + } + setConfig(config) { + const nextKey = JSON.stringify(config); + if (this._configKey === nextKey) return; + this._configKey = nextKey; + this._config = config || {}; + if (config.page_size) this._pageSize = config.page_size; + if (this._hass) this._load(); + } + set hass(hass) { + const isFirst = !this._hass; + this._hass = hass; + if (isFirst) { + this._load(); + this._setupAutoRefresh(); + } + } + get hass() { + return this._hass; + } + connectedCallback() { + super.connectedCallback(); + this._windowListener = () => this._load(); + window.addEventListener("hass-datapoints-event-recorded", this._windowListener); + } + disconnectedCallback() { + super.disconnectedCallback(); + if (this._unsubscribe) { + this._unsubscribe(); + this._unsubscribe = null; + } + if (this._windowListener) { + window.removeEventListener("hass-datapoints-event-recorded", this._windowListener); + this._windowListener = null; + } + } + _setupAutoRefresh() { + if (!this._hass) return; + this._hass.connection.subscribeEvents(() => this._load(), `${DOMAIN}_event_recorded`).then((unsub) => { + this._unsubscribe = unsub; + }).catch(() => {}); + } + async _load() { + if (!this._hass || !this._config) return; + const cfg = this._config; + const endTime = cfg.zoom_end_time || cfg.end_time || void 0; + let startTime = cfg.zoom_start_time || cfg.start_time || void 0; + if (!startTime && cfg.hours_to_show) { + const end = endTime ? new Date(endTime) : /* @__PURE__ */ new Date(); + startTime = (/* @__PURE__ */ new Date(end.getTime() - cfg.hours_to_show * 3600 * 1e3)).toISOString(); + } + if (!startTime) startTime = (/* @__PURE__ */ new Date(0)).toISOString(); + const effectiveEndTime = endTime || (/* @__PURE__ */ new Date()).toISOString(); + let entityIds; + if (cfg.target) { + const resolved = resolveEntityIdsFromTarget(this._hass, cfg.target); + entityIds = resolved.length ? resolved : void 0; + } else if (cfg.entity) entityIds = [cfg.entity]; + else if (cfg.entities) entityIds = cfg.entities.map((e) => typeof e === "string" ? e : e.entity); + else entityIds = void 0; + const hass = this._hass; + if (!hass) { + this._allEvents = []; + return; + } + this._allEvents = [...await fetchEvents(hass, startTime, effectiveEndTime, cfg.datapoint_scope === "all" ? void 0 : entityIds)].reverse(); + } + _filtered() { + const msgFilter = (this._config.message_filter || "").toLowerCase().trim(); + const searchQ = this._searchQuery.toLowerCase().trim(); + return this._allEvents.filter((e) => { + const haystack = [ + e.message.toLowerCase(), + (e.annotation || "").toLowerCase(), + ...(e.entity_ids || []).map((id) => id.toLowerCase()) + ]; + if (msgFilter && !haystack.some((h) => h.includes(msgFilter))) return false; + if (searchQ && !haystack.some((h) => h.includes(searchQ))) return false; + return true; + }); + } + _onSearch(e) { + this._searchQuery = e.detail.query; + this._page = 0; + } + _onPageChange(e) { + this._page = e.detail.page; + this.shadowRoot?.querySelector(".list-scroll")?.scrollTo(0, 0); + } + _navigateToEventHistory(ev) { + const range = this._getNavigationContextForEvent(ev); + navigateToDataPointsHistory(this, { + entity_id: ev?.entity_ids || [], + device_id: ev?.device_ids || [], + area_id: ev?.area_ids || [], + label_id: ev?.label_ids || [] + }, { + start_time: range?.start_time, + end_time: range?.end_time, + zoom_start_time: range?.zoom_start_time, + zoom_end_time: range?.zoom_end_time, + datapoint_scope: typeof this._config?.datapoint_scope === "string" ? this._config.datapoint_scope : void 0 + }); + } + _getNavigationContextForEvent(ev) { + const cfg = this._config || {}; + const startTime = cfg.start_time || null; + const endTime = cfg.end_time || null; + const zoomStartTime = cfg.zoom_start_time || null; + const zoomEndTime = cfg.zoom_end_time || null; + if (startTime && endTime) return { + start_time: startTime, + end_time: endTime, + zoom_start_time: zoomStartTime, + zoom_end_time: zoomEndTime + }; + const eventTime = ev?.timestamp ? new Date(ev.timestamp) : null; + if (!eventTime || !Number.isFinite(eventTime.getTime())) return null; + const start = /* @__PURE__ */ new Date(eventTime.getTime() - 12 * 3600 * 1e3); + const end = new Date(eventTime.getTime() + 12 * 3600 * 1e3); + return { + start_time: start.toISOString(), + end_time: end.toISOString() + }; + } + _openEdit(ev) { + this._editingId = ev.id; + this._editColor = ev.color || "#03a9f4"; + } + _closeEdit() { + this._editingId = null; + } + async _saveEdit(ev, values) { + const message = values.message.trim(); + const ann = values.annotation.trim(); + const icon = values.icon || "mdi:bookmark"; + const color = values.color || this._editColor; + if (!message) return; + const hass = this._hass; + if (!hass) return; + try { + await updateEvent(hass, ev.id, { + message, + annotation: ann || message, + icon, + color + }); + this._closeEdit(); + await this._load(); + } catch (err) { + logger$1.error("[hass-datapoints list-card] update failed", err); + } + } + async _deleteEvent(ev) { + const message = ev.message || "this record"; + if (!await confirmDestructiveAction(this, { + title: msg("Delete record"), + message: `${msg("Delete")} ${message}?`, + confirmLabel: msg("Delete record") + })) return; + const hass = this._hass; + if (!hass) return; + try { + await deleteEvent(hass, ev.id); + await this._load(); + } catch (err) { + logger$1.error("[hass-datapoints list-card] delete failed", err); + } + } + _toggleVisibility(ev) { + this.dispatchEvent(new CustomEvent("hass-datapoints-toggle-event-visibility", { + bubbles: true, + composed: true, + detail: { eventId: ev.id } + })); + } + _fireMoreInfo(entityId) { + this.dispatchEvent(new CustomEvent("hass-more-info", { + bubbles: true, + composed: true, + detail: { entityId } + })); + } + _handleHoverEventRecord(event) { + this.dispatchEvent(new CustomEvent("hass-datapoints-hover-event-record", { + bubbles: true, + composed: true, + detail: { + eventId: event.detail.eventId, + hovered: event.detail.hovered === true, + eventRecord: event.detail.eventRecord + } + })); + } + _itemContext(ev) { + const cfg = this._config; + const hidden = (cfg.hidden_event_ids || []).includes(ev.id); + if (hidden !== ev._lastHidden) ev._lastHidden = hidden; + return { + hass: this._hass, + showActions: cfg.show_actions !== false, + canEdit: this._hass?.user?.is_admin === true, + showEntities: cfg.show_entities !== false, + showFullMessage: cfg.show_full_message !== false, + hidden, + editing: this._editingId === ev.id, + editColor: this._editColor, + language: { + showAnnotation: msg("Show annotation"), + openHistory: msg("Open related data point history"), + editRecord: msg("Edit record"), + deleteRecord: msg("Delete record"), + showChartMarker: msg("Show chart marker"), + hideChartMarker: msg("Hide chart marker"), + chooseColor: msg("Choose colour"), + save: msg("Save"), + cancel: msg("Cancel"), + message: msg("Message"), + annotationFullMessage: msg("Annotation / full message") + } + }; + } + render() { + const cfg = this._config; + const showSearch = cfg.show_search !== false; + const filtered = this._filtered(); + const total = filtered.length; + const pageSize = this._pageSize; + const totalPages = Math.max(1, Math.ceil(total / pageSize)); + const page = Math.min(this._page, totalPages - 1); + const slice = filtered.slice(page * pageSize, (page + 1) * pageSize); + const showPagination = totalPages > 1; + return b` ${cfg.title ? b`
${cfg.title}
` : ""} ${showSearch ? b` @@ -35707,38 +34084,36 @@ ${content.alert}` : "", ${this._searchQuery ? "No matching datapoints." : "No datapoints yet."} - ` : slice.map( - (ev) => b` + ` : slice.map((ev) => b` { - this._navigateToEventHistory(ev); - }} + this._navigateToEventHistory(ev); + }} @dp-edit-event=${() => { - this._openEdit(ev); - }} + this._openEdit(ev); + }} @dp-delete-event=${() => { - this._deleteEvent(ev); - }} + this._deleteEvent(ev); + }} @dp-toggle-visibility=${() => { - this._toggleVisibility(ev); - }} + this._toggleVisibility(ev); + }} @dp-hover-event-record=${(event) => { - this._handleHoverEventRecord(event); - }} + this._handleHoverEventRecord(event); + }} @dp-more-info=${(event) => { - this._fireMoreInfo(event.detail.entityId); - }} + this._fireMoreInfo(event.detail.entityId); + }} @dp-save-edit=${(event) => { - this._saveEdit(ev, event.detail.values); - }} + this._saveEdit(ev, event.detail.values); + }} @dp-cancel-edit=${() => { - this._closeEdit(); - }} + this._closeEdit(); + }} > - ` - )} + `)} ${showPagination ? b` @@ -35754,136 +34129,116 @@ ${content.alert}` : "", ` : ""}
`; - } - static getConfigElement() { - return document.createElement("hass-datapoints-list-card-editor"); - } - static getStubConfig() { - return {}; - } - getGridOptions() { - const rows = this._config?.show_search !== false ? 4 : 3; - return { rows, min_rows: rows }; - } - } - _init$6 = __decoratorStart$6(_a$6); - HassRecordsListCard = __decorateElement$6(_init$6, 0, "HassRecordsListCard", _HassRecordsListCard_decorators, HassRecordsListCard); - __publicField$c(HassRecordsListCard, "properties", { - _config: { state: true }, - _hass: { state: true }, - _allEvents: { state: true }, - _searchQuery: { state: true }, - _page: { state: true }, - _editingId: { state: true }, - _editColor: { state: true } - }); - __publicField$c(HassRecordsListCard, "styles", styles$g); - __runInitializers$6(_init$6, 1, HassRecordsListCard); - const styles$b = i$5``; - var __defProp$b = Object.defineProperty; - var __defNormalProp$b = (obj, key, value) => key in obj ? __defProp$b(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$b = (obj, key, value) => __defNormalProp$b(obj, typeof key !== "symbol" ? key + "" : key, value); - class HassRecordsListCardEditor extends EditorBase { - constructor() { - super(...arguments); - __publicField$b(this, "_onTargetChanged", (e2) => { - const val = e2.detail.value; - const isEmpty = !val || Object.values(val).every((value) => !value?.length); - this._setTargetConfig(isEmpty ? void 0 : val); - }); - } - _configTarget() { - return normalizeTargetValue( - this._config.target ?? { - entity_id: Array.isArray(this._config.entities) && this._config.entities.length ? this._config.entities : this._config.entity - } - ) ?? {}; - } - _syncTargetPicker() { - const tp = this.shadowRoot?.querySelector( - "#target-picker" - ); - if (!tp) { - return; - } - if (this.hass) { - tp.hass = this.hass; - } - tp.value = this._configTarget(); - } - _setTargetConfig(target) { - const cfg = { ...this._config }; - delete cfg.entity; - delete cfg.entities; - if (!target || Object.values(target).every((value) => !value?.length)) { - delete cfg.target; - } else { - cfg.target = target; - } - this._config = cfg; - this._fire(cfg); - } - updated(changedProps) { - if (changedProps.has("hass") || changedProps.has("_config")) { - this._syncTargetPicker(); - } - } - render() { - const c2 = this._config; - return b` + } + static getConfigElement() { + return document.createElement("hass-datapoints-list-card-editor"); + } + static getStubConfig() { + return {}; + } + getGridOptions() { + const rows = this._config?.show_search !== false ? 4 : 3; + return { + rows, + min_rows: rows + }; + } + }, _defineProperty(_HassRecordsListCard, "properties", { + _config: { state: true }, + _hass: { state: true }, + _allEvents: { state: true }, + _searchQuery: { state: true }, + _page: { state: true }, + _editingId: { state: true }, + _editColor: { state: true } + }), _defineProperty(_HassRecordsListCard, "styles", styles$16), _HassRecordsListCard); + HassRecordsListCard = __decorate([localized()], HassRecordsListCard); + //#endregion + //#region custom_components/hass_datapoints/src/cards/list/editor.styles.ts + var styles$11 = i$5``; + //#endregion + //#region custom_components/hass_datapoints/src/cards/list/editor.ts + var HassRecordsListCardEditor = class extends EditorBase { + constructor(..._args) { + super(..._args); + _defineProperty(this, "_onTargetChanged", (e) => { + const val = e.detail.value; + const isEmpty = !val || Object.values(val).every((value) => !value?.length); + this._setTargetConfig(isEmpty ? void 0 : val); + }); + } + _configTarget() { + return normalizeTargetValue(this._config.target ?? { entity_id: Array.isArray(this._config.entities) && this._config.entities.length ? this._config.entities : this._config.entity }) ?? {}; + } + _syncTargetPicker() { + const tp = this.shadowRoot?.querySelector("#target-picker"); + if (!tp) return; + if (this.hass) tp.hass = this.hass; + tp.value = this._configTarget(); + } + _setTargetConfig(target) { + const cfg = { ...this._config }; + delete cfg.entity; + delete cfg.entities; + if (!target || Object.values(target).every((value) => !value?.length)) delete cfg.target; + else cfg.target = target; + this._config = cfg; + this._fire(cfg); + } + updated(changedProps) { + if (changedProps.has("hass") || changedProps.has("_config")) this._syncTargetPicker(); + } + render() { + const c = this._config; + return b`
this._set("title", e2.detail.value)} + .value=${c.title || ""} + @dp-field-change=${(e) => this._set("title", e.detail.value)} > this._set("hours_to_show", e2.detail.value)} + .value=${c.hours_to_show != null ? String(c.hours_to_show) : ""} + @dp-field-change=${(e) => this._set("hours_to_show", e.detail.value)} > this._set("page_size", e2.detail.value)} + .value=${String(c.page_size ?? 15)} + @dp-field-change=${(e) => this._set("page_size", e.detail.value)} > this._set("message_filter", e2.detail.value)} + .value=${c.message_filter || ""} + @dp-field-change=${(e) => this._set("message_filter", e.detail.value)} > this._set("show_search", e2.detail.checked ? void 0 : false)} + .checked=${c.show_search !== false} + @dp-switch-change=${(e) => this._set("show_search", e.detail.checked ? void 0 : false)} > this._set("show_entities", e2.detail.checked ? void 0 : false)} + .checked=${c.show_entities !== false} + @dp-switch-change=${(e) => this._set("show_entities", e.detail.checked ? void 0 : false)} > this._set("show_actions", e2.detail.checked ? void 0 : false)} + .checked=${c.show_actions !== false} + @dp-switch-change=${(e) => this._set("show_actions", e.detail.checked ? void 0 : false)} > this._set( - "show_full_message", - e2.detail.checked ? void 0 : false - )} + .checked=${c.show_full_message !== false} + .tooltip=${msg("User will be able to expand the row if hidden", { id: "User will be able to expand the row if hidden" })} + @dp-switch-change=${(e) => this._set("show_full_message", e.detail.checked ? void 0 : false)} > @@ -35897,10 +34252,12 @@ ${content.alert}` : "", >
`; - } - } - __publicField$b(HassRecordsListCardEditor, "styles", [EditorBase.styles, styles$b]); - const styles$a = i$5` + } + }; + _defineProperty(HassRecordsListCardEditor, "styles", [EditorBase.styles, styles$11]); + //#endregion + //#region custom_components/hass_datapoints/src/cards/quick/quick.styles.ts + var styles$10 = i$5` :host { display: block; height: 100%; @@ -35956,7 +34313,9 @@ ${content.alert}` : "", margin: 0; } `; - const styles$9 = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/cards/quick/quick-annotation/quick-annotation.styles.ts + var styles$9 = i$5` :host { display: block; } @@ -35990,73 +34349,46 @@ ${content.alert}` : "", line-height: 1.45; } `; - var __create$5 = Object.create; - var __defProp$a = Object.defineProperty; - var __getOwnPropDesc$5 = Object.getOwnPropertyDescriptor; - var __knownSymbol$5 = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$5 = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$a = (obj, key, value) => key in obj ? __defProp$a(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$5 = (base) => [, , , __create$5(base?.[__knownSymbol$5("metadata")] ?? null)]; - var __decoratorStrings$5 = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$5 = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$5("Function expected") : fn; - var __decoratorContext$5 = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$5[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$5("Already initialized") : fns.push(__expectFn$5(fn || null)) }); - var __decoratorMetadata$5 = (array, target) => __defNormalProp$a(target, __knownSymbol$5("metadata"), array[3]); - var __runInitializers$5 = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$5 = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$5[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$5({ get [name]() { - return __privateGet$5(this, extra); - }, set [name](x2) { - return __privateSet$5(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$5(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$5(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$5("Object expected"); - else __expectFn$5(fn = it.get) && (desc.get = fn), __expectFn$5(fn = it.set) && (desc.set = fn), __expectFn$5(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$a(target, name, desc), target; - }; - var __publicField$a = (obj, key, value) => __defNormalProp$a(obj, key + "", value); - var __accessCheck$5 = (obj, member, msg2) => member.has(obj) || __typeError$5("Cannot " + msg2); - var __privateGet$5 = (obj, member, getter) => (__accessCheck$5(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$5 = (obj, member, value) => member.has(obj) ? __typeError$5("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$5 = (obj, member, value, setter) => (__accessCheck$5(obj, member, "write to private field"), member.set(obj, value), value); - var _value_dec$3, _placeholder_dec, _label_dec$2, _a$5, _init$5, _label$2, _placeholder, _value$3; - class CardQuickAnnotation extends (_a$5 = i$2, _label_dec$2 = [n({ type: String })], _placeholder_dec = [n({ type: String })], _value_dec$3 = [n({ type: String })], _a$5) { - constructor() { - super(...arguments); - __privateAdd$5(this, _label$2, __runInitializers$5(_init$5, 8, this, "Annotation")), __runInitializers$5(_init$5, 11, this); - __privateAdd$5(this, _placeholder, __runInitializers$5(_init$5, 12, this, "Detailed note shown on chart hover…")), __runInitializers$5(_init$5, 15, this); - __privateAdd$5(this, _value$3, __runInitializers$5(_init$5, 16, this, "")), __runInitializers$5(_init$5, 19, this); - } - _onInput(event) { - this.value = event.currentTarget.value; - this.dispatchEvent( - new CustomEvent("dp-annotation-input", { - detail: { - value: this.value - }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/cards/quick/quick-annotation/quick-annotation.ts + var _label_accessor_storage$2 = /* @__PURE__ */ new WeakMap(); + var _placeholder_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _value_accessor_storage$3 = /* @__PURE__ */ new WeakMap(); + var CardQuickAnnotation = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _label_accessor_storage$2, "Annotation"); + _classPrivateFieldInitSpec(this, _placeholder_accessor_storage, "Detailed note shown on chart hover…"); + _classPrivateFieldInitSpec(this, _value_accessor_storage$3, ""); + } + get label() { + return _classPrivateFieldGet2(_label_accessor_storage$2, this); + } + set label(value) { + _classPrivateFieldSet2(_label_accessor_storage$2, this, value); + } + get placeholder() { + return _classPrivateFieldGet2(_placeholder_accessor_storage, this); + } + set placeholder(value) { + _classPrivateFieldSet2(_placeholder_accessor_storage, this, value); + } + get value() { + return _classPrivateFieldGet2(_value_accessor_storage$3, this); + } + set value(value) { + _classPrivateFieldSet2(_value_accessor_storage$3, this, value); + } + _onInput(event) { + this.value = event.currentTarget.value; + this.dispatchEvent(new CustomEvent("dp-annotation-input", { + detail: { value: this.value }, + bubbles: true, + composed: true + })); + } + render() { + return b`
`; - } - } - _init$5 = __decoratorStart$5(_a$5); - _label$2 = /* @__PURE__ */ new WeakMap(); - _placeholder = /* @__PURE__ */ new WeakMap(); - _value$3 = /* @__PURE__ */ new WeakMap(); - __decorateElement$5(_init$5, 4, "label", _label_dec$2, CardQuickAnnotation, _label$2); - __decorateElement$5(_init$5, 4, "placeholder", _placeholder_dec, CardQuickAnnotation, _placeholder); - __decorateElement$5(_init$5, 4, "value", _value_dec$3, CardQuickAnnotation, _value$3); - __decoratorMetadata$5(_init$5, CardQuickAnnotation); - __publicField$a(CardQuickAnnotation, "styles", styles$9); - customElements.define("quick-annotation", CardQuickAnnotation); - var __defProp$9 = Object.defineProperty; - var __defNormalProp$9 = (obj, key, value) => key in obj ? __defProp$9(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$9 = (obj, key, value) => __defNormalProp$9(obj, typeof key !== "symbol" ? key + "" : key, value); - class HassRecordsQuickCard extends i$2 { - constructor() { - super(); - this._config = {}; - this._hass = null; - this._feedbackClass = ""; - this._feedbackText = ""; - this._feedbackVisible = false; - this._annotation = ""; - } - setConfig(config) { - this._config = config || {}; - } - set hass(hass) { - this._hass = hass; - } - get hass() { - return this._hass; - } - firstUpdated() { - const msgEl = this.shadowRoot?.querySelector("#msg"); - if (msgEl) { - msgEl.addEventListener("keydown", (event) => { - if (event.key === "Enter") { - event.preventDefault(); - this._record(); - } - }); - } - } - async _record() { - const msgEl = this.shadowRoot?.querySelector("#msg"); - const message = (msgEl?.value || "").trim(); - if (!message) { - msgEl?.focus(); - return; - } - const btn = this.shadowRoot?.querySelector("#btn"); - if (btn) { - btn.disabled = true; - } - const cfg = this._config; - const data = { - message, - icon: cfg.icon || "mdi:bookmark", - color: cfg.color || AMBER - }; - const annotation = this._annotation.trim(); - if (annotation) { - data.annotation = annotation; - } - let entityIds; - if (cfg.target) { - entityIds = resolveEntityIdsFromTarget(this._hass, cfg.target); - } else if (cfg.entity) { - entityIds = [cfg.entity]; - } else if (cfg.entities) { - entityIds = Array.isArray(cfg.entities) ? cfg.entities : [cfg.entities]; - } else { - entityIds = []; - } - if (entityIds.length) { - data.entity_ids = entityIds; - } - try { - const hass = this._hass; - if (!hass) { - return; - } - await hass.callService(DOMAIN, "record", data); - window.dispatchEvent(new CustomEvent("hass-datapoints-event-recorded")); - if (msgEl) { - msgEl.value = ""; - } - this._annotation = ""; - this._feedbackClass = "ok"; - this._feedbackText = "Recorded!"; - this._feedbackVisible = true; - setTimeout(() => { - if (this) { - this._feedbackVisible = false; - } - }, 2500); - } catch (e2) { - const error = e2; - this._feedbackClass = "err"; - this._feedbackText = `Error: ${error.message || "unknown error"}`; - this._feedbackVisible = true; - logger$1.error("[hass-datapoints quick-card]", e2); - } - if (btn) { - btn.disabled = false; - } - } - get _isAdmin() { - return this._hass?.user?.is_admin === true; - } - render() { - const cfg = this._config; - const cfgIcon = cfg.icon || "mdi:bookmark"; - const cfgColor = cfg.color || AMBER; - const hasTitle = !!cfg.title; - const showAnnotation = !!cfg.show_annotation; - return b` + } + }; + _defineProperty(CardQuickAnnotation, "styles", styles$9); + __decorate([n$1({ type: String })], CardQuickAnnotation.prototype, "label", null); + __decorate([n$1({ type: String })], CardQuickAnnotation.prototype, "placeholder", null); + __decorate([n$1({ type: String })], CardQuickAnnotation.prototype, "value", null); + customElements.define("quick-annotation", CardQuickAnnotation); + //#endregion + //#region custom_components/hass_datapoints/src/cards/quick/quick.ts + var HassRecordsQuickCard = class extends i$2 { + constructor() { + super(); + this._config = {}; + this._hass = null; + this._feedbackClass = ""; + this._feedbackText = ""; + this._feedbackVisible = false; + this._annotation = ""; + } + setConfig(config) { + this._config = config || {}; + } + set hass(hass) { + this._hass = hass; + } + get hass() { + return this._hass; + } + firstUpdated() { + const msgEl = this.shadowRoot?.querySelector("#msg"); + if (msgEl) msgEl.addEventListener("keydown", (event) => { + if (event.key === "Enter") { + event.preventDefault(); + this._record(); + } + }); + } + async _record() { + const msgEl = this.shadowRoot?.querySelector("#msg"); + const message = (msgEl?.value || "").trim(); + if (!message) { + msgEl?.focus(); + return; + } + const btn = this.shadowRoot?.querySelector("#btn"); + if (btn) btn.disabled = true; + const cfg = this._config; + const data = { + message, + icon: cfg.icon || "mdi:bookmark", + color: cfg.color || "#ff9800" + }; + const annotation = this._annotation.trim(); + if (annotation) data.annotation = annotation; + let entityIds; + if (cfg.target) entityIds = resolveEntityIdsFromTarget(this._hass, cfg.target); + else if (cfg.entity) entityIds = [cfg.entity]; + else if (cfg.entities) entityIds = Array.isArray(cfg.entities) ? cfg.entities : [cfg.entities]; + else entityIds = []; + if (entityIds.length) data.entity_ids = entityIds; + try { + const hass = this._hass; + if (!hass) return; + await hass.callService(DOMAIN, "record", data); + window.dispatchEvent(new CustomEvent("hass-datapoints-event-recorded")); + if (msgEl) msgEl.value = ""; + this._annotation = ""; + this._feedbackClass = "ok"; + this._feedbackText = "Recorded!"; + this._feedbackVisible = true; + setTimeout(() => { + if (this) this._feedbackVisible = false; + }, 2500); + } catch (e) { + const error = e; + this._feedbackClass = "err"; + this._feedbackText = `Error: ${error.message || "unknown error"}`; + this._feedbackVisible = true; + logger$1.error("[hass-datapoints quick-card]", e); + } + if (btn) btn.disabled = false; + } + get _isAdmin() { + return this._hass?.user?.is_admin === true; + } + render() { + const cfg = this._config; + const cfgIcon = cfg.icon || "mdi:bookmark"; + const cfgColor = cfg.color || "#ff9800"; + const hasTitle = !!cfg.title; + const showAnnotation = !!cfg.show_annotation; + return b` ${hasTitle ? b`
${cfg.title}
` : ""} ${this._isAdmin ? b` @@ -36209,8 +34514,8 @@ ${content.alert}` : "", { - this._annotation = event.detail.value; - }} + this._annotation = event.detail.value; + }} > ` : ""} @@ -36227,129 +34532,128 @@ ${content.alert}` : "", `}
`; - } - static getConfigElement() { - return document.createElement("hass-datapoints-quick-card-editor"); - } - static getStubConfig() { - return { title: "Quick Record" }; - } - getGridOptions() { - const hasAnnotation = !!this._config?.show_annotation; - const hasTitle = !!this._config?.title; - const baseRows = hasAnnotation ? 3 : 1; - const rows = hasTitle ? baseRows + 1 : baseRows; - return { - rows, - min_rows: rows, - max_rows: rows - }; - } - getCardSize() { - const baseRows = this._config?.show_annotation ? 3 : 1; - return this._config?.title ? baseRows + 1 : baseRows; - } - } - __publicField$9(HassRecordsQuickCard, "properties", { - _config: { type: Object, state: true }, - _hass: { type: Object, state: true }, - _feedbackClass: { type: String, state: true }, - _feedbackText: { type: String, state: true }, - _feedbackVisible: { type: Boolean, state: true }, - _annotation: { type: String, state: true } - }); - __publicField$9(HassRecordsQuickCard, "styles", styles$a); - const styles$8 = i$5` + } + static getConfigElement() { + return document.createElement("hass-datapoints-quick-card-editor"); + } + static getStubConfig() { + return { title: "Quick Record" }; + } + getGridOptions() { + const hasAnnotation = !!this._config?.show_annotation; + const hasTitle = !!this._config?.title; + const baseRows = hasAnnotation ? 3 : 1; + const rows = hasTitle ? baseRows + 1 : baseRows; + return { + rows, + min_rows: rows, + max_rows: rows + }; + } + getCardSize() { + const baseRows = this._config?.show_annotation ? 3 : 1; + return this._config?.title ? baseRows + 1 : baseRows; + } + }; + _defineProperty(HassRecordsQuickCard, "properties", { + _config: { + type: Object, + state: true + }, + _hass: { + type: Object, + state: true + }, + _feedbackClass: { + type: String, + state: true + }, + _feedbackText: { + type: String, + state: true + }, + _feedbackVisible: { + type: Boolean, + state: true + }, + _annotation: { + type: String, + state: true + } + }); + _defineProperty(HassRecordsQuickCard, "styles", styles$10); + //#endregion + //#region custom_components/hass_datapoints/src/cards/quick/editor.styles.ts + var styles$8 = i$5` .note { font-size: 0.78rem; color: var(--secondary-text-color); } `; - var __defProp$8 = Object.defineProperty; - var __defNormalProp$8 = (obj, key, value) => key in obj ? __defProp$8(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$8 = (obj, key, value) => __defNormalProp$8(obj, typeof key !== "symbol" ? key + "" : key, value); - class HassRecordsQuickCardEditor extends EditorBase { - constructor() { - super(...arguments); - __publicField$8(this, "_onTargetChanged", (e2) => { - const val = e2.detail.value; - const isEmpty = !val || Object.values(val).every((value) => !value?.length); - this._setTargetConfig(isEmpty ? void 0 : val); - }); - } - _configTarget() { - return normalizeTargetValue( - this._config.target ?? { - entity_id: Array.isArray(this._config.entities) && this._config.entities.length ? this._config.entities : this._config.entity - } - ) ?? {}; - } - _syncTargetPicker() { - const tp = this.shadowRoot?.querySelector( - "#target-picker" - ); - if (!tp) { - return; - } - if (this.hass) { - tp.hass = this.hass; - } - tp.value = this._configTarget(); - } - _setTargetConfig(target) { - const cfg = { ...this._config }; - delete cfg.entity; - delete cfg.entities; - if (!target || Object.values(target).every((value) => !value?.length)) { - delete cfg.target; - } else { - cfg.target = target; - } - this._config = cfg; - this._fire(cfg); - } - updated(changedProps) { - if (changedProps.has("hass") || changedProps.has("_config")) { - this._syncTargetPicker(); - } - } - render() { - const c2 = this._config; - return b` + //#endregion + //#region custom_components/hass_datapoints/src/cards/quick/editor.ts + var HassRecordsQuickCardEditor = class extends EditorBase { + constructor(..._args) { + super(..._args); + _defineProperty(this, "_onTargetChanged", (e) => { + const val = e.detail.value; + const isEmpty = !val || Object.values(val).every((value) => !value?.length); + this._setTargetConfig(isEmpty ? void 0 : val); + }); + } + _configTarget() { + return normalizeTargetValue(this._config.target ?? { entity_id: Array.isArray(this._config.entities) && this._config.entities.length ? this._config.entities : this._config.entity }) ?? {}; + } + _syncTargetPicker() { + const tp = this.shadowRoot?.querySelector("#target-picker"); + if (!tp) return; + if (this.hass) tp.hass = this.hass; + tp.value = this._configTarget(); + } + _setTargetConfig(target) { + const cfg = { ...this._config }; + delete cfg.entity; + delete cfg.entities; + if (!target || Object.values(target).every((value) => !value?.length)) delete cfg.target; + else cfg.target = target; + this._config = cfg; + this._fire(cfg); + } + updated(changedProps) { + if (changedProps.has("hass") || changedProps.has("_config")) this._syncTargetPicker(); + } + render() { + const c = this._config; + return b`
this._set("title", e2.detail.value)} + .value=${c.title || ""} + @dp-field-change=${(e) => this._set("title", e.detail.value)} > this._set("placeholder", e2.detail.value)} + .value=${c.placeholder || ""} + @dp-field-change=${(e) => this._set("placeholder", e.detail.value)} > this._set("icon", e2.detail.value)} + @dp-icon-change=${(e) => this._set("icon", e.detail.value)} > this._set("color", e2.detail.color)} + .color=${c.color || "#ff9800"} + @dp-color-change=${(e) => this._set("color", e.detail.color)} >
- ${msg( - "These items will be linked to every record made with this card.", - { - id: "These items will be linked to every record made with this card." - } - )} + ${msg("These items will be linked to every record made with this card.", { id: "These items will be linked to every record made with this card." })}
this._set("show_annotation", e2.detail.checked || void 0)} + .checked=${!!c.show_annotation} + @dp-switch-change=${(e) => this._set("show_annotation", e.detail.checked || void 0)} >
`; - } - } - __publicField$8(HassRecordsQuickCardEditor, "styles", [EditorBase.styles, styles$8]); - const styles$7 = i$5` + } + }; + _defineProperty(HassRecordsQuickCardEditor, "styles", [EditorBase.styles, styles$8]); + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/sensor.styles.ts + var styles$7 = i$5` :host { display: block; height: 100%; @@ -36429,7 +34735,9 @@ ${content.alert}` : "", position: relative; } `; - const styles$6 = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/sensor-header/sensor-header.styles.ts + var styles$6 = i$5` :host { display: block; } @@ -36496,73 +34804,62 @@ ${content.alert}` : "", margin-inline-start: initial; } `; - var __create$4 = Object.create; - var __defProp$7 = Object.defineProperty; - var __getOwnPropDesc$4 = Object.getOwnPropertyDescriptor; - var __knownSymbol$4 = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$4 = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$7 = (obj, key, value) => key in obj ? __defProp$7(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$4 = (base) => [, , , __create$4(base?.[__knownSymbol$4("metadata")] ?? null)]; - var __decoratorStrings$4 = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$4 = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$4("Function expected") : fn; - var __decoratorContext$4 = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$4[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$4("Already initialized") : fns.push(__expectFn$4(fn || null)) }); - var __decoratorMetadata$4 = (array, target) => __defNormalProp$7(target, __knownSymbol$4("metadata"), array[3]); - var __runInitializers$4 = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$4 = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$4[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$4({ get [name]() { - return __privateGet$4(this, extra); - }, set [name](x2) { - return __privateSet$4(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$4(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$4(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$4("Object expected"); - else __expectFn$4(fn = it.get) && (desc.get = fn), __expectFn$4(fn = it.set) && (desc.set = fn), __expectFn$4(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$7(target, name, desc), target; - }; - var __publicField$7 = (obj, key, value) => __defNormalProp$7(obj, key + "", value); - var __accessCheck$4 = (obj, member, msg2) => member.has(obj) || __typeError$4("Cannot " + msg2); - var __privateGet$4 = (obj, member, getter) => (__accessCheck$4(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$4 = (obj, member, value) => member.has(obj) ? __typeError$4("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$4 = (obj, member, value, setter) => (__accessCheck$4(obj, member, "write to private field"), member.set(obj, value), value); - var _hass_dec$1, _stateObj_dec, _unit_dec, _value_dec$2, _name_dec, _a$4, _init$4, _name, _value$2, _unit, _stateObj, _hass$1; - class SensorHeader extends (_a$4 = i$2, _name_dec = [n({ type: String })], _value_dec$2 = [n({ type: String })], _unit_dec = [n({ type: String })], _stateObj_dec = [n({ type: Object, attribute: false })], _hass_dec$1 = [n({ type: Object, attribute: false })], _a$4) { - constructor() { - super(...arguments); - __privateAdd$4(this, _name, __runInitializers$4(_init$4, 8, this, "—")), __runInitializers$4(_init$4, 11, this); - __privateAdd$4(this, _value$2, __runInitializers$4(_init$4, 12, this, "—")), __runInitializers$4(_init$4, 15, this); - __privateAdd$4(this, _unit, __runInitializers$4(_init$4, 16, this, "")), __runInitializers$4(_init$4, 19, this); - __privateAdd$4(this, _stateObj, __runInitializers$4(_init$4, 20, this, null)), __runInitializers$4(_init$4, 23, this); - __privateAdd$4(this, _hass$1, __runInitializers$4(_init$4, 24, this, null)), __runInitializers$4(_init$4, 27, this); - } - _onHeaderClick(event) { - event.preventDefault(); - event.stopPropagation(); - this.dispatchEvent( - new CustomEvent("dp-sensor-header-click", { - bubbles: true, - composed: true - }) - ); - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/sensor-header/sensor-header.ts + var _name_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _value_accessor_storage$2 = /* @__PURE__ */ new WeakMap(); + var _unit_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _stateObj_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _hass_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var SensorHeader = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _name_accessor_storage, "—"); + _classPrivateFieldInitSpec(this, _value_accessor_storage$2, "—"); + _classPrivateFieldInitSpec(this, _unit_accessor_storage, ""); + _classPrivateFieldInitSpec(this, _stateObj_accessor_storage, null); + _classPrivateFieldInitSpec(this, _hass_accessor_storage$1, null); + } + get name() { + return _classPrivateFieldGet2(_name_accessor_storage, this); + } + set name(value) { + _classPrivateFieldSet2(_name_accessor_storage, this, value); + } + get value() { + return _classPrivateFieldGet2(_value_accessor_storage$2, this); + } + set value(value) { + _classPrivateFieldSet2(_value_accessor_storage$2, this, value); + } + get unit() { + return _classPrivateFieldGet2(_unit_accessor_storage, this); + } + set unit(value) { + _classPrivateFieldSet2(_unit_accessor_storage, this, value); + } + get stateObj() { + return _classPrivateFieldGet2(_stateObj_accessor_storage, this); + } + set stateObj(value) { + _classPrivateFieldSet2(_stateObj_accessor_storage, this, value); + } + get hass() { + return _classPrivateFieldGet2(_hass_accessor_storage$1, this); + } + set hass(value) { + _classPrivateFieldSet2(_hass_accessor_storage$1, this, value); + } + _onHeaderClick(event) { + event.preventDefault(); + event.stopPropagation(); + this.dispatchEvent(new CustomEvent("dp-sensor-header-click", { + bubbles: true, + composed: true + })); + } + render() { + return b`
${this.name}
@@ -36577,23 +34874,24 @@ ${content.alert}` : "", ${this.unit}
`; - } - } - _init$4 = __decoratorStart$4(_a$4); - _name = /* @__PURE__ */ new WeakMap(); - _value$2 = /* @__PURE__ */ new WeakMap(); - _unit = /* @__PURE__ */ new WeakMap(); - _stateObj = /* @__PURE__ */ new WeakMap(); - _hass$1 = /* @__PURE__ */ new WeakMap(); - __decorateElement$4(_init$4, 4, "name", _name_dec, SensorHeader, _name); - __decorateElement$4(_init$4, 4, "value", _value_dec$2, SensorHeader, _value$2); - __decorateElement$4(_init$4, 4, "unit", _unit_dec, SensorHeader, _unit); - __decorateElement$4(_init$4, 4, "stateObj", _stateObj_dec, SensorHeader, _stateObj); - __decorateElement$4(_init$4, 4, "hass", _hass_dec$1, SensorHeader, _hass$1); - __decoratorMetadata$4(_init$4, SensorHeader); - __publicField$7(SensorHeader, "styles", styles$6); - customElements.define("sensor-header", SensorHeader); - const styles$5 = i$5` + } + }; + _defineProperty(SensorHeader, "styles", styles$6); + __decorate([n$1({ type: String })], SensorHeader.prototype, "name", null); + __decorate([n$1({ type: String })], SensorHeader.prototype, "value", null); + __decorate([n$1({ type: String })], SensorHeader.prototype, "unit", null); + __decorate([n$1({ + type: Object, + attribute: false + })], SensorHeader.prototype, "stateObj", null); + __decorate([n$1({ + type: Object, + attribute: false + })], SensorHeader.prototype, "hass", null); + customElements.define("sensor-header", SensorHeader); + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/sensor-chart/sensor-chart.styles.ts + var styles$5 = i$5` :host { display: block; height: 100%; @@ -36746,338 +35044,293 @@ ${content.alert}` : "", } } `; - var __defProp$6 = Object.defineProperty; - var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$6 = (obj, key, value) => __defNormalProp$6(obj, typeof key !== "symbol" ? key + "" : key, value); - class SensorChart extends i$2 { - constructor() { - super(); - __publicField$6(this, "_hass", null); - __publicField$6(this, "_canvasClickHandler", null); - __publicField$6(this, "_canvasMoveHandler", null); - __publicField$6(this, "_canvasLeaveHandler", null); - __publicField$6(this, "_previousSeriesEndpoints", /* @__PURE__ */ new Map()); - __publicField$6(this, "_lastDrawArgs", null); - __publicField$6(this, "_resizeObserver", null); - this._chartReady = false; - this._loadMessage = "Loading…"; - this.showAnnotationTooltips = false; - } - get hass() { - return this._hass; - } - set hass(value) { - this._hass = value; - } - firstUpdated() { - this._setupResizeObserver(); - } - connectedCallback() { - super.connectedCallback(); - this._setupResizeObserver(); - } - disconnectedCallback() { - super.disconnectedCallback(); - if (this._resizeObserver) { - this._resizeObserver.disconnect(); - this._resizeObserver = null; - } - const canvas = this.shadowRoot?.querySelector("canvas#chart"); - if (canvas && this._canvasClickHandler) - canvas.removeEventListener("click", this._canvasClickHandler); - if (canvas && this._canvasMoveHandler) - canvas.removeEventListener("mousemove", this._canvasMoveHandler); - if (canvas && this._canvasLeaveHandler) - canvas.removeEventListener("mouseleave", this._canvasLeaveHandler); - } - _findHitAtPointer(hits, clientX, clientY, canvas) { - const rect = canvas.getBoundingClientRect(); - const x2 = clientX - rect.left; - const y2 = clientY - rect.top; - return hits.reduce( - (closest, hit) => { - const dist = Math.hypot(hit.x - x2, hit.y - y2); - if (dist > 18) { - return closest; - } - if (!closest || dist < closest.dist) { - return { hit, dist }; - } - return closest; - }, - null - )?.hit ?? null; - } - _showAnnotationTooltip(hit, clientX, clientY, unit) { - const wrap = this.shadowRoot?.querySelector(".chart-wrap"); - const tooltip = this.shadowRoot?.querySelector("#tooltip"); - const timeEl = this.shadowRoot?.querySelector("#tt-time"); - const valueEl = this.shadowRoot?.querySelector("#tt-value"); - const dotEl = this.shadowRoot?.querySelector("#tt-dot"); - const messageEl = this.shadowRoot?.querySelector("#tt-message"); - const rowEl = this.shadowRoot?.querySelector("#tt-message-row"); - const annotationEl = this.shadowRoot?.querySelector("#tt-annotation"); - const entitiesEl = this.shadowRoot?.querySelector("#tt-entities"); - if (!tooltip || !wrap || !timeEl || !valueEl || !dotEl || !messageEl || !rowEl || !annotationEl || !entitiesEl) { - return; - } - const eventTime = new Date(hit.event.timestamp); - timeEl.textContent = eventTime.toLocaleString(); - valueEl.style.display = "block"; - valueEl.textContent = Number.isFinite(hit.value) ? `${hit.value.toFixed(2)}${unit ? ` ${unit}` : ""}` : ""; - dotEl.style.background = hit.event.color || "#03a9f4"; - messageEl.textContent = hit.event.message || ""; - rowEl.style.display = "flex"; - if (hit.event.annotation) { - annotationEl.style.display = "block"; - annotationEl.textContent = hit.event.annotation; - } else { - annotationEl.style.display = "none"; - annotationEl.textContent = ""; - } - entitiesEl.style.display = "none"; - entitiesEl.textContent = ""; - tooltip.style.display = "block"; - const wrapRect = wrap.getBoundingClientRect(); - const tooltipRect = tooltip.getBoundingClientRect(); - const offset = 12; - const maxLeft = Math.max(8, wrapRect.width - tooltipRect.width - 8); - const maxTop = Math.max(8, wrapRect.height - tooltipRect.height - 8); - const localLeft = Math.min( - maxLeft, - Math.max(8, clientX - wrapRect.left + offset) - ); - const localTop = Math.min( - maxTop, - Math.max(8, clientY - wrapRect.top + offset) - ); - tooltip.style.left = `${localLeft}px`; - tooltip.style.top = `${localTop}px`; - } - _hideAnnotationTooltip() { - const tooltip = this.shadowRoot?.querySelector("#tooltip"); - if (tooltip) { - tooltip.style.display = "none"; - } - } - _attachTooltipHitTarget(overlay, hit, unit) { - const el = document.createElement("div"); - el.className = "ann-hit"; - el.style.left = `${hit.x}px`; - el.style.top = `${hit.y}px`; - el.dataset.eventId = hit.event.id; - this._attachTooltipInteractions(el, hit, unit); - overlay.appendChild(el); - } - _attachTooltipInteractions(el, hit, unit) { - const show = (clientX, clientY) => { - this._showAnnotationTooltip(hit, clientX, clientY, unit); - }; - el.addEventListener("mouseenter", (e2) => { - show(e2.clientX, e2.clientY); - }); - el.addEventListener("mousemove", (e2) => { - show(e2.clientX, e2.clientY); - }); - el.addEventListener("click", (e2) => { - e2.preventDefault(); - e2.stopPropagation(); - show(e2.clientX, e2.clientY); - }); - el.addEventListener("mouseleave", () => { - this._hideAnnotationTooltip(); - }); - } - _setupResizeObserver() { - if (this._resizeObserver) return; - if (!window.ResizeObserver) return; - this._resizeObserver = new ResizeObserver(() => { - if (this._lastDrawArgs) this.draw(...this._lastDrawArgs); - }); - this._resizeObserver.observe(this); - } - /** Draw the chart. Called by the parent card after data loads or on resize/toggle. */ - draw(histResult, events, t0, t1, config, unit, hiddenEventIds) { - this._lastDrawArgs = [ - histResult, - events, - t0, - t1, - config, - unit, - hiddenEventIds - ]; - const canvas = this.shadowRoot?.querySelector("canvas#chart"); - const wrap = this.shadowRoot?.querySelector(".chart-wrap"); - if (!canvas || !wrap) return; - const { w, h: h2 } = setupCanvas(canvas, wrap, null); - const renderer = new ChartRenderer(canvas, w, h2); - const topPadPx = Math.max(6, Math.round(h2 * 0.05)); - renderer.pad = { top: topPadPx, right: 0, bottom: 0, left: 0 }; - renderer.clear(); - const entityId = config.entity; - const lineColor = config.graph_color || COLORS[0]; - const stateList = this._getHistoryStatesForEntity(entityId, histResult); - const pts = []; - const allVals = []; - for (const s2 of stateList) { - const v2 = parseFloat(s2.s); - if (!Number.isNaN(v2)) { - pts.push([Math.round(s2.lu * 1e3), v2]); - allVals.push(v2); - } - } - if (!allVals.length) { - this._loadMessage = "No numeric data in the selected time range."; - this._chartReady = false; - return; - } - this._loadMessage = ""; - this._chartReady = true; - const annotationStyle = config.annotation_style ?? "circle"; - const visibleEvents = events.filter((ev) => !hiddenEventIds.has(ev.id)); - const series = [{ entityId, pts, color: lineColor }]; - const vMin = Math.min(...allVals); - const vMax = Math.max(...allVals); - const range = vMax - vMin; - const chartMin = vMin - (range * 0.03 || 0.2); - const chartMax = vMax + (range * 0.54 || 0.8); - for (const s2 of series) { - renderer.drawLine(s2.pts, s2.color, t0, t1, chartMin, chartMax, { - fillAlpha: 0.18 - }); - if (s2.pts.length) { - const lastPt = s2.pts[s2.pts.length - 1]; - const prev = this._previousSeriesEndpoints.get(s2.entityId); - if (prev && (lastPt[0] !== prev.t || lastPt[1] !== prev.v)) { - const cx = renderer.xOf(lastPt[0], t0, t1); - const cy = renderer.yOf(lastPt[1], chartMin, chartMax); - renderer.drawBlip(cx, cy, s2.color); - } - this._previousSeriesEndpoints.set(s2.entityId, { - t: lastPt[0], - v: lastPt[1] - }); - } - } - const hits = annotationStyle === "line" ? renderer.drawAnnotationLinesOnLine( - visibleEvents, - series, - t0, - t1, - chartMin, - chartMax - ) : renderer.drawAnnotationsOnLine( - visibleEvents, - series, - t0, - t1, - chartMin, - chartMax - ); - const overlay = this.shadowRoot?.querySelector(".icon-overlay"); - if (overlay) { - overlay.innerHTML = ""; - if (annotationStyle === "circle") { - for (const hit of hits) { - const bgColor = hit.event.color || "#03a9f4"; - const el = document.createElement("div"); - el.className = "ann-icon"; - el.style.left = `${hit.x}px`; - el.style.top = `${hit.y}px`; - el.style.background = bgColor; - el.innerHTML = ``; - el.dataset.eventId = hit.event.id; - el.addEventListener("click", (e2) => { - if (this.showAnnotationTooltips) { - e2.preventDefault(); - e2.stopPropagation(); - this._showAnnotationTooltip(hit, e2.clientX, e2.clientY, unit); - return; - } - e2.preventDefault(); - e2.stopPropagation(); - this._emitAnnotationClick(hit.event); - }); - if (this.showAnnotationTooltips) { - this._attachTooltipInteractions(el, hit, unit); - } - overlay.appendChild(el); - if (this.showAnnotationTooltips) { - this._attachTooltipHitTarget(overlay, hit, unit); - } - } - } else if (this.showAnnotationTooltips) { - for (const hit of hits) { - this._attachTooltipHitTarget(overlay, hit, unit); - } - } - } - if (this._canvasClickHandler) - canvas.removeEventListener("click", this._canvasClickHandler); - if (this._canvasMoveHandler) - canvas.removeEventListener("mousemove", this._canvasMoveHandler); - if (this._canvasLeaveHandler) - canvas.removeEventListener("mouseleave", this._canvasLeaveHandler); - this._canvasMoveHandler = () => { - this._hideAnnotationTooltip(); - }; - this._canvasLeaveHandler = () => { - this._hideAnnotationTooltip(); - }; - this._canvasClickHandler = (e2) => { - const best = this._findHitAtPointer(hits, e2.clientX, e2.clientY, canvas); - if (best) { - if (this.showAnnotationTooltips) { - e2.preventDefault(); - e2.stopPropagation(); - this._showAnnotationTooltip(best, e2.clientX, e2.clientY, unit); - return; - } - e2.preventDefault(); - e2.stopPropagation(); - this._emitAnnotationClick(best.event); - } - }; - canvas.addEventListener("mousemove", this._canvasMoveHandler); - canvas.addEventListener("mouseleave", this._canvasLeaveHandler); - canvas.addEventListener("click", this._canvasClickHandler); - } - _emitAnnotationClick(event) { - this.dispatchEvent( - new CustomEvent("dp-sensor-annotation-click", { - detail: { event }, - bubbles: true, - composed: true - }) - ); - } - _getHistoryStatesForEntity(entityId, histResult) { - if (!histResult) return []; - const r2 = histResult; - if (Array.isArray(r2[entityId])) return r2[entityId]; - if (Array.isArray(r2)) { - const rArr = r2; - if (Array.isArray(rArr[0])) return rArr[0] || []; - if (rArr.every( - (e2) => e2 && typeof e2 === "object" && !Array.isArray(e2) - )) - return rArr.filter( - (e2) => e2.entity_id === entityId - ); - } - const rObj = histResult; - if (rObj && typeof rObj === "object") { - const result = rObj.result; - if (Array.isArray(result?.[entityId])) - return result[entityId]; - if (Array.isArray(result?.[0])) - return result[0] || []; - } - return []; - } - render() { - return b` + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/sensor-chart/sensor-chart.ts + var SensorChart = class extends i$2 { + get hass() { + return this._hass; + } + set hass(value) { + this._hass = value; + } + constructor() { + super(); + _defineProperty(this, "_hass", null); + _defineProperty(this, "_canvasClickHandler", null); + _defineProperty(this, "_canvasMoveHandler", null); + _defineProperty(this, "_canvasLeaveHandler", null); + _defineProperty(this, "_previousSeriesEndpoints", /* @__PURE__ */ new Map()); + _defineProperty(this, "_lastDrawArgs", null); + _defineProperty(this, "_resizeObserver", null); + this._chartReady = false; + this._loadMessage = "Loading…"; + this.showAnnotationTooltips = false; + } + firstUpdated() { + this._setupResizeObserver(); + } + connectedCallback() { + super.connectedCallback(); + this._setupResizeObserver(); + } + disconnectedCallback() { + super.disconnectedCallback(); + if (this._resizeObserver) { + this._resizeObserver.disconnect(); + this._resizeObserver = null; + } + const canvas = this.shadowRoot?.querySelector("canvas#chart"); + if (canvas && this._canvasClickHandler) canvas.removeEventListener("click", this._canvasClickHandler); + if (canvas && this._canvasMoveHandler) canvas.removeEventListener("mousemove", this._canvasMoveHandler); + if (canvas && this._canvasLeaveHandler) canvas.removeEventListener("mouseleave", this._canvasLeaveHandler); + } + _findHitAtPointer(hits, clientX, clientY, canvas) { + const rect = canvas.getBoundingClientRect(); + const x = clientX - rect.left; + const y = clientY - rect.top; + return hits.reduce((closest, hit) => { + const dist = Math.hypot(hit.x - x, hit.y - y); + if (dist > 18) return closest; + if (!closest || dist < closest.dist) return { + hit, + dist + }; + return closest; + }, null)?.hit ?? null; + } + _showAnnotationTooltip(hit, clientX, clientY, unit) { + const wrap = this.shadowRoot?.querySelector(".chart-wrap"); + const tooltip = this.shadowRoot?.querySelector("#tooltip"); + const timeEl = this.shadowRoot?.querySelector("#tt-time"); + const valueEl = this.shadowRoot?.querySelector("#tt-value"); + const dotEl = this.shadowRoot?.querySelector("#tt-dot"); + const messageEl = this.shadowRoot?.querySelector("#tt-message"); + const rowEl = this.shadowRoot?.querySelector("#tt-message-row"); + const annotationEl = this.shadowRoot?.querySelector("#tt-annotation"); + const entitiesEl = this.shadowRoot?.querySelector("#tt-entities"); + if (!tooltip || !wrap || !timeEl || !valueEl || !dotEl || !messageEl || !rowEl || !annotationEl || !entitiesEl) return; + timeEl.textContent = new Date(hit.event.timestamp).toLocaleString(); + valueEl.style.display = "block"; + valueEl.textContent = Number.isFinite(hit.value) ? `${hit.value.toFixed(2)}${unit ? ` ${unit}` : ""}` : ""; + dotEl.style.background = hit.event.color || "#03a9f4"; + messageEl.textContent = hit.event.message || ""; + rowEl.style.display = "flex"; + if (hit.event.annotation) { + annotationEl.style.display = "block"; + annotationEl.textContent = hit.event.annotation; + } else { + annotationEl.style.display = "none"; + annotationEl.textContent = ""; + } + entitiesEl.style.display = "none"; + entitiesEl.textContent = ""; + tooltip.style.display = "block"; + const wrapRect = wrap.getBoundingClientRect(); + const tooltipRect = tooltip.getBoundingClientRect(); + const offset = 12; + const maxLeft = Math.max(8, wrapRect.width - tooltipRect.width - 8); + const maxTop = Math.max(8, wrapRect.height - tooltipRect.height - 8); + const localLeft = Math.min(maxLeft, Math.max(8, clientX - wrapRect.left + offset)); + const localTop = Math.min(maxTop, Math.max(8, clientY - wrapRect.top + offset)); + tooltip.style.left = `${localLeft}px`; + tooltip.style.top = `${localTop}px`; + } + _hideAnnotationTooltip() { + const tooltip = this.shadowRoot?.querySelector("#tooltip"); + if (tooltip) tooltip.style.display = "none"; + } + _attachTooltipHitTarget(overlay, hit, unit) { + const el = document.createElement("div"); + el.className = "ann-hit"; + el.style.left = `${hit.x}px`; + el.style.top = `${hit.y}px`; + el.dataset.eventId = hit.event.id; + this._attachTooltipInteractions(el, hit, unit); + overlay.appendChild(el); + } + _attachTooltipInteractions(el, hit, unit) { + const show = (clientX, clientY) => { + this._showAnnotationTooltip(hit, clientX, clientY, unit); + }; + el.addEventListener("mouseenter", (e) => { + show(e.clientX, e.clientY); + }); + el.addEventListener("mousemove", (e) => { + show(e.clientX, e.clientY); + }); + el.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + show(e.clientX, e.clientY); + }); + el.addEventListener("mouseleave", () => { + this._hideAnnotationTooltip(); + }); + } + _setupResizeObserver() { + if (this._resizeObserver) return; + if (!window.ResizeObserver) return; + this._resizeObserver = new ResizeObserver(() => { + if (this._lastDrawArgs) this.draw(...this._lastDrawArgs); + }); + this._resizeObserver.observe(this); + } + /** Draw the chart. Called by the parent card after data loads or on resize/toggle. */ + draw(histResult, events, t0, t1, config, unit, hiddenEventIds) { + this._lastDrawArgs = [ + histResult, + events, + t0, + t1, + config, + unit, + hiddenEventIds + ]; + const canvas = this.shadowRoot?.querySelector("canvas#chart"); + const wrap = this.shadowRoot?.querySelector(".chart-wrap"); + if (!canvas || !wrap) return; + const { w, h } = setupCanvas(canvas, wrap, null); + const renderer = new ChartRenderer(canvas, w, h); + renderer.pad = { + top: Math.max(6, Math.round(h * .05)), + right: 0, + bottom: 0, + left: 0 + }; + renderer.clear(); + const entityId = config.entity; + const lineColor = config.graph_color || COLORS[0]; + const stateList = this._getHistoryStatesForEntity(entityId, histResult); + const pts = []; + const allVals = []; + for (const s of stateList) { + const v = parseFloat(s.s); + if (!Number.isNaN(v)) { + pts.push([Math.round(s.lu * 1e3), v]); + allVals.push(v); + } + } + if (!allVals.length) { + this._loadMessage = "No numeric data in the selected time range."; + this._chartReady = false; + return; + } + this._loadMessage = ""; + this._chartReady = true; + const annotationStyle = config.annotation_style ?? "circle"; + const visibleEvents = events.filter((ev) => !hiddenEventIds.has(ev.id)); + const series = [{ + entityId, + pts, + color: lineColor + }]; + const vMin = Math.min(...allVals); + const vMax = Math.max(...allVals); + const range = vMax - vMin; + const chartMin = vMin - (range * .03 || .2); + const chartMax = vMax + (range * .54 || .8); + for (const s of series) { + renderer.drawLine(s.pts, s.color, t0, t1, chartMin, chartMax, { fillAlpha: .18 }); + if (s.pts.length) { + const lastPt = s.pts[s.pts.length - 1]; + const prev = this._previousSeriesEndpoints.get(s.entityId); + if (prev && (lastPt[0] !== prev.t || lastPt[1] !== prev.v)) { + const cx = renderer.xOf(lastPt[0], t0, t1); + const cy = renderer.yOf(lastPt[1], chartMin, chartMax); + renderer.drawBlip(cx, cy, s.color); + } + this._previousSeriesEndpoints.set(s.entityId, { + t: lastPt[0], + v: lastPt[1] + }); + } + } + const hits = annotationStyle === "line" ? renderer.drawAnnotationLinesOnLine(visibleEvents, series, t0, t1, chartMin, chartMax) : renderer.drawAnnotationsOnLine(visibleEvents, series, t0, t1, chartMin, chartMax); + const overlay = this.shadowRoot?.querySelector(".icon-overlay"); + if (overlay) { + overlay.innerHTML = ""; + if (annotationStyle === "circle") for (const hit of hits) { + const bgColor = hit.event.color || "#03a9f4"; + const el = document.createElement("div"); + el.className = "ann-icon"; + el.style.left = `${hit.x}px`; + el.style.top = `${hit.y}px`; + el.style.background = bgColor; + D(b``, el); + el.dataset.eventId = hit.event.id; + el.addEventListener("click", (e) => { + if (this.showAnnotationTooltips) { + e.preventDefault(); + e.stopPropagation(); + this._showAnnotationTooltip(hit, e.clientX, e.clientY, unit); + return; + } + e.preventDefault(); + e.stopPropagation(); + this._emitAnnotationClick(hit.event); + }); + if (this.showAnnotationTooltips) this._attachTooltipInteractions(el, hit, unit); + overlay.appendChild(el); + if (this.showAnnotationTooltips) this._attachTooltipHitTarget(overlay, hit, unit); + } + else if (this.showAnnotationTooltips) for (const hit of hits) this._attachTooltipHitTarget(overlay, hit, unit); + } + if (this._canvasClickHandler) canvas.removeEventListener("click", this._canvasClickHandler); + if (this._canvasMoveHandler) canvas.removeEventListener("mousemove", this._canvasMoveHandler); + if (this._canvasLeaveHandler) canvas.removeEventListener("mouseleave", this._canvasLeaveHandler); + this._canvasMoveHandler = () => { + this._hideAnnotationTooltip(); + }; + this._canvasLeaveHandler = () => { + this._hideAnnotationTooltip(); + }; + this._canvasClickHandler = (e) => { + const best = this._findHitAtPointer(hits, e.clientX, e.clientY, canvas); + if (best) { + if (this.showAnnotationTooltips) { + e.preventDefault(); + e.stopPropagation(); + this._showAnnotationTooltip(best, e.clientX, e.clientY, unit); + return; + } + e.preventDefault(); + e.stopPropagation(); + this._emitAnnotationClick(best.event); + } + }; + canvas.addEventListener("mousemove", this._canvasMoveHandler); + canvas.addEventListener("mouseleave", this._canvasLeaveHandler); + canvas.addEventListener("click", this._canvasClickHandler); + } + _emitAnnotationClick(event) { + this.dispatchEvent(new CustomEvent("dp-sensor-annotation-click", { + detail: { event }, + bubbles: true, + composed: true + })); + } + _getHistoryStatesForEntity(entityId, histResult) { + if (!histResult) return []; + const r = histResult; + if (Array.isArray(r[entityId])) return r[entityId]; + if (Array.isArray(r)) { + const rArr = r; + if (Array.isArray(rArr[0])) return rArr[0] || []; + if (rArr.every((e) => e && typeof e === "object" && !Array.isArray(e))) return rArr.filter((e) => e.entity_id === entityId); + } + const rObj = histResult; + if (rObj && typeof rObj === "object") { + const result = rObj.result; + if (Array.isArray(result?.[entityId])) return result[entityId]; + if (Array.isArray(result?.[0])) return result[0] || []; + } + return []; + } + render() { + return b`
${!this._chartReady ? b` @@ -37111,19 +35364,21 @@ ${content.alert}` : "",
`; - } - } - __publicField$6(SensorChart, "properties", { - _chartReady: { state: true }, - _loadMessage: { state: true }, - showAnnotationTooltips: { - type: Boolean, - attribute: "show-annotation-tooltips" - } - }); - __publicField$6(SensorChart, "styles", styles$5); - customElements.define("sensor-chart", SensorChart); - const styles$4 = i$5` + } + }; + _defineProperty(SensorChart, "properties", { + _chartReady: { state: true }, + _loadMessage: { state: true }, + showAnnotationTooltips: { + type: Boolean, + attribute: "show-annotation-tooltips" + } + }); + _defineProperty(SensorChart, "styles", styles$5); + customElements.define("sensor-chart", SensorChart); + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/sensor-records/sensor-records.styles.ts + var styles$4 = i$5` :host { display: block; flex: 1 1 0; @@ -37161,7 +35416,9 @@ ${content.alert}` : "", font-size: 0.85em; } `; - const styles$3 = i$5` + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/sensor-record-item/sensor-record-item.styles.ts + var styles$3 = i$5` :host { display: block; } @@ -37318,99 +35575,79 @@ ${content.alert}` : "", font-family: inherit; } `; - var __create$3 = Object.create; - var __defProp$5 = Object.defineProperty; - var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor; - var __knownSymbol$3 = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$3 = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$3 = (base) => [, , , __create$3(base?.[__knownSymbol$3("metadata")] ?? null)]; - var __decoratorStrings$3 = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$3 = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$3("Function expected") : fn; - var __decoratorContext$3 = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$3[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$3("Already initialized") : fns.push(__expectFn$3(fn || null)) }); - var __decoratorMetadata$3 = (array, target) => __defNormalProp$5(target, __knownSymbol$3("metadata"), array[3]); - var __runInitializers$3 = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$3 = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$3[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$3({ get [name]() { - return __privateGet$3(this, extra); - }, set [name](x2) { - return __privateSet$3(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$3(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$3(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$3("Object expected"); - else __expectFn$3(fn = it.get) && (desc.get = fn), __expectFn$3(fn = it.set) && (desc.set = fn), __expectFn$3(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$5(target, name, desc), target; - }; - var __publicField$5 = (obj, key, value) => __defNormalProp$5(obj, key + "", value); - var __accessCheck$3 = (obj, member, msg2) => member.has(obj) || __typeError$3("Cannot " + msg2); - var __privateGet$3 = (obj, member, getter) => (__accessCheck$3(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$3 = (obj, member, value) => member.has(obj) ? __typeError$3("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$3 = (obj, member, value, setter) => (__accessCheck$3(obj, member, "write to private field"), member.set(obj, value), value); - var __noteExpanded_dec, _showFullMessage_dec$1, _hidden_dec, _event_dec, _a$3, _init$3, _event, _hidden, _showFullMessage$1, __noteExpanded; - class SensorRecordItem extends (_a$3 = i$2, _event_dec = [n({ type: Object, attribute: false })], _hidden_dec = [n({ type: Boolean })], _showFullMessage_dec$1 = [n({ type: Boolean, attribute: "show-full-message" })], __noteExpanded_dec = [r()], _a$3) { - constructor() { - super(...arguments); - __privateAdd$3(this, _event, __runInitializers$3(_init$3, 8, this, null)), __runInitializers$3(_init$3, 11, this); - __privateAdd$3(this, _hidden, __runInitializers$3(_init$3, 12, this, false)), __runInitializers$3(_init$3, 15, this); - __privateAdd$3(this, _showFullMessage$1, __runInitializers$3(_init$3, 16, this, true)), __runInitializers$3(_init$3, 19, this); - __privateAdd$3(this, __noteExpanded, __runInitializers$3(_init$3, 20, this, false)), __runInitializers$3(_init$3, 23, this); - } - _onToggleVisibility(e2) { - e2.preventDefault(); - e2.stopPropagation(); - this.dispatchEvent( - new CustomEvent("dp-sensor-record-toggle-visibility", { - detail: { id: this.event?.id }, - bubbles: true, - composed: true - }) - ); - } - _onNavigate(e2) { - e2.preventDefault(); - e2.stopPropagation(); - this.dispatchEvent( - new CustomEvent("dp-sensor-record-navigate", { - detail: { event: this.event }, - bubbles: true, - composed: true - }) - ); - } - render() { - const ev = this.event; - if (!ev) return b``; - const color = ev.color || "#03a9f4"; - const icon = ev.icon || "mdi:bookmark"; - const annText = ev.annotation && ev.annotation !== ev.message ? ev.annotation : ""; - const isSimple = !annText; - const visibilityIcon = this.hidden ? "mdi:eye" : "mdi:eye-off"; - const visibilityLabel = this.hidden ? "Show chart marker" : "Hide chart marker"; - const showNoteInline = this.showFullMessage; - const noteHidden = !showNoteInline && !this._noteExpanded; - return b` + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/sensor-record-item/sensor-record-item.ts + var _event_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _hidden_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _showFullMessage_accessor_storage$1 = /* @__PURE__ */ new WeakMap(); + var _noteExpanded_accessor_storage = /* @__PURE__ */ new WeakMap(); + var SensorRecordItem = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _event_accessor_storage, null); + _classPrivateFieldInitSpec(this, _hidden_accessor_storage, false); + _classPrivateFieldInitSpec(this, _showFullMessage_accessor_storage$1, true); + _classPrivateFieldInitSpec(this, _noteExpanded_accessor_storage, false); + } + get event() { + return _classPrivateFieldGet2(_event_accessor_storage, this); + } + set event(value) { + _classPrivateFieldSet2(_event_accessor_storage, this, value); + } + get hidden() { + return _classPrivateFieldGet2(_hidden_accessor_storage, this); + } + set hidden(value) { + _classPrivateFieldSet2(_hidden_accessor_storage, this, value); + } + get showFullMessage() { + return _classPrivateFieldGet2(_showFullMessage_accessor_storage$1, this); + } + set showFullMessage(value) { + _classPrivateFieldSet2(_showFullMessage_accessor_storage$1, this, value); + } + get _noteExpanded() { + return _classPrivateFieldGet2(_noteExpanded_accessor_storage, this); + } + set _noteExpanded(value) { + _classPrivateFieldSet2(_noteExpanded_accessor_storage, this, value); + } + _onToggleVisibility(e) { + e.preventDefault(); + e.stopPropagation(); + this.dispatchEvent(new CustomEvent("dp-sensor-record-toggle-visibility", { + detail: { id: this.event?.id }, + bubbles: true, + composed: true + })); + } + _onNavigate(e) { + e.preventDefault(); + e.stopPropagation(); + this.dispatchEvent(new CustomEvent("dp-sensor-record-navigate", { + detail: { event: this.event }, + bubbles: true, + composed: true + })); + } + render() { + const ev = this.event; + if (!ev) return b``; + const color = ev.color || "#03a9f4"; + const icon = ev.icon || "mdi:bookmark"; + const annText = ev.annotation && ev.annotation !== ev.message ? ev.annotation : ""; + const isSimple = !annText; + const visibilityIcon = this.hidden ? "mdi:eye" : "mdi:eye-off"; + const visibilityLabel = this.hidden ? "Show chart marker" : "Hide chart marker"; + const showNoteInline = this.showFullMessage; + const noteHidden = !showNoteInline && !this._noteExpanded; + return b`
{ - this._noteExpanded = !this._noteExpanded; - } : void 0} + this._noteExpanded = !this._noteExpanded; + } : void 0} >
`; - } - } - _init$3 = __decoratorStart$3(_a$3); - _event = /* @__PURE__ */ new WeakMap(); - _hidden = /* @__PURE__ */ new WeakMap(); - _showFullMessage$1 = /* @__PURE__ */ new WeakMap(); - __noteExpanded = /* @__PURE__ */ new WeakMap(); - __decorateElement$3(_init$3, 4, "event", _event_dec, SensorRecordItem, _event); - __decorateElement$3(_init$3, 4, "hidden", _hidden_dec, SensorRecordItem, _hidden); - __decorateElement$3(_init$3, 4, "showFullMessage", _showFullMessage_dec$1, SensorRecordItem, _showFullMessage$1); - __decorateElement$3(_init$3, 4, "_noteExpanded", __noteExpanded_dec, SensorRecordItem, __noteExpanded); - __decoratorMetadata$3(_init$3, SensorRecordItem); - __publicField$5(SensorRecordItem, "styles", styles$3); - customElements.define("sensor-record-item", SensorRecordItem); - var __create$2 = Object.create; - var __defProp$4 = Object.defineProperty; - var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor; - var __knownSymbol$2 = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$2 = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$2 = (base) => [, , , __create$2(base?.[__knownSymbol$2("metadata")] ?? null)]; - var __decoratorStrings$2 = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$2 = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$2("Function expected") : fn; - var __decoratorContext$2 = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$2[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$2("Already initialized") : fns.push(__expectFn$2(fn || null)) }); - var __decoratorMetadata$2 = (array, target) => __defNormalProp$4(target, __knownSymbol$2("metadata"), array[3]); - var __runInitializers$2 = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$2 = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$2[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$2({ get [name]() { - return __privateGet$2(this, extra); - }, set [name](x2) { - return __privateSet$2(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$2(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$2(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$2("Object expected"); - else __expectFn$2(fn = it.get) && (desc.get = fn), __expectFn$2(fn = it.set) && (desc.set = fn), __expectFn$2(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$4(target, name, desc), target; - }; - var __publicField$4 = (obj, key, value) => __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value); - var __accessCheck$2 = (obj, member, msg2) => member.has(obj) || __typeError$2("Cannot " + msg2); - var __privateGet$2 = (obj, member, getter) => (__accessCheck$2(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$2 = (obj, member, value) => member.has(obj) ? __typeError$2("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$2 = (obj, member, value, setter) => (__accessCheck$2(obj, member, "write to private field"), member.set(obj, value), value); - var __page_dec, _showFullMessage_dec, _limit_dec, _pageSize_dec, _hiddenEventIds_dec, _events_dec, _a$2, _init$2, _events, _hiddenEventIds, _pageSize, _limit, _showFullMessage, __page; - class SensorRecords extends (_a$2 = i$2, _events_dec = [n({ type: Array, attribute: false })], _hiddenEventIds_dec = [n({ type: Object, attribute: false })], _pageSize_dec = [n({ type: Number, attribute: "page-size" })], _limit_dec = [n({ type: Number })], _showFullMessage_dec = [n({ type: Boolean, attribute: "show-full-message" })], __page_dec = [r()], _a$2) { - constructor() { - super(...arguments); - __privateAdd$2(this, _events, __runInitializers$2(_init$2, 8, this, [])), __runInitializers$2(_init$2, 11, this); - __privateAdd$2(this, _hiddenEventIds, __runInitializers$2(_init$2, 12, this, /* @__PURE__ */ new Set())), __runInitializers$2(_init$2, 15, this); - __privateAdd$2(this, _pageSize, __runInitializers$2(_init$2, 16, this, null)), __runInitializers$2(_init$2, 19, this); - __privateAdd$2(this, _limit, __runInitializers$2(_init$2, 20, this, null)), __runInitializers$2(_init$2, 23, this); - __privateAdd$2(this, _showFullMessage, __runInitializers$2(_init$2, 24, this, true)), __runInitializers$2(_init$2, 27, this); - __privateAdd$2(this, __page, __runInitializers$2(_init$2, 28, this, 0)), __runInitializers$2(_init$2, 31, this); - __publicField$4(this, "_paginationNotifyRaf", null); - } - updated(changedProps) { - if (changedProps.has("events") || changedProps.has("pageSize") || changedProps.has("limit") || changedProps.has("_page")) { - const sorted = [...this.events].sort( - (a2, b2) => new Date(b2.timestamp).getTime() - new Date(a2.timestamp).getTime() - ); - const limited = this.limit ? sorted.slice(0, this.limit) : sorted; - const total = limited.length; - const totalPages = this.pageSize ? Math.max(1, Math.ceil(total / this.pageSize)) : 1; - if (this._paginationNotifyRaf !== null) { - window.cancelAnimationFrame(this._paginationNotifyRaf); - } - this._paginationNotifyRaf = window.requestAnimationFrame(() => { - this._paginationNotifyRaf = null; - const paginationEl = this.shadowRoot?.querySelector("pagination-nav"); - const paginationHeight = paginationEl?.getBoundingClientRect().height ?? 0; - this.dispatchEvent( - new CustomEvent("dp-sensor-pagination-visibility-change", { - detail: { - visible: totalPages > 1, - height: totalPages > 1 ? paginationHeight : 0 - }, - bubbles: true, - composed: true - }) - ); - }); - } - } - disconnectedCallback() { - super.disconnectedCallback(); - if (this._paginationNotifyRaf !== null) { - window.cancelAnimationFrame(this._paginationNotifyRaf); - this._paginationNotifyRaf = null; - } - } - render() { - const sorted = [...this.events].sort( - (a2, b2) => new Date(b2.timestamp).getTime() - new Date(a2.timestamp).getTime() - ); - const limited = this.limit ? sorted.slice(0, this.limit) : sorted; - const total = limited.length; - if (!total) { - return b` + } + }; + _defineProperty(SensorRecordItem, "styles", styles$3); + __decorate([n$1({ + type: Object, + attribute: false + })], SensorRecordItem.prototype, "event", null); + __decorate([n$1({ type: Boolean })], SensorRecordItem.prototype, "hidden", null); + __decorate([n$1({ + type: Boolean, + attribute: "show-full-message" + })], SensorRecordItem.prototype, "showFullMessage", null); + __decorate([r$1()], SensorRecordItem.prototype, "_noteExpanded", null); + customElements.define("sensor-record-item", SensorRecordItem); + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/sensor-records/sensor-records.ts + var _events_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _hiddenEventIds_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _pageSize_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _limit_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _showFullMessage_accessor_storage = /* @__PURE__ */ new WeakMap(); + var _page_accessor_storage = /* @__PURE__ */ new WeakMap(); + var SensorRecords = class extends i$2 { + constructor(..._args) { + super(..._args); + _classPrivateFieldInitSpec(this, _events_accessor_storage, []); + _classPrivateFieldInitSpec(this, _hiddenEventIds_accessor_storage, /* @__PURE__ */ new Set()); + _classPrivateFieldInitSpec(this, _pageSize_accessor_storage, null); + _classPrivateFieldInitSpec(this, _limit_accessor_storage, null); + _classPrivateFieldInitSpec(this, _showFullMessage_accessor_storage, true); + _classPrivateFieldInitSpec(this, _page_accessor_storage, 0); + _defineProperty(this, "_paginationNotifyRaf", null); + } + get events() { + return _classPrivateFieldGet2(_events_accessor_storage, this); + } + set events(value) { + _classPrivateFieldSet2(_events_accessor_storage, this, value); + } + get hiddenEventIds() { + return _classPrivateFieldGet2(_hiddenEventIds_accessor_storage, this); + } + set hiddenEventIds(value) { + _classPrivateFieldSet2(_hiddenEventIds_accessor_storage, this, value); + } + get pageSize() { + return _classPrivateFieldGet2(_pageSize_accessor_storage, this); + } + set pageSize(value) { + _classPrivateFieldSet2(_pageSize_accessor_storage, this, value); + } + get limit() { + return _classPrivateFieldGet2(_limit_accessor_storage, this); + } + set limit(value) { + _classPrivateFieldSet2(_limit_accessor_storage, this, value); + } + get showFullMessage() { + return _classPrivateFieldGet2(_showFullMessage_accessor_storage, this); + } + set showFullMessage(value) { + _classPrivateFieldSet2(_showFullMessage_accessor_storage, this, value); + } + get _page() { + return _classPrivateFieldGet2(_page_accessor_storage, this); + } + set _page(value) { + _classPrivateFieldSet2(_page_accessor_storage, this, value); + } + updated(changedProps) { + if (changedProps.has("events") || changedProps.has("pageSize") || changedProps.has("limit") || changedProps.has("_page")) { + const sorted = [...this.events].sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()); + const total = (this.limit ? sorted.slice(0, this.limit) : sorted).length; + const totalPages = this.pageSize ? Math.max(1, Math.ceil(total / this.pageSize)) : 1; + if (this._paginationNotifyRaf !== null) window.cancelAnimationFrame(this._paginationNotifyRaf); + this._paginationNotifyRaf = window.requestAnimationFrame(() => { + this._paginationNotifyRaf = null; + const paginationHeight = (this.shadowRoot?.querySelector("pagination-nav"))?.getBoundingClientRect().height ?? 0; + this.dispatchEvent(new CustomEvent("dp-sensor-pagination-visibility-change", { + detail: { + visible: totalPages > 1, + height: totalPages > 1 ? paginationHeight : 0 + }, + bubbles: true, + composed: true + })); + }); + } + } + disconnectedCallback() { + super.disconnectedCallback(); + if (this._paginationNotifyRaf !== null) { + window.cancelAnimationFrame(this._paginationNotifyRaf); + this._paginationNotifyRaf = null; + } + } + render() { + const sorted = [...this.events].sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()); + const limited = this.limit ? sorted.slice(0, this.limit) : sorted; + const total = limited.length; + if (!total) return b`
No records in this time window.
`; - } - const totalPages = this.pageSize ? Math.max(1, Math.ceil(total / this.pageSize)) : 1; - const page = Math.min(this._page, totalPages - 1); - const slice = this.pageSize ? limited.slice(page * this.pageSize, (page + 1) * this.pageSize) : limited; - const showPagination = totalPages > 1; - return b` + const totalPages = this.pageSize ? Math.max(1, Math.ceil(total / this.pageSize)) : 1; + const page = Math.min(this._page, totalPages - 1); + const slice = this.pageSize ? limited.slice(page * this.pageSize, (page + 1) * this.pageSize) : limited; + const showPagination = totalPages > 1; + return b`
- ${slice.map( - (ev) => b` + ${slice.map((ev) => b` - ` - )} + `)}
${showPagination ? b` { - this._page = e2.detail.page; - this.shadowRoot?.querySelector(".ann-list")?.scrollTo(0, 0); - }} + @dp-page-change=${(e) => { + this._page = e.detail.page; + this.shadowRoot?.querySelector(".ann-list")?.scrollTo(0, 0); + }} > ` : ""}
`; - } - } - _init$2 = __decoratorStart$2(_a$2); - _events = /* @__PURE__ */ new WeakMap(); - _hiddenEventIds = /* @__PURE__ */ new WeakMap(); - _pageSize = /* @__PURE__ */ new WeakMap(); - _limit = /* @__PURE__ */ new WeakMap(); - _showFullMessage = /* @__PURE__ */ new WeakMap(); - __page = /* @__PURE__ */ new WeakMap(); - __decorateElement$2(_init$2, 4, "events", _events_dec, SensorRecords, _events); - __decorateElement$2(_init$2, 4, "hiddenEventIds", _hiddenEventIds_dec, SensorRecords, _hiddenEventIds); - __decorateElement$2(_init$2, 4, "pageSize", _pageSize_dec, SensorRecords, _pageSize); - __decorateElement$2(_init$2, 4, "limit", _limit_dec, SensorRecords, _limit); - __decorateElement$2(_init$2, 4, "showFullMessage", _showFullMessage_dec, SensorRecords, _showFullMessage); - __decorateElement$2(_init$2, 4, "_page", __page_dec, SensorRecords, __page); - __decoratorMetadata$2(_init$2, SensorRecords); - __publicField$4(SensorRecords, "styles", styles$4); - customElements.define("sensor-records", SensorRecords); - var __defProp$3 = Object.defineProperty; - var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value); - class HassRecordsSensorCard extends i$2 { - constructor() { - super(); - __publicField$3(this, "_initialized", false); - __publicField$3(this, "_lastHistResult", null); - __publicField$3(this, "_lastEvents", []); - __publicField$3(this, "_lastT0", null); - __publicField$3(this, "_lastT1", null); - __publicField$3(this, "_unsubscribe", null); - __publicField$3(this, "_resizeObserver", null); - this._config = {}; - this._hass = null; - this._annEvents = []; - this._hiddenEventIds = /* @__PURE__ */ new Set(); - this._recordsFooterHeight = 0; - } - setConfig(config) { - if (!config.entity) { - throw new Error("hass-datapoints-sensor-card: `entity` is required"); - } - this._config = { - hours_to_show: 24, - annotation_style: "circle", - show_records: false, - records_page_size: null, - records_limit: null, - ...config - }; - } - set hass(hass) { - this._hass = hass; - if (!this._initialized) { - this._initialized = true; - this._setupAutoRefresh(); - } - } - get hass() { - return this._hass; - } - firstUpdated() { - this._setupResizeObserver(); - if (this._hass) this._load(); - } - connectedCallback() { - super.connectedCallback(); - this._setupResizeObserver(); - if (this._initialized && this._hass) this._load(); - } - disconnectedCallback() { - super.disconnectedCallback(); - if (this._unsubscribe) { - this._unsubscribe(); - this._unsubscribe = null; - } - if (this._resizeObserver) { - this._resizeObserver.disconnect(); - this._resizeObserver = null; - } - } - _setupAutoRefresh() { - if (!this.hass) return; - this.hass.connection.subscribeEvents(() => this._load(), `${DOMAIN}_event_recorded`).then((unsub) => { - this._unsubscribe = unsub; - }).catch(() => { - }); - } - _setupResizeObserver() { - if (this._resizeObserver) return; - if (!window.ResizeObserver) return; - this._resizeObserver = new ResizeObserver(() => { - this._applyLayoutSizing(); - }); - this._resizeObserver.observe(this); - } - _applyLayoutSizing() { - const shell = this.shadowRoot?.querySelector(".card-shell"); - if (!shell) return; - const gridRows = this._gridRows(); - if (!this._config?.show_records) { - shell.style.setProperty("--hr-body-rows", String(gridRows)); - shell.style.setProperty("--hr-footer-height", "0px"); - return; - } - const totalRows = Math.max(3, gridRows); - const bodyRows = Math.max(2, this._bodyRows(totalRows)); - shell.style.setProperty("--hr-body-rows", String(bodyRows)); - shell.style.setProperty( - "--hr-footer-height", - `${Math.max(0, this._recordsFooterHeight)}px` - ); - } - _gridRows() { - const raw = getComputedStyle(this).getPropertyValue("--row-size").trim(); - const rows = Number.parseInt(raw, 10); - if (Number.isFinite(rows) && rows > 0) return rows; - return this._config?.show_records ? 4 : 3; - } - _bodyRows(totalRows) { - if (!this._config?.show_records) return totalRows; - const bodyRows = Math.min( - totalRows - 1, - 3 + Math.floor(Math.max(0, totalRows - 4) / 4) - ); - return bodyRows; - } - _toggleEventVisibility(eventId) { - const next = new Set(this._hiddenEventIds); - if (next.has(eventId)) next.delete(eventId); - else next.add(eventId); - this._hiddenEventIds = next; - if (this._lastHistResult !== null) { - this._drawChart( - this._lastHistResult, - this._lastEvents, - this._lastT0, - this._lastT1 - ); - } - } - _navigateToEventHistory(ev) { - navigateToDataPointsHistory( - this, - { - entity_id: [ - this._config?.entity, - ...ev?.entity_ids || [] - ].filter(Boolean), - device_id: ev?.device_ids || [], - area_id: ev?.area_ids || [], - label_id: ev?.label_ids || [] - }, - { - start_time: Number.isFinite(this._lastT0) ? new Date(this._lastT0).toISOString() : null, - end_time: Number.isFinite(this._lastT1) ? new Date(this._lastT1).toISOString() : null - } - ); - } - _openTargetHistoryDialog() { - const entityId = this._config?.entity; - if (!entityId) { - return; - } - this.dispatchEvent( - new CustomEvent("hass-more-info", { - bubbles: true, - composed: true, - detail: { entityId } - }) - ); - } - _onCardClick(event) { - const path = event.composedPath(); - const interactiveTagNames = /* @__PURE__ */ new Set([ - "BUTTON", - "A", - "INPUT", - "TEXTAREA", - "SELECT", - "PAGINATION-NAV" - ]); - const hitInteractive = path.some((node) => { - if (!(node instanceof HTMLElement)) { - return false; - } - if (interactiveTagNames.has(node.tagName)) { - return true; - } - return !!node.closest("button,a,input,textarea,select,pagination-nav"); - }); - if (hitInteractive) { - return; - } - this._openTargetHistoryDialog(); - } - async _load() { - if (!this.hass) return; - const now = /* @__PURE__ */ new Date(); - const start = new Date( - now.getTime() - this._config.hours_to_show * 3600 * 1e3 - ); - const t0 = start.getTime(); - const t1 = now.getTime(); - const entityIds = [this._config.entity]; - try { - const [histResult, events] = await Promise.all([ - this.hass.connection.sendMessagePromise({ - type: "history/history_during_period", - start_time: start.toISOString(), - end_time: now.toISOString(), - entity_ids: entityIds, - include_start_time_state: true, - significant_changes_only: false, - no_attributes: true - }), - fetchEvents( - this.hass, - start.toISOString(), - now.toISOString(), - entityIds - ) - ]); - this._drawChart(histResult || {}, events || [], t0, t1); - } catch (err) { - logger$1.error("[hass-datapoints sensor-card]", err); - } - } - _drawChart(histResult, events, t0, t1) { - this._lastHistResult = histResult; - this._lastEvents = events; - this._lastT0 = t0; - this._lastT1 = t1; - this._annEvents = events; - const chartEl = this.shadowRoot?.querySelector("sensor-chart"); - if (!chartEl) return; - chartEl.hass = this.hass; - const entityId = this._config.entity; - const unit = this.hass?.states?.[entityId]?.attributes?.unit_of_measurement || ""; - chartEl.draw( - histResult, - events, - t0, - t1, - this._config, - unit, - this._hiddenEventIds - ); - } - _onAnnotationClick(e2) { - this._navigateToEventHistory(e2.detail.event); - } - _onHeaderClick(event) { - event.preventDefault(); - event.stopPropagation(); - this._openTargetHistoryDialog(); - } - render() { - const stateObj = this.hass?.states?.[this._config?.entity]; - const sensorName = this._config?.name || stateObj?.attributes?.friendly_name || this._config?.entity || "—"; - const sensorValue = stateObj?.state ?? "—"; - const sensorUnit = stateObj?.attributes?.unit_of_measurement || ""; - return b` + } + }; + _defineProperty(SensorRecords, "styles", styles$4); + __decorate([n$1({ + type: Array, + attribute: false + })], SensorRecords.prototype, "events", null); + __decorate([n$1({ + type: Object, + attribute: false + })], SensorRecords.prototype, "hiddenEventIds", null); + __decorate([n$1({ + type: Number, + attribute: "page-size" + })], SensorRecords.prototype, "pageSize", null); + __decorate([n$1({ type: Number })], SensorRecords.prototype, "limit", null); + __decorate([n$1({ + type: Boolean, + attribute: "show-full-message" + })], SensorRecords.prototype, "showFullMessage", null); + __decorate([r$1()], SensorRecords.prototype, "_page", null); + customElements.define("sensor-records", SensorRecords); + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/sensor.ts + /** + * hass-datapoints-sensor-card – Sensor card with inline annotation icons. + * Canvas rendering is delegated to sensor-chart; annotation list to sensor-records. + */ + var HassRecordsSensorCard = class extends i$2 { + constructor() { + super(); + _defineProperty(this, "_initialized", false); + _defineProperty(this, "_lastHistResult", null); + _defineProperty(this, "_lastEvents", []); + _defineProperty(this, "_lastT0", null); + _defineProperty(this, "_lastT1", null); + _defineProperty(this, "_unsubscribe", null); + _defineProperty(this, "_resizeObserver", null); + this._config = {}; + this._hass = null; + this._annEvents = []; + this._hiddenEventIds = /* @__PURE__ */ new Set(); + this._recordsFooterHeight = 0; + } + setConfig(config) { + if (!config.entity) throw new Error("hass-datapoints-sensor-card: `entity` is required"); + this._config = { + hours_to_show: 24, + annotation_style: "circle", + show_records: false, + records_page_size: null, + records_limit: null, + ...config + }; + } + set hass(hass) { + this._hass = hass; + if (!this._initialized) { + this._initialized = true; + this._setupAutoRefresh(); + } + } + get hass() { + return this._hass; + } + firstUpdated() { + this._setupResizeObserver(); + if (this._hass) this._load(); + } + connectedCallback() { + super.connectedCallback(); + this._setupResizeObserver(); + if (this._initialized && this._hass) this._load(); + } + disconnectedCallback() { + super.disconnectedCallback(); + if (this._unsubscribe) { + this._unsubscribe(); + this._unsubscribe = null; + } + if (this._resizeObserver) { + this._resizeObserver.disconnect(); + this._resizeObserver = null; + } + } + _setupAutoRefresh() { + if (!this.hass) return; + this.hass.connection.subscribeEvents(() => this._load(), `${DOMAIN}_event_recorded`).then((unsub) => { + this._unsubscribe = unsub; + }).catch(() => {}); + } + _setupResizeObserver() { + if (this._resizeObserver) return; + if (!window.ResizeObserver) return; + this._resizeObserver = new ResizeObserver(() => { + this._applyLayoutSizing(); + }); + this._resizeObserver.observe(this); + } + _applyLayoutSizing() { + const shell = this.shadowRoot?.querySelector(".card-shell"); + if (!shell) return; + const gridRows = this._gridRows(); + if (!this._config?.show_records) { + shell.style.setProperty("--hr-body-rows", String(gridRows)); + shell.style.setProperty("--hr-footer-height", "0px"); + return; + } + const totalRows = Math.max(3, gridRows); + const bodyRows = Math.max(2, this._bodyRows(totalRows)); + shell.style.setProperty("--hr-body-rows", String(bodyRows)); + shell.style.setProperty("--hr-footer-height", `${Math.max(0, this._recordsFooterHeight)}px`); + } + _gridRows() { + const raw = getComputedStyle(this).getPropertyValue("--row-size").trim(); + const rows = Number.parseInt(raw, 10); + if (Number.isFinite(rows) && rows > 0) return rows; + return this._config?.show_records ? 4 : 3; + } + _bodyRows(totalRows) { + if (!this._config?.show_records) return totalRows; + return Math.min(totalRows - 1, 3 + Math.floor(Math.max(0, totalRows - 4) / 4)); + } + _toggleEventVisibility(eventId) { + const next = new Set(this._hiddenEventIds); + if (next.has(eventId)) next.delete(eventId); + else next.add(eventId); + this._hiddenEventIds = next; + if (this._lastHistResult !== null) this._drawChart(this._lastHistResult, this._lastEvents, this._lastT0, this._lastT1); + } + _navigateToEventHistory(ev) { + navigateToDataPointsHistory(this, { + entity_id: [this._config?.entity, ...ev?.entity_ids || []].filter(Boolean), + device_id: ev?.device_ids || [], + area_id: ev?.area_ids || [], + label_id: ev?.label_ids || [] + }, { + start_time: Number.isFinite(this._lastT0) ? new Date(this._lastT0).toISOString() : null, + end_time: Number.isFinite(this._lastT1) ? new Date(this._lastT1).toISOString() : null + }); + } + _openTargetHistoryDialog() { + const entityId = this._config?.entity; + if (!entityId) return; + this.dispatchEvent(new CustomEvent("hass-more-info", { + bubbles: true, + composed: true, + detail: { entityId } + })); + } + _onCardClick(event) { + const path = event.composedPath(); + const interactiveTagNames = new Set([ + "BUTTON", + "A", + "INPUT", + "TEXTAREA", + "SELECT", + "PAGINATION-NAV" + ]); + if (path.some((node) => { + if (!(node instanceof HTMLElement)) return false; + if (interactiveTagNames.has(node.tagName)) return true; + return !!node.closest("button,a,input,textarea,select,pagination-nav"); + })) return; + this._openTargetHistoryDialog(); + } + async _load() { + if (!this.hass) return; + const now = /* @__PURE__ */ new Date(); + const start = /* @__PURE__ */ new Date(now.getTime() - this._config.hours_to_show * 3600 * 1e3); + const t0 = start.getTime(); + const t1 = now.getTime(); + const entityIds = [this._config.entity]; + try { + const [histResult, events] = await Promise.all([this.hass.connection.sendMessagePromise({ + type: "history/history_during_period", + start_time: start.toISOString(), + end_time: now.toISOString(), + entity_ids: entityIds, + include_start_time_state: true, + significant_changes_only: false, + no_attributes: true + }), fetchEvents(this.hass, start.toISOString(), now.toISOString(), entityIds)]); + this._drawChart(histResult || {}, events || [], t0, t1); + } catch (err) { + logger$1.error("[hass-datapoints sensor-card]", err); + } + } + _drawChart(histResult, events, t0, t1) { + this._lastHistResult = histResult; + this._lastEvents = events; + this._lastT0 = t0; + this._lastT1 = t1; + this._annEvents = events; + const chartEl = this.shadowRoot?.querySelector("sensor-chart"); + if (!chartEl) return; + chartEl.hass = this.hass; + const entityId = this._config.entity; + const unit = this.hass?.states?.[entityId]?.attributes?.unit_of_measurement || ""; + chartEl.draw(histResult, events, t0, t1, this._config, unit, this._hiddenEventIds); + } + _onAnnotationClick(e) { + this._navigateToEventHistory(e.detail.event); + } + _onHeaderClick(event) { + event.preventDefault(); + event.stopPropagation(); + this._openTargetHistoryDialog(); + } + render() { + const stateObj = this.hass?.states?.[this._config?.entity]; + const sensorName = this._config?.name || stateObj?.attributes?.friendly_name || this._config?.entity || "—"; + const sensorValue = stateObj?.state ?? "—"; + const sensorUnit = stateObj?.attributes?.unit_of_measurement || ""; + return b`
@@ -37898,47 +36073,60 @@ ${content.alert}` : "", .pageSize=${this._config.records_page_size ?? null} .limit=${this._config.records_limit ?? null} .showFullMessage=${this._config.records_show_full_message !== false} - @dp-sensor-record-toggle-visibility=${(e2) => { - this._toggleEventVisibility(e2.detail.id); - }} - @dp-sensor-record-navigate=${(e2) => { - this._navigateToEventHistory(e2.detail.event); - }} - @dp-sensor-pagination-visibility-change=${(e2) => { - this._recordsFooterHeight = e2.detail.visible === true ? e2.detail.height : 0; - }} + @dp-sensor-record-toggle-visibility=${(e) => { + this._toggleEventVisibility(e.detail.id); + }} + @dp-sensor-record-navigate=${(e) => { + this._navigateToEventHistory(e.detail.event); + }} + @dp-sensor-pagination-visibility-change=${(e) => { + this._recordsFooterHeight = e.detail.visible === true ? e.detail.height : 0; + }} > ` : ""}
`; - } - updated() { - this._applyLayoutSizing(); - } - static getConfigElement() { - return document.createElement("hass-datapoints-sensor-card-editor"); - } - static getStubConfig() { - return { entity: "sensor.example", hours_to_show: 24 }; - } - getGridOptions() { - if (this._config?.show_records) { - return { rows: 4, min_rows: 4, max_rows: 12 }; - } - return { rows: 3, min_rows: 2, max_rows: 5 }; - } - } - __publicField$3(HassRecordsSensorCard, "properties", { - _config: { state: true }, - _hass: { state: true }, - _annEvents: { state: true }, - _hiddenEventIds: { state: true }, - _recordsFooterHeight: { state: true } - }); - __publicField$3(HassRecordsSensorCard, "styles", styles$7); - const styles$2 = i$5``; - const styles$1 = i$5` + } + updated() { + this._applyLayoutSizing(); + } + static getConfigElement() { + return document.createElement("hass-datapoints-sensor-card-editor"); + } + static getStubConfig() { + return { + entity: "sensor.example", + hours_to_show: 24 + }; + } + getGridOptions() { + if (this._config?.show_records) return { + rows: 4, + min_rows: 4, + max_rows: 12 + }; + return { + rows: 3, + min_rows: 2, + max_rows: 5 + }; + } + }; + _defineProperty(HassRecordsSensorCard, "properties", { + _config: { state: true }, + _hass: { state: true }, + _annEvents: { state: true }, + _hiddenEventIds: { state: true }, + _recordsFooterHeight: { state: true } + }); + _defineProperty(HassRecordsSensorCard, "styles", styles$7); + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/editor.styles.ts + var styles$2 = i$5``; + //#endregion + //#region custom_components/hass_datapoints/src/atoms/form/editor-entity-picker/editor-entity-picker.styles.ts + var styles$1 = i$5` :host { display: block; } @@ -37947,112 +36135,72 @@ ${content.alert}` : "", width: 100%; } `; - var __create$1 = Object.create; - var __defProp$2 = Object.defineProperty; - var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor; - var __knownSymbol$1 = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError$1 = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart$1 = (base) => [, , , __create$1(base?.[__knownSymbol$1("metadata")] ?? null)]; - var __decoratorStrings$1 = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn$1 = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$1("Function expected") : fn; - var __decoratorContext$1 = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$1[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$1("Already initialized") : fns.push(__expectFn$1(fn || null)) }); - var __decoratorMetadata$1 = (array, target) => __defNormalProp$2(target, __knownSymbol$1("metadata"), array[3]); - var __runInitializers$1 = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement$1 = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings$1[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc$1({ get [name]() { - return __privateGet$1(this, extra); - }, set [name](x2) { - return __privateSet$1(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext$1(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn$1(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError$1("Object expected"); - else __expectFn$1(fn = it.get) && (desc.get = fn), __expectFn$1(fn = it.set) && (desc.set = fn), __expectFn$1(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$2(target, name, desc), target; - }; - var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, key + "", value); - var __accessCheck$1 = (obj, member, msg2) => member.has(obj) || __typeError$1("Cannot " + msg2); - var __privateGet$1 = (obj, member, getter) => (__accessCheck$1(obj, member, "read from private field"), member.get(obj)); - var __privateAdd$1 = (obj, member, value) => member.has(obj) ? __typeError$1("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet$1 = (obj, member, value, setter) => (__accessCheck$1(obj, member, "write to private field"), member.set(obj, value), value); - var _hass_dec, _value_dec$1, _label_dec$1, _a$1, _init$1, _label$1, _value$1, _hass; - class EditorEntityPicker extends (_a$1 = i$2, _label_dec$1 = [n({ type: String })], _value_dec$1 = [n({ type: String })], _hass_dec = [n({ type: Object })], _a$1) { - constructor() { - super(...arguments); - __privateAdd$1(this, _label$1, __runInitializers$1(_init$1, 8, this, "")), __runInitializers$1(_init$1, 11, this); - __privateAdd$1(this, _value$1, __runInitializers$1(_init$1, 12, this, "")), __runInitializers$1(_init$1, 15, this); - __privateAdd$1(this, _hass, __runInitializers$1(_init$1, 16, this, null)), __runInitializers$1(_init$1, 19, this); - } - firstUpdated() { - const el = this.shadowRoot.querySelector( - "ha-selector" - ); - if (el) { - el.label = this.label; - el.selector = { entity: {} }; - if (this.hass) { - el.hass = this.hass; - } - el.value = this.value; - } - } - updated(changedProps) { - const el = this.shadowRoot.querySelector( - "ha-selector" - ); - if (!el) { - return; - } - if (changedProps.has("value")) { - el.value = this.value; - } - if (changedProps.has("hass") && this.hass) { - el.hass = this.hass; - } - } - _onValueChanged(e2) { - this.dispatchEvent( - new CustomEvent("dp-entity-change", { - detail: { value: e2.detail.value }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b``; - } - } - _init$1 = __decoratorStart$1(_a$1); - _label$1 = /* @__PURE__ */ new WeakMap(); - _value$1 = /* @__PURE__ */ new WeakMap(); - _hass = /* @__PURE__ */ new WeakMap(); - __decorateElement$1(_init$1, 4, "label", _label_dec$1, EditorEntityPicker, _label$1); - __decorateElement$1(_init$1, 4, "value", _value_dec$1, EditorEntityPicker, _value$1); - __decorateElement$1(_init$1, 4, "hass", _hass_dec, EditorEntityPicker, _hass); - __decoratorMetadata$1(_init$1, EditorEntityPicker); - __publicField$2(EditorEntityPicker, "styles", styles$1); - customElements.define("editor-entity-picker", EditorEntityPicker); - const styles = i$5` + } + }; + _defineProperty(EditorEntityPicker, "styles", styles$1); + __decorate([n$1({ type: String })], EditorEntityPicker.prototype, "label", null); + __decorate([n$1({ type: String })], EditorEntityPicker.prototype, "value", null); + __decorate([n$1({ type: Object })], EditorEntityPicker.prototype, "hass", null); + customElements.define("editor-entity-picker", EditorEntityPicker); + //#endregion + //#region custom_components/hass_datapoints/src/atoms/form/editor-select/editor-select.styles.ts + var styles = i$5` :host { display: block; } @@ -38061,2987 +36209,210 @@ ${content.alert}` : "", width: 100%; } `; - var __create = Object.create; - var __defProp$1 = Object.defineProperty; - var __getOwnPropDesc = Object.getOwnPropertyDescriptor; - var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name); - var __typeError = (msg2) => { - throw TypeError(msg2); - }; - var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)]; - var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; - var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn; - var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) }); - var __decoratorMetadata = (array, target) => __defNormalProp$1(target, __knownSymbol("metadata"), array[3]); - var __runInitializers = (array, flags, self2, value) => { - for (var i2 = 0, fns = array[flags >> 1], n2 = fns && fns.length; i2 < n2; i2++) flags & 1 ? fns[i2].call(self2) : value = fns[i2].call(self2, value); - return value; - }; - var __decorateElement = (array, flags, name, decorators, target, extra) => { - var fn, it, done, ctx, access, k2 = flags & 7, s2 = false, p2 = false; - var j2 = array.length + 1, key = __decoratorStrings[k2 + 5]; - var initializers = array[j2 - 1] = [], extraInitializers = array[j2] || (array[j2] = []); - var desc = (target = target.prototype, __getOwnPropDesc({ get [name]() { - return __privateGet(this, extra); - }, set [name](x2) { - return __privateSet(this, extra, x2); - } }, name)); - for (var i2 = decorators.length - 1; i2 >= 0; i2--) { - ctx = __decoratorContext(k2, name, done = {}, array[3], extraInitializers); - { - ctx.static = s2, ctx.private = p2, access = ctx.access = { has: (x2) => name in x2 }; - access.get = (x2) => x2[name]; - access.set = (x2, y2) => x2[name] = y2; - } - it = (0, decorators[i2])({ get: desc.get, set: desc.set }, ctx), done._ = 1; - if (it === void 0) __expectFn(it) && (desc[key] = it); - else if (typeof it !== "object" || it === null) __typeError("Object expected"); - else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn); - } - return desc && __defProp$1(target, name, desc), target; - }; - var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, key + "", value); - var __accessCheck = (obj, member, msg2) => member.has(obj) || __typeError("Cannot " + msg2); - var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), member.get(obj)); - var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); - var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value); - var _options_dec, _value_dec, _label_dec, _a, _init, _label, _value, _options; - class EditorSelect extends (_a = i$2, _label_dec = [n({ type: String })], _value_dec = [n({ type: String })], _options_dec = [n({ type: Array })], _a) { - constructor() { - super(...arguments); - __privateAdd(this, _label, __runInitializers(_init, 8, this, "")), __runInitializers(_init, 11, this); - __privateAdd(this, _value, __runInitializers(_init, 12, this, "")), __runInitializers(_init, 15, this); - __privateAdd(this, _options, __runInitializers(_init, 16, this, [])), __runInitializers(_init, 19, this); - } - firstUpdated() { - const el = this.shadowRoot.querySelector( - "ha-selector" - ); - if (el) { - el.label = this.label; - el.selector = { select: { options: this.options } }; - el.value = this.value; - } - } - updated(changedProps) { - const el = this.shadowRoot.querySelector( - "ha-selector" - ); - if (!el) { - return; - } - if (changedProps.has("value")) { - el.value = this.value; - } - if (changedProps.has("options")) { - el.selector = { select: { options: this.options } }; - } - } - _onValueChanged(e2) { - this.dispatchEvent( - new CustomEvent("dp-select-change", { - detail: { value: e2.detail.value }, - bubbles: true, - composed: true - }) - ); - } - render() { - return b``; - } - } - _init = __decoratorStart(_a); - _label = /* @__PURE__ */ new WeakMap(); - _value = /* @__PURE__ */ new WeakMap(); - _options = /* @__PURE__ */ new WeakMap(); - __decorateElement(_init, 4, "label", _label_dec, EditorSelect, _label); - __decorateElement(_init, 4, "value", _value_dec, EditorSelect, _value); - __decorateElement(_init, 4, "options", _options_dec, EditorSelect, _options); - __decoratorMetadata(_init, EditorSelect); - __publicField$1(EditorSelect, "styles", styles); - customElements.define("editor-select", EditorSelect); - var __defProp = Object.defineProperty; - var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; - var __publicField = (obj, key, value) => __defNormalProp(obj, key + "", value); - class HassRecordsSensorCardEditor extends EditorBase { - render() { - const c2 = this._config; - const showRecords = !!c2.show_records; - return b` + } + }; + _defineProperty(EditorSelect, "styles", styles); + __decorate([n$1({ type: String })], EditorSelect.prototype, "label", null); + __decorate([n$1({ type: String })], EditorSelect.prototype, "value", null); + __decorate([n$1({ type: Array })], EditorSelect.prototype, "options", null); + customElements.define("editor-select", EditorSelect); + //#endregion + //#region custom_components/hass_datapoints/src/cards/sensor/editor.ts + var HassRecordsSensorCardEditor = class extends EditorBase { + render() { + const c = this._config; + const showRecords = !!c.show_records; + return b`
this._set("entity", e2.detail.value)} + @dp-entity-change=${(e) => this._set("entity", e.detail.value)} > this._set("name", e2.detail.value)} + .value=${c.name || ""} + @dp-field-change=${(e) => this._set("name", e.detail.value)} > this._set("hours_to_show", e2.detail.value)} + .value=${String(c.hours_to_show ?? 24)} + @dp-field-change=${(e) => this._set("hours_to_show", e.detail.value)} > this._set("graph_color", e2.detail.color)} + .color=${c.graph_color || COLORS[0]} + @dp-color-change=${(e) => this._set("graph_color", e.detail.color)} > this._set("annotation_style", e2.detail.value)} + .value=${c.annotation_style || ""} + .options=${[{ + value: "circle", + label: msg("Circle on line") + }, { + value: "line", + label: msg("Dotted vertical line") + }]} + @dp-select-change=${(e) => this._set("annotation_style", e.detail.value)} > this._set( - "show_annotation_tooltips", - e2.detail.checked ? true : void 0 - )} + .checked=${c.show_annotation_tooltips === true} + @dp-switch-change=${(e) => this._set("show_annotation_tooltips", e.detail.checked ? true : void 0)} > this._set("show_records", e2.detail.checked || void 0)} + @dp-switch-change=${(e) => this._set("show_records", e.detail.checked || void 0)} > this._set("records_page_size", e2.detail.value)} + .value=${c.records_page_size != null ? String(c.records_page_size) : ""} + @dp-field-change=${(e) => this._set("records_page_size", e.detail.value)} > this._set("records_limit", e2.detail.value)} + .value=${c.records_limit != null ? String(c.records_limit) : ""} + @dp-field-change=${(e) => this._set("records_limit", e.detail.value)} > this._set( - "records_show_full_message", - e2.detail.checked ? void 0 : false - )} + .checked=${c.records_show_full_message !== false} + .tooltip=${msg("User will be able to expand the row if hidden", { id: "User will be able to expand the row if hidden" })} + @dp-switch-change=${(e) => this._set("records_show_full_message", e.detail.checked ? void 0 : false)} >
`; - } - } - __publicField(HassRecordsSensorCardEditor, "styles", [EditorBase.styles, styles$2]); - if (!customElements.get("hass-datapoints-action-card")) { - customElements.define("hass-datapoints-action-card", HassRecordsActionCard); - } - if (!customElements.get("hass-datapoints-quick-card")) { - customElements.define("hass-datapoints-quick-card", HassRecordsQuickCard); - } - if (!customElements.get("hass-datapoints-history-card")) { - customElements.define("hass-datapoints-history-card", HassRecordsHistoryCard); - } - if (!customElements.get("hass-datapoints-sensor-card")) { - customElements.define("hass-datapoints-sensor-card", HassRecordsSensorCard); - } - if (!customElements.get("hass-datapoints-list-card")) { - customElements.define("hass-datapoints-list-card", HassRecordsListCard); - } - if (!customElements.get("hass-datapoints-history-panel")) { - customElements.define( - "hass-datapoints-history-panel", - HassRecordsHistoryPanel - ); - } - if (!customElements.get("hass-datapoints-dev-tool-card")) { - customElements.define( - "hass-datapoints-dev-tool-card", - HassRecordsDevToolCard - ); - } - if (!customElements.get("hass-datapoints-action-card-editor")) { - customElements.define( - "hass-datapoints-action-card-editor", - HassRecordsActionCardEditor - ); - } - if (!customElements.get("hass-datapoints-quick-card-editor")) { - customElements.define( - "hass-datapoints-quick-card-editor", - HassRecordsQuickCardEditor - ); - } - if (!customElements.get("hass-datapoints-history-card-editor")) { - customElements.define( - "hass-datapoints-history-card-editor", - HassRecordsHistoryCardEditor - ); - } - if (!customElements.get("hass-datapoints-sensor-card-editor")) { - customElements.define( - "hass-datapoints-sensor-card-editor", - HassRecordsSensorCardEditor - ); - } - if (!customElements.get("hass-datapoints-list-card-editor")) { - customElements.define( - "hass-datapoints-list-card-editor", - HassRecordsListCardEditor - ); - } - if (!customElements.get("hass-datapoints-dev-tool-card-editor")) { - customElements.define( - "hass-datapoints-dev-tool-card-editor", - HassRecordsDevToolCardEditor - ); - } - window.customCards = window.customCards || []; - const registeredTypes = new Set(window.customCards.map((card) => card.type)); - const cardsToAdd = [ - { - type: "hass-datapoints-action-card", - name: "Hass Records – Action Card", - description: "Full form to record a custom event with message, annotation, icon, colour and entity association.", - preview: false - }, - { - type: "hass-datapoints-quick-card", - name: "Hass Records – Quick Card", - description: "Simple one-field card to quickly record a note with a bookmark icon.", - preview: false - }, - { - type: "hass-datapoints-history-card", - name: "Hass Records – History Card", - description: "History line chart with coloured annotation markers for recorded events.", - preview: false - }, - { - type: "hass-datapoints-sensor-card", - name: "Hass Records – Sensor Card", - description: "Sensor card with line chart — annotations shown as icons on the data line.", - preview: false - }, - { - type: "hass-datapoints-list-card", - name: "Hass Records – List Card", - description: "Activity-style datagrid to browse, search, edit and delete all recorded events.", - preview: false - }, - { - type: "hass-datapoints-dev-tool-card", - name: "Hass Records – Dev Tool", - description: "Generate demo datapoints from HA history and bulk-delete dev-flagged events.", - preview: false - } - ]; - cardsToAdd.forEach((card) => { - if (!registeredTypes.has(card.type)) { - window.customCards?.push(card); - } - }); - console.groupCollapsed( - "%c hass-datapoints %c v0.4.1 loaded ", - "color:#fff;background:#03a9f4;font-weight:bold;padding:2px 6px;border-radius:3px 0 0 3px", - "color:#03a9f4;background:#fff;font-weight:bold;padding:2px 6px;border:1px solid #03a9f4;border-radius:0 3px 3px 0" - ); - console.log( - "Enable debug logging by setting %cwindow.__HASS_DATAPOINTS_DEV__ = true", - "color:#333;background:#eee;border:1px solid #777;padding:2px 6px;border-radius:5px; font-family: Courier" - ); - console.groupEnd(); - const translations$2l = { - "Updates with new data": "Päivittyy uusilla tiedoilla", - "Scroll to selected range": "Siirry valittuun alueeseen", - "Start date and time": "Alkamispäivä ja -aika", - "End date and time": "Päättymispäivä ja -aika", - Select: "Valitse" - }; - const __vite_glob_0_0$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$2l - }, Symbol.toStringTag, { value: "Module" })); - const translations$2k = { - General: "Yleiset", - "Related items": "Liittyvät kohteet", - "Datapoint Appearance": "Datapisteen ulkoasu", - "Form fields": "Lomakekentät", - "Card title (optional)": "Kortin otsikko (valinnainen)", - "Pre-fill entities, devices, areas or labels that are always linked to recordings from this card.": "Esitäytä entiteetit, laitteet, alueet tai merkinnät, jotka yhdistetään aina tämän kortin tallenteisiin.", - "Show always included targets on card": "Näytä aina mukana olevat kohteet kortilla", - "Allow user to add more related items": "Salli käyttäjän lisätä lisää liittyviä kohteita", - "Default icon": "Oletuskuvake", - "Default colour": "Oletusväri", - "Show date & time field": "Näytä päivämäärä- ja aikakenttä", - "Show annotation field": "Näytä huomautuskenttä" - }; - const __vite_glob_0_1$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$2k - }, Symbol.toStringTag, { value: "Module" })); - const translations$2j = { - "Date window:": "Aikaikkuna:", - "Actual:": "Todellinen:" - }; - const __vite_glob_0_2$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$2j - }, Symbol.toStringTag, { value: "Module" })); - const translations$2i = { - // Editor section headings (shared across editors) - General: "Yleiset", - Entity: "Entiteetti", - "Multiple entities": "Useita entiteettejä", - Display: "Näyttö", - // Editor field labels - "Card title (optional)": "Kortin otsikko (valinnainen)", - "Hours to show": "Näytettävät tunnit", - "Single entity": "Yksittäinen entiteetti", - "Show data gaps": "Näytä tietoaukot", - "Highlight missing data ranges with dashed lines and boundary markers": "Korosta puuttuvat tietoalueet katkoviivoilla ja rajamerkeillä" - }; - const __vite_glob_0_3$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$2i - }, Symbol.toStringTag, { value: "Module" })); - const translations$2h = { - "Search datapoints…": "Hae datapisteitä…", - "Delete record": "Poista tietue", - Delete: "Poista", - "Show annotation": "Näytä huomautus", - "Open related data point history": "Avaa liittyvän datapisteen historia", - "Edit record": "Muokkaa tietuetta", - "Show chart marker": "Näytä kaavion merkki", - "Hide chart marker": "Piilota kaavion merkki", - "Choose colour": "Valitse väri", - Save: "Tallenna", - Cancel: "Peruuta", - Message: "Viesti", - "Annotation / full message": "Huomautus / koko viesti" - }; - const __vite_glob_0_4$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$2h - }, Symbol.toStringTag, { value: "Module" })); - const translations$2g = { - General: "Yleiset", - "Icon & colour": "Kuvake ja väri", - "Related items": "Liittyvät kohteet", - "Multiple entities": "Useita entiteettejä", - "Form fields": "Lomakekentät", - "Card title (optional)": "Kortin otsikko (valinnainen)", - "Input placeholder text": "Syötteen ohjeteksti", - Icon: "Kuvake", - Colour: "Väri", - "These items will be linked to every record made with this card.": "Nämä kohteet yhdistetään kaikkiin tällä kortilla tehtyihin tallenteisiin.", - "Single entity (optional)": "Yksittäinen entiteetti (valinnainen)", - "Add related items": "Lisää liittyvät kohteet", - "Show annotation field": "Näytä huomautuskenttä" - }; - const __vite_glob_0_5$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$2g - }, Symbol.toStringTag, { value: "Module" })); - const translations$2f = { - Entity: "Entiteetti", - Display: "Näyttö", - "Records list": "Tietueiden luettelo", - "Sensor entity *": "Anturientiteetti *", - "Override display name (optional)": "Korvaa näyttönimi (valinnainen)", - "Hours to show": "Näytettävät tunnit", - "Graph colour": "Kaavion väri", - "Annotation style": "Huomautustyyli", - "Circle on line": "Ympyrä viivalla", - "Dotted vertical line": "Pisteytetty pystylinja", - "Show records list below graph": "Näytä tietueiden luettelo kaavion alla", - "Records per page (blank = show all)": "Tietueita per sivu (tyhjä = kaikki)", - "Max records to show (blank = all)": "Enimmäismäärä näytettäviä tietueita (tyhjä = kaikki)", - "Show full message": "Näytä koko viesti", - "User will be able to expand the row if hidden": "Käyttäjä voi laajentaa rivin, jos se on piilotettu" - }; - const __vite_glob_0_6$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$2f - }, Symbol.toStringTag, { value: "Module" })); - const translations$2e = { - // Anomaly tooltip titles / meta - "⚠️ Anomaly Insight": "⚠️ Poikkeavuushavainto", - "⚠️ Multi-method Anomaly": "⚠️ Monimenetelmäinen poikkeavuus", - "Click the highlighted circle to add an annotation.": "Klikkaa korostettua ympyrää lisätäksesi huomautuksen.", - "Alert:": "Hälytys:", - "Confirmed by": "Vahvistettu", - "methods:": "menetelmällä:", - // Anomaly method labels - "Trend deviation": "Trendipoikkeama", - "Sudden change": "Äkillinen muutos", - "Statistical outlier (IQR)": "Tilastollinen poikkeama (IQR)", - "Rolling Z-score": "Liukuva Z-arvo", - "Flat-line / stuck": "Tasainen / jumittunut", - "Comparison window": "Vertailuikkuna", - // Anomaly description templates ({0} = label, {1} = start, {2} = end) - "{0} deviates from its expected trend between {1} and {2}.": "{0} poikkeaa odotetusta trendistä välillä {1} – {2}.", - "{0} shows an unusual rate of change between {1} and {2}.": "{0} näyttää epätavallista muutosnopeutta välillä {1} – {2}.", - "{0} contains statistical outliers between {1} and {2}.": "{0} sisältää tilastollisia poikkeamia välillä {1} – {2}.", - "{0} shows statistically unusual values between {1} and {2}.": "{0} näyttää tilastollisesti epätavallisia arvoja välillä {1} – {2}.", - "{0} appears stuck or flat between {1} and {2}{3}.": "{0} vaikuttaa jumittuneelta tai tasaiselta välillä {1} – {2}{3}.", - "{0} deviates significantly from the comparison window between {1} and {2}.": "{0} poikkeaa merkittävästi vertailuikkunasta välillä {1} – {2}.", - // Anomaly alert templates ({0}, {1}, {2} = formatted values / timestamps) - "Peak deviation: {0} from a baseline of {1} at {2}.": "Huippupoikkeama: {0} perusarvosta {1} ajankohtana {2}.", - "Peak rate deviation: {0} from a typical rate of {1} at {2}.": "Huippumuutosnopeus: {0} tyypillisestä nopeudesta {1} ajankohtana {2}.", - "Peak value: {0}, deviating {1} from the median at {2}.": "Huippuarvo: {0}, poikkeaa {1} mediaanista ajankohtana {2}.", - "Peak deviation: {0} from a rolling mean of {1} at {2}.": "Huippupoikkeama: {0} liukuvasta keskiarvosta {1} ajankohtana {2}.", - "Value remained near {0} for an unusually long period.": "Arvo pysyi lähellä arvoa {0} epätavallisen pitkään.", - "Peak deviation from comparison: {0} at {1}.": "Huippupoikkeama vertailusta: {0} ajankohtana {1}.", - " (range: {0})": " (vaihteluväli: {0})", - // Tooltip series type labels - "Date window": "Aikaikkuna", - Trend: "Trendi", - Rate: "Muutosnopeus", - Delta: "Delta", - Threshold: "Kynnysarvo" - }; - const __vite_glob_0_7$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$2e - }, Symbol.toStringTag, { value: "Module" })); - const translations$2d = { - "Confirm delete": "Vahvista poisto", - "Are you sure you want to delete this item?": "Oletko varma, että haluat poistaa tämän kohteen?", - Cancel: "Peruuta", - Delete: "Poista", - "Delete date window": "Poista aikaikkuna", - "this date window": "tämä aikaikkuna", - "Edit date window": "Muokkaa aikaikkunaa", - "Add date window": "Lisää aikaikkuna", - "Save date window": "Tallenna aikaikkuna", - "Create date window": "Luo aikaikkuna" - }; - const __vite_glob_0_8$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$2d - }, Symbol.toStringTag, { value: "Module" })); - const translations$2c = { - Wk: "Vk", - "Week of": "Viikko" - }; - const __vite_glob_0_9$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$2c - }, Symbol.toStringTag, { value: "Module" })); - const translations$2b = { - "Show anomalies": "Näytä poikkeamat", - Sensitivity: "Herkkyys", - "Use downsampled data for detection": "Käytä alasnäytteistettyä dataa havaitsemiseen", - "Rate window": "Muutosikkuna", - "Rolling window": "Liukuva ikkuna", - "Min flat duration": "Pienin tasainen kesto", - "Compare to window": "Vertaa ikkunaan", - "— select window —": "— valitse ikkuna —", - "When methods overlap": "Kun menetelmät menevät päällekkäin", - Low: "Matala", - Medium: "Keskitaso", - High: "Korkea", - "Trend deviation": "Trendipoikkeama", - "Sudden change": "Äkillinen muutos", - "Statistical outlier (IQR)": "Tilastollinen poikkeama (IQR)", - "Rolling Z-score": "Liukuva Z-arvo", - "Flat-line / stuck value": "Tasainen / jumittunut arvo", - "Comparison window deviation": "Vertailuikkunan poikkeama", - "Flags points that deviate significantly from a fitted trend line. Good for catching gradual drift or sudden jumps away from a steady baseline.": "Merkitsee pisteet, jotka poikkeavat selvästi sovitetusta trendiviivasta. Sopii asteittaisen ajautumisen tai äkillisten hyppyjen löytämiseen.", - "Flags unusually fast rises or drops compared to the typical rate of change. Best for detecting spikes, crashes, or rapid transitions.": "Merkitsee poikkeuksellisen nopeat nousut ja laskut verrattuna tyypilliseen muutosnopeuteen. Paras piikkien ja romahdusten havaitsemiseen.", - "Uses the interquartile range to flag values far outside the normal spread of data. Robust against outliers that skew averages.": "Käyttää interkvartiiliväliä arvojen merkitsemiseen, kun ne ovat kaukana normaalista vaihtelusta. Kestävä poikkeaville arvoille, jotka vääristävät keskiarvoja.", - "Compares each value to a rolling mean and standard deviation. Catches unusual readings relative to recent context rather than the whole series.": "Vertaa jokaista arvoa liukuvaan keskiarvoon ja keskihajontaan. Löytää epätavalliset lukemat suhteessa tuoreeseen kontekstiin.", - "Flags when a sensor reports nearly the same value for an unusually long time. Useful for detecting stuck sensors or frozen readings.": "Merkitsee tilanteet, joissa sensori raportoi lähes samaa arvoa poikkeuksellisen pitkään. Hyödyllinen jumittuneiden antureiden löytämiseen.", - "Compares the current period to a reference date window. Highlights differences from an expected historical pattern, such as last week or the same day last year.": "Vertaa nykyistä jaksoa viiteajanjaksoon. Korostaa erot odotetusta historiallisesta mallista, kuten viime viikkoon tai samaan päivään viime vuonna.", - "Show all anomalies": "Näytä kaikki poikkeamat", - "Overlaps only": "Vain päällekkäisyydet", - "Computing…": "Lasketaan…", - "1 hour": "1 tunti", - "3 hours": "3 tuntia", - "6 hours": "6 tuntia", - "12 hours": "12 tuntia", - "24 hours": "24 tuntia", - "7 days": "7 päivää", - "30 minutes": "30 minuuttia" - }; - const __vite_glob_0_10$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$2b - }, Symbol.toStringTag, { value: "Module" })); - const translations$2a = { - "Show delta vs selected date window": "Näytä delta vs. valittu aikaikkuna", - "Select a date window tab to enable delta analysis.": "Valitse aikaikkuna-välilehti ottaaksesi delta-analyysin käyttöön.", - "Show delta in tooltip": "Näytä delta työkaluvihjeessä", - "Show delta lines": "Näytä deltaviivat" - }; - const __vite_glob_0_11$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$2a - }, Symbol.toStringTag, { value: "Module" })); - const translations$29 = { - "Show rate of change": "Näytä muutosnopeus", - "Show rate of change crosshairs": "Näytä muutosnopeuden tähtäin", - "Rate window": "Muutosikkuna", - "Point to point": "Piste pisteeseen", - "1 hour": "1 tunti", - "6 hours": "6 tuntia", - "24 hours": "24 tuntia" - }; - const __vite_glob_0_12$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$29 - }, Symbol.toStringTag, { value: "Module" })); - const translations$28 = { - Downsampling: "Alasnäytteistys", - Interval: "Väli", - Aggregate: "Kooste", - "Raw (no sampling)": "Raaka (ei näytteistystä)", - "5 seconds": "5 sekuntia", - "10 seconds": "10 sekuntia", - "15 seconds": "15 sekuntia", - "30 seconds": "30 sekuntia", - "1 minute": "1 minuutti", - "2 minutes": "2 minuuttia", - "5 minutes": "5 minuuttia", - "10 minutes": "10 minuuttia", - "15 minutes": "15 minuuttia", - "30 minutes": "30 minuuttia", - "1 hour": "1 tunti", - "2 hours": "2 tuntia", - "3 hours": "3 tuntia", - "4 hours": "4 tuntia", - "6 hours": "6 tuntia", - "12 hours": "12 tuntia", - "24 hours": "24 tuntia", - "Mean (average)": "Keskiarvo", - Min: "Min", - Max: "Max", - Median: "Mediaani", - First: "Ensimmäinen", - Last: "Viimeinen" - }; - const __vite_glob_0_13$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$28 - }, Symbol.toStringTag, { value: "Module" })); - const translations$27 = { - "Show min / max / mean": "Näytä min / max / keskiarvo", - "Show range shading": "Näytä aluevarjostus" - }; - const __vite_glob_0_14$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$27 - }, Symbol.toStringTag, { value: "Module" })); - const translations$26 = { - "Show threshold analysis": "Näytä kynnysanalyysi", - "Shade threshold area": "Varjosta kynnysalue", - Threshold: "Kynnys", - "Shade area": "Varjostusalue", - "Shade above": "Varjosta yläpuolelle", - "Shade below": "Varjosta alapuolelle" - }; - const __vite_glob_0_15$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$26 - }, Symbol.toStringTag, { value: "Module" })); - const translations$25 = { - "Show trend lines": "Näytä trendiviivat", - "Show trend crosshairs": "Näytä trenditähtäin", - "Trend method": "Trendimenetelmä", - "Trend window": "Trendiikkuna", - "Rolling average": "Liukuva keskiarvo", - "Linear trend": "Lineaarinen trendi", - "1 hour": "1 tunti", - "6 hours": "6 tuntia", - "24 hours": "24 tuntia", - "7 days": "7 päivää", - "14 days": "14 päivää", - "21 days": "21 päivää", - "28 days": "28 päivää" - }; - const __vite_glob_0_16$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$25 - }, Symbol.toStringTag, { value: "Module" })); - const translations$24 = { - "Add date window": "Lisää aikaikkuna" - }; - const __vite_glob_0_17$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$24 - }, Symbol.toStringTag, { value: "Module" })); - const translations$23 = { - "A date window saves a named date range as a tab, so you can quickly preview it against the selected range or jump the chart back to it later.": "Aikaikkuna tallentaa nimetyn päivävälin välilehteksi, jotta voit nopeasti esikatsella sitä suhteessa valittuun alueeseen tai palata kaavion kyseiseen ajanjaksoon.", - Name: "Nimi", - "e.g. Heating season start": "esim. Lämmityskausi alkaa", - "Date range": "Päiväväli", - Start: "Alku", - End: "Loppu", - "Use previous range": "Käytä edellistä aluetta", - "Use next range": "Käytä seuraavaa aluetta", - "Delete date window": "Poista aikaikkuna", - Cancel: "Peruuta" - }; - const __vite_glob_0_18$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$23 - }, Symbol.toStringTag, { value: "Module" })); - const translations$22 = { - Datapoints: "Datapisteet", - "Choose which annotation datapoints appear on the chart.": "Valitse, mitkä huomautusten datapisteet näkyvät kaaviossa.", - "Linked to selected targets": "Linkitetty valittuihin kohteisiin", - "All datapoints": "Kaikki datapisteet", - "Hide datapoints": "Piilota datapisteet" - }; - const __vite_glob_0_19$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$22 - }, Symbol.toStringTag, { value: "Module" })); - const translations$21 = {}; - const __vite_glob_0_20$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$21 - }, Symbol.toStringTag, { value: "Module" })); - const translations$20 = { - "Analysis configured": "Analyysi määritetty", - "Configure analysis": "Määritä analyysi", - "Stepped series": "Porrastettu sarja", - "Hide source series": "Piilota lähdesarja", - "All targets already have the same settings": "Kaikilla kohteilla on jo samat asetukset", - "Copy these analysis settings to all targets": "Kopioi nämä analyysiasetus kaikille kohteille", - "Copy to all targets": "Kopioi kaikille kohteille" - }; - const __vite_glob_0_21$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$20 - }, Symbol.toStringTag, { value: "Module" })); - const translations$1$ = { - Targets: "Kohteet", - "Each row controls one chart series.": "Jokainen rivi ohjaa yhtä kaaviosarjaa.", - "Add target": "Lisää kohde", - "Chart preferences": "Kaavioasetukset" - }; - const __vite_glob_0_22$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1$ - }, Symbol.toStringTag, { value: "Module" })); - const translations$1_ = { - "Loading Datapoints…": "Ladataan Datapoints…", - Datapoints: "Datapoints", - "Page options": "Sivun asetukset", - "Download spreadsheet": "Lataa taulukko", - "Save page state": "Tallenna sivun tila", - "Restore saved page": "Palauta tallennettu sivu", - "Clear saved page": "Tyhjennä tallennettu sivu", - "Expand targets sidebar": "Laajenna kohteiden sivupalkki", - "Collapse targets sidebar": "Kutista kohteiden sivupalkki" - }; - const __vite_glob_0_23$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1_ - }, Symbol.toStringTag, { value: "Module" })); - const translations$1Z = { - "Toggle sidebar": "Vaihda sivupalkki", - Start: "Alku", - End: "Loppu", - "Select date range": "Valitse aikaväli", - "Timeline options": "Aikajanan asetukset", - "Zoom level": "Zoomaustaso", - "Date snapping": "Päivän kohdistus", - Auto: "Automaattinen", - Hour: "Tunti", - Day: "Päivä", - Week: "Viikko", - Month: "Kuukausi", - Minute: "Minuutti", - Second: "Sekunti", - Quarterly: "Neljännesvuosi", - "Month Compressed": "Kuukausi (tiivis)", - "Month Short": "Kuukausi (lyhyt)", - "Month Expanded": "Kuukausi (laaja)", - "Week Compressed": "Viikko (tiivis)", - "Week Expanded": "Viikko (laaja)" - }; - const __vite_glob_0_24$5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1Z - }, Symbol.toStringTag, { value: "Module" })); - const modules$5 = /* @__PURE__ */ Object.assign({ - "../../../atoms/interactive/range-timeline/i18n/fi.ts": __vite_glob_0_0$5, - "../../../cards/action/i18n/fi.ts": __vite_glob_0_1$5, - "../../../cards/history/history-chart/i18n/fi.ts": __vite_glob_0_2$5, - "../../../cards/history/i18n/fi.ts": __vite_glob_0_3$5, - "../../../cards/list/i18n/fi.ts": __vite_glob_0_4$5, - "../../../cards/quick/i18n/fi.ts": __vite_glob_0_5$5, - "../../../cards/sensor/i18n/fi.ts": __vite_glob_0_6$5, - "../../chart/i18n/fi.ts": __vite_glob_0_7$5, - "../../ha/i18n/fi.ts": __vite_glob_0_8$5, - "../../timeline/i18n/fi.ts": __vite_glob_0_9$5, - "../../../molecules/analysis-anomaly-group/i18n/fi.ts": __vite_glob_0_10$5, - "../../../molecules/analysis-delta-group/i18n/fi.ts": __vite_glob_0_11$5, - "../../../molecules/analysis-rate-group/i18n/fi.ts": __vite_glob_0_12$5, - "../../../molecules/analysis-sample-group/i18n/fi.ts": __vite_glob_0_13$5, - "../../../molecules/analysis-summary-group/i18n/fi.ts": __vite_glob_0_14$5, - "../../../molecules/analysis-threshold-group/i18n/fi.ts": __vite_glob_0_15$5, - "../../../molecules/analysis-trend-group/i18n/fi.ts": __vite_glob_0_16$5, - "../../../molecules/comparison-tab-rail/i18n/fi.ts": __vite_glob_0_17$5, - "../../../molecules/date-window-dialog/i18n/fi.ts": __vite_glob_0_18$5, - "../../../molecules/sidebar-options/sections/i18n/fi.ts": __vite_glob_0_19$5, - "../../../molecules/target-row-list/i18n/fi.ts": __vite_glob_0_20$5, - "../../../molecules/target-row/i18n/fi.ts": __vite_glob_0_21$5, - "../../../panels/datapoints/components/history-targets/i18n/fi.ts": __vite_glob_0_22$5, - "../../../panels/datapoints/components/panel-shell/i18n/fi.ts": __vite_glob_0_23$5, - "../../../panels/datapoints/components/range-toolbar/i18n/fi.ts": __vite_glob_0_24$5 - }); - const merged$5 = {}; - for (const mod of Object.values(modules$5)) { - Object.assign(merged$5, mod.translations); - } - const templates$5 = merged$5; - const fi = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - templates: templates$5 - }, Symbol.toStringTag, { value: "Module" })); - const translations$1Y = { - "Updates with new data": "Se met à jour avec de nouvelles données", - "Scroll to selected range": "Faire défiler jusqu’à la plage sélectionnée", - "Start date and time": "Date et heure de début", - "End date and time": "Date et heure de fin", - Select: "Sélectionner" - }; - const __vite_glob_0_0$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1Y - }, Symbol.toStringTag, { value: "Module" })); - const translations$1X = { - General: "Général", - "Related items": "Éléments liés", - "Datapoint Appearance": "Apparence du point de données", - "Form fields": "Champs du formulaire", - "Card title (optional)": "Titre de la carte (facultatif)", - "Pre-fill entities, devices, areas or labels that are always linked to recordings from this card.": "Préremplissez les entités, appareils, zones ou étiquettes toujours liés aux enregistrements créés avec cette carte.", - "Show always included targets on card": "Afficher sur la carte les cibles toujours incluses", - "Allow user to add more related items": "Autoriser l’utilisateur à ajouter d’autres éléments liés", - "Default icon": "Icône par défaut", - "Default colour": "Couleur par défaut", - "Show date & time field": "Afficher le champ date et heure", - "Show annotation field": "Afficher le champ d’annotation" - }; - const __vite_glob_0_1$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1X - }, Symbol.toStringTag, { value: "Module" })); - const translations$1W = { - "Date window:": "Fenêtre de dates :", - "Actual:": "Réel :" - }; - const __vite_glob_0_2$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1W - }, Symbol.toStringTag, { value: "Module" })); - const translations$1V = { - General: "Général", - Entity: "Entité", - "Multiple entities": "Plusieurs entités", - Display: "Affichage", - "Card title (optional)": "Titre de la carte (facultatif)", - "Hours to show": "Heures à afficher", - "Single entity": "Entité unique", - "Show data gaps": "Afficher les lacunes de données", - "Highlight missing data ranges with dashed lines and boundary markers": "Mettre en évidence les plages de données manquantes avec des lignes en pointillés et des marqueurs de bord" - }; - const __vite_glob_0_3$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1V - }, Symbol.toStringTag, { value: "Module" })); - const translations$1U = { - "Search datapoints…": "Rechercher des points de données…", - "Delete record": "Supprimer l’enregistrement", - Delete: "Supprimer", - "Show annotation": "Afficher l’annotation", - "Open related data point history": "Ouvrir l’historique du point de données lié", - "Edit record": "Modifier l’enregistrement", - "Show chart marker": "Afficher le marqueur du graphique", - "Hide chart marker": "Masquer le marqueur du graphique", - "Choose colour": "Choisir une couleur", - Save: "Enregistrer", - Cancel: "Annuler", - Message: "Message", - "Annotation / full message": "Annotation / message complet" - }; - const __vite_glob_0_4$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1U - }, Symbol.toStringTag, { value: "Module" })); - const translations$1T = { - General: "Général", - "Icon & colour": "Icône et couleur", - "Related items": "Éléments liés", - "Multiple entities": "Plusieurs entités", - "Form fields": "Champs du formulaire", - "Card title (optional)": "Titre de la carte (facultatif)", - "Input placeholder text": "Texte indicatif du champ", - Icon: "Icône", - Colour: "Couleur", - "These items will be linked to every record made with this card.": "Ces éléments seront liés à chaque enregistrement créé avec cette carte.", - "Single entity (optional)": "Entité unique (facultatif)", - "Add related items": "Ajouter des éléments liés", - "Show annotation field": "Afficher le champ d’annotation" - }; - const __vite_glob_0_5$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1T - }, Symbol.toStringTag, { value: "Module" })); - const translations$1S = { - Entity: "Entité", - Display: "Affichage", - "Records list": "Liste des enregistrements", - "Sensor entity *": "Entité capteur *", - "Override display name (optional)": "Remplacer le nom affiché (facultatif)", - "Hours to show": "Heures à afficher", - "Graph colour": "Couleur du graphique", - "Annotation style": "Style d’annotation", - "Circle on line": "Cercle sur la ligne", - "Dotted vertical line": "Ligne verticale pointillée", - "Show records list below graph": "Afficher la liste des enregistrements sous le graphique", - "Records per page (blank = show all)": "Enregistrements par page (vide = tout afficher)", - "Max records to show (blank = all)": "Nombre maximal d’enregistrements à afficher (vide = tous)", - "Show full message": "Afficher le message complet", - "User will be able to expand the row if hidden": "L’utilisateur pourra développer la ligne si elle est masquée" - }; - const __vite_glob_0_6$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1S - }, Symbol.toStringTag, { value: "Module" })); - const translations$1R = { - "⚠️ Anomaly Insight": "⚠️ Analyse d’anomalie", - "⚠️ Multi-method Anomaly": "⚠️ Anomalie multi-méthodes", - "Click the highlighted circle to add an annotation.": "Cliquez sur le cercle en surbrillance pour ajouter une annotation.", - "Alert:": "Alerte :", - "Confirmed by": "Confirmé par", - "methods:": "méthodes :", - "Trend deviation": "Écart de tendance", - "Sudden change": "Changement brusque", - "Statistical outlier (IQR)": "Valeur aberrante statistique (IQR)", - "Rolling Z-score": "Z-score glissant", - "Flat-line / stuck": "Stable / bloqué", - "Comparison window": "Fenêtre de comparaison", - "{0} deviates from its expected trend between {1} and {2}.": "{0} s’écarte de sa tendance attendue entre {1} et {2}.", - "{0} shows an unusual rate of change between {1} and {2}.": "{0} présente un taux de variation inhabituel entre {1} et {2}.", - "{0} contains statistical outliers between {1} and {2}.": "{0} contient des valeurs aberrantes statistiques entre {1} et {2}.", - "{0} shows statistically unusual values between {1} and {2}.": "{0} présente des valeurs statistiquement inhabituelles entre {1} et {2}.", - "{0} appears stuck or flat between {1} and {2}{3}.": "{0} semble bloqué ou stable entre {1} et {2}{3}.", - "{0} deviates significantly from the comparison window between {1} and {2}.": "{0} s’écarte significativement de la fenêtre de comparaison entre {1} et {2}.", - "Peak deviation: {0} from a baseline of {1} at {2}.": "Écart maximal : {0} par rapport à une base de {1} à {2}.", - "Peak rate deviation: {0} from a typical rate of {1} at {2}.": "Écart maximal de vitesse : {0} par rapport à une vitesse typique de {1} à {2}.", - "Peak value: {0}, deviating {1} from the median at {2}.": "Valeur maximale : {0}, s’écarte de {1} par rapport à la médiane à {2}.", - "Peak deviation: {0} from a rolling mean of {1} at {2}.": "Écart maximal : {0} par rapport à une moyenne mobile de {1} à {2}.", - "Value remained near {0} for an unusually long period.": "La valeur est restée proche de {0} pendant une durée inhabituellement longue.", - "Peak deviation from comparison: {0} at {1}.": "Écart maximal par rapport à la comparaison : {0} à {1}.", - " (range: {0})": " (plage : {0})", - "Date window": "Fenêtre de dates", - Trend: "Tendance", - Rate: "Vitesse", - Delta: "Delta", - Threshold: "Seuil" - }; - const __vite_glob_0_7$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1R - }, Symbol.toStringTag, { value: "Module" })); - const translations$1Q = { - "Confirm delete": "Confirmer la suppression", - "Are you sure you want to delete this item?": "Voulez-vous vraiment supprimer cet élément ?", - Cancel: "Annuler", - Delete: "Supprimer", - "Delete date window": "Supprimer la fenêtre de dates", - "this date window": "cette fenêtre de dates", - "Edit date window": "Modifier la fenêtre de dates", - "Add date window": "Ajouter une fenêtre de dates", - "Save date window": "Enregistrer la fenêtre de dates", - "Create date window": "Créer une fenêtre de dates" - }; - const __vite_glob_0_8$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1Q - }, Symbol.toStringTag, { value: "Module" })); - const translations$1P = { - Wk: "Sem.", - "Week of": "Semaine du" - }; - const __vite_glob_0_9$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1P - }, Symbol.toStringTag, { value: "Module" })); - const translations$1O = { - "Show anomalies": "Afficher les anomalies", - Sensitivity: "Sensibilité", - "Use downsampled data for detection": "Utiliser les données rééchantillonnées pour la détection", - "Rate window": "Fenêtre de variation", - "Rolling window": "Fenêtre glissante", - "Min flat duration": "Durée minimale de stabilité", - "Compare to window": "Comparer à la fenêtre", - "— select window —": "— sélectionner une fenêtre —", - "When methods overlap": "Lorsque les méthodes se chevauchent", - Low: "Faible", - Medium: "Moyenne", - High: "Élevée", - "Trend deviation": "Écart de tendance", - "Sudden change": "Changement brusque", - "Statistical outlier (IQR)": "Valeur aberrante statistique (IQR)", - "Rolling Z-score": "Z-score glissant", - "Flat-line / stuck value": "Valeur stable / bloquée", - "Comparison window deviation": "Écart par rapport à la fenêtre de comparaison", - "Flags points that deviate significantly from a fitted trend line. Good for catching gradual drift or sudden jumps away from a steady baseline.": "Signale les points qui s’écartent fortement d’une ligne de tendance ajustée. Idéal pour repérer une dérive progressive ou des sauts soudains loin d’une base stable.", - "Flags unusually fast rises or drops compared to the typical rate of change. Best for detecting spikes, crashes, or rapid transitions.": "Signale les hausses ou baisses inhabituellement rapides par rapport au taux de variation habituel. Idéal pour détecter des pics, des chutes ou des transitions rapides.", - "Uses the interquartile range to flag values far outside the normal spread of data. Robust against outliers that skew averages.": "Utilise l’intervalle interquartile pour signaler les valeurs très en dehors de la dispersion normale des données. Robuste face aux valeurs extrêmes qui faussent les moyennes.", - "Compares each value to a rolling mean and standard deviation. Catches unusual readings relative to recent context rather than the whole series.": "Compare chaque valeur à une moyenne mobile et à un écart-type. Détecte les lectures inhabituelles par rapport au contexte récent plutôt qu’à l’ensemble de la série.", - "Flags when a sensor reports nearly the same value for an unusually long time. Useful for detecting stuck sensors or frozen readings.": "Signale lorsqu’un capteur rapporte presque la même valeur pendant une durée inhabituellement longue. Utile pour détecter des capteurs bloqués ou des lectures figées.", - "Compares the current period to a reference date window. Highlights differences from an expected historical pattern, such as last week or the same day last year.": "Compare la période actuelle à une fenêtre de dates de référence. Met en évidence les différences par rapport à un modèle historique attendu, comme la semaine dernière ou le même jour l’année précédente.", - "Show all anomalies": "Afficher toutes les anomalies", - "Overlaps only": "Chevauchements uniquement", - "Computing…": "Calcul…", - "1 hour": "1 heure", - "3 hours": "3 heures", - "6 hours": "6 heures", - "12 hours": "12 heures", - "24 hours": "24 heures", - "7 days": "7 jours", - "30 minutes": "30 minutes" - }; - const __vite_glob_0_10$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1O - }, Symbol.toStringTag, { value: "Module" })); - const translations$1N = { - "Show delta vs selected date window": "Afficher le delta par rapport à la fenêtre de dates sélectionnée", - "Select a date window tab to enable delta analysis.": "Sélectionnez un onglet de fenêtre de dates pour activer l’analyse delta.", - "Show delta in tooltip": "Afficher le delta dans l’infobulle", - "Show delta lines": "Afficher les lignes delta" - }; - const __vite_glob_0_11$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1N - }, Symbol.toStringTag, { value: "Module" })); - const translations$1M = { - "Show rate of change": "Afficher le taux de variation", - "Show rate of change crosshairs": "Afficher les repères du taux de variation", - "Rate window": "Fenêtre de variation", - "Point to point": "Point à point", - "1 hour": "1 heure", - "6 hours": "6 heures", - "24 hours": "24 heures" - }; - const __vite_glob_0_12$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1M - }, Symbol.toStringTag, { value: "Module" })); - const translations$1L = { - Downsampling: "Rééchantillonnage", - Interval: "Intervalle", - Aggregate: "Agrégat", - "Raw (no sampling)": "Brut (sans échantillonnage)", - "5 seconds": "5 secondes", - "10 seconds": "10 secondes", - "15 seconds": "15 secondes", - "30 seconds": "30 secondes", - "1 minute": "1 minute", - "2 minutes": "2 minutes", - "5 minutes": "5 minutes", - "10 minutes": "10 minutes", - "15 minutes": "15 minutes", - "30 minutes": "30 minutes", - "1 hour": "1 heure", - "2 hours": "2 heures", - "3 hours": "3 heures", - "4 hours": "4 heures", - "6 hours": "6 heures", - "12 hours": "12 heures", - "24 hours": "24 heures", - "Mean (average)": "Moyenne", - Min: "Min", - Max: "Max", - Median: "Médiane", - First: "Premier", - Last: "Dernier" - }; - const __vite_glob_0_13$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1L - }, Symbol.toStringTag, { value: "Module" })); - const translations$1K = { - "Show min / max / mean": "Afficher min / max / moyenne", - "Show range shading": "Afficher l’ombrage de la plage" - }; - const __vite_glob_0_14$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1K - }, Symbol.toStringTag, { value: "Module" })); - const translations$1J = { - "Show threshold analysis": "Afficher l’analyse de seuil", - "Shade threshold area": "Ombrer la zone du seuil", - Threshold: "Seuil", - "Shade area": "Ombrer la zone", - "Shade above": "Ombrer au-dessus", - "Shade below": "Ombrer en dessous" - }; - const __vite_glob_0_15$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1J - }, Symbol.toStringTag, { value: "Module" })); - const translations$1I = { - "Show trend lines": "Afficher les lignes de tendance", - "Show trend crosshairs": "Afficher les repères de tendance", - "Trend method": "Méthode de tendance", - "Trend window": "Fenêtre de tendance", - "Rolling average": "Moyenne mobile", - "Linear trend": "Tendance linéaire", - "1 hour": "1 heure", - "6 hours": "6 heures", - "24 hours": "24 heures", - "7 days": "7 jours", - "14 days": "14 jours", - "21 days": "21 jours", - "28 days": "28 jours" - }; - const __vite_glob_0_16$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1I - }, Symbol.toStringTag, { value: "Module" })); - const translations$1H = { - "Add date window": "Ajouter une fenêtre de dates" - }; - const __vite_glob_0_17$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1H - }, Symbol.toStringTag, { value: "Module" })); - const translations$1G = { - "A date window saves a named date range as a tab, so you can quickly preview it against the selected range or jump the chart back to it later.": "Une fenêtre de dates enregistre une plage de dates nommée comme onglet afin que vous puissiez la prévisualiser rapidement par rapport à la plage sélectionnée ou y revenir plus tard dans le graphique.", - Name: "Nom", - "e.g. Heating season start": "ex. Début de la saison de chauffe", - "Date range": "Plage de dates", - Start: "Début", - End: "Fin", - "Use previous range": "Utiliser la plage précédente", - "Use next range": "Utiliser la plage suivante", - "Delete date window": "Supprimer la fenêtre de dates", - Cancel: "Annuler" - }; - const __vite_glob_0_18$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1G - }, Symbol.toStringTag, { value: "Module" })); - const translations$1F = { - Datapoints: "Points de données", - "Choose which annotation datapoints appear on the chart.": "Choisissez quels points de données d’annotation apparaissent sur le graphique.", - "Linked to selected targets": "Liés aux cibles sélectionnées", - "All datapoints": "Tous les points de données", - "Hide datapoints": "Masquer les points de données" - }; - const __vite_glob_0_19$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1F - }, Symbol.toStringTag, { value: "Module" })); - const translations$1E = {}; - const __vite_glob_0_20$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1E - }, Symbol.toStringTag, { value: "Module" })); - const translations$1D = { - "Analysis configured": "Analyse configurée", - "Configure analysis": "Configurer l’analyse", - "Stepped series": "Série en escalier", - "Hide source series": "Masquer la série source", - "All targets already have the same settings": "Toutes les cibles ont déjà les mêmes paramètres", - "Copy these analysis settings to all targets": "Copier ces paramètres d’analyse vers toutes les cibles", - "Copy to all targets": "Copier vers toutes les cibles" - }; - const __vite_glob_0_21$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1D - }, Symbol.toStringTag, { value: "Module" })); - const translations$1C = { - Targets: "Cibles", - "Each row controls one chart series.": "Chaque ligne contrôle une série du graphique.", - "Add target": "Ajouter une cible", - "Chart preferences": "Préférences du graphique" - }; - const __vite_glob_0_22$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1C - }, Symbol.toStringTag, { value: "Module" })); - const translations$1B = { - "Loading Datapoints…": "Chargement des points de données…", - Datapoints: "Points de données", - "Page options": "Options de la page", - "Download spreadsheet": "Télécharger la feuille de calcul", - "Save page state": "Enregistrer l’état de la page", - "Restore saved page": "Restaurer la page enregistrée", - "Clear saved page": "Effacer la page enregistrée", - "Expand targets sidebar": "Développer la barre latérale des cibles", - "Collapse targets sidebar": "Réduire la barre latérale des cibles" - }; - const __vite_glob_0_23$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1B - }, Symbol.toStringTag, { value: "Module" })); - const translations$1A = { - "Toggle sidebar": "Basculer la barre latérale", - Start: "Début", - End: "Fin", - "Select date range": "Sélectionner une plage de dates", - "Timeline options": "Options de la chronologie", - "Zoom level": "Niveau de zoom", - "Date snapping": "Ajustement des dates", - Auto: "Auto", - Hour: "Heure", - Day: "Jour", - Week: "Semaine", - Month: "Mois", - Minute: "Minute", - Second: "Seconde", - Quarterly: "Trimestriel", - "Month Compressed": "Mois compact", - "Month Short": "Mois abrégé", - "Month Expanded": "Mois développé", - "Week Compressed": "Semaine compacte", - "Week Expanded": "Semaine développée" - }; - const __vite_glob_0_24$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1A - }, Symbol.toStringTag, { value: "Module" })); - const modules$4 = /* @__PURE__ */ Object.assign({ - "../../../atoms/interactive/range-timeline/i18n/fr.ts": __vite_glob_0_0$4, - "../../../cards/action/i18n/fr.ts": __vite_glob_0_1$4, - "../../../cards/history/history-chart/i18n/fr.ts": __vite_glob_0_2$4, - "../../../cards/history/i18n/fr.ts": __vite_glob_0_3$4, - "../../../cards/list/i18n/fr.ts": __vite_glob_0_4$4, - "../../../cards/quick/i18n/fr.ts": __vite_glob_0_5$4, - "../../../cards/sensor/i18n/fr.ts": __vite_glob_0_6$4, - "../../chart/i18n/fr.ts": __vite_glob_0_7$4, - "../../ha/i18n/fr.ts": __vite_glob_0_8$4, - "../../timeline/i18n/fr.ts": __vite_glob_0_9$4, - "../../../molecules/analysis-anomaly-group/i18n/fr.ts": __vite_glob_0_10$4, - "../../../molecules/analysis-delta-group/i18n/fr.ts": __vite_glob_0_11$4, - "../../../molecules/analysis-rate-group/i18n/fr.ts": __vite_glob_0_12$4, - "../../../molecules/analysis-sample-group/i18n/fr.ts": __vite_glob_0_13$4, - "../../../molecules/analysis-summary-group/i18n/fr.ts": __vite_glob_0_14$4, - "../../../molecules/analysis-threshold-group/i18n/fr.ts": __vite_glob_0_15$4, - "../../../molecules/analysis-trend-group/i18n/fr.ts": __vite_glob_0_16$4, - "../../../molecules/comparison-tab-rail/i18n/fr.ts": __vite_glob_0_17$4, - "../../../molecules/date-window-dialog/i18n/fr.ts": __vite_glob_0_18$4, - "../../../molecules/sidebar-options/sections/i18n/fr.ts": __vite_glob_0_19$4, - "../../../molecules/target-row-list/i18n/fr.ts": __vite_glob_0_20$4, - "../../../molecules/target-row/i18n/fr.ts": __vite_glob_0_21$4, - "../../../panels/datapoints/components/history-targets/i18n/fr.ts": __vite_glob_0_22$4, - "../../../panels/datapoints/components/panel-shell/i18n/fr.ts": __vite_glob_0_23$4, - "../../../panels/datapoints/components/range-toolbar/i18n/fr.ts": __vite_glob_0_24$4 - }); - const merged$4 = {}; - for (const mod of Object.values(modules$4)) { - Object.assign(merged$4, mod.translations); - } - const templates$4 = merged$4; - const fr = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - templates: templates$4 - }, Symbol.toStringTag, { value: "Module" })); - const translations$1z = { - "Updates with new data": "Aktualisiert sich mit neuen Daten", - "Scroll to selected range": "Zum ausgewählten Bereich scrollen", - "Start date and time": "Startdatum und -zeit", - "End date and time": "Enddatum und -zeit", - Select: "Auswählen" - }; - const __vite_glob_0_0$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1z - }, Symbol.toStringTag, { value: "Module" })); - const translations$1y = { - General: "Allgemein", - "Related items": "Verknüpfte Elemente", - "Datapoint Appearance": "Darstellung des Datenpunkts", - "Form fields": "Formularfelder", - "Card title (optional)": "Kartentitel (optional)", - "Pre-fill entities, devices, areas or labels that are always linked to recordings from this card.": "Entitäten, Geräte, Bereiche oder Labels vorab ausfüllen, die immer mit Aufzeichnungen dieser Karte verknüpft sind.", - "Show always included targets on card": "Immer enthaltene Ziele auf der Karte anzeigen", - "Allow user to add more related items": "Benutzer dürfen weitere verknüpfte Elemente hinzufügen", - "Default icon": "Standardsymbol", - "Default colour": "Standardfarbe", - "Show date & time field": "Datums- und Uhrzeitfeld anzeigen", - "Show annotation field": "Anmerkungsfeld anzeigen" - }; - const __vite_glob_0_1$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1y - }, Symbol.toStringTag, { value: "Module" })); - const translations$1x = { - "Date window:": "Datumsfenster:", - "Actual:": "Tatsächlich:" - }; - const __vite_glob_0_2$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1x - }, Symbol.toStringTag, { value: "Module" })); - const translations$1w = { - General: "Allgemein", - Entity: "Entität", - "Multiple entities": "Mehrere Entitäten", - Display: "Anzeige", - "Card title (optional)": "Kartentitel (optional)", - "Hours to show": "Anzuzeigende Stunden", - "Single entity": "Einzelne Entität", - "Show data gaps": "Datenlücken anzeigen", - "Highlight missing data ranges with dashed lines and boundary markers": "Fehlende Datenbereiche mit gestrichelten Linien und Randmarkierungen hervorheben" - }; - const __vite_glob_0_3$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1w - }, Symbol.toStringTag, { value: "Module" })); - const translations$1v = { - "Search datapoints…": "Datenpunkte suchen…", - "Delete record": "Eintrag löschen", - Delete: "Löschen", - "Show annotation": "Anmerkung anzeigen", - "Open related data point history": "Verlauf des verknüpften Datenpunkts öffnen", - "Edit record": "Eintrag bearbeiten", - "Show chart marker": "Diagrammmarkierung anzeigen", - "Hide chart marker": "Diagrammmarkierung ausblenden", - "Choose colour": "Farbe auswählen", - Save: "Speichern", - Cancel: "Abbrechen", - Message: "Nachricht", - "Annotation / full message": "Anmerkung / vollständige Nachricht" - }; - const __vite_glob_0_4$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1v - }, Symbol.toStringTag, { value: "Module" })); - const translations$1u = { - General: "Allgemein", - "Icon & colour": "Symbol und Farbe", - "Related items": "Verknüpfte Elemente", - "Multiple entities": "Mehrere Entitäten", - "Form fields": "Formularfelder", - "Card title (optional)": "Kartentitel (optional)", - "Input placeholder text": "Platzhaltertext", - Icon: "Symbol", - Colour: "Farbe", - "These items will be linked to every record made with this card.": "Diese Elemente werden mit jedem Eintrag verknüpft, der mit dieser Karte erstellt wird.", - "Single entity (optional)": "Einzelne Entität (optional)", - "Add related items": "Verknüpfte Elemente hinzufügen", - "Show annotation field": "Anmerkungsfeld anzeigen" - }; - const __vite_glob_0_5$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1u - }, Symbol.toStringTag, { value: "Module" })); - const translations$1t = { - Entity: "Entität", - Display: "Anzeige", - "Records list": "Eintragsliste", - "Sensor entity *": "Sensor-Entität *", - "Override display name (optional)": "Anzeigenamen überschreiben (optional)", - "Hours to show": "Anzuzeigende Stunden", - "Graph colour": "Diagrammfarbe", - "Annotation style": "Anmerkungsstil", - "Circle on line": "Kreis auf der Linie", - "Dotted vertical line": "Gepunktete vertikale Linie", - "Show records list below graph": "Eintragsliste unter dem Diagramm anzeigen", - "Records per page (blank = show all)": "Einträge pro Seite (leer = alle anzeigen)", - "Max records to show (blank = all)": "Maximal anzuzeigende Einträge (leer = alle)", - "Show full message": "Vollständige Nachricht anzeigen", - "User will be able to expand the row if hidden": "Benutzer können die Zeile erweitern, wenn sie ausgeblendet ist" - }; - const __vite_glob_0_6$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1t - }, Symbol.toStringTag, { value: "Module" })); - const translations$1s = { - "⚠️ Anomaly Insight": "⚠️ Anomalie-Einblick", - "⚠️ Multi-method Anomaly": "⚠️ Anomalie mit mehreren Methoden", - "Click the highlighted circle to add an annotation.": "Klicken Sie auf den hervorgehobenen Kreis, um eine Anmerkung hinzuzufügen.", - "Alert:": "Warnung:", - "Confirmed by": "Bestätigt durch", - "methods:": "Methoden:", - "Trend deviation": "Trendabweichung", - "Sudden change": "Plötzliche Änderung", - "Statistical outlier (IQR)": "Statistischer Ausreißer (IQR)", - "Rolling Z-score": "Gleitender Z-Score", - "Flat-line / stuck": "Flach / festhängend", - "Comparison window": "Vergleichsfenster", - "{0} deviates from its expected trend between {1} and {2}.": "{0} weicht zwischen {1} und {2} von seinem erwarteten Trend ab.", - "{0} shows an unusual rate of change between {1} and {2}.": "{0} zeigt zwischen {1} und {2} eine ungewöhnliche Änderungsrate.", - "{0} contains statistical outliers between {1} and {2}.": "{0} enthält statistische Ausreißer zwischen {1} und {2}.", - "{0} shows statistically unusual values between {1} and {2}.": "{0} zeigt zwischen {1} und {2} statistisch ungewöhnliche Werte.", - "{0} appears stuck or flat between {1} and {2}{3}.": "{0} scheint zwischen {1} und {2}{3} festzuhängen oder flach zu sein.", - "{0} deviates significantly from the comparison window between {1} and {2}.": "{0} weicht zwischen {1} und {2} deutlich vom Vergleichsfenster ab.", - "Peak deviation: {0} from a baseline of {1} at {2}.": "Maximale Abweichung: {0} von einer Basis von {1} um {2}.", - "Peak rate deviation: {0} from a typical rate of {1} at {2}.": "Maximale Ratenabweichung: {0} von einer typischen Rate von {1} um {2}.", - "Peak value: {0}, deviating {1} from the median at {2}.": "Spitzenwert: {0}, weicht um {1} vom Median bei {2} ab.", - "Peak deviation: {0} from a rolling mean of {1} at {2}.": "Maximale Abweichung: {0} von einem gleitenden Mittelwert von {1} um {2}.", - "Value remained near {0} for an unusually long period.": "Der Wert blieb ungewöhnlich lange nahe bei {0}.", - "Peak deviation from comparison: {0} at {1}.": "Maximale Abweichung vom Vergleich: {0} um {1}.", - " (range: {0})": " (Bereich: {0})", - "Date window": "Datumsfenster", - Trend: "Trend", - Rate: "Rate", - Delta: "Delta", - Threshold: "Schwellenwert" - }; - const __vite_glob_0_7$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1s - }, Symbol.toStringTag, { value: "Module" })); - const translations$1r = { - "Confirm delete": "Löschen bestätigen", - "Are you sure you want to delete this item?": "Möchten Sie dieses Element wirklich löschen?", - Cancel: "Abbrechen", - Delete: "Löschen", - "Delete date window": "Datumsfenster löschen", - "this date window": "dieses Datumsfenster", - "Edit date window": "Datumsfenster bearbeiten", - "Add date window": "Datumsfenster hinzufügen", - "Save date window": "Datumsfenster speichern", - "Create date window": "Datumsfenster erstellen" - }; - const __vite_glob_0_8$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1r - }, Symbol.toStringTag, { value: "Module" })); - const translations$1q = { - Wk: "KW", - "Week of": "Woche von" - }; - const __vite_glob_0_9$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1q - }, Symbol.toStringTag, { value: "Module" })); - const translations$1p = { - "Show anomalies": "Anomalien anzeigen", - Sensitivity: "Empfindlichkeit", - "Use downsampled data for detection": "Heruntergesampelte Daten für die Erkennung verwenden", - "Rate window": "Ratenfenster", - "Rolling window": "Gleitendes Fenster", - "Min flat duration": "Minimale Flachdauer", - "Compare to window": "Mit Fenster vergleichen", - "— select window —": "— Fenster auswählen —", - "When methods overlap": "Wenn sich Methoden überschneiden", - Low: "Niedrig", - Medium: "Mittel", - High: "Hoch", - "Trend deviation": "Trendabweichung", - "Sudden change": "Plötzliche Änderung", - "Statistical outlier (IQR)": "Statistischer Ausreißer (IQR)", - "Rolling Z-score": "Gleitender Z-Score", - "Flat-line / stuck value": "Flacher / festhängender Wert", - "Comparison window deviation": "Abweichung vom Vergleichsfenster", - "Flags points that deviate significantly from a fitted trend line. Good for catching gradual drift or sudden jumps away from a steady baseline.": "Markiert Punkte, die deutlich von einer angepassten Trendlinie abweichen. Gut geeignet, um schleichende Drift oder plötzliche Sprünge von einer stabilen Basis zu erkennen.", - "Flags unusually fast rises or drops compared to the typical rate of change. Best for detecting spikes, crashes, or rapid transitions.": "Markiert ungewöhnlich schnelle Anstiege oder Abfälle im Vergleich zur typischen Änderungsrate. Ideal zum Erkennen von Spitzen, Einbrüchen oder schnellen Übergängen.", - "Uses the interquartile range to flag values far outside the normal spread of data. Robust against outliers that skew averages.": "Verwendet den Interquartilsabstand, um Werte zu markieren, die weit außerhalb der normalen Datenstreuung liegen. Robust gegenüber Ausreißern, die Mittelwerte verzerren.", - "Compares each value to a rolling mean and standard deviation. Catches unusual readings relative to recent context rather than the whole series.": "Vergleicht jeden Wert mit einem gleitenden Mittelwert und einer Standardabweichung. Erkennt ungewöhnliche Werte relativ zum jüngsten Kontext statt zur gesamten Serie.", - "Flags when a sensor reports nearly the same value for an unusually long time. Useful for detecting stuck sensors or frozen readings.": "Markiert, wenn ein Sensor ungewöhnlich lange nahezu denselben Wert meldet. Nützlich zum Erkennen festhängender Sensoren oder eingefrorener Messwerte.", - "Compares the current period to a reference date window. Highlights differences from an expected historical pattern, such as last week or the same day last year.": "Vergleicht den aktuellen Zeitraum mit einem Referenz-Datumsfenster. Hebt Unterschiede zu einem erwarteten historischen Muster hervor, etwa zur letzten Woche oder zum gleichen Tag im Vorjahr.", - "Show all anomalies": "Alle Anomalien anzeigen", - "Overlaps only": "Nur Überlappungen", - "Computing…": "Wird berechnet…", - "1 hour": "1 Stunde", - "3 hours": "3 Stunden", - "6 hours": "6 Stunden", - "12 hours": "12 Stunden", - "24 hours": "24 Stunden", - "7 days": "7 Tage", - "30 minutes": "30 Minuten" - }; - const __vite_glob_0_10$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1p - }, Symbol.toStringTag, { value: "Module" })); - const translations$1o = { - "Show delta vs selected date window": "Delta gegenüber dem ausgewählten Datumsfenster anzeigen", - "Select a date window tab to enable delta analysis.": "Wählen Sie einen Tab für ein Datumsfenster, um die Delta-Analyse zu aktivieren.", - "Show delta in tooltip": "Delta im Tooltip anzeigen", - "Show delta lines": "Delta-Linien anzeigen" - }; - const __vite_glob_0_11$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1o - }, Symbol.toStringTag, { value: "Module" })); - const translations$1n = { - "Show rate of change": "Änderungsrate anzeigen", - "Show rate of change crosshairs": "Fadenkreuz für Änderungsrate anzeigen", - "Rate window": "Ratenfenster", - "Point to point": "Punkt zu Punkt", - "1 hour": "1 Stunde", - "6 hours": "6 Stunden", - "24 hours": "24 Stunden" - }; - const __vite_glob_0_12$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1n - }, Symbol.toStringTag, { value: "Module" })); - const translations$1m = { - Downsampling: "Downsampling", - Interval: "Intervall", - Aggregate: "Aggregation", - "Raw (no sampling)": "Roh (ohne Sampling)", - "5 seconds": "5 Sekunden", - "10 seconds": "10 Sekunden", - "15 seconds": "15 Sekunden", - "30 seconds": "30 Sekunden", - "1 minute": "1 Minute", - "2 minutes": "2 Minuten", - "5 minutes": "5 Minuten", - "10 minutes": "10 Minuten", - "15 minutes": "15 Minuten", - "30 minutes": "30 Minuten", - "1 hour": "1 Stunde", - "2 hours": "2 Stunden", - "3 hours": "3 Stunden", - "4 hours": "4 Stunden", - "6 hours": "6 Stunden", - "12 hours": "12 Stunden", - "24 hours": "24 Stunden", - "Mean (average)": "Mittelwert", - Min: "Min.", - Max: "Max.", - Median: "Median", - First: "Erster", - Last: "Letzter" - }; - const __vite_glob_0_13$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1m - }, Symbol.toStringTag, { value: "Module" })); - const translations$1l = { - "Show min / max / mean": "Min / Max / Mittelwert anzeigen", - "Show range shading": "Bereichsschattierung anzeigen" - }; - const __vite_glob_0_14$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1l - }, Symbol.toStringTag, { value: "Module" })); - const translations$1k = { - "Show threshold analysis": "Schwellwertanalyse anzeigen", - "Shade threshold area": "Schwellwertbereich schattieren", - Threshold: "Schwellenwert", - "Shade area": "Bereich schattieren", - "Shade above": "Oberhalb schattieren", - "Shade below": "Unterhalb schattieren" - }; - const __vite_glob_0_15$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1k - }, Symbol.toStringTag, { value: "Module" })); - const translations$1j = { - "Show trend lines": "Trendlinien anzeigen", - "Show trend crosshairs": "Fadenkreuz für Trend anzeigen", - "Trend method": "Trendmethode", - "Trend window": "Trendfenster", - "Rolling average": "Gleitender Durchschnitt", - "Linear trend": "Linearer Trend", - "1 hour": "1 Stunde", - "6 hours": "6 Stunden", - "24 hours": "24 Stunden", - "7 days": "7 Tage", - "14 days": "14 Tage", - "21 days": "21 Tage", - "28 days": "28 Tage" - }; - const __vite_glob_0_16$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1j - }, Symbol.toStringTag, { value: "Module" })); - const translations$1i = { - "Add date window": "Datumsfenster hinzufügen" - }; - const __vite_glob_0_17$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1i - }, Symbol.toStringTag, { value: "Module" })); - const translations$1h = { - "A date window saves a named date range as a tab, so you can quickly preview it against the selected range or jump the chart back to it later.": "Ein Datumsfenster speichert einen benannten Datumsbereich als Registerkarte, damit Sie ihn schnell mit dem ausgewählten Bereich vergleichen oder später im Diagramm wieder dorthin springen können.", - Name: "Name", - "e.g. Heating season start": "z. B. Beginn der Heizsaison", - "Date range": "Datumsbereich", - Start: "Start", - End: "Ende", - "Use previous range": "Vorherigen Bereich verwenden", - "Use next range": "Nächsten Bereich verwenden", - "Delete date window": "Datumsfenster löschen", - Cancel: "Abbrechen" - }; - const __vite_glob_0_18$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1h - }, Symbol.toStringTag, { value: "Module" })); - const translations$1g = { - Datapoints: "Datenpunkte", - "Choose which annotation datapoints appear on the chart.": "Wählen Sie aus, welche Anmerkungs-Datenpunkte im Diagramm angezeigt werden.", - "Linked to selected targets": "Mit ausgewählten Zielen verknüpft", - "All datapoints": "Alle Datenpunkte", - "Hide datapoints": "Datenpunkte ausblenden" - }; - const __vite_glob_0_19$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1g - }, Symbol.toStringTag, { value: "Module" })); - const translations$1f = {}; - const __vite_glob_0_20$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1f - }, Symbol.toStringTag, { value: "Module" })); - const translations$1e = { - "Analysis configured": "Analyse konfiguriert", - "Configure analysis": "Analyse konfigurieren", - "Stepped series": "Stufenserie", - "Hide source series": "Quellserie ausblenden", - "All targets already have the same settings": "Alle Ziele haben bereits dieselben Einstellungen", - "Copy these analysis settings to all targets": "Diese Analyseeinstellungen auf alle Ziele kopieren", - "Copy to all targets": "Auf alle Ziele kopieren" - }; - const __vite_glob_0_21$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1e - }, Symbol.toStringTag, { value: "Module" })); - const translations$1d = { - Targets: "Ziele", - "Each row controls one chart series.": "Jede Zeile steuert eine Diagrammserie.", - "Add target": "Ziel hinzufügen", - "Chart preferences": "Diagrammeinstellungen" - }; - const __vite_glob_0_22$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1d - }, Symbol.toStringTag, { value: "Module" })); - const translations$1c = { - "Loading Datapoints…": "Datenpunkte werden geladen…", - Datapoints: "Datenpunkte", - "Page options": "Seitenoptionen", - "Download spreadsheet": "Tabellenblatt herunterladen", - "Save page state": "Seitenstatus speichern", - "Restore saved page": "Gespeicherte Seite wiederherstellen", - "Clear saved page": "Gespeicherte Seite löschen", - "Expand targets sidebar": "Ziel-Seitenleiste ausklappen", - "Collapse targets sidebar": "Ziel-Seitenleiste einklappen" - }; - const __vite_glob_0_23$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1c - }, Symbol.toStringTag, { value: "Module" })); - const translations$1b = { - "Toggle sidebar": "Seitenleiste umschalten", - Start: "Start", - End: "Ende", - "Select date range": "Datumsbereich auswählen", - "Timeline options": "Zeitachsenoptionen", - "Zoom level": "Zoomstufe", - "Date snapping": "Datumsraster", - Auto: "Auto", - Hour: "Stunde", - Day: "Tag", - Week: "Woche", - Month: "Monat", - Minute: "Minute", - Second: "Sekunde", - Quarterly: "Quartalsweise", - "Month Compressed": "Monat kompakt", - "Month Short": "Monat kurz", - "Month Expanded": "Monat erweitert", - "Week Compressed": "Woche kompakt", - "Week Expanded": "Woche erweitert" - }; - const __vite_glob_0_24$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1b - }, Symbol.toStringTag, { value: "Module" })); - const modules$3 = /* @__PURE__ */ Object.assign({ - "../../../atoms/interactive/range-timeline/i18n/de.ts": __vite_glob_0_0$3, - "../../../cards/action/i18n/de.ts": __vite_glob_0_1$3, - "../../../cards/history/history-chart/i18n/de.ts": __vite_glob_0_2$3, - "../../../cards/history/i18n/de.ts": __vite_glob_0_3$3, - "../../../cards/list/i18n/de.ts": __vite_glob_0_4$3, - "../../../cards/quick/i18n/de.ts": __vite_glob_0_5$3, - "../../../cards/sensor/i18n/de.ts": __vite_glob_0_6$3, - "../../chart/i18n/de.ts": __vite_glob_0_7$3, - "../../ha/i18n/de.ts": __vite_glob_0_8$3, - "../../timeline/i18n/de.ts": __vite_glob_0_9$3, - "../../../molecules/analysis-anomaly-group/i18n/de.ts": __vite_glob_0_10$3, - "../../../molecules/analysis-delta-group/i18n/de.ts": __vite_glob_0_11$3, - "../../../molecules/analysis-rate-group/i18n/de.ts": __vite_glob_0_12$3, - "../../../molecules/analysis-sample-group/i18n/de.ts": __vite_glob_0_13$3, - "../../../molecules/analysis-summary-group/i18n/de.ts": __vite_glob_0_14$3, - "../../../molecules/analysis-threshold-group/i18n/de.ts": __vite_glob_0_15$3, - "../../../molecules/analysis-trend-group/i18n/de.ts": __vite_glob_0_16$3, - "../../../molecules/comparison-tab-rail/i18n/de.ts": __vite_glob_0_17$3, - "../../../molecules/date-window-dialog/i18n/de.ts": __vite_glob_0_18$3, - "../../../molecules/sidebar-options/sections/i18n/de.ts": __vite_glob_0_19$3, - "../../../molecules/target-row-list/i18n/de.ts": __vite_glob_0_20$3, - "../../../molecules/target-row/i18n/de.ts": __vite_glob_0_21$3, - "../../../panels/datapoints/components/history-targets/i18n/de.ts": __vite_glob_0_22$3, - "../../../panels/datapoints/components/panel-shell/i18n/de.ts": __vite_glob_0_23$3, - "../../../panels/datapoints/components/range-toolbar/i18n/de.ts": __vite_glob_0_24$3 - }); - const merged$3 = {}; - for (const mod of Object.values(modules$3)) { - Object.assign(merged$3, mod.translations); - } - const templates$3 = merged$3; - const de = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - templates: templates$3 - }, Symbol.toStringTag, { value: "Module" })); - const translations$1a = { - "Updates with new data": "Se actualiza con datos nuevos", - "Scroll to selected range": "Desplazarse al rango seleccionado", - "Start date and time": "Fecha y hora de inicio", - "End date and time": "Fecha y hora de fin", - Select: "Seleccionar" - }; - const __vite_glob_0_0$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1a - }, Symbol.toStringTag, { value: "Module" })); - const translations$19 = { - General: "General", - "Related items": "Elementos relacionados", - "Datapoint Appearance": "Apariencia del punto de datos", - "Form fields": "Campos del formulario", - "Card title (optional)": "Título de la tarjeta (opcional)", - "Pre-fill entities, devices, areas or labels that are always linked to recordings from this card.": "Rellena previamente entidades, dispositivos, áreas o etiquetas que siempre estarán vinculados a los registros creados con esta tarjeta.", - "Show always included targets on card": "Mostrar en la tarjeta los objetivos siempre incluidos", - "Allow user to add more related items": "Permitir que el usuario añada más elementos relacionados", - "Default icon": "Icono predeterminado", - "Default colour": "Color predeterminado", - "Show date & time field": "Mostrar campo de fecha y hora", - "Show annotation field": "Mostrar campo de anotación" - }; - const __vite_glob_0_1$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$19 - }, Symbol.toStringTag, { value: "Module" })); - const translations$18 = { - "Date window:": "Ventana de fechas:", - "Actual:": "Real:" - }; - const __vite_glob_0_2$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$18 - }, Symbol.toStringTag, { value: "Module" })); - const translations$17 = { - General: "General", - Entity: "Entidad", - "Multiple entities": "Varias entidades", - Display: "Visualización", - "Card title (optional)": "Título de la tarjeta (opcional)", - "Hours to show": "Horas a mostrar", - "Single entity": "Entidad única", - "Show data gaps": "Mostrar huecos de datos", - "Highlight missing data ranges with dashed lines and boundary markers": "Resaltar los rangos de datos faltantes con líneas discontinuas y marcadores de límite" - }; - const __vite_glob_0_3$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$17 - }, Symbol.toStringTag, { value: "Module" })); - const translations$16 = { - "Search datapoints…": "Buscar puntos de datos…", - "Delete record": "Eliminar registro", - Delete: "Eliminar", - "Show annotation": "Mostrar anotación", - "Open related data point history": "Abrir el historial del punto de datos relacionado", - "Edit record": "Editar registro", - "Show chart marker": "Mostrar marcador del gráfico", - "Hide chart marker": "Ocultar marcador del gráfico", - "Choose colour": "Elegir color", - Save: "Guardar", - Cancel: "Cancelar", - Message: "Mensaje", - "Annotation / full message": "Anotación / mensaje completo" - }; - const __vite_glob_0_4$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$16 - }, Symbol.toStringTag, { value: "Module" })); - const translations$15 = { - General: "General", - "Icon & colour": "Icono y color", - "Related items": "Elementos relacionados", - "Multiple entities": "Varias entidades", - "Form fields": "Campos del formulario", - "Card title (optional)": "Título de la tarjeta (opcional)", - "Input placeholder text": "Texto de marcador de posición", - Icon: "Icono", - Colour: "Color", - "These items will be linked to every record made with this card.": "Estos elementos se vincularán a cada registro creado con esta tarjeta.", - "Single entity (optional)": "Entidad única (opcional)", - "Add related items": "Añadir elementos relacionados", - "Show annotation field": "Mostrar campo de anotación" - }; - const __vite_glob_0_5$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$15 - }, Symbol.toStringTag, { value: "Module" })); - const translations$14 = { - Entity: "Entidad", - Display: "Visualización", - "Records list": "Lista de registros", - "Sensor entity *": "Entidad de sensor *", - "Override display name (optional)": "Sobrescribir nombre visible (opcional)", - "Hours to show": "Horas a mostrar", - "Graph colour": "Color del gráfico", - "Annotation style": "Estilo de anotación", - "Circle on line": "Círculo sobre la línea", - "Dotted vertical line": "Línea vertical punteada", - "Show records list below graph": "Mostrar lista de registros debajo del gráfico", - "Records per page (blank = show all)": "Registros por página (en blanco = mostrar todos)", - "Max records to show (blank = all)": "Máximo de registros a mostrar (en blanco = todos)", - "Show full message": "Mostrar mensaje completo", - "User will be able to expand the row if hidden": "El usuario podrá ampliar la fila si está oculta" - }; - const __vite_glob_0_6$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$14 - }, Symbol.toStringTag, { value: "Module" })); - const translations$13 = { - "⚠️ Anomaly Insight": "⚠️ Información de anomalía", - "⚠️ Multi-method Anomaly": "⚠️ Anomalía multimétodo", - "Click the highlighted circle to add an annotation.": "Haz clic en el círculo resaltado para añadir una anotación.", - "Alert:": "Alerta:", - "Confirmed by": "Confirmado por", - "methods:": "métodos:", - "Trend deviation": "Desviación de tendencia", - "Sudden change": "Cambio repentino", - "Statistical outlier (IQR)": "Valor atípico estadístico (IQR)", - "Rolling Z-score": "Puntuación Z móvil", - "Flat-line / stuck": "Plano / atascado", - "Comparison window": "Ventana de comparación", - "{0} deviates from its expected trend between {1} and {2}.": "{0} se desvía de su tendencia esperada entre {1} y {2}.", - "{0} shows an unusual rate of change between {1} and {2}.": "{0} muestra una tasa de cambio inusual entre {1} y {2}.", - "{0} contains statistical outliers between {1} and {2}.": "{0} contiene valores atípicos estadísticos entre {1} y {2}.", - "{0} shows statistically unusual values between {1} and {2}.": "{0} muestra valores estadísticamente inusuales entre {1} y {2}.", - "{0} appears stuck or flat between {1} and {2}{3}.": "{0} parece atascado o plano entre {1} y {2}{3}.", - "{0} deviates significantly from the comparison window between {1} and {2}.": "{0} se desvía significativamente de la ventana de comparación entre {1} y {2}.", - "Peak deviation: {0} from a baseline of {1} at {2}.": "Desviación máxima: {0} respecto a una base de {1} en {2}.", - "Peak rate deviation: {0} from a typical rate of {1} at {2}.": "Desviación máxima de la tasa: {0} respecto a una tasa típica de {1} en {2}.", - "Peak value: {0}, deviating {1} from the median at {2}.": "Valor máximo: {0}, con una desviación de {1} respecto a la mediana en {2}.", - "Peak deviation: {0} from a rolling mean of {1} at {2}.": "Desviación máxima: {0} respecto a una media móvil de {1} en {2}.", - "Value remained near {0} for an unusually long period.": "El valor permaneció cerca de {0} durante un período inusualmente largo.", - "Peak deviation from comparison: {0} at {1}.": "Desviación máxima respecto a la comparación: {0} en {1}.", - " (range: {0})": " (rango: {0})", - "Date window": "Ventana de fechas", - Trend: "Tendencia", - Rate: "Tasa", - Delta: "Delta", - Threshold: "Umbral" - }; - const __vite_glob_0_7$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$13 - }, Symbol.toStringTag, { value: "Module" })); - const translations$12 = { - "Confirm delete": "Confirmar eliminación", - "Are you sure you want to delete this item?": "¿Seguro que quieres eliminar este elemento?", - Cancel: "Cancelar", - Delete: "Eliminar", - "Delete date window": "Eliminar ventana de fechas", - "this date window": "esta ventana de fechas", - "Edit date window": "Editar ventana de fechas", - "Add date window": "Añadir ventana de fechas", - "Save date window": "Guardar ventana de fechas", - "Create date window": "Crear ventana de fechas" - }; - const __vite_glob_0_8$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$12 - }, Symbol.toStringTag, { value: "Module" })); - const translations$11 = { - Wk: "Sem.", - "Week of": "Semana del" - }; - const __vite_glob_0_9$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$11 - }, Symbol.toStringTag, { value: "Module" })); - const translations$10 = { - "Show anomalies": "Mostrar anomalías", - Sensitivity: "Sensibilidad", - "Use downsampled data for detection": "Usar datos submuestreados para la detección", - "Rate window": "Ventana de tasa", - "Rolling window": "Ventana móvil", - "Min flat duration": "Duración mínima plana", - "Compare to window": "Comparar con la ventana", - "— select window —": "— seleccionar ventana —", - "When methods overlap": "Cuando los métodos se superponen", - Low: "Baja", - Medium: "Media", - High: "Alta", - "Trend deviation": "Desviación de tendencia", - "Sudden change": "Cambio repentino", - "Statistical outlier (IQR)": "Valor atípico estadístico (IQR)", - "Rolling Z-score": "Puntuación Z móvil", - "Flat-line / stuck value": "Valor plano / atascado", - "Comparison window deviation": "Desviación respecto a la ventana de comparación", - "Flags points that deviate significantly from a fitted trend line. Good for catching gradual drift or sudden jumps away from a steady baseline.": "Marca puntos que se desvían significativamente de una línea de tendencia ajustada. Es útil para detectar una deriva gradual o saltos bruscos desde una base estable.", - "Flags unusually fast rises or drops compared to the typical rate of change. Best for detecting spikes, crashes, or rapid transitions.": "Marca subidas o bajadas inusualmente rápidas en comparación con la tasa de cambio típica. Es ideal para detectar picos, caídas o transiciones rápidas.", - "Uses the interquartile range to flag values far outside the normal spread of data. Robust against outliers that skew averages.": "Usa el rango intercuartílico para marcar valores muy alejados de la dispersión normal de los datos. Es robusto frente a valores atípicos que sesgan las medias.", - "Compares each value to a rolling mean and standard deviation. Catches unusual readings relative to recent context rather than the whole series.": "Compara cada valor con una media móvil y una desviación estándar. Detecta lecturas inusuales en relación con el contexto reciente, no con toda la serie.", - "Flags when a sensor reports nearly the same value for an unusually long time. Useful for detecting stuck sensors or frozen readings.": "Marca cuando un sensor informa casi el mismo valor durante un tiempo inusualmente largo. Es útil para detectar sensores atascados o lecturas congeladas.", - "Compares the current period to a reference date window. Highlights differences from an expected historical pattern, such as last week or the same day last year.": "Compara el periodo actual con una ventana de fechas de referencia. Destaca las diferencias respecto a un patrón histórico esperado, como la semana pasada o el mismo día del año anterior.", - "Show all anomalies": "Mostrar todas las anomalías", - "Overlaps only": "Solo solapamientos", - "Computing…": "Calculando…", - "1 hour": "1 hora", - "3 hours": "3 horas", - "6 hours": "6 horas", - "12 hours": "12 horas", - "24 hours": "24 horas", - "7 days": "7 días", - "30 minutes": "30 minutos" - }; - const __vite_glob_0_10$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$10 - }, Symbol.toStringTag, { value: "Module" })); - const translations$$ = { - "Show delta vs selected date window": "Mostrar delta frente a la ventana de fechas seleccionada", - "Select a date window tab to enable delta analysis.": "Selecciona una pestaña de ventana de fechas para habilitar el análisis delta.", - "Show delta in tooltip": "Mostrar delta en la información sobre herramientas", - "Show delta lines": "Mostrar líneas delta" - }; - const __vite_glob_0_11$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$$ - }, Symbol.toStringTag, { value: "Module" })); - const translations$_ = { - "Show rate of change": "Mostrar tasa de cambio", - "Show rate of change crosshairs": "Mostrar guías de la tasa de cambio", - "Rate window": "Ventana de tasa", - "Point to point": "Punto a punto", - "1 hour": "1 hora", - "6 hours": "6 horas", - "24 hours": "24 horas" - }; - const __vite_glob_0_12$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$_ - }, Symbol.toStringTag, { value: "Module" })); - const translations$Z = { - Downsampling: "Submuestreo", - Interval: "Intervalo", - Aggregate: "Agregado", - "Raw (no sampling)": "Sin procesar (sin muestreo)", - "5 seconds": "5 segundos", - "10 seconds": "10 segundos", - "15 seconds": "15 segundos", - "30 seconds": "30 segundos", - "1 minute": "1 minuto", - "2 minutes": "2 minutos", - "5 minutes": "5 minutos", - "10 minutes": "10 minutos", - "15 minutes": "15 minutos", - "30 minutes": "30 minutos", - "1 hour": "1 hora", - "2 hours": "2 horas", - "3 hours": "3 horas", - "4 hours": "4 horas", - "6 hours": "6 horas", - "12 hours": "12 horas", - "24 hours": "24 horas", - "Mean (average)": "Media", - Min: "Mín.", - Max: "Máx.", - Median: "Mediana", - First: "Primero", - Last: "Último" - }; - const __vite_glob_0_13$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$Z - }, Symbol.toStringTag, { value: "Module" })); - const translations$Y = { - "Show min / max / mean": "Mostrar mín. / máx. / media", - "Show range shading": "Mostrar sombreado del rango" - }; - const __vite_glob_0_14$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$Y - }, Symbol.toStringTag, { value: "Module" })); - const translations$X = { - "Show threshold analysis": "Mostrar análisis de umbral", - "Shade threshold area": "Sombrear área del umbral", - Threshold: "Umbral", - "Shade area": "Sombrear área", - "Shade above": "Sombrear arriba", - "Shade below": "Sombrear abajo" - }; - const __vite_glob_0_15$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$X - }, Symbol.toStringTag, { value: "Module" })); - const translations$W = { - "Show trend lines": "Mostrar líneas de tendencia", - "Show trend crosshairs": "Mostrar guías de tendencia", - "Trend method": "Método de tendencia", - "Trend window": "Ventana de tendencia", - "Rolling average": "Media móvil", - "Linear trend": "Tendencia lineal", - "1 hour": "1 hora", - "6 hours": "6 horas", - "24 hours": "24 horas", - "7 days": "7 días", - "14 days": "14 días", - "21 days": "21 días", - "28 days": "28 días" - }; - const __vite_glob_0_16$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$W - }, Symbol.toStringTag, { value: "Module" })); - const translations$V = { - "Add date window": "Añadir ventana de fechas" - }; - const __vite_glob_0_17$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$V - }, Symbol.toStringTag, { value: "Module" })); - const translations$U = { - "A date window saves a named date range as a tab, so you can quickly preview it against the selected range or jump the chart back to it later.": "Una ventana de fechas guarda un rango de fechas con nombre como pestaña, para que puedas previsualizarlo rápidamente frente al rango seleccionado o volver más tarde a él en el gráfico.", - Name: "Nombre", - "e.g. Heating season start": "p. ej., inicio de la temporada de calefacción", - "Date range": "Rango de fechas", - Start: "Inicio", - End: "Fin", - "Use previous range": "Usar rango anterior", - "Use next range": "Usar rango siguiente", - "Delete date window": "Eliminar ventana de fechas", - Cancel: "Cancelar" - }; - const __vite_glob_0_18$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$U - }, Symbol.toStringTag, { value: "Module" })); - const translations$T = { - Datapoints: "Puntos de datos", - "Choose which annotation datapoints appear on the chart.": "Elige qué puntos de datos de anotación aparecen en el gráfico.", - "Linked to selected targets": "Vinculados a los objetivos seleccionados", - "All datapoints": "Todos los puntos de datos", - "Hide datapoints": "Ocultar puntos de datos" - }; - const __vite_glob_0_19$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$T - }, Symbol.toStringTag, { value: "Module" })); - const translations$S = {}; - const __vite_glob_0_20$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$S - }, Symbol.toStringTag, { value: "Module" })); - const translations$R = { - "Analysis configured": "Análisis configurado", - "Configure analysis": "Configurar análisis", - "Stepped series": "Serie escalonada", - "Hide source series": "Ocultar serie de origen", - "All targets already have the same settings": "Todos los objetivos ya tienen la misma configuración", - "Copy these analysis settings to all targets": "Copiar estos ajustes de análisis a todos los objetivos", - "Copy to all targets": "Copiar a todos los objetivos" - }; - const __vite_glob_0_21$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$R - }, Symbol.toStringTag, { value: "Module" })); - const translations$Q = { - Targets: "Objetivos", - "Each row controls one chart series.": "Cada fila controla una serie del gráfico.", - "Add target": "Añadir objetivo", - "Chart preferences": "Preferencias del gráfico" - }; - const __vite_glob_0_22$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$Q - }, Symbol.toStringTag, { value: "Module" })); - const translations$P = { - "Loading Datapoints…": "Cargando puntos de datos…", - Datapoints: "Puntos de datos", - "Page options": "Opciones de la página", - "Download spreadsheet": "Descargar hoja de cálculo", - "Save page state": "Guardar estado de la página", - "Restore saved page": "Restaurar página guardada", - "Clear saved page": "Borrar página guardada", - "Expand targets sidebar": "Expandir la barra lateral de objetivos", - "Collapse targets sidebar": "Contraer la barra lateral de objetivos" - }; - const __vite_glob_0_23$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$P - }, Symbol.toStringTag, { value: "Module" })); - const translations$O = { - "Toggle sidebar": "Alternar barra lateral", - Start: "Inicio", - End: "Fin", - "Select date range": "Seleccionar rango de fechas", - "Timeline options": "Opciones de la línea temporal", - "Zoom level": "Nivel de zoom", - "Date snapping": "Ajuste de fechas", - Auto: "Auto", - Hour: "Hora", - Day: "Día", - Week: "Semana", - Month: "Mes", - Minute: "Minuto", - Second: "Segundo", - Quarterly: "Trimestral", - "Month Compressed": "Mes comprimido", - "Month Short": "Mes corto", - "Month Expanded": "Mes expandido", - "Week Compressed": "Semana comprimida", - "Week Expanded": "Semana expandida" - }; - const __vite_glob_0_24$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$O - }, Symbol.toStringTag, { value: "Module" })); - const modules$2 = /* @__PURE__ */ Object.assign({ - "../../../atoms/interactive/range-timeline/i18n/es.ts": __vite_glob_0_0$2, - "../../../cards/action/i18n/es.ts": __vite_glob_0_1$2, - "../../../cards/history/history-chart/i18n/es.ts": __vite_glob_0_2$2, - "../../../cards/history/i18n/es.ts": __vite_glob_0_3$2, - "../../../cards/list/i18n/es.ts": __vite_glob_0_4$2, - "../../../cards/quick/i18n/es.ts": __vite_glob_0_5$2, - "../../../cards/sensor/i18n/es.ts": __vite_glob_0_6$2, - "../../chart/i18n/es.ts": __vite_glob_0_7$2, - "../../ha/i18n/es.ts": __vite_glob_0_8$2, - "../../timeline/i18n/es.ts": __vite_glob_0_9$2, - "../../../molecules/analysis-anomaly-group/i18n/es.ts": __vite_glob_0_10$2, - "../../../molecules/analysis-delta-group/i18n/es.ts": __vite_glob_0_11$2, - "../../../molecules/analysis-rate-group/i18n/es.ts": __vite_glob_0_12$2, - "../../../molecules/analysis-sample-group/i18n/es.ts": __vite_glob_0_13$2, - "../../../molecules/analysis-summary-group/i18n/es.ts": __vite_glob_0_14$2, - "../../../molecules/analysis-threshold-group/i18n/es.ts": __vite_glob_0_15$2, - "../../../molecules/analysis-trend-group/i18n/es.ts": __vite_glob_0_16$2, - "../../../molecules/comparison-tab-rail/i18n/es.ts": __vite_glob_0_17$2, - "../../../molecules/date-window-dialog/i18n/es.ts": __vite_glob_0_18$2, - "../../../molecules/sidebar-options/sections/i18n/es.ts": __vite_glob_0_19$2, - "../../../molecules/target-row-list/i18n/es.ts": __vite_glob_0_20$2, - "../../../molecules/target-row/i18n/es.ts": __vite_glob_0_21$2, - "../../../panels/datapoints/components/history-targets/i18n/es.ts": __vite_glob_0_22$2, - "../../../panels/datapoints/components/panel-shell/i18n/es.ts": __vite_glob_0_23$2, - "../../../panels/datapoints/components/range-toolbar/i18n/es.ts": __vite_glob_0_24$2 - }); - const merged$2 = {}; - for (const mod of Object.values(modules$2)) { - Object.assign(merged$2, mod.translations); - } - const templates$2 = merged$2; - const es = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - templates: templates$2 - }, Symbol.toStringTag, { value: "Module" })); - const translations$N = { - "Updates with new data": "Atualiza com novos dados", - "Scroll to selected range": "Deslocar para o intervalo selecionado", - "Start date and time": "Data e hora de início", - "End date and time": "Data e hora de fim", - Select: "Selecionar" - }; - const __vite_glob_0_0$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$N - }, Symbol.toStringTag, { value: "Module" })); - const translations$M = { - General: "Geral", - "Related items": "Itens relacionados", - "Datapoint Appearance": "Aspeto do ponto de dados", - "Form fields": "Campos do formulário", - "Card title (optional)": "Título do cartão (opcional)", - "Pre-fill entities, devices, areas or labels that are always linked to recordings from this card.": "Pré-preencha entidades, dispositivos, áreas ou etiquetas que estejam sempre ligados aos registos feitos com este cartão.", - "Show always included targets on card": "Mostrar no cartão os alvos sempre incluídos", - "Allow user to add more related items": "Permitir que o utilizador adicione mais itens relacionados", - "Default icon": "Ícone predefinido", - "Default colour": "Cor predefinida", - "Show date & time field": "Mostrar campo de data e hora", - "Show annotation field": "Mostrar campo de anotação" - }; - const __vite_glob_0_1$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$M - }, Symbol.toStringTag, { value: "Module" })); - const translations$L = { - "Date window:": "Janela de datas:", - "Actual:": "Real:" - }; - const __vite_glob_0_2$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$L - }, Symbol.toStringTag, { value: "Module" })); - const translations$K = { - General: "Geral", - Entity: "Entidade", - "Multiple entities": "Múltiplas entidades", - Display: "Visualização", - "Card title (optional)": "Título do cartão (opcional)", - "Hours to show": "Horas a mostrar", - "Single entity": "Entidade única", - "Show data gaps": "Mostrar lacunas de dados", - "Highlight missing data ranges with dashed lines and boundary markers": "Realçar intervalos de dados em falta com linhas tracejadas e marcadores de limite" - }; - const __vite_glob_0_3$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$K - }, Symbol.toStringTag, { value: "Module" })); - const translations$J = { - "Search datapoints…": "Pesquisar pontos de dados…", - "Delete record": "Eliminar registo", - Delete: "Eliminar", - "Show annotation": "Mostrar anotação", - "Open related data point history": "Abrir histórico do ponto de dados relacionado", - "Edit record": "Editar registo", - "Show chart marker": "Mostrar marcador do gráfico", - "Hide chart marker": "Ocultar marcador do gráfico", - "Choose colour": "Escolher cor", - Save: "Guardar", - Cancel: "Cancelar", - Message: "Mensagem", - "Annotation / full message": "Anotação / mensagem completa" - }; - const __vite_glob_0_4$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$J - }, Symbol.toStringTag, { value: "Module" })); - const translations$I = { - General: "Geral", - "Icon & colour": "Ícone e cor", - "Related items": "Itens relacionados", - "Multiple entities": "Múltiplas entidades", - "Form fields": "Campos do formulário", - "Card title (optional)": "Título do cartão (opcional)", - "Input placeholder text": "Texto do marcador de posição", - Icon: "Ícone", - Colour: "Cor", - "These items will be linked to every record made with this card.": "Estes itens serão ligados a cada registo feito com este cartão.", - "Single entity (optional)": "Entidade única (opcional)", - "Add related items": "Adicionar itens relacionados", - "Show annotation field": "Mostrar campo de anotação" - }; - const __vite_glob_0_5$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$I - }, Symbol.toStringTag, { value: "Module" })); - const translations$H = { - Entity: "Entidade", - Display: "Visualização", - "Records list": "Lista de registos", - "Sensor entity *": "Entidade do sensor *", - "Override display name (optional)": "Substituir nome apresentado (opcional)", - "Hours to show": "Horas a mostrar", - "Graph colour": "Cor do gráfico", - "Annotation style": "Estilo da anotação", - "Circle on line": "Círculo na linha", - "Dotted vertical line": "Linha vertical pontilhada", - "Show records list below graph": "Mostrar lista de registos abaixo do gráfico", - "Records per page (blank = show all)": "Registos por página (em branco = mostrar todos)", - "Max records to show (blank = all)": "Máximo de registos a mostrar (em branco = todos)", - "Show full message": "Mostrar mensagem completa", - "User will be able to expand the row if hidden": "O utilizador poderá expandir a linha se estiver oculta" - }; - const __vite_glob_0_6$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$H - }, Symbol.toStringTag, { value: "Module" })); - const translations$G = { - "⚠️ Anomaly Insight": "⚠️ Informação de anomalia", - "⚠️ Multi-method Anomaly": "⚠️ Anomalia multimétodo", - "Click the highlighted circle to add an annotation.": "Clique no círculo destacado para adicionar uma anotação.", - "Alert:": "Alerta:", - "Confirmed by": "Confirmado por", - "methods:": "métodos:", - "Trend deviation": "Desvio de tendência", - "Sudden change": "Mudança súbita", - "Statistical outlier (IQR)": "Valor atípico estatístico (IQR)", - "Rolling Z-score": "Z-score móvel", - "Flat-line / stuck": "Plano / bloqueado", - "Comparison window": "Janela de comparação", - "{0} deviates from its expected trend between {1} and {2}.": "{0} desvia-se da tendência esperada entre {1} e {2}.", - "{0} shows an unusual rate of change between {1} and {2}.": "{0} mostra uma taxa de variação invulgar entre {1} e {2}.", - "{0} contains statistical outliers between {1} and {2}.": "{0} contém valores atípicos estatísticos entre {1} e {2}.", - "{0} shows statistically unusual values between {1} and {2}.": "{0} mostra valores estatisticamente invulgares entre {1} e {2}.", - "{0} appears stuck or flat between {1} and {2}{3}.": "{0} parece bloqueado ou plano entre {1} e {2}{3}.", - "{0} deviates significantly from the comparison window between {1} and {2}.": "{0} desvia-se significativamente da janela de comparação entre {1} e {2}.", - "Peak deviation: {0} from a baseline of {1} at {2}.": "Desvio máximo: {0} em relação a uma base de {1} em {2}.", - "Peak rate deviation: {0} from a typical rate of {1} at {2}.": "Desvio máximo da taxa: {0} em relação a uma taxa típica de {1} em {2}.", - "Peak value: {0}, deviating {1} from the median at {2}.": "Valor máximo: {0}, desviando-se {1} da mediana em {2}.", - "Peak deviation: {0} from a rolling mean of {1} at {2}.": "Desvio máximo: {0} em relação a uma média móvel de {1} em {2}.", - "Value remained near {0} for an unusually long period.": "O valor manteve-se perto de {0} durante um período invulgarmente longo.", - "Peak deviation from comparison: {0} at {1}.": "Desvio máximo da comparação: {0} em {1}.", - " (range: {0})": " (intervalo: {0})", - "Date window": "Janela de datas", - Trend: "Tendência", - Rate: "Taxa", - Delta: "Delta", - Threshold: "Limiar" - }; - const __vite_glob_0_7$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$G - }, Symbol.toStringTag, { value: "Module" })); - const translations$F = { - "Confirm delete": "Confirmar eliminação", - "Are you sure you want to delete this item?": "Tem a certeza de que pretende eliminar este item?", - Cancel: "Cancelar", - Delete: "Eliminar", - "Delete date window": "Eliminar janela de datas", - "this date window": "esta janela de datas", - "Edit date window": "Editar janela de datas", - "Add date window": "Adicionar janela de datas", - "Save date window": "Guardar janela de datas", - "Create date window": "Criar janela de datas" - }; - const __vite_glob_0_8$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$F - }, Symbol.toStringTag, { value: "Module" })); - const translations$E = { - Wk: "Sem.", - "Week of": "Semana de" - }; - const __vite_glob_0_9$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$E - }, Symbol.toStringTag, { value: "Module" })); - const translations$D = { - "Show anomalies": "Mostrar anomalias", - Sensitivity: "Sensibilidade", - "Use downsampled data for detection": "Usar dados reamostrados para deteção", - "Rate window": "Janela da taxa", - "Rolling window": "Janela móvel", - "Min flat duration": "Duração mínima plana", - "Compare to window": "Comparar com a janela", - "— select window —": "— selecionar janela —", - "When methods overlap": "Quando os métodos se sobrepõem", - Low: "Baixa", - Medium: "Média", - High: "Alta", - "Trend deviation": "Desvio de tendência", - "Sudden change": "Mudança súbita", - "Statistical outlier (IQR)": "Valor atípico estatístico (IQR)", - "Rolling Z-score": "Z-score móvel", - "Flat-line / stuck value": "Valor plano / bloqueado", - "Comparison window deviation": "Desvio da janela de comparação", - "Flags points that deviate significantly from a fitted trend line. Good for catching gradual drift or sudden jumps away from a steady baseline.": "Assinala pontos que se desviam significativamente de uma linha de tendência ajustada. É útil para detetar deriva gradual ou saltos súbitos a partir de uma base estável.", - "Flags unusually fast rises or drops compared to the typical rate of change. Best for detecting spikes, crashes, or rapid transitions.": "Assinala subidas ou descidas invulgarmente rápidas em comparação com a taxa de variação típica. É ideal para detetar picos, quedas ou transições rápidas.", - "Uses the interquartile range to flag values far outside the normal spread of data. Robust against outliers that skew averages.": "Usa o intervalo interquartil para assinalar valores muito fora da dispersão normal dos dados. É robusto contra valores atípicos que distorcem as médias.", - "Compares each value to a rolling mean and standard deviation. Catches unusual readings relative to recent context rather than the whole series.": "Compara cada valor com uma média móvel e um desvio padrão. Deteta leituras invulgares em relação ao contexto recente, e não à série completa.", - "Flags when a sensor reports nearly the same value for an unusually long time. Useful for detecting stuck sensors or frozen readings.": "Assinala quando um sensor reporta quase o mesmo valor durante um período invulgarmente longo. É útil para detetar sensores bloqueados ou leituras congeladas.", - "Compares the current period to a reference date window. Highlights differences from an expected historical pattern, such as last week or the same day last year.": "Compara o período atual com uma janela de datas de referência. Destaca diferenças face a um padrão histórico esperado, como a semana passada ou o mesmo dia do ano anterior.", - "Show all anomalies": "Mostrar todas as anomalias", - "Overlaps only": "Apenas sobreposições", - "Computing…": "A calcular…", - "1 hour": "1 hora", - "3 hours": "3 horas", - "6 hours": "6 horas", - "12 hours": "12 horas", - "24 hours": "24 horas", - "7 days": "7 dias", - "30 minutes": "30 minutos" - }; - const __vite_glob_0_10$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$D - }, Symbol.toStringTag, { value: "Module" })); - const translations$C = { - "Show delta vs selected date window": "Mostrar delta face à janela de datas selecionada", - "Select a date window tab to enable delta analysis.": "Selecione um separador de janela de datas para ativar a análise delta.", - "Show delta in tooltip": "Mostrar delta na dica", - "Show delta lines": "Mostrar linhas delta" - }; - const __vite_glob_0_11$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$C - }, Symbol.toStringTag, { value: "Module" })); - const translations$B = { - "Show rate of change": "Mostrar taxa de variação", - "Show rate of change crosshairs": "Mostrar guias da taxa de variação", - "Rate window": "Janela da taxa", - "Point to point": "Ponto a ponto", - "1 hour": "1 hora", - "6 hours": "6 horas", - "24 hours": "24 horas" - }; - const __vite_glob_0_12$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$B - }, Symbol.toStringTag, { value: "Module" })); - const translations$A = { - Downsampling: "Reamostragem", - Interval: "Intervalo", - Aggregate: "Agregado", - "Raw (no sampling)": "Em bruto (sem amostragem)", - "5 seconds": "5 segundos", - "10 seconds": "10 segundos", - "15 seconds": "15 segundos", - "30 seconds": "30 segundos", - "1 minute": "1 minuto", - "2 minutes": "2 minutos", - "5 minutes": "5 minutos", - "10 minutes": "10 minutos", - "15 minutes": "15 minutos", - "30 minutes": "30 minutos", - "1 hour": "1 hora", - "2 hours": "2 horas", - "3 hours": "3 horas", - "4 hours": "4 horas", - "6 hours": "6 horas", - "12 hours": "12 horas", - "24 hours": "24 horas", - "Mean (average)": "Média", - Min: "Mín.", - Max: "Máx.", - Median: "Mediana", - First: "Primeiro", - Last: "Último" - }; - const __vite_glob_0_13$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$A - }, Symbol.toStringTag, { value: "Module" })); - const translations$z = { - "Show min / max / mean": "Mostrar mín. / máx. / média", - "Show range shading": "Mostrar sombreamento do intervalo" - }; - const __vite_glob_0_14$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$z - }, Symbol.toStringTag, { value: "Module" })); - const translations$y = { - "Show threshold analysis": "Mostrar análise de limiar", - "Shade threshold area": "Sombrear área do limiar", - Threshold: "Limiar", - "Shade area": "Sombrear área", - "Shade above": "Sombrear acima", - "Shade below": "Sombrear abaixo" - }; - const __vite_glob_0_15$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$y - }, Symbol.toStringTag, { value: "Module" })); - const translations$x = { - "Show trend lines": "Mostrar linhas de tendência", - "Show trend crosshairs": "Mostrar guias da tendência", - "Trend method": "Método de tendência", - "Trend window": "Janela de tendência", - "Rolling average": "Média móvel", - "Linear trend": "Tendência linear", - "1 hour": "1 hora", - "6 hours": "6 horas", - "24 hours": "24 horas", - "7 days": "7 dias", - "14 days": "14 dias", - "21 days": "21 dias", - "28 days": "28 dias" - }; - const __vite_glob_0_16$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$x - }, Symbol.toStringTag, { value: "Module" })); - const translations$w = { - "Add date window": "Adicionar janela de datas" - }; - const __vite_glob_0_17$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$w - }, Symbol.toStringTag, { value: "Module" })); - const translations$v = { - "A date window saves a named date range as a tab, so you can quickly preview it against the selected range or jump the chart back to it later.": "Uma janela de datas guarda um intervalo de datas com nome como separador, para que possa pré-visualizá-lo rapidamente face ao intervalo selecionado ou regressar mais tarde a ele no gráfico.", - Name: "Nome", - "e.g. Heating season start": "ex.: início da época de aquecimento", - "Date range": "Intervalo de datas", - Start: "Início", - End: "Fim", - "Use previous range": "Usar intervalo anterior", - "Use next range": "Usar intervalo seguinte", - "Delete date window": "Eliminar janela de datas", - Cancel: "Cancelar" - }; - const __vite_glob_0_18$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$v - }, Symbol.toStringTag, { value: "Module" })); - const translations$u = { - Datapoints: "Pontos de dados", - "Choose which annotation datapoints appear on the chart.": "Escolha quais os pontos de dados de anotação que aparecem no gráfico.", - "Linked to selected targets": "Ligados aos alvos selecionados", - "All datapoints": "Todos os pontos de dados", - "Hide datapoints": "Ocultar pontos de dados" - }; - const __vite_glob_0_19$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$u - }, Symbol.toStringTag, { value: "Module" })); - const translations$t = {}; - const __vite_glob_0_20$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$t - }, Symbol.toStringTag, { value: "Module" })); - const translations$s = { - "Analysis configured": "Análise configurada", - "Configure analysis": "Configurar análise", - "Stepped series": "Série em degraus", - "Hide source series": "Ocultar série de origem", - "All targets already have the same settings": "Todos os alvos já têm as mesmas definições", - "Copy these analysis settings to all targets": "Copiar estas definições de análise para todos os alvos", - "Copy to all targets": "Copiar para todos os alvos" - }; - const __vite_glob_0_21$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$s - }, Symbol.toStringTag, { value: "Module" })); - const translations$r = { - Targets: "Alvos", - "Each row controls one chart series.": "Cada linha controla uma série do gráfico.", - "Add target": "Adicionar alvo", - "Chart preferences": "Preferências do gráfico" - }; - const __vite_glob_0_22$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$r - }, Symbol.toStringTag, { value: "Module" })); - const translations$q = { - "Loading Datapoints…": "A carregar pontos de dados…", - Datapoints: "Pontos de dados", - "Page options": "Opções da página", - "Download spreadsheet": "Transferir folha de cálculo", - "Save page state": "Guardar estado da página", - "Restore saved page": "Restaurar página guardada", - "Clear saved page": "Limpar página guardada", - "Expand targets sidebar": "Expandir barra lateral dos alvos", - "Collapse targets sidebar": "Recolher barra lateral dos alvos" - }; - const __vite_glob_0_23$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$q - }, Symbol.toStringTag, { value: "Module" })); - const translations$p = { - "Toggle sidebar": "Alternar barra lateral", - Start: "Início", - End: "Fim", - "Select date range": "Selecionar intervalo de datas", - "Timeline options": "Opções da linha temporal", - "Zoom level": "Nível de zoom", - "Date snapping": "Ajuste de datas", - Auto: "Auto", - Hour: "Hora", - Day: "Dia", - Week: "Semana", - Month: "Mês", - Minute: "Minuto", - Second: "Segundo", - Quarterly: "Trimestral", - "Month Compressed": "Mês comprimido", - "Month Short": "Mês curto", - "Month Expanded": "Mês expandido", - "Week Compressed": "Semana comprimida", - "Week Expanded": "Semana expandida" - }; - const __vite_glob_0_24$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$p - }, Symbol.toStringTag, { value: "Module" })); - const modules$1 = /* @__PURE__ */ Object.assign({ - "../../../atoms/interactive/range-timeline/i18n/pt.ts": __vite_glob_0_0$1, - "../../../cards/action/i18n/pt.ts": __vite_glob_0_1$1, - "../../../cards/history/history-chart/i18n/pt.ts": __vite_glob_0_2$1, - "../../../cards/history/i18n/pt.ts": __vite_glob_0_3$1, - "../../../cards/list/i18n/pt.ts": __vite_glob_0_4$1, - "../../../cards/quick/i18n/pt.ts": __vite_glob_0_5$1, - "../../../cards/sensor/i18n/pt.ts": __vite_glob_0_6$1, - "../../chart/i18n/pt.ts": __vite_glob_0_7$1, - "../../ha/i18n/pt.ts": __vite_glob_0_8$1, - "../../timeline/i18n/pt.ts": __vite_glob_0_9$1, - "../../../molecules/analysis-anomaly-group/i18n/pt.ts": __vite_glob_0_10$1, - "../../../molecules/analysis-delta-group/i18n/pt.ts": __vite_glob_0_11$1, - "../../../molecules/analysis-rate-group/i18n/pt.ts": __vite_glob_0_12$1, - "../../../molecules/analysis-sample-group/i18n/pt.ts": __vite_glob_0_13$1, - "../../../molecules/analysis-summary-group/i18n/pt.ts": __vite_glob_0_14$1, - "../../../molecules/analysis-threshold-group/i18n/pt.ts": __vite_glob_0_15$1, - "../../../molecules/analysis-trend-group/i18n/pt.ts": __vite_glob_0_16$1, - "../../../molecules/comparison-tab-rail/i18n/pt.ts": __vite_glob_0_17$1, - "../../../molecules/date-window-dialog/i18n/pt.ts": __vite_glob_0_18$1, - "../../../molecules/sidebar-options/sections/i18n/pt.ts": __vite_glob_0_19$1, - "../../../molecules/target-row-list/i18n/pt.ts": __vite_glob_0_20$1, - "../../../molecules/target-row/i18n/pt.ts": __vite_glob_0_21$1, - "../../../panels/datapoints/components/history-targets/i18n/pt.ts": __vite_glob_0_22$1, - "../../../panels/datapoints/components/panel-shell/i18n/pt.ts": __vite_glob_0_23$1, - "../../../panels/datapoints/components/range-toolbar/i18n/pt.ts": __vite_glob_0_24$1 - }); - const merged$1 = {}; - for (const mod of Object.values(modules$1)) { - Object.assign(merged$1, mod.translations); - } - const templates$1 = merged$1; - const pt = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - templates: templates$1 - }, Symbol.toStringTag, { value: "Module" })); - const translations$o = { - "Updates with new data": "使用新数据更新", - "Scroll to selected range": "滚动到选定范围", - "Start date and time": "开始日期和时间", - "End date and time": "结束日期和时间", - Select: "选择" - }; - const __vite_glob_0_0 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$o - }, Symbol.toStringTag, { value: "Module" })); - const translations$n = { - General: "常规", - "Related items": "关联项", - "Datapoint Appearance": "数据点外观", - "Form fields": "表单字段", - "Card title (optional)": "卡片标题(可选)", - "Pre-fill entities, devices, areas or labels that are always linked to recordings from this card.": "预先填写始终与此卡片创建的记录关联的实体、设备、区域或标签。", - "Show always included targets on card": "在卡片上显示始终包含的目标", - "Allow user to add more related items": "允许用户添加更多关联项", - "Default icon": "默认图标", - "Default colour": "默认颜色", - "Show date & time field": "显示日期和时间字段", - "Show annotation field": "显示注释字段" - }; - const __vite_glob_0_1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$n - }, Symbol.toStringTag, { value: "Module" })); - const translations$m = { - "Date window:": "日期窗口:", - "Actual:": "实际:" - }; - const __vite_glob_0_2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$m - }, Symbol.toStringTag, { value: "Module" })); - const translations$l = { - General: "常规", - Entity: "实体", - "Multiple entities": "多个实体", - Display: "显示", - "Card title (optional)": "卡片标题(可选)", - "Hours to show": "显示小时数", - "Single entity": "单个实体", - "Show data gaps": "显示数据缺口", - "Highlight missing data ranges with dashed lines and boundary markers": "用虚线和边界标记突出显示缺失数据范围" - }; - const __vite_glob_0_3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$l - }, Symbol.toStringTag, { value: "Module" })); - const translations$k = { - "Search datapoints…": "搜索数据点…", - "Delete record": "删除记录", - Delete: "删除", - "Show annotation": "显示注释", - "Open related data point history": "打开相关数据点历史", - "Edit record": "编辑记录", - "Show chart marker": "显示图表标记", - "Hide chart marker": "隐藏图表标记", - "Choose colour": "选择颜色", - Save: "保存", - Cancel: "取消", - Message: "消息", - "Annotation / full message": "注释 / 完整消息" - }; - const __vite_glob_0_4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$k - }, Symbol.toStringTag, { value: "Module" })); - const translations$j = { - General: "常规", - "Icon & colour": "图标和颜色", - "Related items": "关联项", - "Multiple entities": "多个实体", - "Form fields": "表单字段", - "Card title (optional)": "卡片标题(可选)", - "Input placeholder text": "输入占位文本", - Icon: "图标", - Colour: "颜色", - "These items will be linked to every record made with this card.": "这些项目将关联到使用此卡片创建的每一条记录。", - "Single entity (optional)": "单个实体(可选)", - "Add related items": "添加关联项", - "Show annotation field": "显示注释字段" - }; - const __vite_glob_0_5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$j - }, Symbol.toStringTag, { value: "Module" })); - const translations$i = { - Entity: "实体", - Display: "显示", - "Records list": "记录列表", - "Sensor entity *": "传感器实体 *", - "Override display name (optional)": "覆盖显示名称(可选)", - "Hours to show": "显示小时数", - "Graph colour": "图表颜色", - "Annotation style": "注释样式", - "Circle on line": "线上的圆点", - "Dotted vertical line": "点状垂直线", - "Show records list below graph": "在图表下方显示记录列表", - "Records per page (blank = show all)": "每页记录数(留空 = 显示全部)", - "Max records to show (blank = all)": "显示的最大记录数(留空 = 全部)", - "Show full message": "显示完整消息", - "User will be able to expand the row if hidden": "如果该行被隐藏,用户仍可展开它" - }; - const __vite_glob_0_6 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$i - }, Symbol.toStringTag, { value: "Module" })); - const translations$h = { - "⚠️ Anomaly Insight": "⚠️ 异常洞察", - "⚠️ Multi-method Anomaly": "⚠️ 多方法异常", - "Click the highlighted circle to add an annotation.": "点击高亮圆圈以添加注释。", - "Alert:": "警报:", - "Confirmed by": "确认方式", - "methods:": "方法:", - "Trend deviation": "趋势偏差", - "Sudden change": "突变", - "Statistical outlier (IQR)": "统计离群值(IQR)", - "Rolling Z-score": "滚动 Z 分数", - "Flat-line / stuck": "平直 / 卡住", - "Comparison window": "比较窗口", - "{0} deviates from its expected trend between {1} and {2}.": "{0} 在 {1} 到 {2} 之间偏离了预期趋势。", - "{0} shows an unusual rate of change between {1} and {2}.": "{0} 在 {1} 到 {2} 之间表现出异常的变化率。", - "{0} contains statistical outliers between {1} and {2}.": "{0} 在 {1} 到 {2} 之间包含统计离群值。", - "{0} shows statistically unusual values between {1} and {2}.": "{0} 在 {1} 到 {2} 之间显示出统计上异常的值。", - "{0} appears stuck or flat between {1} and {2}{3}.": "{0} 在 {1} 到 {2}{3} 之间似乎卡住或保持平直。", - "{0} deviates significantly from the comparison window between {1} and {2}.": "{0} 在 {1} 到 {2} 之间显著偏离比较窗口。", - "Peak deviation: {0} from a baseline of {1} at {2}.": "峰值偏差:{2} 时相对基线 {1} 偏差 {0}。", - "Peak rate deviation: {0} from a typical rate of {1} at {2}.": "峰值速率偏差:{2} 时相对典型速率 {1} 偏差 {0}。", - "Peak value: {0}, deviating {1} from the median at {2}.": "峰值:{0},在 {2} 时偏离中位数 {1}。", - "Peak deviation: {0} from a rolling mean of {1} at {2}.": "峰值偏差:{2} 时相对滚动平均值 {1} 偏差 {0}。", - "Value remained near {0} for an unusually long period.": "该值在异常长的时间内一直接近 {0}。", - "Peak deviation from comparison: {0} at {1}.": "与比较值的峰值偏差:{1} 时为 {0}。", - " (range: {0})": "(范围:{0})", - "Date window": "日期窗口", - Trend: "趋势", - Rate: "变化率", - Delta: "差值", - Threshold: "阈值" - }; - const __vite_glob_0_7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$h - }, Symbol.toStringTag, { value: "Module" })); - const translations$g = { - "Confirm delete": "确认删除", - "Are you sure you want to delete this item?": "确定要删除此项目吗?", - Cancel: "取消", - Delete: "删除", - "Delete date window": "删除日期窗口", - "this date window": "此日期窗口", - "Edit date window": "编辑日期窗口", - "Add date window": "添加日期窗口", - "Save date window": "保存日期窗口", - "Create date window": "创建日期窗口" - }; - const __vite_glob_0_8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$g - }, Symbol.toStringTag, { value: "Module" })); - const translations$f = { - Wk: "周", - "Week of": "所在周" - }; - const __vite_glob_0_9 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$f - }, Symbol.toStringTag, { value: "Module" })); - const translations$e = { - "Show anomalies": "显示异常", - Sensitivity: "灵敏度", - "Use downsampled data for detection": "检测时使用降采样数据", - "Rate window": "变化率窗口", - "Rolling window": "滚动窗口", - "Min flat duration": "最小平稳持续时间", - "Compare to window": "与窗口比较", - "— select window —": "— 选择窗口 —", - "When methods overlap": "当方法重叠时", - Low: "低", - Medium: "中", - High: "高", - "Trend deviation": "趋势偏差", - "Sudden change": "突变", - "Statistical outlier (IQR)": "统计离群值(IQR)", - "Rolling Z-score": "滚动 Z 分数", - "Flat-line / stuck value": "平直 / 卡住值", - "Comparison window deviation": "比较窗口偏差", - "Flags points that deviate significantly from a fitted trend line. Good for catching gradual drift or sudden jumps away from a steady baseline.": "标记显著偏离拟合趋势线的点。适合捕捉从稳定基线逐渐漂移或突然跳变的情况。", - "Flags unusually fast rises or drops compared to the typical rate of change. Best for detecting spikes, crashes, or rapid transitions.": "标记相对于典型变化率异常快速的上升或下降。最适合检测尖峰、崩跌或快速变化。", - "Uses the interquartile range to flag values far outside the normal spread of data. Robust against outliers that skew averages.": "使用四分位距来标记远超正常数据分布范围的值。对会扭曲平均值的离群值具有较强鲁棒性。", - "Compares each value to a rolling mean and standard deviation. Catches unusual readings relative to recent context rather than the whole series.": "将每个值与滚动平均值和标准差进行比较。它根据近期上下文而不是整条序列来捕捉异常读数。", - "Flags when a sensor reports nearly the same value for an unusually long time. Useful for detecting stuck sensors or frozen readings.": "当传感器在异常长时间内报告几乎相同的值时进行标记。适用于检测卡住的传感器或冻结的读数。", - "Compares the current period to a reference date window. Highlights differences from an expected historical pattern, such as last week or the same day last year.": "将当前时间段与参考日期窗口进行比较。突出显示与预期历史模式的差异,例如上周或去年的同一天。", - "Show all anomalies": "显示所有异常", - "Overlaps only": "仅重叠项", - "Computing…": "计算中…", - "1 hour": "1小时", - "3 hours": "3小时", - "6 hours": "6小时", - "12 hours": "12小时", - "24 hours": "24小时", - "7 days": "7天", - "30 minutes": "30分钟" - }; - const __vite_glob_0_10 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$e - }, Symbol.toStringTag, { value: "Module" })); - const translations$d = { - "Show delta vs selected date window": "显示相对于所选日期窗口的差值", - "Select a date window tab to enable delta analysis.": "选择一个日期窗口标签以启用差值分析。", - "Show delta in tooltip": "在提示中显示差值", - "Show delta lines": "显示差值线" - }; - const __vite_glob_0_11 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$d - }, Symbol.toStringTag, { value: "Module" })); - const translations$c = { - "Show rate of change": "显示变化率", - "Show rate of change crosshairs": "显示变化率准星", - "Rate window": "变化率窗口", - "Point to point": "点对点", - "1 hour": "1小时", - "6 hours": "6小时", - "24 hours": "24小时" - }; - const __vite_glob_0_12 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$c - }, Symbol.toStringTag, { value: "Module" })); - const translations$b = { - Downsampling: "降采样", - Interval: "间隔", - Aggregate: "聚合", - "Raw (no sampling)": "原始(无采样)", - "5 seconds": "5秒", - "10 seconds": "10秒", - "15 seconds": "15秒", - "30 seconds": "30秒", - "1 minute": "1分钟", - "2 minutes": "2分钟", - "5 minutes": "5分钟", - "10 minutes": "10分钟", - "15 minutes": "15分钟", - "30 minutes": "30分钟", - "1 hour": "1小时", - "2 hours": "2小时", - "3 hours": "3小时", - "4 hours": "4小时", - "6 hours": "6小时", - "12 hours": "12小时", - "24 hours": "24小时", - "Mean (average)": "平均值", - Min: "最小", - Max: "最大", - Median: "中位数", - First: "首个", - Last: "最后一个" - }; - const __vite_glob_0_13 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$b - }, Symbol.toStringTag, { value: "Module" })); - const translations$a = { - "Show min / max / mean": "显示最小 / 最大 / 平均值", - "Show range shading": "显示范围阴影" - }; - const __vite_glob_0_14 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$a - }, Symbol.toStringTag, { value: "Module" })); - const translations$9 = { - "Show threshold analysis": "显示阈值分析", - "Shade threshold area": "为阈值区域着色", - Threshold: "阈值", - "Shade area": "着色区域", - "Shade above": "对上方区域着色", - "Shade below": "对下方区域着色" - }; - const __vite_glob_0_15 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$9 - }, Symbol.toStringTag, { value: "Module" })); - const translations$8 = { - "Show trend lines": "显示趋势线", - "Show trend crosshairs": "显示趋势准星", - "Trend method": "趋势方法", - "Trend window": "趋势窗口", - "Rolling average": "滚动平均", - "Linear trend": "线性趋势", - "1 hour": "1小时", - "6 hours": "6小时", - "24 hours": "24小时", - "7 days": "7天", - "14 days": "14天", - "21 days": "21天", - "28 days": "28天" - }; - const __vite_glob_0_16 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$8 - }, Symbol.toStringTag, { value: "Module" })); - const translations$7 = { - "Add date window": "添加日期窗口" - }; - const __vite_glob_0_17 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$7 - }, Symbol.toStringTag, { value: "Module" })); - const translations$6 = { - "A date window saves a named date range as a tab, so you can quickly preview it against the selected range or jump the chart back to it later.": "日期窗口会将一个命名的日期范围保存为标签页,这样你就可以快速将其与所选范围进行预览对比,或稍后在图表中跳回该范围。", - Name: "名称", - "e.g. Heating season start": "例如:供暖季开始", - "Date range": "日期范围", - Start: "开始", - End: "结束", - "Use previous range": "使用上一个范围", - "Use next range": "使用下一个范围", - "Delete date window": "删除日期窗口", - Cancel: "取消" - }; - const __vite_glob_0_18 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$6 - }, Symbol.toStringTag, { value: "Module" })); - const translations$5 = { - Datapoints: "数据点", - "Choose which annotation datapoints appear on the chart.": "选择哪些注释数据点显示在图表上。", - "Linked to selected targets": "关联到所选目标", - "All datapoints": "所有数据点", - "Hide datapoints": "隐藏数据点" - }; - const __vite_glob_0_19 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$5 - }, Symbol.toStringTag, { value: "Module" })); - const translations$4 = {}; - const __vite_glob_0_20 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$4 - }, Symbol.toStringTag, { value: "Module" })); - const translations$3 = { - "Analysis configured": "分析已配置", - "Configure analysis": "配置分析", - "Stepped series": "阶梯序列", - "Hide source series": "隐藏源序列", - "All targets already have the same settings": "所有目标已经具有相同设置", - "Copy these analysis settings to all targets": "将这些分析设置复制到所有目标", - "Copy to all targets": "复制到所有目标" - }; - const __vite_glob_0_21 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$3 - }, Symbol.toStringTag, { value: "Module" })); - const translations$2 = { - Targets: "目标", - "Each row controls one chart series.": "每一行控制一条图表序列。", - "Add target": "添加目标", - "Chart preferences": "图表偏好设置" - }; - const __vite_glob_0_22 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$2 - }, Symbol.toStringTag, { value: "Module" })); - const translations$1 = { - "Loading Datapoints…": "正在加载数据点…", - Datapoints: "数据点", - "Page options": "页面选项", - "Download spreadsheet": "下载电子表格", - "Save page state": "保存页面状态", - "Restore saved page": "恢复已保存页面", - "Clear saved page": "清除已保存页面", - "Expand targets sidebar": "展开目标侧边栏", - "Collapse targets sidebar": "折叠目标侧边栏" - }; - const __vite_glob_0_23 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations: translations$1 - }, Symbol.toStringTag, { value: "Module" })); - const translations = { - "Toggle sidebar": "切换侧边栏", - Start: "开始", - End: "结束", - "Select date range": "选择日期范围", - "Timeline options": "时间线选项", - "Zoom level": "缩放级别", - "Date snapping": "日期对齐", - Auto: "自动", - Hour: "小时", - Day: "日", - Week: "周", - Month: "月", - Minute: "分钟", - Second: "秒", - Quarterly: "按季度", - "Month Compressed": "月份紧凑", - "Month Short": "月份简写", - "Month Expanded": "月份展开", - "Week Compressed": "周紧凑", - "Week Expanded": "周展开" - }; - const __vite_glob_0_24 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - translations - }, Symbol.toStringTag, { value: "Module" })); - const modules = /* @__PURE__ */ Object.assign({ - "../../../atoms/interactive/range-timeline/i18n/zh-hans.ts": __vite_glob_0_0, - "../../../cards/action/i18n/zh-hans.ts": __vite_glob_0_1, - "../../../cards/history/history-chart/i18n/zh-hans.ts": __vite_glob_0_2, - "../../../cards/history/i18n/zh-hans.ts": __vite_glob_0_3, - "../../../cards/list/i18n/zh-hans.ts": __vite_glob_0_4, - "../../../cards/quick/i18n/zh-hans.ts": __vite_glob_0_5, - "../../../cards/sensor/i18n/zh-hans.ts": __vite_glob_0_6, - "../../chart/i18n/zh-hans.ts": __vite_glob_0_7, - "../../ha/i18n/zh-hans.ts": __vite_glob_0_8, - "../../timeline/i18n/zh-hans.ts": __vite_glob_0_9, - "../../../molecules/analysis-anomaly-group/i18n/zh-hans.ts": __vite_glob_0_10, - "../../../molecules/analysis-delta-group/i18n/zh-hans.ts": __vite_glob_0_11, - "../../../molecules/analysis-rate-group/i18n/zh-hans.ts": __vite_glob_0_12, - "../../../molecules/analysis-sample-group/i18n/zh-hans.ts": __vite_glob_0_13, - "../../../molecules/analysis-summary-group/i18n/zh-hans.ts": __vite_glob_0_14, - "../../../molecules/analysis-threshold-group/i18n/zh-hans.ts": __vite_glob_0_15, - "../../../molecules/analysis-trend-group/i18n/zh-hans.ts": __vite_glob_0_16, - "../../../molecules/comparison-tab-rail/i18n/zh-hans.ts": __vite_glob_0_17, - "../../../molecules/date-window-dialog/i18n/zh-hans.ts": __vite_glob_0_18, - "../../../molecules/sidebar-options/sections/i18n/zh-hans.ts": __vite_glob_0_19, - "../../../molecules/target-row-list/i18n/zh-hans.ts": __vite_glob_0_20, - "../../../molecules/target-row/i18n/zh-hans.ts": __vite_glob_0_21, - "../../../panels/datapoints/components/history-targets/i18n/zh-hans.ts": __vite_glob_0_22, - "../../../panels/datapoints/components/panel-shell/i18n/zh-hans.ts": __vite_glob_0_23, - "../../../panels/datapoints/components/range-toolbar/i18n/zh-hans.ts": __vite_glob_0_24 - }); - const merged = {}; - for (const mod of Object.values(modules)) { - Object.assign(merged, mod.translations); - } - const templates = merged; - const zhHans = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ - __proto__: null, - templates - }, Symbol.toStringTag, { value: "Module" })); + } + }; + _defineProperty(HassRecordsSensorCardEditor, "styles", [EditorBase.styles, styles$2]); + //#endregion + //#region custom_components/hass_datapoints/src/register.ts + /** + * Register all custom elements and advertise them to the Lovelace card picker. + */ + if (!customElements.get("hass-datapoints-action-card")) customElements.define("hass-datapoints-action-card", HassRecordsActionCard); + if (!customElements.get("hass-datapoints-quick-card")) customElements.define("hass-datapoints-quick-card", HassRecordsQuickCard); + if (!customElements.get("hass-datapoints-history-card")) customElements.define("hass-datapoints-history-card", HassRecordsHistoryCard); + if (!customElements.get("hass-datapoints-sensor-card")) customElements.define("hass-datapoints-sensor-card", HassRecordsSensorCard); + if (!customElements.get("hass-datapoints-list-card")) customElements.define("hass-datapoints-list-card", HassRecordsListCard); + if (!customElements.get("hass-datapoints-history-panel")) customElements.define("hass-datapoints-history-panel", HassRecordsHistoryPanel); + if (!customElements.get("hass-datapoints-dev-tool-card")) customElements.define("hass-datapoints-dev-tool-card", HassRecordsDevToolCard); + if (!customElements.get("hass-datapoints-action-card-editor")) customElements.define("hass-datapoints-action-card-editor", HassRecordsActionCardEditor); + if (!customElements.get("hass-datapoints-quick-card-editor")) customElements.define("hass-datapoints-quick-card-editor", HassRecordsQuickCardEditor); + if (!customElements.get("hass-datapoints-history-card-editor")) customElements.define("hass-datapoints-history-card-editor", HassRecordsHistoryCardEditor); + if (!customElements.get("hass-datapoints-sensor-card-editor")) customElements.define("hass-datapoints-sensor-card-editor", HassRecordsSensorCardEditor); + if (!customElements.get("hass-datapoints-list-card-editor")) customElements.define("hass-datapoints-list-card-editor", HassRecordsListCardEditor); + if (!customElements.get("hass-datapoints-dev-tool-card-editor")) customElements.define("hass-datapoints-dev-tool-card-editor", HassRecordsDevToolCardEditor); + window.customCards = window.customCards || []; + var registeredTypes = new Set(window.customCards.map((card) => card.type)); + [ + { + type: "hass-datapoints-action-card", + name: "Hass Records – Action Card", + description: "Full form to record a custom event with message, annotation, icon, colour and entity association.", + preview: false + }, + { + type: "hass-datapoints-quick-card", + name: "Hass Records – Quick Card", + description: "Simple one-field card to quickly record a note with a bookmark icon.", + preview: false + }, + { + type: "hass-datapoints-history-card", + name: "Hass Records – History Card", + description: "History line chart with coloured annotation markers for recorded events.", + preview: false + }, + { + type: "hass-datapoints-sensor-card", + name: "Hass Records – Sensor Card", + description: "Sensor card with line chart — annotations shown as icons on the data line.", + preview: false + }, + { + type: "hass-datapoints-list-card", + name: "Hass Records – List Card", + description: "Activity-style datagrid to browse, search, edit and delete all recorded events.", + preview: false + }, + { + type: "hass-datapoints-dev-tool-card", + name: "Hass Records – Dev Tool", + description: "Generate demo datapoints from HA history and bulk-delete dev-flagged events.", + preview: false + } + ].forEach((card) => { + if (!registeredTypes.has(card.type)) window.customCards?.push(card); + }); + console.groupCollapsed(`%c hass-datapoints %c v0.4.2 loaded `, "color:#fff;background:#03a9f4;font-weight:bold;padding:2px 6px;border-radius:3px 0 0 3px", "color:#03a9f4;background:#fff;font-weight:bold;padding:2px 6px;border:1px solid #03a9f4;border-radius:0 3px 3px 0", ...[]); + console.log("Enable debug logging by setting %cwindow.__HASS_DATAPOINTS_DEV__ = true", "color:#333;background:#eee;border:1px solid #777;padding:2px 6px;border-radius:5px; font-family: Courier"); + console.groupEnd(); + //#endregion })(); diff --git a/custom_components/hass_datapoints/manifest.json b/custom_components/hass_datapoints/manifest.json index 096ed3bf..4c5a07c4 100644 --- a/custom_components/hass_datapoints/manifest.json +++ b/custom_components/hass_datapoints/manifest.json @@ -8,5 +8,5 @@ "iot_class": "local_push", "issue_tracker": "https://github.com/buggedcom/hass-datapoints/issues", "requirements": [], - "version": "0.4.1" + "version": "0.4.2" } diff --git a/custom_components/hass_datapoints/src/atoms/interactive/range-timeline/range-timeline.ts b/custom_components/hass_datapoints/src/atoms/interactive/range-timeline/range-timeline.ts index 113c35c6..996e056b 100644 --- a/custom_components/hass_datapoints/src/atoms/interactive/range-timeline/range-timeline.ts +++ b/custom_components/hass_datapoints/src/atoms/interactive/range-timeline/range-timeline.ts @@ -182,7 +182,6 @@ export class RangeTimeline extends LitElement { } disconnectedCallback() { - // eslint-disable-next-line wc/guard-super-call super.disconnectedCallback(); if (this._rangeScrollViewportEl) { this._rangeScrollViewportEl.removeEventListener( diff --git a/custom_components/hass_datapoints/src/atoms/interactive/resizable-panes/resizable-panes.ts b/custom_components/hass_datapoints/src/atoms/interactive/resizable-panes/resizable-panes.ts index 4daef681..33faa58e 100644 --- a/custom_components/hass_datapoints/src/atoms/interactive/resizable-panes/resizable-panes.ts +++ b/custom_components/hass_datapoints/src/atoms/interactive/resizable-panes/resizable-panes.ts @@ -51,7 +51,6 @@ export class ResizablePanes extends LitElement { } disconnectedCallback() { - // eslint-disable-next-line wc/guard-super-call super.disconnectedCallback(); // Clean up global pointer listeners if disconnected during an active drag. if (this._pointerId != null) { diff --git a/custom_components/hass_datapoints/src/cards/dev-tool/dev-tool-windows/dev-tool-windows.ts b/custom_components/hass_datapoints/src/cards/dev-tool/dev-tool-windows/dev-tool-windows.ts index 6b72ae70..5199e16f 100644 --- a/custom_components/hass_datapoints/src/cards/dev-tool/dev-tool-windows/dev-tool-windows.ts +++ b/custom_components/hass_datapoints/src/cards/dev-tool/dev-tool-windows/dev-tool-windows.ts @@ -12,7 +12,6 @@ export class CardDevToolWindows extends LitElement { @state() accessor _nextWindowId = 1; connectedCallback() { - // eslint-disable-next-line wc/guard-super-call super.connectedCallback(); if (this.windows.length === 0) { this.windows = [this._createWindow()]; diff --git a/custom_components/hass_datapoints/src/cards/dev-tool/dev-tool.ts b/custom_components/hass_datapoints/src/cards/dev-tool/dev-tool.ts index 3d2a550a..74ed11e4 100644 --- a/custom_components/hass_datapoints/src/cards/dev-tool/dev-tool.ts +++ b/custom_components/hass_datapoints/src/cards/dev-tool/dev-tool.ts @@ -1,6 +1,6 @@ +import { html, render } from "lit"; import { DOMAIN } from "@/constants"; import { confirmDestructiveAction } from "@/lib/ha/ha-components"; -import { esc } from "@/lib/util/format"; import type { HassLike } from "@/lib/types"; import { logger } from "@/lib/logger"; import { styles } from "./dev-tool.styles"; @@ -75,39 +75,53 @@ export class HassRecordsDevToolCard extends HTMLElement { _render() { this._rendered = true; const cfg = this._config; - this.shadowRoot!.innerHTML = ` - - - ${cfg.title ? `
${esc(cfg.title as string)}
` : ""} - -
Analyze HA History
- -
- -
- - - -
- Analyze all windows -
- - - - - -
- -
-
Dev Datapoints
-
- Currently recorded:  dev data points + if (!this.shadowRoot!.adoptedStyleSheets.length) { + const sheet = new CSSStyleSheet(); + sheet.replaceSync(styles); + this.shadowRoot!.adoptedStyleSheets = [sheet]; + } + render( + html` + + ${cfg.title + ? html`
${cfg.title as string}
` + : ""} +
Analyze HA History
+
+
- Delete all dev datapoints - -
- - `; + +
+ Analyze all windows +
+ + +
+
+
Dev Datapoints
+
+ Currently recorded:  dev data points +
+ Delete all dev datapoints + +
+ + `, + this.shadowRoot! + ); const entityPicker = this.shadowRoot!.getElementById( "entity-picker" @@ -273,9 +287,9 @@ export class HassRecordsDevToolCard extends HTMLElement { ? new Date(timestampRaw * 1000).toISOString() : new Date().toISOString(); - let message: Nullable = null; - let icon = "mdi:bookmark"; - let color = "#03a9f4"; + let message: Nullable; + let icon: string; + let color: string; if (domain === "binary_sensor" || domain === "input_boolean") { message = `${friendlyName}: ${this._binaryLabel(deviceClass, currentValue)}`; diff --git a/custom_components/hass_datapoints/src/cards/history/history-chart/history-chart.ts b/custom_components/hass_datapoints/src/cards/history/history-chart/history-chart.ts index 3deee680..e12dc8b2 100644 --- a/custom_components/hass_datapoints/src/cards/history/history-chart/history-chart.ts +++ b/custom_components/hass_datapoints/src/cards/history/history-chart/history-chart.ts @@ -9,8 +9,8 @@ * legend remain accessible from the parent card's shadow root. */ +import { html, render } from "lit"; import { msg } from "@/lib/i18n/localize"; -import { esc } from "@/lib/util/format"; import { binaryOffLabel, binaryOnLabel, @@ -83,7 +83,7 @@ import { buildRollingAverageTrend, buildSummaryStats, getTrendWindowMs, -} from "../analysis/index"; +} from "../analysis"; import { navigateToDataPointsHistory } from "@/lib/ha/navigation"; import { styles } from "./history-chart.styles"; @@ -531,10 +531,17 @@ export class HistoryChart extends HTMLElement { } else { overlayEl.style.left = ""; } - overlayEl.innerHTML = ` -
${msg("Date window:")} ${esc(overlay.window_range_label)}
-
${msg("Actual:")} ${esc(overlay.actual_range_label)}
- `; + render( + html` +
+ ${msg("Date window:")} ${overlay.window_range_label} +
+
+ ${msg("Actual:")} ${overlay.actual_range_label} +
+ `, + overlayEl + ); overlayEl.hidden = false; } @@ -720,33 +727,33 @@ export class HistoryChart extends HTMLElement { }>), ]; this._updateLegendLayout(legendEl); - legendEl.innerHTML = allItems - .map((item) => { + render( + html`${allItems.map((item) => { const hidden = this._hiddenSeries?.has(item.entityId); - return `
- -
`; - }) - .join(""); - legendEl.querySelectorAll(".legend-toggle").forEach((btn) => { - btn.addEventListener("click", () => { - const entityId = (btn as HTMLElement).dataset.entityId; - if (!entityId || !this._hiddenSeries) return; - if (this._hiddenSeries.has(entityId)) { - this._hiddenSeries.delete(entityId); - } else { - this._hiddenSeries.add(entityId); - } - btn.setAttribute( - "aria-pressed", - this._hiddenSeries.has(entityId) ? "false" : "true" - ); - this._redrawLastDraw(); - }); - }); + return html` +
+ +
+ `; + })}`, + legendEl + ); } /** Draw a single series line onto the renderer, with optional data-gap rendering. */ @@ -1038,7 +1045,13 @@ export class HistoryChart extends HTMLElement { "aria-label", event.message || "Open related history" ); - iconEl.innerHTML = ``; + render( + html``, + iconEl + ); iconEl.addEventListener("click", navigateToHistory); overlay.appendChild(iconEl); } else if (overlay) { @@ -1725,21 +1738,40 @@ export class HistoryChart extends HTMLElement { ); const labelRight = 10; - let labelsHtml = ""; + const labelItems: import("lit").TemplateResult[] = []; for (const { renderer, axis, rowOffset } of tracks) { if (!axis?.ticks?.length) continue; for (const tick of axis.ticks) { const y = rowOffset + renderer.yOf(tick, axis.min, axis.max); const formatted = renderer._formatAxisTick(tick, axis.unit); - labelsHtml += `
${esc(formatted)}
`; + labelItems.push( + html`
+ ${formatted} +
` + ); } if (axis.unit) { const unitY = rowOffset + Math.max(0, primaryRenderer.pad.top - 18); - labelsHtml += `
${esc(axis.unit)}
`; + labelItems.push( + html`
+ ${axis.unit} +
` + ); } } - leftEl.innerHTML = `
${labelsHtml}`; + render( + html`
+ ${labelItems}`, + leftEl + ); leftEl.classList.add("visible"); rightEl.innerHTML = ""; rightEl.classList.remove("visible"); diff --git a/custom_components/hass_datapoints/src/cards/list/list.ts b/custom_components/hass_datapoints/src/cards/list/list.ts index 25a9c056..afa5cfd8 100644 --- a/custom_components/hass_datapoints/src/cards/list/list.ts +++ b/custom_components/hass_datapoints/src/cards/list/list.ts @@ -95,7 +95,6 @@ export class HassRecordsListCard extends LitElement { } connectedCallback() { - // eslint-disable-next-line wc/guard-super-call super.connectedCallback(); this._windowListener = () => this._load(); window.addEventListener( @@ -105,7 +104,6 @@ export class HassRecordsListCard extends LitElement { } disconnectedCallback() { - // eslint-disable-next-line wc/guard-super-call super.disconnectedCallback(); if (this._unsubscribe) { this._unsubscribe(); diff --git a/custom_components/hass_datapoints/src/cards/sensor/sensor-chart/sensor-chart.ts b/custom_components/hass_datapoints/src/cards/sensor/sensor-chart/sensor-chart.ts index cfc5d2cc..78d01732 100644 --- a/custom_components/hass_datapoints/src/cards/sensor/sensor-chart/sensor-chart.ts +++ b/custom_components/hass_datapoints/src/cards/sensor/sensor-chart/sensor-chart.ts @@ -1,7 +1,6 @@ -import { html, LitElement } from "lit"; +import { html, LitElement, render } from "lit"; import { COLORS } from "@/constants"; import { contrastColor } from "@/lib/util/color"; -import { esc } from "@/lib/util/format"; import { setupCanvas } from "@/charts/utils/chart-dom"; import { ChartRenderer } from "@/lib/chart/chart-renderer"; import type { @@ -117,13 +116,11 @@ export class SensorChart extends LitElement { } connectedCallback() { - // eslint-disable-next-line wc/guard-super-call super.connectedCallback(); this._setupResizeObserver(); } disconnectedCallback() { - // eslint-disable-next-line wc/guard-super-call super.disconnectedCallback(); if (this._resizeObserver) { this._resizeObserver.disconnect(); @@ -409,7 +406,13 @@ export class SensorChart extends LitElement { el.style.left = `${hit.x}px`; el.style.top = `${hit.y}px`; el.style.background = bgColor; - el.innerHTML = ``; + render( + html``, + el + ); el.dataset.eventId = hit.event.id; el.addEventListener("click", (e) => { if (this.showAnnotationTooltips) { diff --git a/custom_components/hass_datapoints/src/cards/sensor/sensor-records/sensor-records.ts b/custom_components/hass_datapoints/src/cards/sensor/sensor-records/sensor-records.ts index 9d0422b2..713796e2 100644 --- a/custom_components/hass_datapoints/src/cards/sensor/sensor-records/sensor-records.ts +++ b/custom_components/hass_datapoints/src/cards/sensor/sensor-records/sensor-records.ts @@ -66,7 +66,6 @@ export class SensorRecords extends LitElement { } disconnectedCallback(): void { - // eslint-disable-next-line wc/guard-super-call super.disconnectedCallback(); if (this._paginationNotifyRaf !== null) { window.cancelAnimationFrame(this._paginationNotifyRaf); diff --git a/custom_components/hass_datapoints/src/cards/sensor/sensor.ts b/custom_components/hass_datapoints/src/cards/sensor/sensor.ts index 535d20d6..56c7a7d4 100644 --- a/custom_components/hass_datapoints/src/cards/sensor/sensor.ts +++ b/custom_components/hass_datapoints/src/cards/sensor/sensor.ts @@ -92,14 +92,12 @@ export class HassRecordsSensorCard extends LitElement { } connectedCallback() { - // eslint-disable-next-line wc/guard-super-call super.connectedCallback(); this._setupResizeObserver(); if (this._initialized && this._hass) this._load(); } disconnectedCallback() { - // eslint-disable-next-line wc/guard-super-call super.disconnectedCallback(); if (this._unsubscribe) { this._unsubscribe(); diff --git a/custom_components/hass_datapoints/src/charts/base/chart-card-base.ts b/custom_components/hass_datapoints/src/charts/base/chart-card-base.ts index 487ff165..8009992c 100644 --- a/custom_components/hass_datapoints/src/charts/base/chart-card-base.ts +++ b/custom_components/hass_datapoints/src/charts/base/chart-card-base.ts @@ -85,7 +85,6 @@ export abstract class ChartCardBase extends LitElement { // ── Lifecycle ───────────────────────────────────────────────────────────── connectedCallback(): void { - // eslint-disable-next-line wc/guard-super-call super.connectedCallback(); if (this._initialized) { if (!this._unsubscribe || !this._windowListener) { @@ -111,7 +110,6 @@ export abstract class ChartCardBase extends LitElement { } disconnectedCallback(): void { - // eslint-disable-next-line wc/guard-super-call super.disconnectedCallback(); this._cleanup(); } diff --git a/custom_components/hass_datapoints/src/charts/utils/chart-dom.ts b/custom_components/hass_datapoints/src/charts/utils/chart-dom.ts index e04f8d95..31ec420e 100644 --- a/custom_components/hass_datapoints/src/charts/utils/chart-dom.ts +++ b/custom_components/hass_datapoints/src/charts/utils/chart-dom.ts @@ -1,4 +1,4 @@ -import { esc } from "@/lib/util/format"; +import { html, render, type TemplateResult } from "lit"; import { ChartRenderer, type ResolvedAxis } from "@/lib/chart/chart-renderer"; /** @@ -696,7 +696,7 @@ export function buildChartCardShell(title?: Nullable): string { return ` - ${title ? `
${esc(title)}
` : ""} + ${title ? `
${title}
` : ""}
@@ -856,31 +856,56 @@ export function renderChartAxisOverlays( if (!duplicateUnit || !axis?.color) { return ""; } - return `color:${esc(axis.color)};`; + return `color:${axis.color};`; }; - const buildAxisMarkup = (axis: ResolvedAxis) => { - const labels = (axis.ticks || []) - .map((tick: number) => { - const y = renderer.yOf(tick, axis.min, axis.max); - return `
${esc(renderer._formatAxisTick(tick, axis.unit))}
`; - }) - .join(""); - const unit = axis.unit - ? `
${esc(axis.unit)}
` + const buildAxisMarkup = (axis: ResolvedAxis): TemplateResult => { + const labelItems = (axis.ticks || []).map((tick: number) => { + const y = renderer.yOf(tick, axis.min, axis.max); + const sideStyle = + axis.side === "left" + ? `right:${axisOffset(axis)}px;text-align:right;` + : `left:${axisOffset(axis)}px;text-align:left;`; + return html`
+ ${renderer._formatAxisTick(tick, axis.unit)} +
`; + }); + const unitItem = axis.unit + ? html`
+ ${axis.unit} +
` : ""; - return `${labels}${unit}`; + return html`${labelItems}${unitItem}`; }; const leftAxes = axes.filter((axis) => axis.side !== "right"); const rightAxes = axes.filter((axis) => axis.side === "right"); - leftEl.innerHTML = leftAxes.length - ? `
${leftAxes.map((axis) => buildAxisMarkup(axis)).join("")}` - : ""; - rightEl.innerHTML = rightAxes.length - ? `
${rightAxes.map((axis) => buildAxisMarkup(axis)).join("")}` - : ""; + render( + leftAxes.length + ? html`
+ ${leftAxes.map((axis) => buildAxisMarkup(axis))}` + : html``, + leftEl + ); + render( + rightAxes.length + ? html`
+ ${rightAxes.map((axis) => buildAxisMarkup(axis))}` + : html``, + rightEl + ); leftEl.classList.toggle("visible", !!leftAxes.length); rightEl.classList.toggle("visible", !!rightAxes.length); diff --git a/custom_components/hass_datapoints/src/components/annotation-dialog/__tests__/annotation-dialog.spec.ts b/custom_components/hass_datapoints/src/components/annotation-dialog/__tests__/annotation-dialog.spec.ts index 4398f6e1..ae584bdf 100644 --- a/custom_components/hass_datapoints/src/components/annotation-dialog/__tests__/annotation-dialog.spec.ts +++ b/custom_components/hass_datapoints/src/components/annotation-dialog/__tests__/annotation-dialog.spec.ts @@ -150,4 +150,141 @@ describe("HistoryAnnotationDialogController", () => { }); }); }); + + // --------------------------------------------------------------------------- + // Layout + // --------------------------------------------------------------------------- + + describe("GIVEN dialog is open", () => { + beforeEach(() => { + host = createHost(); + controller = new HistoryAnnotationDialogController(host); + controller.open(makeHover()); + }); + + describe("WHEN rendered", () => { + it("THEN message and date fields share the message-date row", () => { + const panel = host.shadowRoot.querySelector( + "#chart-context-dialog-panel" + ); + const row = panel?.querySelector(".context-form-row-message-date"); + expect(row).not.toBeNull(); + expect(row?.querySelector("#chart-context-message")).not.toBeNull(); + expect(row?.querySelector("#chart-context-date")).not.toBeNull(); + }); + + it("THEN icon and color fields share the icon-color row", () => { + const panel = host.shadowRoot.querySelector( + "#chart-context-dialog-panel" + ); + const row = panel?.querySelector(".context-form-row-icon-color"); + expect(row).not.toBeNull(); + expect(row?.querySelector("#chart-context-icon")).not.toBeNull(); + expect(row?.querySelector("#chart-context-color")).not.toBeNull(); + }); + }); + }); + + // --------------------------------------------------------------------------- + // Target selector — always visible, no toggle button + // --------------------------------------------------------------------------- + + describe("GIVEN dialog is open", () => { + let panel; + + beforeEach(() => { + host = createHost(); + controller = new HistoryAnnotationDialogController(host); + controller.open(makeHover()); + panel = host.shadowRoot.querySelector("#chart-context-dialog-panel"); + }); + + describe("WHEN rendered", () => { + it("THEN the ha-selector for targets is present in the DOM", () => { + expect(panel?.querySelector("#chart-context-target")).not.toBeNull(); + }); + + it("THEN there is no hidden target wrapper element", () => { + expect(panel?.querySelector("#chart-context-target-wrap")).toBeNull(); + }); + + it("THEN there is no Add-target toggle button", () => { + expect(panel?.querySelector("#chart-context-add-target")).toBeNull(); + }); + }); + }); + + // --------------------------------------------------------------------------- + // Target accumulation + // --------------------------------------------------------------------------- + + describe("GIVEN dialog is open", () => { + let panel; + let chipRow; + + beforeEach(() => { + host = createHost(); + // Reset entity ids so the default linked target is empty, giving us a + // clean baseline for testing extra-target accumulation. + host._entityIds = []; + controller = new HistoryAnnotationDialogController(host); + controller.open(makeHover()); + panel = host.shadowRoot.querySelector("#chart-context-dialog-panel"); + chipRow = panel?.querySelector("annotation-chip-row"); + }); + + describe("WHEN value-changed fires twice with different entity_ids", () => { + beforeEach(() => { + const targetSel = panel?.querySelector("#chart-context-target"); + targetSel?.dispatchEvent( + new CustomEvent("value-changed", { + detail: { value: { entity_id: ["sensor.a"] } }, + bubbles: false, + }) + ); + targetSel?.dispatchEvent( + new CustomEvent("value-changed", { + detail: { value: { entity_id: ["sensor.b"] } }, + bubbles: false, + }) + ); + }); + + it("THEN the chip row contains chips for both entities", () => { + const chips = chipRow?.chips ?? []; + const ids = chips.map((c) => c.itemId); + expect(ids).toContain("sensor.a"); + expect(ids).toContain("sensor.b"); + }); + + it("THEN the chip row contains exactly two chips", () => { + expect((chipRow?.chips ?? []).length).toBe(2); + }); + }); + + describe("WHEN value-changed fires with entity_id then device_id", () => { + beforeEach(() => { + const targetSel = panel?.querySelector("#chart-context-target"); + targetSel?.dispatchEvent( + new CustomEvent("value-changed", { + detail: { value: { entity_id: ["sensor.x"] } }, + bubbles: false, + }) + ); + targetSel?.dispatchEvent( + new CustomEvent("value-changed", { + detail: { value: { device_id: ["device-1"] } }, + bubbles: false, + }) + ); + }); + + it("THEN the chip row contains both the entity and the device", () => { + const chips = chipRow?.chips ?? []; + const ids = chips.map((c) => c.itemId); + expect(ids).toContain("sensor.x"); + expect(ids).toContain("device-1"); + }); + }); + }); }); diff --git a/custom_components/hass_datapoints/src/components/annotation-dialog/annotation-dialog.ts b/custom_components/hass_datapoints/src/components/annotation-dialog/annotation-dialog.ts index e594164c..1d15d9b1 100644 --- a/custom_components/hass_datapoints/src/components/annotation-dialog/annotation-dialog.ts +++ b/custom_components/hass_datapoints/src/components/annotation-dialog/annotation-dialog.ts @@ -233,7 +233,9 @@ export class HistoryAnnotationDialogController { this._linkedTarget = current; if (this._chipRowEl) { this._chipRowEl.hass = this._host._hass ?? null; - this._chipRowEl.chips = this._buildChips(this._linkedTarget); + this._chipRowEl.chips = this._buildChips( + mergeTargetSelections(this._linkedTarget, this._target) + ); } } @@ -294,18 +296,6 @@ export class HistoryAnnotationDialogController { iconPicker.hass = this._host._hass; iconPicker.value = prefill.icon || hover?.event?.icon || "mdi:bookmark"; } - const targetSel = this._panelEl.querySelector( - "#chart-context-target" - ) as Nullable; - if (targetSel) { - targetSel.hass = this._host._hass; - targetSel.value = "{}"; - targetSel.addEventListener("value-changed", (ev) => { - this._target = normalizeTargetSelection( - (ev as CustomEvent).detail.value || {} - ); - }); - } if (messageEl) { messageEl.value = prefill.message || ""; } @@ -333,6 +323,31 @@ export class HistoryAnnotationDialogController { this._chipRowEl.chips = this._buildChips(this._linkedTarget); } + // Wire up the always-visible target selector. + const targetSel = this._panelEl.querySelector( + "#chart-context-target" + ) as Nullable; + if (targetSel) { + targetSel.hass = this._host._hass; + (targetSel as HaInputElement & { selector: unknown }).selector = { + target: {}, + }; + targetSel.addEventListener("value-changed", (ev) => { + // Merge incoming selection with existing _target so picking a second + // entity/device doesn't wipe out the first. + this._target = mergeTargetSelections( + this._target, + normalizeTargetSelection((ev as CustomEvent).detail.value || {}) + ); + if (this._chipRowEl) { + this._chipRowEl.hass = this._host._hass ?? null; + this._chipRowEl.chips = this._buildChips( + mergeTargetSelections(this._linkedTarget, this._target) + ); + } + }); + } + this.bindTargetChipActions(); const colorInput = this._panelEl.querySelector( "#chart-context-color" @@ -490,10 +505,10 @@ export class HistoryAnnotationDialogController { .context-dialog-content { display: grid; gap: 16px; padding-top: 4px; } .context-form { display: grid; gap: 16px; } .context-form-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; } - .context-form-main, .context-form-side { display: grid; gap: 16px; min-width: 0; } - .context-form-side { align-content: start; justify-items: start; } + .context-form-row { display: grid; gap: 16px; align-items: start; } + .context-form-row-message-date { grid-template-columns: minmax(0, 1fr) 220px; } + .context-form-row-icon-color { grid-template-columns: 220px auto; } .context-form-field { display: grid; gap: 6px; min-width: 0; } - .context-form-field.compact-field { justify-items: start; } .context-form-label { font-size: 0.9rem; font-weight: 600; color: var(--primary-text-color); } .context-form-help { font-size: 0.8rem; color: var(--secondary-text-color); line-height: 1.45; } .context-form-help-inline { display: inline-flex; align-items: center; gap: 6px; } @@ -508,42 +523,49 @@ export class HistoryAnnotationDialogController { .context-chip-remove:hover { background: color-mix(in srgb, currentColor 12%, transparent); } .context-chip-remove ha-icon { --mdc-icon-size: 12px; pointer-events: none; } .context-color-control { display: flex; align-items: center; gap: 10px; } - .context-color-preview { width: 28px; height: 28px; border-radius: 50%; border: 2px solid var(--divider-color, #ccc); background: ${esc(defaultColor)}; flex: 0 0 auto; } + .context-color-preview { width: 28px; height: 28px; border-radius: 50%; border: 2px solid var(--divider-color, #ccc); flex: 0 0 auto; } .context-color-input { width: 56px; height: 36px; padding: 0; border: none; background: transparent; cursor: pointer; } - .context-date-input { width: 220px; max-width: 100%; } - .context-icon-input { width: 220px; max-width: 100%; } + .context-date-input { width: 100%; box-sizing: border-box; } + .context-icon-input { width: 100%; } .context-form-feedback { color: var(--error-color); font-size: 0.84rem; } .context-form-footer { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 8px; } .context-form-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-left: auto; } + @media (max-width: 600px) { + .context-form-row-message-date, + .context-form-row-icon-color { grid-template-columns: minmax(0, 1fr); } + }
-
+ +
Use a short title that will be shown in the chart tooltip and records list.
- -
Add any longer context, outcome, or note you want to keep with this data point.
- -
-
-
- -
Optionally add more entities, devices, areas, or labels that should also be linked to this annotation.
- -
-
-
-
The annotation will be placed at this exact moment on the chart.
-
+
+ +
+ +
Add any longer context, outcome, or note you want to keep with this data point.
+ +
+ +
+ +
+ +
+ +
+
Choose the icon shown for this data point in the chart and records list.
@@ -575,12 +597,6 @@ export class HistoryAnnotationDialogController { this._dialogEl.hass = this._host._hass; this._dialogEl.dialogInitialFocus = "#chart-context-message"; } - const targetSel = this._panelEl.querySelector( - "#chart-context-target" - ) as Nullable; - if (targetSel) { - targetSel.selector = { target: {} }; - } this.bindFields(hover); this._dialogEl.open = true; this._host._creatingContextAnnotation = true; diff --git a/custom_components/hass_datapoints/src/lib/chart/__tests__/chart-interaction.spec.ts b/custom_components/hass_datapoints/src/lib/chart/__tests__/chart-interaction.spec.ts index 461efca5..0ec34122 100644 --- a/custom_components/hass_datapoints/src/lib/chart/__tests__/chart-interaction.spec.ts +++ b/custom_components/hass_datapoints/src/lib/chart/__tests__/chart-interaction.spec.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from "vitest"; +import { html, render } from "lit"; import { buildTooltipRelatedChips, @@ -103,13 +104,15 @@ describe("chart-interaction", () => { expect.assertions(2); const card = createCard(); - const markup = buildTooltipRelatedChips(card._hass, { + const chips = buildTooltipRelatedChips(card._hass, { entity_ids: ["sensor.alpha"], device_ids: ["device.one"], }); + const container = document.createElement("div"); + render(chips ?? html``, container); - expect(markup).toContain("Alpha Sensor"); - expect(markup).toContain("Radiator"); + expect(container.textContent).toContain("Alpha Sensor"); + expect(container.textContent).toContain("Radiator"); }); }); }); diff --git a/custom_components/hass_datapoints/src/lib/chart/chart-interaction.ts b/custom_components/hass_datapoints/src/lib/chart/chart-interaction.ts index 2fa5a251..044338d9 100644 --- a/custom_components/hass_datapoints/src/lib/chart/chart-interaction.ts +++ b/custom_components/hass_datapoints/src/lib/chart/chart-interaction.ts @@ -1,3 +1,4 @@ +import { html, render, type TemplateResult } from "lit"; import { msg } from "@/lib/i18n/localize"; import { areaIcon, @@ -9,7 +10,7 @@ import { labelIcon, labelName, } from "@/lib/ha/entity-name"; -import { esc, fmtDateTime } from "@/lib/util/format"; +import { fmtDateTime } from "@/lib/util/format"; import { clampChartValue, formatTooltipDisplayValue, @@ -662,26 +663,40 @@ function buildAnnotationTooltip( tooltip.className = "tooltip secondary annotation-tooltip"; const hasValue = event?.chart_value != null && event.chart_value !== ""; - const valueMarkup = hasValue - ? `
${esc(formatTooltipValue(event.chart_value, event.chart_unit))}
` - : ""; const message = event?.message || "Data point"; const annotation = event?.annotation && event.annotation !== event.message ? event.annotation : ""; - const relatedMarkup = buildTooltipRelatedChips(interactionState._hass, event); - - tooltip.innerHTML = ` -
${esc(fmtDateTime(event.timestamp))}
- ${valueMarkup} -
- - ${esc(message)} -
-
${esc(annotation)}
-
${relatedMarkup}
- `; + const chips = buildTooltipRelatedChips(interactionState._hass, event); + + render( + html` +
${fmtDateTime(event.timestamp)}
+ ${hasValue + ? html`
+ ${formatTooltipValue(event.chart_value, event.chart_unit)} +
` + : ""} +
+ + ${message} +
+
+ ${annotation} +
+
+ ${chips} +
+ `, + tooltip + ); return tooltip; } @@ -759,7 +774,7 @@ export function showTooltip( : ""; ttValue.style.display = hasValue ? "block" : "none"; if (ttSeries) { - ttSeries.innerHTML = ""; + render(html``, ttSeries); ttSeries.style.display = "none"; } ttDot.style.background = event.color || "#03a9f4"; @@ -770,9 +785,9 @@ export function showTooltip( const ann = event.annotation !== event.message ? event.annotation : ""; ttAnn.textContent = ann || ""; ttAnn.style.display = ann ? "block" : "none"; - const relatedMarkup = buildTooltipRelatedChips(interactionState._hass, event); - ttEntities.innerHTML = relatedMarkup; - ttEntities.style.display = relatedMarkup ? "flex" : "none"; + const chips = buildTooltipRelatedChips(interactionState._hass, event); + render(chips ?? html``, ttEntities); + ttEntities.style.display = chips ? "flex" : "none"; const chartBounds = ( root.querySelector(".chart-wrap") ?? card @@ -1238,30 +1253,41 @@ export function showLineChartTooltip( : ""; ttValue.style.display = value ? "block" : "none"; if (ttSeries) { - ttSeries.innerHTML = ""; + render(html``, ttSeries); ttSeries.style.display = "none"; } } else { ttValue.textContent = ""; ttValue.style.display = "none"; if (ttSeries) { - ttSeries.innerHTML = displayRows - .map( - (entry) => ` -
-
- ${ - entry.grouped === true && entry.rawVisible === true - ? "" - : `` - } - ${esc(resolveTooltipSeriesLabel(entry))} -
- ${esc(formatTooltipDisplayValue(entry.value, entry.unit))} -
- ` - ) - .join(""); + render( + html`${displayRows.map( + (entry) => html` +
+
+ ${entry.grouped === true && entry.rawVisible === true + ? "" + : html``} + ${resolveTooltipSeriesLabel(entry)} +
+ ${formatTooltipDisplayValue(entry.value, entry.unit)} +
+ ` + )}`, + ttSeries + ); ttSeries.style.display = displayRows.length ? "grid" : "none"; } } @@ -1270,7 +1296,7 @@ export function showLineChartTooltip( ttMsg.textContent = ""; ttAnn.textContent = ""; ttAnn.style.display = "none"; - ttEntities.innerHTML = ""; + render(html``, ttEntities); ttEntities.style.display = "none"; if ( @@ -1318,7 +1344,7 @@ export function showLineChartTooltip( export function buildTooltipRelatedChips( hass: Nullable | undefined, event: Nullable | undefined -): string { +): TemplateResult | null { const entities = Array.isArray(event?.entity_ids) ? event.entity_ids : []; const devices = Array.isArray(event?.device_ids) ? event.device_ids : []; const areas = Array.isArray(event?.area_ids) ? event.area_ids : []; @@ -1341,17 +1367,15 @@ export function buildTooltipRelatedChips( label: labelName(hass, id), })), ].filter((chip) => chip.label); - if (!chips.length) return ""; - return chips - .map( - (chip) => ` - - - ${esc(chip.label)} - - ` - ) - .join(""); + if (!chips.length) return null; + return html`${chips.map( + (chip) => html` + + + ${chip.label} + + ` + )}`; } export function showLineChartCrosshair( @@ -1388,30 +1412,39 @@ export function showLineChartCrosshair( : []), ...(hover.comparisonValues || []), ]; - points.innerHTML = ` - ${crosshairValues - .filter((entry) => entry.hasValue !== false) - .map( - (entry) => ` - - ` - ) - .join("")} - ${crosshairValues - .filter((entry) => entry.hasValue !== false) - .map( - (entry) => ` - - ` - ) - .join("")} - `; + render( + html` + ${crosshairValues + .filter((entry) => entry.hasValue !== false) + .map( + (entry) => html` + + ` + )} + ${crosshairValues + .filter((entry) => entry.hasValue !== false) + .map( + (entry) => html` + + ` + )} + `, + points + ); renderChartAxisHoverDots(card, crosshairValues); if (addButton && addButton.dataset.allowAddAnnotation !== "false") { addButton.hidden = false; @@ -1499,7 +1532,7 @@ export function hideLineChartHover(card: ChartInteractionHost): void { const points = getRoot(card).getElementById("crosshair-points"); const addButton = getRoot(card).getElementById("chart-add-annotation"); if (overlay) overlay.hidden = true; - if (points) points.innerHTML = ""; + if (points) render(html``, points); renderChartAxisHoverDots(card, []); const horizontal = getRoot(card).getElementById("crosshair-horizontal"); if (horizontal) horizontal.hidden = true; diff --git a/custom_components/hass_datapoints/src/lib/ha/ha-components.ts b/custom_components/hass_datapoints/src/lib/ha/ha-components.ts index 361f0384..ff7bba2a 100644 --- a/custom_components/hass_datapoints/src/lib/ha/ha-components.ts +++ b/custom_components/hass_datapoints/src/lib/ha/ha-components.ts @@ -1,5 +1,5 @@ import { loadHaComponents } from "@kipk/load-ha-components"; -import { esc } from "@/lib/util/format"; +import { html, render } from "lit"; import { msg } from "@/lib/i18n/localize"; const HA_COMPONENT_LOAD_TIMEOUT_MS = 6000; @@ -192,48 +192,6 @@ export function confirmDestructiveAction( if (host?._hass) { dialog.hass = host._hass; } - dialog.innerHTML = ` - -
-
${esc(options.message || msg("Are you sure you want to delete this item?"))}
-
- - -
-
- `; - let settled = false; const finish = (value: boolean) => { if (settled) { @@ -244,19 +202,66 @@ export function confirmDestructiveAction( resolve(value); }; - const cancelButton = dialog.querySelector( - ".confirm-dialog-button.cancel" - ); - const confirmButton = dialog.querySelector( - ".confirm-dialog-button.confirm" + render( + html` + +
+
+ ${options.message || + msg("Are you sure you want to delete this item?")} +
+
+ + +
+
+ `, + dialog ); - cancelButton?.addEventListener("click", () => { - finish(false); - }); - confirmButton?.addEventListener("click", () => { - finish(true); - }); dialog.addEventListener("keydown", (event) => { if ( event.key !== "Enter" || @@ -284,7 +289,11 @@ export function confirmDestructiveAction( root.appendChild(dialog); dialog.open = true; window.requestAnimationFrame(() => { - (confirmButton as Nullable)?.focus(); + ( + dialog.querySelector( + ".confirm-dialog-button.confirm" + ) as Nullable + )?.focus(); }); }) ); diff --git a/custom_components/hass_datapoints/src/lib/util/format.ts b/custom_components/hass_datapoints/src/lib/util/format.ts index da08b512..18465614 100644 --- a/custom_components/hass_datapoints/src/lib/util/format.ts +++ b/custom_components/hass_datapoints/src/lib/util/format.ts @@ -42,5 +42,6 @@ export function esc(str: unknown): string { .replace(/&/g, "&") .replace(//g, ">") - .replace(/"/g, """); + .replace(/"/g, """) + .replace(/'/g, "'"); } diff --git a/custom_components/hass_datapoints/src/molecules/annotation-chip-row/__tests__/annotation-chip-row.spec.ts b/custom_components/hass_datapoints/src/molecules/annotation-chip-row/__tests__/annotation-chip-row.spec.ts index b083eb61..96a40ec0 100644 --- a/custom_components/hass_datapoints/src/molecules/annotation-chip-row/__tests__/annotation-chip-row.spec.ts +++ b/custom_components/hass_datapoints/src/molecules/annotation-chip-row/__tests__/annotation-chip-row.spec.ts @@ -61,13 +61,9 @@ describe("annotation-chip-row", () => { }); describe("WHEN rendered", () => { - it("THEN renders the label", () => { + it("THEN does not render an internal label (label is owned by the outer form field)", () => { expect.assertions(1); - expect( - el - .shadowRoot!.querySelector(".context-form-label") - ?.textContent?.trim() - ).toBe("Linked targets"); + expect(el.shadowRoot!.querySelector(".context-form-label")).toBeNull(); }); it("THEN renders the help text", () => { @@ -217,23 +213,23 @@ describe("annotation-chip-row", () => { }); // --------------------------------------------------------------------------- - // Custom label / text + // Custom text props // --------------------------------------------------------------------------- - describe("GIVEN a custom label", () => { + describe("GIVEN a custom emptyText", () => { beforeEach(async () => { - el = createElement({ label: "Related items" }); + el = createElement({ chips: [], emptyText: "Nothing here yet." }); await el.updateComplete; }); - describe("WHEN rendered", () => { - it("THEN shows the custom label", () => { + describe("WHEN rendered with no chips", () => { + it("THEN shows the custom empty text", () => { expect.assertions(1); expect( el - .shadowRoot!.querySelector(".context-form-label") + .shadowRoot!.querySelector(".context-form-help") ?.textContent?.trim() - ).toBe("Related items"); + ).toBe("Nothing here yet."); }); }); }); diff --git a/custom_components/hass_datapoints/src/molecules/annotation-chip-row/annotation-chip-row.ts b/custom_components/hass_datapoints/src/molecules/annotation-chip-row/annotation-chip-row.ts index afdbb616..c983af8d 100644 --- a/custom_components/hass_datapoints/src/molecules/annotation-chip-row/annotation-chip-row.ts +++ b/custom_components/hass_datapoints/src/molecules/annotation-chip-row/annotation-chip-row.ts @@ -70,7 +70,6 @@ export class AnnotationChipRow extends LitElement { render() { return html`
-
${this.chips.length > 0 ? this.helpText : this.emptyText}
diff --git a/custom_components/hass_datapoints/src/molecules/collapsed-options-menu/collapsed-options-menu.ts b/custom_components/hass_datapoints/src/molecules/collapsed-options-menu/collapsed-options-menu.ts index db1d0a34..4e4675e9 100644 --- a/custom_components/hass_datapoints/src/molecules/collapsed-options-menu/collapsed-options-menu.ts +++ b/custom_components/hass_datapoints/src/molecules/collapsed-options-menu/collapsed-options-menu.ts @@ -56,7 +56,6 @@ export class CollapsedOptionsMenu extends LitElement { private _closeTimer: Nullable> = null; disconnectedCallback() { - // eslint-disable-next-line wc/guard-super-call super.disconnectedCallback(); if (this._closeTimer !== null) { clearTimeout(this._closeTimer); diff --git a/custom_components/hass_datapoints/src/molecules/comparison-tab-rail/comparison-tab-rail.ts b/custom_components/hass_datapoints/src/molecules/comparison-tab-rail/comparison-tab-rail.ts index 22ef9ef4..5af6a13a 100644 --- a/custom_components/hass_datapoints/src/molecules/comparison-tab-rail/comparison-tab-rail.ts +++ b/custom_components/hass_datapoints/src/molecules/comparison-tab-rail/comparison-tab-rail.ts @@ -47,7 +47,6 @@ export class ComparisonTabRail extends LitElement { private _resizeObserver?: ResizeObserver; connectedCallback() { - // eslint-disable-next-line wc/guard-super-call super.connectedCallback(); if (this._resizeObserver) { return; @@ -67,7 +66,6 @@ export class ComparisonTabRail extends LitElement { } disconnectedCallback() { - // eslint-disable-next-line wc/guard-super-call super.disconnectedCallback(); this._resizeObserver?.disconnect(); this._resizeObserver = undefined; diff --git a/custom_components/hass_datapoints/src/molecules/floating-menu/floating-menu.ts b/custom_components/hass_datapoints/src/molecules/floating-menu/floating-menu.ts index f3c46011..c791ebe2 100644 --- a/custom_components/hass_datapoints/src/molecules/floating-menu/floating-menu.ts +++ b/custom_components/hass_datapoints/src/molecules/floating-menu/floating-menu.ts @@ -21,14 +21,12 @@ export class FloatingMenu extends LitElement { @property({ type: Boolean, reflect: true }) accessor open: boolean = false; connectedCallback() { - // eslint-disable-next-line wc/guard-super-call super.connectedCallback(); this._onPointerDown = this._onPointerDown.bind(this); window.addEventListener("pointerdown", this._onPointerDown, true); } disconnectedCallback() { - // eslint-disable-next-line wc/guard-super-call super.disconnectedCallback(); window.removeEventListener("pointerdown", this._onPointerDown, true); } diff --git a/custom_components/hass_datapoints/src/panels/datapoints/components/history-targets/history-targets.styles.ts b/custom_components/hass_datapoints/src/panels/datapoints/components/history-targets/history-targets.styles.ts index b6baf5af..5345624c 100644 --- a/custom_components/hass_datapoints/src/panels/datapoints/components/history-targets/history-targets.styles.ts +++ b/custom_components/hass_datapoints/src/panels/datapoints/components/history-targets/history-targets.styles.ts @@ -24,7 +24,7 @@ export const styles = css` } .history-target-rows { - width: calc(var(--sidebar-width-expanded) - var(--dp-spacing-lg) * 2); + width: 100%; } .sidebar-section-header { diff --git a/custom_components/hass_datapoints/src/panels/datapoints/components/history-targets/history-targets.ts b/custom_components/hass_datapoints/src/panels/datapoints/components/history-targets/history-targets.ts index 66549192..67aa4c94 100644 --- a/custom_components/hass_datapoints/src/panels/datapoints/components/history-targets/history-targets.ts +++ b/custom_components/hass_datapoints/src/panels/datapoints/components/history-targets/history-targets.ts @@ -4,7 +4,6 @@ import { localized, msg } from "@/lib/i18n/localize"; import { styles } from "./history-targets.styles"; import { entityName } from "@/lib/ha/entity-name"; -import { esc } from "@/lib/util/format"; import type { HassLike, HassState } from "@/lib/types"; import "@/molecules/target-row-list/target-row-list"; @@ -125,8 +124,8 @@ export class HistoryTargets extends LitElement { ? "is-hidden" : ""}" data-entity-id=${row.entity_id} - style="--row-color:${esc(row.color)}" - aria-label=${esc(label)} + style="--row-color:${row.color}" + aria-label=${label} aria-pressed=${row.visible === false ? "false" : "true"} @click=${(ev: Event) => this._onCollapsedEntityClick(ev, row.entity_id)} diff --git a/custom_components/hass_datapoints/src/panels/datapoints/components/panel-shell/panel-shell.styles.ts b/custom_components/hass_datapoints/src/panels/datapoints/components/panel-shell/panel-shell.styles.ts index b4a5beac..e9915e01 100644 --- a/custom_components/hass_datapoints/src/panels/datapoints/components/panel-shell/panel-shell.styles.ts +++ b/custom_components/hass_datapoints/src/panels/datapoints/components/panel-shell/panel-shell.styles.ts @@ -342,6 +342,7 @@ export const styles = css` position: absolute; top: 0; left: 0; + min-width: min(380px, 85vw); width: min(380px, 85vw); height: 100%; z-index: 10; @@ -407,4 +408,10 @@ export const styles = css` display: none; } } + + @media (max-width: 545px) { + .page-sidebar { + width: min(380px, 95vw); + } + } `; diff --git a/custom_components/hass_datapoints/src/panels/datapoints/datapoints.styles.ts b/custom_components/hass_datapoints/src/panels/datapoints/datapoints.styles.ts index da695a30..95fa5fc7 100644 --- a/custom_components/hass_datapoints/src/panels/datapoints/datapoints.styles.ts +++ b/custom_components/hass_datapoints/src/panels/datapoints/datapoints.styles.ts @@ -2114,6 +2114,7 @@ export const PANEL_HISTORY_STYLE = ` position: absolute; top: 0; left: 0; + min-width: min(380px, 85vw); width: min(380px, 85vw); height: 100%; z-index: 10; @@ -2195,6 +2196,12 @@ export const PANEL_HISTORY_STYLE = ` display: flex; } } + + @media (max-width: 545px) { + .page-sidebar { + width: min(380px, 95vw); + } + } `; export const PANEL_HISTORY_LOADING_STYLE = ` diff --git a/custom_components/hass_datapoints/src/panels/datapoints/datapoints.ts b/custom_components/hass_datapoints/src/panels/datapoints/datapoints.ts index ec675db2..08b2e71d 100644 --- a/custom_components/hass_datapoints/src/panels/datapoints/datapoints.ts +++ b/custom_components/hass_datapoints/src/panels/datapoints/datapoints.ts @@ -2859,8 +2859,7 @@ export class HassRecordsHistoryPanel extends HTMLElement { targetControl.addEventListener( "value-changed", (ev: DetailEvent<{ value?: unknown }>) => { - const hasValue = - ev.detail && Object.prototype.hasOwnProperty.call(ev.detail, "value"); + const hasValue = ev.detail && Object.hasOwn(ev.detail, "value"); if (!hasValue) { return; } diff --git a/custom_components/hass_datapoints/src/register.ts b/custom_components/hass_datapoints/src/register.ts index 7de58d58..933e247e 100644 --- a/custom_components/hass_datapoints/src/register.ts +++ b/custom_components/hass_datapoints/src/register.ts @@ -150,10 +150,17 @@ cardsToAdd.forEach((card) => { } }); +const _devSyncId = import.meta.env.VITE_DEV_SYNC_ID as string | undefined; console.groupCollapsed( - "%c hass-datapoints %c v0.3.0 loaded ", + `%c hass-datapoints %c v0.3.0 loaded${_devSyncId ? `%c %c DEV#${_devSyncId} ` : " "}`, "color:#fff;background:#03a9f4;font-weight:bold;padding:2px 6px;border-radius:3px 0 0 3px", - "color:#03a9f4;background:#fff;font-weight:bold;padding:2px 6px;border:1px solid #03a9f4;border-radius:0 3px 3px 0" + "color:#03a9f4;background:#fff;font-weight:bold;padding:2px 6px;border:1px solid #03a9f4;border-radius:0 3px 3px 0", + ...(_devSyncId + ? [ + "background:transparent;", + "color:#fff;background:#f57c00;font-weight:bold;padding:2px 6px;border-radius:3px", + ] + : []) ); console.log( "Enable debug logging by setting %cwindow.__HASS_DATAPOINTS_DEV__ = true", diff --git a/custom_components/hass_datapoints/src/test-support/ha-stubs.ts b/custom_components/hass_datapoints/src/test-support/ha-stubs.ts index 247b7f31..2db75cf0 100644 --- a/custom_components/hass_datapoints/src/test-support/ha-stubs.ts +++ b/custom_components/hass_datapoints/src/test-support/ha-stubs.ts @@ -7,6 +7,7 @@ * Containers pass child content through a . */ +import { html, svg, render } from "lit"; import * as mdi from "@mdi/js"; type MdiIconModule = RecordWithStringValues; @@ -53,13 +54,44 @@ function resolveMdiPath( // Icon elements — ha-icon, ha-state-icon, ha-svg-icon // --------------------------------------------------------------------------- +const HA_ICON_STYLES = ` + :host { + display: inline-flex; + align-items: center; + justify-content: center; + width: var(--mdc-icon-size, 24px); + height: var(--mdc-icon-size, 24px); + flex-shrink: 0; + } + svg { + width: 100%; + height: 100%; + display: block; + fill: currentColor; + } +`; + class HaIconStub extends HTMLElement { protected _icon: Nullable; + // Shared stylesheet — created once per class, applied to every icon shadow root. + private static _sheet: CSSStyleSheet | null = null; + + private static _getSheet(): CSSStyleSheet { + if (!HaIconStub._sheet) { + HaIconStub._sheet = new CSSStyleSheet(); + HaIconStub._sheet.replaceSync(HA_ICON_STYLES); + } + return HaIconStub._sheet; + } + constructor() { super(); this.attachShadow({ mode: "open" }); this._icon = null; + if (this.shadowRoot) { + this.shadowRoot.adoptedStyleSheets = [HaIconStub._getSheet()]; + } } connectedCallback() { @@ -94,31 +126,17 @@ class HaIconStub extends HTMLElement { if (!this.shadowRoot) { return; } - this.shadowRoot.innerHTML = ` - - `, + this.shadowRoot + ); } } @@ -173,26 +191,20 @@ customElements.define( _render(): void { const path = this.path || null; - const fallback = ``; if (!this.shadowRoot) { return; } - this.shadowRoot.innerHTML = ` - - - `; + // Styles are inherited via adoptedStyleSheets set in HaIconStub constructor. + render( + svg``, + this.shadowRoot + ); } } ); @@ -201,6 +213,23 @@ customElements.define( // Button elements — ha-icon-button // --------------------------------------------------------------------------- +const HA_ICON_BUTTON_STYLES = ` + :host { + display: inline-flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + background: transparent; + cursor: pointer; + box-sizing: border-box; + transition: background 120ms; + } + :host(:hover) { background: rgba(128, 128, 128, 0.12); } + ::slotted(*), slot { pointer-events: none; } +`; + customElements.define( "ha-icon-button", class extends HTMLElement { @@ -210,25 +239,10 @@ customElements.define( if (!this.shadowRoot) { return; } - this.shadowRoot.innerHTML = ` - - - `; + const sheet = new CSSStyleSheet(); + sheet.replaceSync(HA_ICON_BUTTON_STYLES); + this.shadowRoot.adoptedStyleSheets = [sheet]; + this.shadowRoot.appendChild(document.createElement("slot")); } } ); @@ -303,9 +317,23 @@ const HA_FIELD_STYLES = ` class HaFieldStub extends HTMLElement { value: Nullable = null; + // Shared stylesheet — created once per class, applied to every shadow root. + private static _sheet: CSSStyleSheet | null = null; + + private static _getSheet(): CSSStyleSheet { + if (!HaFieldStub._sheet) { + HaFieldStub._sheet = new CSSStyleSheet(); + HaFieldStub._sheet.replaceSync(HA_FIELD_STYLES); + } + return HaFieldStub._sheet; + } + constructor() { super(); this.attachShadow({ mode: "open" }); + if (this.shadowRoot) { + this.shadowRoot.adoptedStyleSheets = [HaFieldStub._getSheet()]; + } } connectedCallback() { @@ -329,14 +357,20 @@ class HaFieldStub extends HTMLElement { if (!this.shadowRoot) { return; } - this.shadowRoot.innerHTML = ` - -
- ${label ? `${label}` : ""} - ${hasValue ? `${value}` : ""} - -
- `; + render( + html` +
+ ${label ? html`${label}` : ""} + ${hasValue ? html`${value}` : ""} + +
+ `, + this.shadowRoot + ); } } @@ -361,6 +395,37 @@ for (const tag of FIELD_ELEMENTS) { // Switch / toggle — ha-switch // --------------------------------------------------------------------------- +const HA_SWITCH_STYLES = ` + :host { + display: inline-flex; + align-items: center; + cursor: pointer; + } + .track { + width: 36px; + height: 20px; + border-radius: 10px; + background: var(--disabled-text-color, #bdbdbd); + position: relative; + transition: background 120ms; + } + :host([checked]) .track, + .track.on { background: var(--primary-color, #03a9f4); } + .thumb { + position: absolute; + top: 2px; + left: 2px; + width: 16px; + height: 16px; + border-radius: 50%; + background: #fff; + box-shadow: 0 1px 3px rgba(0,0,0,0.3); + transition: transform 120ms; + } + :host([checked]) .thumb, + .track.on .thumb { transform: translateX(16px); } +`; + if (!customElements.get("ha-switch")) { customElements.define( "ha-switch", @@ -370,6 +435,11 @@ if (!customElements.get("ha-switch")) { constructor() { super(); this.attachShadow({ mode: "open" }); + if (this.shadowRoot) { + const sheet = new CSSStyleSheet(); + sheet.replaceSync(HA_SWITCH_STYLES); + this.shadowRoot.adoptedStyleSheets = [sheet]; + } this._renderSwitch(); } @@ -386,41 +456,14 @@ if (!customElements.get("ha-switch")) { if (!this.shadowRoot) { return; } - this.shadowRoot.innerHTML = ` - -
-
-
- `; + render( + html` +
+
+
+ `, + this.shadowRoot + ); } } ); diff --git a/custom_components/hass_datapoints/websocket_api.py b/custom_components/hass_datapoints/websocket_api.py index 275aa540..bd5cec09 100644 --- a/custom_components/hass_datapoints/websocket_api.py +++ b/custom_components/hass_datapoints/websocket_api.py @@ -8,6 +8,7 @@ from datetime import UTC, datetime import voluptuous as vol +from homeassistant.auth.permissions.const import POLICY_READ from homeassistant.components import websocket_api from homeassistant.components.recorder import get_instance from homeassistant.core import HomeAssistant, callback @@ -61,6 +62,22 @@ "persistence", "comparison_window", ] +_VALID_ANOMALY_SENSITIVITY = ["low", "medium", "high"] +_VALID_ANOMALY_OVERLAP_MODES = ["all", "highlight", "only"] +_VALID_TREND_METHODS = ["rolling_average", "linear_trend"] + +# Field-length caps for free-text event fields stored in .storage/ +_MAX_LEN_MESSAGE = 10_000 +_MAX_LEN_ICON = 255 +_MAX_LEN_COLOR = 20 +_MAX_LEN_WINDOW = 20 # interval strings like "24h", "30m" + +# Validator for MDI icon names (e.g. "mdi:bookmark", "mdi:home-outline") +_RE_MDI_ICON = r"^mdi:[a-z0-9][a-z0-9\-]*$" +# Validator for CSS hex colors (#rgb, #rrggbb, #rrggbbaa) +_RE_HEX_COLOR = r"^#[0-9a-fA-F]{3,8}$" +# Validator for duration strings accepted by parse_interval_seconds ("1h", "30m", "24h", …) +_RE_DURATION = r"^\d+[smhd]$" # Maximum date-range span accepted by ws_get_history. Requests beyond this # limit return an empty pts list so the frontend gracefully falls back to the @@ -105,10 +122,17 @@ async def ws_get_events( ) -> None: """Return recorded events, with optional time/entity filters.""" store = hass.data[DOMAIN]["store"] + entity_ids = msg.get("entity_ids") + # For non-admin users, remove any entity IDs they are not permitted to read + # so the store never returns events tagged with inaccessible entities. + if entity_ids is not None and not connection.user.is_admin: + entity_ids = [ + eid for eid in entity_ids if _can_read_entity(connection.user, eid) + ] events = store.get_events( start=msg.get("start_time"), end=msg.get("end_time"), - entity_ids=msg.get("entity_ids"), + entity_ids=entity_ids, ) connection.send_result(msg["id"], {"events": events}) @@ -292,6 +316,22 @@ def _require_admin(connection: websocket_api.ActiveConnection, msg: dict) -> boo return True +def _can_read_entity(user, entity_id: str) -> bool: + """Return True if *user* has read permission for *entity_id*. + + Admin users always pass. Non-admin users are checked against the HA + permission policy attached to their account. Any unexpected error + (e.g. missing permissions object on a system user) is treated as + permissive so we don't accidentally block valid callers. + """ + if user.is_admin: + return True + try: + return bool(user.permissions.check_entity(entity_id, POLICY_READ)) + except Exception: # noqa: BLE001 + return True + + def _valid_uuid(value: str) -> str: """Voluptuous validator — raises Invalid if value is not a well-formed UUID.""" try: @@ -305,14 +345,18 @@ def _valid_uuid(value: str) -> str: { vol.Required("type"): f"{DOMAIN}/events/update", vol.Required("event_id"): vol.All(str, _valid_uuid), - vol.Optional("message"): str, - vol.Optional("annotation"): str, + vol.Optional("message"): vol.All(str, vol.Length(max=_MAX_LEN_MESSAGE)), + vol.Optional("annotation"): vol.All(str, vol.Length(max=_MAX_LEN_MESSAGE)), vol.Optional("entity_ids"): [str], vol.Optional("device_ids"): [str], vol.Optional("area_ids"): [str], vol.Optional("label_ids"): [str], - vol.Optional("icon"): str, - vol.Optional("color"): str, + vol.Optional("icon"): vol.All( + str, vol.Length(max=_MAX_LEN_ICON), vol.Match(_RE_MDI_ICON) + ), + vol.Optional("color"): vol.All( + str, vol.Length(max=_MAX_LEN_COLOR), vol.Match(_RE_HEX_COLOR) + ), } ) @websocket_api.async_response @@ -386,7 +430,7 @@ async def ws_delete_dev_events( @websocket_api.websocket_command( { vol.Required("type"): f"{DOMAIN}/history", - vol.Required("entity_id"): str, + vol.Required("entity_id"): cv.entity_id, vol.Required("start_time"): str, vol.Required("end_time"): str, vol.Required("interval"): vol.In(_VALID_INTERVALS), @@ -401,6 +445,11 @@ async def ws_get_history( ) -> None: """Return (optionally downsampled) entity history as [[timeMs, value], ...].""" entity_id: str = msg["entity_id"] + if not _can_read_entity(connection.user, entity_id): + connection.send_error( + msg["id"], "unauthorized", "Access to this entity is not permitted" + ) + return start_time: str = msg["start_time"] end_time: str = msg["end_time"] interval: str = msg["interval"] @@ -488,23 +537,41 @@ def _run_detection(pts: list, config: dict, comparison_pts: list | None = None) @websocket_api.websocket_command( { vol.Required("type"): f"{DOMAIN}/anomalies", - vol.Required("entity_id"): str, + vol.Required("entity_id"): cv.entity_id, vol.Required("start_time"): str, vol.Required("end_time"): str, - vol.Required("anomaly_methods"): [str], - vol.Optional("anomaly_sensitivity", default="medium"): str, - vol.Optional("anomaly_overlap_mode", default="all"): str, - vol.Optional("anomaly_rate_window", default="1h"): str, - vol.Optional("anomaly_zscore_window", default="24h"): str, - vol.Optional("anomaly_persistence_window", default="1h"): str, - vol.Optional("trend_method", default="rolling_average"): str, - vol.Optional("trend_window", default="24h"): str, + vol.Required("anomaly_methods"): vol.All( + [vol.In(_VALID_ANOMALY_METHODS)], vol.Length(min=1) + ), + vol.Optional("anomaly_sensitivity", default="medium"): vol.In( + _VALID_ANOMALY_SENSITIVITY + ), + vol.Optional("anomaly_overlap_mode", default="all"): vol.In( + _VALID_ANOMALY_OVERLAP_MODES + ), + vol.Optional("anomaly_rate_window", default="1h"): vol.All( + str, vol.Length(max=_MAX_LEN_WINDOW), vol.Match(_RE_DURATION) + ), + vol.Optional("anomaly_zscore_window", default="24h"): vol.All( + str, vol.Length(max=_MAX_LEN_WINDOW), vol.Match(_RE_DURATION) + ), + vol.Optional("anomaly_persistence_window", default="1h"): vol.All( + str, vol.Length(max=_MAX_LEN_WINDOW), vol.Match(_RE_DURATION) + ), + vol.Optional("trend_method", default="rolling_average"): vol.In( + _VALID_TREND_METHODS + ), + vol.Optional("trend_window", default="24h"): vol.All( + str, vol.Length(max=_MAX_LEN_WINDOW), vol.Match(_RE_DURATION) + ), vol.Optional("sample_interval"): vol.In(_VALID_INTERVALS), vol.Optional("sample_aggregate", default="mean"): vol.In(_VALID_AGGREGATES), vol.Optional("comparison_entity_id"): cv.entity_id, vol.Optional("comparison_start_time"): str, vol.Optional("comparison_end_time"): str, - vol.Optional("comparison_time_offset_ms", default=0): int, + vol.Optional("comparison_time_offset_ms", default=0): vol.All( + int, vol.Range(min=-315_576_000_000, max=315_576_000_000) + ), } ) @websocket_api.async_response @@ -515,6 +582,21 @@ async def ws_get_anomalies( ) -> None: """Run backend anomaly detection for a single entity and return clusters.""" entity_id: str = msg["entity_id"] + if not _can_read_entity(connection.user, entity_id): + connection.send_error( + msg["id"], "unauthorized", "Access to this entity is not permitted" + ) + return + comparison_entity_id: str | None = msg.get("comparison_entity_id") + if comparison_entity_id and not _can_read_entity( + connection.user, comparison_entity_id + ): + connection.send_error( + msg["id"], + "unauthorized", + "Access to the comparison entity is not permitted", + ) + return start_time: str = msg["start_time"] end_time: str = msg["end_time"] @@ -655,7 +737,7 @@ async def ws_get_anomalies( @websocket_api.websocket_command( { vol.Required("type"): f"{DOMAIN}/cache/clear", - vol.Optional("entity_id"): str, + vol.Optional("entity_id"): cv.entity_id, } ) @websocket_api.async_response @@ -665,6 +747,7 @@ async def ws_clear_cache( msg: dict, ) -> None: """Clear anomaly cache entries (all, or for a specific entity).""" + _require_admin(connection, msg) cache: AnomalyCache | None = hass.data.get(DOMAIN, {}).get("anomaly_cache") if cache is None: connection.send_result(msg["id"], {"cleared": 0}) diff --git a/eslint.config.mjs b/eslint.config.mjs index 2ce54fef..dd7a3d2f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,9 +1,10 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; +import { includeIgnoreFile } from "@eslint/compat"; import js from "@eslint/js"; -import { FlatCompat } from "@eslint/eslintrc"; import eslintConfigPrettier from "eslint-config-prettier"; +import { configs, plugins } from "eslint-config-airbnb-extended"; import litPlugin from "eslint-plugin-lit"; import litA11yPlugin from "eslint-plugin-lit-a11y"; import unusedImports from "eslint-plugin-unused-imports"; @@ -13,10 +14,7 @@ import tseslint from "typescript-eslint"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); - -const compat = new FlatCompat({ - baseDirectory: __dirname, -}); +const gitignorePath = path.resolve(__dirname, ".gitignore"); export default [ { @@ -31,8 +29,17 @@ export default [ ".idea/**", ], }, + // Ignore files and folders listed in .gitignore + includeIgnoreFile(gitignorePath), + // ESLint recommended js.configs.recommended, - ...compat.extends("airbnb-base"), + // Airbnb base rules (replaces legacy compat.extends("airbnb-base")). + // configs.base.recommended uses @stylistic and import-x but does not bundle + // their plugin registrations — include the plugin objects first. + plugins.stylistic, + plugins.importX, + ...configs.base.recommended, + // TypeScript recommended (non-type-aware — no parserOptions.project needed) ...tseslint.configs.recommended.map((config) => ({ ...config, files: ["custom_components/hass_datapoints/src/**/*.{js,ts}"], @@ -57,10 +64,11 @@ export default [ "class-methods-use-this": "off", "consistent-return": "off", curly: ["error", "all"], - "import/extensions": "off", - "import/no-mutable-exports": "off", - "import/no-unresolved": "off", - "import/prefer-default-export": "off", + // airbnb-extended uses import-x plugin (successor to eslint-plugin-import) + "import-x/extensions": "off", + "import-x/no-mutable-exports": "off", + "import-x/no-unresolved": "off", + "import-x/prefer-default-export": "off", "no-console": "warn", "no-continue": "off", "no-param-reassign": "off", @@ -107,7 +115,7 @@ export default [ ], rules: { "@typescript-eslint/no-explicit-any": "off", - "import/no-extraneous-dependencies": "off", + "import-x/no-extraneous-dependencies": "off", }, }, eslintConfigPrettier, diff --git a/package.json b/package.json index 98bb6ad3..07a9d85f 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,16 @@ { "name": "hass-datapoints", - "version": "0.4.1", + "version": "0.4.2", "private": true, "description": "Build and development entrypoints for the hass-datapoints Home Assistant integration.", "packageManager": "pnpm@10.33.0", "engines": { - "node": ">=18.18.0" + "node": ">=24.0.0" }, "scripts": { + "version:patch": "bash scripts/bump-version.sh patch", + "version:minor": "bash scripts/bump-version.sh minor", + "version:major": "bash scripts/bump-version.sh major", "build": "bash scripts/build.sh", "test": "vitest run", "test:watch": "vitest", @@ -18,51 +21,57 @@ "lint:eslint:cmd": "eslint \"custom_components/hass_datapoints/src/**/*.{js,ts}\" --cache --cache-strategy=content --cache-location=node_modules/.cache/eslint/.eslintcache", "lint:eslint": "pnpm lint:eslint:cmd --max-warnings=0", "format:eslint": "pnpm lint:eslint:cmd --fix", + "lint:package-json": "npmPkgJsonLint .", "lint:prettier": "prettier . --cache --check", "format:prettier": "prettier . --cache --write", "lint:types": "tsc --noEmit", - "lint": "pnpm run lint:eslint && pnpm run lint:prettier && pnpm run lint:types", + "lint": "pnpm run lint:eslint && pnpm run lint:package-json && pnpm run lint:prettier && pnpm run lint:types", "format": "pnpm run format:eslint && pnpm run format:prettier", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build" }, "devDependencies": { - "@chromatic-com/storybook": "^5.1.1", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "^9.39.1", - "@mdi/js": "^7.4.47", - "@storybook/addon-a11y": "^10.3.3", - "@storybook/addon-docs": "^10.3.3", - "@storybook/addon-mcp": "^0.5.0", - "@storybook/addon-vitest": "^10.3.3", - "@storybook/test": "^8.6.15", - "@storybook/web-components-vite": "^10.3.3", + "@chromatic-com/storybook": "5.1.1", + "@eslint/compat": "2.0.5", + "@eslint/js": "10.0.1", + "@mdi/js": "7.4.47", + "@storybook/addon-a11y": "10.3.3", + "@storybook/addon-docs": "10.3.3", + "@storybook/addon-mcp": "0.5.0", + "@storybook/addon-vitest": "10.3.3", + "@storybook/test": "8.6.15", + "@storybook/web-components-vite": "10.3.3", "@vitest/browser": "3.2.4", "@vitest/coverage-v8": "3.2.4", "@vitest/ui": "3.2.4", - "chromatic": "^16.0.0", - "eslint": "^9.39.1", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-import": "^2.32.0", - "eslint-plugin-lit": "^2.1.1", - "eslint-plugin-lit-a11y": "^5.1.1", - "eslint-plugin-unused-imports": "^4.3.0", - "eslint-plugin-wc": "^2.0.3", - "globals": "^16.4.0", - "happy-dom": "^20.8.9", - "playwright": "^1.58.2", - "prettier": "^3.6.2", - "storybook": "^10.3.3", - "typescript": "^5.9.3", - "typescript-eslint": "^8.46.2", - "vite": "^7.1.11", - "vitest": "^3.2.4" + "chromatic": "16.0.0", + "eslint": "10.2.0", + "eslint-config-airbnb-extended": "3.1.0", + "eslint-config-prettier": "10.1.8", + "eslint-plugin-lit": "2.2.1", + "eslint-plugin-lit-a11y": "5.1.1", + "eslint-plugin-unused-imports": "4.4.1", + "eslint-plugin-wc": "3.1.0", + "globals": "17.4.0", + "happy-dom": "20.8.9", + "npm-package-json-lint": "10.2.0", + "playwright": "1.58.2", + "prettier": "3.8.1", + "storybook": "10.3.3", + "typescript": "5.9.3", + "typescript-eslint": "8.58.1", + "vite": "8.0.5", + "vitest": "3.2.4" + }, + "pnpm": { + "overrides": { + "lodash": "4.18.0" + } }, "dependencies": { - "@kipk/load-ha-components": "^1.0.3", - "@lit/context": "^1.1.6", - "@lit/localize": "^0.12.2", - "lit": "^3.3.2" + "@kipk/load-ha-components": "1.0.3", + "@lit/context": "1.1.6", + "@lit/localize": "0.12.2", + "lit": "3.3.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ba77b2dc..d0b6f5c1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,55 +4,58 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + lodash: 4.18.0 + importers: .: dependencies: "@kipk/load-ha-components": - specifier: ^1.0.3 + specifier: 1.0.3 version: 1.0.3 "@lit/context": - specifier: ^1.1.6 + specifier: 1.1.6 version: 1.1.6 "@lit/localize": - specifier: ^0.12.2 + specifier: 0.12.2 version: 0.12.2 lit: - specifier: ^3.3.2 + specifier: 3.3.2 version: 3.3.2 devDependencies: "@chromatic-com/storybook": - specifier: ^5.1.1 + specifier: 5.1.1 version: 5.1.1(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) - "@eslint/eslintrc": - specifier: ^3.3.1 - version: 3.3.5 + "@eslint/compat": + specifier: 2.0.5 + version: 2.0.5(eslint@10.2.0) "@eslint/js": - specifier: ^9.39.1 - version: 9.39.4 + specifier: 10.0.1 + version: 10.0.1(eslint@10.2.0) "@mdi/js": - specifier: ^7.4.47 + specifier: 7.4.47 version: 7.4.47 "@storybook/addon-a11y": - specifier: ^10.3.3 + specifier: 10.3.3 version: 10.3.3(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) "@storybook/addon-docs": - specifier: ^10.3.3 - version: 10.3.3(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.5.0)) + specifier: 10.3.3 + version: 10.3.3(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4)) "@storybook/addon-mcp": - specifier: ^0.5.0 + specifier: 0.5.0 version: 0.5.0(@storybook/addon-vitest@10.3.3(@vitest/browser@3.2.4)(@vitest/runner@3.2.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vitest@3.2.4))(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) "@storybook/addon-vitest": - specifier: ^10.3.3 + specifier: 10.3.3 version: 10.3.3(@vitest/browser@3.2.4)(@vitest/runner@3.2.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vitest@3.2.4) "@storybook/test": - specifier: ^8.6.15 + specifier: 8.6.15 version: 8.6.15(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) "@storybook/web-components-vite": - specifier: ^10.3.3 - version: 10.3.3(esbuild@0.27.4)(lit@3.3.2)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.5.0)) + specifier: 10.3.3 + version: 10.3.3(esbuild@0.27.4)(lit@3.3.2)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4)) "@vitest/browser": specifier: 3.2.4 - version: 3.2.4(playwright@1.58.2)(vite@7.3.1(@types/node@25.5.0))(vitest@3.2.4) + version: 3.2.4(playwright@1.58.2)(vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4))(vitest@3.2.4) "@vitest/coverage-v8": specifier: 3.2.4 version: 3.2.4(@vitest/browser@3.2.4)(vitest@3.2.4) @@ -60,59 +63,59 @@ importers: specifier: 3.2.4 version: 3.2.4(vitest@3.2.4) chromatic: - specifier: ^16.0.0 + specifier: 16.0.0 version: 16.0.0 eslint: - specifier: ^9.39.1 - version: 9.39.4 - eslint-config-airbnb-base: - specifier: ^15.0.0 - version: 15.0.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4))(eslint@9.39.4) + specifier: 10.2.0 + version: 10.2.0 + eslint-config-airbnb-extended: + specifier: 3.1.0 + version: 3.1.0(@typescript-eslint/parser@8.58.1(eslint@10.2.0)(typescript@5.9.3))(@typescript-eslint/utils@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@10.2.0)(typescript@5.9.3) eslint-config-prettier: - specifier: ^10.1.8 - version: 10.1.8(eslint@9.39.4) - eslint-plugin-import: - specifier: ^2.32.0 - version: 2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4) + specifier: 10.1.8 + version: 10.1.8(eslint@10.2.0) eslint-plugin-lit: - specifier: ^2.1.1 - version: 2.2.1(eslint@9.39.4) + specifier: 2.2.1 + version: 2.2.1(eslint@10.2.0) eslint-plugin-lit-a11y: - specifier: ^5.1.1 - version: 5.1.1(eslint@9.39.4) + specifier: 5.1.1 + version: 5.1.1(eslint@10.2.0) eslint-plugin-unused-imports: - specifier: ^4.3.0 - version: 4.4.1(@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4) + specifier: 4.4.1 + version: 4.4.1(@typescript-eslint/eslint-plugin@8.58.1(@typescript-eslint/parser@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint@10.2.0)(typescript@5.9.3))(eslint@10.2.0) eslint-plugin-wc: - specifier: ^2.0.3 - version: 2.2.1(eslint@9.39.4) + specifier: 3.1.0 + version: 3.1.0(eslint@10.2.0) globals: - specifier: ^16.4.0 - version: 16.5.0 + specifier: 17.4.0 + version: 17.4.0 happy-dom: - specifier: ^20.8.9 + specifier: 20.8.9 version: 20.8.9 + npm-package-json-lint: + specifier: 10.2.0 + version: 10.2.0(typescript@5.9.3) playwright: - specifier: ^1.58.2 + specifier: 1.58.2 version: 1.58.2 prettier: - specifier: ^3.6.2 + specifier: 3.8.1 version: 3.8.1 storybook: - specifier: ^10.3.3 + specifier: 10.3.3 version: 10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) typescript: - specifier: ^5.9.3 + specifier: 5.9.3 version: 5.9.3 typescript-eslint: - specifier: ^8.46.2 - version: 8.57.2(eslint@9.39.4)(typescript@5.9.3) + specifier: 8.58.1 + version: 8.58.1(eslint@10.2.0)(typescript@5.9.3) vite: - specifier: ^7.1.11 - version: 7.3.1(@types/node@25.5.0) + specifier: 8.0.5 + version: 8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4) vitest: - specifier: ^3.2.4 - version: 3.2.4(@types/node@25.5.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.8.9) + specifier: 3.2.4 + version: 3.2.4(@types/node@25.5.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.8.9)(lightningcss@1.32.0) packages: "@adobe/css-tools@4.4.4": @@ -135,6 +138,57 @@ packages: } engines: { node: ">=6.9.0" } + "@babel/compat-data@7.29.0": + resolution: + { + integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==, + } + engines: { node: ">=6.9.0" } + + "@babel/core@7.29.0": + resolution: + { + integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==, + } + engines: { node: ">=6.9.0" } + + "@babel/generator@7.29.1": + resolution: + { + integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==, + } + engines: { node: ">=6.9.0" } + + "@babel/helper-compilation-targets@7.28.6": + resolution: + { + integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==, + } + engines: { node: ">=6.9.0" } + + "@babel/helper-globals@7.28.0": + resolution: + { + integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==, + } + engines: { node: ">=6.9.0" } + + "@babel/helper-module-imports@7.28.6": + resolution: + { + integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==, + } + engines: { node: ">=6.9.0" } + + "@babel/helper-module-transforms@7.28.6": + resolution: + { + integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==, + } + engines: { node: ">=6.9.0" } + peerDependencies: + "@babel/core": ^7.0.0 + "@babel/helper-string-parser@7.27.1": resolution: { @@ -149,6 +203,20 @@ packages: } engines: { node: ">=6.9.0" } + "@babel/helper-validator-option@7.27.1": + resolution: + { + integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==, + } + engines: { node: ">=6.9.0" } + + "@babel/helpers@7.29.2": + resolution: + { + integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==, + } + engines: { node: ">=6.9.0" } + "@babel/parser@7.29.2": resolution: { @@ -164,6 +232,20 @@ packages: } engines: { node: ">=6.9.0" } + "@babel/template@7.28.6": + resolution: + { + integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==, + } + engines: { node: ">=6.9.0" } + + "@babel/traverse@7.29.0": + resolution: + { + integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==, + } + engines: { node: ">=6.9.0" } + "@babel/types@7.29.0": resolution: { @@ -187,6 +269,24 @@ packages: peerDependencies: storybook: ^0.0.0-0 || ^10.1.0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0 + "@emnapi/core@1.9.2": + resolution: + { + integrity: sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==, + } + + "@emnapi/runtime@1.9.2": + resolution: + { + integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==, + } + + "@emnapi/wasi-threads@1.2.1": + resolution: + { + integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==, + } + "@esbuild/aix-ppc64@0.27.4": resolution: { @@ -437,54 +537,64 @@ packages: } engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } - "@eslint/config-array@0.21.2": + "@eslint/compat@2.0.5": resolution: { - integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==, + integrity: sha512-IbHDbHJfkVNv6xjlET8AIVo/K1NQt7YT4Rp6ok/clyBGcpRx1l6gv0Rq3vBvYfPJIZt6ODf66Zq08FJNDpnzgg==, } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + peerDependencies: + eslint: ^8.40 || 9 || 10 + peerDependenciesMeta: + eslint: + optional: true - "@eslint/config-helpers@0.4.2": + "@eslint/config-array@0.23.5": resolution: { - integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==, + integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==, } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - "@eslint/core@0.17.0": + "@eslint/config-helpers@0.5.5": resolution: { - integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==, + integrity: sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==, } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - "@eslint/eslintrc@3.3.5": + "@eslint/core@1.2.1": resolution: { - integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==, + integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==, } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - "@eslint/js@9.39.4": + "@eslint/js@10.0.1": resolution: { - integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==, + integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==, } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + peerDependencies: + eslint: ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true - "@eslint/object-schema@2.1.7": + "@eslint/object-schema@3.0.5": resolution: { - integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==, + integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==, } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - "@eslint/plugin-kit@0.4.1": + "@eslint/plugin-kit@0.7.1": resolution: { - integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==, + integrity: sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==, } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } "@humanfs/core@0.19.1": resolution: @@ -604,12 +714,66 @@ packages: "@types/react": ">=16" react: ">=16" + "@napi-rs/wasm-runtime@0.2.12": + resolution: + { + integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==, + } + + "@napi-rs/wasm-runtime@1.1.3": + resolution: + { + integrity: sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==, + } + peerDependencies: + "@emnapi/core": ^1.7.1 + "@emnapi/runtime": ^1.7.1 + "@neoconfetti/react@1.0.0": resolution: { integrity: sha512-klcSooChXXOzIm+SE5IISIAn3bYzYfPjbX7D7HoqZL84oAfgREeSg5vSIaSFH+DaGzzvImTyWe1OyrJ67vik4A==, } + "@next/eslint-plugin-next@16.2.2": + resolution: + { + integrity: sha512-IOPbWzDQ+76AtjZioaCjpIY72xNSDMnarZ2GMQ4wjNLvnJEJHqxQwGFhgnIWLV9klb4g/+amg88Tk5OXVpyLTw==, + } + + "@nodelib/fs.scandir@2.1.5": + resolution: + { + integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, + } + engines: { node: ">= 8" } + + "@nodelib/fs.stat@2.0.5": + resolution: + { + integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==, + } + engines: { node: ">= 8" } + + "@nodelib/fs.walk@1.2.8": + resolution: + { + integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, + } + engines: { node: ">= 8" } + + "@oxc-project/types@0.122.0": + resolution: + { + integrity: sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==, + } + + "@package-json/types@0.0.12": + resolution: + { + integrity: sha512-uu43FGU34B5VM9mCNjXCwLaGHYjXdNincqKLaraaCW+7S2+SmiBg1Nv8bPnmschrIfZmfKNY9f3fC376MRrObw==, + } + "@pkgjs/parseargs@0.11.0": resolution: { @@ -623,6 +787,152 @@ packages: integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==, } + "@rolldown/binding-android-arm64@1.0.0-rc.12": + resolution: + { + integrity: sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [android] + + "@rolldown/binding-darwin-arm64@1.0.0-rc.12": + resolution: + { + integrity: sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [darwin] + + "@rolldown/binding-darwin-x64@1.0.0-rc.12": + resolution: + { + integrity: sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [darwin] + + "@rolldown/binding-freebsd-x64@1.0.0-rc.12": + resolution: + { + integrity: sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [freebsd] + + "@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12": + resolution: + { + integrity: sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm] + os: [linux] + + "@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12": + resolution: + { + integrity: sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [linux] + libc: [glibc] + + "@rolldown/binding-linux-arm64-musl@1.0.0-rc.12": + resolution: + { + integrity: sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [linux] + libc: [musl] + + "@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12": + resolution: + { + integrity: sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [ppc64] + os: [linux] + libc: [glibc] + + "@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12": + resolution: + { + integrity: sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [s390x] + os: [linux] + libc: [glibc] + + "@rolldown/binding-linux-x64-gnu@1.0.0-rc.12": + resolution: + { + integrity: sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [linux] + libc: [glibc] + + "@rolldown/binding-linux-x64-musl@1.0.0-rc.12": + resolution: + { + integrity: sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [linux] + libc: [musl] + + "@rolldown/binding-openharmony-arm64@1.0.0-rc.12": + resolution: + { + integrity: sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [openharmony] + + "@rolldown/binding-wasm32-wasi@1.0.0-rc.12": + resolution: + { + integrity: sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==, + } + engines: { node: ">=14.0.0" } + cpu: [wasm32] + + "@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12": + resolution: + { + integrity: sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [win32] + + "@rolldown/binding-win32-x64-msvc@1.0.0-rc.12": + resolution: + { + integrity: sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [win32] + + "@rolldown/pluginutils@1.0.0-rc.12": + resolution: + { + integrity: sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==, + } + "@rollup/rollup-android-arm-eabi@4.60.1": resolution: { @@ -991,6 +1301,15 @@ packages: lit: ^2.0.0 || ^3.0.0 storybook: ^10.3.3 + "@stylistic/eslint-plugin@5.10.0": + resolution: + { + integrity: sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^9.0.0 || ^10.0.0 + "@testing-library/dom@10.4.0": resolution: { @@ -1065,6 +1384,12 @@ packages: "@tmcp/auth": optional: true + "@tybys/wasm-util@0.10.1": + resolution: + { + integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==, + } + "@types/aria-query@5.0.4": resolution: { @@ -1083,6 +1408,12 @@ packages: integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==, } + "@types/esrecurse@4.3.1": + resolution: + { + integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==, + } + "@types/estree@1.0.8": resolution: { @@ -1107,12 +1438,24 @@ packages: integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==, } + "@types/minimist@1.2.5": + resolution: + { + integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==, + } + "@types/node@25.5.0": resolution: { integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==, } + "@types/normalize-package-data@2.4.4": + resolution: + { + integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==, + } + "@types/parse5@2.2.34": resolution: { @@ -1143,61 +1486,61 @@ packages: integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==, } - "@typescript-eslint/eslint-plugin@8.57.2": + "@typescript-eslint/eslint-plugin@8.58.1": resolution: { - integrity: sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==, + integrity: sha512-eSkwoemjo76bdXl2MYqtxg51HNwUSkWfODUOQ3PaTLZGh9uIWWFZIjyjaJnex7wXDu+TRx+ATsnSxdN9YWfRTQ==, } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } peerDependencies: - "@typescript-eslint/parser": ^8.57.2 + "@typescript-eslint/parser": ^8.58.1 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.0.0" + typescript: ">=4.8.4 <6.1.0" - "@typescript-eslint/parser@8.57.2": + "@typescript-eslint/parser@8.58.1": resolution: { - integrity: sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==, + integrity: sha512-gGkiNMPqerb2cJSVcruigx9eHBlLG14fSdPdqMoOcBfh+vvn4iCq2C8MzUB89PrxOXk0y3GZ1yIWb9aOzL93bw==, } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.0.0" + typescript: ">=4.8.4 <6.1.0" - "@typescript-eslint/project-service@8.57.2": + "@typescript-eslint/project-service@8.58.1": resolution: { - integrity: sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==, + integrity: sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g==, } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } peerDependencies: - typescript: ">=4.8.4 <6.0.0" + typescript: ">=4.8.4 <6.1.0" - "@typescript-eslint/scope-manager@8.57.2": + "@typescript-eslint/scope-manager@8.58.1": resolution: { - integrity: sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==, + integrity: sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w==, } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - "@typescript-eslint/tsconfig-utils@8.57.2": + "@typescript-eslint/tsconfig-utils@8.58.1": resolution: { - integrity: sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==, + integrity: sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw==, } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } peerDependencies: - typescript: ">=4.8.4 <6.0.0" + typescript: ">=4.8.4 <6.1.0" - "@typescript-eslint/type-utils@8.57.2": + "@typescript-eslint/type-utils@8.58.1": resolution: { - integrity: sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==, + integrity: sha512-HUFxvTJVroT+0rXVJC7eD5zol6ID+Sn5npVPWoFuHGg9Ncq5Q4EYstqR+UOqaNRFXi5TYkpXXkLhoCHe3G0+7w==, } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.0.0" + typescript: ">=4.8.4 <6.1.0" "@typescript-eslint/types@8.57.2": resolution: @@ -1206,32 +1549,199 @@ packages: } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - "@typescript-eslint/typescript-estree@8.57.2": + "@typescript-eslint/types@8.58.1": resolution: { - integrity: sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==, + integrity: sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@typescript-eslint/typescript-estree@8.58.1": + resolution: + { + integrity: sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg==, } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } peerDependencies: - typescript: ">=4.8.4 <6.0.0" + typescript: ">=4.8.4 <6.1.0" - "@typescript-eslint/utils@8.57.2": + "@typescript-eslint/utils@8.58.1": resolution: { - integrity: sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==, + integrity: sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ==, } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.0.0" + typescript: ">=4.8.4 <6.1.0" - "@typescript-eslint/visitor-keys@8.57.2": + "@typescript-eslint/visitor-keys@8.58.1": resolution: { - integrity: sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==, + integrity: sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ==, } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + "@unrs/resolver-binding-android-arm-eabi@1.11.1": + resolution: + { + integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==, + } + cpu: [arm] + os: [android] + + "@unrs/resolver-binding-android-arm64@1.11.1": + resolution: + { + integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==, + } + cpu: [arm64] + os: [android] + + "@unrs/resolver-binding-darwin-arm64@1.11.1": + resolution: + { + integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==, + } + cpu: [arm64] + os: [darwin] + + "@unrs/resolver-binding-darwin-x64@1.11.1": + resolution: + { + integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==, + } + cpu: [x64] + os: [darwin] + + "@unrs/resolver-binding-freebsd-x64@1.11.1": + resolution: + { + integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==, + } + cpu: [x64] + os: [freebsd] + + "@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1": + resolution: + { + integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==, + } + cpu: [arm] + os: [linux] + + "@unrs/resolver-binding-linux-arm-musleabihf@1.11.1": + resolution: + { + integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==, + } + cpu: [arm] + os: [linux] + + "@unrs/resolver-binding-linux-arm64-gnu@1.11.1": + resolution: + { + integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==, + } + cpu: [arm64] + os: [linux] + libc: [glibc] + + "@unrs/resolver-binding-linux-arm64-musl@1.11.1": + resolution: + { + integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==, + } + cpu: [arm64] + os: [linux] + libc: [musl] + + "@unrs/resolver-binding-linux-ppc64-gnu@1.11.1": + resolution: + { + integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==, + } + cpu: [ppc64] + os: [linux] + libc: [glibc] + + "@unrs/resolver-binding-linux-riscv64-gnu@1.11.1": + resolution: + { + integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==, + } + cpu: [riscv64] + os: [linux] + libc: [glibc] + + "@unrs/resolver-binding-linux-riscv64-musl@1.11.1": + resolution: + { + integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==, + } + cpu: [riscv64] + os: [linux] + libc: [musl] + + "@unrs/resolver-binding-linux-s390x-gnu@1.11.1": + resolution: + { + integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==, + } + cpu: [s390x] + os: [linux] + libc: [glibc] + + "@unrs/resolver-binding-linux-x64-gnu@1.11.1": + resolution: + { + integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==, + } + cpu: [x64] + os: [linux] + libc: [glibc] + + "@unrs/resolver-binding-linux-x64-musl@1.11.1": + resolution: + { + integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==, + } + cpu: [x64] + os: [linux] + libc: [musl] + + "@unrs/resolver-binding-wasm32-wasi@1.11.1": + resolution: + { + integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==, + } + engines: { node: ">=14.0.0" } + cpu: [wasm32] + + "@unrs/resolver-binding-win32-arm64-msvc@1.11.1": + resolution: + { + integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==, + } + cpu: [arm64] + os: [win32] + + "@unrs/resolver-binding-win32-ia32-msvc@1.11.1": + resolution: + { + integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==, + } + cpu: [ia32] + os: [win32] + + "@unrs/resolver-binding-win32-x64-msvc@1.11.1": + resolution: + { + integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==, + } + cpu: [x64] + os: [win32] + "@valibot/to-json-schema@1.6.0": resolution: { @@ -1380,6 +1890,20 @@ packages: engines: { node: ">=0.4.0" } hasBin: true + ajv-errors@1.0.1: + resolution: + { + integrity: sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==, + } + peerDependencies: + ajv: ">=5.0.0" + + ajv@6.12.6: + resolution: + { + integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==, + } + ajv@6.14.0: resolution: { @@ -1454,6 +1978,20 @@ packages: } engines: { node: ">= 0.4" } + array-union@2.1.0: + resolution: + { + integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==, + } + engines: { node: ">=8" } + + array.prototype.findlast@1.2.5: + resolution: + { + integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==, + } + engines: { node: ">= 0.4" } + array.prototype.findlastindex@1.2.6: resolution: { @@ -1475,6 +2013,13 @@ packages: } engines: { node: ">= 0.4" } + array.prototype.tosorted@1.1.4: + resolution: + { + integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==, + } + engines: { node: ">= 0.4" } + arraybuffer.prototype.slice@1.0.4: resolution: { @@ -1482,6 +2027,13 @@ packages: } engines: { node: ">= 0.4" } + arrify@1.0.1: + resolution: + { + integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==, + } + engines: { node: ">=0.10.0" } + assertion-error@2.0.1: resolution: { @@ -1489,6 +2041,12 @@ packages: } engines: { node: ">=12" } + ast-types-flow@0.0.8: + resolution: + { + integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==, + } + ast-types@0.16.1: resolution: { @@ -1523,6 +2081,13 @@ packages: } engines: { node: ">=4" } + axobject-query@4.1.0: + resolution: + { + integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==, + } + engines: { node: ">= 0.4" } + balanced-match@1.0.2: resolution: { @@ -1536,6 +2101,14 @@ packages: } engines: { node: 18 || 20 || >=22 } + baseline-browser-mapping@2.10.16: + resolution: + { + integrity: sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==, + } + engines: { node: ">=6.0.0" } + hasBin: true + brace-expansion@1.1.13: resolution: { @@ -1555,6 +2128,21 @@ packages: } engines: { node: 18 || 20 || >=22 } + braces@3.0.3: + resolution: + { + integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, + } + engines: { node: ">=8" } + + browserslist@4.28.2: + resolution: + { + integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==, + } + engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } + hasBin: true + bundle-name@4.1.0: resolution: { @@ -1597,6 +2185,26 @@ packages: } engines: { node: ">=6" } + camelcase-keys@6.2.2: + resolution: + { + integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==, + } + engines: { node: ">=8" } + + camelcase@5.3.1: + resolution: + { + integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==, + } + engines: { node: ">=6" } + + caniuse-lite@1.0.30001787: + resolution: + { + integrity: sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==, + } + chai@5.3.3: resolution: { @@ -1675,6 +2283,13 @@ packages: integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, } + comment-parser@1.4.6: + resolution: + { + integrity: sha512-ObxuY6vnbWTN6Od72xfwN9DbzC7Y2vv8u1Soi9ahRKL37gb6y1qk6/dgjs+3JWuXJHWvsg3BXIwzd/rkmAwavg==, + } + engines: { node: ">= 12.0.0" } + concat-map@0.0.1: resolution: { @@ -1687,6 +2302,24 @@ packages: integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==, } + convert-source-map@2.0.0: + resolution: + { + integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, + } + + cosmiconfig@8.3.6: + resolution: + { + integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==, + } + engines: { node: ">=14" } + peerDependencies: + typescript: ">=4.9.5" + peerDependenciesMeta: + typescript: + optional: true + cross-spawn@7.0.6: resolution: { @@ -1706,6 +2339,12 @@ packages: integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==, } + damerau-levenshtein@1.0.8: + resolution: + { + integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==, + } + data-view-buffer@1.0.2: resolution: { @@ -1750,6 +2389,20 @@ packages: supports-color: optional: true + decamelize-keys@1.1.1: + resolution: + { + integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==, + } + engines: { node: ">=0.10.0" } + + decamelize@1.2.0: + resolution: + { + integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==, + } + engines: { node: ">=0.10.0" } + deep-eql@5.0.2: resolution: { @@ -1805,6 +2458,20 @@ packages: } engines: { node: ">=6" } + detect-libc@2.1.2: + resolution: + { + integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==, + } + engines: { node: ">=8" } + + dir-glob@3.0.1: + resolution: + { + integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==, + } + engines: { node: ">=8" } + doctrine@2.1.0: resolution: { @@ -1843,6 +2510,12 @@ packages: integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==, } + electron-to-chromium@1.5.334: + resolution: + { + integrity: sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog==, + } + emoji-regex@10.6.0: resolution: { @@ -1861,6 +2534,13 @@ packages: integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==, } + enhanced-resolve@5.20.1: + resolution: + { + integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==, + } + engines: { node: ">=10.13.0" } + entities@6.0.1: resolution: { @@ -1875,6 +2555,12 @@ packages: } engines: { node: ">=0.12" } + error-ex@1.3.4: + resolution: + { + integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==, + } + es-abstract@1.24.1: resolution: { @@ -1896,6 +2582,13 @@ packages: } engines: { node: ">= 0.4" } + es-iterator-helpers@1.3.1: + resolution: + { + integrity: sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==, + } + engines: { node: ">= 0.4" } + es-module-lexer@1.7.0: resolution: { @@ -1938,6 +2631,13 @@ packages: engines: { node: ">=18" } hasBin: true + escalade@3.2.0: + resolution: + { + integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, + } + engines: { node: ">=6" } + escape-string-regexp@4.0.0: resolution: { @@ -1945,15 +2645,23 @@ packages: } engines: { node: ">=10" } - eslint-config-airbnb-base@15.0.0: + eslint-compat-utils@0.5.1: resolution: { - integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==, + integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==, } - engines: { node: ^10.12.0 || >=12.0.0 } + engines: { node: ">=12" } peerDependencies: - eslint: ^7.32.0 || ^8.2.0 - eslint-plugin-import: ^2.25.2 + eslint: ">=6.0.0" + + eslint-config-airbnb-extended@3.1.0: + resolution: + { + integrity: sha512-uUE5+8gQ9h+QqHOI0OIBIQW0+/bA4l/GE1i5fPAGk64Y5lBKxyZRNDcQUGSaovOzX7yMa0Q+3MS1lj5bq/BC/Q==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^9.0.0 eslint-config-prettier@10.1.8: resolution: @@ -1964,12 +2672,40 @@ packages: peerDependencies: eslint: ">=7.0.0" + eslint-import-context@0.1.9: + resolution: + { + integrity: sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==, + } + engines: { node: ^12.20.0 || ^14.18.0 || >=16.0.0 } + peerDependencies: + unrs-resolver: ^1.0.0 + peerDependenciesMeta: + unrs-resolver: + optional: true + eslint-import-resolver-node@0.3.9: resolution: { integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==, } + eslint-import-resolver-typescript@4.4.4: + resolution: + { + integrity: sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw==, + } + engines: { node: ^16.17.0 || >=18.6.0 } + peerDependencies: + eslint: "*" + eslint-plugin-import: "*" + eslint-plugin-import-x: "*" + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + eslint-module-utils@2.12.1: resolution: { @@ -1994,6 +2730,31 @@ packages: eslint-import-resolver-webpack: optional: true + eslint-plugin-es-x@7.8.0: + resolution: + { + integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==, + } + engines: { node: ^14.18.0 || >=16.0.0 } + peerDependencies: + eslint: ">=8" + + eslint-plugin-import-x@4.16.2: + resolution: + { + integrity: sha512-rM9K8UBHcWKpzQzStn1YRN2T5NvdeIfSVoKu/lKF41znQXHAUcBbYXe5wd6GNjZjTrP7viQ49n1D83x/2gYgIw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + "@typescript-eslint/utils": ^8.56.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + eslint-import-resolver-node: "*" + peerDependenciesMeta: + "@typescript-eslint/utils": + optional: true + eslint-import-resolver-node: + optional: true + eslint-plugin-import@2.32.0: resolution: { @@ -2007,6 +2768,15 @@ packages: "@typescript-eslint/parser": optional: true + eslint-plugin-jsx-a11y@6.10.2: + resolution: + { + integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==, + } + engines: { node: ">=4.0" } + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + eslint-plugin-lit-a11y@5.1.1: resolution: { @@ -2024,6 +2794,33 @@ packages: peerDependencies: eslint: ">= 8" + eslint-plugin-n@17.24.0: + resolution: + { + integrity: sha512-/gC7/KAYmfNnPNOb3eu8vw+TdVnV0zhdQwexsw6FLXbhzroVj20vRn2qL8lDWDGnAQ2J8DhdfvXxX9EoxvERvw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ">=8.23.0" + + eslint-plugin-react-hooks@7.0.1: + resolution: + { + integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==, + } + engines: { node: ">=18" } + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react@7.37.5: + resolution: + { + integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==, + } + engines: { node: ">=4" } + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + eslint-plugin-unused-imports@4.4.1: resolution: { @@ -2036,10 +2833,10 @@ packages: "@typescript-eslint/eslint-plugin": optional: true - eslint-plugin-wc@2.2.1: + eslint-plugin-wc@3.1.0: resolution: { - integrity: sha512-KstLqGmyQz088DvFlDYHg0sHih+w2QeulreCi1D1ftr357klO2zqHdG/bbnNMmuQdVFDuNkopNIyNhmG0XCT/g==, + integrity: sha512-spvXHD2/GTTgYXxFB3xlMThnXGUeNJaiCwWuPGzjDOLXnVGLcQpDt0fyiN6yiLoaLs/yhsj+7G1FpBZKeigCSA==, } peerDependencies: eslint: ">=8.40.0" @@ -2051,12 +2848,12 @@ packages: } engines: { node: ">=10" } - eslint-scope@8.4.0: + eslint-scope@9.1.2: resolution: { - integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==, + integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==, } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } eslint-visitor-keys@3.4.3: resolution: @@ -2079,12 +2876,12 @@ packages: } engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - eslint@9.39.4: + eslint@10.2.0: resolution: { - integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==, + integrity: sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==, } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } hasBin: true peerDependencies: jiti: "*" @@ -2105,6 +2902,13 @@ packages: } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + espree@11.2.0: + resolution: + { + integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + esprima@4.0.1: resolution: { @@ -2160,6 +2964,20 @@ packages: integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, } + fast-glob@3.3.1: + resolution: + { + integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==, + } + engines: { node: ">=8.6.0" } + + fast-glob@3.3.3: + resolution: + { + integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==, + } + engines: { node: ">=8.6.0" } + fast-json-stable-stringify@2.1.0: resolution: { @@ -2172,6 +2990,12 @@ packages: integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, } + fastq@1.20.1: + resolution: + { + integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==, + } + fdir@6.5.0: resolution: { @@ -2204,6 +3028,20 @@ packages: } engines: { node: ">= 10.4.0" } + fill-range@7.1.1: + resolution: + { + integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, + } + engines: { node: ">=8" } + + find-up@4.1.0: + resolution: + { + integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==, + } + engines: { node: ">=8" } + find-up@5.0.0: resolution: { @@ -2280,6 +3118,13 @@ packages: } engines: { node: ">= 0.4" } + gensync@1.0.0-beta.2: + resolution: + { + integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==, + } + engines: { node: ">=6.9.0" } + get-intrinsic@1.3.0: resolution: { @@ -2301,6 +3146,19 @@ packages: } engines: { node: ">= 0.4" } + get-tsconfig@4.13.7: + resolution: + { + integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==, + } + + glob-parent@5.1.2: + resolution: + { + integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, + } + engines: { node: ">= 6" } + glob-parent@6.0.2: resolution: { @@ -2316,17 +3174,17 @@ packages: deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true - globals@14.0.0: + globals@15.15.0: resolution: { - integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==, + integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==, } engines: { node: ">=18" } - globals@16.5.0: + globals@17.4.0: resolution: { - integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==, + integrity: sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==, } engines: { node: ">=18" } @@ -2337,6 +3195,19 @@ packages: } engines: { node: ">= 0.4" } + globby@11.1.0: + resolution: + { + integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==, + } + engines: { node: ">=10" } + + globrex@0.1.2: + resolution: + { + integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==, + } + gopd@1.2.0: resolution: { @@ -2357,6 +3228,13 @@ packages: } engines: { node: ">=20.0.0" } + hard-rejection@2.1.0: + resolution: + { + integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==, + } + engines: { node: ">=6" } + has-bigints@1.1.0: resolution: { @@ -2405,6 +3283,31 @@ packages: } engines: { node: ">= 0.4" } + hermes-estree@0.25.1: + resolution: + { + integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==, + } + + hermes-parser@0.25.1: + resolution: + { + integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==, + } + + hosted-git-info@2.8.9: + resolution: + { + integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==, + } + + hosted-git-info@4.1.0: + resolution: + { + integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==, + } + engines: { node: ">=10" } + html-escaper@2.0.2: resolution: { @@ -2453,6 +3356,13 @@ packages: } engines: { node: ">= 0.4" } + irregular-plurals@3.5.0: + resolution: + { + integrity: sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==, + } + engines: { node: ">=8" } + is-array-buffer@3.0.5: resolution: { @@ -2460,6 +3370,12 @@ packages: } engines: { node: ">= 0.4" } + is-arrayish@0.2.1: + resolution: + { + integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==, + } + is-async-function@2.1.1: resolution: { @@ -2481,6 +3397,12 @@ packages: } engines: { node: ">= 0.4" } + is-bun-module@2.0.0: + resolution: + { + integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==, + } + is-callable@1.2.7: resolution: { @@ -2581,6 +3503,27 @@ packages: } engines: { node: ">= 0.4" } + is-number@7.0.0: + resolution: + { + integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, + } + engines: { node: ">=0.12.0" } + + is-plain-obj@1.1.0: + resolution: + { + integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==, + } + engines: { node: ">=0.10.0" } + + is-plain-obj@3.0.0: + resolution: + { + integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==, + } + engines: { node: ">=10" } + is-potential-custom-element-name@1.0.1: resolution: { @@ -2629,6 +3572,13 @@ packages: } engines: { node: ">= 0.4" } + is-unicode-supported@0.1.0: + resolution: + { + integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==, + } + engines: { node: ">=10" } + is-valid-element-name@1.0.0: resolution: { @@ -2703,16 +3653,23 @@ packages: } engines: { node: ">=8" } + iterator.prototype@1.1.5: + resolution: + { + integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==, + } + engines: { node: ">= 0.4" } + jackspeak@3.4.3: resolution: { integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==, } - js-levenshtein-esm@1.2.0: + js-levenshtein-esm@2.0.0: resolution: { - integrity: sha512-fzreKVq1eD7eGcQr7MtRpQH94f8gIfhdrc7yeih38xh684TNMK9v5aAu2wxfIRMk/GpAJRrzcirMAPIaSDaByQ==, + integrity: sha512-1n4LEPOL4wRXY8rOQcuA7Iuaphe5xCMayvufCzlLAi+hRsnBRDbSS6XPuV58CBVJxj5D9ApFLyjQ7KzFToyHBw==, } js-tokens@10.0.0: @@ -2740,12 +3697,26 @@ packages: } hasBin: true + jsesc@3.1.0: + resolution: + { + integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==, + } + engines: { node: ">=6" } + hasBin: true + json-buffer@3.0.1: resolution: { integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==, } + json-parse-even-better-errors@2.3.1: + resolution: + { + integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==, + } + json-rpc-2.0@1.7.1: resolution: { @@ -2771,18 +3742,46 @@ packages: } hasBin: true + json5@2.2.3: + resolution: + { + integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==, + } + engines: { node: ">=6" } + hasBin: true + + jsonc-parser@3.3.1: + resolution: + { + integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==, + } + jsonfile@6.2.0: resolution: { integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==, } + jsx-ast-utils@3.3.5: + resolution: + { + integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==, + } + engines: { node: ">=4.0" } + keyv@4.5.4: resolution: { integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, } + kind-of@6.0.3: + resolution: + { + integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==, + } + engines: { node: ">=0.10.0" } + language-subtag-registry@0.3.23: resolution: { @@ -2803,6 +3802,122 @@ packages: } engines: { node: ">= 0.8.0" } + lightningcss-android-arm64@1.32.0: + resolution: + { + integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: + { + integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: + { + integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: + { + integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: + { + integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: + { + integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: + { + integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: + { + integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: + { + integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: + { + integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: + { + integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: + { + integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==, + } + engines: { node: ">= 12.0.0" } + + lines-and-columns@1.2.4: + resolution: + { + integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==, + } + lit-element@4.2.2: resolution: { @@ -2821,6 +3936,13 @@ packages: integrity: sha512-NF9zbsP79l4ao2SNrH3NkfmFgN/hBYSQo90saIVI1o5GpjAdCPVstVzO1MrLOakHoEhYkrtRjPK6Ob521aoYWQ==, } + locate-path@5.0.0: + resolution: + { + integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==, + } + engines: { node: ">=8" } + locate-path@6.0.0: resolution: { @@ -2828,17 +3950,26 @@ packages: } engines: { node: ">=10" } - lodash.merge@4.6.2: + lodash@4.18.0: resolution: { - integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, + integrity: sha512-l1mfj2atMqndAHI3ls7XqPxEjV2J9ZkcNyHpoZA3r2T1LLwDB69jgkMWh71YKwhBbK0G2f4WSn05ahmQXVxupA==, } + deprecated: Bad release. Please use lodash@4.17.21 instead. - lodash@4.17.23: + log-symbols@4.1.0: resolution: { - integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==, + integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==, } + engines: { node: ">=10" } + + loose-envify@1.4.0: + resolution: + { + integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==, + } + hasBin: true loupe@3.2.1: resolution: @@ -2852,6 +3983,19 @@ packages: integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==, } + lru-cache@5.1.1: + resolution: + { + integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, + } + + lru-cache@6.0.0: + resolution: + { + integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==, + } + engines: { node: ">=10" } + lz-string@1.5.0: resolution: { @@ -2878,6 +4022,20 @@ packages: } engines: { node: ">=10" } + map-obj@1.0.1: + resolution: + { + integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==, + } + engines: { node: ">=0.10.0" } + + map-obj@4.3.0: + resolution: + { + integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==, + } + engines: { node: ">=8" } + math-intrinsics@1.1.0: resolution: { @@ -2885,6 +4043,27 @@ packages: } engines: { node: ">= 0.4" } + meow@9.0.0: + resolution: + { + integrity: sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==, + } + engines: { node: ">=10" } + + merge2@1.4.1: + resolution: + { + integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, + } + engines: { node: ">= 8" } + + micromatch@4.0.8: + resolution: + { + integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==, + } + engines: { node: ">=8.6" } + min-indent@1.0.1: resolution: { @@ -2912,6 +4091,13 @@ packages: } engines: { node: ">=16 || 14 >=14.17" } + minimist-options@4.1.0: + resolution: + { + integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==, + } + engines: { node: ">= 6" } + minimist@1.2.8: resolution: { @@ -2946,12 +4132,61 @@ packages: engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } hasBin: true + napi-postinstall@0.3.4: + resolution: + { + integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==, + } + engines: { node: ^12.20.0 || ^14.18.0 || >=16.0.0 } + hasBin: true + natural-compare@1.4.0: resolution: { integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, } + node-exports-info@1.6.0: + resolution: + { + integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==, + } + engines: { node: ">= 0.4" } + + node-releases@2.0.37: + resolution: + { + integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==, + } + + normalize-package-data@2.5.0: + resolution: + { + integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==, + } + + normalize-package-data@3.0.3: + resolution: + { + integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==, + } + engines: { node: ">=10" } + + npm-package-json-lint@10.2.0: + resolution: + { + integrity: sha512-p0fKe65OyXsJslOxGIcnqSo3tyMddH0n3X8AI7Ab0BSxVPc2eZ0ey1z6/L9Rh59k+BW9mHJNK9LKr4k+pA8Ojg==, + } + engines: { node: ">=22.0.0", npm: ">=10.0.0" } + hasBin: true + + object-assign@4.1.1: + resolution: + { + integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, + } + engines: { node: ">=0.10.0" } + object-inspect@1.13.4: resolution: { @@ -3022,6 +4257,13 @@ packages: } engines: { node: ">= 0.4" } + p-limit@2.3.0: + resolution: + { + integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==, + } + engines: { node: ">=6" } + p-limit@3.1.0: resolution: { @@ -3029,6 +4271,13 @@ packages: } engines: { node: ">=10" } + p-locate@4.1.0: + resolution: + { + integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==, + } + engines: { node: ">=8" } + p-locate@5.0.0: resolution: { @@ -3036,6 +4285,13 @@ packages: } engines: { node: ">=10" } + p-try@2.2.0: + resolution: + { + integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==, + } + engines: { node: ">=6" } + package-json-from-dist@1.0.1: resolution: { @@ -3049,6 +4305,13 @@ packages: } engines: { node: ">=6" } + parse-json@5.2.0: + resolution: + { + integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==, + } + engines: { node: ">=8" } + parse5-htmlparser2-tree-adapter@6.0.1: resolution: { @@ -3100,6 +4363,13 @@ packages: } engines: { node: ">=16 || 14 >=14.18" } + path-type@4.0.0: + resolution: + { + integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==, + } + engines: { node: ">=8" } + pathe@2.0.3: resolution: { @@ -3119,6 +4389,13 @@ packages: integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, } + picomatch@2.3.2: + resolution: + { + integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==, + } + engines: { node: ">=8.6" } + picomatch@4.0.4: resolution: { @@ -3148,6 +4425,13 @@ packages: engines: { node: ">=18" } hasBin: true + plur@4.0.0: + resolution: + { + integrity: sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==, + } + engines: { node: ">=10" } + possible-typed-array-names@1.1.0: resolution: { @@ -3184,6 +4468,12 @@ packages: } engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + prop-types@15.8.1: + resolution: + { + integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==, + } + punycode@2.3.1: resolution: { @@ -3191,6 +4481,19 @@ packages: } engines: { node: ">=6" } + queue-microtask@1.2.3: + resolution: + { + integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, + } + + quick-lru@4.0.1: + resolution: + { + integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==, + } + engines: { node: ">=8" } + react-dom@19.2.4: resolution: { @@ -3199,6 +4502,12 @@ packages: peerDependencies: react: ^19.2.4 + react-is@16.13.1: + resolution: + { + integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==, + } + react-is@17.0.2: resolution: { @@ -3212,6 +4521,20 @@ packages: } engines: { node: ">=0.10.0" } + read-pkg-up@7.0.1: + resolution: + { + integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==, + } + engines: { node: ">=8" } + + read-pkg@5.2.0: + resolution: + { + integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==, + } + engines: { node: ">=8" } + recast@0.23.11: resolution: { @@ -3247,12 +4570,41 @@ packages: } engines: { node: ">=4" } - resolve@1.22.11: + resolve-pkg-maps@1.0.0: + resolution: + { + integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, + } + + resolve@1.22.11: + resolution: + { + integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==, + } + engines: { node: ">= 0.4" } + hasBin: true + + resolve@2.0.0-next.6: + resolution: + { + integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==, + } + engines: { node: ">= 0.4" } + hasBin: true + + reusify@1.1.0: + resolution: + { + integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==, + } + engines: { iojs: ">=1.0.0", node: ">=0.10.0" } + + rolldown@1.0.0-rc.12: resolution: { - integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==, + integrity: sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==, } - engines: { node: ">= 0.4" } + engines: { node: ^20.19.0 || >=22.12.0 } hasBin: true rollup@4.60.1: @@ -3270,6 +4622,12 @@ packages: } engines: { node: ">=18" } + run-parallel@1.2.0: + resolution: + { + integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, + } + safe-array-concat@1.1.3: resolution: { @@ -3297,6 +4655,13 @@ packages: integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==, } + semver@5.7.2: + resolution: + { + integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==, + } + hasBin: true + semver@6.3.1: resolution: { @@ -3395,6 +4760,13 @@ packages: } engines: { node: ">=18" } + slash@3.0.0: + resolution: + { + integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==, + } + engines: { node: ">=8" } + source-map-js@1.2.1: resolution: { @@ -3409,12 +4781,43 @@ packages: } engines: { node: ">=0.10.0" } + spdx-correct@3.2.0: + resolution: + { + integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==, + } + + spdx-exceptions@2.5.0: + resolution: + { + integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==, + } + + spdx-expression-parse@3.0.1: + resolution: + { + integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==, + } + + spdx-license-ids@3.0.23: + resolution: + { + integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==, + } + sqids@0.3.0: resolution: { integrity: sha512-lOQK1ucVg+W6n3FhRwwSeUijxe93b51Bfz5PMRMihVf1iVkl82ePQG7V5vwrhzB11v0NtsR25PSZRGiSomJaJw==, } + stable-hash-x@0.2.0: + resolution: + { + integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==, + } + engines: { node: ">=12.0.0" } + stackback@0.0.2: resolution: { @@ -3460,6 +4863,26 @@ packages: } engines: { node: ">=12" } + string.prototype.includes@2.0.1: + resolution: + { + integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==, + } + engines: { node: ">= 0.4" } + + string.prototype.matchall@4.0.12: + resolution: + { + integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==, + } + engines: { node: ">= 0.4" } + + string.prototype.repeat@1.0.0: + resolution: + { + integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==, + } + string.prototype.trim@1.2.10: resolution: { @@ -3536,6 +4959,20 @@ packages: } engines: { node: ">= 0.4" } + tagged-tag@1.0.0: + resolution: + { + integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==, + } + engines: { node: ">=20" } + + tapable@2.3.2: + resolution: + { + integrity: sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==, + } + engines: { node: ">=6" } + test-exclude@7.0.2: resolution: { @@ -3609,6 +5046,13 @@ packages: integrity: sha512-plz/TLKNFrdfQN32LjCTN6ULy6pynfGPgHcU7KGCI5dBrxQ9Mub99SmcYuzxEkLjJooQuOD3gosSwZEl1htOtw==, } + to-regex-range@5.0.1: + resolution: + { + integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, + } + engines: { node: ">=8.0" } + totalist@3.0.1: resolution: { @@ -3616,6 +5060,13 @@ packages: } engines: { node: ">=6" } + trim-newlines@3.0.1: + resolution: + { + integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==, + } + engines: { node: ">=8" } + ts-api-utils@2.5.0: resolution: { @@ -3625,6 +5076,14 @@ packages: peerDependencies: typescript: ">=4.8.4" + ts-declaration-location@1.0.7: + resolution: + { + integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==, + } + peerDependencies: + typescript: ">=4.0.0" + ts-dedent@2.2.0: resolution: { @@ -3651,6 +5110,34 @@ packages: } engines: { node: ">= 0.8.0" } + type-fest@0.18.1: + resolution: + { + integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==, + } + engines: { node: ">=10" } + + type-fest@0.6.0: + resolution: + { + integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==, + } + engines: { node: ">=8" } + + type-fest@0.8.1: + resolution: + { + integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==, + } + engines: { node: ">=8" } + + type-fest@5.3.1: + resolution: + { + integrity: sha512-VCn+LMHbd4t6sF3wfU/+HKT63C9OoyrSIf4b+vtWHpt2U7/4InZG467YDNMFMR70DdHjAdpPWmw2lzRdg0Xqqg==, + } + engines: { node: ">=20" } + typed-array-buffer@1.0.3: resolution: { @@ -3679,15 +5166,15 @@ packages: } engines: { node: ">= 0.4" } - typescript-eslint@8.57.2: + typescript-eslint@8.58.1: resolution: { - integrity: sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==, + integrity: sha512-gf6/oHChByg9HJvhMO1iBexJh12AqqTfnuxscMDOVqfJW3htsdRJI/GfPpHTTcyeB8cSTUY2JcZmVgoyPqcrDg==, } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.0.0" + typescript: ">=4.8.4 <6.1.0" typescript@5.9.3: resolution: @@ -3724,6 +5211,21 @@ packages: } engines: { node: ">=18.12.0" } + unrs-resolver@1.11.1: + resolution: + { + integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==, + } + + update-browserslist-db@1.2.3: + resolution: + { + integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==, + } + hasBin: true + peerDependencies: + browserslist: ">= 4.21.0" + uri-js@4.4.1: resolution: { @@ -3755,6 +5257,19 @@ packages: typescript: optional: true + validate-npm-package-license@3.0.4: + resolution: + { + integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==, + } + + validate-npm-package-name@6.0.0: + resolution: + { + integrity: sha512-d7KLgL1LD3U3fgnvWEY1cQXoO/q6EQ1BSz48Sa149V/5zVTAbgmZIpyI8TRi6U9/JNyeYLlTKsEMPtLC27RFUg==, + } + engines: { node: ^18.17.0 || >=20.5.0 } + vite-node@3.2.4: resolution: { @@ -3806,6 +5321,52 @@ packages: yaml: optional: true + vite@8.0.5: + resolution: + { + integrity: sha512-nmu43Qvq9UopTRfMx2jOYW5l16pb3iDC1JH6yMuPkpVbzK0k+L7dfsEDH4jRgYFmsg0sTAqkojoZgzLMlwHsCQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + hasBin: true + peerDependencies: + "@types/node": ^20.19.0 || >=22.12.0 + "@vitejs/devtools": ^0.1.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: ">=1.21.0" + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: ">=0.54.8" + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + "@types/node": + optional: true + "@vitejs/devtools": + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vitest@3.2.4: resolution: { @@ -3937,6 +5498,25 @@ packages: } engines: { node: ">=18" } + yallist@3.1.1: + resolution: + { + integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==, + } + + yallist@4.0.0: + resolution: + { + integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==, + } + + yargs-parser@20.2.9: + resolution: + { + integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==, + } + engines: { node: ">=10" } + yocto-queue@0.1.0: resolution: { @@ -3944,6 +5524,21 @@ packages: } engines: { node: ">=10" } + zod-validation-error@4.0.2: + resolution: + { + integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==, + } + engines: { node: ">=18.0.0" } + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.3.6: + resolution: + { + integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==, + } + snapshots: "@adobe/css-tools@4.4.4": {} @@ -3958,16 +5553,97 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + "@babel/compat-data@7.29.0": {} + + "@babel/core@7.29.0": + dependencies: + "@babel/code-frame": 7.29.0 + "@babel/generator": 7.29.1 + "@babel/helper-compilation-targets": 7.28.6 + "@babel/helper-module-transforms": 7.28.6(@babel/core@7.29.0) + "@babel/helpers": 7.29.2 + "@babel/parser": 7.29.2 + "@babel/template": 7.28.6 + "@babel/traverse": 7.29.0 + "@babel/types": 7.29.0 + "@jridgewell/remapping": 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + "@babel/generator@7.29.1": + dependencies: + "@babel/parser": 7.29.2 + "@babel/types": 7.29.0 + "@jridgewell/gen-mapping": 0.3.13 + "@jridgewell/trace-mapping": 0.3.31 + jsesc: 3.1.0 + + "@babel/helper-compilation-targets@7.28.6": + dependencies: + "@babel/compat-data": 7.29.0 + "@babel/helper-validator-option": 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + "@babel/helper-globals@7.28.0": {} + + "@babel/helper-module-imports@7.28.6": + dependencies: + "@babel/traverse": 7.29.0 + "@babel/types": 7.29.0 + transitivePeerDependencies: + - supports-color + + "@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)": + dependencies: + "@babel/core": 7.29.0 + "@babel/helper-module-imports": 7.28.6 + "@babel/helper-validator-identifier": 7.28.5 + "@babel/traverse": 7.29.0 + transitivePeerDependencies: + - supports-color + "@babel/helper-string-parser@7.27.1": {} "@babel/helper-validator-identifier@7.28.5": {} + "@babel/helper-validator-option@7.27.1": {} + + "@babel/helpers@7.29.2": + dependencies: + "@babel/template": 7.28.6 + "@babel/types": 7.29.0 + "@babel/parser@7.29.2": dependencies: "@babel/types": 7.29.0 "@babel/runtime@7.29.2": {} + "@babel/template@7.28.6": + dependencies: + "@babel/code-frame": 7.29.0 + "@babel/parser": 7.29.2 + "@babel/types": 7.29.0 + + "@babel/traverse@7.29.0": + dependencies: + "@babel/code-frame": 7.29.0 + "@babel/generator": 7.29.1 + "@babel/helper-globals": 7.28.0 + "@babel/parser": 7.29.2 + "@babel/template": 7.28.6 + "@babel/types": 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + "@babel/types@7.29.0": dependencies: "@babel/helper-string-parser": 7.27.1 @@ -3987,6 +5663,22 @@ snapshots: - "@chromatic-com/cypress" - "@chromatic-com/playwright" + "@emnapi/core@1.9.2": + dependencies: + "@emnapi/wasi-threads": 1.2.1 + tslib: 2.8.1 + optional: true + + "@emnapi/runtime@1.9.2": + dependencies: + tslib: 2.8.1 + optional: true + + "@emnapi/wasi-threads@1.2.1": + dependencies: + tslib: 2.8.1 + optional: true + "@esbuild/aix-ppc64@0.27.4": optional: true @@ -4065,50 +5757,44 @@ snapshots: "@esbuild/win32-x64@0.27.4": optional: true - "@eslint-community/eslint-utils@4.9.1(eslint@9.39.4)": + "@eslint-community/eslint-utils@4.9.1(eslint@10.2.0)": dependencies: - eslint: 9.39.4 + eslint: 10.2.0 eslint-visitor-keys: 3.4.3 "@eslint-community/regexpp@4.12.2": {} - "@eslint/config-array@0.21.2": - dependencies: - "@eslint/object-schema": 2.1.7 - debug: 4.4.3 - minimatch: 3.1.5 - transitivePeerDependencies: - - supports-color - - "@eslint/config-helpers@0.4.2": - dependencies: - "@eslint/core": 0.17.0 - - "@eslint/core@0.17.0": + "@eslint/compat@2.0.5(eslint@10.2.0)": dependencies: - "@types/json-schema": 7.0.15 + "@eslint/core": 1.2.1 + optionalDependencies: + eslint: 10.2.0 - "@eslint/eslintrc@3.3.5": + "@eslint/config-array@0.23.5": dependencies: - ajv: 6.14.0 + "@eslint/object-schema": 3.0.5 debug: 4.4.3 - espree: 10.4.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - minimatch: 3.1.5 - strip-json-comments: 3.1.1 + minimatch: 10.2.4 transitivePeerDependencies: - supports-color - "@eslint/js@9.39.4": {} + "@eslint/config-helpers@0.5.5": + dependencies: + "@eslint/core": 1.2.1 + + "@eslint/core@1.2.1": + dependencies: + "@types/json-schema": 7.0.15 + + "@eslint/js@10.0.1(eslint@10.2.0)": + optionalDependencies: + eslint: 10.2.0 - "@eslint/object-schema@2.1.7": {} + "@eslint/object-schema@3.0.5": {} - "@eslint/plugin-kit@0.4.1": + "@eslint/plugin-kit@0.7.1": dependencies: - "@eslint/core": 0.17.0 + "@eslint/core": 1.2.1 levn: 0.4.1 "@humanfs/core@0.19.1": {} @@ -4176,13 +5862,99 @@ snapshots: "@types/react": 19.2.14 react: 19.2.4 + "@napi-rs/wasm-runtime@0.2.12": + dependencies: + "@emnapi/core": 1.9.2 + "@emnapi/runtime": 1.9.2 + "@tybys/wasm-util": 0.10.1 + optional: true + + "@napi-rs/wasm-runtime@1.1.3(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)": + dependencies: + "@emnapi/core": 1.9.2 + "@emnapi/runtime": 1.9.2 + "@tybys/wasm-util": 0.10.1 + optional: true + "@neoconfetti/react@1.0.0": {} + "@next/eslint-plugin-next@16.2.2": + dependencies: + fast-glob: 3.3.1 + + "@nodelib/fs.scandir@2.1.5": + dependencies: + "@nodelib/fs.stat": 2.0.5 + run-parallel: 1.2.0 + + "@nodelib/fs.stat@2.0.5": {} + + "@nodelib/fs.walk@1.2.8": + dependencies: + "@nodelib/fs.scandir": 2.1.5 + fastq: 1.20.1 + + "@oxc-project/types@0.122.0": {} + + "@package-json/types@0.0.12": {} + "@pkgjs/parseargs@0.11.0": optional: true "@polka/url@1.0.0-next.29": {} + "@rolldown/binding-android-arm64@1.0.0-rc.12": + optional: true + + "@rolldown/binding-darwin-arm64@1.0.0-rc.12": + optional: true + + "@rolldown/binding-darwin-x64@1.0.0-rc.12": + optional: true + + "@rolldown/binding-freebsd-x64@1.0.0-rc.12": + optional: true + + "@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12": + optional: true + + "@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12": + optional: true + + "@rolldown/binding-linux-arm64-musl@1.0.0-rc.12": + optional: true + + "@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12": + optional: true + + "@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12": + optional: true + + "@rolldown/binding-linux-x64-gnu@1.0.0-rc.12": + optional: true + + "@rolldown/binding-linux-x64-musl@1.0.0-rc.12": + optional: true + + "@rolldown/binding-openharmony-arm64@1.0.0-rc.12": + optional: true + + "@rolldown/binding-wasm32-wasi@1.0.0-rc.12(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)": + dependencies: + "@napi-rs/wasm-runtime": 1.1.3(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2) + transitivePeerDependencies: + - "@emnapi/core" + - "@emnapi/runtime" + optional: true + + "@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12": + optional: true + + "@rolldown/binding-win32-x64-msvc@1.0.0-rc.12": + optional: true + + "@rolldown/pluginutils@1.0.0-rc.12": {} + "@rollup/rollup-android-arm-eabi@4.60.1": optional: true @@ -4268,10 +6040,10 @@ snapshots: axe-core: 4.11.1 storybook: 10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - "@storybook/addon-docs@10.3.3(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.5.0))": + "@storybook/addon-docs@10.3.3(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4))": dependencies: "@mdx-js/react": 3.1.1(@types/react@19.2.14)(react@19.2.4) - "@storybook/csf-plugin": 10.3.3(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.5.0)) + "@storybook/csf-plugin": 10.3.3(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4)) "@storybook/icons": 2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) "@storybook/react-dom-shim": 10.3.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) react: 19.2.4 @@ -4306,32 +6078,32 @@ snapshots: "@storybook/icons": 2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) storybook: 10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) optionalDependencies: - "@vitest/browser": 3.2.4(playwright@1.58.2)(vite@7.3.1(@types/node@25.5.0))(vitest@3.2.4) + "@vitest/browser": 3.2.4(playwright@1.58.2)(vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4))(vitest@3.2.4) "@vitest/runner": 3.2.4 - vitest: 3.2.4(@types/node@25.5.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.8.9) + vitest: 3.2.4(@types/node@25.5.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.8.9)(lightningcss@1.32.0) transitivePeerDependencies: - react - react-dom - "@storybook/builder-vite@10.3.3(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.5.0))": + "@storybook/builder-vite@10.3.3(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4))": dependencies: - "@storybook/csf-plugin": 10.3.3(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.5.0)) + "@storybook/csf-plugin": 10.3.3(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4)) storybook: 10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) ts-dedent: 2.2.0 - vite: 7.3.1(@types/node@25.5.0) + vite: 8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4) transitivePeerDependencies: - esbuild - rollup - webpack - "@storybook/csf-plugin@10.3.3(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.5.0))": + "@storybook/csf-plugin@10.3.3(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4))": dependencies: storybook: 10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) unplugin: 2.3.11 optionalDependencies: esbuild: 0.27.4 rollup: 4.60.1 - vite: 7.3.1(@types/node@25.5.0) + vite: 8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4) "@storybook/global@5.0.0": {} @@ -4373,9 +6145,9 @@ snapshots: "@vitest/spy": 2.0.5 storybook: 10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - "@storybook/web-components-vite@10.3.3(esbuild@0.27.4)(lit@3.3.2)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.5.0))": + "@storybook/web-components-vite@10.3.3(esbuild@0.27.4)(lit@3.3.2)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4))": dependencies: - "@storybook/builder-vite": 10.3.3(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.5.0)) + "@storybook/builder-vite": 10.3.3(esbuild@0.27.4)(rollup@4.60.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4)) "@storybook/web-components": 10.3.3(lit@3.3.2)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) storybook: 10.3.3(@testing-library/dom@10.4.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) transitivePeerDependencies: @@ -4393,6 +6165,16 @@ snapshots: tiny-invariant: 1.3.3 ts-dedent: 2.2.0 + "@stylistic/eslint-plugin@5.10.0(eslint@10.2.0)": + dependencies: + "@eslint-community/eslint-utils": 4.9.1(eslint@10.2.0) + "@typescript-eslint/types": 8.57.2 + eslint: 10.2.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + estraverse: 5.3.0 + picomatch: 4.0.4 + "@testing-library/dom@10.4.0": dependencies: "@babel/code-frame": 7.29.0 @@ -4411,7 +6193,7 @@ snapshots: chalk: 3.0.0 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 - lodash: 4.17.23 + lodash: 4.18.0 redent: 3.0.0 "@testing-library/jest-dom@6.9.1": @@ -4452,6 +6234,11 @@ snapshots: esm-env: 1.2.2 tmcp: 1.19.3(typescript@5.9.3) + "@tybys/wasm-util@0.10.1": + dependencies: + tslib: 2.8.1 + optional: true + "@types/aria-query@5.0.4": {} "@types/chai@5.2.3": @@ -4461,6 +6248,8 @@ snapshots: "@types/deep-eql@4.0.2": {} + "@types/esrecurse@4.3.1": {} + "@types/estree@1.0.8": {} "@types/json-schema@7.0.15": {} @@ -4469,10 +6258,14 @@ snapshots: "@types/mdx@2.0.13": {} + "@types/minimist@1.2.5": {} + "@types/node@25.5.0": dependencies: undici-types: 7.18.2 + "@types/normalize-package-data@2.4.4": {} + "@types/parse5@2.2.34": dependencies: "@types/node": 25.5.0 @@ -4489,15 +6282,15 @@ snapshots: dependencies: "@types/node": 25.5.0 - "@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)": + "@typescript-eslint/eslint-plugin@8.58.1(@typescript-eslint/parser@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint@10.2.0)(typescript@5.9.3)": dependencies: "@eslint-community/regexpp": 4.12.2 - "@typescript-eslint/parser": 8.57.2(eslint@9.39.4)(typescript@5.9.3) - "@typescript-eslint/scope-manager": 8.57.2 - "@typescript-eslint/type-utils": 8.57.2(eslint@9.39.4)(typescript@5.9.3) - "@typescript-eslint/utils": 8.57.2(eslint@9.39.4)(typescript@5.9.3) - "@typescript-eslint/visitor-keys": 8.57.2 - eslint: 9.39.4 + "@typescript-eslint/parser": 8.58.1(eslint@10.2.0)(typescript@5.9.3) + "@typescript-eslint/scope-manager": 8.58.1 + "@typescript-eslint/type-utils": 8.58.1(eslint@10.2.0)(typescript@5.9.3) + "@typescript-eslint/utils": 8.58.1(eslint@10.2.0)(typescript@5.9.3) + "@typescript-eslint/visitor-keys": 8.58.1 + eslint: 10.2.0 ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@5.9.3) @@ -4505,43 +6298,43 @@ snapshots: transitivePeerDependencies: - supports-color - "@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3)": + "@typescript-eslint/parser@8.58.1(eslint@10.2.0)(typescript@5.9.3)": dependencies: - "@typescript-eslint/scope-manager": 8.57.2 - "@typescript-eslint/types": 8.57.2 - "@typescript-eslint/typescript-estree": 8.57.2(typescript@5.9.3) - "@typescript-eslint/visitor-keys": 8.57.2 + "@typescript-eslint/scope-manager": 8.58.1 + "@typescript-eslint/types": 8.58.1 + "@typescript-eslint/typescript-estree": 8.58.1(typescript@5.9.3) + "@typescript-eslint/visitor-keys": 8.58.1 debug: 4.4.3 - eslint: 9.39.4 + eslint: 10.2.0 typescript: 5.9.3 transitivePeerDependencies: - supports-color - "@typescript-eslint/project-service@8.57.2(typescript@5.9.3)": + "@typescript-eslint/project-service@8.58.1(typescript@5.9.3)": dependencies: - "@typescript-eslint/tsconfig-utils": 8.57.2(typescript@5.9.3) - "@typescript-eslint/types": 8.57.2 + "@typescript-eslint/tsconfig-utils": 8.58.1(typescript@5.9.3) + "@typescript-eslint/types": 8.58.1 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - "@typescript-eslint/scope-manager@8.57.2": + "@typescript-eslint/scope-manager@8.58.1": dependencies: - "@typescript-eslint/types": 8.57.2 - "@typescript-eslint/visitor-keys": 8.57.2 + "@typescript-eslint/types": 8.58.1 + "@typescript-eslint/visitor-keys": 8.58.1 - "@typescript-eslint/tsconfig-utils@8.57.2(typescript@5.9.3)": + "@typescript-eslint/tsconfig-utils@8.58.1(typescript@5.9.3)": dependencies: typescript: 5.9.3 - "@typescript-eslint/type-utils@8.57.2(eslint@9.39.4)(typescript@5.9.3)": + "@typescript-eslint/type-utils@8.58.1(eslint@10.2.0)(typescript@5.9.3)": dependencies: - "@typescript-eslint/types": 8.57.2 - "@typescript-eslint/typescript-estree": 8.57.2(typescript@5.9.3) - "@typescript-eslint/utils": 8.57.2(eslint@9.39.4)(typescript@5.9.3) + "@typescript-eslint/types": 8.58.1 + "@typescript-eslint/typescript-estree": 8.58.1(typescript@5.9.3) + "@typescript-eslint/utils": 8.58.1(eslint@10.2.0)(typescript@5.9.3) debug: 4.4.3 - eslint: 9.39.4 + eslint: 10.2.0 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -4549,12 +6342,14 @@ snapshots: "@typescript-eslint/types@8.57.2": {} - "@typescript-eslint/typescript-estree@8.57.2(typescript@5.9.3)": + "@typescript-eslint/types@8.58.1": {} + + "@typescript-eslint/typescript-estree@8.58.1(typescript@5.9.3)": dependencies: - "@typescript-eslint/project-service": 8.57.2(typescript@5.9.3) - "@typescript-eslint/tsconfig-utils": 8.57.2(typescript@5.9.3) - "@typescript-eslint/types": 8.57.2 - "@typescript-eslint/visitor-keys": 8.57.2 + "@typescript-eslint/project-service": 8.58.1(typescript@5.9.3) + "@typescript-eslint/tsconfig-utils": 8.58.1(typescript@5.9.3) + "@typescript-eslint/types": 8.58.1 + "@typescript-eslint/visitor-keys": 8.58.1 debug: 4.4.3 minimatch: 10.2.4 semver: 7.7.4 @@ -4564,36 +6359,95 @@ snapshots: transitivePeerDependencies: - supports-color - "@typescript-eslint/utils@8.57.2(eslint@9.39.4)(typescript@5.9.3)": + "@typescript-eslint/utils@8.58.1(eslint@10.2.0)(typescript@5.9.3)": dependencies: - "@eslint-community/eslint-utils": 4.9.1(eslint@9.39.4) - "@typescript-eslint/scope-manager": 8.57.2 - "@typescript-eslint/types": 8.57.2 - "@typescript-eslint/typescript-estree": 8.57.2(typescript@5.9.3) - eslint: 9.39.4 + "@eslint-community/eslint-utils": 4.9.1(eslint@10.2.0) + "@typescript-eslint/scope-manager": 8.58.1 + "@typescript-eslint/types": 8.58.1 + "@typescript-eslint/typescript-estree": 8.58.1(typescript@5.9.3) + eslint: 10.2.0 typescript: 5.9.3 transitivePeerDependencies: - supports-color - "@typescript-eslint/visitor-keys@8.57.2": + "@typescript-eslint/visitor-keys@8.58.1": dependencies: - "@typescript-eslint/types": 8.57.2 + "@typescript-eslint/types": 8.58.1 eslint-visitor-keys: 5.0.1 + "@unrs/resolver-binding-android-arm-eabi@1.11.1": + optional: true + + "@unrs/resolver-binding-android-arm64@1.11.1": + optional: true + + "@unrs/resolver-binding-darwin-arm64@1.11.1": + optional: true + + "@unrs/resolver-binding-darwin-x64@1.11.1": + optional: true + + "@unrs/resolver-binding-freebsd-x64@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-arm-musleabihf@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-arm64-gnu@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-arm64-musl@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-ppc64-gnu@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-riscv64-gnu@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-riscv64-musl@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-s390x-gnu@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-x64-gnu@1.11.1": + optional: true + + "@unrs/resolver-binding-linux-x64-musl@1.11.1": + optional: true + + "@unrs/resolver-binding-wasm32-wasi@1.11.1": + dependencies: + "@napi-rs/wasm-runtime": 0.2.12 + optional: true + + "@unrs/resolver-binding-win32-arm64-msvc@1.11.1": + optional: true + + "@unrs/resolver-binding-win32-ia32-msvc@1.11.1": + optional: true + + "@unrs/resolver-binding-win32-x64-msvc@1.11.1": + optional: true + "@valibot/to-json-schema@1.6.0(valibot@1.2.0(typescript@5.9.3))": dependencies: valibot: 1.2.0(typescript@5.9.3) - "@vitest/browser@3.2.4(playwright@1.58.2)(vite@7.3.1(@types/node@25.5.0))(vitest@3.2.4)": + "@vitest/browser@3.2.4(playwright@1.58.2)(vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4))(vitest@3.2.4)": dependencies: "@testing-library/dom": 10.4.0 "@testing-library/user-event": 14.6.1(@testing-library/dom@10.4.0) - "@vitest/mocker": 3.2.4(vite@7.3.1(@types/node@25.5.0)) + "@vitest/mocker": 3.2.4(vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4)) "@vitest/utils": 3.2.4 magic-string: 0.30.21 sirv: 3.0.2 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/node@25.5.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.8.9) + vitest: 3.2.4(@types/node@25.5.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.8.9)(lightningcss@1.32.0) ws: 8.20.0 optionalDependencies: playwright: 1.58.2 @@ -4618,9 +6472,9 @@ snapshots: std-env: 3.10.0 test-exclude: 7.0.2 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/node@25.5.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.8.9) + vitest: 3.2.4(@types/node@25.5.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.8.9)(lightningcss@1.32.0) optionalDependencies: - "@vitest/browser": 3.2.4(playwright@1.58.2)(vite@7.3.1(@types/node@25.5.0))(vitest@3.2.4) + "@vitest/browser": 3.2.4(playwright@1.58.2)(vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4))(vitest@3.2.4) transitivePeerDependencies: - supports-color @@ -4639,13 +6493,21 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - "@vitest/mocker@3.2.4(vite@7.3.1(@types/node@25.5.0))": + "@vitest/mocker@3.2.4(vite@7.3.1(@types/node@25.5.0)(lightningcss@1.32.0))": + dependencies: + "@vitest/spy": 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.1(@types/node@25.5.0)(lightningcss@1.32.0) + + "@vitest/mocker@3.2.4(vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4))": dependencies: "@vitest/spy": 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@25.5.0) + vite: 8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4) "@vitest/pretty-format@2.0.5": dependencies: @@ -4688,7 +6550,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/node@25.5.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.8.9) + vitest: 3.2.4(@types/node@25.5.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.8.9)(lightningcss@1.32.0) "@vitest/utils@2.0.5": dependencies: @@ -4715,6 +6577,17 @@ snapshots: acorn@8.16.0: {} + ajv-errors@1.0.1(ajv@6.12.6): + dependencies: + ajv: 6.12.6 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + ajv@6.14.0: dependencies: fast-deep-equal: 3.1.3 @@ -4758,6 +6631,17 @@ snapshots: is-string: 1.1.1 math-intrinsics: 1.1.0 + array-union@2.1.0: {} + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + array.prototype.findlastindex@1.2.6: dependencies: call-bind: 1.0.8 @@ -4782,6 +6666,14 @@ snapshots: es-abstract: 1.24.1 es-shim-unscopables: 1.1.0 + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + arraybuffer.prototype.slice@1.0.4: dependencies: array-buffer-byte-length: 1.0.2 @@ -4792,8 +6684,12 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + arrify@1.0.1: {} + assertion-error@2.0.1: {} + ast-types-flow@0.0.8: {} + ast-types@0.16.1: dependencies: tslib: 2.8.1 @@ -4812,10 +6708,14 @@ snapshots: axe-core@4.11.1: {} + axobject-query@4.1.0: {} + balanced-match@1.0.2: {} balanced-match@4.0.4: {} + baseline-browser-mapping@2.10.16: {} + brace-expansion@1.1.13: dependencies: balanced-match: 1.0.2 @@ -4829,6 +6729,18 @@ snapshots: dependencies: balanced-match: 4.0.4 + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.16 + caniuse-lite: 1.0.30001787 + electron-to-chromium: 1.5.334 + node-releases: 2.0.37 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + bundle-name@4.1.0: dependencies: run-applescript: 7.1.0 @@ -4854,6 +6766,16 @@ snapshots: callsites@3.1.0: {} + camelcase-keys@6.2.2: + dependencies: + camelcase: 5.3.1 + map-obj: 4.3.0 + quick-lru: 4.0.1 + + camelcase@5.3.1: {} + + caniuse-lite@1.0.30001787: {} + chai@5.3.3: dependencies: assertion-error: 2.0.1 @@ -4886,10 +6808,23 @@ snapshots: color-name@1.1.4: {} + comment-parser@1.4.6: {} + concat-map@0.0.1: {} confusing-browser-globals@1.0.11: {} + convert-source-map@2.0.0: {} + + cosmiconfig@8.3.6(typescript@5.9.3): + dependencies: + import-fresh: 3.3.1 + js-yaml: 4.1.1 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.9.3 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -4900,6 +6835,8 @@ snapshots: csstype@3.2.3: {} + damerau-levenshtein@1.0.8: {} + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -4926,6 +6863,13 @@ snapshots: dependencies: ms: 2.1.3 + decamelize-keys@1.1.1: + dependencies: + decamelize: 1.2.0 + map-obj: 1.0.1 + + decamelize@1.2.0: {} + deep-eql@5.0.2: {} deep-is@0.1.4: {} @@ -4953,6 +6897,12 @@ snapshots: dequal@2.0.3: {} + detect-libc@2.1.2: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -4975,16 +6925,27 @@ snapshots: eastasianwidth@0.2.0: {} + electron-to-chromium@1.5.334: {} + emoji-regex@10.6.0: {} emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} + enhanced-resolve@5.20.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.2 + entities@6.0.1: {} entities@7.0.1: {} + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + es-abstract@1.24.1: dependencies: array-buffer-byte-length: 1.0.2 @@ -5046,6 +7007,26 @@ snapshots: es-errors@1.3.0: {} + es-iterator-helpers@1.3.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + math-intrinsics: 1.1.0 + safe-array-concat: 1.1.3 + es-module-lexer@1.7.0: {} es-object-atoms@1.1.1: @@ -5098,20 +7079,48 @@ snapshots: "@esbuild/win32-ia32": 0.27.4 "@esbuild/win32-x64": 0.27.4 + escalade@3.2.0: {} + escape-string-regexp@4.0.0: {} - eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4))(eslint@9.39.4): + eslint-compat-utils@0.5.1(eslint@10.2.0): + dependencies: + eslint: 10.2.0 + semver: 7.7.4 + + eslint-config-airbnb-extended@3.1.0(@typescript-eslint/parser@8.58.1(eslint@10.2.0)(typescript@5.9.3))(@typescript-eslint/utils@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@10.2.0)(typescript@5.9.3): dependencies: + "@next/eslint-plugin-next": 16.2.2 + "@stylistic/eslint-plugin": 5.10.0(eslint@10.2.0) confusing-browser-globals: 1.0.11 - eslint: 9.39.4 - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4) - object.assign: 4.1.7 - object.entries: 1.1.9 - semver: 6.3.1 + eslint: 10.2.0 + eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@10.2.0))(eslint-plugin-import@2.32.0)(eslint@10.2.0) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@10.2.0) + eslint-plugin-import-x: 4.16.2(@typescript-eslint/utils@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@10.2.0) + eslint-plugin-jsx-a11y: 6.10.2(eslint@10.2.0) + eslint-plugin-n: 17.24.0(eslint@10.2.0)(typescript@5.9.3) + eslint-plugin-react: 7.37.5(eslint@10.2.0) + eslint-plugin-react-hooks: 7.0.1(eslint@10.2.0) + globals: 17.4.0 + typescript-eslint: 8.58.1(eslint@10.2.0)(typescript@5.9.3) + transitivePeerDependencies: + - "@typescript-eslint/parser" + - "@typescript-eslint/utils" + - eslint-import-resolver-node + - eslint-import-resolver-webpack + - supports-color + - typescript + + eslint-config-prettier@10.1.8(eslint@10.2.0): + dependencies: + eslint: 10.2.0 - eslint-config-prettier@10.1.8(eslint@9.39.4): + eslint-import-context@0.1.9(unrs-resolver@1.11.1): dependencies: - eslint: 9.39.4 + get-tsconfig: 4.13.7 + stable-hash-x: 0.2.0 + optionalDependencies: + unrs-resolver: 1.11.1 eslint-import-resolver-node@0.3.9: dependencies: @@ -5121,17 +7130,60 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4): + eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@10.2.0))(eslint-plugin-import@2.32.0)(eslint@10.2.0): + dependencies: + debug: 4.4.3 + eslint: 10.2.0 + eslint-import-context: 0.1.9(unrs-resolver@1.11.1) + get-tsconfig: 4.13.7 + is-bun-module: 2.0.0 + stable-hash-x: 0.2.0 + tinyglobby: 0.2.15 + unrs-resolver: 1.11.1 + optionalDependencies: + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@10.2.0) + eslint-plugin-import-x: 4.16.2(@typescript-eslint/utils@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@10.2.0) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@10.2.0): dependencies: debug: 3.2.7 optionalDependencies: - "@typescript-eslint/parser": 8.57.2(eslint@9.39.4)(typescript@5.9.3) - eslint: 9.39.4 + "@typescript-eslint/parser": 8.58.1(eslint@10.2.0)(typescript@5.9.3) + eslint: 10.2.0 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@10.2.0))(eslint-plugin-import@2.32.0)(eslint@10.2.0) + transitivePeerDependencies: + - supports-color + + eslint-plugin-es-x@7.8.0(eslint@10.2.0): + dependencies: + "@eslint-community/eslint-utils": 4.9.1(eslint@10.2.0) + "@eslint-community/regexpp": 4.12.2 + eslint: 10.2.0 + eslint-compat-utils: 0.5.1(eslint@10.2.0) + + eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@10.2.0): + dependencies: + "@package-json/types": 0.0.12 + "@typescript-eslint/types": 8.57.2 + comment-parser: 1.4.6 + debug: 4.4.3 + eslint: 10.2.0 + eslint-import-context: 0.1.9(unrs-resolver@1.11.1) + is-glob: 4.0.3 + minimatch: 10.2.4 + semver: 7.7.4 + stable-hash-x: 0.2.0 + unrs-resolver: 1.11.1 + optionalDependencies: + "@typescript-eslint/utils": 8.58.1(eslint@10.2.0)(typescript@5.9.3) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@10.2.0): dependencies: "@rtsao/scc": 1.1.0 array-includes: 3.1.9 @@ -5140,9 +7192,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.39.4 + eslint: 10.2.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@10.2.0) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -5154,48 +7206,117 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - "@typescript-eslint/parser": 8.57.2(eslint@9.39.4)(typescript@5.9.3) + "@typescript-eslint/parser": 8.58.1(eslint@10.2.0)(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-lit-a11y@5.1.1(eslint@9.39.4): + eslint-plugin-jsx-a11y@6.10.2(eslint@10.2.0): + dependencies: + aria-query: 5.3.2 + array-includes: 3.1.9 + array.prototype.flatmap: 1.3.3 + ast-types-flow: 0.0.8 + axe-core: 4.11.1 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 10.2.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 + + eslint-plugin-lit-a11y@5.1.1(eslint@10.2.0): dependencies: "@thepassle/axobject-query": 4.0.0 aria-query: 5.3.2 axe-core: 4.11.1 dom5: 3.0.1 emoji-regex: 10.6.0 - eslint: 9.39.4 - eslint-plugin-lit: 2.2.1(eslint@9.39.4) + eslint: 10.2.0 + eslint-plugin-lit: 2.2.1(eslint@10.2.0) eslint-rule-extender: 0.0.1 language-tags: 1.0.9 parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 6.0.1 - eslint-plugin-lit@2.2.1(eslint@9.39.4): + eslint-plugin-lit@2.2.1(eslint@10.2.0): dependencies: - eslint: 9.39.4 + eslint: 10.2.0 parse5: 6.0.1 parse5-htmlparser2-tree-adapter: 6.0.1 - eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4): + eslint-plugin-n@17.24.0(eslint@10.2.0)(typescript@5.9.3): + dependencies: + "@eslint-community/eslint-utils": 4.9.1(eslint@10.2.0) + enhanced-resolve: 5.20.1 + eslint: 10.2.0 + eslint-plugin-es-x: 7.8.0(eslint@10.2.0) + get-tsconfig: 4.13.7 + globals: 15.15.0 + globrex: 0.1.2 + ignore: 5.3.2 + semver: 7.7.4 + ts-declaration-location: 1.0.7(typescript@5.9.3) + transitivePeerDependencies: + - typescript + + eslint-plugin-react-hooks@7.0.1(eslint@10.2.0): + dependencies: + "@babel/core": 7.29.0 + "@babel/parser": 7.29.2 + eslint: 10.2.0 + hermes-parser: 0.25.1 + zod: 4.3.6 + zod-validation-error: 4.0.2(zod@4.3.6) + transitivePeerDependencies: + - supports-color + + eslint-plugin-react@7.37.5(eslint@10.2.0): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.3.1 + eslint: 10.2.0 + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.6 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.58.1(@typescript-eslint/parser@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint@10.2.0)(typescript@5.9.3))(eslint@10.2.0): dependencies: - eslint: 9.39.4 + eslint: 10.2.0 optionalDependencies: - "@typescript-eslint/eslint-plugin": 8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + "@typescript-eslint/eslint-plugin": 8.58.1(@typescript-eslint/parser@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint@10.2.0)(typescript@5.9.3) - eslint-plugin-wc@2.2.1(eslint@9.39.4): + eslint-plugin-wc@3.1.0(eslint@10.2.0): dependencies: - eslint: 9.39.4 + eslint: 10.2.0 is-valid-element-name: 1.0.0 - js-levenshtein-esm: 1.2.0 + js-levenshtein-esm: 2.0.0 eslint-rule-extender@0.0.1: {} - eslint-scope@8.4.0: + eslint-scope@9.1.2: dependencies: + "@types/esrecurse": 4.3.1 + "@types/estree": 1.0.8 esrecurse: 4.3.0 estraverse: 5.3.0 @@ -5205,28 +7326,25 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@9.39.4: + eslint@10.2.0: dependencies: - "@eslint-community/eslint-utils": 4.9.1(eslint@9.39.4) + "@eslint-community/eslint-utils": 4.9.1(eslint@10.2.0) "@eslint-community/regexpp": 4.12.2 - "@eslint/config-array": 0.21.2 - "@eslint/config-helpers": 0.4.2 - "@eslint/core": 0.17.0 - "@eslint/eslintrc": 3.3.5 - "@eslint/js": 9.39.4 - "@eslint/plugin-kit": 0.4.1 + "@eslint/config-array": 0.23.5 + "@eslint/config-helpers": 0.5.5 + "@eslint/core": 1.2.1 + "@eslint/plugin-kit": 0.7.1 "@humanfs/node": 0.16.7 "@humanwhocodes/module-importer": 1.0.1 "@humanwhocodes/retry": 0.4.3 "@types/estree": 1.0.8 ajv: 6.14.0 - chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.3 escape-string-regexp: 4.0.0 - eslint-scope: 8.4.0 - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 esquery: 1.7.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 @@ -5237,8 +7355,7 @@ snapshots: imurmurhash: 0.1.4 is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.5 + minimatch: 10.2.4 natural-compare: 1.4.0 optionator: 0.9.4 transitivePeerDependencies: @@ -5252,6 +7369,12 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.16.0) eslint-visitor-keys: 4.2.1 + espree@11.2.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 5.0.1 + esprima@4.0.1: {} esquery@1.7.0: @@ -5274,10 +7397,30 @@ snapshots: fast-deep-equal@3.1.3: {} + fast-glob@3.3.1: + dependencies: + "@nodelib/fs.stat": 2.0.5 + "@nodelib/fs.walk": 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-glob@3.3.3: + dependencies: + "@nodelib/fs.stat": 2.0.5 + "@nodelib/fs.walk": 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 @@ -5290,6 +7433,15 @@ snapshots: filesize@10.1.6: {} + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -5332,6 +7484,8 @@ snapshots: generator-function@2.0.1: {} + gensync@1.0.0-beta.2: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -5356,6 +7510,14 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 + get-tsconfig@4.13.7: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 @@ -5369,19 +7531,29 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - globals@14.0.0: {} + globals@15.15.0: {} - globals@16.5.0: {} + globals@17.4.0: {} globalthis@1.0.4: dependencies: define-properties: 1.2.1 gopd: 1.2.0 + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + globrex@0.1.2: {} + gopd@1.2.0: {} - graceful-fs@4.2.11: - optional: true + graceful-fs@4.2.11: {} happy-dom@20.8.9: dependencies: @@ -5395,6 +7567,8 @@ snapshots: - bufferutil - utf-8-validate + hard-rejection@2.1.0: {} + has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -5417,6 +7591,18 @@ snapshots: dependencies: function-bind: 1.1.2 + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + + hosted-git-info@2.8.9: {} + + hosted-git-info@4.1.0: + dependencies: + lru-cache: 6.0.0 + html-escaper@2.0.2: {} ignore@5.3.2: {} @@ -5438,12 +7624,16 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + irregular-plurals@3.5.0: {} + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 call-bound: 1.0.4 get-intrinsic: 1.3.0 + is-arrayish@0.2.1: {} + is-async-function@2.1.1: dependencies: async-function: 1.0.0 @@ -5461,6 +7651,10 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-bun-module@2.0.0: + dependencies: + semver: 7.7.4 + is-callable@1.2.7: {} is-core-module@2.16.1: @@ -5513,6 +7707,12 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-number@7.0.0: {} + + is-plain-obj@1.1.0: {} + + is-plain-obj@3.0.0: {} + is-potential-custom-element-name@1.0.1: {} is-regex@1.2.1: @@ -5543,6 +7743,8 @@ snapshots: dependencies: which-typed-array: 1.1.20 + is-unicode-supported@0.1.0: {} + is-valid-element-name@1.0.0: dependencies: is-potential-custom-element-name: 1.0.1 @@ -5587,13 +7789,22 @@ snapshots: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + jackspeak@3.4.3: dependencies: "@isaacs/cliui": 8.0.2 optionalDependencies: "@pkgjs/parseargs": 0.11.0 - js-levenshtein-esm@1.2.0: {} + js-levenshtein-esm@2.0.0: {} js-tokens@10.0.0: {} @@ -5605,8 +7816,12 @@ snapshots: dependencies: argparse: 2.0.1 + jsesc@3.1.0: {} + json-buffer@3.0.1: {} + json-parse-even-better-errors@2.3.1: {} + json-rpc-2.0@1.7.1: {} json-schema-traverse@0.4.1: {} @@ -5617,16 +7832,29 @@ snapshots: dependencies: minimist: 1.2.8 + json5@2.2.3: {} + + jsonc-parser@3.3.1: {} + jsonfile@6.2.0: dependencies: universalify: 2.0.1 optionalDependencies: graceful-fs: 4.2.11 + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 + kind-of@6.0.3: {} + language-subtag-registry@0.3.23: {} language-tags@1.0.9: @@ -5638,6 +7866,57 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + lines-and-columns@1.2.4: {} + lit-element@4.2.2: dependencies: "@lit-labs/ssr-dom-shim": 1.5.1 @@ -5654,18 +7933,37 @@ snapshots: lit-element: 4.2.2 lit-html: 3.3.2 + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 - lodash.merge@4.6.2: {} + lodash@4.18.0: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 - lodash@4.17.23: {} + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 loupe@3.2.1: {} lru-cache@10.4.3: {} + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + lz-string@1.5.0: {} magic-string@0.30.21: @@ -5682,8 +7980,34 @@ snapshots: dependencies: semver: 7.7.4 + map-obj@1.0.1: {} + + map-obj@4.3.0: {} + math-intrinsics@1.1.0: {} + meow@9.0.0: + dependencies: + "@types/minimist": 1.2.5 + camelcase-keys: 6.2.2 + decamelize: 1.2.0 + decamelize-keys: 1.1.1 + hard-rejection: 2.1.0 + minimist-options: 4.1.0 + normalize-package-data: 3.0.3 + read-pkg-up: 7.0.1 + redent: 3.0.0 + trim-newlines: 3.0.1 + type-fest: 0.18.1 + yargs-parser: 20.2.9 + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + min-indent@1.0.1: {} minimatch@10.2.4: @@ -5698,6 +8022,12 @@ snapshots: dependencies: brace-expansion: 2.0.3 + minimist-options@4.1.0: + dependencies: + arrify: 1.0.1 + is-plain-obj: 1.1.0 + kind-of: 6.0.3 + minimist@1.2.8: {} minipass@7.1.3: {} @@ -5708,8 +8038,58 @@ snapshots: nanoid@3.3.11: {} + napi-postinstall@0.3.4: {} + natural-compare@1.4.0: {} + node-exports-info@1.6.0: + dependencies: + array.prototype.flatmap: 1.3.3 + es-errors: 1.3.0 + object.entries: 1.1.9 + semver: 6.3.1 + + node-releases@2.0.37: {} + + normalize-package-data@2.5.0: + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.11 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 + + normalize-package-data@3.0.3: + dependencies: + hosted-git-info: 4.1.0 + is-core-module: 2.16.1 + semver: 7.7.4 + validate-npm-package-license: 3.0.4 + + npm-package-json-lint@10.2.0(typescript@5.9.3): + dependencies: + ajv: 6.12.6 + ajv-errors: 1.0.1(ajv@6.12.6) + chalk: 4.1.2 + cosmiconfig: 8.3.6(typescript@5.9.3) + debug: 4.4.3 + globby: 11.1.0 + ignore: 5.3.2 + is-plain-obj: 3.0.0 + jsonc-parser: 3.3.1 + log-symbols: 4.1.0 + meow: 9.0.0 + plur: 4.0.0 + semver: 7.7.4 + slash: 3.0.0 + strip-json-comments: 3.1.1 + type-fest: 5.3.1 + validate-npm-package-name: 6.0.0 + transitivePeerDependencies: + - supports-color + - typescript + + object-assign@4.1.1: {} + object-inspect@1.13.4: {} object-keys@1.1.1: {} @@ -5772,20 +8152,37 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + p-locate@5.0.0: dependencies: p-limit: 3.1.0 + p-try@2.2.0: {} + package-json-from-dist@1.0.1: {} parent-module@1.0.1: dependencies: callsites: 3.1.0 + parse-json@5.2.0: + dependencies: + "@babel/code-frame": 7.29.0 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + parse5-htmlparser2-tree-adapter@6.0.1: dependencies: parse5: 6.0.1 @@ -5809,12 +8206,16 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.3 + path-type@4.0.0: {} + pathe@2.0.3: {} pathval@2.0.1: {} picocolors@1.1.1: {} + picomatch@2.3.2: {} + picomatch@4.0.4: {} picoquery@2.5.0: {} @@ -5827,6 +8228,10 @@ snapshots: optionalDependencies: fsevents: 2.3.2 + plur@4.0.0: + dependencies: + irregular-plurals: 3.5.0 + possible-typed-array-names@1.1.0: {} postcss@8.5.8: @@ -5845,17 +8250,42 @@ snapshots: ansi-styles: 5.2.0 react-is: 17.0.2 + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + punycode@2.3.1: {} + queue-microtask@1.2.3: {} + + quick-lru@4.0.1: {} + react-dom@19.2.4(react@19.2.4): dependencies: react: 19.2.4 scheduler: 0.27.0 + react-is@16.13.1: {} + react-is@17.0.2: {} react@19.2.4: {} + read-pkg-up@7.0.1: + dependencies: + find-up: 4.1.0 + read-pkg: 5.2.0 + type-fest: 0.8.1 + + read-pkg@5.2.0: + dependencies: + "@types/normalize-package-data": 2.4.4 + normalize-package-data: 2.5.0 + parse-json: 5.2.0 + type-fest: 0.6.0 + recast@0.23.11: dependencies: ast-types: 0.16.1 @@ -5891,12 +8321,49 @@ snapshots: resolve-from@4.0.0: {} + resolve-pkg-maps@1.0.0: {} + resolve@1.22.11: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + resolve@2.0.0-next.6: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.1 + node-exports-info: 1.6.0 + object-keys: 1.1.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + + rolldown@1.0.0-rc.12(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2): + dependencies: + "@oxc-project/types": 0.122.0 + "@rolldown/pluginutils": 1.0.0-rc.12 + optionalDependencies: + "@rolldown/binding-android-arm64": 1.0.0-rc.12 + "@rolldown/binding-darwin-arm64": 1.0.0-rc.12 + "@rolldown/binding-darwin-x64": 1.0.0-rc.12 + "@rolldown/binding-freebsd-x64": 1.0.0-rc.12 + "@rolldown/binding-linux-arm-gnueabihf": 1.0.0-rc.12 + "@rolldown/binding-linux-arm64-gnu": 1.0.0-rc.12 + "@rolldown/binding-linux-arm64-musl": 1.0.0-rc.12 + "@rolldown/binding-linux-ppc64-gnu": 1.0.0-rc.12 + "@rolldown/binding-linux-s390x-gnu": 1.0.0-rc.12 + "@rolldown/binding-linux-x64-gnu": 1.0.0-rc.12 + "@rolldown/binding-linux-x64-musl": 1.0.0-rc.12 + "@rolldown/binding-openharmony-arm64": 1.0.0-rc.12 + "@rolldown/binding-wasm32-wasi": 1.0.0-rc.12(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2) + "@rolldown/binding-win32-arm64-msvc": 1.0.0-rc.12 + "@rolldown/binding-win32-x64-msvc": 1.0.0-rc.12 + transitivePeerDependencies: + - "@emnapi/core" + - "@emnapi/runtime" + rollup@4.60.1: dependencies: "@types/estree": 1.0.8 @@ -5930,6 +8397,10 @@ snapshots: run-applescript@7.1.0: {} + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + safe-array-concat@1.1.3: dependencies: call-bind: 1.0.8 @@ -5951,6 +8422,8 @@ snapshots: scheduler@0.27.0: {} + semver@5.7.2: {} + semver@6.3.1: {} semver@7.7.4: {} @@ -6021,12 +8494,30 @@ snapshots: mrmime: 2.0.1 totalist: 3.0.1 + slash@3.0.0: {} + source-map-js@1.2.1: {} source-map@0.6.1: {} + spdx-correct@3.2.0: + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.23 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.23 + + spdx-license-ids@3.0.23: {} + sqids@0.3.0: {} + stable-hash-x@0.2.0: {} + stackback@0.0.2: {} std-env@3.10.0: {} @@ -6071,6 +8562,33 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.2.0 + string.prototype.includes@2.0.1: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.1 + string.prototype.trim@1.2.10: dependencies: call-bind: 1.0.8 @@ -6120,6 +8638,10 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + tagged-tag@1.0.0: {} + + tapable@2.3.2: {} + test-exclude@7.0.2: dependencies: "@istanbuljs/schema": 0.1.3 @@ -6157,12 +8679,23 @@ snapshots: transitivePeerDependencies: - typescript + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + totalist@3.0.1: {} + trim-newlines@3.0.1: {} + ts-api-utils@2.5.0(typescript@5.9.3): dependencies: typescript: 5.9.3 + ts-declaration-location@1.0.7(typescript@5.9.3): + dependencies: + picomatch: 4.0.4 + typescript: 5.9.3 + ts-dedent@2.2.0: {} tsconfig-paths@3.15.0: @@ -6178,6 +8711,16 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-fest@0.18.1: {} + + type-fest@0.6.0: {} + + type-fest@0.8.1: {} + + type-fest@5.3.1: + dependencies: + tagged-tag: 1.0.0 + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -6211,13 +8754,13 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.57.2(eslint@9.39.4)(typescript@5.9.3): + typescript-eslint@8.58.1(eslint@10.2.0)(typescript@5.9.3): dependencies: - "@typescript-eslint/eslint-plugin": 8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - "@typescript-eslint/parser": 8.57.2(eslint@9.39.4)(typescript@5.9.3) - "@typescript-eslint/typescript-estree": 8.57.2(typescript@5.9.3) - "@typescript-eslint/utils": 8.57.2(eslint@9.39.4)(typescript@5.9.3) - eslint: 9.39.4 + "@typescript-eslint/eslint-plugin": 8.58.1(@typescript-eslint/parser@8.58.1(eslint@10.2.0)(typescript@5.9.3))(eslint@10.2.0)(typescript@5.9.3) + "@typescript-eslint/parser": 8.58.1(eslint@10.2.0)(typescript@5.9.3) + "@typescript-eslint/typescript-estree": 8.58.1(typescript@5.9.3) + "@typescript-eslint/utils": 8.58.1(eslint@10.2.0)(typescript@5.9.3) + eslint: 10.2.0 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -6242,6 +8785,36 @@ snapshots: picomatch: 4.0.4 webpack-virtual-modules: 0.6.2 + unrs-resolver@1.11.1: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + "@unrs/resolver-binding-android-arm-eabi": 1.11.1 + "@unrs/resolver-binding-android-arm64": 1.11.1 + "@unrs/resolver-binding-darwin-arm64": 1.11.1 + "@unrs/resolver-binding-darwin-x64": 1.11.1 + "@unrs/resolver-binding-freebsd-x64": 1.11.1 + "@unrs/resolver-binding-linux-arm-gnueabihf": 1.11.1 + "@unrs/resolver-binding-linux-arm-musleabihf": 1.11.1 + "@unrs/resolver-binding-linux-arm64-gnu": 1.11.1 + "@unrs/resolver-binding-linux-arm64-musl": 1.11.1 + "@unrs/resolver-binding-linux-ppc64-gnu": 1.11.1 + "@unrs/resolver-binding-linux-riscv64-gnu": 1.11.1 + "@unrs/resolver-binding-linux-riscv64-musl": 1.11.1 + "@unrs/resolver-binding-linux-s390x-gnu": 1.11.1 + "@unrs/resolver-binding-linux-x64-gnu": 1.11.1 + "@unrs/resolver-binding-linux-x64-musl": 1.11.1 + "@unrs/resolver-binding-wasm32-wasi": 1.11.1 + "@unrs/resolver-binding-win32-arm64-msvc": 1.11.1 + "@unrs/resolver-binding-win32-ia32-msvc": 1.11.1 + "@unrs/resolver-binding-win32-x64-msvc": 1.11.1 + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -6256,13 +8829,20 @@ snapshots: optionalDependencies: typescript: 5.9.3 - vite-node@3.2.4(@types/node@25.5.0): + validate-npm-package-license@3.0.4: + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + + validate-npm-package-name@6.0.0: {} + + vite-node@3.2.4(@types/node@25.5.0)(lightningcss@1.32.0): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.1(@types/node@25.5.0) + vite: 7.3.1(@types/node@25.5.0)(lightningcss@1.32.0) transitivePeerDependencies: - "@types/node" - jiti @@ -6277,7 +8857,7 @@ snapshots: - tsx - yaml - vite@7.3.1(@types/node@25.5.0): + vite@7.3.1(@types/node@25.5.0)(lightningcss@1.32.0): dependencies: esbuild: 0.27.4 fdir: 6.5.0(picomatch@4.0.4) @@ -6288,12 +8868,28 @@ snapshots: optionalDependencies: "@types/node": 25.5.0 fsevents: 2.3.3 + lightningcss: 1.32.0 - vitest@3.2.4(@types/node@25.5.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.8.9): + vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.8 + rolldown: 1.0.0-rc.12(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2) + tinyglobby: 0.2.15 + optionalDependencies: + "@types/node": 25.5.0 + esbuild: 0.27.4 + fsevents: 2.3.3 + transitivePeerDependencies: + - "@emnapi/core" + - "@emnapi/runtime" + + vitest@3.2.4(@types/node@25.5.0)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(happy-dom@20.8.9)(lightningcss@1.32.0): dependencies: "@types/chai": 5.2.3 "@vitest/expect": 3.2.4 - "@vitest/mocker": 3.2.4(vite@7.3.1(@types/node@25.5.0)) + "@vitest/mocker": 3.2.4(vite@7.3.1(@types/node@25.5.0)(lightningcss@1.32.0)) "@vitest/pretty-format": 3.2.4 "@vitest/runner": 3.2.4 "@vitest/snapshot": 3.2.4 @@ -6311,12 +8907,12 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.3.1(@types/node@25.5.0) - vite-node: 3.2.4(@types/node@25.5.0) + vite: 7.3.1(@types/node@25.5.0)(lightningcss@1.32.0) + vite-node: 3.2.4(@types/node@25.5.0)(lightningcss@1.32.0) why-is-node-running: 2.3.0 optionalDependencies: "@types/node": 25.5.0 - "@vitest/browser": 3.2.4(playwright@1.58.2)(vite@7.3.1(@types/node@25.5.0))(vitest@3.2.4) + "@vitest/browser": 3.2.4(playwright@1.58.2)(vite@8.0.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.4))(vitest@3.2.4) "@vitest/ui": 3.2.4(vitest@3.2.4) happy-dom: 20.8.9 transitivePeerDependencies: @@ -6407,4 +9003,16 @@ snapshots: dependencies: is-wsl: 3.1.1 + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yargs-parser@20.2.9: {} + yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@4.3.6): + dependencies: + zod: 4.3.6 + + zod@4.3.6: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e7361ee8..d46bcce2 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,3 +3,8 @@ packages: minimumReleaseAge: 1440 trustPolicy: no-downgrade +trustPolicyExclude: + # Package: semver@6.3.1 AND semver@5.7.2 + # Reason: Old version released after higher-trust versions; dependency chain cannot yet be upgraded. Version and repo manually checked. + # Risk Assessment: Used only as version parser; no runtime execution path; no CVE exposure. + - semver@6.3.1 || 5.7.2 diff --git a/requirements_dev.txt b/requirements_dev.txt index 92734702..3c0be922 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -2,3 +2,4 @@ pytest pytest-asyncio ruff +voluptuous diff --git a/scripts/bump-version.sh b/scripts/bump-version.sh index 299e87db..cd894789 100755 --- a/scripts/bump-version.sh +++ b/scripts/bump-version.sh @@ -1,13 +1,12 @@ #!/usr/bin/env bash -# bump-version.sh — Increment the patch segment of the version in +# bump-version.sh — Increment the version in # custom_components/hass_datapoints/manifest.json (canonical source) # package.json (kept in sync) # -# Usage: bash scripts/bump-version.sh [--dry-run] +# Usage: bash scripts/bump-version.sh [patch|minor|major] [--dry-run] # -# Reads the current version from manifest.json, bumps the PATCH component by 1, -# and writes the new version back to both files. Prints the new version string -# to stdout so callers can capture it. +# Bump type defaults to "patch" when omitted. +# Prints the new version string to stdout so callers can capture it. set -euo pipefail @@ -17,11 +16,13 @@ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" MANIFEST="$REPO_ROOT/custom_components/hass_datapoints/manifest.json" PACKAGE="$REPO_ROOT/package.json" +BUMP_TYPE="patch" DRY_RUN=0 while [[ $# -gt 0 ]]; do case "$1" in + patch|minor|major) BUMP_TYPE="$1" ;; --dry-run) DRY_RUN=1 ;; - *) echo "Unknown argument: $1" >&2; exit 1 ;; + *) echo "Usage: bump-version.sh [patch|minor|major] [--dry-run]" >&2; exit 1 ;; esac shift done @@ -40,8 +41,11 @@ if [[ -z "$MAJOR" || -z "$MINOR" || -z "$PATCH" ]]; then exit 1 fi -NEW_PATCH=$(( PATCH + 1 )) -NEW_VERSION="${MAJOR}.${MINOR}.${NEW_PATCH}" +case "$BUMP_TYPE" in + major) NEW_VERSION="$(( MAJOR + 1 )).0.0" ;; + minor) NEW_VERSION="${MAJOR}.$(( MINOR + 1 )).0" ;; + patch) NEW_VERSION="${MAJOR}.${MINOR}.$(( PATCH + 1 ))" ;; +esac echo "[bump-version] $CURRENT_VERSION → $NEW_VERSION" diff --git a/scripts/dev-sync.sh b/scripts/dev-sync.sh index b8168b60..cda2c7f9 100755 --- a/scripts/dev-sync.sh +++ b/scripts/dev-sync.sh @@ -135,8 +135,14 @@ fi # Build # --------------------------------------------------------------------------- +# Generate a short ID stamped into the bundle so the browser console confirms +# which dev-sync build is loaded. Only set here — build.sh never sets it, so +# production/CI builds never carry a sync ID. +VITE_DEV_SYNC_ID=$(( RANDOM % 9000 + 1000 )) +export VITE_DEV_SYNC_ID + if [[ $DO_BUILD -eq 1 ]]; then - echo "Building frontend bundle..." + echo "Building frontend bundle... (sync #${VITE_DEV_SYNC_ID})" bash "$REPO_ROOT/scripts/build.sh" fi @@ -269,3 +275,7 @@ if [[ $DO_RESTART -eq 1 ]]; then fi echo "Sync complete." + +if [[ $DO_BUILD -eq 1 ]]; then + echo "Look for DEV#${VITE_DEV_SYNC_ID}" +fi diff --git a/scripts/pre-commit b/scripts/pre-commit index 440a0937..86a31d0d 100755 --- a/scripts/pre-commit +++ b/scripts/pre-commit @@ -3,8 +3,9 @@ # pre-commit hook: # 1. Format all staged Prettier-supported files (ts, js, md, json, css, html…) # 2. Lint & format staged Python files with ruff -# 3. Validate TypeScript types when frontend sources/config change -# 4. Rebuild hass-datapoints-cards.js when any src/ file is staged +# 3. Lint package.json version specifiers when package metadata changes +# 4. Validate TypeScript types when frontend sources/config change +# 5. Rebuild hass-datapoints-cards.js when any src/ file is staged # # Install: pnpm hooks:install # (or manually: ln -sf ../../scripts/pre-commit .git/hooks/pre-commit) @@ -35,7 +36,11 @@ STAGED_SRC=$(git diff --cached --name-only --diff-filter=ACMR \ STAGED_TYPECHECK=$(git diff --cached --name-only --diff-filter=ACMR \ | grep -E "^(custom_components/hass_datapoints/src/.*\\.(ts|js)|tsconfig.*\\.json|package\\.json)$" || true) -if [ -z "$STAGED_PRETTIER" ] && [ -z "$STAGED_PY" ] && [ -z "$STAGED_SRC" ] && [ -z "$STAGED_TYPECHECK" ]; then +# Staged package metadata files that should trigger package.json linting +STAGED_PACKAGE_JSON=$(git diff --cached --name-only --diff-filter=ACMR \ + | grep -E "^package\\.json$" || true) + +if [ -z "$STAGED_PRETTIER" ] && [ -z "$STAGED_PY" ] && [ -z "$STAGED_SRC" ] && [ -z "$STAGED_TYPECHECK" ] && [ -z "$STAGED_PACKAGE_JSON" ]; then exit 0 fi @@ -45,8 +50,8 @@ if [ -n "$STAGED_PRETTIER" ]; then echo "[pre-commit] pnpm is required for Prettier." >&2 exit 1 fi - echo "[pre-commit] Formatting staged files with Prettier…" - echo "$STAGED_PRETTIER" | xargs pnpm prettier --cache --write -- + echo "[pre-commit] Formatting staged files with Prettier and Eslint…" + echo "$STAGED_PRETTIER" | xargs pnpm format -- # Re-stage any files Prettier modified echo "$STAGED_PRETTIER" | xargs git add fi @@ -71,7 +76,17 @@ if [ -n "$STAGED_PY" ]; then echo "$STAGED_PY" | xargs git add fi -# ── 3. Validate frontend types ───────────────────────────────────────────────── +# ── 3. Lint package.json versions ────────────────────────────────────────────── +if [ -n "$STAGED_PACKAGE_JSON" ]; then + if ! command -v pnpm >/dev/null 2>&1; then + echo "[pre-commit] pnpm is required to lint package.json." >&2 + exit 1 + fi + echo "[pre-commit] Linting package.json versions…" + pnpm lint:package-json +fi + +# ── 4. Validate frontend types ───────────────────────────────────────────────── if [ -n "$STAGED_TYPECHECK" ]; then if ! command -v pnpm >/dev/null 2>&1; then echo "[pre-commit] pnpm is required to validate TypeScript types." >&2 @@ -81,13 +96,13 @@ if [ -n "$STAGED_TYPECHECK" ]; then pnpm lint:types fi -# ── 4. Rebuild the bundle ────────────────────────────────────────────────────── +# ── 5. Rebuild the bundle ────────────────────────────────────────────────────── if [ -n "$STAGED_SRC" ]; then if ! command -v pnpm >/dev/null 2>&1; then echo "[pre-commit] pnpm is required to build frontend assets." >&2 exit 1 fi - echo "[pre-commit] src/ files changed — rebuilding $OUT_FILE…" + echo "[pre-commit] src/ files changed — rebuilding $OUT_FILE …" pnpm build git add "$OUT_FILE" echo "[pre-commit] ✓ $OUT_FILE rebuilt and staged" diff --git a/scripts/pre-push b/scripts/pre-push index 5f10a7c0..71fc7f13 100755 --- a/scripts/pre-push +++ b/scripts/pre-push @@ -18,6 +18,9 @@ pnpm test echo "[pre-push] Running ESLint without fixes…" pnpm lint:eslint +echo "[pre-push] Linting package.json versions…" +pnpm lint:package-json + echo "[pre-push] Running Prettier checks…" pnpm lint:prettier diff --git a/tests/conftest.py b/tests/conftest.py index d5cfe19f..85a9f663 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -28,15 +28,8 @@ def _stub(name: str, obj: object | None = None) -> None: # -- voluptuous --------------------------------------------------------------- -_vol = MagicMock() -_vol.Schema = MagicMock(return_value=MagicMock()) -_vol.Required = MagicMock(return_value="required") -_vol.Optional = MagicMock(return_value="optional") -_vol.All = MagicMock(return_value=MagicMock()) -_vol.Any = MagicMock(return_value=MagicMock()) -_vol.Coerce = MagicMock(return_value=MagicMock()) -_vol.Length = MagicMock(return_value=MagicMock()) -_stub("voluptuous", _vol) +# voluptuous is a real dependency listed in requirements_dev.txt; do NOT stub +# it so that tests exercising vol.Match / vol.In / vol.Range work correctly. # -- sqlalchemy --------------------------------------------------------------- _sa = MagicMock() @@ -54,6 +47,15 @@ class _Unauthorized(Exception): _ha_exceptions.Unauthorized = _Unauthorized _stub("homeassistant.exceptions", _ha_exceptions) +# -- homeassistant.auth ------------------------------------------------------- +# POLICY_READ must be a real string constant so _can_read_entity can pass it +# as an argument to user.permissions.check_entity in tests. +_ha_auth_permissions_const = MagicMock() +_ha_auth_permissions_const.POLICY_READ = "read" +_stub("homeassistant.auth", MagicMock()) +_stub("homeassistant.auth.permissions", MagicMock()) +_stub("homeassistant.auth.permissions.const", _ha_auth_permissions_const) + # -- homeassistant core ------------------------------------------------------- _ha_core = MagicMock() _ha_core.HomeAssistant = MagicMock diff --git a/tests/test_websocket_api.py b/tests/test_websocket_api.py index faffcfd3..34bc71d9 100644 --- a/tests/test_websocket_api.py +++ b/tests/test_websocket_api.py @@ -6,16 +6,31 @@ from unittest.mock import AsyncMock, MagicMock import pytest +import voluptuous as vol from homeassistant.exceptions import Unauthorized from custom_components.hass_datapoints.const import DOMAIN from custom_components.hass_datapoints.websocket_api import ( + _MAX_LEN_COLOR, + _MAX_LEN_ICON, + _MAX_LEN_MESSAGE, + _MAX_LEN_WINDOW, + _RE_DURATION, + _RE_HEX_COLOR, + _RE_MDI_ICON, + _VALID_ANOMALY_METHODS, + _VALID_ANOMALY_OVERLAP_MODES, + _VALID_ANOMALY_SENSITIVITY, + _VALID_TREND_METHODS, + _can_read_entity, _normalize_recorder_timestamp, _require_admin, ws_clear_cache, ws_delete_dev_events, ws_delete_event, + ws_get_anomalies, ws_get_events, + ws_get_history, ws_update_event, ) @@ -85,12 +100,26 @@ def _make_hass(store: object) -> MagicMock: return hass -def _make_connection(*, is_admin: bool = True) -> MagicMock: - """Return a mock ActiveConnection. Defaults to an admin user.""" +def _make_connection( + *, + is_admin: bool = True, + allowed_entities: list[str] | None = None, +) -> MagicMock: + """Return a mock ActiveConnection. + + *allowed_entities* is only relevant for non-admin users. When provided, + ``user.permissions.check_entity`` returns True only for listed entity IDs. + When omitted for a non-admin user, all entity checks return False (no access). + """ connection = MagicMock() connection.send_result = MagicMock() connection.send_error = MagicMock() connection.user.is_admin = is_admin + if not is_admin: + allowed = set(allowed_entities or []) + connection.user.permissions.check_entity.side_effect = ( + lambda entity_id, _policy: entity_id in allowed + ) return connection @@ -352,3 +381,456 @@ async def test_GIVEN_cache_present_and_entity_id_provided_WHEN_called_THEN_clear cache.clear_entity.assert_called_once_with("sensor.temp") result = connection.send_result.call_args[0][1] assert result["cleared"] == 2 + + async def test_GIVEN_non_admin_user_WHEN_called_THEN_raises_unauthorized(self): + cache = MagicMock() + hass = MagicMock() + hass.data = {DOMAIN: {"anomaly_cache": cache}} + connection = _make_connection(is_admin=False) + msg = {"id": 1} + + with pytest.raises(Unauthorized): + await ws_clear_cache(hass, connection, msg) + + cache.clear_all.assert_not_called() + connection.send_result.assert_not_called() + + +# --------------------------------------------------------------------------- +# Validation constants — unit tests for regex patterns and allowed values +# --------------------------------------------------------------------------- + + +class DescribeValidationConstants: + # --- _RE_MDI_ICON --- + + def test_GIVEN_valid_mdi_icon_WHEN_matched_THEN_passes(self): + validator = vol.Match(_RE_MDI_ICON) + assert validator("mdi:home") == "mdi:home" + assert validator("mdi:thermometer-lines") == "mdi:thermometer-lines" + assert validator("mdi:ab1") == "mdi:ab1" + + def test_GIVEN_invalid_icon_WHEN_matched_THEN_raises(self): + validator = vol.Match(_RE_MDI_ICON) + with pytest.raises(vol.Invalid): + validator("home") + with pytest.raises(vol.Invalid): + validator("fa:home") + with pytest.raises(vol.Invalid): + validator("mdi:") + with pytest.raises(vol.Invalid): + validator("mdi:Home") # uppercase not allowed + + def test_GIVEN_icon_exceeds_max_len_WHEN_validated_THEN_raises(self): + validator = vol.Length(max=_MAX_LEN_ICON) + with pytest.raises(vol.Invalid): + validator("x" * (_MAX_LEN_ICON + 1)) + + # --- _RE_HEX_COLOR --- + + def test_GIVEN_valid_hex_colors_WHEN_matched_THEN_passes(self): + validator = vol.Match(_RE_HEX_COLOR) + assert validator("#fff") == "#fff" + assert validator("#FF0000") == "#FF0000" + assert validator("#aabbccdd") == "#aabbccdd" + + def test_GIVEN_invalid_hex_color_WHEN_matched_THEN_raises(self): + validator = vol.Match(_RE_HEX_COLOR) + with pytest.raises(vol.Invalid): + validator("red") + with pytest.raises(vol.Invalid): + validator("ff0000") # missing # + with pytest.raises(vol.Invalid): + validator("#gg0000") # invalid hex chars + + def test_GIVEN_color_exceeds_max_len_WHEN_validated_THEN_raises(self): + validator = vol.Length(max=_MAX_LEN_COLOR) + with pytest.raises(vol.Invalid): + validator("#" + "0" * _MAX_LEN_COLOR) + + # --- _RE_DURATION --- + + def test_GIVEN_valid_duration_strings_WHEN_matched_THEN_passes(self): + validator = vol.Match(_RE_DURATION) + for value in ["1s", "30m", "24h", "7d", "100s"]: + assert validator(value) == value + + def test_GIVEN_invalid_duration_WHEN_matched_THEN_raises(self): + validator = vol.Match(_RE_DURATION) + with pytest.raises(vol.Invalid): + validator("1hour") + with pytest.raises(vol.Invalid): + validator("h") + with pytest.raises(vol.Invalid): + validator("1H") # uppercase not allowed + with pytest.raises(vol.Invalid): + validator("") + + def test_GIVEN_window_exceeds_max_len_WHEN_validated_THEN_raises(self): + validator = vol.Length(max=_MAX_LEN_WINDOW) + with pytest.raises(vol.Invalid): + validator("1" * (_MAX_LEN_WINDOW + 1) + "h") + + # --- _VALID_ANOMALY_SENSITIVITY --- + + def test_GIVEN_valid_sensitivity_WHEN_checked_THEN_passes(self): + validator = vol.In(_VALID_ANOMALY_SENSITIVITY) + for value in ["low", "medium", "high"]: + assert validator(value) == value + + def test_GIVEN_invalid_sensitivity_WHEN_checked_THEN_raises(self): + validator = vol.In(_VALID_ANOMALY_SENSITIVITY) + with pytest.raises(vol.Invalid): + validator("extreme") + + # --- _VALID_ANOMALY_OVERLAP_MODES --- + + def test_GIVEN_valid_overlap_mode_WHEN_checked_THEN_passes(self): + validator = vol.In(_VALID_ANOMALY_OVERLAP_MODES) + for value in ["all", "highlight", "only"]: + assert validator(value) == value + + def test_GIVEN_invalid_overlap_mode_WHEN_checked_THEN_raises(self): + validator = vol.In(_VALID_ANOMALY_OVERLAP_MODES) + with pytest.raises(vol.Invalid): + validator("none") + + # --- _VALID_TREND_METHODS --- + + def test_GIVEN_valid_trend_method_WHEN_checked_THEN_passes(self): + validator = vol.In(_VALID_TREND_METHODS) + for value in ["rolling_average", "linear_trend"]: + assert validator(value) == value + + def test_GIVEN_invalid_trend_method_WHEN_checked_THEN_raises(self): + validator = vol.In(_VALID_TREND_METHODS) + with pytest.raises(vol.Invalid): + validator("polynomial") + + # --- _VALID_ANOMALY_METHODS list --- + + def test_GIVEN_valid_anomaly_methods_WHEN_checked_THEN_all_pass(self): + for method in _VALID_ANOMALY_METHODS: + assert vol.In(_VALID_ANOMALY_METHODS)(method) == method + + def test_GIVEN_invalid_anomaly_method_in_list_WHEN_validated_THEN_raises(self): + schema = vol.All([vol.In(_VALID_ANOMALY_METHODS)], vol.Length(min=1)) + with pytest.raises(vol.Invalid): + schema(["not_a_method"]) + + def test_GIVEN_empty_anomaly_methods_list_WHEN_validated_THEN_raises(self): + schema = vol.All([vol.In(_VALID_ANOMALY_METHODS)], vol.Length(min=1)) + with pytest.raises(vol.Invalid): + schema([]) + + # --- comparison_time_offset_ms range --- + + def test_GIVEN_offset_within_range_WHEN_validated_THEN_passes(self): + validator = vol.Range(min=-315_576_000_000, max=315_576_000_000) + assert validator(0) == 0 + assert validator(-315_576_000_000) == -315_576_000_000 + assert validator(315_576_000_000) == 315_576_000_000 + + def test_GIVEN_offset_outside_range_WHEN_validated_THEN_raises(self): + validator = vol.Range(min=-315_576_000_000, max=315_576_000_000) + with pytest.raises(vol.Invalid): + validator(315_576_000_001) + with pytest.raises(vol.Invalid): + validator(-315_576_000_001) + + # --- message / annotation length cap --- + + def test_GIVEN_message_within_limit_WHEN_validated_THEN_passes(self): + validator = vol.Length(max=_MAX_LEN_MESSAGE) + assert validator("hello") == "hello" + assert validator("x" * _MAX_LEN_MESSAGE) == "x" * _MAX_LEN_MESSAGE + + def test_GIVEN_message_exceeds_limit_WHEN_validated_THEN_raises(self): + validator = vol.Length(max=_MAX_LEN_MESSAGE) + with pytest.raises(vol.Invalid): + validator("x" * (_MAX_LEN_MESSAGE + 1)) + + +# --------------------------------------------------------------------------- +# _can_read_entity — unit tests for the permission helper +# --------------------------------------------------------------------------- + + +class DescribeCanReadEntity: + def test_GIVEN_admin_user_WHEN_called_THEN_returns_true_for_any_entity(self): + user = MagicMock() + user.is_admin = True + assert _can_read_entity(user, "sensor.secret") is True + + def test_GIVEN_non_admin_with_permission_WHEN_called_THEN_returns_true(self): + user = MagicMock() + user.is_admin = False + user.permissions.check_entity.return_value = True + assert _can_read_entity(user, "sensor.allowed") is True + + def test_GIVEN_non_admin_without_permission_WHEN_called_THEN_returns_false(self): + user = MagicMock() + user.is_admin = False + user.permissions.check_entity.return_value = False + assert _can_read_entity(user, "sensor.forbidden") is False + + def test_GIVEN_permissions_raises_WHEN_called_THEN_returns_true_permissive(self): + """Unexpected permission errors should not block the caller.""" + user = MagicMock() + user.is_admin = False + user.permissions.check_entity.side_effect = RuntimeError("boom") + assert _can_read_entity(user, "sensor.any") is True + + +# --------------------------------------------------------------------------- +# ws_get_events — entity permission filtering +# --------------------------------------------------------------------------- + + +class DescribeWsGetEventsPermissions: + async def test_GIVEN_admin_user_with_entity_filter_WHEN_called_THEN_passes_filter_unchanged( + self, + ): + store = MagicMock() + store.get_events.return_value = [] + hass = _make_hass(store) + connection = _make_connection(is_admin=True) + msg = { + "id": 1, + "type": f"{DOMAIN}/events", + "entity_ids": ["sensor.a", "sensor.b"], + } + + await ws_get_events(hass, connection, msg) + + store.get_events.assert_called_once_with( + start=None, end=None, entity_ids=["sensor.a", "sensor.b"] + ) + + async def test_GIVEN_non_admin_user_WHEN_entity_ids_include_forbidden_THEN_forbidden_stripped( + self, + ): + store = MagicMock() + store.get_events.return_value = [] + hass = _make_hass(store) + # sensor.a is allowed, sensor.secret is not + connection = _make_connection(is_admin=False, allowed_entities=["sensor.a"]) + msg = { + "id": 1, + "type": f"{DOMAIN}/events", + "entity_ids": ["sensor.a", "sensor.secret"], + } + + await ws_get_events(hass, connection, msg) + + store.get_events.assert_called_once_with( + start=None, end=None, entity_ids=["sensor.a"] + ) + + async def test_GIVEN_non_admin_user_WHEN_all_entity_ids_forbidden_THEN_empty_filter_passed( + self, + ): + store = MagicMock() + store.get_events.return_value = [] + hass = _make_hass(store) + connection = _make_connection(is_admin=False, allowed_entities=[]) + msg = { + "id": 1, + "type": f"{DOMAIN}/events", + "entity_ids": ["sensor.secret"], + } + + await ws_get_events(hass, connection, msg) + + store.get_events.assert_called_once_with(start=None, end=None, entity_ids=[]) + + async def test_GIVEN_non_admin_user_WHEN_no_entity_filter_THEN_store_called_with_none( + self, + ): + """No entity_ids in message → pass None to store (no filtering applied).""" + store = MagicMock() + store.get_events.return_value = [] + hass = _make_hass(store) + connection = _make_connection(is_admin=False, allowed_entities=[]) + msg = {"id": 1, "type": f"{DOMAIN}/events"} + + await ws_get_events(hass, connection, msg) + + store.get_events.assert_called_once_with(start=None, end=None, entity_ids=None) + + +# --------------------------------------------------------------------------- +# ws_get_history — entity permission check +# --------------------------------------------------------------------------- + + +def _make_hass_for_history() -> MagicMock: + """hass mock whose recorder instance has an awaitable async_add_executor_job.""" + import custom_components.hass_datapoints.websocket_api as ws_mod # noqa: PLC0415 + + hass = MagicMock() + hass.data = {DOMAIN: {}} + recorder_instance = MagicMock() + recorder_instance.async_add_executor_job = AsyncMock(return_value=[]) + # get_instance is imported at module level; patch it on the module object. + ws_mod.get_instance = MagicMock(return_value=recorder_instance) + return hass + + +class DescribeWsGetHistoryPermissions: + async def test_GIVEN_admin_user_WHEN_entity_requested_THEN_proceeds(self): + hass = _make_hass_for_history() + connection = _make_connection(is_admin=True) + msg = { + "id": 1, + "entity_id": "sensor.secret", + "start_time": "2024-01-01T00:00:00+00:00", + "end_time": "2024-01-02T00:00:00+00:00", + "interval": "raw", + "aggregate": "mean", + } + + await ws_get_history(hass, connection, msg) + + connection.send_error.assert_not_called() + connection.send_result.assert_called_once() + + async def test_GIVEN_non_admin_user_with_permission_WHEN_called_THEN_proceeds(self): + hass = _make_hass_for_history() + connection = _make_connection( + is_admin=False, allowed_entities=["sensor.allowed"] + ) + msg = { + "id": 1, + "entity_id": "sensor.allowed", + "start_time": "2024-01-01T00:00:00+00:00", + "end_time": "2024-01-02T00:00:00+00:00", + "interval": "raw", + "aggregate": "mean", + } + + await ws_get_history(hass, connection, msg) + + connection.send_error.assert_not_called() + connection.send_result.assert_called_once() + + async def test_GIVEN_non_admin_user_without_permission_WHEN_called_THEN_sends_unauthorized( + self, + ): + hass = _make_hass_for_history() + connection = _make_connection(is_admin=False, allowed_entities=[]) + msg = { + "id": 1, + "entity_id": "sensor.secret", + "start_time": "2024-01-01T00:00:00+00:00", + "end_time": "2024-01-02T00:00:00+00:00", + "interval": "raw", + "aggregate": "mean", + } + + await ws_get_history(hass, connection, msg) + + connection.send_result.assert_not_called() + connection.send_error.assert_called_once() + error_code = connection.send_error.call_args[0][1] + assert error_code == "unauthorized" + + +# --------------------------------------------------------------------------- +# ws_get_anomalies — entity permission check +# --------------------------------------------------------------------------- + + +def _make_hass_for_anomalies() -> MagicMock: + """hass mock suitable for ws_get_anomalies (cache absent, executor returns []).""" + import custom_components.hass_datapoints.websocket_api as ws_mod # noqa: PLC0415 + + hass = MagicMock() + hass.data = {DOMAIN: {}} # no anomaly_cache + recorder_instance = MagicMock() + recorder_instance.async_add_executor_job = AsyncMock(return_value=[]) + ws_mod.get_instance = MagicMock(return_value=recorder_instance) + hass.async_add_executor_job = AsyncMock(return_value=[]) + return hass + + +def _anomaly_msg(entity_id: str, comparison_entity_id: str | None = None) -> dict: + msg = { + "id": 1, + "entity_id": entity_id, + "start_time": "2024-01-01T00:00:00+00:00", + "end_time": "2024-01-02T00:00:00+00:00", + "anomaly_methods": ["iqr"], + "anomaly_sensitivity": "medium", + "anomaly_overlap_mode": "all", + "anomaly_rate_window": "1h", + "anomaly_zscore_window": "24h", + "anomaly_persistence_window": "1h", + "trend_method": "rolling_average", + "trend_window": "24h", + "comparison_time_offset_ms": 0, + } + if comparison_entity_id: + msg["comparison_entity_id"] = comparison_entity_id + msg["comparison_start_time"] = "2024-01-01T00:00:00+00:00" + msg["comparison_end_time"] = "2024-01-02T00:00:00+00:00" + return msg + + +class DescribeWsGetAnomaliesPermissions: + async def test_GIVEN_non_admin_without_permission_WHEN_called_THEN_sends_unauthorized( + self, + ): + hass = _make_hass_for_anomalies() + connection = _make_connection(is_admin=False, allowed_entities=[]) + + await ws_get_anomalies(hass, connection, _anomaly_msg("sensor.secret")) + + connection.send_result.assert_not_called() + connection.send_error.assert_called_once() + assert connection.send_error.call_args[0][1] == "unauthorized" + + async def test_GIVEN_admin_WHEN_called_THEN_proceeds(self): + hass = _make_hass_for_anomalies() + connection = _make_connection(is_admin=True) + + await ws_get_anomalies(hass, connection, _anomaly_msg("sensor.any")) + + connection.send_error.assert_not_called() + connection.send_result.assert_called_once() + + async def test_GIVEN_non_admin_with_primary_permission_but_not_comparison_WHEN_called_THEN_sends_unauthorized( + self, + ): + hass = _make_hass_for_anomalies() + connection = _make_connection( + is_admin=False, allowed_entities=["sensor.primary"] + ) + + await ws_get_anomalies( + hass, + connection, + _anomaly_msg("sensor.primary", comparison_entity_id="sensor.forbidden"), + ) + + connection.send_result.assert_not_called() + connection.send_error.assert_called_once() + assert connection.send_error.call_args[0][1] == "unauthorized" + + async def test_GIVEN_non_admin_with_both_permissions_WHEN_called_THEN_proceeds( + self, + ): + hass = _make_hass_for_anomalies() + connection = _make_connection( + is_admin=False, + allowed_entities=["sensor.primary", "sensor.comparison"], + ) + + await ws_get_anomalies( + hass, + connection, + _anomaly_msg("sensor.primary", comparison_entity_id="sensor.comparison"), + ) + + connection.send_error.assert_not_called() + connection.send_result.assert_called_once() diff --git a/tsconfig.json b/tsconfig.json index 27ab3c0d..4e6aa78b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,6 +3,7 @@ "target": "ES2022", "module": "ESNext", "moduleResolution": "Bundler", + "experimentalDecorators": true, "allowJs": false, "checkJs": false, "noEmit": true,