diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index be773f9ba8..cb654867b5 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,7 +5,7 @@ "context": ".." }, "appPort": "8124:8123", - "postCreateCommand": "sudo apt update && sudo apt upgrade -y && sudo apt install -y libpcap-dev", + "postCreateCommand": "./.devcontainer/post_create.sh", "postStartCommand": "script/bootstrap", "containerEnv": { "DEV_CONTAINER": "1", diff --git a/.devcontainer/post_create.sh b/.devcontainer/post_create.sh new file mode 100755 index 0000000000..f71a688f30 --- /dev/null +++ b/.devcontainer/post_create.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# This script will run after the container is created + +# add github cli +(type -p wget >/dev/null || (sudo apt update && sudo apt-get install wget -y)) \ + && sudo mkdir -p -m 755 /etc/apt/keyrings \ + && out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg \ + && cat $out | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \ + && sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null + +# Update package lists +sudo apt-get update + +sudo apt upgrade -y + +# Install necessary packages +sudo apt-get install -y libpcap-dev gh + +# Display a message +echo "Post-create script has been executed successfully." \ No newline at end of file diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4753c58f2a..cfa1b10126 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -37,7 +37,7 @@ jobs: - name: Build resources run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages - name: Setup lint cache - uses: actions/cache@v4.2.0 + uses: actions/cache@v4.2.1 with: path: | node_modules/.cache/prettier @@ -89,7 +89,7 @@ jobs: env: IS_TEST: "true" - name: Upload bundle stats - uses: actions/upload-artifact@v4.6.0 + uses: actions/upload-artifact@v4.6.1 with: name: frontend-bundle-stats path: build/stats/*.json @@ -113,7 +113,7 @@ jobs: env: IS_TEST: "true" - name: Upload bundle stats - uses: actions/upload-artifact@v4.6.0 + uses: actions/upload-artifact@v4.6.1 with: name: supervisor-bundle-stats path: build/stats/*.json diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 231bcfd15e..2c927c6736 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -57,14 +57,14 @@ jobs: run: tar -czvf translations.tar.gz translations - name: Upload build artifacts - uses: actions/upload-artifact@v4.6.0 + uses: actions/upload-artifact@v4.6.1 with: name: wheels path: dist/home_assistant_frontend*.whl if-no-files-found: error - name: Upload translations - uses: actions/upload-artifact@v4.6.0 + uses: actions/upload-artifact@v4.6.1 with: name: translations path: translations.tar.gz diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 676e27e6d9..401f183930 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,6 +1,42 @@ { "version": "2.0.0", "tasks": [ + { + "label": "Develop and serve Frontend", + "type": "shell", + "command": "script/develop_and_serve -c ${input:coreUrl}", + // Sync changes here to other tasks until issue resolved + // https://github.com/Microsoft/vscode/issues/61497 + "problemMatcher": { + "owner": "ha-build", + "source": "ha-build", + "fileLocation": "absolute", + "severity": "error", + "pattern": [ + { + "regexp": "(SyntaxError): (.+): (.+) \\((\\d+):(\\d+)\\)", + "severity": 1, + "file": 2, + "message": 3, + "line": 4, + "column": 5 + } + ], + "background": { + "activeOnStart": true, + "beginsPattern": "Changes detected. Starting compilation", + "endsPattern": "Build done @" + } + }, + "isBackground": true, + "group": { + "kind": "build", + "isDefault": true + }, + "runOptions": { + "instanceLimit": 1 + } + }, { "label": "Develop Frontend", "type": "gulp", @@ -241,6 +277,12 @@ "id": "supervisorToken", "type": "promptString", "description": "The token for the Remote API proxy add-on" + }, + { + "id": "coreUrl", + "type": "promptString", + "description": "The URL of the Home Assistant Core instance", + "default": "http://127.0.0.1:8123" } ] } diff --git a/build-scripts/eslint.config.mjs b/build-scripts/eslint.config.mjs index 6c33f43ba5..781dcf3898 100644 --- a/build-scripts/eslint.config.mjs +++ b/build-scripts/eslint.config.mjs @@ -1,16 +1,16 @@ +// @ts-check + +import tseslint from "typescript-eslint"; import rootConfig from "../eslint.config.mjs"; -export default [ - ...rootConfig, - { - rules: { - "no-console": "off", - "import/no-extraneous-dependencies": "off", - "import/extensions": "off", - "import/no-dynamic-require": "off", - "global-require": "off", - "@typescript-eslint/no-require-imports": "off", - "prefer-arrow-callback": "off", - }, +export default tseslint.config(...rootConfig, { + rules: { + "no-console": "off", + "import/no-extraneous-dependencies": "off", + "import/extensions": "off", + "import/no-dynamic-require": "off", + "global-require": "off", + "@typescript-eslint/no-require-imports": "off", + "prefer-arrow-callback": "off", }, -]; +}); diff --git a/build-scripts/gulp/gather-static.js b/build-scripts/gulp/gather-static.js index 5b9a668675..ab3984552e 100644 --- a/build-scripts/gulp/gather-static.js +++ b/build-scripts/gulp/gather-static.js @@ -90,6 +90,14 @@ function copyMapPanel(staticDir) { npmPath("leaflet/dist/leaflet.css"), staticPath("images/leaflet/") ); + copyFileDir( + npmPath("leaflet.markercluster/dist/MarkerCluster.css"), + staticPath("images/leaflet/") + ); + copyFileDir( + npmPath("leaflet.markercluster/dist/MarkerCluster.Default.css"), + staticPath("images/leaflet/") + ); fs.copySync( npmPath("leaflet/dist/images"), staticPath("images/leaflet/images/") diff --git a/eslint.config.mjs b/eslint.config.mjs index a5986d29af..6907fdecbe 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,11 +1,16 @@ +// @ts-check + /* eslint-disable import/no-extraneous-dependencies */ import unusedImports from "eslint-plugin-unused-imports"; import globals from "globals"; -import tsParser from "@typescript-eslint/parser"; import path from "node:path"; import { fileURLToPath } from "node:url"; import js from "@eslint/js"; import { FlatCompat } from "@eslint/eslintrc"; +import tseslint from "typescript-eslint"; +import eslintConfigPrettier from "eslint-config-prettier"; +import { configs as litConfigs } from "eslint-plugin-lit"; +import { configs as wcConfigs } from "eslint-plugin-wc"; const _filename = fileURLToPath(import.meta.url); const _dirname = path.dirname(_filename); @@ -15,17 +20,14 @@ const compat = new FlatCompat({ allConfig: js.configs.all, }); -export default [ - ...compat.extends( - "airbnb-base", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/strict", - "plugin:@typescript-eslint/stylistic", - "plugin:wc/recommended", - "plugin:lit/all", - "plugin:lit-a11y/recommended", - "prettier" - ), +export default tseslint.config( + ...compat.extends("airbnb-base", "plugin:lit-a11y/recommended"), + eslintConfigPrettier, + litConfigs["flat/all"], + tseslint.configs.recommended, + tseslint.configs.strict, + tseslint.configs.stylistic, + wcConfigs["flat/recommended"], { plugins: { "unused-imports": unusedImports, @@ -43,7 +45,7 @@ export default [ Polymer: true, }, - parser: tsParser, + parser: tseslint.parser, ecmaVersion: 2020, sourceType: "module", @@ -184,5 +186,5 @@ export default [ ], "no-use-before-define": "off", }, - }, -]; + } +); diff --git a/gallery/eslint.config.mjs b/gallery/eslint.config.mjs index 95108521b0..1860147df8 100644 --- a/gallery/eslint.config.mjs +++ b/gallery/eslint.config.mjs @@ -1,10 +1,10 @@ +// @ts-check + +import tseslint from "typescript-eslint"; import rootConfig from "../eslint.config.mjs"; -export default [ - ...rootConfig, - { - rules: { - "no-console": "off", - }, +export default tseslint.config(...rootConfig, { + rules: { + "no-console": "off", }, -]; +}); diff --git a/landing-page/src/components/landing-page-network.ts b/landing-page/src/components/landing-page-network.ts index 7e32877c84..bf5a7e9115 100644 --- a/landing-page/src/components/landing-page-network.ts +++ b/landing-page/src/components/landing-page-network.ts @@ -17,6 +17,7 @@ import "../../../src/components/ha-alert"; import { ALTERNATIVE_DNS_SERVERS, getSupervisorNetworkInfo, + pingSupervisor, setSupervisorNetworkDns, } from "../data/supervisor"; import { fireEvent } from "../../../src/common/dom/fire_event"; @@ -85,7 +86,28 @@ class LandingPageNetwork extends LitElement { protected firstUpdated(_changedProperties: PropertyValues): void { super.firstUpdated(_changedProperties); - this._fetchSupervisorInfo(); + this._pingSupervisor(); + } + + private _schedulePingSupervisor() { + setTimeout( + () => this._pingSupervisor(), + SCHEDULE_FETCH_NETWORK_INFO_SECONDS * 1000 + ); + } + + private async _pingSupervisor() { + try { + const response = await pingSupervisor(); + if (!response.ok) { + throw new Error("Failed to ping supervisor, assume update in progress"); + } + this._fetchSupervisorInfo(); + } catch (err) { + // eslint-disable-next-line no-console + console.error(err); + this._schedulePingSupervisor(); + } } private _scheduleFetchSupervisorInfo() { diff --git a/landing-page/src/data/supervisor.ts b/landing-page/src/data/supervisor.ts index 8b75f3f753..41c28297e0 100644 --- a/landing-page/src/data/supervisor.ts +++ b/landing-page/src/data/supervisor.ts @@ -18,7 +18,7 @@ export const ALTERNATIVE_DNS_SERVERS: { ]; export async function getSupervisorLogs(lines = 100) { - return fetch(`/supervisor/supervisor/logs?lines=${lines}`, { + return fetch(`/supervisor-api/supervisor/logs?lines=${lines}`, { headers: { Accept: "text/plain", }, @@ -26,22 +26,26 @@ export async function getSupervisorLogs(lines = 100) { } export async function getSupervisorLogsFollow(lines = 500) { - return fetch(`/supervisor/supervisor/logs/follow?lines=${lines}`, { + return fetch(`/supervisor-api/supervisor/logs/follow?lines=${lines}`, { headers: { Accept: "text/plain", }, }); } +export async function pingSupervisor() { + return fetch("/supervisor-api/supervisor/ping"); +} + export async function getSupervisorNetworkInfo() { - return fetch("/supervisor/network/info"); + return fetch("/supervisor-api/network/info"); } export const setSupervisorNetworkDns = async ( dnsServerIndex: number, primaryInterface: string ) => - fetch(`/supervisor/network/interface/${primaryInterface}/update`, { + fetch(`/supervisor-api/network/interface/${primaryInterface}/update`, { method: "POST", body: JSON.stringify({ ipv4: { diff --git a/package.json b/package.json index 939b7a9dbe..e4c0c7fe7d 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "license": "Apache-2.0", "type": "module", "dependencies": { - "@babel/runtime": "7.26.7", + "@babel/runtime": "7.26.9", "@braintree/sanitize-url": "7.1.1", "@codemirror/autocomplete": "6.18.6", "@codemirror/commands": "6.8.0", @@ -34,7 +34,7 @@ "@codemirror/legacy-modes": "6.4.3", "@codemirror/search": "6.5.9", "@codemirror/state": "6.5.2", - "@codemirror/view": "6.36.2", + "@codemirror/view": "6.36.3", "@egjs/hammerjs": "2.0.17", "@formatjs/intl-datetimeformat": "6.17.3", "@formatjs/intl-displaynames": "6.8.10", @@ -53,9 +53,9 @@ "@fullcalendar/timegrid": "6.1.15", "@lezer/highlight": "1.2.1", "@lit-labs/context": "0.4.1", - "@lit-labs/motion": "1.0.7", - "@lit-labs/observers": "2.0.4", - "@lit-labs/virtualizer": "2.0.15", + "@lit-labs/motion": "1.0.8", + "@lit-labs/observers": "2.0.5", + "@lit-labs/virtualizer": "2.1.0", "@lrnwebcomponents/simple-tooltip": "8.0.2", "@material/chips": "=14.0.0-canary.53b3cad2f.0", "@material/data-table": "=14.0.0-canary.53b3cad2f.0", @@ -92,8 +92,8 @@ "@replit/codemirror-indentation-markers": "6.5.3", "@shoelace-style/shoelace": "2.20.0", "@thomasloven/round-slider": "0.6.0", - "@vaadin/combo-box": "24.6.4", - "@vaadin/vaadin-themable-mixin": "24.6.4", + "@vaadin/combo-box": "24.6.5", + "@vaadin/vaadin-themable-mixin": "24.6.5", "@vibrant/color": "4.0.0", "@vue/web-component-wrapper": "1.3.0", "@webcomponents/scoped-custom-element-registry": "0.0.9", @@ -121,6 +121,7 @@ "js-yaml": "4.1.0", "leaflet": "1.9.4", "leaflet-draw": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch", + "leaflet.markercluster": "1.5.3", "lit": "2.8.0", "lit-html": "2.8.0", "luxon": "3.5.0", @@ -154,20 +155,20 @@ "xss": "1.0.15" }, "devDependencies": { - "@babel/core": "7.26.8", + "@babel/core": "7.26.9", "@babel/helper-define-polyfill-provider": "0.6.3", "@babel/plugin-proposal-decorators": "7.25.9", - "@babel/plugin-transform-runtime": "7.26.8", - "@babel/preset-env": "7.26.8", + "@babel/plugin-transform-runtime": "7.26.9", + "@babel/preset-env": "7.26.9", "@babel/preset-typescript": "7.26.0", "@bundle-stats/plugin-webpack-filter": "4.18.2", - "@lokalise/node-api": "13.1.0", + "@lokalise/node-api": "13.2.0", "@octokit/auth-oauth-device": "7.1.3", - "@octokit/plugin-retry": "7.1.3", - "@octokit/rest": "21.1.0", + "@octokit/plugin-retry": "7.1.4", + "@octokit/rest": "21.1.1", "@rsdoctor/rspack-plugin": "0.4.13", - "@rspack/cli": "1.2.3", - "@rspack/core": "1.2.3", + "@rspack/cli": "1.2.5", + "@rspack/core": "1.2.5", "@types/babel__plugin-transform-runtime": "7.9.5", "@types/chromecast-caf-receiver": "6.0.21", "@types/chromecast-caf-sender": "1.0.11", @@ -177,6 +178,7 @@ "@types/js-yaml": "4.0.9", "@types/leaflet": "1.9.16", "@types/leaflet-draw": "1.0.11", + "@types/leaflet.markercluster": "1.5.5", "@types/lodash.merge": "4.6.9", "@types/luxon": "3.4.2", "@types/mocha": "10.0.10", @@ -185,9 +187,7 @@ "@types/tar": "6.1.13", "@types/ua-parser-js": "0.7.39", "@types/webspeechapi": "0.0.29", - "@typescript-eslint/eslint-plugin": "8.24.0", - "@typescript-eslint/parser": "8.24.0", - "@vitest/coverage-v8": "3.0.5", + "@vitest/coverage-v8": "3.0.6", "babel-loader": "9.2.1", "babel-plugin-template-html-minifier": "4.1.0", "browserslist-useragent-regexp": "4.1.3", @@ -200,7 +200,7 @@ "eslint-plugin-lit": "1.15.0", "eslint-plugin-lit-a11y": "4.1.4", "eslint-plugin-unused-imports": "4.1.4", - "eslint-plugin-wc": "2.2.0", + "eslint-plugin-wc": "2.2.1", "fancy-log": "2.0.0", "fs-extra": "11.3.0", "glob": "11.0.1", @@ -226,7 +226,9 @@ "terser-webpack-plugin": "5.3.11", "ts-lit-plugin": "2.0.2", "typescript": "5.7.3", - "vitest": "3.0.5", + "typescript-eslint": "8.24.1", + "vite-tsconfig-paths": "5.1.4", + "vitest": "3.0.6", "webpack-stats-plugin": "1.1.3", "webpackbar": "7.0.0", "workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch" @@ -240,7 +242,7 @@ "clean-css": "5.3.3", "@lit/reactive-element": "1.6.3", "@fullcalendar/daygrid": "6.1.15", - "globals": "15.15.0", + "globals": "16.0.0", "tslib": "2.8.1" }, "packageManager": "yarn@4.6.0" diff --git a/src/common/color/convert-color.ts b/src/common/color/convert-color.ts index 5afac322e8..2253b0fe24 100644 --- a/src/common/color/convert-color.ts +++ b/src/common/color/convert-color.ts @@ -136,11 +136,18 @@ export function theme2hex(themeColor: string): string { } const rgbFromColorName = colors[themeColor]; - if (!rgbFromColorName) { - // We have a named color, and there's nothing in the table, - // so nothing further we can do with it. - // Compare/border/background color will all be the same. - return themeColor; + if (rgbFromColorName) { + return rgb2hex(rgbFromColorName); } - return rgb2hex(rgbFromColorName); + + const rgbMatch = themeColor.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/); + if (rgbMatch) { + const [, r, g, b] = rgbMatch.map(Number); + return rgb2hex([r, g, b]); + } + + // We have a named color, and there's nothing in the table, + // so nothing further we can do with it. + // Compare/border/background color will all be the same. + return themeColor; } diff --git a/src/common/dom/setup-leaflet-map.ts b/src/common/dom/setup-leaflet-map.ts index 21de1f050b..d94ebfa846 100644 --- a/src/common/dom/setup-leaflet-map.ts +++ b/src/common/dom/setup-leaflet-map.ts @@ -16,11 +16,30 @@ export const setupLeafletMap = async ( const Leaflet = (await import("leaflet")).default as LeafletModuleType; Leaflet.Icon.Default.imagePath = "/static/images/leaflet/images/"; + await import("leaflet.markercluster"); + const map = Leaflet.map(mapElement); const style = document.createElement("link"); style.setAttribute("href", "/static/images/leaflet/leaflet.css"); style.setAttribute("rel", "stylesheet"); mapElement.parentNode.appendChild(style); + + const markerClusterStyle = document.createElement("link"); + markerClusterStyle.setAttribute( + "href", + "/static/images/leaflet/MarkerCluster.css" + ); + markerClusterStyle.setAttribute("rel", "stylesheet"); + mapElement.parentNode.appendChild(markerClusterStyle); + + const defaultMarkerClusterStyle = document.createElement("link"); + defaultMarkerClusterStyle.setAttribute( + "href", + "/static/images/leaflet/MarkerCluster.Default.css" + ); + defaultMarkerClusterStyle.setAttribute("rel", "stylesheet"); + mapElement.parentNode.appendChild(defaultMarkerClusterStyle); + map.setView([52.3731339, 4.8903147], 13); const tileLayer = createTileLayer(Leaflet).addTo(map); diff --git a/src/common/entity/compute_domain.ts b/src/common/entity/compute_domain.ts index 29e5688859..005bdb25b3 100644 --- a/src/common/entity/compute_domain.ts +++ b/src/common/entity/compute_domain.ts @@ -1,2 +1,2 @@ export const computeDomain = (entityId: string): string => - entityId.substr(0, entityId.indexOf(".")); + entityId.substring(0, entityId.indexOf(".")); diff --git a/src/common/entity/compute_state_display.ts b/src/common/entity/compute_state_display.ts index 49c9d5182a..bd61083c82 100644 --- a/src/common/entity/compute_state_display.ts +++ b/src/common/entity/compute_state_display.ts @@ -120,11 +120,6 @@ export const computeStateDisplayFromEntityAttributes = ( return value; } - if (domain === "datetime") { - const time = new Date(state); - return formatDateTime(time, locale, config); - } - if (["date", "input_datetime", "time"].includes(domain)) { // If trying to display an explicit state, need to parse the explicit state to `Date` then format. // Attributes aren't available, we have to use `state`. @@ -181,6 +176,7 @@ export const computeStateDisplayFromEntityAttributes = ( "tag", "tts", "wake_word", + "datetime", ].includes(domain) || (domain === "sensor" && attributes.device_class === "timestamp") ) { diff --git a/src/common/map/decorated_marker.ts b/src/common/map/decorated_marker.ts new file mode 100644 index 0000000000..69d4cb4cce --- /dev/null +++ b/src/common/map/decorated_marker.ts @@ -0,0 +1,32 @@ +import type { LatLngExpression, Layer, Map, MarkerOptions } from "leaflet"; +import { Marker } from "leaflet"; + +export class DecoratedMarker extends Marker { + decorationLayer: Layer | undefined; + + constructor( + latlng: LatLngExpression, + decorationLayer?: Layer, + options?: MarkerOptions + ) { + super(latlng, options); + + this.decorationLayer = decorationLayer; + } + + onAdd(map: Map) { + super.onAdd(map); + + // If decoration has been provided, add it to the map as well + this.decorationLayer?.addTo(map); + + return this; + } + + onRemove(map: Map) { + // If decoration has been provided, remove it from the map as well + this.decorationLayer?.remove(); + + return super.onRemove(map); + } +} diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 78902bc018..a87b7aa3a2 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -6,7 +6,6 @@ import type { DataZoomComponentOption } from "echarts/components"; import type { EChartsType } from "echarts/core"; import type { ECElementEvent, - SetOptionOpts, XAXisOption, YAXisOption, } from "echarts/types/dist/shared"; @@ -25,6 +24,7 @@ import type { HomeAssistant } from "../../types"; import { isMac } from "../../util/is_mac"; import "../ha-icon-button"; import { formatTimeLabel } from "./axis-label"; +import { ensureArray } from "../../common/array/ensure-array"; export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000; @@ -68,12 +68,16 @@ export class HaChartBase extends LitElement { private _listeners: (() => void)[] = []; + private _originalZrFlush?: () => void; + public disconnectedCallback() { super.disconnectedCallback(); while (this._listeners.length) { this._listeners.pop()!(); } this.chart?.dispose(); + this.chart = undefined; + this._originalZrFlush = undefined; } public connectedCallback() { @@ -86,7 +90,7 @@ export class HaChartBase extends LitElement { listenMediaQuery("(prefers-reduced-motion)", (matches) => { if (this._reducedMotion !== matches) { this._reducedMotion = matches; - this.chart?.setOption({ animation: !this._reducedMotion }); + this._setChartOptions({ animation: !this._reducedMotion }); } }) ); @@ -96,7 +100,7 @@ export class HaChartBase extends LitElement { if ((isMac && ev.key === "Meta") || (!isMac && ev.key === "Control")) { this._modifierPressed = true; if (!this.options?.dataZoom) { - this.chart?.setOption({ dataZoom: this._getDataZoomConfig() }); + this._setChartOptions({ dataZoom: this._getDataZoomConfig() }); } } }; @@ -105,7 +109,7 @@ export class HaChartBase extends LitElement { if ((isMac && ev.key === "Meta") || (!isMac && ev.key === "Control")) { this._modifierPressed = false; if (!this.options?.dataZoom) { - this.chart?.setOption({ dataZoom: this._getDataZoomConfig() }); + this._setChartOptions({ dataZoom: this._getDataZoomConfig() }); } } }; @@ -131,10 +135,8 @@ export class HaChartBase extends LitElement { return; } let chartOptions: ECOption = {}; - const chartUpdateParams: SetOptionOpts = { lazyUpdate: true }; if (changedProps.has("data")) { chartOptions.series = this.data; - chartUpdateParams.replaceMerge = ["series"]; } if (changedProps.has("options")) { chartOptions = { ...chartOptions, ...this._createOptions() }; @@ -142,7 +144,7 @@ export class HaChartBase extends LitElement { chartOptions.dataZoom = this._getDataZoomConfig(); } if (Object.keys(chartOptions).length > 0) { - this.chart.setOption(chartOptions, chartUpdateParams); + this._setChartOptions(chartOptions); } } @@ -509,6 +511,31 @@ export class HaChartBase extends LitElement { return Math.max(this.clientWidth / 2, 200); } + private _setChartOptions(options: ECOption) { + if (!this.chart) { + return; + } + if (!this._originalZrFlush) { + const dataSize = ensureArray(this.data).reduce( + (acc, series) => acc + (series.data as any[]).length, + 0 + ); + if (dataSize > 10000) { + // delay the last bit of the render to avoid blocking the main thread + // this is not that impactful with sampling enabled but it doesn't hurt to have it + const zr = this.chart.getZr(); + this._originalZrFlush = zr.flush; + zr.flush = () => { + setTimeout(() => { + this._originalZrFlush?.call(zr); + }, 5); + }; + } + } + const replaceMerge = options.series ? ["series"] : []; + this.chart.setOption(options, { replaceMerge }); + } + private _handleZoomReset() { this.chart?.dispatchAction({ type: "dataZoom", start: 0, end: 100 }); } diff --git a/src/components/chart/state-history-chart-line.ts b/src/components/chart/state-history-chart-line.ts index f7e60f7804..27de961e41 100644 --- a/src/components/chart/state-history-chart-line.ts +++ b/src/components/chart/state-history-chart-line.ts @@ -354,9 +354,10 @@ export class StateHistoryChartLine extends LitElement { name: nameY, color, symbol: "circle", - step: "end", - animationDurationUpdate: 0, symbolSize: 1, + step: "end", + sampling: "minmax", + animationDurationUpdate: 0, lineStyle: { width: fill ? 0 : 1.5, }, diff --git a/src/components/chart/statistics-chart.ts b/src/components/chart/statistics-chart.ts index 7954d76010..5c2372826c 100644 --- a/src/components/chart/statistics-chart.ts +++ b/src/components/chart/statistics-chart.ts @@ -492,8 +492,8 @@ export class StatisticsChart extends LitElement { : this.hass.localize( `ui.components.statistics_charts.statistic_types.${type}` ), - symbol: "circle", - symbolSize: 0, + symbol: "none", + sampling: "minmax", animationDurationUpdate: 0, lineStyle: { width: 1.5, @@ -511,7 +511,6 @@ export class StatisticsChart extends LitElement { if (band && this.chartType === "line") { series.stack = `band-${statistic_id}`; series.stackStrategy = "all"; - (series as LineSeriesOption).symbol = "none"; if (drawBands && type === "max") { (series as LineSeriesOption).areaStyle = { color: color + "3F", diff --git a/src/components/ha-copy-textfield.ts b/src/components/ha-copy-textfield.ts new file mode 100644 index 0000000000..4980524130 --- /dev/null +++ b/src/components/ha-copy-textfield.ts @@ -0,0 +1,110 @@ +import { customElement, property, state } from "lit/decorators"; +import { css, html, LitElement, nothing } from "lit"; +import { mdiContentCopy, mdiEye, mdiEyeOff } from "@mdi/js"; + +import "./ha-button"; +import "./ha-icon-button"; +import "./ha-svg-icon"; +import "./ha-textfield"; +import type { HomeAssistant } from "../types"; +import { copyToClipboard } from "../common/util/copy-clipboard"; +import { showToast } from "../util/toast"; +import type { HaTextField } from "./ha-textfield"; + +@customElement("ha-copy-textfield") +export class HaCopyTextfield extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: "value" }) public value!: string; + + @property({ attribute: "masked-value" }) public maskedValue?: string; + + @property({ attribute: "label" }) public label?: string; + + @state() private _showMasked = true; + + public render() { + return html` +
+
+
` + : nothing} + @click=${this._focusInput} + > + ${this.maskedValue + ? html`` + : nothing} +
+ + + ${this.label || this.hass.localize("ui.common.copy")} + + + `; + } + + private _focusInput(ev) { + const inputElement = ev.currentTarget as HaTextField; + inputElement.select(); + } + + private _toggleMasked(): void { + this._showMasked = !this._showMasked; + } + + private async _copy(): Promise { + await copyToClipboard(this.value); + showToast(this, { + message: this.hass.localize("ui.common.copied_clipboard"), + }); + } + + static styles = css` + .container { + display: flex; + align-items: center; + gap: 8px; + margin-top: 8px; + } + + .textfield-container { + position: relative; + flex: 1; + } + + .textfield-container ha-textfield { + display: block; + } + + .toggle-unmasked { + position: absolute; + top: 8px; + right: 8px; + inset-inline-start: initial; + inset-inline-end: 8px; + --mdc-icon-button-size: 40px; + --mdc-icon-size: 20px; + color: var(--secondary-text-color); + direction: var(--direction); + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-copy-textfield": HaCopyTextfield; + } +} diff --git a/src/components/ha-form/ha-form-string.ts b/src/components/ha-form/ha-form-string.ts index 306f61bf96..74c8333a0f 100644 --- a/src/components/ha-form/ha-form-string.ts +++ b/src/components/ha-form/ha-form-string.ts @@ -80,7 +80,6 @@ export class HaFormString extends LitElement implements HaFormElement { if (!this.isPassword) return nothing; return html` ${this.selector.text?.type === "password" ? html` map.addLayer(marker)); } - this._mapItems.forEach((marker) => map.addLayer(marker)); this._mapZones.forEach((marker) => map.addLayer(marker)); } diff --git a/src/data/backup.ts b/src/data/backup.ts index 54424f0b21..b65ce73611 100644 --- a/src/data/backup.ts +++ b/src/data/backup.ts @@ -12,6 +12,7 @@ import type { HomeAssistant } from "../types"; import { fileDownload } from "../util/file_download"; import { domainToName } from "./integration"; import type { FrontendLocaleData } from "./translation"; +import type { BackupManagerState, ManagerStateEvent } from "./backup_manager"; import checkValidDate from "../common/datetime/check_valid_date"; import { handleFetchPromise } from "../util/hass-call-api"; @@ -130,7 +131,13 @@ export interface BackupContentExtended extends BackupContent, BackupData {} export interface BackupInfo { backups: BackupContent[]; - backing_up: boolean; + agent_errors: Record; + last_attempted_automatic_backup: string | null; + last_completed_automatic_backup: string | null; + last_non_idle_event: ManagerStateEvent | null; + next_automatic_backup: string | null; + next_automatic_backup_additional: boolean; + state: BackupManagerState; } export interface BackupDetails { diff --git a/src/data/cloud.ts b/src/data/cloud.ts index b7fea8fae2..59882cae75 100644 --- a/src/data/cloud.ts +++ b/src/data/cloud.ts @@ -73,6 +73,7 @@ export interface CloudWebhook { interface CloudLoginBase { hass: HomeAssistant; email: string; + check_connection?: boolean; } export interface CloudLoginPassword extends CloudLoginBase { diff --git a/src/data/hassio/backup.ts b/src/data/hassio/backup.ts index 0879ec28bd..09a410fe6b 100644 --- a/src/data/hassio/backup.ts +++ b/src/data/hassio/backup.ts @@ -233,11 +233,11 @@ export const restoreBackup = async ( type: HassioBackupDetail["type"], backupSlug: string, backupDetails: HassioPartialBackupCreateParams | HassioFullBackupCreateParams, - useSnapshotUrl: boolean + useBackupUrl: boolean ): Promise => { await hass.callApi>( "POST", - `hassio/${useSnapshotUrl ? "snapshots" : "backups"}/${backupSlug}/restore/${type}`, + `hassio/${useBackupUrl ? "backups" : "snapshots"}/${backupSlug}/restore/${type}`, backupDetails ); }; diff --git a/src/dialogs/config-flow/step-flow-create-entry.ts b/src/dialogs/config-flow/step-flow-create-entry.ts index f02f8cec63..64f7ac1b3b 100644 --- a/src/dialogs/config-flow/step-flow-create-entry.ts +++ b/src/dialogs/config-flow/step-flow-create-entry.ts @@ -85,6 +85,7 @@ class StepFlowCreateEntry extends LitElement { assistSatelliteSupportsSetupFlow(this.hass.states[satellite.entity_id]) ) ) { + this.navigateToResult = false; this._flowDone(); showVoiceAssistantSetupDialog(this, { deviceId: devices[0].id, diff --git a/src/dialogs/enter-code/dialog-enter-code.ts b/src/dialogs/enter-code/dialog-enter-code.ts index 5a76df3e64..933567705e 100644 --- a/src/dialogs/enter-code/dialog-enter-code.ts +++ b/src/dialogs/enter-code/dialog-enter-code.ts @@ -40,8 +40,13 @@ export class DialogEnterCode @state() private _showClearButton = false; + @state() private _narrow = false; + public async showDialog(dialogParams: EnterCodeDialogParams): Promise { this._dialogParams = dialogParams; + this._narrow = matchMedia( + "all and (max-width: 450px), all and (max-height: 500px)" + ).matches; await this.updateComplete; } @@ -96,7 +101,7 @@ export class DialogEnterCode >
${BUTTONS.map((value) => diff --git a/src/dialogs/more-info/components/lights/light-color-rgb-picker.ts b/src/dialogs/more-info/components/lights/light-color-rgb-picker.ts index 6436e7d1e4..1cceea263b 100644 --- a/src/dialogs/more-info/components/lights/light-color-rgb-picker.ts +++ b/src/dialogs/more-info/components/lights/light-color-rgb-picker.ts @@ -49,6 +49,8 @@ class LightRgbColorPicker extends LitElement { @state() private _hsPickerValue?: [number, number]; + @state() private _isInteracting?: boolean; + protected render() { if (!this.stateObj) { return nothing; @@ -211,7 +213,10 @@ class LightRgbColorPicker extends LitElement { public willUpdate(changedProps: PropertyValues) { super.willUpdate(changedProps); - if (!changedProps.has("entityId") && !changedProps.has("hass")) { + if ( + this._isInteracting || + (!changedProps.has("entityId") && !changedProps.has("hass")) + ) { return; } @@ -219,10 +224,13 @@ class LightRgbColorPicker extends LitElement { } private _hsColorCursorMoved(ev: CustomEvent) { - if (!ev.detail.value) { + const color = ev.detail.value; + this._isInteracting = color !== undefined; + + if (color === undefined) { return; } - this._hsPickerValue = ev.detail.value; + this._hsPickerValue = color; this._throttleUpdateColor(); } diff --git a/src/dialogs/more-info/components/lights/light-color-temp-picker.ts b/src/dialogs/more-info/components/lights/light-color-temp-picker.ts index b9b1fe82f3..2f3480646d 100644 --- a/src/dialogs/more-info/components/lights/light-color-temp-picker.ts +++ b/src/dialogs/more-info/components/lights/light-color-temp-picker.ts @@ -22,7 +22,6 @@ import { DOMAIN_ATTRIBUTES_UNITS } from "../../../../data/entity_attributes"; declare global { interface HASSDomEvents { "color-changed": LightColor; - "color-hovered": LightColor | undefined; } } @@ -54,6 +53,8 @@ class LightColorTempPicker extends LitElement { @state() private _ctPickerValue?: number; + @state() private _isInteracting?: boolean; + protected render() { if (!this.stateObj) { return nothing; @@ -113,7 +114,7 @@ class LightColorTempPicker extends LitElement { public willUpdate(changedProps: PropertyValues) { super.willUpdate(changedProps); - if (!changedProps.has("stateObj")) { + if (this._isInteracting || !changedProps.has("stateObj")) { return; } @@ -123,16 +124,14 @@ class LightColorTempPicker extends LitElement { private _ctColorCursorMoved(ev: CustomEvent) { const ct = ev.detail.value; + this._isInteracting = ct !== undefined; + if (isNaN(ct) || this._ctPickerValue === ct) { return; } this._ctPickerValue = ct; - fireEvent(this, "color-hovered", { - color_temp_kelvin: ct, - }); - this._throttleUpdateColorTemp(); } @@ -143,8 +142,6 @@ class LightColorTempPicker extends LitElement { private _ctColorChanged(ev: CustomEvent) { const ct = ev.detail.value; - fireEvent(this, "color-hovered", undefined); - if (isNaN(ct) || this._ctPickerValue === ct) { return; } diff --git a/src/dialogs/more-info/components/siren/ha-more-info-siren-advanced-controls.ts b/src/dialogs/more-info/components/siren/ha-more-info-siren-advanced-controls.ts index 93fa0df503..4f6b3719b0 100644 --- a/src/dialogs/more-info/components/siren/ha-more-info-siren-advanced-controls.ts +++ b/src/dialogs/more-info/components/siren/ha-more-info-siren-advanced-controls.ts @@ -99,7 +99,12 @@ class MoreInfoSirenAdvancedControls extends LitElement { this._stateObj.attributes.available_tones ).map( ([toneId, toneName]) => html` - ${toneName} ` @@ -179,7 +184,7 @@ class MoreInfoSirenAdvancedControls extends LitElement { await this.hass.callService("siren", "turn_on", { entity_id: this._stateObj!.entity_id, tone: this._tone, - volume: this._volume, + volume_level: this._volume, duration: this._duration, }); } diff --git a/src/dialogs/quick-bar/ha-quick-bar.ts b/src/dialogs/quick-bar/ha-quick-bar.ts index 8c108e1f5f..6fa529e2c7 100644 --- a/src/dialogs/quick-bar/ha-quick-bar.ts +++ b/src/dialogs/quick-bar/ha-quick-bar.ts @@ -251,6 +251,7 @@ export class QuickBar extends LitElement { ${this._opened ? html` ${item.primaryText} ${item.area @@ -346,6 +348,7 @@ export class QuickBar extends LitElement { .item=${item} index=${ifDefined(index)} graphic="icon" + tabindex="0" > ${item.iconPath ? html` @@ -375,6 +378,7 @@ export class QuickBar extends LitElement { index=${ifDefined(index)} class="command-item" hasMeta + tabindex="0" > { + this._checkConnection = false; + doLogin(username); + }, + closeDialog: () => { + this._requestInProgress = false; + }, + }); + return; + } + if (errCode === "usernotfound" && username !== username.toLowerCase()) { await doLogin(username.toLowerCase()); return; diff --git a/src/panels/config/backup/components/config/ha-backup-config-agents.ts b/src/panels/config/backup/components/config/ha-backup-config-agents.ts index 4444e917a2..687321f7c5 100644 --- a/src/panels/config/backup/components/config/ha-backup-config-agents.ts +++ b/src/panels/config/backup/components/config/ha-backup-config-agents.ts @@ -4,6 +4,7 @@ import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../../common/dom/fire_event"; import { computeDomain } from "../../../../../common/entity/compute_domain"; +import { navigate } from "../../../../../common/navigate"; import "../../../../../components/ha-icon-button"; import "../../../../../components/ha-md-list"; import "../../../../../components/ha-md-list-item"; @@ -22,7 +23,6 @@ import { import type { CloudStatus } from "../../../../../data/cloud"; import type { HomeAssistant } from "../../../../../types"; import { brandsUrl } from "../../../../../util/brands-url"; -import { navigate } from "../../../../../common/navigate"; const DEFAULT_AGENTS = []; diff --git a/src/panels/config/backup/components/config/ha-backup-config-schedule.ts b/src/panels/config/backup/components/config/ha-backup-config-schedule.ts index d57389a001..c19d334487 100644 --- a/src/panels/config/backup/components/config/ha-backup-config-schedule.ts +++ b/src/panels/config/backup/components/config/ha-backup-config-schedule.ts @@ -46,7 +46,7 @@ enum BackupScheduleTime { } interface RetentionData { - type: "copies" | "days"; + type: "copies" | "days" | "forever"; value: number; } @@ -55,7 +55,7 @@ const RETENTION_PRESETS: Record< RetentionData > = { copies_3: { type: "copies", value: 3 }, - forever: { type: "days", value: 0 }, + forever: { type: "forever", value: 0 }, }; const SCHEDULE_OPTIONS = [ @@ -79,7 +79,10 @@ const computeRetentionPreset = ( data: RetentionData ): RetentionPreset | undefined => { for (const [key, value] of Object.entries(RETENTION_PRESETS)) { - if (value.type === data.type && value.value === data.value) { + if ( + value.type === data.type && + (value.type === RetentionPreset.FOREVER || value.value === data.value) + ) { return key as RetentionPreset; } } @@ -92,7 +95,7 @@ interface FormData { time?: string | null; days: BackupDay[]; retention: { - type: "copies" | "days"; + type: "copies" | "days" | "forever"; value: number; }; } @@ -142,7 +145,12 @@ class HaBackupConfigSchedule extends LitElement { ? config.schedule.days : [], retention: { - type: config.retention.days != null ? "days" : "copies", + type: + config.retention.days === null && config.retention.copies === null + ? "forever" + : config.retention.days != null + ? "days" + : "copies", value: config.retention.days ?? config.retention.copies ?? 3, }, }; @@ -160,9 +168,11 @@ class HaBackupConfigSchedule extends LitElement { : [], }, retention: - data.retention.type === "days" - ? { days: data.retention.value, copies: null } - : { copies: data.retention.value, days: null }, + data.retention.type === "forever" + ? { days: null, copies: null } + : data.retention.type === "days" + ? { days: data.retention.value, copies: null } + : { copies: data.retention.value, days: null }, }; fireEvent(this, "value-changed", { value: this.value }); @@ -481,9 +491,19 @@ class HaBackupConfigSchedule extends LitElement { private _retentionPresetChanged(ev) { ev.stopPropagation(); const target = ev.currentTarget as HaMdSelect; - const value = target.value as RetentionPreset; + let value = target.value as RetentionPreset; + + // custom needs to have a type of days or copies, set it to default copies 3 + if ( + value === RetentionPreset.CUSTOM && + this._retentionPreset === RetentionPreset.FOREVER + ) { + this._retentionPreset = value; + value = RetentionPreset.COPIES_3; + } else { + this._retentionPreset = value; + } - this._retentionPreset = value; if (value !== RetentionPreset.CUSTOM) { const data = this._getData(this.value); const retention = RETENTION_PRESETS[value]; @@ -493,7 +513,7 @@ class HaBackupConfigSchedule extends LitElement { } this._setData({ ...data, - retention: RETENTION_PRESETS[value], + retention, }); } } @@ -504,6 +524,7 @@ class HaBackupConfigSchedule extends LitElement { const value = parseInt(target.value); const clamped = clamp(value, MIN_VALUE, MAX_VALUE); const data = this._getData(this.value); + target.value = clamped.toString(); this._setData({ ...data, retention: { diff --git a/src/panels/config/backup/ha-config-backup-backups.ts b/src/panels/config/backup/ha-config-backup-backups.ts index 6caa2190af..9690a89f9b 100644 --- a/src/panels/config/backup/ha-config-backup-backups.ts +++ b/src/panels/config/backup/ha-config-backup-backups.ts @@ -8,7 +8,7 @@ import { mdiUpload, } from "@mdi/js"; import type { CSSResultGroup, TemplateResult } from "lit"; -import { html, LitElement, nothing } from "lit"; +import { css, html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; @@ -27,6 +27,7 @@ import type { } from "../../../components/data-table/ha-data-table"; import "../../../components/ha-button"; import "../../../components/ha-button-menu"; +import "../../../components/ha-circular-progress"; import "../../../components/ha-fab"; import "../../../components/ha-filter-states"; import "../../../components/ha-icon"; @@ -460,7 +461,17 @@ class HaConfigBackupBackups extends SubscribeMixin(LitElement) { extended @click=${this._newBackup} > - + ${backupInProgress + ? html`
+ +
` + : html``} ` : nothing} @@ -605,7 +616,14 @@ class HaConfigBackupBackups extends SubscribeMixin(LitElement) { } static get styles(): CSSResultGroup { - return haStyle; + return [ + haStyle, + css` + ha-circular-progress { + --md-sys-color-primary: var(--mdc-theme-on-secondary); + } + `, + ]; } } diff --git a/src/panels/config/backup/ha-config-backup-overview.ts b/src/panels/config/backup/ha-config-backup-overview.ts index 8d23b61b66..e9c75d935f 100644 --- a/src/panels/config/backup/ha-config-backup-overview.ts +++ b/src/panels/config/backup/ha-config-backup-overview.ts @@ -8,6 +8,7 @@ import "../../../components/ha-button"; import "../../../components/ha-button-menu"; import "../../../components/ha-card"; import "../../../components/ha-fab"; +import "../../../components/ha-circular-progress"; import "../../../components/ha-icon"; import "../../../components/ha-icon-next"; import "../../../components/ha-icon-overflow-menu"; @@ -17,8 +18,10 @@ import type { BackupAgent, BackupConfig, BackupContent, + BackupInfo, } from "../../../data/backup"; import { + computeBackupAgentName, generateBackup, generateBackupWithAutomaticSettings, } from "../../../data/backup"; @@ -50,6 +53,8 @@ class HaConfigBackupOverview extends LitElement { @property({ attribute: false }) public manager!: ManagerStateEvent; + @property({ attribute: false }) public info?: BackupInfo; + @property({ attribute: false }) public backups: BackupContent[] = []; @property({ attribute: false }) public fetching = false; @@ -151,6 +156,26 @@ class HaConfigBackupOverview extends LitElement {
+ ${this.info && Object.keys(this.info.agent_errors).length + ? html`${Object.entries(this.info.agent_errors).map( + ([agentId, error]) => + html` + ${error} + ` + )}` + : nothing} ${backupInProgress ? html` - + ${backupInProgress + ? html`
+ +
` + : html``} `; @@ -231,6 +263,9 @@ class HaConfigBackupOverview extends LitElement { padding-left: 0; padding-right: 0; } + ha-circular-progress { + --md-sys-color-primary: var(--mdc-theme-on-secondary); + } `, ]; } diff --git a/src/panels/config/backup/ha-config-backup-settings.ts b/src/panels/config/backup/ha-config-backup-settings.ts index d7bb4266da..e841b872aa 100644 --- a/src/panels/config/backup/ha-config-backup-settings.ts +++ b/src/panels/config/backup/ha-config-backup-settings.ts @@ -1,4 +1,4 @@ -import { mdiDotsVertical, mdiHarddisk } from "@mdi/js"; +import { mdiDotsVertical, mdiHarddisk, mdiOpenInNew } from "@mdi/js"; import type { PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; @@ -28,6 +28,7 @@ import "./components/config/ha-backup-config-encryption-key"; import "./components/config/ha-backup-config-schedule"; import type { BackupConfigSchedule } from "./components/config/ha-backup-config-schedule"; import { showLocalBackupLocationDialog } from "./dialogs/show-dialog-local-backup-location"; +import { documentationUrl } from "../../../util/documentation-url"; @customElement("ha-config-backup-settings") class HaConfigBackupSettings extends LitElement { @@ -98,6 +99,8 @@ class HaConfigBackupSettings extends LitElement { return nothing; } + const supervisor = isComponentLoaded(this.hass, "hassio"); + return html` - ${isComponentLoaded(this.hass, "hassio") + ${supervisor ? html` +
@@ -342,6 +368,9 @@ class HaConfigBackupSettings extends LitElement { .card-content { padding-bottom: 0; } + a { + text-decoration: none; + } `; } diff --git a/src/panels/config/backup/ha-config-backup.ts b/src/panels/config/backup/ha-config-backup.ts index b488058b58..d61eb33a36 100644 --- a/src/panels/config/backup/ha-config-backup.ts +++ b/src/panels/config/backup/ha-config-backup.ts @@ -4,7 +4,7 @@ import { customElement, property, state } from "lit/decorators"; import type { BackupAgent, BackupConfig, - BackupContent, + BackupInfo, } from "../../../data/backup"; import { compareAgents, @@ -44,7 +44,7 @@ class HaConfigBackup extends SubscribeMixin(HassRouterPage) { @state() private _manager: ManagerStateEvent = DEFAULT_MANAGER_STATE; - @state() private _backups: BackupContent[] = []; + @state() private _info?: BackupInfo; @state() private _agents: BackupAgent[] = []; @@ -87,8 +87,7 @@ class HaConfigBackup extends SubscribeMixin(HassRouterPage) { } private async _fetchBackupInfo() { - const info = await fetchBackupInfo(this.hass); - this._backups = info.backups; + this._info = await fetchBackupInfo(this.hass); } private async _fetchBackupConfig() { @@ -134,7 +133,8 @@ class HaConfigBackup extends SubscribeMixin(HassRouterPage) { pageEl.narrow = this.narrow; pageEl.cloudStatus = this.cloudStatus; pageEl.manager = this._manager; - pageEl.backups = this._backups; + pageEl.info = this._info; + pageEl.backups = this._info?.backups || []; pageEl.config = this._config; pageEl.agents = this._agents; pageEl.fetching = this._fetching; diff --git a/src/panels/config/cloud/account/cloud-remote-pref.ts b/src/panels/config/cloud/account/cloud-remote-pref.ts index 60ddbd50a0..d7ff6a8049 100644 --- a/src/panels/config/cloud/account/cloud-remote-pref.ts +++ b/src/panels/config/cloud/account/cloud-remote-pref.ts @@ -1,17 +1,13 @@ -import { mdiContentCopy, mdiEye, mdiEyeOff, mdiHelpCircle } from "@mdi/js"; +import { mdiHelpCircle } from "@mdi/js"; import { LitElement, css, html, nothing } from "lit"; -import { customElement, property, state } from "lit/decorators"; +import { customElement, property } from "lit/decorators"; import { fireEvent } from "../../../../common/dom/fire_event"; -import { copyToClipboard } from "../../../../common/util/copy-clipboard"; import "../../../../components/ha-alert"; import "../../../../components/ha-button"; import "../../../../components/ha-card"; import "../../../../components/ha-expansion-panel"; -import "../../../../components/ha-formfield"; -import "../../../../components/ha-radio"; import "../../../../components/ha-settings-row"; import "../../../../components/ha-switch"; -import "../../../../components/ha-textfield"; import { formatDate } from "../../../../common/datetime/format_date"; import type { HaSwitch } from "../../../../components/ha-switch"; @@ -25,6 +21,7 @@ import type { HomeAssistant } from "../../../../types"; import { showToast } from "../../../../util/toast"; import { showCloudCertificateDialog } from "../dialog-cloud-certificate/show-dialog-cloud-certificate"; import { obfuscateUrl } from "../../../../util/url"; +import "../../../../components/ha-copy-textfield"; @customElement("cloud-remote-pref") export class CloudRemotePref extends LitElement { @@ -34,8 +31,6 @@ export class CloudRemotePref extends LitElement { @property({ type: Boolean }) public narrow = false; - @state() private _unmaskedUrl = false; - protected render() { if (!this.cloudStatus) { return nothing; @@ -139,37 +134,13 @@ export class CloudRemotePref extends LitElement { )}

`} -
-
-
` - } - > - -
- - - ${this.hass.localize("ui.panel.config.common.copy_link")} - -
+ + { - const url = ev.currentTarget.url; - await copyToClipboard(url); - showToast(this, { - message: this.hass.localize("ui.common.copied_clipboard"), - }); - } - static styles = css` .preparing { padding: 0 16px 16px; @@ -335,30 +294,6 @@ export class CloudRemotePref extends LitElement { display: block; margin-bottom: 16px; } - .url-container { - display: flex; - align-items: center; - gap: 8px; - margin-top: 8px; - } - .textfield-container { - position: relative; - flex: 1; - } - .textfield-container ha-textfield { - display: block; - } - .toggle-unmasked-url { - position: absolute; - top: 8px; - right: 8px; - inset-inline-start: initial; - inset-inline-end: 8px; - --mdc-icon-button-size: 40px; - --mdc-icon-size: 20px; - color: var(--secondary-text-color); - direction: var(--direction); - } hr { border: none; height: 1px; diff --git a/src/panels/config/cloud/dialog-cloud-already-connected/dialog-cloud-already-connected.ts b/src/panels/config/cloud/dialog-cloud-already-connected/dialog-cloud-already-connected.ts new file mode 100644 index 0000000000..8daccffeea --- /dev/null +++ b/src/panels/config/cloud/dialog-cloud-already-connected/dialog-cloud-already-connected.ts @@ -0,0 +1,171 @@ +import type { CSSResultGroup } from "lit"; +import { css, html, LitElement, nothing } from "lit"; +import { customElement, state } from "lit/decorators"; +import { mdiEye, mdiEyeOff } from "@mdi/js"; +import { formatDateTime } from "../../../../common/datetime/format_date_time"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import "../../../../components/ha-alert"; +import "../../../../components/ha-button"; +import "../../../../components/ha-icon-button"; +import { createCloseHeading } from "../../../../components/ha-dialog"; +import { haStyleDialog } from "../../../../resources/styles"; +import type { HomeAssistant } from "../../../../types"; +import type { CloudAlreadyConnectedParams as CloudAlreadyConnectedDialogParams } from "./show-dialog-cloud-already-connected"; +import { obfuscateUrl } from "../../../../util/url"; + +@customElement("dialog-cloud-already-connected") +class DialogCloudAlreadyConnected extends LitElement { + public hass!: HomeAssistant; + + @state() private _params?: CloudAlreadyConnectedDialogParams; + + @state() private _obfuscateIp = true; + + public showDialog(params: CloudAlreadyConnectedDialogParams) { + this._params = params; + } + + public closeDialog() { + this._params?.closeDialog(); + this._params = undefined; + this._obfuscateIp = true; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + + protected render() { + if (!this._params) { + return nothing; + } + const { details } = this._params; + + return html` + +
+ + ${this.hass.localize( + "ui.panel.config.cloud.dialog_already_connected.description" + )} + + + ${this.hass.localize( + "ui.panel.config.cloud.dialog_already_connected.other_home_assistant" + )} + +
+
+
+ + ${this.hass.localize( + "ui.panel.config.cloud.dialog_already_connected.ip_address" + )}: + +
+ + ${this._obfuscateIp + ? obfuscateUrl(details.remote_ip_address) + : details.remote_ip_address} + + + +
+
+
+ + ${this.hass.localize( + "ui.panel.config.cloud.dialog_already_connected.connected_at" + )}: + + + ${formatDateTime( + new Date(details.connected_at), + this.hass.locale, + this.hass.config + )} + +
+
+ + ${this.hass.localize( + "ui.panel.config.cloud.dialog_already_connected.info_backups.description" + )} + + + + ${this.hass!.localize("ui.common.cancel")} + + + ${this.hass!.localize( + "ui.panel.config.cloud.dialog_already_connected.login_here" + )} + +
+ `; + } + + private _toggleObfuscateIp() { + this._obfuscateIp = !this._obfuscateIp; + } + + private _logInHere() { + this._params?.logInHereAction(); + this.closeDialog(); + } + + static get styles(): CSSResultGroup { + return [ + haStyleDialog, + css` + ha-dialog { + --mdc-dialog-max-width: 535px; + } + .intro b { + display: block; + margin-top: 16px; + } + .instance-details { + display: flex; + flex-direction: column; + margin-bottom: 16px; + } + .instance-detail { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + } + .obfuscated { + align-items: center; + display: flex; + flex-direction: row; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "dialog-cloud-already-connected": DialogCloudAlreadyConnected; + } +} diff --git a/src/panels/config/cloud/dialog-cloud-already-connected/show-dialog-cloud-already-connected.ts b/src/panels/config/cloud/dialog-cloud-already-connected/show-dialog-cloud-already-connected.ts new file mode 100644 index 0000000000..7c95db2247 --- /dev/null +++ b/src/panels/config/cloud/dialog-cloud-already-connected/show-dialog-cloud-already-connected.ts @@ -0,0 +1,21 @@ +import { fireEvent } from "../../../../common/dom/fire_event"; + +export interface CloudAlreadyConnectedParams { + details: { + remote_ip_address: string; + connected_at: string; + }; + logInHereAction: () => void; + closeDialog: () => void; +} + +export const showCloudAlreadyConnectedDialog = ( + element: HTMLElement, + webhookDialogParams: CloudAlreadyConnectedParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "dialog-cloud-already-connected", + dialogImport: () => import("./dialog-cloud-already-connected"), + dialogParams: webhookDialogParams, + }); +}; diff --git a/src/panels/config/cloud/dialog-manage-cloudhook/dialog-manage-cloudhook.ts b/src/panels/config/cloud/dialog-manage-cloudhook/dialog-manage-cloudhook.ts index b1c313fe6b..909866095e 100644 --- a/src/panels/config/cloud/dialog-manage-cloudhook/dialog-manage-cloudhook.ts +++ b/src/panels/config/cloud/dialog-manage-cloudhook/dialog-manage-cloudhook.ts @@ -1,27 +1,23 @@ import "@material/mwc-button"; -import { mdiContentCopy, mdiOpenInNew } from "@mdi/js"; +import { mdiOpenInNew } from "@mdi/js"; import type { CSSResultGroup } from "lit"; import { css, html, LitElement, nothing } from "lit"; -import { query, state } from "lit/decorators"; +import { state } from "lit/decorators"; import { fireEvent } from "../../../../common/dom/fire_event"; -import { copyToClipboard } from "../../../../common/util/copy-clipboard"; import { createCloseHeading } from "../../../../components/ha-dialog"; -import "../../../../components/ha-textfield"; -import type { HaTextField } from "../../../../components/ha-textfield"; import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box"; import { haStyle, haStyleDialog } from "../../../../resources/styles"; import type { HomeAssistant } from "../../../../types"; import { documentationUrl } from "../../../../util/documentation-url"; -import { showToast } from "../../../../util/toast"; import type { WebhookDialogParams } from "./show-dialog-manage-cloudhook"; +import "../../../../components/ha-copy-textfield"; + export class DialogManageCloudhook extends LitElement { protected hass?: HomeAssistant; @state() private _params?: WebhookDialogParams; - @query("ha-textfield") _input!: HaTextField; - public showDialog(params: WebhookDialogParams) { this._params = params; } @@ -82,21 +78,12 @@ export class DialogManageCloudhook extends LitElement {

- - - + .label=${this.hass!.localize("ui.panel.config.common.copy_link")} + >
{ - if (!this.hass) return; - ev.stopPropagation(); - const inputElement = ev.target.parentElement as HaTextField; - inputElement.select(); - const url = this.hass.hassUrl(inputElement.value); - - await copyToClipboard(url); - showToast(this, { - message: this.hass.localize("ui.common.copied_clipboard"), - }); - } - static get styles(): CSSResultGroup { return [ haStyle, @@ -163,13 +132,6 @@ export class DialogManageCloudhook extends LitElement { ha-dialog { width: 650px; } - ha-textfield { - display: block; - } - ha-textfield > ha-icon-button { - --mdc-icon-button-size: 24px; - --mdc-icon-size: 18px; - } button.link { color: var(--primary-color); text-decoration: none; diff --git a/src/panels/config/cloud/login/cloud-login.ts b/src/panels/config/cloud/login/cloud-login.ts index 5fe14e24ba..bc03fe8cb8 100644 --- a/src/panels/config/cloud/login/cloud-login.ts +++ b/src/panels/config/cloud/login/cloud-login.ts @@ -28,6 +28,7 @@ import { haStyle } from "../../../../resources/styles"; import type { HomeAssistant } from "../../../../types"; import "../../ha-config-section"; import { showSupportPackageDialog } from "../account/show-dialog-cloud-support-package"; +import { showCloudAlreadyConnectedDialog } from "../dialog-cloud-already-connected/show-dialog-cloud-already-connected"; @customElement("cloud-login") export class CloudLogin extends LitElement { @@ -47,6 +48,8 @@ export class CloudLogin extends LitElement { @state() private _error?: string; + @state() private _checkConnection = true; + @query("#email", true) private _emailField!: HaTextField; @query("#password", true) private _passwordField!: HaPasswordField; @@ -244,6 +247,7 @@ export class CloudLogin extends LitElement { hass: this.hass, email: username, ...(code ? { code } : { password }), + check_connection: this._checkConnection, }); this.email = ""; this._password = ""; @@ -283,6 +287,21 @@ export class CloudLogin extends LitElement { return; } } + if (errCode === "alreadyconnectederror") { + showCloudAlreadyConnectedDialog(this, { + details: JSON.parse(err.body.message), + logInHereAction: () => { + this._checkConnection = false; + doLogin(username); + }, + closeDialog: () => { + this._requestInProgress = false; + this.email = ""; + this._password = ""; + }, + }); + return; + } if (errCode === "PasswordChangeRequired") { showAlertDialog(this, { title: this.hass.localize( diff --git a/src/panels/config/integrations/dialog-add-integration.ts b/src/panels/config/integrations/dialog-add-integration.ts index 17c6b0d715..1ace965ebf 100644 --- a/src/panels/config/integrations/dialog-add-integration.ts +++ b/src/panels/config/integrations/dialog-add-integration.ts @@ -185,6 +185,14 @@ class AddIntegrationDialog extends LitElement { const yamlIntegrations: IntegrationListItem[] = []; Object.entries(i).forEach(([domain, integration]) => { + if ( + "integration_type" in integration && + integration.integration_type === "hardware" + ) { + // Ignore hardware integrations, they cannot be added via UI + return; + } + if ( "integration_type" in integration && (integration.config_flow || diff --git a/src/panels/config/network/ha-config-url-form.ts b/src/panels/config/network/ha-config-url-form.ts index 1daab56bd3..ad0c9b8e9f 100644 --- a/src/panels/config/network/ha-config-url-form.ts +++ b/src/panels/config/network/ha-config-url-form.ts @@ -153,7 +153,6 @@ class ConfigUrlForm extends LitElement { ? html` - ${this.features.map( - (feature) => html` - - ` - )} -
+ ${this.features.map( + (feature) => html` + + ` + )} `; } static styles = css` :host { --feature-color: var(--state-icon-color); - --feature-padding: 12px; --feature-height: 42px; --feature-border-radius: 12px; --feature-button-spacing: 12px; position: relative; width: 100%; - } - .container { - position: relative; display: flex; flex-direction: column; - padding: var(--feature-padding); - padding-top: 0px; - gap: var(--feature-padding); + gap: 12px; width: 100%; - height: 100%; box-sizing: border-box; justify-content: space-evenly; } diff --git a/src/panels/lovelace/card-features/hui-counter-actions-card-feature.ts b/src/panels/lovelace/card-features/hui-counter-actions-card-feature.ts new file mode 100644 index 0000000000..d4373a6827 --- /dev/null +++ b/src/panels/lovelace/card-features/hui-counter-actions-card-feature.ts @@ -0,0 +1,134 @@ +import { mdiRestore, mdiPlus, mdiMinus } from "@mdi/js"; +import type { HassEntity } from "home-assistant-js-websocket"; +import type { TemplateResult } from "lit"; +import { LitElement, html } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { computeDomain } from "../../../common/entity/compute_domain"; +import "../../../components/ha-control-select"; +import { UNAVAILABLE } from "../../../data/entity"; +import type { HomeAssistant } from "../../../types"; +import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types"; +import { cardFeatureStyles } from "./common/card-feature-styles"; +import { COUNTER_ACTIONS, type CounterActionsCardFeatureConfig } from "./types"; +import "../../../components/ha-control-button-group"; +import "../../../components/ha-control-button"; + +export const supportsCounterActionsCardFeature = (stateObj: HassEntity) => { + const domain = computeDomain(stateObj.entity_id); + return domain === "counter"; +}; + +interface CounterButton { + translationKey: string; + icon: string; + serviceName: string; + disabled: boolean; +} + +export const COUNTER_ACTIONS_BUTTON: Record< + string, + (stateObj: HassEntity) => CounterButton +> = { + increment: (stateObj) => ({ + translationKey: "increment", + icon: mdiPlus, + serviceName: "increment", + disabled: parseInt(stateObj.state) === stateObj.attributes.maximum, + }), + reset: () => ({ + translationKey: "reset", + icon: mdiRestore, + serviceName: "reset", + disabled: false, + }), + decrement: (stateObj) => ({ + translationKey: "decrement", + icon: mdiMinus, + serviceName: "decrement", + disabled: parseInt(stateObj.state) === stateObj.attributes.minimum, + }), +}; + +@customElement("hui-counter-actions-card-feature") +class HuiCounterActionsCardFeature + extends LitElement + implements LovelaceCardFeature +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public stateObj?: HassEntity; + + @state() private _config?: CounterActionsCardFeatureConfig; + + public static async getConfigElement(): Promise { + await import( + "../editor/config-elements/hui-counter-actions-card-feature-editor" + ); + return document.createElement("hui-counter-actions-card-feature-editor"); + } + + static getStubConfig(): CounterActionsCardFeatureConfig { + return { + type: "counter-actions", + actions: COUNTER_ACTIONS.map((action) => action), + }; + } + + public setConfig(config: CounterActionsCardFeatureConfig): void { + if (!config) { + throw new Error("Invalid configuration"); + } + this._config = config; + } + + protected render(): TemplateResult | null { + if ( + !this._config || + !this.hass || + !this.stateObj || + !supportsCounterActionsCardFeature(this.stateObj) + ) { + return null; + } + + return html` + + ${this._config?.actions + ?.filter((action) => COUNTER_ACTIONS.includes(action)) + .map((action) => { + const button = COUNTER_ACTIONS_BUTTON[action](this.stateObj!); + return html` + + + + `; + })} + + `; + } + + private _onActionTap(ev): void { + ev.stopPropagation(); + const entry = (ev.target! as any).entry as CounterButton; + this.hass!.callService("counter", entry.serviceName, { + entity_id: this.stateObj!.entity_id, + }); + } + + static styles = cardFeatureStyles; +} + +declare global { + interface HTMLElementTagNameMap { + "hui-counter-actions-card-feature": HuiCounterActionsCardFeature; + } +} diff --git a/src/panels/lovelace/card-features/hui-toggle-card-feature.ts b/src/panels/lovelace/card-features/hui-toggle-card-feature.ts new file mode 100644 index 0000000000..ce9b2294cf --- /dev/null +++ b/src/panels/lovelace/card-features/hui-toggle-card-feature.ts @@ -0,0 +1,111 @@ +import { mdiPowerOff, mdiPower } from "@mdi/js"; +import type { HassEntity } from "home-assistant-js-websocket"; +import type { TemplateResult } from "lit"; +import { LitElement, html } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { styleMap } from "lit/directives/style-map"; +import { computeDomain } from "../../../common/entity/compute_domain"; +import { stateColorCss } from "../../../common/entity/state_color"; +import "../../../components/ha-control-select"; +import type { ControlSelectOption } from "../../../components/ha-control-select"; +import { UNAVAILABLE } from "../../../data/entity"; +import type { HomeAssistant } from "../../../types"; +import type { LovelaceCardFeature } from "../types"; +import { cardFeatureStyles } from "./common/card-feature-styles"; +import type { ToggleCardFeatureConfig } from "./types"; +import { showToast } from "../../../util/toast"; + +export const supportsToggleCardFeature = (stateObj: HassEntity) => { + const domain = computeDomain(stateObj.entity_id); + return ["switch", "input_boolean"].includes(domain); +}; + +@customElement("hui-toggle-card-feature") +class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature { + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public stateObj?: HassEntity; + + @state() private _config?: ToggleCardFeatureConfig; + + static getStubConfig(): ToggleCardFeatureConfig { + return { + type: "toggle", + }; + } + + public setConfig(config: ToggleCardFeatureConfig): void { + if (!config) { + throw new Error("Invalid configuration"); + } + this._config = config; + } + + protected render(): TemplateResult | null { + if ( + !this._config || + !this.hass || + !this.stateObj || + !supportsToggleCardFeature(this.stateObj) + ) { + return null; + } + + const color = stateColorCss(this.stateObj); + + const options = ["on", "off"].map((entityState) => ({ + value: entityState, + label: this.hass!.formatEntityState(this.stateObj!, entityState), + path: entityState === "on" ? mdiPower : mdiPowerOff, + })); + + return html` + + + `; + } + + private async _valueChanged(ev: CustomEvent) { + const newState = (ev.detail as any).value; + + if ( + newState === this.stateObj!.state && + !this.stateObj!.attributes.assumed_state + ) + return; + const service = newState === "on" ? "turn_on" : "turn_off"; + const domain = computeDomain(this.stateObj!.entity_id); + + try { + await this.hass!.callService(domain, service, { + entity_id: this.stateObj!.entity_id, + }); + } catch (_err) { + showToast(this, { + message: this.hass!.localize("ui.notification_toast.action_failed", { + service: domain + "." + service, + }), + duration: 5000, + dismissable: true, + }); + } + } + + static styles = cardFeatureStyles; +} + +declare global { + interface HTMLElementTagNameMap { + "hui-toggle-card-feature": HuiToggleCardFeature; + } +} diff --git a/src/panels/lovelace/card-features/types.ts b/src/panels/lovelace/card-features/types.ts index 24349dfe31..3b30ac46d8 100644 --- a/src/panels/lovelace/card-features/types.ts +++ b/src/panels/lovelace/card-features/types.ts @@ -83,6 +83,15 @@ export interface ClimatePresetModesCardFeatureConfig { preset_modes?: string[]; } +export const COUNTER_ACTIONS = ["increment", "reset", "decrement"] as const; + +export type CounterActions = (typeof COUNTER_ACTIONS)[number]; + +export interface CounterActionsCardFeatureConfig { + type: "counter-actions"; + actions?: CounterActions[]; +} + export interface SelectOptionsCardFeatureConfig { type: "select-options"; options?: string[]; @@ -101,6 +110,10 @@ export interface TargetTemperatureCardFeatureConfig { type: "target-temperature"; } +export interface ToggleCardFeatureConfig { + type: "toggle"; +} + export interface WaterHeaterOperationModesCardFeatureConfig { type: "water-heater-operation-modes"; operation_modes?: OperationMode[]; @@ -152,6 +165,7 @@ export type LovelaceCardFeatureConfig = | ClimateSwingHorizontalModesCardFeatureConfig | ClimateHvacModesCardFeatureConfig | ClimatePresetModesCardFeatureConfig + | CounterActionsCardFeatureConfig | CoverOpenCloseCardFeatureConfig | CoverPositionCardFeatureConfig | CoverTiltPositionCardFeatureConfig @@ -170,6 +184,7 @@ export type LovelaceCardFeatureConfig = | SelectOptionsCardFeatureConfig | TargetHumidityCardFeatureConfig | TargetTemperatureCardFeatureConfig + | ToggleCardFeatureConfig | UpdateActionsCardFeatureConfig | VacuumCommandsCardFeatureConfig | WaterHeaterOperationModesCardFeatureConfig; diff --git a/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts index 82ae3d31a4..9ece46a8d2 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts @@ -327,17 +327,19 @@ export class HuiEnergyDevicesDetailGraphCard ); const untrackedConsumption: BarSeriesOption["data"] = []; - Object.keys(consumptionData.total).forEach((time) => { - const ts = Number(time); - const value = - consumptionData.total[time] - (totalDeviceConsumption[time] || 0); - const dataPoint: number[] = [ts, value]; - if (compare) { - dataPoint[2] = dataPoint[0]; - dataPoint[0] = compareTransform(new Date(ts)).getTime(); - } - untrackedConsumption.push(dataPoint); - }); + Object.keys(consumptionData.total) + .sort((a, b) => Number(a) - Number(b)) + .forEach((time) => { + const ts = Number(time); + const value = + consumptionData.total[time] - (totalDeviceConsumption[time] || 0); + const dataPoint: number[] = [ts, value]; + if (compare) { + dataPoint[2] = dataPoint[0]; + dataPoint[0] = compareTransform(new Date(ts)).getTime(); + } + untrackedConsumption.push(dataPoint); + }); // random id to always add untracked at the end const order = Date.now(); const dataset: BarSeriesOption = { diff --git a/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts index 18e737696f..cda61fecbf 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts @@ -291,20 +291,19 @@ export class HuiEnergyUsageGraphCard true ) ); - } else { - // add empty dataset so compare bars are first - // `stack: usage` so it doesn't take up space yet - const firstId = statIds.from_grid?.[0] ?? "placeholder"; - datasets.push({ - id: "compare-" + firstId, - type: "bar", - stack: "usage", - data: [], - // @ts-expect-error - order: 0, - }); } + // add empty dataset so compare bars are first + // `stack: usage` so it doesn't take up space yet + datasets.push({ + id: "compare-placeholder", + type: "bar", + stack: energyData.statsCompare ? "compare" : "usage", + data: [], + // @ts-expect-error + order: 0, + }); + datasets.push( ...this._processDataSet( energyData.stats, diff --git a/src/panels/lovelace/cards/hui-humidifier-card.ts b/src/panels/lovelace/cards/hui-humidifier-card.ts index 3a6921f091..826952292e 100644 --- a/src/panels/lovelace/cards/hui-humidifier-card.ts +++ b/src/panels/lovelace/cards/hui-humidifier-card.ts @@ -256,6 +256,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { hui-card-features { width: 100%; flex: none; + padding: 0 12px 12px 12px; } `; } diff --git a/src/panels/lovelace/cards/hui-map-card.ts b/src/panels/lovelace/cards/hui-map-card.ts index ee74d074f9..cd2c94a83e 100644 --- a/src/panels/lovelace/cards/hui-map-card.ts +++ b/src/panels/lovelace/cards/hui-map-card.ts @@ -1,4 +1,8 @@ -import { mdiImageFilterCenterFocus } from "@mdi/js"; +import { + mdiDotsHexagon, + mdiGoogleCirclesCommunities, + mdiImageFilterCenterFocus, +} from "@mdi/js"; import type { HassEntities } from "home-assistant-js-websocket"; import type { LatLngTuple } from "leaflet"; import type { PropertyValues } from "lit"; @@ -72,6 +76,8 @@ class HuiMapCard extends LitElement implements LovelaceCard { @state() private _error?: { code: string; message: string }; + @state() private _clusterMarkers = true; + private _subscribed?: Promise<(() => Promise) | undefined>; public setConfig(config: MapCardConfig): void { @@ -170,18 +176,32 @@ class HuiMapCard extends LitElement implements LovelaceCard { .autoFit=${this._config.auto_fit || false} .fitZones=${this._config.fit_zones} .themeMode=${themeMode} + .clusterMarkers=${this._clusterMarkers} interactive-zones render-passive > - +
+ + +
`; @@ -320,6 +340,10 @@ class HuiMapCard extends LitElement implements LovelaceCard { this._map?.fitMap(); } + private _toggleClusterMarkers() { + this._clusterMarkers = !this._clusterMarkers; + } + private _getColor(entityId: string): string { let color = this._colorDict[entityId]; if (color) { @@ -464,11 +488,12 @@ class HuiMapCard extends LitElement implements LovelaceCard { overflow: hidden; } - ha-icon-button { + #buttons { position: absolute; top: 75px; left: 3px; - outline: none; + display: flex; + flex-direction: column; } #root { diff --git a/src/panels/lovelace/cards/hui-markdown-card.ts b/src/panels/lovelace/cards/hui-markdown-card.ts index 77cb23f734..b1f2d71ca9 100644 --- a/src/panels/lovelace/cards/hui-markdown-card.ts +++ b/src/panels/lovelace/cards/hui-markdown-card.ts @@ -107,18 +107,26 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard { return html` ${this._error - ? html`${this._error}` + ? html` + + ${this._error} + + ` : nothing} - + @@ -135,7 +143,7 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard { this._tryConnect(); } const shouldBeHidden = - this._templateResult && + !!this._templateResult && this._config.show_empty === false && this._templateResult.result.length === 0; if (shouldBeHidden !== this.hidden) { @@ -228,11 +236,19 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard { margin-bottom: 8px; } ha-markdown { - padding: 0 16px 16px; + padding: 16px; word-wrap: break-word; } - ha-markdown.no-header { - padding-top: 16px; + .with-header ha-markdown { + padding: 0 16px 16px; + } + .text-only { + background: none; + box-shadow: none; + border: none; + } + .text-only ha-markdown { + padding: 2px 4px; } `; } diff --git a/src/panels/lovelace/cards/hui-thermostat-card.ts b/src/panels/lovelace/cards/hui-thermostat-card.ts index c2154bb148..57655de210 100644 --- a/src/panels/lovelace/cards/hui-thermostat-card.ts +++ b/src/panels/lovelace/cards/hui-thermostat-card.ts @@ -248,6 +248,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard { hui-card-features { width: 100%; flex: none; + padding: 0 12px 12px 12px; } `; } diff --git a/src/panels/lovelace/cards/hui-tile-card.ts b/src/panels/lovelace/cards/hui-tile-card.ts index a6a789b3e7..a7fe97c773 100644 --- a/src/panels/lovelace/cards/hui-tile-card.ts +++ b/src/panels/lovelace/cards/hui-tile-card.ts @@ -100,10 +100,13 @@ export class HuiTileCard extends LitElement implements LovelaceCard { } public getCardSize(): number { + const featuresPosition = + this._config && this._featurePosition(this._config); + const featuresCount = this._config?.features?.length || 0; return ( 1 + (this._config?.vertical ? 1 : 0) + - (this._config?.features?.length || 0) + (featuresPosition === "inline" ? 0 : featuresCount) ); } @@ -111,9 +114,16 @@ export class HuiTileCard extends LitElement implements LovelaceCard { const columns = 6; let min_columns = 6; let rows = 1; - if (this._config?.features?.length) { - rows += this._config.features.length; + const featurePosition = this._config && this._featurePosition(this._config); + const featuresCount = this._config?.features?.length || 0; + if (featuresCount) { + if (featurePosition === "inline") { + min_columns = 12; + } else { + rows += featuresCount; + } } + if (this._config?.vertical) { rows++; min_columns = 3; @@ -210,6 +220,23 @@ export class HuiTileCard extends LitElement implements LovelaceCard { ); } + private _featurePosition = memoizeOne((config: TileCardConfig) => { + if (config.vertical) { + return "bottom"; + } + return config.features_position || "bottom"; + }); + + private _displayedFeatures = memoizeOne((config: TileCardConfig) => { + const features = config.features || []; + const featurePosition = this._featurePosition(config); + + if (featurePosition === "inline") { + return features.slice(0, 1); + } + return features; + }); + protected render() { if (!this._config || !this.hass) { return nothing; @@ -263,6 +290,12 @@ export class HuiTileCard extends LitElement implements LovelaceCard { ? this._getImageUrl(stateObj) : undefined; + const featurePosition = this._featurePosition(this._config); + const features = this._displayedFeatures(this._config); + + const containerOrientationClass = + featurePosition === "inline" ? "horizontal" : ""; + return html`
-
+
- ${this._config.features + ${features.length > 0 ? html` ` : nothing} @@ -372,6 +405,10 @@ export class HuiTileCard extends LitElement implements LovelaceCard { flex-direction: column; flex: 1; } + .container.horizontal { + flex-direction: row; + } + .content { position: relative; display: flex; @@ -379,10 +416,12 @@ export class HuiTileCard extends LitElement implements LovelaceCard { align-items: center; padding: 10px; flex: 1; + min-width: 0; box-sizing: border-box; pointer-events: none; gap: 10px; } + .vertical { flex-direction: column; text-align: center; @@ -413,6 +452,14 @@ export class HuiTileCard extends LitElement implements LovelaceCard { } hui-card-features { --feature-color: var(--tile-color); + padding: 0 12px 12px 12px; + } + .container.horizontal hui-card-features { + width: calc(50% - var(--column-gap, 0px) / 2 - 12px); + flex: none; + --feature-height: 36px; + padding: 0 12px; + padding-inline-start: 0; } ha-tile-icon[data-domain="alarm_control_panel"][data-state="pending"], diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts index 01ddaf2f53..9771067a18 100644 --- a/src/panels/lovelace/cards/types.ts +++ b/src/panels/lovelace/cards/types.ts @@ -336,6 +336,7 @@ export interface MapCardConfig extends LovelaceCardConfig { export interface MarkdownCardConfig extends LovelaceCardConfig { type: "markdown"; content: string; + text_only?: boolean; title?: string; card_size?: number; entity_ids?: string | string[]; @@ -533,6 +534,7 @@ export interface TileCardConfig extends LovelaceCardConfig { icon_hold_action?: ActionConfig; icon_double_tap_action?: ActionConfig; features?: LovelaceCardFeatureConfig[]; + features_position?: "bottom" | "inline"; } export interface HeadingCardConfig extends LovelaceCardConfig { diff --git a/src/panels/lovelace/components/hui-card-edit-mode.ts b/src/panels/lovelace/components/hui-card-edit-mode.ts index ff22edd0c5..8d88907260 100644 --- a/src/panels/lovelace/components/hui-card-edit-mode.ts +++ b/src/panels/lovelace/components/hui-card-edit-mode.ts @@ -23,8 +23,10 @@ import type { LovelaceCardConfig } from "../../../data/lovelace/config/card"; import { haStyle } from "../../../resources/styles"; import type { HomeAssistant } from "../../../types"; import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog"; +import { addCard } from "../editor/config-util"; import type { LovelaceCardPath } from "../editor/lovelace-path"; import { + findLovelaceContainer, findLovelaceItems, getLovelaceContainerPath, parseLovelaceCardPath, @@ -253,14 +255,24 @@ export class HuiCardEditMode extends LitElement { } private _duplicateCard(): void { - const { cardIndex } = parseLovelaceCardPath(this.path!); + const { cardIndex, sectionIndex } = parseLovelaceCardPath(this.path!); const containerPath = getLovelaceContainerPath(this.path!); + const sectionConfig = + sectionIndex !== undefined + ? findLovelaceContainer(this.lovelace!.config, containerPath) + : undefined; + const cardConfig = this._cards![cardIndex]; + showEditCardDialog(this, { lovelaceConfig: this.lovelace!.config, - saveConfig: this.lovelace!.saveConfig, - path: containerPath, + saveCardConfig: async (config) => { + const newConfig = addCard(this.lovelace!.config, containerPath, config); + await this.lovelace!.saveConfig(newConfig); + }, cardConfig, + sectionConfig, + isNew: true, }); } diff --git a/src/panels/lovelace/components/hui-card-options.ts b/src/panels/lovelace/components/hui-card-options.ts index d27c21d9ca..b74d609f96 100644 --- a/src/panels/lovelace/components/hui-card-options.ts +++ b/src/panels/lovelace/components/hui-card-options.ts @@ -278,9 +278,12 @@ export class HuiCardOptions extends LitElement { const cardConfig = this._cards![cardIndex]; showEditCardDialog(this, { lovelaceConfig: this.lovelace!.config, - saveConfig: this.lovelace!.saveConfig, - path: containerPath, + saveCardConfig: async (config) => { + const newConfig = addCard(this.lovelace!.config, containerPath, config); + await this.lovelace!.saveConfig(newConfig); + }, cardConfig, + isNew: true, }); } diff --git a/src/panels/lovelace/create-element/create-card-feature-element.ts b/src/panels/lovelace/create-element/create-card-feature-element.ts index 8cad72f8a8..31ce2eccd5 100644 --- a/src/panels/lovelace/create-element/create-card-feature-element.ts +++ b/src/panels/lovelace/create-element/create-card-feature-element.ts @@ -4,6 +4,7 @@ import "../card-features/hui-climate-swing-modes-card-feature"; import "../card-features/hui-climate-swing-horizontal-modes-card-feature"; import "../card-features/hui-climate-hvac-modes-card-feature"; import "../card-features/hui-climate-preset-modes-card-feature"; +import "../card-features/hui-counter-actions-card-feature"; import "../card-features/hui-cover-open-close-card-feature"; import "../card-features/hui-cover-position-card-feature"; import "../card-features/hui-cover-tilt-card-feature"; @@ -22,6 +23,7 @@ import "../card-features/hui-numeric-input-card-feature"; import "../card-features/hui-select-options-card-feature"; import "../card-features/hui-target-temperature-card-feature"; import "../card-features/hui-target-humidity-card-feature"; +import "../card-features/hui-toggle-card-feature"; import "../card-features/hui-update-actions-card-feature"; import "../card-features/hui-vacuum-commands-card-feature"; import "../card-features/hui-water-heater-operation-modes-card-feature"; @@ -39,6 +41,7 @@ const TYPES = new Set([ "climate-swing-horizontal-modes", "climate-hvac-modes", "climate-preset-modes", + "counter-actions", "cover-open-close", "cover-position", "cover-tilt-position", @@ -57,6 +60,7 @@ const TYPES = new Set([ "select-options", "target-humidity", "target-temperature", + "toggle", "update-actions", "vacuum-commands", "water-heater-operation-modes", diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts index dcc3e49167..403bc7c31e 100644 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts @@ -3,10 +3,10 @@ import "@material/mwc-tab/mwc-tab"; import { mdiClose } from "@mdi/js"; import type { CSSResultGroup } from "lit"; import { css, html, LitElement, nothing } from "lit"; -import { ifDefined } from "lit/directives/if-defined"; import { customElement, property, state } from "lit/decorators"; import { cache } from "lit/directives/cache"; import { classMap } from "lit/directives/class-map"; +import { ifDefined } from "lit/directives/if-defined"; import memoize from "memoize-one"; import { fireEvent } from "../../../../common/dom/fire_event"; import { computeDomain } from "../../../../common/entity/compute_domain"; @@ -24,6 +24,7 @@ import { computeCards, computeSection, } from "../../common/generate-lovelace-config"; +import { addCard } from "../config-util"; import { findLovelaceContainer, parseLovelaceContainerPath, @@ -241,11 +242,24 @@ export class HuiCreateDialogCard } } + const lovelaceConfig = this._params!.lovelaceConfig; + const containerPath = this._params!.path; + const saveConfig = this._params!.saveConfig; + + const sectionConfig = + containerPath.length === 2 + ? findLovelaceContainer(lovelaceConfig, containerPath) + : undefined; + showEditCardDialog(this, { - lovelaceConfig: this._params!.lovelaceConfig, - saveConfig: this._params!.saveConfig, - path: this._params!.path, + lovelaceConfig, + saveCardConfig: async (newCardConfig) => { + const newConfig = addCard(lovelaceConfig, containerPath, newCardConfig); + await saveConfig(newConfig); + }, cardConfig: config, + sectionConfig, + isNew: true, }); this.closeDialog(); diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts index e0720420f2..a1fc6de482 100644 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts @@ -13,7 +13,6 @@ import "../../../../components/ha-dialog-header"; import "../../../../components/ha-icon-button"; import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card"; import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section"; -import type { LovelaceViewConfig } from "../../../../data/lovelace/config/view"; import { getCustomCardEntry, isCustomType, @@ -23,13 +22,12 @@ import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog- import type { HassDialog } from "../../../../dialogs/make-dialog-manager"; import { haStyleDialog } from "../../../../resources/styles"; import type { HomeAssistant } from "../../../../types"; +import { showToast } from "../../../../util/toast"; import { showSaveSuccessToast } from "../../../../util/toast-saved-success"; import "../../cards/hui-card"; import "../../sections/hui-section"; -import { addCard, replaceCard } from "../config-util"; import { getCardDocumentationURL } from "../get-dashboard-documentation-url"; import type { ConfigChangedEvent } from "../hui-element-editor"; -import { findLovelaceContainer } from "../lovelace-path"; import type { GUIModeChangedEvent } from "../types"; import "./hui-card-element-editor"; import type { HuiCardElementEditor } from "./hui-card-element-editor"; @@ -59,9 +57,7 @@ export class HuiDialogEditCard @state() private _cardConfig?: LovelaceCardConfig; - @state() private _containerConfig!: - | LovelaceViewConfig - | LovelaceSectionConfig; + @state() private _sectionConfig?: LovelaceSectionConfig; @state() private _saving = false; @@ -85,23 +81,10 @@ export class HuiDialogEditCard this._GUImode = true; this._guiModeAvailable = true; - const containerConfig = findLovelaceContainer( - params.lovelaceConfig, - params.path - ); + this._sectionConfig = this._params.sectionConfig; - if ("strategy" in containerConfig) { - throw new Error("Can't edit strategy"); - } - - this._containerConfig = containerConfig; - - if ("cardConfig" in params) { - this._cardConfig = params.cardConfig; - this._dirty = true; - } else { - this._cardConfig = this._containerConfig.cards?.[params.cardIndex]; - } + this._cardConfig = params.cardConfig; + this._dirty = Boolean(this._params.isNew); this.large = false; if (this._cardConfig && !Object.isFrozen(this._cardConfig)) { @@ -156,12 +139,12 @@ export class HuiDialogEditCard }; protected render() { - if (!this._params) { + if (!this._params || !this._cardConfig) { return nothing; } let heading: string; - if (this._cardConfig && this._cardConfig.type) { + if (this._cardConfig.type) { let cardName: string | undefined; if (isCustomType(this._cardConfig.type)) { // prettier-ignore @@ -181,13 +164,6 @@ export class HuiDialogEditCard "ui.panel.lovelace.editor.edit_card.typed_header", { type: cardName } ); - } else if (!this._cardConfig) { - heading = this._containerConfig.title - ? this.hass!.localize( - "ui.panel.lovelace.editor.edit_card.pick_card_view_title", - { name: this._containerConfig.title } - ) - : this.hass!.localize("ui.panel.lovelace.editor.edit_card.pick_card"); } else { heading = this.hass!.localize( "ui.panel.lovelace.editor.edit_card.header" @@ -230,10 +206,8 @@ export class HuiDialogEditCard
- ${this._isInSection + ${this._sectionConfig ? html` { + (cardConfig: LovelaceCardConfig) => { const { cards, title, ...containerConfig } = this - ._containerConfig as LovelaceSectionConfig; + ._sectionConfig as LovelaceSectionConfig; return { ...containerConfig, @@ -411,20 +381,18 @@ export class HuiDialogEditCard return; } this._saving = true; - const path = this._params!.path; - await this._params!.saveConfig( - "cardConfig" in this._params! - ? addCard(this._params!.lovelaceConfig, path, this._cardConfig!) - : replaceCard( - this._params!.lovelaceConfig, - [...path, this._params!.cardIndex], - this._cardConfig! - ) - ); - this._saving = false; - this._dirty = false; - showSaveSuccessToast(this, this.hass); - this.closeDialog(); + try { + await this._params!.saveCardConfig(this._cardConfig!); + this._saving = false; + this._dirty = false; + showSaveSuccessToast(this, this.hass); + this.closeDialog(); + } catch (err: any) { + showToast(this, { + message: err.message, + }); + this._saving = false; + } } static get styles(): CSSResultGroup { diff --git a/src/panels/lovelace/editor/card-editor/show-edit-card-dialog.ts b/src/panels/lovelace/editor/card-editor/show-edit-card-dialog.ts index a1da36b660..ae3930ba55 100644 --- a/src/panels/lovelace/editor/card-editor/show-edit-card-dialog.ts +++ b/src/panels/lovelace/editor/card-editor/show-edit-card-dialog.ts @@ -1,20 +1,15 @@ import { fireEvent } from "../../../../common/dom/fire_event"; import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card"; +import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section"; import type { LovelaceConfig } from "../../../../data/lovelace/config/types"; -import type { LovelaceContainerPath } from "../lovelace-path"; -export type EditCardDialogParams = { +export interface EditCardDialogParams { lovelaceConfig: LovelaceConfig; - saveConfig: (config: LovelaceConfig) => void; - path: LovelaceContainerPath; -} & ( - | { - cardIndex: number; - } - | { - cardConfig: LovelaceCardConfig; - } -); + saveCardConfig: (config: LovelaceCardConfig) => void; + cardConfig: LovelaceCardConfig; + sectionConfig?: LovelaceSectionConfig; + isNew?: boolean; +} export const importEditCardDialog = () => import("./hui-dialog-edit-card"); diff --git a/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts b/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts index 32170317b1..cb779eb5d2 100644 --- a/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts @@ -24,6 +24,7 @@ import { supportsClimateHvacModesCardFeature } from "../../card-features/hui-cli import { supportsClimatePresetModesCardFeature } from "../../card-features/hui-climate-preset-modes-card-feature"; import { supportsClimateSwingModesCardFeature } from "../../card-features/hui-climate-swing-modes-card-feature"; import { supportsClimateSwingHorizontalModesCardFeature } from "../../card-features/hui-climate-swing-horizontal-modes-card-feature"; +import { supportsCounterActionsCardFeature } from "../../card-features/hui-counter-actions-card-feature"; import { supportsCoverOpenCloseCardFeature } from "../../card-features/hui-cover-open-close-card-feature"; import { supportsCoverPositionCardFeature } from "../../card-features/hui-cover-position-card-feature"; import { supportsCoverTiltCardFeature } from "../../card-features/hui-cover-tilt-card-feature"; @@ -42,6 +43,7 @@ import { supportsNumericInputCardFeature } from "../../card-features/hui-numeric import { supportsSelectOptionsCardFeature } from "../../card-features/hui-select-options-card-feature"; import { supportsTargetHumidityCardFeature } from "../../card-features/hui-target-humidity-card-feature"; import { supportsTargetTemperatureCardFeature } from "../../card-features/hui-target-temperature-card-feature"; +import { supportsToggleCardFeature } from "../../card-features/hui-toggle-card-feature"; import { supportsUpdateActionsCardFeature } from "../../card-features/hui-update-actions-card-feature"; import { supportsVacuumCommandsCardFeature } from "../../card-features/hui-vacuum-commands-card-feature"; import { supportsWaterHeaterOperationModesCardFeature } from "../../card-features/hui-water-heater-operation-modes-card-feature"; @@ -58,6 +60,7 @@ const UI_FEATURE_TYPES = [ "climate-preset-modes", "climate-swing-modes", "climate-swing-horizontal-modes", + "counter-actions", "cover-open-close", "cover-position", "cover-tilt-position", @@ -76,6 +79,7 @@ const UI_FEATURE_TYPES = [ "select-options", "target-humidity", "target-temperature", + "toggle", "update-actions", "vacuum-commands", "water-heater-operation-modes", @@ -90,6 +94,7 @@ const EDITABLES_FEATURE_TYPES = new Set([ "climate-preset-modes", "climate-swing-modes", "climate-swing-horizontal-modes", + "counter-actions", "fan-preset-modes", "humidifier-modes", "lawn-mower-commands", @@ -111,6 +116,7 @@ const SUPPORTS_FEATURE_TYPES: Record< supportsClimateSwingHorizontalModesCardFeature, "climate-hvac-modes": supportsClimateHvacModesCardFeature, "climate-preset-modes": supportsClimatePresetModesCardFeature, + "counter-actions": supportsCounterActionsCardFeature, "cover-open-close": supportsCoverOpenCloseCardFeature, "cover-position": supportsCoverPositionCardFeature, "cover-tilt-position": supportsCoverTiltPositionCardFeature, @@ -129,6 +135,7 @@ const SUPPORTS_FEATURE_TYPES: Record< "select-options": supportsSelectOptionsCardFeature, "target-humidity": supportsTargetHumidityCardFeature, "target-temperature": supportsTargetTemperatureCardFeature, + toggle: supportsToggleCardFeature, "update-actions": supportsUpdateActionsCardFeature, "vacuum-commands": supportsVacuumCommandsCardFeature, "water-heater-operation-modes": supportsWaterHeaterOperationModesCardFeature, diff --git a/src/panels/lovelace/editor/config-elements/hui-counter-actions-card-feature-editor.ts b/src/panels/lovelace/editor/config-elements/hui-counter-actions-card-feature-editor.ts new file mode 100644 index 0000000000..97e17f7c93 --- /dev/null +++ b/src/panels/lovelace/editor/config-elements/hui-counter-actions-card-feature-editor.ts @@ -0,0 +1,91 @@ +import { html, LitElement, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import type { LocalizeFunc } from "../../../../common/translations/localize"; +import type { SchemaUnion } from "../../../../components/ha-form/types"; +import "../../../../components/ha-form/ha-form"; +import type { HomeAssistant } from "../../../../types"; +import { + COUNTER_ACTIONS, + type LovelaceCardFeatureContext, + type CounterActionsCardFeatureConfig, +} from "../../card-features/types"; +import type { LovelaceCardFeatureEditor } from "../../types"; + +@customElement("hui-counter-actions-card-feature-editor") +export class HuiCounterActionsCardFeatureEditor + extends LitElement + implements LovelaceCardFeatureEditor +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public context?: LovelaceCardFeatureContext; + + @state() private _config?: CounterActionsCardFeatureConfig; + + public setConfig(config: CounterActionsCardFeatureConfig): void { + this._config = config; + } + + private _schema = memoizeOne( + (localize: LocalizeFunc) => + [ + { + name: "actions", + selector: { + select: { + multiple: true, + mode: "list", + reorder: true, + options: COUNTER_ACTIONS.map((action) => ({ + value: action, + label: `${localize( + `ui.panel.lovelace.editor.features.types.counter-actions.actions.${action}` + )}`, + })), + }, + }, + }, + ] as const + ); + + protected render() { + if (!this.hass || !this._config) { + return nothing; + } + + const schema = this._schema(this.hass.localize); + + return html` + + `; + } + + private _valueChanged(ev: CustomEvent): void { + fireEvent(this, "config-changed", { config: ev.detail.value }); + } + + private _computeLabelCallback = ( + schema: SchemaUnion> + ) => { + switch (schema.name) { + default: + return this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.${schema.name}` + ); + } + }; +} + +declare global { + interface HTMLElementTagNameMap { + "hui-counter-actions-card-feature-editor": HuiCounterActionsCardFeatureEditor; + } +} diff --git a/src/panels/lovelace/editor/config-elements/hui-markdown-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-markdown-card-editor.ts index e6f1c29827..8b96938011 100644 --- a/src/panels/lovelace/editor/config-elements/hui-markdown-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-markdown-card-editor.ts @@ -1,29 +1,28 @@ import { html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { assert, assign, object, optional, string } from "superstruct"; +import { assert, assign, boolean, object, optional, string } from "superstruct"; +import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-form/ha-form"; -import type { SchemaUnion } from "../../../../components/ha-form/types"; +import type { + HaFormSchema, + SchemaUnion, +} from "../../../../components/ha-form/types"; import type { HomeAssistant } from "../../../../types"; import type { MarkdownCardConfig } from "../../cards/types"; import type { LovelaceCardEditor } from "../../types"; import { baseLovelaceCardConfig } from "../structs/base-card-struct"; +import type { LocalizeFunc } from "../../../../common/translations/localize"; const cardConfigStruct = assign( baseLovelaceCardConfig, object({ + text_only: optional(boolean()), title: optional(string()), content: string(), - theme: optional(string()), }) ); -const SCHEMA = [ - { name: "title", selector: { text: {} } }, - { name: "content", required: true, selector: { template: {} } }, - { name: "theme", selector: { theme: {} } }, -] as const; - @customElement("hui-markdown-card-editor") export class HuiMarkdownCardEditor extends LitElement @@ -38,16 +37,51 @@ export class HuiMarkdownCardEditor this._config = config; } + private _schema = memoizeOne( + (localize: LocalizeFunc, text_only: boolean) => + [ + { + name: "style", + required: true, + selector: { + select: { + mode: "dropdown", + options: ["card", "text-only"].map((style) => ({ + label: localize( + `ui.panel.lovelace.editor.card.markdown.style_options.${style}` + ), + value: style, + })), + }, + }, + }, + ...(!text_only + ? ([{ name: "title", selector: { text: {} } }] as const) + : []), + { name: "content", required: true, selector: { template: {} } }, + ] as const satisfies HaFormSchema[] + ); + protected render() { if (!this.hass || !this._config) { return nothing; } + const data = { + ...this._config, + style: this._config.text_only ? "text-only" : "card", + }; + + const schema = this._schema( + this.hass.localize, + this._config.text_only || false + ); + return html` @@ -55,17 +89,23 @@ export class HuiMarkdownCardEditor } private _valueChanged(ev: CustomEvent): void { - fireEvent(this, "config-changed", { config: ev.detail.value }); + const config = { ...ev.detail.value }; + + if (config.style === "text-only") { + config.text_only = true; + } else { + delete config.text_only; + } + delete config.style; + + fireEvent(this, "config-changed", { config }); } - private _computeLabelCallback = (schema: SchemaUnion) => { + private _computeLabelCallback = ( + schema: SchemaUnion> + ) => { switch (schema.name) { - case "theme": - return `${this.hass!.localize( - "ui.panel.lovelace.editor.card.generic.theme" - )} (${this.hass!.localize( - "ui.panel.lovelace.editor.card.config.optional" - )})`; + case "style": case "content": return this.hass!.localize( `ui.panel.lovelace.editor.card.markdown.${schema.name}` diff --git a/src/panels/lovelace/editor/config-elements/hui-stack-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-stack-card-editor.ts index 1d5d945dbc..595eae1866 100644 --- a/src/panels/lovelace/editor/config-elements/hui-stack-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-stack-card-editor.ts @@ -21,6 +21,7 @@ import { optional, string, } from "superstruct"; +import { keyed } from "lit/directives/keyed"; import type { HaFormSchema, SchemaUnion, @@ -84,6 +85,8 @@ export class HuiStackCardEditor @state() protected _guiModeAvailable? = true; + protected _keys = new WeakMap(); + protected _schema: readonly HaFormSchema[] = SCHEMA; @query("hui-card-element-editor") @@ -199,14 +202,16 @@ export class HuiStackCardEditor @click=${this._handleDeleteCard} >
- - + ${keyed( + this._getKey(this._config.cards[selected]), + html`` + )} ` : html` [ @@ -148,12 +153,6 @@ export class HuiTileCardEditor boolean: {}, }, }, - { - name: "vertical", - selector: { - boolean: {}, - }, - }, { name: "hide_state", selector: { @@ -175,6 +174,43 @@ export class HuiTileCardEditor }, ] as const satisfies readonly HaFormSchema[]) : []), + { + name: "", + type: "grid", + schema: [ + { + name: "content_layout", + required: true, + selector: { + select: { + mode: "dropdown", + options: ["horizontal", "vertical"].map((value) => ({ + label: localize( + `ui.panel.lovelace.editor.card.tile.content_layout_options.${value}` + ), + value, + })), + }, + }, + }, + { + name: "features_position", + required: true, + selector: { + select: { + mode: "dropdown", + options: ["bottom", "inline"].map((value) => ({ + label: localize( + `ui.panel.lovelace.editor.card.tile.features_position_options.${value}` + ), + value, + disabled: vertical && value === "inline", + })), + }, + }, + }, + ], + }, ], }, { @@ -223,12 +259,22 @@ export class HuiTileCardEditor const stateObj = entityId ? this.hass!.states[entityId] : undefined; const schema = this._schema( + this.hass.localize, entityId, - this._config!.hide_state ?? false, + this._config.hide_state ?? false, + this._config.vertical ?? false, this._displayActions ); - const data = this._config; + const data = { + ...this._config, + content_layout: this._config.vertical ? "vertical" : "horizontal", + }; + + // Default features position to bottom and force it to bottom in vertical mode + if (!data.features_position || data.vertical) { + data.features_position = "bottom"; + } return html` { + const newConfig = replaceCard( + this.lovelace!.config, + [this.viewIndex, this.index, cardIndex], + newCardConfig + ); + await this.lovelace!.saveConfig(newConfig); + }, + sectionConfig, + cardConfig, }); }); this._layoutElement.addEventListener("ll-delete-card", (ev) => { diff --git a/src/panels/lovelace/views/hui-view.ts b/src/panels/lovelace/views/hui-view.ts index 7111c20d01..f862915c86 100644 --- a/src/panels/lovelace/views/hui-view.ts +++ b/src/panels/lovelace/views/hui-view.ts @@ -21,6 +21,7 @@ import { showCreateBadgeDialog } from "../editor/badge-editor/show-create-badge- import { showEditBadgeDialog } from "../editor/badge-editor/show-edit-badge-dialog"; import { showCreateCardDialog } from "../editor/card-editor/show-create-card-dialog"; import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog"; +import { replaceCard } from "../editor/config-util"; import { type DeleteBadgeParams, performDeleteBadge, @@ -270,11 +271,22 @@ export class HUIView extends ReactiveElement { }); this._layoutElement.addEventListener("ll-edit-card", (ev) => { const { cardIndex } = parseLovelaceCardPath(ev.detail.path); + const viewConfig = this.lovelace!.config.views[this.index]; + if (isStrategyView(viewConfig)) { + return; + } + const cardConfig = viewConfig.cards![cardIndex]; showEditCardDialog(this, { lovelaceConfig: this.lovelace.config, - saveConfig: this.lovelace.saveConfig, - path: [this.index], - cardIndex, + saveCardConfig: async (newCardConfig) => { + const newConfig = replaceCard( + this.lovelace!.config, + [this.index, cardIndex], + newCardConfig + ); + await this.lovelace.saveConfig(newConfig); + }, + cardConfig, }); }); this._layoutElement.addEventListener("ll-delete-card", (ev) => { diff --git a/src/state-control/alarm_control_panel/ha-state-control-alarm_control_panel-modes.ts b/src/state-control/alarm_control_panel/ha-state-control-alarm_control_panel-modes.ts index 7cc31275f1..26f58ca468 100644 --- a/src/state-control/alarm_control_panel/ha-state-control-alarm_control_panel-modes.ts +++ b/src/state-control/alarm_control_panel/ha-state-control-alarm_control_panel-modes.ts @@ -47,7 +47,12 @@ export class HaStateControlAlarmControlPanelModes extends LitElement { } private async _setMode(mode: AlarmMode) { - setProtectedAlarmControlPanelMode(this, this.hass!, this.stateObj!, mode); + await setProtectedAlarmControlPanelMode( + this, + this.hass!, + this.stateObj!, + mode + ); } private async _valueChanged(ev: CustomEvent) { diff --git a/src/state/connection-mixin.ts b/src/state/connection-mixin.ts index 2a219abccb..fa017031dc 100644 --- a/src/state/connection-mixin.ts +++ b/src/state/connection-mixin.ts @@ -286,7 +286,10 @@ export const connectionMixin = >( clearInterval(this.__backendPingInterval); this.__backendPingInterval = setInterval(() => { if (this.hass?.connected) { - promiseTimeout(5000, this.hass?.connection.ping()).catch(() => { + // If the backend is busy, or the connection is latent, + // it can take more than 10 seconds for the ping to return. + // We give it a 15 second timeout to be safe. + promiseTimeout(15000, this.hass?.connection.ping()).catch(() => { if (!this.hass?.connected) { return; } @@ -296,7 +299,7 @@ export const connectionMixin = >( this.hass?.connection.reconnect(true); }); } - }, 10000); + }, 30000); } protected hassReconnected() { diff --git a/src/translations/en.json b/src/translations/en.json index 569f8c5e4d..54a143498e 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -370,7 +370,10 @@ "name": "Name", "optional": "optional", "default": "Default", - "dont_save": "Don't save" + "dont_save": "Don't save", + "copy": "Copy", + "show": "Show", + "hide": "Hide" }, "components": { "selectors": { @@ -2469,7 +2472,7 @@ }, "retention": "Retention", "custom_retention": "Custom retention", - "custom_retention_label": "Clean up every", + "custom_retention_label": "Keep only", "retention_description": "Based on the maximum number of backups or how many days they should be kept.", "retention_presets": { "copies_3": "3 backups", @@ -2512,6 +2515,7 @@ "menu": { "upload_backup": "Upload backup" }, + "agent_error": "Error in location {name}", "new_backup": "Backup now", "onboarding": { "title": "Set up backups", @@ -2650,7 +2654,9 @@ "title": "Locations", "description": "Your backup will be stored on these locations when this default backup is created. You can use all locations for custom backups.", "no_location": "No location selected", - "no_location_description": "You have to select at least one location to create a backup." + "no_location_description": "You have to select at least one location to create a backup.", + "more_locations": "Explore more locations", + "manage_network_storage": "Manage network storage" }, "encryption_key": { "title": "Encryption key", @@ -4724,6 +4730,23 @@ "fingerprint": "Certificate fingerprint:", "close": "Close" }, + "dialog_already_connected": { + "heading": "Account linked to other Home Assistant", + "description": "We noticed that another instance is currently connected to your Home Assistant Cloud account. Your Home Assistant Cloud account can only be signed into one Home Assistant instance at a time. If you log in here, the other instance will be disconnected along with its Cloud services.", + "other_home_assistant": "Other Home Assistant", + "ip_address": "IP Address", + "connected_at": "Connected at", + "obfuscated_ip": { + "show": "Show IP address", + "hide": "Hide IP address" + }, + "info_backups": { + "title": "Home Assistant Cloud backups", + "description": "Your Cloud backup may be overwritten if you proceed. We strongly recommend downloading your current backup from your Nabu Casa account page before continuing." + }, + "close": "Close", + "login_here": "Log in here" + }, "dialog_cloudhook": { "webhook_for": "Webhook for {name}", "managed_by_integration": "This webhook is managed by an integration and cannot be disabled.", @@ -6307,7 +6330,8 @@ "description": "Home Assistant is starting, please wait…" }, "map": { - "reset_focus": "Reset focus" + "reset_focus": "Reset focus", + "toggle_grouping": "Toggle grouping" }, "energy": { "loading": "Loading…", @@ -6994,7 +7018,8 @@ "suggested_cards": "Suggested cards", "other_cards": "Other cards", "custom_cards": "Custom cards", - "features": "Features" + "features": "Features", + "actions": "Actions" }, "heading": { "name": "Heading", @@ -7041,6 +7066,11 @@ "markdown": { "name": "Markdown", "content": "Content", + "style": "Style", + "style_options": { + "card": "Card", + "text-only": "Text only" + }, "description": "The Markdown card is used to render Markdown." }, "media-control": { @@ -7119,12 +7149,20 @@ "icon_tap_action": "Icon tap behavior", "icon_hold_action": "Icon hold behavior", "icon_double_tap_action": "Icon double tap behavior", - "interactions": "Interactions", "appearance": "Appearance", "show_entity_picture": "Show entity picture", - "vertical": "Vertical", "hide_state": "Hide state", - "state_content": "State content" + "state_content": "State content", + "features_position": "Features position", + "features_position_options": { + "bottom": "Bottom", + "inline": "Inline" + }, + "content_layout": "Content layout", + "content_layout_options": { + "horizontal": "Horizontal", + "vertical": "Vertical" + } }, "vertical-stack": { "name": "Vertical stack", @@ -7300,6 +7338,14 @@ "customize_modes": "Customize preset modes", "preset_modes": "Preset modes" }, + "counter-actions": { + "label": "Counter actions", + "actions": { + "increment": "Increment", + "decrement": "Decrement", + "reset": "Reset" + } + }, "fan-preset-modes": { "label": "Fan preset modes", "style": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style%]", @@ -7328,6 +7374,9 @@ "options": "Options", "customize_options": "Customize options" }, + "toggle": { + "label": "Toggle" + }, "numeric-input": { "label": "Numeric input", "style": "Style", diff --git a/test/.eslintrc b/test/.eslintrc deleted file mode 100644 index 04cfba7cd0..0000000000 --- a/test/.eslintrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "rules": { - "import/no-extraneous-dependencies": 0 - } -} diff --git a/test/common/color/convert-color.test.ts b/test/common/color/convert-color.test.ts index c04e995b2c..f3cc873109 100644 --- a/test/common/color/convert-color.test.ts +++ b/test/common/color/convert-color.test.ts @@ -51,4 +51,16 @@ describe("Color Conversion Tests", () => { expect(theme2hex("#ff0000")).toBe("#ff0000"); expect(theme2hex("unicorn")).toBe("unicorn"); }); + + it("should convert rgb theme color to hex", () => { + expect(theme2hex("rgb( 255, 0, 0)")).toBe("#ff0000"); + expect(theme2hex("rgb(0,255, 0)")).toBe("#00ff00"); + expect(theme2hex("rgb(0, 0,255 )")).toBe("#0000ff"); + }); + + it("should convert rgba theme color to hex by ignoring alpha", () => { + expect(theme2hex("rgba( 255, 0, 0, 0.5)")).toBe("#ff0000"); + expect(theme2hex("rgba(0,255, 0, 0.3)")).toBe("#00ff00"); + expect(theme2hex("rgba(0, 0,255 , 0.7)")).toBe("#0000ff"); + }); }); diff --git a/test/common/entity/can_toggle_state.test.ts b/test/common/entity/can_toggle_state.test.ts index 42a71d1944..c85ea7887b 100644 --- a/test/common/entity/can_toggle_state.test.ts +++ b/test/common/entity/can_toggle_state.test.ts @@ -63,4 +63,28 @@ describe("canToggleState", () => { }; assert.isFalse(canToggleState(hass, stateObj)); }); + + it("Detects group with missing entity", () => { + const stateObj: any = { + entity_id: "group.bla", + state: "on", + attributes: { + entity_id: ["light.non_existing"], + }, + }; + + assert.isFalse(canToggleState(hass, stateObj)); + }); + + it("Detects group with off state", () => { + const stateObj: any = { + entity_id: "group.bla", + state: "off", + attributes: { + entity_id: ["light.test"], + }, + }; + + assert.isTrue(canToggleState(hass, stateObj)); + }); }); diff --git a/test/common/entity/compute_attribute_display.test.ts b/test/common/entity/compute_attribute_display.test.ts new file mode 100644 index 0000000000..ad606e9d01 --- /dev/null +++ b/test/common/entity/compute_attribute_display.test.ts @@ -0,0 +1,371 @@ +import type { + HassConfig, + HassEntity, + HassEntityBase, +} from "home-assistant-js-websocket"; +import { describe, it, expect } from "vitest"; +import { + computeAttributeValueDisplay, + computeAttributeNameDisplay, +} from "../../../src/common/entity/compute_attribute_display"; +import type { FrontendLocaleData } from "../../../src/data/translation"; +import type { HomeAssistant } from "../../../src/types"; + +export const localizeMock = (key: string) => { + const translations = { + "state.default.unknown": "Unknown", + "component.test_platform.entity.sensor.test_translation_key.state_attributes.attribute.state.42": + "42", + "component.test_platform.entity.sensor.test_translation_key.state_attributes.attribute.state.attributeValue": + "Localized Attribute Name", + "component.media_player.entity_component.media_player.state_attributes.attribute.state.attributeValue": + "Localized Media Player Attribute Name", + "component.media_player.entity_component._.state_attributes.attribute.state.attributeValue": + "Media Player Attribute Name", + }; + return translations[key] || ""; +}; + +export const stateObjMock = { + entity_id: "sensor.test", + attributes: { + device_class: "temperature", + }, +} as HassEntityBase; + +export const localeMock = { + language: "en", +} as FrontendLocaleData; + +export const configMock = { + unit_system: { + temperature: "°C", + }, +} as HassConfig; + +export const entitiesMock = { + "sensor.test": { + platform: "test_platform", + translation_key: "test_translation_key", + }, + "media_player.test": { + platform: "media_player", + }, +} as unknown as HomeAssistant["entities"]; + +describe("computeAttributeValueDisplay", () => { + it("should return unknown state for null value", () => { + const result = computeAttributeValueDisplay( + localizeMock, + stateObjMock, + localeMock, + configMock, + entitiesMock, + "attribute", + null + ); + expect(result).toBe("Unknown"); + }); + + it("should return formatted number for numeric value", () => { + const result = computeAttributeValueDisplay( + localizeMock, + stateObjMock, + localeMock, + configMock, + entitiesMock, + "attribute", + 42 + ); + expect(result).toBe("42"); + }); + + it("should return number from formatter", () => { + const stateObj = { + entity_id: "media_player.test", + attributes: { + device_class: "media_player", + volume_level: 0.42, + }, + } as unknown as HassEntityBase; + const result = computeAttributeValueDisplay( + localizeMock, + stateObj, + localeMock, + configMock, + entitiesMock, + "volume_level" + ); + expect(result).toBe("42%"); + }); + + it("should return formatted date for date string", () => { + const result = computeAttributeValueDisplay( + localizeMock, + stateObjMock, + localeMock, + configMock, + entitiesMock, + "attribute", + "2023-10-10" + ); + expect(result).toBe("October 10, 2023"); + }); + + it("should return formatted datetime for timestamp", () => { + const result = computeAttributeValueDisplay( + localizeMock, + stateObjMock, + localeMock, + configMock, + entitiesMock, + "attribute", + "2023-10-10T10:10:10" + ); + expect(result).toBe("October 10, 2023 at 10:10:10"); + }); + + it("should return JSON string for object value", () => { + const result = computeAttributeValueDisplay( + localizeMock, + stateObjMock, + localeMock, + configMock, + entitiesMock, + "attribute", + { key: "value" } + ); + expect(result).toBe('{"key":"value"}'); + }); + + it("should return concatenated values for array", () => { + const result = computeAttributeValueDisplay( + localizeMock, + stateObjMock, + localeMock, + configMock, + entitiesMock, + "attribute", + [1, 2, 3] + ); + expect(result).toBe("1, 2, 3"); + }); + + it("should set special unit for weather domain", () => { + const stateObj = { + entity_id: "weather.test", + attributes: { + temperature: 42, + }, + } as unknown as HassEntityBase; + const result = computeAttributeValueDisplay( + localizeMock, + stateObj, + localeMock, + configMock, + entitiesMock, + "temperature" + ); + expect(result).toBe("42 °C"); + }); + + it("should set temperature unit for temperature attribute", () => { + const stateObj = { + entity_id: "sensor.test", + attributes: { + temperature: 42, + }, + } as unknown as HassEntityBase; + const result = computeAttributeValueDisplay( + localizeMock, + stateObj, + localeMock, + configMock, + entitiesMock, + "temperature" + ); + expect(result).toBe("42 °C"); + }); + + it("should return translation from translation key", () => { + const result = computeAttributeValueDisplay( + localizeMock, + stateObjMock, + localeMock, + configMock, + entitiesMock, + "attribute", + "attributeValue" + ); + expect(result).toBe("Localized Attribute Name"); + }); + + it("should return device class translation", () => { + const stateObj = { + entity_id: "media_player.test", + attributes: { + device_class: "media_player", + volume_level: 0.42, + }, + } as unknown as HassEntityBase; + const result = computeAttributeValueDisplay( + localizeMock, + stateObj, + localeMock, + configMock, + entitiesMock, + "attribute", + "attributeValue" + ); + expect(result).toBe("Localized Media Player Attribute Name"); + }); + + it("should return attribute value translation", () => { + const stateObj = { + entity_id: "media_player.test", + attributes: { + volume_level: 0.42, + }, + } as unknown as HassEntityBase; + const result = computeAttributeValueDisplay( + localizeMock, + stateObj, + localeMock, + configMock, + entitiesMock, + "attribute", + "attributeValue" + ); + expect(result).toBe("Media Player Attribute Name"); + }); + + it("should return attribute value", () => { + const stateObj = { + entity_id: "media_player.test", + attributes: { + volume_level: 0.42, + }, + } as unknown as HassEntityBase; + const result = computeAttributeValueDisplay( + localizeMock, + stateObj, + localeMock, + configMock, + entitiesMock, + "attribute", + "attributeValue2" + ); + expect(result).toBe("attributeValue2"); + }); +}); + +describe("computeAttributeNameDisplay", () => { + it("should return localized name for attribute", () => { + const localize = (key: string) => { + if ( + key === + "component.light.entity.light.entity_translation_key.state_attributes.updated_at.name" + ) { + return "Updated at"; + } + return "unknown"; + }; + + const stateObj = { + entity_id: "light.test", + attributes: { + device_class: "light", + }, + } as HassEntity; + + const entities = { + "light.test": { + translation_key: "entity_translation_key", + platform: "light", + }, + } as unknown as HomeAssistant["entities"]; + + const result = computeAttributeNameDisplay( + localize, + stateObj, + entities, + "updated_at" + ); + expect(result).toBe("Updated at"); + }); + + it("should return device class translation", () => { + const localize = (key: string) => { + if ( + key === + "component.light.entity_component.light.state_attributes.brightness.name" + ) { + return "Brightness"; + } + return "unknown"; + }; + + const stateObj = { + entity_id: "light.test", + attributes: { + device_class: "light", + }, + } as HassEntity; + + const entities = {} as unknown as HomeAssistant["entities"]; + + const result = computeAttributeNameDisplay( + localize, + stateObj, + entities, + "brightness" + ); + expect(result).toBe("Brightness"); + }); + + it("should return default attribute name", () => { + const localize = (key: string) => { + if ( + key === + "component.light.entity_component._.state_attributes.brightness.name" + ) { + return "Brightness"; + } + return "unknown"; + }; + + const stateObj = { + entity_id: "light.test", + attributes: {}, + } as HassEntity; + + const entities = {} as unknown as HomeAssistant["entities"]; + + const result = computeAttributeNameDisplay( + localize, + stateObj, + entities, + "brightness" + ); + expect(result).toBe("Brightness"); + }); + + it("should return capitalized attribute name", () => { + const localize = () => ""; + + const stateObj = { + entity_id: "light.test", + attributes: {}, + } as HassEntity; + + const entities = {} as unknown as HomeAssistant["entities"]; + + const result = computeAttributeNameDisplay( + localize, + stateObj, + entities, + "brightness__ip_id_mac_gps_GPS" + ); + expect(result).toBe("Brightness IP ID MAC GPS GPS"); + }); +}); diff --git a/test/common/entity/compute_state_display.test.ts b/test/common/entity/compute_state_display.test.ts index 2a120abe5c..05934c5ba2 100644 --- a/test/common/entity/compute_state_display.test.ts +++ b/test/common/entity/compute_state_display.test.ts @@ -1,5 +1,9 @@ -import { assert, describe, it, beforeEach } from "vitest"; -import { computeStateDisplay } from "../../../src/common/entity/compute_state_display"; +import type { HassConfig } from "home-assistant-js-websocket"; +import { assert, describe, it, beforeEach, expect } from "vitest"; +import { + computeStateDisplay, + computeStateDisplayFromEntityAttributes, +} from "../../../src/common/entity/compute_state_display"; import { UNKNOWN } from "../../../src/data/entity"; import type { FrontendLocaleData } from "../../../src/data/translation"; import { @@ -10,6 +14,7 @@ import { TimeZone, } from "../../../src/data/translation"; import { demoConfig } from "../../../src/fake_data/demo_config"; +import type { EntityRegistryDisplayEntry } from "../../../src/data/entity_registry"; let localeData: FrontendLocaleData; @@ -617,3 +622,85 @@ describe("computeStateDisplay", () => { ); }); }); + +describe("computeStateDisplayFromEntityAttributes with numeric device classes", () => { + it("Should format duration sensor", () => { + const result = computeStateDisplayFromEntityAttributes( + // eslint-disable-next-line @typescript-eslint/no-empty-function + (() => {}) as any, + { + language: "en", + } as FrontendLocaleData, + [], + {} as HassConfig, + { + display_precision: 2, + } as EntityRegistryDisplayEntry, + "number.test", + { + device_class: "duration", + unit_of_measurement: "min", + }, + "12" + ); + expect(result).toBe("12.00 min"); + }); + it("Should format duration sensor with seconds", () => { + const result = computeStateDisplayFromEntityAttributes( + // eslint-disable-next-line @typescript-eslint/no-empty-function + (() => {}) as any, + { + language: "en", + } as FrontendLocaleData, + [], + {} as HassConfig, + undefined, + "number.test", + { + device_class: "duration", + unit_of_measurement: "s", + }, + "12" + ); + expect(result).toBe("12 s"); + }); + + it("Should format monetary device_class", () => { + const result = computeStateDisplayFromEntityAttributes( + // eslint-disable-next-line @typescript-eslint/no-empty-function + (() => {}) as any, + { + language: "en", + } as FrontendLocaleData, + [], + {} as HassConfig, + undefined, + "number.test", + { + device_class: "monetary", + unit_of_measurement: "$", + }, + "12" + ); + expect(result).toBe("12 $"); + }); +}); + +describe("computeStateDisplayFromEntityAttributes datetime device calss", () => { + it("Should format datetime sensor", () => { + const result = computeStateDisplayFromEntityAttributes( + // eslint-disable-next-line @typescript-eslint/no-empty-function + (() => {}) as any, + { + language: "en", + } as FrontendLocaleData, + [], + {} as HassConfig, + undefined, + "button.test", + {}, + "2020-01-01T12:00:00+00:00" + ); + expect(result).toBe("January 1, 2020 at 12:00"); + }); +}); diff --git a/test/vitest.config.ts b/test/vitest.config.ts index 811a8a8a7d..6c2fff36f7 100644 --- a/test/vitest.config.ts +++ b/test/vitest.config.ts @@ -1,6 +1,8 @@ import { defineConfig } from "vitest/config"; +import tsconfigPaths from "vite-tsconfig-paths"; export default defineConfig({ + plugins: [tsconfigPaths()], test: { environment: "jsdom", // to run in browser-like environment env: { diff --git a/tsconfig.json b/tsconfig.json index 83c5f840ac..7c24087739 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -49,6 +49,36 @@ "./node_modules/@lrnwebcomponents/simple-tooltip/custom-elements.json" ] } - ] + ], + "paths": { + "lit/static-html": ["./node_modules/lit/static-html.js"], + "lit/decorators": ["./node_modules/lit/decorators.js"], + "lit/directive": ["./node_modules/lit/directive.js"], + "lit/directives/until": ["./node_modules/lit/directives/until.js"], + "lit/directives/class-map": [ + "./node_modules/lit/directives/class-map.js" + ], + "lit/directives/style-map": [ + "./node_modules/lit/directives/style-map.js" + ], + "lit/directives/if-defined": [ + "./node_modules/lit/directives/if-defined.js" + ], + "lit/directives/guard": ["./node_modules/lit/directives/guard.js"], + "lit/directives/cache": ["./node_modules/lit/directives/cache.js"], + "lit/directives/repeat": ["./node_modules/lit/directives/repeat.js"], + "lit/directives/live": ["./node_modules/lit/directives/live.js"], + "lit/directives/keyed": ["./node_modules/lit/directives/keyed.js"], + "lit/polyfill-support": ["./node_modules/lit/polyfill-support.js"], + "@lit-labs/virtualizer/layouts/grid": [ + "./node_modules/@lit-labs/virtualizer/layouts/grid.js" + ], + "@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver": [ + "./node_modules/@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver.js" + ], + "@lit-labs/observers/resize-controller": [ + "./node_modules/@lit-labs/observers/resize-controller.js" + ] + } } } diff --git a/yarn.lock b/yarn.lock index 34445df444..0bcecb40cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -69,40 +69,39 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:7.26.8, @babel/core@npm:^7.24.4": - version: 7.26.8 - resolution: "@babel/core@npm:7.26.8" +"@babel/core@npm:7.26.9, @babel/core@npm:^7.24.4": + version: 7.26.9 + resolution: "@babel/core@npm:7.26.9" dependencies: "@ampproject/remapping": "npm:^2.2.0" "@babel/code-frame": "npm:^7.26.2" - "@babel/generator": "npm:^7.26.8" + "@babel/generator": "npm:^7.26.9" "@babel/helper-compilation-targets": "npm:^7.26.5" "@babel/helper-module-transforms": "npm:^7.26.0" - "@babel/helpers": "npm:^7.26.7" - "@babel/parser": "npm:^7.26.8" - "@babel/template": "npm:^7.26.8" - "@babel/traverse": "npm:^7.26.8" - "@babel/types": "npm:^7.26.8" - "@types/gensync": "npm:^1.0.0" + "@babel/helpers": "npm:^7.26.9" + "@babel/parser": "npm:^7.26.9" + "@babel/template": "npm:^7.26.9" + "@babel/traverse": "npm:^7.26.9" + "@babel/types": "npm:^7.26.9" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 10/a70d903dfd2c97e044b27fb480584fab6111954ced6987c6628ee4d37071ca446eca7830d72763a8d16a0da64eb83e02e3073d16c09e9eefa4a4ab38162636b4 + checksum: 10/ceed199dbe25f286a0a59a2ea7879aed37c1f3bb289375d061eda4752cab2ba365e7f9e969c7fd3b9b95c930493db6eeb5a6d6f017dd135fb5a4503449aad753 languageName: node linkType: hard -"@babel/generator@npm:^7.26.8": - version: 7.26.8 - resolution: "@babel/generator@npm:7.26.8" +"@babel/generator@npm:^7.26.9": + version: 7.26.9 + resolution: "@babel/generator@npm:7.26.9" dependencies: - "@babel/parser": "npm:^7.26.8" - "@babel/types": "npm:^7.26.8" + "@babel/parser": "npm:^7.26.9" + "@babel/types": "npm:^7.26.9" "@jridgewell/gen-mapping": "npm:^0.3.5" "@jridgewell/trace-mapping": "npm:^0.3.25" jsesc: "npm:^3.0.2" - checksum: 10/8c5af0f74aad2e575f2f833af0a9a38dda5abe0574752b5e0812677c78e5dc713b6b0c9ac3b30799ba6ef883614f9f0ef79d3aa10ba8f0e54f7f0284381b0059 + checksum: 10/95075dd6158a49efcc71d7f2c5d20194fcf245348de7723ca35e37cd5800587f1d4de2be6c4ba87b5f5fbb967c052543c109eaab14b43f6a73eb05ccd9a5bb44 languageName: node linkType: hard @@ -236,15 +235,15 @@ __metadata: linkType: hard "@babel/helper-replace-supers@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/helper-replace-supers@npm:7.25.9" + version: 7.26.5 + resolution: "@babel/helper-replace-supers@npm:7.26.5" dependencies: "@babel/helper-member-expression-to-functions": "npm:^7.25.9" "@babel/helper-optimise-call-expression": "npm:^7.25.9" - "@babel/traverse": "npm:^7.25.9" + "@babel/traverse": "npm:^7.26.5" peerDependencies: "@babel/core": ^7.0.0 - checksum: 10/8ebf787016953e4479b99007bac735c9c860822fafc51bc3db67bc53814539888797238c81fa8b948b6da897eb7b1c1d4f04df11e501a7f0596b356be02de2ab + checksum: 10/cfb911d001a8c3d2675077dbb74ee8d7d5533b22d74f8d775cefabf19c604f6cbc22cfeb94544fe8efa626710d920f04acb22923017e68f46f5fdb1cb08b32ad languageName: node linkType: hard @@ -290,13 +289,13 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.26.7": - version: 7.26.7 - resolution: "@babel/helpers@npm:7.26.7" +"@babel/helpers@npm:^7.26.9": + version: 7.26.9 + resolution: "@babel/helpers@npm:7.26.9" dependencies: - "@babel/template": "npm:^7.25.9" - "@babel/types": "npm:^7.26.7" - checksum: 10/97593a0c9b3c5e2e7cf824e549b5f6fa6dc739593ad93d5bb36d06883d8124beac63ee2154c9a514dbee68a169d5683ab463e0ac6713ad92fb4854cea35ed4d4 + "@babel/template": "npm:^7.26.9" + "@babel/types": "npm:^7.26.9" + checksum: 10/267dfa7d04dff7720610497f466aa7b60652b7ec8dde5914527879350c9d655271e892117c5b2f0f083d92d2a8e5e2cf9832d4f98cd7fb72d78f796002af19a1 languageName: node linkType: hard @@ -312,14 +311,14 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.23.5, @babel/parser@npm:^7.25.4, @babel/parser@npm:^7.26.8": - version: 7.26.8 - resolution: "@babel/parser@npm:7.26.8" +"@babel/parser@npm:^7.23.5, @babel/parser@npm:^7.25.4, @babel/parser@npm:^7.26.9": + version: 7.26.9 + resolution: "@babel/parser@npm:7.26.9" dependencies: - "@babel/types": "npm:^7.26.8" + "@babel/types": "npm:^7.26.9" bin: parser: ./bin/babel-parser.js - checksum: 10/0dd9d6b2022806b696b7a9ffb50b147f13525c497663d758a95adcc3ca0fa1d1bbb605fcc0604acc1cade60c3dbf2c1e0dd22b7aed17f8ad1c58c954208ffe7a + checksum: 10/cb84fe3ba556d6a4360f3373cf7eb0901c46608c8d77330cc1ca021d60f5d6ebb4056a8e7f9dd0ef231923ef1fe69c87b11ce9e160d2252e089a20232a2b942b languageName: node linkType: hard @@ -661,15 +660,15 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-for-of@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/plugin-transform-for-of@npm:7.25.9" +"@babel/plugin-transform-for-of@npm:^7.26.9": + version: 7.26.9 + resolution: "@babel/plugin-transform-for-of@npm:7.26.9" dependencies: - "@babel/helper-plugin-utils": "npm:^7.25.9" + "@babel/helper-plugin-utils": "npm:^7.26.5" "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.25.9" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/63a2db7fe06c2e3f5fc1926f478dac66a5f7b3eaeb4a0ffae577e6f3cb3d822cb1ed2ed3798f70f5cb1aa06bc2ad8bcd1f557342f5c425fd83c37a8fc1cfd2ba + checksum: 10/25df1ea3bcecc1bcef99f273fbd8f4a73a509ab7ef3db93629817cb02f9d24868ca3760347f864c8fa4ab79ffa86fb09b2f2de1f2ba1f73f27dbe0c3973c6868 languageName: node linkType: hard @@ -955,9 +954,9 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-runtime@npm:7.26.8": - version: 7.26.8 - resolution: "@babel/plugin-transform-runtime@npm:7.26.8" +"@babel/plugin-transform-runtime@npm:7.26.9": + version: 7.26.9 + resolution: "@babel/plugin-transform-runtime@npm:7.26.9" dependencies: "@babel/helper-module-imports": "npm:^7.25.9" "@babel/helper-plugin-utils": "npm:^7.26.5" @@ -967,7 +966,7 @@ __metadata: semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/2292a98b764165423768a4d2dc9e916a89930854c015d551eaf448f0662bcc36edaefc8dc67e390929a5f2e9006aad5883ec9e82ffddf11af3c6aa7d9af5e779 + checksum: 10/7893c3501474ef19af778d02c711edc511b82df2cd4500c575f1c61565026253b024a6c9868808ea8fc3c67168e3c8bfda3b943b0d3ac3e870ada5a2dff4a77a languageName: node linkType: hard @@ -1089,9 +1088,9 @@ __metadata: languageName: node linkType: hard -"@babel/preset-env@npm:7.26.8, @babel/preset-env@npm:^7.11.0": - version: 7.26.8 - resolution: "@babel/preset-env@npm:7.26.8" +"@babel/preset-env@npm:7.26.9, @babel/preset-env@npm:^7.11.0": + version: 7.26.9 + resolution: "@babel/preset-env@npm:7.26.9" dependencies: "@babel/compat-data": "npm:^7.26.8" "@babel/helper-compilation-targets": "npm:^7.26.5" @@ -1122,7 +1121,7 @@ __metadata: "@babel/plugin-transform-dynamic-import": "npm:^7.25.9" "@babel/plugin-transform-exponentiation-operator": "npm:^7.26.3" "@babel/plugin-transform-export-namespace-from": "npm:^7.25.9" - "@babel/plugin-transform-for-of": "npm:^7.25.9" + "@babel/plugin-transform-for-of": "npm:^7.26.9" "@babel/plugin-transform-function-name": "npm:^7.25.9" "@babel/plugin-transform-json-strings": "npm:^7.25.9" "@babel/plugin-transform-literals": "npm:^7.25.9" @@ -1164,7 +1163,7 @@ __metadata: semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/0f2b5c252b6ee1298f3a3f28ae6a85551cf062b2397cf4f2b11d5235c7bba58b8db2aa651ea42f7642f7c79024684a40da0f53035c820afa03bc37850bc28cbf + checksum: 10/ac6fad331760c0bc25ed428b7696b297bad7046a75f30e544b392acfb33709f12316b9a5b0c8606f933d5756e1b9d527b46fda09693db52e851325443dd6a574 languageName: node linkType: hard @@ -1196,48 +1195,48 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:7.26.7, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.8.4": - version: 7.26.7 - resolution: "@babel/runtime@npm:7.26.7" +"@babel/runtime@npm:7.26.9, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.8.4": + version: 7.26.9 + resolution: "@babel/runtime@npm:7.26.9" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: 10/c7a661a6836b332d9d2e047cba77ba1862c1e4f78cec7146db45808182ef7636d8a7170be9797e5d8fd513180bffb9fa16f6ca1c69341891efec56113cf22bfc + checksum: 10/08edd07d774eafbf157fdc8450ed6ddd22416fdd8e2a53e4a00349daba1b502c03ab7f7ad3ad3a7c46b9a24d99b5697591d0f852ee2f84642082ef7dda90b83d languageName: node linkType: hard -"@babel/template@npm:^7.25.9, @babel/template@npm:^7.26.8": - version: 7.26.8 - resolution: "@babel/template@npm:7.26.8" +"@babel/template@npm:^7.25.9, @babel/template@npm:^7.26.9": + version: 7.26.9 + resolution: "@babel/template@npm:7.26.9" dependencies: "@babel/code-frame": "npm:^7.26.2" - "@babel/parser": "npm:^7.26.8" - "@babel/types": "npm:^7.26.8" - checksum: 10/bc45db0fd4e92d35813c2a8e8fa80b8a887c275b323537b8ebd9c64228c1614e81c74236d08f744017a6562987e48b10501688f7a8be5d6a53fb6acb61aa01c8 + "@babel/parser": "npm:^7.26.9" + "@babel/types": "npm:^7.26.9" + checksum: 10/240288cebac95b1cc1cb045ad143365643da0470e905e11731e63280e43480785bd259924f4aea83898ef68e9fa7c176f5f2d1e8b0a059b27966e8ca0b41a1b6 languageName: node linkType: hard -"@babel/traverse@npm:^7.25.9, @babel/traverse@npm:^7.26.8": - version: 7.26.8 - resolution: "@babel/traverse@npm:7.26.8" +"@babel/traverse@npm:^7.25.9, @babel/traverse@npm:^7.26.5, @babel/traverse@npm:^7.26.8, @babel/traverse@npm:^7.26.9": + version: 7.26.9 + resolution: "@babel/traverse@npm:7.26.9" dependencies: "@babel/code-frame": "npm:^7.26.2" - "@babel/generator": "npm:^7.26.8" - "@babel/parser": "npm:^7.26.8" - "@babel/template": "npm:^7.26.8" - "@babel/types": "npm:^7.26.8" + "@babel/generator": "npm:^7.26.9" + "@babel/parser": "npm:^7.26.9" + "@babel/template": "npm:^7.26.9" + "@babel/types": "npm:^7.26.9" debug: "npm:^4.3.1" globals: "npm:^11.1.0" - checksum: 10/2785718e54d7a243a4c1b92fe9c2cec0d3b8725b095061b8fdb9812bbcf1b94b743b39d96312644efa05692f9c2646772a8154c89625f428aa6b568cebf4ecf9 + checksum: 10/c16a79522eafa0a7e40eb556bf1e8a3d50dbb0ff943a80f2c06cee2ec7ff87baa0c5d040a5cff574d9bcb3bed05e7d8c6f13b238a931c97267674b02c6cf45b4 languageName: node linkType: hard -"@babel/types@npm:^7.25.4, @babel/types@npm:^7.25.9, @babel/types@npm:^7.26.7, @babel/types@npm:^7.26.8, @babel/types@npm:^7.4.4": - version: 7.26.8 - resolution: "@babel/types@npm:7.26.8" +"@babel/types@npm:^7.25.4, @babel/types@npm:^7.25.9, @babel/types@npm:^7.26.9, @babel/types@npm:^7.4.4": + version: 7.26.9 + resolution: "@babel/types@npm:7.26.9" dependencies: "@babel/helper-string-parser": "npm:^7.25.9" "@babel/helper-validator-identifier": "npm:^7.25.9" - checksum: 10/e6889246889706ee5e605cbfe62657c829427e0ddef0e4d18679a0d989bdb23e700b5a851d84821c2bdce3ded9ae5b9285fe1028562201b28f816e3ade6c3d0d + checksum: 10/11b62ea7ed64ef7e39cc9b33852c1084064c3b970ae0eaa5db659241cfb776577d1e68cbff4de438bada885d3a827b52cc0f3746112d8e1bc672bb99a8eb5b56 languageName: node linkType: hard @@ -1331,14 +1330,14 @@ __metadata: languageName: node linkType: hard -"@codemirror/view@npm:6.36.2, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0": - version: 6.36.2 - resolution: "@codemirror/view@npm:6.36.2" +"@codemirror/view@npm:6.36.3, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0": + version: 6.36.3 + resolution: "@codemirror/view@npm:6.36.3" dependencies: "@codemirror/state": "npm:^6.5.0" style-mod: "npm:^4.1.0" w3c-keyname: "npm:^2.2.4" - checksum: 10/9ef7fcf4f9d9b6e66645ae65da1bf0c90e08f6ba786de0373b9f3644632066b91b8ea20faf67bb81eb9adf310ae76888cc7fd0901e2bb4821193f5427455c137 + checksum: 10/a41b9af6c57c74cab2dc3e7a3e5a6d3912253514667878f7cacb5b889892e445e514e68200a7ac4d4c3eefc1a221ae5f7b5a85f211c3b7fdd6749475086e4dff languageName: node linkType: hard @@ -2259,22 +2258,22 @@ __metadata: languageName: node linkType: hard -"@lit-labs/motion@npm:1.0.7": - version: 1.0.7 - resolution: "@lit-labs/motion@npm:1.0.7" +"@lit-labs/motion@npm:1.0.8": + version: 1.0.8 + resolution: "@lit-labs/motion@npm:1.0.8" dependencies: lit: "npm:^3.1.2" - checksum: 10/6ac9e35b6272648299ae59d3f728e4a016b21df7d75c18d673b8a0cda2d64baad9ced61892df43c43224d5e5f69d94b148d4ecdc2eadb3d2fe26b2c93c799b63 + checksum: 10/7b7f1c195ff592eb10e549e5bde07b3d8558a14b07205dc9768bb52c7ae9881a52b9790beb95c45384827227100b4e8a9f03386627c70c6683117e513419899e languageName: node linkType: hard -"@lit-labs/observers@npm:2.0.4": - version: 2.0.4 - resolution: "@lit-labs/observers@npm:2.0.4" +"@lit-labs/observers@npm:2.0.5": + version: 2.0.5 + resolution: "@lit-labs/observers@npm:2.0.5" dependencies: "@lit/reactive-element": "npm:^1.0.0 || ^2.0.0" lit-html: "npm:^3.2.0" - checksum: 10/b1deb80e88d7de01e90e084a5b7e0a22938d6ef0ddb4e2da86a8356e37ca7be1bd7c94f5abe51883f787f650b2a9c7a024b1eef854da3ac59870a5abf7fdf74b + checksum: 10/4de55e32072f5825ca288c0b32b40e6805c09cd6101c345c44dfceb6bfd03cadf652dbfcd023252ebc2f7519f036d7e0f2f8fa1e58915abbc18733ed11218086 languageName: node linkType: hard @@ -2285,13 +2284,13 @@ __metadata: languageName: node linkType: hard -"@lit-labs/virtualizer@npm:2.0.15": - version: 2.0.15 - resolution: "@lit-labs/virtualizer@npm:2.0.15" +"@lit-labs/virtualizer@npm:2.1.0": + version: 2.1.0 + resolution: "@lit-labs/virtualizer@npm:2.1.0" dependencies: lit: "npm:^3.2.0" tslib: "npm:^2.0.3" - checksum: 10/52becfbe1ae3ee771cc873e2f47e58fbc4bc0cabdadc9caed8aa4565242ce72f59a2633f687387317ce69eb455216583e54c53d0f64ff5c9e2c843f713c35fca + checksum: 10/63837ee0728e3f4873ab604a135c78b2188e2edd15b2cdd70be1651c1bcbd3918f418e41586349d3bb92e264ba4db44fe83bdd580045beac5b43d2d0723e34de languageName: node linkType: hard @@ -2313,10 +2312,10 @@ __metadata: languageName: node linkType: hard -"@lokalise/node-api@npm:13.1.0": - version: 13.1.0 - resolution: "@lokalise/node-api@npm:13.1.0" - checksum: 10/708b4c2dc5de7e30b7af28fb67539281875fad2e721db56d88e1860b9d8836e8ce3dc9af5c1b16aa7f1ab6dded1373206834c301d22d8e60539e3ceae4abcf57 +"@lokalise/node-api@npm:13.2.0": + version: 13.2.0 + resolution: "@lokalise/node-api@npm:13.2.0" + checksum: 10/0b3e67cc0784c4ab9a499e210db5ab48f2445300cecb96cee0d39de7aa037bb624aaf2d4b8f4973c9644dfca4f7dd38385d12cb25da03674648d6dee3b742109 languageName: node linkType: hard @@ -3472,18 +3471,18 @@ __metadata: languageName: node linkType: hard -"@octokit/core@npm:^6.1.3": - version: 6.1.3 - resolution: "@octokit/core@npm:6.1.3" +"@octokit/core@npm:^6.1.4": + version: 6.1.4 + resolution: "@octokit/core@npm:6.1.4" dependencies: "@octokit/auth-token": "npm:^5.0.0" "@octokit/graphql": "npm:^8.1.2" - "@octokit/request": "npm:^9.1.4" - "@octokit/request-error": "npm:^6.1.6" + "@octokit/request": "npm:^9.2.1" + "@octokit/request-error": "npm:^6.1.7" "@octokit/types": "npm:^13.6.2" before-after-hook: "npm:^3.0.2" universal-user-agent: "npm:^7.0.0" - checksum: 10/623b3586c987796133fa3675865f2055726b8c49f71d4044cb74e7248500514aa28d21c6ae58f5f830cb755b83633a81cdb36551b632f21efada7e046f1665bb + checksum: 10/e6ca903ce037a854c86da93ecf4d12315963745cc3580804cfd55ef6490b4df12de5c46a5864929d88584ba6016d415375115953d15e6c7458a5e037f9282427 languageName: node linkType: hard @@ -3534,7 +3533,7 @@ __metadata: languageName: node linkType: hard -"@octokit/plugin-paginate-rest@npm:^11.4.0": +"@octokit/plugin-paginate-rest@npm:^11.4.2": version: 11.4.2 resolution: "@octokit/plugin-paginate-rest@npm:11.4.2" dependencies: @@ -3565,20 +3564,20 @@ __metadata: languageName: node linkType: hard -"@octokit/plugin-retry@npm:7.1.3": - version: 7.1.3 - resolution: "@octokit/plugin-retry@npm:7.1.3" +"@octokit/plugin-retry@npm:7.1.4": + version: 7.1.4 + resolution: "@octokit/plugin-retry@npm:7.1.4" dependencies: - "@octokit/request-error": "npm:^6.1.6" + "@octokit/request-error": "npm:^6.1.7" "@octokit/types": "npm:^13.6.2" bottleneck: "npm:^2.15.3" peerDependencies: "@octokit/core": ">=6" - checksum: 10/05ec7cd2ddf4f1423015d1033746ef40d8c68fae811e8616bea92bcd7f2bd15ab6ff04b60235cd5785d94ae0a18f9002d437eb1974a752ab588ec2a276b8831a + checksum: 10/5b1f2681c33c3ba5e99da77b077fbb7b766780fdd48321d2b8e48b78acd6b83598b3e9915e3d49f8dfbc945f802bdeb26d94ae0fc9722c2e979debd049a0eeff languageName: node linkType: hard -"@octokit/request-error@npm:^6.1.6, @octokit/request-error@npm:^6.1.7": +"@octokit/request-error@npm:^6.1.7": version: 6.1.7 resolution: "@octokit/request-error@npm:6.1.7" dependencies: @@ -3600,24 +3599,24 @@ __metadata: languageName: node linkType: hard -"@octokit/rest@npm:21.1.0": - version: 21.1.0 - resolution: "@octokit/rest@npm:21.1.0" +"@octokit/rest@npm:21.1.1": + version: 21.1.1 + resolution: "@octokit/rest@npm:21.1.1" dependencies: - "@octokit/core": "npm:^6.1.3" - "@octokit/plugin-paginate-rest": "npm:^11.4.0" + "@octokit/core": "npm:^6.1.4" + "@octokit/plugin-paginate-rest": "npm:^11.4.2" "@octokit/plugin-request-log": "npm:^5.3.1" "@octokit/plugin-rest-endpoint-methods": "npm:^13.3.0" - checksum: 10/a8dd1dc82c333ba4beadf2217f929ea3c31bc016aea82879cdd1955a42a2121ef73766951ca3819970240fae8c2dc13d4d0e8a742b2e31be376b97b0b363b148 + checksum: 10/34a0088c19a202e64bb32bfc939411b96267cf4b38773c4483957600f9d5669381bcfe86f3078d1e03cade9d289dc95e196422eac3c1d0939aaba25a78cce9a7 languageName: node linkType: hard "@octokit/types@npm:^13.6.2, @octokit/types@npm:^13.7.0": - version: 13.7.0 - resolution: "@octokit/types@npm:13.7.0" + version: 13.8.0 + resolution: "@octokit/types@npm:13.8.0" dependencies: "@octokit/openapi-types": "npm:^23.0.1" - checksum: 10/2c5f42b727ee4479ffc8891c61c4a0a1668dd58deeee51bdedfc6ef0bb7dd11819441db7f8d30a946911b20804fccb15f6f7c927e87eccafb4e19e6665668968 + checksum: 10/7f260cd3f98887626e791cc0e71ae718b689f359ff6546ed0343364bb213c70807f71135956659470ecdf2b4a5a0c32b6437bd5a3af412883ef3a62f41e811f8 languageName: node linkType: hard @@ -4239,82 +4238,82 @@ __metadata: languageName: node linkType: hard -"@rspack/binding-darwin-arm64@npm:1.2.3": - version: 1.2.3 - resolution: "@rspack/binding-darwin-arm64@npm:1.2.3" +"@rspack/binding-darwin-arm64@npm:1.2.5": + version: 1.2.5 + resolution: "@rspack/binding-darwin-arm64@npm:1.2.5" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rspack/binding-darwin-x64@npm:1.2.3": - version: 1.2.3 - resolution: "@rspack/binding-darwin-x64@npm:1.2.3" +"@rspack/binding-darwin-x64@npm:1.2.5": + version: 1.2.5 + resolution: "@rspack/binding-darwin-x64@npm:1.2.5" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rspack/binding-linux-arm64-gnu@npm:1.2.3": - version: 1.2.3 - resolution: "@rspack/binding-linux-arm64-gnu@npm:1.2.3" +"@rspack/binding-linux-arm64-gnu@npm:1.2.5": + version: 1.2.5 + resolution: "@rspack/binding-linux-arm64-gnu@npm:1.2.5" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rspack/binding-linux-arm64-musl@npm:1.2.3": - version: 1.2.3 - resolution: "@rspack/binding-linux-arm64-musl@npm:1.2.3" +"@rspack/binding-linux-arm64-musl@npm:1.2.5": + version: 1.2.5 + resolution: "@rspack/binding-linux-arm64-musl@npm:1.2.5" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rspack/binding-linux-x64-gnu@npm:1.2.3": - version: 1.2.3 - resolution: "@rspack/binding-linux-x64-gnu@npm:1.2.3" +"@rspack/binding-linux-x64-gnu@npm:1.2.5": + version: 1.2.5 + resolution: "@rspack/binding-linux-x64-gnu@npm:1.2.5" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rspack/binding-linux-x64-musl@npm:1.2.3": - version: 1.2.3 - resolution: "@rspack/binding-linux-x64-musl@npm:1.2.3" +"@rspack/binding-linux-x64-musl@npm:1.2.5": + version: 1.2.5 + resolution: "@rspack/binding-linux-x64-musl@npm:1.2.5" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rspack/binding-win32-arm64-msvc@npm:1.2.3": - version: 1.2.3 - resolution: "@rspack/binding-win32-arm64-msvc@npm:1.2.3" +"@rspack/binding-win32-arm64-msvc@npm:1.2.5": + version: 1.2.5 + resolution: "@rspack/binding-win32-arm64-msvc@npm:1.2.5" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rspack/binding-win32-ia32-msvc@npm:1.2.3": - version: 1.2.3 - resolution: "@rspack/binding-win32-ia32-msvc@npm:1.2.3" +"@rspack/binding-win32-ia32-msvc@npm:1.2.5": + version: 1.2.5 + resolution: "@rspack/binding-win32-ia32-msvc@npm:1.2.5" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rspack/binding-win32-x64-msvc@npm:1.2.3": - version: 1.2.3 - resolution: "@rspack/binding-win32-x64-msvc@npm:1.2.3" +"@rspack/binding-win32-x64-msvc@npm:1.2.5": + version: 1.2.5 + resolution: "@rspack/binding-win32-x64-msvc@npm:1.2.5" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@rspack/binding@npm:1.2.3": - version: 1.2.3 - resolution: "@rspack/binding@npm:1.2.3" +"@rspack/binding@npm:1.2.5": + version: 1.2.5 + resolution: "@rspack/binding@npm:1.2.5" dependencies: - "@rspack/binding-darwin-arm64": "npm:1.2.3" - "@rspack/binding-darwin-x64": "npm:1.2.3" - "@rspack/binding-linux-arm64-gnu": "npm:1.2.3" - "@rspack/binding-linux-arm64-musl": "npm:1.2.3" - "@rspack/binding-linux-x64-gnu": "npm:1.2.3" - "@rspack/binding-linux-x64-musl": "npm:1.2.3" - "@rspack/binding-win32-arm64-msvc": "npm:1.2.3" - "@rspack/binding-win32-ia32-msvc": "npm:1.2.3" - "@rspack/binding-win32-x64-msvc": "npm:1.2.3" + "@rspack/binding-darwin-arm64": "npm:1.2.5" + "@rspack/binding-darwin-x64": "npm:1.2.5" + "@rspack/binding-linux-arm64-gnu": "npm:1.2.5" + "@rspack/binding-linux-arm64-musl": "npm:1.2.5" + "@rspack/binding-linux-x64-gnu": "npm:1.2.5" + "@rspack/binding-linux-x64-musl": "npm:1.2.5" + "@rspack/binding-win32-arm64-msvc": "npm:1.2.5" + "@rspack/binding-win32-ia32-msvc": "npm:1.2.5" + "@rspack/binding-win32-x64-msvc": "npm:1.2.5" dependenciesMeta: "@rspack/binding-darwin-arm64": optional: true @@ -4334,13 +4333,13 @@ __metadata: optional: true "@rspack/binding-win32-x64-msvc": optional: true - checksum: 10/0f52ea4f2d1b41f3332918dedb61fab55e4459dea90964f29199dc075c399ff2785aa625f0dbfd8efdc2e99c3ab4d8d76d16a50a271cc5d497330e49de47d11e + checksum: 10/f5d482f80e2ecd5a35656a41079021bab8b75e57c8bd048973515a51aefa763ada6b76b467bfbafc30cf10ee98c9a553217184df811c3a652ec2f0bb5cd32785 languageName: node linkType: hard -"@rspack/cli@npm:1.2.3": - version: 1.2.3 - resolution: "@rspack/cli@npm:1.2.3" +"@rspack/cli@npm:1.2.5": + version: 1.2.5 + resolution: "@rspack/cli@npm:1.2.5" dependencies: "@discoveryjs/json-ext": "npm:^0.5.7" "@rspack/dev-server": "npm:1.0.10" @@ -4349,7 +4348,7 @@ __metadata: interpret: "npm:^3.1.1" rechoir: "npm:^0.8.0" webpack-bundle-analyzer: "npm:4.6.1" - yargs: "npm:17.6.2" + yargs: "npm:17.7.2" peerDependencies: "@rspack/core": ^1.0.0-alpha || ^1.x "@rspack/tracing": ^1.x @@ -4358,16 +4357,16 @@ __metadata: optional: true bin: rspack: bin/rspack.js - checksum: 10/12de55160d62e4e7d74c428a8b8028f0fd22d314e0cf2e2f1494f2446221dc3ae00192f515cb6ac8025daf3dae9e0ed9d38272561706f712f8d9214003782cf0 + checksum: 10/7f00742b79985bd344bb78bab66192f9c8fae28d5cf7e2c73cc87a213ab1c816b48a4123c59ada252aa6ea58a156ef91c5347b25927bd45e4fd9a843bc0ef041 languageName: node linkType: hard -"@rspack/core@npm:1.2.3": - version: 1.2.3 - resolution: "@rspack/core@npm:1.2.3" +"@rspack/core@npm:1.2.5": + version: 1.2.5 + resolution: "@rspack/core@npm:1.2.5" dependencies: "@module-federation/runtime-tools": "npm:0.8.4" - "@rspack/binding": "npm:1.2.3" + "@rspack/binding": "npm:1.2.5" "@rspack/lite-tapable": "npm:1.0.1" caniuse-lite: "npm:^1.0.30001616" peerDependencies: @@ -4378,7 +4377,7 @@ __metadata: optional: true "@swc/helpers": optional: true - checksum: 10/29ba8019a78a353f0fd416135c38db25b2b7911c2caeb20a76dbf28493353254b56394cd152ca9aca193ace8b7d1d431b2056969323d1cee18fd6b7539d98fe0 + checksum: 10/9d4bade382e26d0faf11a4967c165fbc25ee861f1e2b35f01307806360ecb4f2b4d69aba66e86b063d5d7cdb181799b1e0a47ea9561efe7d6d2833c7fbffd1a1 languageName: node linkType: hard @@ -4656,14 +4655,14 @@ __metadata: linkType: hard "@types/express-serve-static-core@npm:*": - version: 5.0.2 - resolution: "@types/express-serve-static-core@npm:5.0.2" + version: 5.0.6 + resolution: "@types/express-serve-static-core@npm:5.0.6" dependencies: "@types/node": "npm:*" "@types/qs": "npm:*" "@types/range-parser": "npm:*" "@types/send": "npm:*" - checksum: 10/43b360b63da3817691030f00cb723a3fca3a6ec724260b10147e08da2a3647912f35adc402afeb493c773270fcec6396b5369899452b1c97ad54418d15287906 + checksum: 10/9dc51bdee7da9ad4792e97dd1be5b3071b5128f26d3b87a753070221bb36c8f9d16074b95a8b972acc965641e987b1e279a44675e7312ac8f3e18ec9abe93940 languageName: node linkType: hard @@ -4717,13 +4716,6 @@ __metadata: languageName: node linkType: hard -"@types/gensync@npm:^1.0.0": - version: 1.0.4 - resolution: "@types/gensync@npm:1.0.4" - checksum: 10/99c3aa0d3f1198973c7e51bea5947b815f3338ce89ce09a39ac8abb41cd844c5b95189da254ea45e50a395fe25fd215664d8ca76c5438814963597afb01f686e - languageName: node - linkType: hard - "@types/geojson@npm:*": version: 7946.0.15 resolution: "@types/geojson@npm:7946.0.15" @@ -4817,6 +4809,15 @@ __metadata: languageName: node linkType: hard +"@types/leaflet.markercluster@npm:1.5.5": + version: 1.5.5 + resolution: "@types/leaflet.markercluster@npm:1.5.5" + dependencies: + "@types/leaflet": "npm:*" + checksum: 10/17647d187ed8c9c38124005c3c45c0c7998c6359d8783e2ea162f9649b151862750c813eba2373054e90156a11a37af2b220429f937b302889b9d6e2105bf2ca + languageName: node + linkType: hard + "@types/leaflet@npm:*, @types/leaflet@npm:1.9.16": version: 1.9.16 resolution: "@types/leaflet@npm:1.9.16" @@ -5092,15 +5093,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:8.24.0": - version: 8.24.0 - resolution: "@typescript-eslint/eslint-plugin@npm:8.24.0" +"@typescript-eslint/eslint-plugin@npm:8.24.1": + version: 8.24.1 + resolution: "@typescript-eslint/eslint-plugin@npm:8.24.1" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:8.24.0" - "@typescript-eslint/type-utils": "npm:8.24.0" - "@typescript-eslint/utils": "npm:8.24.0" - "@typescript-eslint/visitor-keys": "npm:8.24.0" + "@typescript-eslint/scope-manager": "npm:8.24.1" + "@typescript-eslint/type-utils": "npm:8.24.1" + "@typescript-eslint/utils": "npm:8.24.1" + "@typescript-eslint/visitor-keys": "npm:8.24.1" graphemer: "npm:^1.4.0" ignore: "npm:^5.3.1" natural-compare: "npm:^1.4.0" @@ -5109,64 +5110,64 @@ __metadata: "@typescript-eslint/parser": ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.8.0" - checksum: 10/2b65131dab6159285cd8688ae8fe4708e87f6aede7a6bcf65deec6f506a26f04409c7320d7957f59380f5b387fff2acfaa2c8117e2cbfc1b9368002e7905f616 + checksum: 10/4c455e98d47f8dc1ea12c0dae0a849de49b0ad9aa5f9591b2ba24c07b75af0782a349d13cf6c5c375c6e8ba43d12555f932d43d31f25c8848eceb972021c12ee languageName: node linkType: hard -"@typescript-eslint/parser@npm:8.24.0": - version: 8.24.0 - resolution: "@typescript-eslint/parser@npm:8.24.0" +"@typescript-eslint/parser@npm:8.24.1": + version: 8.24.1 + resolution: "@typescript-eslint/parser@npm:8.24.1" dependencies: - "@typescript-eslint/scope-manager": "npm:8.24.0" - "@typescript-eslint/types": "npm:8.24.0" - "@typescript-eslint/typescript-estree": "npm:8.24.0" - "@typescript-eslint/visitor-keys": "npm:8.24.0" + "@typescript-eslint/scope-manager": "npm:8.24.1" + "@typescript-eslint/types": "npm:8.24.1" + "@typescript-eslint/typescript-estree": "npm:8.24.1" + "@typescript-eslint/visitor-keys": "npm:8.24.1" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.8.0" - checksum: 10/b5c66a3208c69144cd5d0b7a2c763205ef8ae88eea76067186bab0909aa9756fe015616b98a7f252a5106aa5e86baeb98f9affbdc0f5d19863a2150f2431bfe0 + checksum: 10/9a0f86b140a2c63ff8eca17f40fe315d8a5b7ab51594e2630caff845717aab1c2138edd070e710d7edb0daf685d6bba827e983e8cb076b53d03eda07307b0113 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.24.0": - version: 8.24.0 - resolution: "@typescript-eslint/scope-manager@npm:8.24.0" +"@typescript-eslint/scope-manager@npm:8.24.1": + version: 8.24.1 + resolution: "@typescript-eslint/scope-manager@npm:8.24.1" dependencies: - "@typescript-eslint/types": "npm:8.24.0" - "@typescript-eslint/visitor-keys": "npm:8.24.0" - checksum: 10/175032d4f714d68b734d7281c340e073a37d348010d308b9cccf8d63d745b8cc9515229e32dcd838acf4a85e21a4e8eff6c557c31ba45e36917e3417de32d723 + "@typescript-eslint/types": "npm:8.24.1" + "@typescript-eslint/visitor-keys": "npm:8.24.1" + checksum: 10/ab668c073c51cf801a1f5ef8578d0ae29d778d92b143cb1575bb7a867016f45ef4d044ce374fbe47606391f2d39b6963df725964e90af85bff1c435d8006b535 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.24.0": - version: 8.24.0 - resolution: "@typescript-eslint/type-utils@npm:8.24.0" +"@typescript-eslint/type-utils@npm:8.24.1": + version: 8.24.1 + resolution: "@typescript-eslint/type-utils@npm:8.24.1" dependencies: - "@typescript-eslint/typescript-estree": "npm:8.24.0" - "@typescript-eslint/utils": "npm:8.24.0" + "@typescript-eslint/typescript-estree": "npm:8.24.1" + "@typescript-eslint/utils": "npm:8.24.1" debug: "npm:^4.3.4" ts-api-utils: "npm:^2.0.1" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.8.0" - checksum: 10/a6558d0b0ab7a43826b481c103c556abbafa93c9941d75d647266dc0f55e68950f44a63842a2e7839a7448329f9b7ee88c84913084438dbac38dba5efbc1afbc + checksum: 10/7161f6218f2f1a100142c50d71d5e470459821e3715a4d6717be3ae4e1ef8aac06c6144f1010690f15c34ee9d8330526324a8133e541aa7382439f180ccb2860 languageName: node linkType: hard -"@typescript-eslint/types@npm:8.24.0": - version: 8.24.0 - resolution: "@typescript-eslint/types@npm:8.24.0" - checksum: 10/ddaaec99c191830cc29ce289678d44f7201dd06c29540750ca4802b6bd2a6dfd8cc29b46ed270dc0198f23e742540bb1e4fe618b6b44e4e76bad7f774bd3fc4a +"@typescript-eslint/types@npm:8.24.1": + version: 8.24.1 + resolution: "@typescript-eslint/types@npm:8.24.1" + checksum: 10/f3f624d7494c02a35810988388e2d5cc35ac10860e455148faba0fe332c6b8cf4be0aa0c1e0f0012813e2d6e86c17aadadfd0c7c6e73433c064755df7d81535b languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.24.0": - version: 8.24.0 - resolution: "@typescript-eslint/typescript-estree@npm:8.24.0" +"@typescript-eslint/typescript-estree@npm:8.24.1": + version: 8.24.1 + resolution: "@typescript-eslint/typescript-estree@npm:8.24.1" dependencies: - "@typescript-eslint/types": "npm:8.24.0" - "@typescript-eslint/visitor-keys": "npm:8.24.0" + "@typescript-eslint/types": "npm:8.24.1" + "@typescript-eslint/visitor-keys": "npm:8.24.1" debug: "npm:^4.3.4" fast-glob: "npm:^3.3.2" is-glob: "npm:^4.0.3" @@ -5175,160 +5176,160 @@ __metadata: ts-api-utils: "npm:^2.0.1" peerDependencies: typescript: ">=4.8.4 <5.8.0" - checksum: 10/89e451f5d2136b405d046823c93ac7065d90c7a9f084ffb324e374c769b17ee2580d3711ada1e1575331b234059148f173230e560b08efa3073f8f0c04ce1224 + checksum: 10/b0645010607d3469b85479344245ef1fd6bd24804271fb439280167ad87e9f05cdf6a2ba2ccbcdc946c339c323249a86dd1e7ce6e130eb6e73ea619795b76151 languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.24.0": - version: 8.24.0 - resolution: "@typescript-eslint/utils@npm:8.24.0" +"@typescript-eslint/utils@npm:8.24.1": + version: 8.24.1 + resolution: "@typescript-eslint/utils@npm:8.24.1" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" - "@typescript-eslint/scope-manager": "npm:8.24.0" - "@typescript-eslint/types": "npm:8.24.0" - "@typescript-eslint/typescript-estree": "npm:8.24.0" + "@typescript-eslint/scope-manager": "npm:8.24.1" + "@typescript-eslint/types": "npm:8.24.1" + "@typescript-eslint/typescript-estree": "npm:8.24.1" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.8.0" - checksum: 10/773a4085e45a94f5a64b34550e7d890b5418c69a9dcd58862410e7e0ded46e3380c8dd7d38baafaa93ef40b2a77320092bded3ca36f15b2f7ea6babeb831e590 + checksum: 10/90890afc1de2eaabf94fb80e03713b81e976d927fa998159d132a0cf17c093a1722e27be9a642c5b94104db6dedb86a15addac046853c1f608bdcef27cfb1fd1 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.24.0": - version: 8.24.0 - resolution: "@typescript-eslint/visitor-keys@npm:8.24.0" +"@typescript-eslint/visitor-keys@npm:8.24.1": + version: 8.24.1 + resolution: "@typescript-eslint/visitor-keys@npm:8.24.1" dependencies: - "@typescript-eslint/types": "npm:8.24.0" + "@typescript-eslint/types": "npm:8.24.1" eslint-visitor-keys: "npm:^4.2.0" - checksum: 10/a93bc9e587784cbc47d6849f14581aa3f15574b187a7b98597362acbca43b06c36a6dfa889b5320ced62b182abf0b9759a694489d72fc7902cdea11830a7a535 + checksum: 10/94876bd771e050dadf4af6e2bbb3819d3a14407d69a643153eb56857dae982cd3b68ba644613c433449e305ec0fd6f4aeab573ceb8f8d25fea9c55396153d6b9 languageName: node linkType: hard -"@vaadin/a11y-base@npm:~24.6.4": - version: 24.6.4 - resolution: "@vaadin/a11y-base@npm:24.6.4" +"@vaadin/a11y-base@npm:~24.6.5": + version: 24.6.5 + resolution: "@vaadin/a11y-base@npm:24.6.5" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:~24.6.4" + "@vaadin/component-base": "npm:~24.6.5" lit: "npm:^3.0.0" - checksum: 10/5d0a9261f329fda7b28c5487999774fedd0e2edb339b6f7eecf6749acbddb137569bf730e0701812c52d6e23fe65101d393c0c860931257f5cdfbbedd537c1cd + checksum: 10/7732c9a898542874ab292548abb9bcf7bf5707a3a82745018d2c83497133968f807080be70b24c5fcb749b6900f1308785a0b10314030d6b96608d9da5540332 languageName: node linkType: hard -"@vaadin/combo-box@npm:24.6.4": - version: 24.6.4 - resolution: "@vaadin/combo-box@npm:24.6.4" +"@vaadin/combo-box@npm:24.6.5": + version: 24.6.5 + resolution: "@vaadin/combo-box@npm:24.6.5" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:~24.6.4" - "@vaadin/component-base": "npm:~24.6.4" - "@vaadin/field-base": "npm:~24.6.4" - "@vaadin/input-container": "npm:~24.6.4" - "@vaadin/item": "npm:~24.6.4" - "@vaadin/lit-renderer": "npm:~24.6.4" - "@vaadin/overlay": "npm:~24.6.4" - "@vaadin/vaadin-lumo-styles": "npm:~24.6.4" - "@vaadin/vaadin-material-styles": "npm:~24.6.4" - "@vaadin/vaadin-themable-mixin": "npm:~24.6.4" + "@vaadin/a11y-base": "npm:~24.6.5" + "@vaadin/component-base": "npm:~24.6.5" + "@vaadin/field-base": "npm:~24.6.5" + "@vaadin/input-container": "npm:~24.6.5" + "@vaadin/item": "npm:~24.6.5" + "@vaadin/lit-renderer": "npm:~24.6.5" + "@vaadin/overlay": "npm:~24.6.5" + "@vaadin/vaadin-lumo-styles": "npm:~24.6.5" + "@vaadin/vaadin-material-styles": "npm:~24.6.5" + "@vaadin/vaadin-themable-mixin": "npm:~24.6.5" lit: "npm:^3.0.0" - checksum: 10/9c014ab3647a1c887e7cf2f707681c453bedcfc619580900341730b1de2588ecc139ac6dd79b614a13bd1a9fa07d8499cf8bddddffd7d4eac9f30094dca152a1 + checksum: 10/633a6aa57cc269a67bbf5df4998b9699cd7466cd5effca2da5db91be102706aab04150c9732a88798934aa5bb986b7c6afdc59d1c6c07a13f346006bfffbb7fc languageName: node linkType: hard -"@vaadin/component-base@npm:~24.6.4": - version: 24.6.4 - resolution: "@vaadin/component-base@npm:24.6.4" +"@vaadin/component-base@npm:~24.6.5": + version: 24.6.5 + resolution: "@vaadin/component-base@npm:24.6.5" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" "@vaadin/vaadin-development-mode-detector": "npm:^2.0.0" "@vaadin/vaadin-usage-statistics": "npm:^2.1.0" lit: "npm:^3.0.0" - checksum: 10/e3fe5e74b06fa9496b5a448a1328c03385ac078b25125a610fc330a05bf26b82234a0264e4d4c9b2399e207b14c6a6ba65bd908afa05073053059b4a01115995 + checksum: 10/4b901f51e6894d7032d9ce20388136428b374a72ffc58ece7bb05e54d2c412a13dee38dc74d9a1a674a56720d22037d91d441fa84b221d2df5add82d12798081 languageName: node linkType: hard -"@vaadin/field-base@npm:~24.6.4": - version: 24.6.4 - resolution: "@vaadin/field-base@npm:24.6.4" +"@vaadin/field-base@npm:~24.6.5": + version: 24.6.5 + resolution: "@vaadin/field-base@npm:24.6.5" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:~24.6.4" - "@vaadin/component-base": "npm:~24.6.4" + "@vaadin/a11y-base": "npm:~24.6.5" + "@vaadin/component-base": "npm:~24.6.5" lit: "npm:^3.0.0" - checksum: 10/ede2a08cf332e639117835dfcb2ee274b9df8c5f9af0a1611638719090cdeb29f925d11244183219c346d3bf4b6929042f8008087afe38c26ea6c6ed7d849491 + checksum: 10/8644b025a8916ffcc7bf961c0ff41af4feaf5a92ca55e90974df0ad700bbe17ad29a1bd7ccb58b078658daba175f5d9895e49b7aa41c15f598a9edac0d063ca3 languageName: node linkType: hard -"@vaadin/icon@npm:~24.6.4": - version: 24.6.4 - resolution: "@vaadin/icon@npm:24.6.4" +"@vaadin/icon@npm:~24.6.5": + version: 24.6.5 + resolution: "@vaadin/icon@npm:24.6.5" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:~24.6.4" - "@vaadin/vaadin-lumo-styles": "npm:~24.6.4" - "@vaadin/vaadin-themable-mixin": "npm:~24.6.4" + "@vaadin/component-base": "npm:~24.6.5" + "@vaadin/vaadin-lumo-styles": "npm:~24.6.5" + "@vaadin/vaadin-themable-mixin": "npm:~24.6.5" lit: "npm:^3.0.0" - checksum: 10/bf611bbb43801cba77d6cebf2eb9c7de9939b238a111f1364f183d0f96775639b4312bf2e7d866684b829e028af7dca4d55b1277b780267e104b424101270daa + checksum: 10/6b29eff3c8a79feffc15cea3eb30a181a609294eb71be5a6c8555cde32be9fc8e687473955a819900622a0a9dece4bf7ed79110c2b77eb692c4d6dc54158da4e languageName: node linkType: hard -"@vaadin/input-container@npm:~24.6.4": - version: 24.6.4 - resolution: "@vaadin/input-container@npm:24.6.4" +"@vaadin/input-container@npm:~24.6.5": + version: 24.6.5 + resolution: "@vaadin/input-container@npm:24.6.5" dependencies: "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:~24.6.4" - "@vaadin/vaadin-lumo-styles": "npm:~24.6.4" - "@vaadin/vaadin-material-styles": "npm:~24.6.4" - "@vaadin/vaadin-themable-mixin": "npm:~24.6.4" + "@vaadin/component-base": "npm:~24.6.5" + "@vaadin/vaadin-lumo-styles": "npm:~24.6.5" + "@vaadin/vaadin-material-styles": "npm:~24.6.5" + "@vaadin/vaadin-themable-mixin": "npm:~24.6.5" lit: "npm:^3.0.0" - checksum: 10/7805a861e9f0a4644586e3b106f8a71b9c888194a212453b4856465442365c880a1655f8dbbd060d9dd5705f59ca0011dda7efdf93b4d47508d58ebcea42e4f4 + checksum: 10/aa30c001145a175d89bb76786af2733b98879a7fac62afe57a522e759776dda939fd879dd57d0383869d8c0af7d540cee58ad53a831626e68f74f41a87661087 languageName: node linkType: hard -"@vaadin/item@npm:~24.6.4": - version: 24.6.4 - resolution: "@vaadin/item@npm:24.6.4" +"@vaadin/item@npm:~24.6.5": + version: 24.6.5 + resolution: "@vaadin/item@npm:24.6.5" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:~24.6.4" - "@vaadin/component-base": "npm:~24.6.4" - "@vaadin/vaadin-lumo-styles": "npm:~24.6.4" - "@vaadin/vaadin-material-styles": "npm:~24.6.4" - "@vaadin/vaadin-themable-mixin": "npm:~24.6.4" + "@vaadin/a11y-base": "npm:~24.6.5" + "@vaadin/component-base": "npm:~24.6.5" + "@vaadin/vaadin-lumo-styles": "npm:~24.6.5" + "@vaadin/vaadin-material-styles": "npm:~24.6.5" + "@vaadin/vaadin-themable-mixin": "npm:~24.6.5" lit: "npm:^3.0.0" - checksum: 10/d44f766a92aeca1a8d96dccb40fd432846c00feeb25787962a5726a5c4c44937121537e6d6b5b1cb69baba4b905faefeec2a34e5f7bd4d405508f955398d61de + checksum: 10/2dcb20b643b29426fb77072910dbe1fd0a69935369a8df5283a5c82928b7f15f5644b31a9a6a21b4a0d5e3d6ddf2d0073b5e4cb0b2cfd97092818df22861f95e languageName: node linkType: hard -"@vaadin/lit-renderer@npm:~24.6.4": - version: 24.6.4 - resolution: "@vaadin/lit-renderer@npm:24.6.4" +"@vaadin/lit-renderer@npm:~24.6.5": + version: 24.6.5 + resolution: "@vaadin/lit-renderer@npm:24.6.5" dependencies: lit: "npm:^3.0.0" - checksum: 10/ab99999f9e7d140abc62b3182ca5c4905b8ba794be84d9842fb4238d67400620dbe14e924f304cb2493e3bf7435f70fc9719b8f19c6161b32a59ead5b8bf7aa9 + checksum: 10/39a3e8efd775382bd628b15dafe9e786b5c4a3ac0a51f2ec333eee1e13fbbd566bf8e6cabfbf80cece311ccc83f26434c47e097e8a92e2b0eda2e98e6e7737f7 languageName: node linkType: hard -"@vaadin/overlay@npm:~24.6.4": - version: 24.6.4 - resolution: "@vaadin/overlay@npm:24.6.4" +"@vaadin/overlay@npm:~24.6.5": + version: 24.6.5 + resolution: "@vaadin/overlay@npm:24.6.5" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:~24.6.4" - "@vaadin/component-base": "npm:~24.6.4" - "@vaadin/vaadin-lumo-styles": "npm:~24.6.4" - "@vaadin/vaadin-material-styles": "npm:~24.6.4" - "@vaadin/vaadin-themable-mixin": "npm:~24.6.4" + "@vaadin/a11y-base": "npm:~24.6.5" + "@vaadin/component-base": "npm:~24.6.5" + "@vaadin/vaadin-lumo-styles": "npm:~24.6.5" + "@vaadin/vaadin-material-styles": "npm:~24.6.5" + "@vaadin/vaadin-themable-mixin": "npm:~24.6.5" lit: "npm:^3.0.0" - checksum: 10/c237994571696c3fb5d2ac1661befabc301a5020cbf77302eb2c00c4b1e05483c96bd1d4f202efe2a25adab47889d5e851a80373787b9904987f0edd4c045fc1 + checksum: 10/76521a5136074f834d6a25a1c0167989b17da85f931f1595b223ca65df810cf5e5d914e67da69fdec9c6567c29e51303a3965a1eb233e4ec027f627907d7128e languageName: node linkType: hard @@ -5339,36 +5340,36 @@ __metadata: languageName: node linkType: hard -"@vaadin/vaadin-lumo-styles@npm:~24.6.4": - version: 24.6.4 - resolution: "@vaadin/vaadin-lumo-styles@npm:24.6.4" +"@vaadin/vaadin-lumo-styles@npm:~24.6.5": + version: 24.6.5 + resolution: "@vaadin/vaadin-lumo-styles@npm:24.6.5" dependencies: "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:~24.6.4" - "@vaadin/icon": "npm:~24.6.4" - "@vaadin/vaadin-themable-mixin": "npm:~24.6.4" - checksum: 10/35ae45a433b864236f1bdec27111c0b25feffa16f199205c896add86d4ef1d53487cab1ef13573d60365f5b3d6cf2e0b7bcd56787a4fbe7de53d5fcfdc74e46f + "@vaadin/component-base": "npm:~24.6.5" + "@vaadin/icon": "npm:~24.6.5" + "@vaadin/vaadin-themable-mixin": "npm:~24.6.5" + checksum: 10/c6a4acce967c15f582164347a8c1919deeb28d33b7847bfeb29b988dbd51532a8ca2975315ece9fea8d7abe5cfb0da18a88164d4fb489f8d032b08f9fd1989a5 languageName: node linkType: hard -"@vaadin/vaadin-material-styles@npm:~24.6.4": - version: 24.6.4 - resolution: "@vaadin/vaadin-material-styles@npm:24.6.4" +"@vaadin/vaadin-material-styles@npm:~24.6.5": + version: 24.6.5 + resolution: "@vaadin/vaadin-material-styles@npm:24.6.5" dependencies: "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:~24.6.4" - "@vaadin/vaadin-themable-mixin": "npm:~24.6.4" - checksum: 10/6ce2c3017819ca03ade65107f664b3e305b4642c151f7012f769aefef50c467d5ae7f2ff92fb3d7ced9981bf8d881cf9b0c30aef561263b0348b7f215505ecb6 + "@vaadin/component-base": "npm:~24.6.5" + "@vaadin/vaadin-themable-mixin": "npm:~24.6.5" + checksum: 10/cb53060bf22bb74b8b297da02af61710390fc5ec810401c374856c7c26e2ec61a8b878e351925e98977230009cafd0593835c84f243d3c7a3138872c31a083a2 languageName: node linkType: hard -"@vaadin/vaadin-themable-mixin@npm:24.6.4, @vaadin/vaadin-themable-mixin@npm:~24.6.4": - version: 24.6.4 - resolution: "@vaadin/vaadin-themable-mixin@npm:24.6.4" +"@vaadin/vaadin-themable-mixin@npm:24.6.5, @vaadin/vaadin-themable-mixin@npm:~24.6.5": + version: 24.6.5 + resolution: "@vaadin/vaadin-themable-mixin@npm:24.6.5" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" lit: "npm:^3.0.0" - checksum: 10/05a8bee2b254dd5c30829ebfc17a04f86e53b0fac163b7e2b53a677b8487cac859afa905a1624dc7a25e15f88bb81572de601aaa4b921fc4b99dd27df7d4a249 + checksum: 10/9751426b32620e21030beea630d204d94bea6d1a46172428a7ddae9d12d04542611412e20997464eb0b3a3452d665f7c83d625e175020c9740579e69ac30ca79 languageName: node linkType: hard @@ -5489,9 +5490,9 @@ __metadata: languageName: node linkType: hard -"@vitest/coverage-v8@npm:3.0.5": - version: 3.0.5 - resolution: "@vitest/coverage-v8@npm:3.0.5" +"@vitest/coverage-v8@npm:3.0.6": + version: 3.0.6 + resolution: "@vitest/coverage-v8@npm:3.0.6" dependencies: "@ampproject/remapping": "npm:^2.3.0" "@bcoe/v8-coverage": "npm:^1.0.2" @@ -5506,32 +5507,32 @@ __metadata: test-exclude: "npm:^7.0.1" tinyrainbow: "npm:^2.0.0" peerDependencies: - "@vitest/browser": 3.0.5 - vitest: 3.0.5 + "@vitest/browser": 3.0.6 + vitest: 3.0.6 peerDependenciesMeta: "@vitest/browser": optional: true - checksum: 10/aa1aa681b4ee86d5ecf53390c56e24e7b61ea7cb47a178630c224de4bdd7eaf8a30b184299741b6e358d214670759905e2f90fb08d0745abdc3974992dfa137f + checksum: 10/ed5c852e0d6e9e632f298b879bd2e69e89f2afd8b9c12cf5436a65a0e61da82f69096beacb8298238b6efe935783b08672ee7e2dd5e9a6f6a40094340d5df1b9 languageName: node linkType: hard -"@vitest/expect@npm:3.0.5": - version: 3.0.5 - resolution: "@vitest/expect@npm:3.0.5" +"@vitest/expect@npm:3.0.6": + version: 3.0.6 + resolution: "@vitest/expect@npm:3.0.6" dependencies: - "@vitest/spy": "npm:3.0.5" - "@vitest/utils": "npm:3.0.5" - chai: "npm:^5.1.2" + "@vitest/spy": "npm:3.0.6" + "@vitest/utils": "npm:3.0.6" + chai: "npm:^5.2.0" tinyrainbow: "npm:^2.0.0" - checksum: 10/e9dfaed51e3a2952306fa621b4fe6c4323b367c8b731fc57d661d971628df89d1bfa163be79e4de3004d6e2e32c99b496efb8d065db6cf41d6be01dc2b833f8d + checksum: 10/f14b44d2e152bda5133c971d0b8132b46ac134aca2ef1816bfb4d34eec396c031803504371365d74023906a7b350296489ad252671760e1276625a52ae185c13 languageName: node linkType: hard -"@vitest/mocker@npm:3.0.5": - version: 3.0.5 - resolution: "@vitest/mocker@npm:3.0.5" +"@vitest/mocker@npm:3.0.6": + version: 3.0.6 + resolution: "@vitest/mocker@npm:3.0.6" dependencies: - "@vitest/spy": "npm:3.0.5" + "@vitest/spy": "npm:3.0.6" estree-walker: "npm:^3.0.3" magic-string: "npm:^0.30.17" peerDependencies: @@ -5542,57 +5543,57 @@ __metadata: optional: true vite: optional: true - checksum: 10/84f3f8bbefdde91467d4bb6e5ea62227fdd86dce5567d0a2a04329033e1ed6cffe140d5b1cd58d323792d4116ba67562539d22c80910d60310eede940c94eb8b + checksum: 10/c7d0c626075dd3cf39aa403304bf67d2720fd1064b61fa79b3b7da6bb91266fcd0a07ed335b9251dd6e3a8c63d82d5139f2f629febe98cb59d24675f44b7fd5d languageName: node linkType: hard -"@vitest/pretty-format@npm:3.0.5, @vitest/pretty-format@npm:^3.0.5": - version: 3.0.5 - resolution: "@vitest/pretty-format@npm:3.0.5" +"@vitest/pretty-format@npm:3.0.6, @vitest/pretty-format@npm:^3.0.6": + version: 3.0.6 + resolution: "@vitest/pretty-format@npm:3.0.6" dependencies: tinyrainbow: "npm:^2.0.0" - checksum: 10/1ffbee16e9aa2cd7862bc6b83c30b7b53031d29ddae0302d09e6b1f6bfa0e4338e5c74a2dfaeed1bab317aff300c4fd309004dbaa69baf9ebe71f6806b132e96 + checksum: 10/b0fb4b55be6b684907c9e6794cdf66483efa79523c24a384243e1afefd780e52f62b97c7d4e9bcd55017ac2b9928c6707502e58d7c238da20823d04fca24e2fe languageName: node linkType: hard -"@vitest/runner@npm:3.0.5": - version: 3.0.5 - resolution: "@vitest/runner@npm:3.0.5" +"@vitest/runner@npm:3.0.6": + version: 3.0.6 + resolution: "@vitest/runner@npm:3.0.6" dependencies: - "@vitest/utils": "npm:3.0.5" - pathe: "npm:^2.0.2" - checksum: 10/7aedf5d445aec3da83790cc94e135f64a1c407e437276694ca5a0567db055f49481b2622ab24faabb4482a1829d18dbc5cae31738b5a015669651cda8e0e7238 + "@vitest/utils": "npm:3.0.6" + pathe: "npm:^2.0.3" + checksum: 10/7fa15d58b19bc407e8c2d8edf234e572b04ce37195bda740f49044ef36fdfe1bfeed71f9bdd61cabc1fdf87a280feb664475f4d96051240d5f861f9b1c5954bd languageName: node linkType: hard -"@vitest/snapshot@npm:3.0.5": - version: 3.0.5 - resolution: "@vitest/snapshot@npm:3.0.5" +"@vitest/snapshot@npm:3.0.6": + version: 3.0.6 + resolution: "@vitest/snapshot@npm:3.0.6" dependencies: - "@vitest/pretty-format": "npm:3.0.5" + "@vitest/pretty-format": "npm:3.0.6" magic-string: "npm:^0.30.17" - pathe: "npm:^2.0.2" - checksum: 10/3c6a3165556dc4a3fc50c9532dc047b5bf57df1bbad657ca7e34ca65e9aeb61740a0eaebe9eb6200a30d92f457a402ce3d22b21700a1763a5ec4bddf81733709 + pathe: "npm:^2.0.3" + checksum: 10/6ab4cef960b32baa05ae6eb7c67bb963272f6b19b7b1969f30930285397a54c3c67b37b5736c14a747123a5942fe9657382284a8b725f68f775077d155fabaaf languageName: node linkType: hard -"@vitest/spy@npm:3.0.5": - version: 3.0.5 - resolution: "@vitest/spy@npm:3.0.5" +"@vitest/spy@npm:3.0.6": + version: 3.0.6 + resolution: "@vitest/spy@npm:3.0.6" dependencies: tinyspy: "npm:^3.0.2" - checksum: 10/ed85319cd03f3f35121e84ce31721316daf94a7c01d493dff746ff5469d12e40b218cc728d57c5a71612c5a3882e8e66d9cefe82b82c2044d5f257954ec7e9d8 + checksum: 10/d30b7415c5aff38a0cc8cde6f73629e11e293fe68cf078083f513a22c71b73a668d634daf7717b75740d37afbdd73bb041fd3550cc8c7abe12f599bff029bb45 languageName: node linkType: hard -"@vitest/utils@npm:3.0.5": - version: 3.0.5 - resolution: "@vitest/utils@npm:3.0.5" +"@vitest/utils@npm:3.0.6": + version: 3.0.6 + resolution: "@vitest/utils@npm:3.0.6" dependencies: - "@vitest/pretty-format": "npm:3.0.5" - loupe: "npm:^3.1.2" + "@vitest/pretty-format": "npm:3.0.6" + loupe: "npm:^3.1.3" tinyrainbow: "npm:^2.0.0" - checksum: 10/4e85a7514592df63870eb4ec27c434034cc91c9e63c052bcb2304c4cc2f4fbb49350099280480313e93526247d020b42bea52436cf7f93fee0bd98cfac51a644 + checksum: 10/67f903ec931a1767fcd8421697fde6751cebbbc9491527c2f7df202366ee269856b2befa694f5f71f912af34b9df13a51783e502251f68142304140d68532562 languageName: node linkType: hard @@ -5957,7 +5958,7 @@ __metadata: languageName: node linkType: hard -"array-buffer-byte-length@npm:^1.0.1": +"array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": version: 1.0.2 resolution: "array-buffer-byte-length@npm:1.0.2" dependencies: @@ -6577,16 +6578,16 @@ __metadata: languageName: node linkType: hard -"chai@npm:^5.1.2": - version: 5.1.2 - resolution: "chai@npm:5.1.2" +"chai@npm:^5.2.0": + version: 5.2.0 + resolution: "chai@npm:5.2.0" dependencies: assertion-error: "npm:^2.0.1" check-error: "npm:^2.1.1" deep-eql: "npm:^5.0.1" loupe: "npm:^3.1.0" pathval: "npm:^2.0.0" - checksum: 10/e8c2bbc83cb5a2f87130d93056d4cfbbe04106e12aa798b504816dbe3fa538a9f68541b472e56cbf0f54558b501d7e31867d74b8218abcd5a8cc8ba536fba46c + checksum: 10/2ce03671c159c6a567bf1912756daabdbb7c075f3c0078f1b59d61da8d276936367ee696dfe093b49e1479d9ba93a6074c8e55d49791dddd8061728cdcad249e languageName: node linkType: hard @@ -7160,7 +7161,7 @@ __metadata: languageName: node linkType: hard -"data-view-buffer@npm:^1.0.1": +"data-view-buffer@npm:^1.0.2": version: 1.0.2 resolution: "data-view-buffer@npm:1.0.2" dependencies: @@ -7171,7 +7172,7 @@ __metadata: languageName: node linkType: hard -"data-view-byte-length@npm:^1.0.1": +"data-view-byte-length@npm:^1.0.2": version: 1.0.2 resolution: "data-view-byte-length@npm:1.0.2" dependencies: @@ -7182,7 +7183,7 @@ __metadata: languageName: node linkType: hard -"data-view-byte-offset@npm:^1.0.0": +"data-view-byte-offset@npm:^1.0.1": version: 1.0.1 resolution: "data-view-byte-offset@npm:1.0.1" dependencies: @@ -7778,25 +7779,26 @@ __metadata: linkType: hard "es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.5, es-abstract@npm:^1.23.6": - version: 1.23.6 - resolution: "es-abstract@npm:1.23.6" + version: 1.23.9 + resolution: "es-abstract@npm:1.23.9" dependencies: - array-buffer-byte-length: "npm:^1.0.1" + array-buffer-byte-length: "npm:^1.0.2" arraybuffer.prototype.slice: "npm:^1.0.4" available-typed-arrays: "npm:^1.0.7" call-bind: "npm:^1.0.8" call-bound: "npm:^1.0.3" - data-view-buffer: "npm:^1.0.1" - data-view-byte-length: "npm:^1.0.1" - data-view-byte-offset: "npm:^1.0.0" + data-view-buffer: "npm:^1.0.2" + data-view-byte-length: "npm:^1.0.2" + data-view-byte-offset: "npm:^1.0.1" es-define-property: "npm:^1.0.1" es-errors: "npm:^1.3.0" es-object-atoms: "npm:^1.0.0" - es-set-tostringtag: "npm:^2.0.3" + es-set-tostringtag: "npm:^2.1.0" es-to-primitive: "npm:^1.3.0" - function.prototype.name: "npm:^1.1.7" - get-intrinsic: "npm:^1.2.6" - get-symbol-description: "npm:^1.0.2" + function.prototype.name: "npm:^1.1.8" + get-intrinsic: "npm:^1.2.7" + get-proto: "npm:^1.0.0" + get-symbol-description: "npm:^1.1.0" globalthis: "npm:^1.0.4" gopd: "npm:^1.2.0" has-property-descriptors: "npm:^1.0.2" @@ -7804,32 +7806,34 @@ __metadata: has-symbols: "npm:^1.1.0" hasown: "npm:^2.0.2" internal-slot: "npm:^1.1.0" - is-array-buffer: "npm:^3.0.4" + is-array-buffer: "npm:^3.0.5" is-callable: "npm:^1.2.7" is-data-view: "npm:^1.0.2" - is-negative-zero: "npm:^2.0.3" is-regex: "npm:^1.2.1" - is-shared-array-buffer: "npm:^1.0.3" + is-shared-array-buffer: "npm:^1.0.4" is-string: "npm:^1.1.1" - is-typed-array: "npm:^1.1.13" + is-typed-array: "npm:^1.1.15" is-weakref: "npm:^1.1.0" - math-intrinsics: "npm:^1.0.0" + math-intrinsics: "npm:^1.1.0" object-inspect: "npm:^1.13.3" object-keys: "npm:^1.1.1" - object.assign: "npm:^4.1.5" + object.assign: "npm:^4.1.7" + own-keys: "npm:^1.0.1" regexp.prototype.flags: "npm:^1.5.3" safe-array-concat: "npm:^1.1.3" + safe-push-apply: "npm:^1.0.0" safe-regex-test: "npm:^1.1.0" + set-proto: "npm:^1.0.0" string.prototype.trim: "npm:^1.2.10" string.prototype.trimend: "npm:^1.0.9" string.prototype.trimstart: "npm:^1.0.8" - typed-array-buffer: "npm:^1.0.2" - typed-array-byte-length: "npm:^1.0.1" - typed-array-byte-offset: "npm:^1.0.3" + typed-array-buffer: "npm:^1.0.3" + typed-array-byte-length: "npm:^1.0.3" + typed-array-byte-offset: "npm:^1.0.4" typed-array-length: "npm:^1.0.7" - unbox-primitive: "npm:^1.0.2" - which-typed-array: "npm:^1.1.16" - checksum: 10/a8987ea76445505bedbdee09251ca5cb9bdbb1578df991eb69b888bd721448d17111ba847b560f6e7c8974989b885830663fef07b0bdf4ddf8b61ed7ecd34d58 + unbox-primitive: "npm:^1.1.0" + which-typed-array: "npm:^1.1.18" + checksum: 10/31a321966d760d88fc2ed984104841b42f4f24fc322b246002b9be0af162e03803ee41fcc3cf8be89e07a27ba3033168f877dd983703cb81422ffe5322a27582 languageName: node linkType: hard @@ -7863,14 +7867,15 @@ __metadata: languageName: node linkType: hard -"es-set-tostringtag@npm:^2.0.3": - version: 2.0.3 - resolution: "es-set-tostringtag@npm:2.0.3" +"es-set-tostringtag@npm:^2.1.0": + version: 2.1.0 + resolution: "es-set-tostringtag@npm:2.1.0" dependencies: - get-intrinsic: "npm:^1.2.4" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.6" has-tostringtag: "npm:^1.0.2" - hasown: "npm:^2.0.1" - checksum: 10/7227fa48a41c0ce83e0377b11130d324ac797390688135b8da5c28994c0165be8b252e15cd1de41e1325e5a5412511586960213e88f9ab4a5e7d028895db5129 + hasown: "npm:^2.0.2" + checksum: 10/86814bf8afbcd8966653f731415888019d4bc4aca6b6c354132a7a75bb87566751e320369654a101d23a91c87a85c79b178bcf40332839bd347aff437c4fb65f languageName: node linkType: hard @@ -8154,15 +8159,15 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-wc@npm:2.2.0": - version: 2.2.0 - resolution: "eslint-plugin-wc@npm:2.2.0" +"eslint-plugin-wc@npm:2.2.1": + version: 2.2.1 + resolution: "eslint-plugin-wc@npm:2.2.1" dependencies: is-valid-element-name: "npm:^1.0.0" js-levenshtein-esm: "npm:^1.2.0" peerDependencies: eslint: ">=8.40.0" - checksum: 10/12ffc021f0e5a42d97b782e92f249fad923d7c1af4c2603384482c8371e72bbd6084891b14928cae71084e0a8cef9ad914acf746aad9cdaca6cd6212e499513d + checksum: 10/7861074dbf057be818dddfeaa3442e538696bdaeb3971038c9216337be071a67f775400d394d06b1166180b77171756c6037f92eb4d6ea5ae749f128f8d9654d languageName: node linkType: hard @@ -8503,15 +8508,15 @@ __metadata: linkType: hard "fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.2, fast-glob@npm:^3.3.2": - version: 3.3.2 - resolution: "fast-glob@npm:3.3.2" + version: 3.3.3 + resolution: "fast-glob@npm:3.3.3" dependencies: "@nodelib/fs.stat": "npm:^2.0.2" "@nodelib/fs.walk": "npm:^1.2.3" glob-parent: "npm:^5.1.2" merge2: "npm:^1.3.0" - micromatch: "npm:^4.0.4" - checksum: 10/222512e9315a0efca1276af9adb2127f02105d7288fa746145bf45e2716383fb79eb983c89601a72a399a56b7c18d38ce70457c5466218c5f13fad957cee16df + micromatch: "npm:^4.0.8" + checksum: 10/dcc6432b269762dd47381d8b8358bf964d8f4f60286ac6aa41c01ade70bda459ff2001b516690b96d5365f68a49242966112b5d5cc9cd82395fa8f9d017c90ad languageName: node linkType: hard @@ -8886,7 +8891,7 @@ __metadata: languageName: node linkType: hard -"function.prototype.name@npm:^1.1.6, function.prototype.name@npm:^1.1.7": +"function.prototype.name@npm:^1.1.6, function.prototype.name@npm:^1.1.8": version: 1.1.8 resolution: "function.prototype.name@npm:1.1.8" dependencies: @@ -8935,21 +8940,21 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6": - version: 1.2.6 - resolution: "get-intrinsic@npm:1.2.6" +"get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.2.7": + version: 1.2.7 + resolution: "get-intrinsic@npm:1.2.7" dependencies: call-bind-apply-helpers: "npm:^1.0.1" - dunder-proto: "npm:^1.0.0" es-define-property: "npm:^1.0.1" es-errors: "npm:^1.3.0" es-object-atoms: "npm:^1.0.0" function-bind: "npm:^1.1.2" + get-proto: "npm:^1.0.0" gopd: "npm:^1.2.0" has-symbols: "npm:^1.1.0" hasown: "npm:^2.0.2" - math-intrinsics: "npm:^1.0.0" - checksum: 10/a1ffae6d7893a6fa0f4d1472adbc85095edd6b3b0943ead97c3738539cecb19d422ff4d48009eed8c3c27ad678c2b1e38a83b1a1e96b691d13ed8ecefca1068d + math-intrinsics: "npm:^1.1.0" + checksum: 10/4f7149c9a826723f94c6d49f70bcb3df1d3f9213994fab3668f12f09fa72074681460fb29ebb6f135556ec6372992d63802386098791a8f09cfa6f27090fa67b languageName: node linkType: hard @@ -8967,6 +8972,16 @@ __metadata: languageName: node linkType: hard +"get-proto@npm:^1.0.0": + version: 1.0.1 + resolution: "get-proto@npm:1.0.1" + dependencies: + dunder-proto: "npm:^1.0.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10/4fc96afdb58ced9a67558698b91433e6b037aaa6f1493af77498d7c85b141382cf223c0e5946f334fb328ee85dfe6edd06d218eaf09556f4bc4ec6005d7f5f7b + languageName: node + linkType: hard + "get-stream@npm:^6.0.0": version: 6.0.1 resolution: "get-stream@npm:6.0.1" @@ -8981,7 +8996,7 @@ __metadata: languageName: node linkType: hard -"get-symbol-description@npm:^1.0.2": +"get-symbol-description@npm:^1.1.0": version: 1.1.0 resolution: "get-symbol-description@npm:1.1.0" dependencies: @@ -9116,10 +9131,10 @@ __metadata: languageName: node linkType: hard -"globals@npm:15.15.0": - version: 15.15.0 - resolution: "globals@npm:15.15.0" - checksum: 10/7f561c87b2fd381b27fc2db7df8a4ea7a9bb378667b8a7193e61fd2ca3a876479174e2a303a74345fbea6e1242e16db48915c1fd3bf35adcf4060a795b425e18 +"globals@npm:16.0.0": + version: 16.0.0 + resolution: "globals@npm:16.0.0" + checksum: 10/aa05d569af9c763d9982e6885f3ac6d21c84cd54c9a12eeace55b3334d0631128f189902d34ae2a924694311f92d700dbd3e8e62e8a9e1094a882f9f8897149a languageName: node linkType: hard @@ -9147,6 +9162,13 @@ __metadata: languageName: node linkType: hard +"globrex@npm:^0.1.2": + version: 0.1.2 + resolution: "globrex@npm:0.1.2" + checksum: 10/81ce62ee6f800d823d6b7da7687f841676d60ee8f51f934ddd862e4057316d26665c4edc0358d4340a923ac00a514f8b67c787e28fe693aae16350f4e60d55e9 + languageName: node + linkType: hard + "glogg@npm:^2.2.0": version: 2.2.0 resolution: "glogg@npm:2.2.0" @@ -9345,7 +9367,7 @@ __metadata: languageName: node linkType: hard -"hasown@npm:^2.0.0, hasown@npm:^2.0.1, hasown@npm:^2.0.2": +"hasown@npm:^2.0.0, hasown@npm:^2.0.2": version: 2.0.2 resolution: "hasown@npm:2.0.2" dependencies: @@ -9381,13 +9403,13 @@ __metadata: version: 0.0.0-use.local resolution: "home-assistant-frontend@workspace:." dependencies: - "@babel/core": "npm:7.26.8" + "@babel/core": "npm:7.26.9" "@babel/helper-define-polyfill-provider": "npm:0.6.3" "@babel/plugin-proposal-decorators": "npm:7.25.9" - "@babel/plugin-transform-runtime": "npm:7.26.8" - "@babel/preset-env": "npm:7.26.8" + "@babel/plugin-transform-runtime": "npm:7.26.9" + "@babel/preset-env": "npm:7.26.9" "@babel/preset-typescript": "npm:7.26.0" - "@babel/runtime": "npm:7.26.7" + "@babel/runtime": "npm:7.26.9" "@braintree/sanitize-url": "npm:7.1.1" "@bundle-stats/plugin-webpack-filter": "npm:4.18.2" "@codemirror/autocomplete": "npm:6.18.6" @@ -9396,7 +9418,7 @@ __metadata: "@codemirror/legacy-modes": "npm:6.4.3" "@codemirror/search": "npm:6.5.9" "@codemirror/state": "npm:6.5.2" - "@codemirror/view": "npm:6.36.2" + "@codemirror/view": "npm:6.36.3" "@egjs/hammerjs": "npm:2.0.17" "@formatjs/intl-datetimeformat": "npm:6.17.3" "@formatjs/intl-displaynames": "npm:6.8.10" @@ -9415,10 +9437,10 @@ __metadata: "@fullcalendar/timegrid": "npm:6.1.15" "@lezer/highlight": "npm:1.2.1" "@lit-labs/context": "npm:0.4.1" - "@lit-labs/motion": "npm:1.0.7" - "@lit-labs/observers": "npm:2.0.4" - "@lit-labs/virtualizer": "npm:2.0.15" - "@lokalise/node-api": "npm:13.1.0" + "@lit-labs/motion": "npm:1.0.8" + "@lit-labs/observers": "npm:2.0.5" + "@lit-labs/virtualizer": "npm:2.1.0" + "@lokalise/node-api": "npm:13.2.0" "@lrnwebcomponents/simple-tooltip": "npm:8.0.2" "@material/chips": "npm:=14.0.0-canary.53b3cad2f.0" "@material/data-table": "npm:=14.0.0-canary.53b3cad2f.0" @@ -9449,16 +9471,16 @@ __metadata: "@mdi/js": "npm:7.4.47" "@mdi/svg": "npm:7.4.47" "@octokit/auth-oauth-device": "npm:7.1.3" - "@octokit/plugin-retry": "npm:7.1.3" - "@octokit/rest": "npm:21.1.0" + "@octokit/plugin-retry": "npm:7.1.4" + "@octokit/rest": "npm:21.1.1" "@polymer/paper-item": "npm:3.0.1" "@polymer/paper-listbox": "npm:3.0.1" "@polymer/paper-tabs": "npm:3.1.0" "@polymer/polymer": "npm:3.5.2" "@replit/codemirror-indentation-markers": "npm:6.5.3" "@rsdoctor/rspack-plugin": "npm:0.4.13" - "@rspack/cli": "npm:1.2.3" - "@rspack/core": "npm:1.2.3" + "@rspack/cli": "npm:1.2.5" + "@rspack/core": "npm:1.2.5" "@shoelace-style/shoelace": "npm:2.20.0" "@thomasloven/round-slider": "npm:0.6.0" "@types/babel__plugin-transform-runtime": "npm:7.9.5" @@ -9470,6 +9492,7 @@ __metadata: "@types/js-yaml": "npm:4.0.9" "@types/leaflet": "npm:1.9.16" "@types/leaflet-draw": "npm:1.0.11" + "@types/leaflet.markercluster": "npm:1.5.5" "@types/lodash.merge": "npm:4.6.9" "@types/luxon": "npm:3.4.2" "@types/mocha": "npm:10.0.10" @@ -9478,12 +9501,10 @@ __metadata: "@types/tar": "npm:6.1.13" "@types/ua-parser-js": "npm:0.7.39" "@types/webspeechapi": "npm:0.0.29" - "@typescript-eslint/eslint-plugin": "npm:8.24.0" - "@typescript-eslint/parser": "npm:8.24.0" - "@vaadin/combo-box": "npm:24.6.4" - "@vaadin/vaadin-themable-mixin": "npm:24.6.4" + "@vaadin/combo-box": "npm:24.6.5" + "@vaadin/vaadin-themable-mixin": "npm:24.6.5" "@vibrant/color": "npm:4.0.0" - "@vitest/coverage-v8": "npm:3.0.5" + "@vitest/coverage-v8": "npm:3.0.6" "@vue/web-component-wrapper": "npm:1.3.0" "@webcomponents/scoped-custom-element-registry": "npm:0.0.9" "@webcomponents/webcomponentsjs": "npm:2.8.0" @@ -9512,7 +9533,7 @@ __metadata: eslint-plugin-lit: "npm:1.15.0" eslint-plugin-lit-a11y: "npm:4.1.4" eslint-plugin-unused-imports: "npm:4.1.4" - eslint-plugin-wc: "npm:2.2.0" + eslint-plugin-wc: "npm:2.2.1" fancy-log: "npm:2.0.0" fs-extra: "npm:11.3.0" fuse.js: "npm:7.1.0" @@ -9534,6 +9555,7 @@ __metadata: jszip: "npm:3.10.1" leaflet: "npm:1.9.4" leaflet-draw: "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch" + leaflet.markercluster: "npm:1.5.3" lint-staged: "npm:15.4.3" lit: "npm:2.8.0" lit-analyzer: "npm:2.0.3" @@ -9566,10 +9588,12 @@ __metadata: tsparticles-engine: "npm:2.12.0" tsparticles-preset-links: "npm:2.12.0" typescript: "npm:5.7.3" + typescript-eslint: "npm:8.24.1" ua-parser-js: "npm:2.0.2" vis-data: "npm:7.1.9" vis-network: "npm:9.1.9" - vitest: "npm:3.0.5" + vite-tsconfig-paths: "npm:5.1.4" + vitest: "npm:3.0.6" vue: "npm:2.7.16" vue2-daterange-picker: "npm:0.6.8" webpack-stats-plugin: "npm:1.1.3" @@ -10187,13 +10211,6 @@ __metadata: languageName: node linkType: hard -"is-negative-zero@npm:^2.0.3": - version: 2.0.3 - resolution: "is-negative-zero@npm:2.0.3" - checksum: 10/8fe5cffd8d4fb2ec7b49d657e1691889778d037494c6f40f4d1a524cadd658b4b53ad7b6b73a59bcb4b143ae9a3d15829af864b2c0f9d65ac1e678c4c80f17e5 - languageName: node - linkType: hard - "is-network-error@npm:^1.0.0": version: 1.1.0 resolution: "is-network-error@npm:1.1.0" @@ -10311,7 +10328,7 @@ __metadata: languageName: node linkType: hard -"is-shared-array-buffer@npm:^1.0.3": +"is-shared-array-buffer@npm:^1.0.4": version: 1.0.4 resolution: "is-shared-array-buffer@npm:1.0.4" dependencies: @@ -10838,6 +10855,15 @@ __metadata: languageName: node linkType: hard +"leaflet.markercluster@npm:1.5.3": + version: 1.5.3 + resolution: "leaflet.markercluster@npm:1.5.3" + peerDependencies: + leaflet: ^1.3.1 + checksum: 10/28dc441de7012b19628144407bde89576f758dbea31ecb86e3412d1fb3a46723fb47d2ba45d9b858c2e65592479a127474fb1cbf7ff33b3023c4d14f851e5fe4 + languageName: node + linkType: hard + "leaflet@npm:1.9.4": version: 1.9.4 resolution: "leaflet@npm:1.9.4" @@ -11110,10 +11136,10 @@ __metadata: languageName: node linkType: hard -"loupe@npm:^3.1.0, loupe@npm:^3.1.2": - version: 3.1.2 - resolution: "loupe@npm:3.1.2" - checksum: 10/8f5734e53fb64cd914aa7d986e01b6d4c2e3c6c56dcbd5428d71c2703f0ab46b5ab9f9eeaaf2b485e8a1c43f865bdd16ec08ae1a661c8f55acdbd9f4d59c607a +"loupe@npm:^3.1.0, loupe@npm:^3.1.3": + version: 3.1.3 + resolution: "loupe@npm:3.1.3" + checksum: 10/9e98c34daf0eba48ccc603595e51f2ae002110982d84879cf78c51de2c632f0c571dfe82ce4210af60c32203d06b443465c269bda925076fe6d9b612cc65c321 languageName: node linkType: hard @@ -11236,7 +11262,7 @@ __metadata: languageName: node linkType: hard -"math-intrinsics@npm:^1.0.0": +"math-intrinsics@npm:^1.1.0": version: 1.1.0 resolution: "math-intrinsics@npm:1.1.0" checksum: 10/11df2eda46d092a6035479632e1ec865b8134bdfc4bd9e571a656f4191525404f13a283a515938c3a8de934dbfd9c09674d9da9fa831e6eb7e22b50b197d2edd @@ -11798,7 +11824,7 @@ __metadata: languageName: node linkType: hard -"object.assign@npm:^4.1.2, object.assign@npm:^4.1.5": +"object.assign@npm:^4.1.2, object.assign@npm:^4.1.7": version: 4.1.7 resolution: "object.assign@npm:4.1.7" dependencies: @@ -12000,6 +12026,17 @@ __metadata: languageName: node linkType: hard +"own-keys@npm:^1.0.1": + version: 1.0.1 + resolution: "own-keys@npm:1.0.1" + dependencies: + get-intrinsic: "npm:^1.2.6" + object-keys: "npm:^1.1.1" + safe-push-apply: "npm:^1.0.0" + checksum: 10/ab4bb3b8636908554fc19bf899e225444195092864cb61503a0d048fdaf662b04be2605b636a4ffeaf6e8811f6fcfa8cbb210ec964c0eb1a41eb853e1d5d2f41 + languageName: node + linkType: hard + "p-limit@npm:^2.2.0": version: 2.3.0 resolution: "p-limit@npm:2.3.0" @@ -12323,10 +12360,10 @@ __metadata: languageName: node linkType: hard -"pathe@npm:^2.0.2": - version: 2.0.2 - resolution: "pathe@npm:2.0.2" - checksum: 10/027dd246720ec6d3b5567e2b0201f1a815b6a69f2912a4dcafed59620afc729af15b4aff4bc780504c88d11dfb081c051e37327b928a093e714c3e09bf35aff3 +"pathe@npm:^2.0.3": + version: 2.0.3 + resolution: "pathe@npm:2.0.3" + checksum: 10/01e9a69928f39087d96e1751ce7d6d50da8c39abf9a12e0ac2389c42c83bc76f78c45a475bd9026a02e6a6f79be63acc75667df855862fe567d99a00a540d23d languageName: node linkType: hard @@ -12453,13 +12490,13 @@ __metadata: linkType: hard "postcss@npm:^8.4.14, postcss@npm:^8.4.49": - version: 8.5.1 - resolution: "postcss@npm:8.5.1" + version: 8.5.2 + resolution: "postcss@npm:8.5.2" dependencies: nanoid: "npm:^3.3.8" picocolors: "npm:^1.1.1" source-map-js: "npm:^1.2.1" - checksum: 10/1fbd28753143f7f03e4604813639918182b15343c7ad0f4e72f3875fc2cc0b8494c887f55dc05008fad5fbf1e1e908ce2edbbce364a91f84dcefb71edf7cd31d + checksum: 10/e08c2be3cf461cc63cf4c8e97bb3d5185e196ee0a9b75879cf130590f32bc38c7829c6c4e260158e214fb68a828a95bdac84c8f17fefba993d3ced686643c3e2 languageName: node linkType: hard @@ -13208,6 +13245,16 @@ __metadata: languageName: node linkType: hard +"safe-push-apply@npm:^1.0.0": + version: 1.0.0 + resolution: "safe-push-apply@npm:1.0.0" + dependencies: + es-errors: "npm:^1.3.0" + isarray: "npm:^2.0.5" + checksum: 10/2bd4e53b6694f7134b9cf93631480e7fafc8637165f0ee91d5a4af5e7f33d37de9562d1af5021178dd4217d0230cde8d6530fa28cfa1ebff9a431bf8fff124b4 + languageName: node + linkType: hard + "safe-regex-test@npm:^1.1.0": version: 1.1.0 resolution: "safe-regex-test@npm:1.1.0" @@ -13426,6 +13473,17 @@ __metadata: languageName: node linkType: hard +"set-proto@npm:^1.0.0": + version: 1.0.0 + resolution: "set-proto@npm:1.0.0" + dependencies: + dunder-proto: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" + checksum: 10/b87f8187bca595ddc3c0721ece4635015fd9d7cb294e6dd2e394ce5186a71bbfa4dc8a35010958c65e43ad83cde09642660e61a952883c24fd6b45ead15f045c + languageName: node + linkType: hard + "setimmediate@npm:^1.0.5": version: 1.0.5 resolution: "setimmediate@npm:1.0.5" @@ -14540,6 +14598,20 @@ __metadata: languageName: node linkType: hard +"tsconfck@npm:^3.0.3": + version: 3.1.5 + resolution: "tsconfck@npm:3.1.5" + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + bin: + tsconfck: bin/tsconfck.js + checksum: 10/46b68f0fcec7da045490e427400c2a7fea67bdb6dae871257e8d2ec48e9dc99674214df86ff51c6d01ceb68ba9d7d806d3b69de432efa3c76b5fba160c252857 + languageName: node + linkType: hard + "tsconfig-paths@npm:^3.15.0": version: 3.15.0 resolution: "tsconfig-paths@npm:3.15.0" @@ -14709,7 +14781,7 @@ __metadata: languageName: node linkType: hard -"typed-array-buffer@npm:^1.0.2": +"typed-array-buffer@npm:^1.0.3": version: 1.0.3 resolution: "typed-array-buffer@npm:1.0.3" dependencies: @@ -14720,7 +14792,7 @@ __metadata: languageName: node linkType: hard -"typed-array-byte-length@npm:^1.0.1": +"typed-array-byte-length@npm:^1.0.3": version: 1.0.3 resolution: "typed-array-byte-length@npm:1.0.3" dependencies: @@ -14733,7 +14805,7 @@ __metadata: languageName: node linkType: hard -"typed-array-byte-offset@npm:^1.0.3": +"typed-array-byte-offset@npm:^1.0.4": version: 1.0.4 resolution: "typed-array-byte-offset@npm:1.0.4" dependencies: @@ -14762,6 +14834,20 @@ __metadata: languageName: node linkType: hard +"typescript-eslint@npm:8.24.1": + version: 8.24.1 + resolution: "typescript-eslint@npm:8.24.1" + dependencies: + "@typescript-eslint/eslint-plugin": "npm:8.24.1" + "@typescript-eslint/parser": "npm:8.24.1" + "@typescript-eslint/utils": "npm:8.24.1" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.8.0" + checksum: 10/c50e555c5a5a42f843d2a7d57315b35749eb05fdf2b264fd8471f8a825a744444fb534c0a6bb3f0086ad3b3dc0ef76da6ac3154a917af81c908016d5874cbbae + languageName: node + linkType: hard + "typescript@npm:5.7.3": version: 5.7.3 resolution: "typescript@npm:5.7.3" @@ -14831,7 +14917,7 @@ __metadata: languageName: node linkType: hard -"unbox-primitive@npm:^1.0.2": +"unbox-primitive@npm:^1.1.0": version: 1.1.0 resolution: "unbox-primitive@npm:1.1.0" dependencies: @@ -15159,18 +15245,34 @@ __metadata: languageName: node linkType: hard -"vite-node@npm:3.0.5": - version: 3.0.5 - resolution: "vite-node@npm:3.0.5" +"vite-node@npm:3.0.6": + version: 3.0.6 + resolution: "vite-node@npm:3.0.6" dependencies: cac: "npm:^6.7.14" debug: "npm:^4.4.0" es-module-lexer: "npm:^1.6.0" - pathe: "npm:^2.0.2" + pathe: "npm:^2.0.3" vite: "npm:^5.0.0 || ^6.0.0" bin: vite-node: vite-node.mjs - checksum: 10/804d3a4a794f9fa7d5c7b433e96b0813eee39b8c0d4da5c8fe28c9a2aa226702ec711e272a66a5208944f26a35e46d931fc09b1404b04db1cf607f58af1baf6b + checksum: 10/77fad00a16ec032fa1146c5c685f73a29dc5f544a5bf35ee46fef963c7e065230983a1b5b49a110ca138b0a28c40ec710f8cb2854b6e608caad780215838473d + languageName: node + linkType: hard + +"vite-tsconfig-paths@npm:5.1.4": + version: 5.1.4 + resolution: "vite-tsconfig-paths@npm:5.1.4" + dependencies: + debug: "npm:^4.1.1" + globrex: "npm:^0.1.2" + tsconfck: "npm:^3.0.3" + peerDependencies: + vite: "*" + peerDependenciesMeta: + vite: + optional: true + checksum: 10/b409dbd17829f560021a71dba3e473b9c06dcf5fdc9d630b72c1f787145ec478b38caff1be04868971ac8bdcbf0f5af45eeece23dbc9c59c54b901f867740ae0 languageName: node linkType: hard @@ -15226,36 +15328,36 @@ __metadata: languageName: node linkType: hard -"vitest@npm:3.0.5": - version: 3.0.5 - resolution: "vitest@npm:3.0.5" +"vitest@npm:3.0.6": + version: 3.0.6 + resolution: "vitest@npm:3.0.6" dependencies: - "@vitest/expect": "npm:3.0.5" - "@vitest/mocker": "npm:3.0.5" - "@vitest/pretty-format": "npm:^3.0.5" - "@vitest/runner": "npm:3.0.5" - "@vitest/snapshot": "npm:3.0.5" - "@vitest/spy": "npm:3.0.5" - "@vitest/utils": "npm:3.0.5" - chai: "npm:^5.1.2" + "@vitest/expect": "npm:3.0.6" + "@vitest/mocker": "npm:3.0.6" + "@vitest/pretty-format": "npm:^3.0.6" + "@vitest/runner": "npm:3.0.6" + "@vitest/snapshot": "npm:3.0.6" + "@vitest/spy": "npm:3.0.6" + "@vitest/utils": "npm:3.0.6" + chai: "npm:^5.2.0" debug: "npm:^4.4.0" expect-type: "npm:^1.1.0" magic-string: "npm:^0.30.17" - pathe: "npm:^2.0.2" + pathe: "npm:^2.0.3" std-env: "npm:^3.8.0" tinybench: "npm:^2.9.0" tinyexec: "npm:^0.3.2" tinypool: "npm:^1.0.2" tinyrainbow: "npm:^2.0.0" vite: "npm:^5.0.0 || ^6.0.0" - vite-node: "npm:3.0.5" + vite-node: "npm:3.0.6" why-is-node-running: "npm:^2.3.0" peerDependencies: "@edge-runtime/vm": "*" "@types/debug": ^4.1.12 "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 - "@vitest/browser": 3.0.5 - "@vitest/ui": 3.0.5 + "@vitest/browser": 3.0.6 + "@vitest/ui": 3.0.6 happy-dom: "*" jsdom: "*" peerDependenciesMeta: @@ -15275,7 +15377,7 @@ __metadata: optional: true bin: vitest: vitest.mjs - checksum: 10/63bf6474d314e0694489d23236a6aebd4f2173b40e47f861824668fe4b3dde5b6b95d30134acc7b1a0694c0b82b4996deb7ebc7c0ae62cb58823ff51cdcadbe1 + checksum: 10/12d7d6e31d5ec3b9242cb478e5b799a1a4e52fd3c951bb98c3c99091d6d76c7aee63b80c6523704afa70bcb660e586fd8f342c933d1c19fa30aee6cc8ac76ae0 languageName: node linkType: hard @@ -15686,7 +15788,7 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.16": +"which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.18": version: 1.1.18 resolution: "which-typed-array@npm:1.1.18" dependencies: @@ -16255,9 +16357,9 @@ __metadata: languageName: node linkType: hard -"yargs@npm:17.6.2": - version: 17.6.2 - resolution: "yargs@npm:17.6.2" +"yargs@npm:17.7.2, yargs@npm:^17.7.2": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" dependencies: cliui: "npm:^8.0.1" escalade: "npm:^3.1.1" @@ -16266,7 +16368,7 @@ __metadata: string-width: "npm:^4.2.3" y18n: "npm:^5.0.5" yargs-parser: "npm:^21.1.1" - checksum: 10/77e4221b49867d50ce5ded87e91ff11f439b46aa4f01d2116f65402c3ac7dfba937d5bb29d50cecf4acda5aaf848d6ff4facd50b2428098c3990c46d58d5b539 + checksum: 10/abb3e37678d6e38ea85485ed86ebe0d1e3464c640d7d9069805ea0da12f69d5a32df8e5625e370f9c96dd1c2dc088ab2d0a4dd32af18222ef3c4224a19471576 languageName: node linkType: hard @@ -16304,21 +16406,6 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.7.2": - version: 17.7.2 - resolution: "yargs@npm:17.7.2" - dependencies: - cliui: "npm:^8.0.1" - escalade: "npm:^3.1.1" - get-caller-file: "npm:^2.0.5" - require-directory: "npm:^2.1.1" - string-width: "npm:^4.2.3" - y18n: "npm:^5.0.5" - yargs-parser: "npm:^21.1.1" - checksum: 10/abb3e37678d6e38ea85485ed86ebe0d1e3464c640d7d9069805ea0da12f69d5a32df8e5625e370f9c96dd1c2dc088ab2d0a4dd32af18222ef3c4224a19471576 - languageName: node - linkType: hard - "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0"