Merge branch 'dev'

This commit is contained in:
Bram Kragten 2024-11-04 19:04:13 +01:00
commit 452cfee2cd
57 changed files with 676 additions and 238 deletions

View File

@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Send bundle stats and build information to RelativeCI - name: Send bundle stats and build information to RelativeCI
uses: relative-ci/agent-action@v2.1.12 uses: relative-ci/agent-action@v2.1.13
with: with:
key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }} key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }}
token: ${{ github.token }} token: ${{ github.token }}

View File

@ -55,7 +55,7 @@ jobs:
script/release script/release
- name: Upload release assets - name: Upload release assets
uses: softprops/action-gh-release@v2.0.8 uses: softprops/action-gh-release@v2.0.9
with: with:
files: | files: |
dist/*.whl dist/*.whl

View File

@ -223,7 +223,10 @@ class HassioAddonInfo extends LitElement {
<div class="description light-color"> <div class="description light-color">
${this.addon.version ${this.addon.version
? html` ? html`
Current version: ${this.addon.version} ${this.supervisor.localize(
"addon.dashboard.current_version",
{ version: this.addon.version }
)}
<div class="changelog" @click=${this._openChangelog}> <div class="changelog" @click=${this._openChangelog}>
(<span class="changelog-link" (<span class="changelog-link"
>${this.supervisor.localize( >${this.supervisor.localize(

View File

@ -27,11 +27,11 @@
"dependencies": { "dependencies": {
"@babel/runtime": "7.26.0", "@babel/runtime": "7.26.0",
"@braintree/sanitize-url": "7.1.0", "@braintree/sanitize-url": "7.1.0",
"@codemirror/autocomplete": "6.18.1", "@codemirror/autocomplete": "6.18.2",
"@codemirror/commands": "6.7.1", "@codemirror/commands": "6.7.1",
"@codemirror/language": "6.10.3", "@codemirror/language": "6.10.3",
"@codemirror/legacy-modes": "6.4.1", "@codemirror/legacy-modes": "6.4.1",
"@codemirror/search": "6.5.6", "@codemirror/search": "6.5.7",
"@codemirror/state": "6.4.1", "@codemirror/state": "6.4.1",
"@codemirror/view": "6.34.1", "@codemirror/view": "6.34.1",
"@egjs/hammerjs": "2.0.17", "@egjs/hammerjs": "2.0.17",
@ -101,7 +101,7 @@
"chart.js": "4.4.6", "chart.js": "4.4.6",
"color-name": "2.0.0", "color-name": "2.0.0",
"comlink": "4.4.1", "comlink": "4.4.1",
"core-js": "3.38.1", "core-js": "3.39.0",
"cropperjs": "1.6.2", "cropperjs": "1.6.2",
"date-fns": "4.1.0", "date-fns": "4.1.0",
"date-fns-tz": "3.2.0", "date-fns-tz": "3.2.0",
@ -142,12 +142,12 @@
"vue": "2.7.16", "vue": "2.7.16",
"vue2-daterange-picker": "0.6.8", "vue2-daterange-picker": "0.6.8",
"weekstart": "2.0.0", "weekstart": "2.0.0",
"workbox-cacheable-response": "7.1.0", "workbox-cacheable-response": "7.3.0",
"workbox-core": "7.1.0", "workbox-core": "7.3.0",
"workbox-expiration": "7.1.0", "workbox-expiration": "7.3.0",
"workbox-precaching": "7.1.0", "workbox-precaching": "7.3.0",
"workbox-routing": "7.1.0", "workbox-routing": "7.3.0",
"workbox-strategies": "7.1.0", "workbox-strategies": "7.3.0",
"xss": "1.0.15" "xss": "1.0.15"
}, },
"devDependencies": { "devDependencies": {
@ -224,7 +224,7 @@
"lodash.template": "4.5.0", "lodash.template": "4.5.0",
"magic-string": "0.30.12", "magic-string": "0.30.12",
"map-stream": "0.0.7", "map-stream": "0.0.7",
"mocha": "10.7.3", "mocha": "10.8.2",
"object-hash": "3.0.0", "object-hash": "3.0.0",
"open": "10.1.0", "open": "10.1.0",
"pinst": "3.0.0", "pinst": "3.0.0",
@ -241,7 +241,7 @@
"transform-async-modules-webpack-plugin": "1.1.1", "transform-async-modules-webpack-plugin": "1.1.1",
"ts-lit-plugin": "2.0.2", "ts-lit-plugin": "2.0.2",
"typescript": "5.6.3", "typescript": "5.6.3",
"webpack": "5.95.0", "webpack": "5.96.1",
"webpack-cli": "5.1.4", "webpack-cli": "5.1.4",
"webpack-dev-server": "5.1.0", "webpack-dev-server": "5.1.0",
"webpack-manifest-plugin": "5.0.0", "webpack-manifest-plugin": "5.0.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "home-assistant-frontend" name = "home-assistant-frontend"
version = "20241031.0" version = "20241104.0"
license = {text = "Apache-2.0"} license = {text = "Apache-2.0"}
description = "The Home Assistant frontend" description = "The Home Assistant frontend"
readme = "README.md" readme = "README.md"

View File

@ -102,7 +102,9 @@ export class HaCameraStream extends LitElement {
.entityid=${this.stateObj.entity_id} .entityid=${this.stateObj.entity_id}
.posterUrl=${this._posterUrl} .posterUrl=${this._posterUrl}
@streams=${this._handleHlsStreams} @streams=${this._handleHlsStreams}
class=${!this._streamType && this._webRtcStreams ? "hidden" : ""} class=${!this._streamType && this._webRtcStreams?.hasVideo
? "hidden"
: ""}
></ha-hls-player>` ></ha-hls-player>`
: nothing} : nothing}
${this._streamType === STREAM_TYPE_WEB_RTC || ${this._streamType === STREAM_TYPE_WEB_RTC ||

View File

@ -43,6 +43,7 @@ class HaDurationInput extends LitElement {
.label=${this.label} .label=${this.label}
.helper=${this.helper} .helper=${this.helper}
.required=${this.required} .required=${this.required}
.clearable=${!this.required && this.data !== undefined}
.autoValidate=${this.required} .autoValidate=${this.required}
.disabled=${this.disabled} .disabled=${this.disabled}
errorMessage="Required" errorMessage="Required"
@ -67,28 +68,56 @@ class HaDurationInput extends LitElement {
} }
private get _days() { private get _days() {
return this.data?.days ? Number(this.data.days) : 0; return this.data?.days
? Number(this.data.days)
: this.required || this.data
? 0
: NaN;
} }
private get _hours() { private get _hours() {
return this.data?.hours ? Number(this.data.hours) : 0; return this.data?.hours
? Number(this.data.hours)
: this.required || this.data
? 0
: NaN;
} }
private get _minutes() { private get _minutes() {
return this.data?.minutes ? Number(this.data.minutes) : 0; return this.data?.minutes
? Number(this.data.minutes)
: this.required || this.data
? 0
: NaN;
} }
private get _seconds() { private get _seconds() {
return this.data?.seconds ? Number(this.data.seconds) : 0; return this.data?.seconds
? Number(this.data.seconds)
: this.required || this.data
? 0
: NaN;
} }
private get _milliseconds() { private get _milliseconds() {
return this.data?.milliseconds ? Number(this.data.milliseconds) : 0; return this.data?.milliseconds
? Number(this.data.milliseconds)
: this.required || this.data
? 0
: NaN;
} }
private _durationChanged(ev: CustomEvent<{ value: TimeChangedEvent }>) { private _durationChanged(ev: CustomEvent<{ value?: TimeChangedEvent }>) {
ev.stopPropagation(); ev.stopPropagation();
const value = { ...ev.detail.value }; const value = ev.detail.value ? { ...ev.detail.value } : undefined;
if (value) {
value.hours ||= 0;
value.minutes ||= 0;
value.seconds ||= 0;
if ("days" in value) value.days ||= 0;
if ("milliseconds" in value) value.milliseconds ||= 0;
if (!this.enableMillisecond && !value.milliseconds) { if (!this.enableMillisecond && !value.milliseconds) {
// @ts-ignore // @ts-ignore
@ -112,6 +141,7 @@ class HaDurationInput extends LitElement {
value.days = (value.days ?? 0) + Math.floor(value.hours / 24); value.days = (value.days ?? 0) + Math.floor(value.hours / 24);
value.hours %= 24; value.hours %= 24;
} }
}
fireEvent(this, "value-changed", { fireEvent(this, "value-changed", {
value, value,

View File

@ -8,7 +8,6 @@ import { customIcons } from "../data/custom_icons";
import type { Chunks, Icons } from "../data/iconsets"; import type { Chunks, Icons } from "../data/iconsets";
import { import {
MDI_PREFIXES, MDI_PREFIXES,
checkCacheVersion,
findIconChunk, findIconChunk,
getIcon, getIcon,
writeCache, writeCache,
@ -26,11 +25,6 @@ const mdiDeprecatedIcons: DeprecatedIcon = {};
const chunks: Chunks = {}; const chunks: Chunks = {};
// Supervisor doesn't use icons, and should not update/downgrade the icon DB.
if (!__SUPERVISOR__) {
checkCacheVersion();
}
const debouncedWriteCache = debounce(() => writeCache(chunks), 2000); const debouncedWriteCache = debounce(() => writeCache(chunks), 2000);
const cachedIcons: Record<string, string> = {}; const cachedIcons: Record<string, string> = {};

View File

@ -24,7 +24,7 @@ export class HaToast extends Snackbar {
max-width: 650px; max-width: 650px;
} }
// Revert the default styles set by mwc-snackbar /* Revert the default styles set by mwc-snackbar */
@media (max-width: 480px), (max-width: 344px) { @media (max-width: 480px), (max-width: 344px) {
.mdc-snackbar__surface { .mdc-snackbar__surface {
min-width: inherit; min-width: inherit;

View File

@ -86,6 +86,8 @@ export class HaMap extends ReactiveElement {
private _mapZones: Array<Marker | Circle> = []; private _mapZones: Array<Marker | Circle> = [];
private _mapFocusZones: Array<Marker | Circle> = [];
private _mapPaths: Array<Polyline | CircleMarker> = []; private _mapPaths: Array<Polyline | CircleMarker> = [];
public connectedCallback(): void { public connectedCallback(): void {
@ -201,7 +203,11 @@ export class HaMap extends ReactiveElement {
return; return;
} }
if (!this._mapFocusItems.length && !this.layers?.length) { if (
!this._mapFocusItems.length &&
!this._mapFocusZones.length &&
!this.layers?.length
) {
this.leafletMap.setView( this.leafletMap.setView(
new this.Leaflet.LatLng( new this.Leaflet.LatLng(
this.hass.config.latitude, this.hass.config.latitude,
@ -218,13 +224,9 @@ export class HaMap extends ReactiveElement {
: [] : []
); );
if (this.fitZones) { this._mapFocusZones?.forEach((zone) => {
this._mapZones?.forEach((zone) => { bounds.extend("getBounds" in zone ? zone.getBounds() : zone.getLatLng());
bounds.extend(
"getBounds" in zone ? zone.getBounds() : zone.getLatLng()
);
}); });
}
this.layers?.forEach((layer: any) => { this.layers?.forEach((layer: any) => {
bounds.extend( bounds.extend(
@ -395,6 +397,7 @@ export class HaMap extends ReactiveElement {
if (this._mapZones.length) { if (this._mapZones.length) {
this._mapZones.forEach((marker) => marker.remove()); this._mapZones.forEach((marker) => marker.remove());
this._mapZones = []; this._mapZones = [];
this._mapFocusZones = [];
} }
if (!this.entities) { if (!this.entities) {
@ -466,13 +469,18 @@ export class HaMap extends ReactiveElement {
); );
// create circle around it // create circle around it
this._mapZones.push( const circle = Leaflet.circle([latitude, longitude], {
Leaflet.circle([latitude, longitude], {
interactive: false, interactive: false,
color: passive ? passiveZoneColor : zoneColor, color: passive ? passiveZoneColor : zoneColor,
radius, radius,
}) });
); this._mapZones.push(circle);
if (
this.fitZones &&
(typeof entity === "string" || entity.focus !== false)
) {
this._mapFocusZones.push(circle);
}
continue; continue;
} }

View File

@ -193,7 +193,7 @@ export const fetchHassioLogs = async (
) => ) =>
hass.callApiRaw( hass.callApiRaw(
"GET", "GET",
`hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs/boots/${boot}`, `hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs${boot !== 0 ? `/boots/${boot}` : ""}`,
undefined, undefined,
range range
? { ? {
@ -203,20 +203,6 @@ export const fetchHassioLogs = async (
); );
export const fetchHassioLogsFollow = async ( export const fetchHassioLogsFollow = async (
hass: HomeAssistant,
provider: string,
signal: AbortSignal,
lines = 100
) =>
hass.callApiRaw(
"GET",
`hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs/follow?lines=${lines}`,
undefined,
undefined,
signal
);
export const fetchHassioLogsBootFollow = async (
hass: HomeAssistant, hass: HomeAssistant,
provider: string, provider: string,
signal: AbortSignal, signal: AbortSignal,
@ -225,7 +211,7 @@ export const fetchHassioLogsBootFollow = async (
) => ) =>
hass.callApiRaw( hass.callApiRaw(
"GET", "GET",
`hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs/boots/${boot}/follow?lines=${lines}`, `hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs${boot !== 0 ? `/boots/${boot}` : ""}/follow?lines=${lines}`,
undefined, undefined,
undefined, undefined,
signal signal
@ -236,19 +222,14 @@ export const getHassioLogDownloadUrl = (provider: string) =>
provider.includes("_") ? `addons/${provider}` : provider provider.includes("_") ? `addons/${provider}` : provider
}/logs`; }/logs`;
export const getHassioLogDownloadLinesUrl = (provider: string, lines: number) => export const getHassioLogDownloadLinesUrl = (
`/api/hassio/${
provider.includes("_") ? `addons/${provider}` : provider
}/logs?lines=${lines}`;
export const getHassioLogBootDownloadLinesUrl = (
provider: string, provider: string,
lines: number, lines: number,
boot = 0 boot = 0
) => ) =>
`/api/hassio/${ `/api/hassio/${
provider.includes("_") ? `addons/${provider}` : provider provider.includes("_") ? `addons/${provider}` : provider
}/logs/boots/${boot}?lines=${lines}`; }/logs${boot !== 0 ? `/boots/${boot}` : ""}?lines=${lines}`;
export const setSupervisorOption = async ( export const setSupervisorOption = async (
hass: HomeAssistant, hass: HomeAssistant,

View File

@ -1,4 +1,5 @@
import { clear, get, set, createStore, promisifyRequest } from "idb-keyval"; import { clear, get, set, createStore, promisifyRequest } from "idb-keyval";
import memoizeOne from "memoize-one";
import { promiseTimeout } from "../common/util/promise-timeout"; import { promiseTimeout } from "../common/util/promise-timeout";
import { iconMetadata } from "../resources/icon-metadata"; import { iconMetadata } from "../resources/icon-metadata";
import type { IconMeta } from "../types"; import type { IconMeta } from "../types";
@ -11,7 +12,23 @@ export interface Chunks {
[key: string]: Promise<Icons>; [key: string]: Promise<Icons>;
} }
export const iconStore = createStore("hass-icon-db", "mdi-icon-store"); const getStore = memoizeOne(async () => {
const iconStore = createStore("hass-icon-db", "mdi-icon-store");
// Supervisor doesn't use icons, and should not update/downgrade the icon DB.
if (!__SUPERVISOR__) {
const version = await get("_version", iconStore);
if (!version) {
set("_version", iconMetadata.version, iconStore);
} else if (version !== iconMetadata.version) {
await clear(iconStore);
set("_version", iconMetadata.version, iconStore);
}
}
return iconStore;
});
export const MDI_PREFIXES = ["mdi", "hass", "hassio", "hademo"]; export const MDI_PREFIXES = ["mdi", "hass", "hassio", "hademo"];
@ -28,7 +45,10 @@ export const getIcon = (iconName: string) =>
return; return;
} }
const readIcons = () => // Start initializing the store, so it's ready when we need it
const iconStoreProm = getStore();
const readIcons = async () => {
const iconStore = await iconStoreProm;
iconStore("readonly", (store) => { iconStore("readonly", (store) => {
for (const [iconName_, resolve_, reject_] of toRead) { for (const [iconName_, resolve_, reject_] of toRead) {
promisifyRequest<string | undefined>(store.get(iconName_)) promisifyRequest<string | undefined>(store.get(iconName_))
@ -37,6 +57,7 @@ export const getIcon = (iconName: string) =>
} }
toRead = []; toRead = [];
}); });
};
promiseTimeout(1000, readIcons()).catch((e) => { promiseTimeout(1000, readIcons()).catch((e) => {
// Firefox in private mode doesn't support IDB // Firefox in private mode doesn't support IDB
@ -62,6 +83,7 @@ export const findIconChunk = (icon: string): string => {
export const writeCache = async (chunks: Chunks) => { export const writeCache = async (chunks: Chunks) => {
const keys = Object.keys(chunks); const keys = Object.keys(chunks);
const iconsSets: Icons[] = await Promise.all(Object.values(chunks)); const iconsSets: Icons[] = await Promise.all(Object.values(chunks));
const iconStore = await getStore();
// We do a batch opening the store just once, for (considerable) performance // We do a batch opening the store just once, for (considerable) performance
iconStore("readwrite", (store) => { iconStore("readwrite", (store) => {
iconsSets.forEach((icons, idx) => { iconsSets.forEach((icons, idx) => {
@ -72,14 +94,3 @@ export const writeCache = async (chunks: Chunks) => {
}); });
}); });
}; };
export const checkCacheVersion = async () => {
const version = await get("_version", iconStore);
if (!version) {
set("_version", iconMetadata.version, iconStore);
} else if (version !== iconMetadata.version) {
await clear(iconStore);
set("_version", iconMetadata.version, iconStore);
}
};

View File

@ -1,10 +1,24 @@
import type { HassEntity } from "home-assistant-js-websocket"; import type { HassEntity } from "home-assistant-js-websocket";
import type { CSSResultGroup } from "lit"; import type { CSSResultGroup, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit"; import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { format } from "date-fns";
import { computeStateName } from "../../../common/entity/compute_state_name"; import { computeStateName } from "../../../common/entity/compute_state_name";
import "../../../components/ha-climate-state";
import "../../../components/ha-cover-controls";
import "../../../components/ha-cover-tilt-controls";
import "../../../components/ha-date-input";
import "../../../components/ha-humidifier-state";
import "../../../components/ha-select";
import "../../../components/ha-slider";
import "../../../components/ha-time-input";
import "../../../components/entity/ha-entity-toggle";
import "../../../components/entity/state-badge"; import "../../../components/entity/state-badge";
import { isTiltOnly } from "../../../data/cover";
import { isUnavailableState } from "../../../data/entity"; import { isUnavailableState } from "../../../data/entity";
import type { ImageEntity } from "../../../data/image";
import { computeImageUrl } from "../../../data/image";
import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../../../data/sensor"; import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../../../data/sensor";
import "../../../panels/lovelace/components/hui-timestamp-display"; import "../../../panels/lovelace/components/hui-timestamp-display";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
@ -28,18 +42,7 @@ class EntityPreviewRow extends LitElement {
<div class="name" .title=${computeStateName(stateObj)}> <div class="name" .title=${computeStateName(stateObj)}>
${computeStateName(stateObj)} ${computeStateName(stateObj)}
</div> </div>
<div class="value"> <div class="value">${this.renderEntityState(stateObj)}</div>`;
${stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP &&
!isUnavailableState(stateObj.state)
? html`
<hui-timestamp-display
.hass=${this.hass}
.ts=${new Date(stateObj.state)}
capitalize
></hui-timestamp-display>
`
: this.hass.formatEntityState(stateObj)}
</div>`;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
@ -59,8 +62,308 @@ class EntityPreviewRow extends LitElement {
.value { .value {
direction: ltr; direction: ltr;
} }
.numberflex {
display: flex;
align-items: center;
justify-content: flex-end;
flex-grow: 2;
}
.numberstate {
min-width: 45px;
text-align: end;
}
ha-textfield {
text-align: end;
direction: ltr !important;
}
ha-slider {
width: 100%;
max-width: 200px;
}
ha-time-input {
margin-left: 4px;
margin-inline-start: 4px;
margin-inline-end: initial;
direction: var(--direction);
}
.datetimeflex {
display: flex;
justify-content: flex-end;
width: 100%;
}
mwc-button {
margin-right: -0.57em;
margin-inline-end: -0.57em;
margin-inline-start: initial;
}
img {
display: block;
width: 100%;
}
`; `;
} }
private renderEntityState(stateObj: HassEntity): TemplateResult | string {
const domain = stateObj.entity_id.split(".", 1)[0];
if (domain === "button") {
return html`
<mwc-button .disabled=${isUnavailableState(stateObj.state)}>
${this.hass.localize("ui.card.button.press")}
</mwc-button>
`;
}
const climateDomains = ["climate", "water_heater"];
if (climateDomains.includes(domain)) {
return html`
<ha-climate-state .hass=${this.hass} .stateObj=${stateObj}>
</ha-climate-state>
`;
}
if (domain === "cover") {
return html`
${isTiltOnly(stateObj)
? html`
<ha-cover-tilt-controls
.hass=${this.hass}
.stateObj=${stateObj}
></ha-cover-tilt-controls>
`
: html`
<ha-cover-controls
.hass=${this.hass}
.stateObj=${stateObj}
></ha-cover-controls>
`}
`;
}
if (domain === "date") {
return html`
<ha-date-input
.locale=${this.hass.locale}
.disabled=${isUnavailableState(stateObj.state)}
.value=${isUnavailableState(stateObj.state)
? undefined
: stateObj.state}
>
</ha-date-input>
`;
}
if (domain === "datetime") {
const dateObj = isUnavailableState(stateObj.state)
? undefined
: new Date(stateObj.state);
const time = dateObj ? format(dateObj, "HH:mm:ss") : undefined;
const date = dateObj ? format(dateObj, "yyyy-MM-dd") : undefined;
return html`
<div class="datetimeflex">
<ha-date-input
.label=${computeStateName(stateObj)}
.locale=${this.hass.locale}
.value=${date}
.disabled=${isUnavailableState(stateObj.state)}
>
</ha-date-input>
<ha-time-input
.value=${time}
.disabled=${isUnavailableState(stateObj.state)}
.locale=${this.hass.locale}
></ha-time-input>
</div>
`;
}
if (domain === "event") {
return html`
<div class="when">
${isUnavailableState(stateObj.state)
? this.hass.formatEntityState(stateObj)
: html`<hui-timestamp-display
.hass=${this.hass}
.ts=${new Date(stateObj.state)}
capitalize
></hui-timestamp-display>`}
</div>
<div class="what">
${isUnavailableState(stateObj.state)
? nothing
: this.hass.formatEntityAttributeValue(stateObj, "event_type")}
</div>
`;
}
const toggleDomains = ["fan", "light", "remote", "siren", "switch"];
if (toggleDomains.includes(domain)) {
const showToggle =
stateObj.state === "on" ||
stateObj.state === "off" ||
isUnavailableState(stateObj.state);
return html`
${showToggle
? html`
<ha-entity-toggle
.hass=${this.hass}
.stateObj=${stateObj}
></ha-entity-toggle>
`
: this.hass.formatEntityState(stateObj)}
`;
}
if (domain === "humidifier") {
return html`
<ha-humidifier-state .hass=${this.hass} .stateObj=${stateObj}>
</ha-humidifier-state>
`;
}
if (domain === "image") {
const image: string = computeImageUrl(stateObj as ImageEntity);
return html`
<img
alt=${ifDefined(stateObj?.attributes.friendly_name)}
src=${this.hass.hassUrl(image)}
/>
`;
}
if (domain === "lock") {
return html`
<mwc-button
.disabled=${isUnavailableState(stateObj.state)}
class="text-content"
>
${stateObj.state === "locked"
? this.hass!.localize("ui.card.lock.unlock")
: this.hass!.localize("ui.card.lock.lock")}
</mwc-button>
`;
}
if (domain === "number") {
const showNumberSlider =
stateObj.attributes.mode === "slider" ||
(stateObj.attributes.mode === "auto" &&
(Number(stateObj.attributes.max) - Number(stateObj.attributes.min)) /
Number(stateObj.attributes.step) <=
256);
return html`
${showNumberSlider
? html`
<div class="numberflex">
<ha-slider
labeled
.disabled=${isUnavailableState(stateObj.state)}
.step=${Number(stateObj.attributes.step)}
.min=${Number(stateObj.attributes.min)}
.max=${Number(stateObj.attributes.max)}
.value=${Number(stateObj.state)}
></ha-slider>
<span class="state">
${this.hass.formatEntityState(stateObj)}
</span>
</div>
`
: html` <div class="numberflex numberstate">
<ha-textfield
autoValidate
.disabled=${isUnavailableState(stateObj.state)}
pattern="[0-9]+([\\.][0-9]+)?"
.step=${Number(stateObj.attributes.step)}
.min=${Number(stateObj.attributes.min)}
.max=${Number(stateObj.attributes.max)}
.value=${stateObj.state}
.suffix=${stateObj.attributes.unit_of_measurement}
type="number"
></ha-textfield>
</div>`}
`;
}
if (domain === "select") {
return html`
<ha-select
.label=${computeStateName(stateObj)}
.value=${stateObj.state}
.disabled=${isUnavailableState(stateObj.state)}
naturalMenuWidth
>
${stateObj.attributes.options
? stateObj.attributes.options.map(
(option) => html`
<mwc-list-item .value=${option}>
${this.hass!.formatEntityState(stateObj, option)}
</mwc-list-item>
`
)
: ""}
</ha-select>
`;
}
if (domain === "sensor") {
const showSensor =
stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP &&
!isUnavailableState(stateObj.state);
return html`
${showSensor
? html`
<hui-timestamp-display
.hass=${this.hass}
.ts=${new Date(stateObj.state)}
capitalize
></hui-timestamp-display>
`
: this.hass.formatEntityState(stateObj)}
`;
}
if (domain === "text") {
return html`
<ha-textfield
.label=${computeStateName(stateObj)}
.disabled=${isUnavailableState(stateObj.state)}
.value=${stateObj.state}
.minlength=${stateObj.attributes.min}
.maxlength=${stateObj.attributes.max}
.autoValidate=${stateObj.attributes.pattern}
.pattern=${stateObj.attributes.pattern}
.type=${stateObj.attributes.mode}
placeholder=${this.hass!.localize("ui.card.text.emtpy_value")}
></ha-textfield>
`;
}
if (domain === "time") {
return html`
<ha-time-input
.value=${isUnavailableState(stateObj.state)
? undefined
: stateObj.state}
.locale=${this.hass.locale}
.disabled=${isUnavailableState(stateObj.state)}
></ha-time-input>
`;
}
if (domain === "weather") {
return html`
<div>
${isUnavailableState(stateObj.state) ||
stateObj.attributes.temperature === undefined ||
stateObj.attributes.temperature === null
? this.hass.formatEntityState(stateObj)
: this.hass.formatEntityAttributeValue(stateObj, "temperature")}
</div>
`;
}
return this.hass.formatEntityState(stateObj);
}
} }
declare global { declare global {

View File

@ -143,7 +143,7 @@ class MoreInfoUpdate extends LitElement {
)} )}
</span> </span>
<ha-switch <ha-switch
id="create_backup" id="create-backup"
checked checked
.disabled=${updateIsInstalling(this.stateObj)} .disabled=${updateIsInstalling(this.stateObj)}
></ha-switch> ></ha-switch>

View File

@ -66,6 +66,8 @@ export class HaVoiceAssistantSetupDialog extends LitElement {
private _dialogClosed() { private _dialogClosed() {
this._params = undefined; this._params = undefined;
this._assistConfiguration = undefined; this._assistConfiguration = undefined;
this._previousSteps = [];
this._nextStep = undefined;
this._step = STEP.INIT; this._step = STEP.INIT;
fireEvent(this, "dialog-closed", { dialog: this.localName }); fireEvent(this, "dialog-closed", { dialog: this.localName });
} }

View File

@ -17,7 +17,7 @@ export class HaVoiceAssistantSetupStepArea extends LitElement {
const device = this.hass.devices[this.deviceId]; const device = this.hass.devices[this.deviceId];
return html`<div class="content"> return html`<div class="content">
<img src="/static/images/voice-assistant/area.gif" /> <img src="/static/images/voice-assistant/area.png" />
<h1>Select area</h1> <h1>Select area</h1>
<p class="secondary"> <p class="secondary">
When you voice assistant knows where it is, it can better control the When you voice assistant knows where it is, it can better control the

View File

@ -21,7 +21,7 @@ export class HaVoiceAssistantSetupStepChangeWakeWord extends LitElement {
protected override render() { protected override render() {
return html`<div class="padding content"> return html`<div class="padding content">
<img src="/static/images/voice-assistant/change-wake-word.gif" /> <img src="/static/images/voice-assistant/change-wake-word.png" />
<h1>Change wake word</h1> <h1>Change wake word</h1>
<p class="secondary"> <p class="secondary">
Some wake words are better for Some wake words are better for

View File

@ -6,6 +6,7 @@ import "../../components/ha-circular-progress";
import { testAssistSatelliteConnection } from "../../data/assist_satellite"; import { testAssistSatelliteConnection } from "../../data/assist_satellite";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import { AssistantSetupStyles } from "./styles"; import { AssistantSetupStyles } from "./styles";
import { documentationUrl } from "../../util/documentation-url";
@customElement("ha-voice-assistant-setup-step-check") @customElement("ha-voice-assistant-setup-step-check")
export class HaVoiceAssistantSetupStepCheck extends LitElement { export class HaVoiceAssistantSetupStepCheck extends LitElement {
@ -35,7 +36,7 @@ export class HaVoiceAssistantSetupStepCheck extends LitElement {
protected override render() { protected override render() {
return html`<div class="content"> return html`<div class="content">
${this._status === "timeout" ${this._status === "timeout"
? html`<img src="/static/images/voice-assistant/error.gif" /> ? html`<img src="/static/images/voice-assistant/error.png" />
<h1>The voice assistant is unable to connect to Home Assistant</h1> <h1>The voice assistant is unable to connect to Home Assistant</h1>
<p class="secondary"> <p class="secondary">
To play audio, the voice assistant device has to connect to Home To play audio, the voice assistant device has to connect to Home
@ -44,12 +45,15 @@ export class HaVoiceAssistantSetupStepCheck extends LitElement {
</p> </p>
<div class="footer"> <div class="footer">
<a <a
href="https://www.home-assistant.io/docs/configuration/remote/#adding-a-remote-url-to-home-assistant" href=${documentationUrl(
this.hass,
"/voice_control/troubleshooting/#i-dont-get-a-voice-response"
)}
><ha-button>Help me</ha-button></a ><ha-button>Help me</ha-button></a
> >
<ha-button @click=${this._testConnection}>Retry</ha-button> <ha-button @click=${this._testConnection}>Retry</ha-button>
</div>` </div>`
: html`<img src="/static/images/voice-assistant/hi.gif" /> : html`<img src="/static/images/voice-assistant/hi.png" />
<h1>Hi</h1> <h1>Hi</h1>
<p class="secondary"> <p class="secondary">
Over the next couple steps we're going to personalize your voice Over the next couple steps we're going to personalize your voice

View File

@ -67,7 +67,7 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement {
: undefined; : undefined;
return html`<div class="content"> return html`<div class="content">
<img src="/static/images/voice-assistant/heart.gif" /> <img src="/static/images/voice-assistant/heart.png" />
<h1>Ready to Assist!</h1> <h1>Ready to Assist!</h1>
<p class="secondary"> <p class="secondary">
Make any final customizations here. You can always change these in the Make any final customizations here. You can always change these in the

View File

@ -65,7 +65,7 @@ export class HaVoiceAssistantSetupStepUpdate extends LitElement {
const progressIsNumeric = stateObj && updateUsesProgress(stateObj); const progressIsNumeric = stateObj && updateUsesProgress(stateObj);
return html`<div class="content"> return html`<div class="content">
<img src="/static/images/voice-assistant/update.gif" /> <img src="/static/images/voice-assistant/update.png" />
<h1> <h1>
${stateObj && ${stateObj &&
(stateObj.state === "unavailable" || updateIsInstalling(stateObj)) (stateObj.state === "unavailable" || updateIsInstalling(stateObj))

View File

@ -64,14 +64,14 @@ export class HaVoiceAssistantSetupStepWakeWord extends LitElement {
return html`<div class="content"> return html`<div class="content">
${!this._detected ${!this._detected
? html` ? html`
<img src="/static/images/voice-assistant/sleep.gif" /> <img src="/static/images/voice-assistant/sleep.png" />
<h1> <h1>
Say ${this._activeWakeWord(this.assistConfiguration)} to wake the Say ${this._activeWakeWord(this.assistConfiguration)} to wake the
device up device up
</h1> </h1>
<p class="secondary">Setup will continue once the device is awake.</p> <p class="secondary">Setup will continue once the device is awake.</p>
</div>` </div>`
: html`<img src="/static/images/voice-assistant/ok-nabu.gif" /> : html`<img src="/static/images/voice-assistant/ok-nabu.png" />
<h1> <h1>
Say ${this._activeWakeWord(this.assistConfiguration)} again Say ${this._activeWakeWord(this.assistConfiguration)} again
</h1> </h1>

View File

@ -16,6 +16,8 @@ import type { ValueChangedEvent } from "../types";
import { onBoardingStyles } from "./styles"; import { onBoardingStyles } from "./styles";
import { debounce } from "../common/util/debounce"; import { debounce } from "../common/util/debounce";
const CHECK_USERNAME_REGEX = /\s|[A-Z]/;
const CREATE_USER_SCHEMA: HaFormSchema[] = [ const CREATE_USER_SCHEMA: HaFormSchema[] = [
{ {
name: "name", name: "name",
@ -121,6 +123,7 @@ class OnboardingCreateUser extends LitElement {
ev: ValueChangedEvent<HaFormDataContainer> ev: ValueChangedEvent<HaFormDataContainer>
): void { ): void {
const nameChanged = ev.detail.value.name !== this._newUser.name; const nameChanged = ev.detail.value.name !== this._newUser.name;
const usernameChanged = ev.detail.value.username !== this._newUser.username;
const passwordChanged = const passwordChanged =
ev.detail.value.password !== this._newUser.password || ev.detail.value.password !== this._newUser.password ||
ev.detail.value.password_confirm !== this._newUser.password_confirm; ev.detail.value.password_confirm !== this._newUser.password_confirm;
@ -135,6 +138,9 @@ class OnboardingCreateUser extends LitElement {
this._debouncedCheckPasswordMatch(); this._debouncedCheckPasswordMatch();
} }
} }
if (usernameChanged) {
this._checkUsername();
}
} }
private _debouncedCheckPasswordMatch = debounce( private _debouncedCheckPasswordMatch = debounce(
@ -164,6 +170,21 @@ class OnboardingCreateUser extends LitElement {
const parts = String(this._newUser.name).split(" "); const parts = String(this._newUser.name).split(" ");
if (parts.length) { if (parts.length) {
this._newUser.username = parts[0].toLowerCase(); this._newUser.username = parts[0].toLowerCase();
this._checkUsername();
}
}
private _checkUsername(): void {
const old = this._formError.username;
if (CHECK_USERNAME_REGEX.test(this._newUser.username as string)) {
this._formError.username = this.localize(
"ui.panel.page-onboarding.user.error.username_not_normalized"
);
} else {
this._formError.username = "";
}
if (old !== this._formError.username) {
this.requestUpdate("_formError");
} }
} }

View File

@ -49,6 +49,7 @@ export class HaDelayAction extends LitElement implements ActionElement {
.disabled=${this.disabled} .disabled=${this.disabled}
.data=${this._timeData} .data=${this._timeData}
enableMillisecond enableMillisecond
required
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></ha-duration-input>`; ></ha-duration-input>`;
} }

View File

@ -67,9 +67,6 @@ export class HaWaitForTriggerAction
private _timeoutChanged(ev: CustomEvent<{ value: TimeChangedEvent }>): void { private _timeoutChanged(ev: CustomEvent<{ value: TimeChangedEvent }>): void {
ev.stopPropagation(); ev.stopPropagation();
const value = ev.detail.value; const value = ev.detail.value;
if (!value) {
return;
}
fireEvent(this, "value-changed", { fireEvent(this, "value-changed", {
value: { ...this.action, timeout: value }, value: { ...this.action, timeout: value },
}); });

View File

@ -712,8 +712,12 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
private async _duplicate() { private async _duplicate() {
const result = this._readOnly const result = this._readOnly
? await showConfirmationDialog(this, { ? await showConfirmationDialog(this, {
title: "Migrate automation?", title: this.hass.localize(
text: "You can migrate this automation, so it can be edited from the UI. After it is migrated and you have saved it, you will have to manually delete your old automation from your configuration. Do you want to migrate this automation?", "ui.panel.config.automation.picker.migrate_automation"
),
text: this.hass.localize(
"ui.panel.config.automation.picker.migrate_automation_description"
),
}) })
: await this.confirmUnsavedChanged(); : await this.confirmUnsavedChanged();
if (result) { if (result) {

View File

@ -46,7 +46,7 @@ export class HaCalendarTrigger extends LitElement implements TriggerElement {
], ],
], ],
}, },
{ name: "offset", selector: { duration: {} } }, { name: "offset", required: true, selector: { duration: {} } },
{ {
name: "offset_type", name: "offset_type",
type: "select", type: "select",

View File

@ -584,6 +584,10 @@ class AddIntegrationDialog extends LitElement {
}); });
if (configEntries.length > 0) { if (configEntries.length > 0) {
this.closeDialog(); this.closeDialog();
const localize = await this.hass.loadBackendTranslation(
"title",
integration.name
);
showAlertDialog(this, { showAlertDialog(this, {
title: this.hass.localize( title: this.hass.localize(
"ui.panel.config.integrations.config_flow.single_config_entry_title" "ui.panel.config.integrations.config_flow.single_config_entry_title"
@ -591,7 +595,7 @@ class AddIntegrationDialog extends LitElement {
text: this.hass.localize( text: this.hass.localize(
"ui.panel.config.integrations.config_flow.single_config_entry", "ui.panel.config.integrations.config_flow.single_config_entry",
{ {
integration_name: integration.name, integration_name: domainToName(localize, integration.name),
} }
), ),
}); });

View File

@ -1387,6 +1387,10 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
this._extraConfigEntries || this.configEntries this._extraConfigEntries || this.configEntries
); );
if (entries.length > 0) { if (entries.length > 0) {
const localize = await this.hass.loadBackendTranslation(
"title",
this._manifest.name
);
await showAlertDialog(this, { await showAlertDialog(this, {
title: this.hass.localize( title: this.hass.localize(
"ui.panel.config.integrations.config_flow.single_config_entry_title" "ui.panel.config.integrations.config_flow.single_config_entry_title"
@ -1394,7 +1398,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
text: this.hass.localize( text: this.hass.localize(
"ui.panel.config.integrations.config_flow.single_config_entry", "ui.panel.config.integrations.config_flow.single_config_entry",
{ {
integration_name: this._manifest.name, integration_name: domainToName(localize, this._manifest.name),
} }
), ),
}); });

View File

@ -744,6 +744,10 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) {
if (integration.single_config_entry) { if (integration.single_config_entry) {
const configEntries = await getConfigEntries(this.hass, { domain }); const configEntries = await getConfigEntries(this.hass, { domain });
if (configEntries.length > 0) { if (configEntries.length > 0) {
const localize = await this.hass.loadBackendTranslation(
"title",
integration.name
);
showAlertDialog(this, { showAlertDialog(this, {
title: this.hass.localize( title: this.hass.localize(
"ui.panel.config.integrations.config_flow.single_config_entry_title" "ui.panel.config.integrations.config_flow.single_config_entry_title"
@ -751,7 +755,7 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) {
text: this.hass.localize( text: this.hass.localize(
"ui.panel.config.integrations.config_flow.single_config_entry", "ui.panel.config.integrations.config_flow.single_config_entry",
{ {
integration_name: integration.name, integration_name: domainToName(localize, integration.name!),
} }
), ),
}); });

View File

@ -44,7 +44,6 @@ class MatterAddDeviceGoogleHome extends LitElement {
home_assistant: html`<b>Home Assistant</b>`, home_assistant: html`<b>Home Assistant</b>`,
} }
)} )}
<br />
<span <span
class="link" class="link"
type="button" type="button"
@ -57,13 +56,13 @@ class MatterAddDeviceGoogleHome extends LitElement {
)} )}
</span> </span>
</li> </li>
</ol> <li>
<br />
<p>
${this.hass.localize( ${this.hass.localize(
`ui.dialogs.matter-add-device.google_home.redirect` `ui.dialogs.matter-add-device.google_home.redirect`
)} )}
</p> </li>
</ol>
<br />
</div> </div>
`; `;
} }

View File

@ -14,10 +14,7 @@ import type { DownloadLogsDialogParams } from "./show-dialog-download-logs";
import "../../../components/ha-select"; import "../../../components/ha-select";
import "../../../components/ha-list-item"; import "../../../components/ha-list-item";
import { stopPropagation } from "../../../common/dom/stop_propagation"; import { stopPropagation } from "../../../common/dom/stop_propagation";
import { import { getHassioLogDownloadLinesUrl } from "../../../data/hassio/supervisor";
getHassioLogDownloadLinesUrl,
getHassioLogBootDownloadLinesUrl,
} from "../../../data/hassio/supervisor";
import { getSignedPath } from "../../../data/auth"; import { getSignedPath } from "../../../data/auth";
import { fileDownload } from "../../../util/file_download"; import { fileDownload } from "../../../util/file_download";
@ -115,7 +112,7 @@ class DownloadLogsDialog extends LitElement {
const boot = this._dialogParams!.boot; const boot = this._dialogParams!.boot;
const timeString = new Date().toISOString().replace(/:/g, "-"); const timeString = new Date().toISOString().replace(/:/g, "-");
const downloadUrl = this._getDownloadUrlFunction()( const downloadUrl = getHassioLogDownloadLinesUrl(
provider, provider,
this._lineCount, this._lineCount,
boot boot
@ -129,13 +126,6 @@ class DownloadLogsDialog extends LitElement {
this.closeDialog(); this.closeDialog();
} }
private _getDownloadUrlFunction() {
if (this._dialogParams!.boot === 0) {
return getHassioLogDownloadLinesUrl;
}
return getHassioLogBootDownloadLinesUrl;
}
private _setNumberOfLogs(ev) { private _setNumberOfLogs(ev) {
this._lineCount = Number(ev.target.value); this._lineCount = Number(ev.target.value);
} }

View File

@ -39,7 +39,6 @@ import { extractApiErrorMessage } from "../../../data/hassio/common";
import { import {
fetchHassioBoots, fetchHassioBoots,
fetchHassioLogs, fetchHassioLogs,
fetchHassioLogsBootFollow,
fetchHassioLogsFollow, fetchHassioLogsFollow,
getHassioLogDownloadUrl, getHassioLogDownloadUrl,
} from "../../../data/hassio/supervisor"; } from "../../../data/hassio/supervisor";
@ -379,7 +378,7 @@ class ErrorLogCard extends LitElement {
isComponentLoaded(this.hass, "hassio") && isComponentLoaded(this.hass, "hassio") &&
this.provider this.provider
) { ) {
const response = await this._fetchLogsFunction()( const response = await fetchHassioLogsFollow(
this.hass, this.hass,
this.provider, this.provider,
this._logStreamAborter.signal, this._logStreamAborter.signal,
@ -469,13 +468,6 @@ class ErrorLogCard extends LitElement {
} }
} }
private _fetchLogsFunction = () => {
if (this._boot === 0) {
return fetchHassioLogsFollow;
}
return fetchHassioLogsBootFollow;
};
private _debounceSearch = debounce(() => { private _debounceSearch = debounce(() => {
this._noSearchResults = !this._ansiToHtmlElement?.filterLines(this.filter); this._noSearchResults = !this._ansiToHtmlElement?.filterLines(this.filter);

View File

@ -681,8 +681,12 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
private async _duplicate() { private async _duplicate() {
const result = this._readOnly const result = this._readOnly
? await showConfirmationDialog(this, { ? await showConfirmationDialog(this, {
title: "Migrate script?", title: this.hass.localize(
text: "You can migrate this script, so it can be edited from the UI. After it is migrated and you have saved it, you will have to manually delete your old script from your configuration. Do you want to migrate this script?", "ui.panel.config.script.picker.migrate_script"
),
text: this.hass.localize(
"ui.panel.config.script.picker.migrate_script_description"
),
}) })
: await this.confirmUnsavedChanged(); : await this.confirmUnsavedChanged();
if (result) { if (result) {

View File

@ -59,18 +59,12 @@ export class HuiEnergyDateSelectionCard
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return css` return css`
:host {
ha-card { ha-card {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
} }
.padded {
padding-left: 16px !important;
padding-inline-start: 16px !important;
padding-inline-end: initial !important;
}
`; `;
} }
} }

View File

@ -28,6 +28,7 @@ export class HuiHorizontalStackCard extends HuiStackCard {
css` css`
#root { #root {
display: flex; display: flex;
height: 100%;
gap: var(--horizontal-stack-card-gap, var(--stack-card-gap, 8px)); gap: var(--horizontal-stack-card-gap, var(--stack-card-gap, 8px));
} }
#root > hui-card { #root > hui-card {

View File

@ -64,6 +64,7 @@ const cardConfigStruct = assign(
hours_to_show: optional(number()), hours_to_show: optional(number()),
geo_location_sources: optional(array(geoSourcesConfigStruct)), geo_location_sources: optional(array(geoSourcesConfigStruct)),
auto_fit: optional(boolean()), auto_fit: optional(boolean()),
fit_zones: optional(boolean()),
theme_mode: optional(string()), theme_mode: optional(string()),
}) })
); );

View File

@ -242,8 +242,9 @@ export class GridSection extends LitElement implements LovelaceSectionElement {
min-height: var(--row-height); min-height: var(--row-height);
} }
.container.edit-mode:not(.import-only) { .container.import-only {
border-start-end-radius: 0px; border: none;
padding: 0 !important;
} }
.card { .card {

View File

@ -1,5 +1,11 @@
import { ResizeController } from "@lit-labs/observers/resize-controller"; import { ResizeController } from "@lit-labs/observers/resize-controller";
import { mdiDelete, mdiDrag, mdiPencil, mdiViewGridPlus } from "@mdi/js"; import {
mdiDelete,
mdiDrag,
mdiEyeOff,
mdiPencil,
mdiViewGridPlus,
} from "@mdi/js";
import type { CSSResultGroup, PropertyValues } from "lit"; import type { CSSResultGroup, PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit"; import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
@ -245,6 +251,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
<div class="section imported-cards"> <div class="section imported-cards">
<div class="imported-card-header"> <div class="imported-card-header">
<p class="title"> <p class="title">
<ha-svg-icon .path=${mdiEyeOff}></ha-svg-icon>
${this.hass.localize( ${this.hass.localize(
"ui.panel.lovelace.editor.section.imported_cards_title" "ui.panel.lovelace.editor.section.imported_cards_title"
)} )}
@ -480,9 +487,9 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
} }
.imported-card-header { .imported-card-header {
margin-top: 24px; margin-top: 36px;
padding: 16px 8px; padding: 32px 0 16px 0;
border-top: 2px dashed var(--divider-color); border-top: 4px dotted var(--divider-color);
} }
.imported-card-header .title { .imported-card-header .title {
@ -491,6 +498,11 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
line-height: 24px; line-height: 24px;
--mdc-icon-size: 18px;
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
} }
.imported-card-header .subtitle { .imported-card-header .subtitle {
margin: 0; margin: 0;

View File

@ -1745,8 +1745,8 @@
"answer_generic": "Other controllers" "answer_generic": "Other controllers"
}, },
"google_home": { "google_home": {
"header": "Link Matter app", "header": "Share from Google Home",
"step_1": "Find your device in Google Home. Tap the gear icon to open the device settings.", "step_1": "Find your device in the Google Home app. Tap the gear icon to open the device settings.",
"step_2": "Tap {linked_matter_apps_services}.", "step_2": "Tap {linked_matter_apps_services}.",
"step_3": "Tap {link_apps_services} and choose {home_assistant} from the list.", "step_3": "Tap {link_apps_services} and choose {home_assistant} from the list.",
"linked_matter_apps_services": "Linked Matter apps and services", "linked_matter_apps_services": "Linked Matter apps and services",
@ -1776,8 +1776,8 @@
"code_instructions": "Paste the code you just received from the other controller." "code_instructions": "Paste the code you just received from the other controller."
}, },
"generic": { "generic": {
"header": "Copy setup code", "header": "Enter setup code",
"code_instructions": "Search for the sharing mode in the app of your controller, and activate it. You will get a sharing code, enter that below.", "code_instructions": "Search for the sharing mode in the app of your controller, and activate it. You will get a setup code, enter that below.",
"setup_code": "Setup code" "setup_code": "Setup code"
} }
} }
@ -2792,7 +2792,9 @@
}, },
"empty_header": "Start automating", "empty_header": "Start automating",
"empty_text_1": "Automations make Home Assistant automatically respond to things happening in and around your home.", "empty_text_1": "Automations make Home Assistant automatically respond to things happening in and around your home.",
"empty_text_2": "Automations connect triggers to actions in a ''when trigger then action'' fashion with optional conditions. For example: ''When the sun sets and if {user} is home, then turn on the lights''." "empty_text_2": "Automations connect triggers to actions in a ''when trigger then action'' fashion with optional conditions. For example: ''When the sun sets and if {user} is home, then turn on the lights''.",
"migrate_automation": "Migrate automation?",
"migrate_automation_description": "You can migrate this automation, so it can be edited from the UI. After it is migrated and you have saved it, you will have to manually delete your old automation from your configuration. Do you want to migrate this automation?"
}, },
"dialog_new": { "dialog_new": {
"header": "Create automation", "header": "Create automation",
@ -3215,7 +3217,7 @@
"description": { "description": {
"picker": "If an entity (or attribute) is in a specific state.", "picker": "If an entity (or attribute) is in a specific state.",
"no_entity": "Confirm state", "no_entity": "Confirm state",
"full": "If{hasAttribute, select, \n true { {attribute} of}\n other {}\n} {numberOfEntities, plural,\n zero {an entity is}\n one {{entities} is}\n other {{entities} are}\n} {numberOfStates, plural,\n zero {a state}\n other {{states}}\n}{hasDuration, select, \n true { for {duration}} \n other {}\n }" "full": "If{hasAttribute, select, \n true { {attribute} of}\n other {}\n} {numberOfEntities, plural,\n =0 {an entity is}\n one {{entities} is}\n other {{entities} are}\n} {numberOfStates, plural,\n =0 {a state}\n other {{states}}\n}{hasDuration, select, \n true { for {duration}} \n other {}\n }"
} }
}, },
"sun": { "sun": {
@ -3676,7 +3678,9 @@
"duplicate": "[%key:ui::common::duplicate%]", "duplicate": "[%key:ui::common::duplicate%]",
"empty_header": "Create your first script", "empty_header": "Create your first script",
"empty_text": "A script is a sequence of actions that can be run from a dashboard, an automation, or be triggered by voice. For example, a ''Wake-up routine''' script that gradually turns on the light in the bedroom and opens the blinds after a delay.", "empty_text": "A script is a sequence of actions that can be run from a dashboard, an automation, or be triggered by voice. For example, a ''Wake-up routine''' script that gradually turns on the light in the bedroom and opens the blinds after a delay.",
"search": "Search {number} scripts" "search": "Search {number} scripts",
"migrate_script": "Migrate script?",
"migrate_script_description": "You can migrate this script, so it can be edited from the UI. After it is migrated and you have saved it, you will have to manually delete your old script from your configuration. Do you want to migrate this script?"
}, },
"dialog_new": { "dialog_new": {
"header": "Create script", "header": "Create script",
@ -4115,7 +4119,7 @@
"hidden": "Hidden" "hidden": "Hidden"
}, },
"confirm_rename_entity_ids": "Do you also want to rename the entity IDs of your entities?", "confirm_rename_entity_ids": "Do you also want to rename the entity IDs of your entities?",
"confirm_rename_entity_ids_warning": "This will not change any configuration (like automations, scripts, scenes, dashboards) that is currently using these entities! You will have to update them yourself to use the new entity IDs!", "confirm_rename_entity_ids_warning": "This will not change any configuration (like automations, scripts, scenes, dashboards) that is currently using these entities! You will have to manually edit them yourself to use the new entity IDs!",
"confirm_rename_entity_will_rename": "{count} {count, plural,\n one {entity ID}\n other {entity IDs}\n} will be renamed", "confirm_rename_entity_will_rename": "{count} {count, plural,\n one {entity ID}\n other {entity IDs}\n} will be renamed",
"confirm_rename_new": "New", "confirm_rename_new": "New",
"confirm_rename_old": "Old", "confirm_rename_old": "Old",
@ -5306,7 +5310,7 @@
"share": "Share" "share": "Share"
}, },
"mount_type": { "mount_type": {
"nfs": "Network file share (NFS)", "nfs": "Network File System (NFS)",
"cifs": "Samba/Windows (CIFS)" "cifs": "Samba/Windows (CIFS)"
}, },
"cifs_versions": { "cifs_versions": {
@ -5884,7 +5888,7 @@
}, },
"entities": { "entities": {
"name": "Entities", "name": "Entities",
"show_header_toggle": "Show header toggle?", "show_header_toggle": "Show header toggle",
"toggle": "Toggle entities.", "toggle": "Toggle entities.",
"description": "The Entities card is the most common type of card. It groups items together into lists.", "description": "The Entities card is the most common type of card. It groups items together into lists.",
"special_row": "special row", "special_row": "special row",
@ -5931,9 +5935,9 @@
}, },
"gauge": { "gauge": {
"name": "Gauge", "name": "Gauge",
"needle_gauge": "Display as needle gauge?", "needle_gauge": "Display as needle gauge",
"severity": { "severity": {
"define": "Define severity?", "define": "Define severity",
"green": "Green", "green": "Green",
"red": "Red", "red": "Red",
"yellow": "Yellow" "yellow": "Yellow"
@ -6067,7 +6071,7 @@
"state": "State", "state": "State",
"secondary_info_attribute": "Secondary info attribute", "secondary_info_attribute": "Secondary info attribute",
"search": "Search", "search": "Search",
"state_color": "Color icons based on state?", "state_color": "Show state color",
"suggested_cards": "Suggested cards", "suggested_cards": "Suggested cards",
"other_cards": "Other cards", "other_cards": "Other cards",
"custom_cards": "Custom cards", "custom_cards": "Custom cards",
@ -6104,7 +6108,6 @@
"name": "Map", "name": "Map",
"geo_location_sources": "Geolocation sources", "geo_location_sources": "Geolocation sources",
"no_geo_location_sources": "No geolocation sources available", "no_geo_location_sources": "No geolocation sources available",
"dark_mode": "Dark mode?",
"appearance": "Appearance", "appearance": "Appearance",
"theme_mode": "Theme Mode", "theme_mode": "Theme Mode",
"theme_modes": { "theme_modes": {
@ -7203,6 +7206,7 @@
}, },
"create_account": "Create account", "create_account": "Create account",
"error": { "error": {
"username_not_normalized": "Username can only contain lowercase letters, and can not contain whitespace.",
"password_not_match": "Passwords don't match" "password_not_match": "Passwords don't match"
} }
}, },
@ -7372,6 +7376,7 @@
}, },
"dashboard": { "dashboard": {
"changelog": "Changelog", "changelog": "Changelog",
"current_version": "Current version: {version}",
"cpu_usage": "Add-on CPU usage", "cpu_usage": "Add-on CPU usage",
"ram_usage": "Add-on RAM usage", "ram_usage": "Add-on RAM usage",
"hostname": "Hostname", "hostname": "Hostname",
@ -7472,7 +7477,7 @@
}, },
"watchdog": { "watchdog": {
"title": "Watchdog", "title": "Watchdog",
"description": "This will start the add-on if it crashes" "description": "This will restart the add-on if it crashes"
}, },
"auto_update": { "auto_update": {
"title": "Auto update", "title": "Auto update",

168
yarn.lock
View File

@ -1265,9 +1265,9 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@codemirror/autocomplete@npm:6.18.1": "@codemirror/autocomplete@npm:6.18.2":
version: 6.18.1 version: 6.18.2
resolution: "@codemirror/autocomplete@npm:6.18.1" resolution: "@codemirror/autocomplete@npm:6.18.2"
dependencies: dependencies:
"@codemirror/language": "npm:^6.0.0" "@codemirror/language": "npm:^6.0.0"
"@codemirror/state": "npm:^6.0.0" "@codemirror/state": "npm:^6.0.0"
@ -1278,7 +1278,7 @@ __metadata:
"@codemirror/state": ^6.0.0 "@codemirror/state": ^6.0.0
"@codemirror/view": ^6.0.0 "@codemirror/view": ^6.0.0
"@lezer/common": ^1.0.0 "@lezer/common": ^1.0.0
checksum: 10/3b56ac6c57214e3e50c6ed79c12ac1822e3774afb033e0e4fb98dffd252f5ae64e5bed67dc2ad9cbd5d784373031be90995ddb1b36a10c16a2eef6af832041e2 checksum: 10/35bd17afb53e8c99b1342964616f0bcc13f5f06a5d4e2d9936afdaea61742b1c20b3856d513c5d5676e3a9b6fd95e997c842467d21dfa106845e65ab1720b2f4
languageName: node languageName: node
linkType: hard linkType: hard
@ -1317,14 +1317,14 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@codemirror/search@npm:6.5.6": "@codemirror/search@npm:6.5.7":
version: 6.5.6 version: 6.5.7
resolution: "@codemirror/search@npm:6.5.6" resolution: "@codemirror/search@npm:6.5.7"
dependencies: dependencies:
"@codemirror/state": "npm:^6.0.0" "@codemirror/state": "npm:^6.0.0"
"@codemirror/view": "npm:^6.0.0" "@codemirror/view": "npm:^6.0.0"
crelt: "npm:^1.0.5" crelt: "npm:^1.0.5"
checksum: 10/6668a34b4617e909617d3d831627d74b7a7985e8cd86d396bfcb3e86262f2310fc029fd6c846f1b8f1e6768e75985c9f1b0b18b31e05341f06b5b75c1ffde38d checksum: 10/0a4c5e23c42231ffb829513940ee43a630585b4277fa8cc919a947f3821c9c2dc095d334bb0e4d51b3ebb50739a34a81ddbcc39ca9c1f6f935fdaa51a86661bf
languageName: node languageName: node
linkType: hard linkType: hard
@ -3944,7 +3944,27 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/estree@npm:*, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.5": "@types/eslint-scope@npm:^3.7.7":
version: 3.7.7
resolution: "@types/eslint-scope@npm:3.7.7"
dependencies:
"@types/eslint": "npm:*"
"@types/estree": "npm:*"
checksum: 10/e2889a124aaab0b89af1bab5959847c5bec09809209255de0e63b9f54c629a94781daa04adb66bffcdd742f5e25a17614fb933965093c0eea64aacda4309380e
languageName: node
linkType: hard
"@types/eslint@npm:*":
version: 9.6.1
resolution: "@types/eslint@npm:9.6.1"
dependencies:
"@types/estree": "npm:*"
"@types/json-schema": "npm:*"
checksum: 10/719fcd255760168a43d0e306ef87548e1e15bffe361d5f4022b0f266575637acc0ecb85604ac97879ee8ae83c6a6d0613b0ed31d0209ddf22a0fe6d608fc56fe
languageName: node
linkType: hard
"@types/estree@npm:*, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.6":
version: 1.0.6 version: 1.0.6
resolution: "@types/estree@npm:1.0.6" resolution: "@types/estree@npm:1.0.6"
checksum: 10/9d35d475095199c23e05b431bcdd1f6fec7380612aed068b14b2a08aa70494de8a9026765a5a91b1073f636fb0368f6d8973f518a31391d519e20c59388ed88d checksum: 10/9d35d475095199c23e05b431bcdd1f6fec7380612aed068b14b2a08aa70494de8a9026765a5a91b1073f636fb0368f6d8973f518a31391d519e20c59388ed88d
@ -4090,7 +4110,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": "@types/json-schema@npm:*, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9":
version: 7.0.15 version: 7.0.15
resolution: "@types/json-schema@npm:7.0.15" resolution: "@types/json-schema@npm:7.0.15"
checksum: 10/1a3c3e06236e4c4aab89499c428d585527ce50c24fe8259e8b3926d3df4cfbbbcf306cfc73ddfb66cbafc973116efd15967020b0f738f63e09e64c7d260519e7 checksum: 10/1a3c3e06236e4c4aab89499c428d585527ce50c24fe8259e8b3926d3df4cfbbbcf306cfc73ddfb66cbafc973116efd15967020b0f738f63e09e64c7d260519e7
@ -5187,15 +5207,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"acorn-import-attributes@npm:^1.9.5":
version: 1.9.5
resolution: "acorn-import-attributes@npm:1.9.5"
peerDependencies:
acorn: ^8
checksum: 10/8bfbfbb6e2467b9b47abb4d095df717ab64fce2525da65eabee073e85e7975fb3a176b6c8bba17c99a7d8ede283a10a590272304eb54a93c4aa1af9790d47a8b
languageName: node
linkType: hard
"acorn-jsx@npm:^5.3.2": "acorn-jsx@npm:^5.3.2":
version: 5.3.2 version: 5.3.2
resolution: "acorn-jsx@npm:5.3.2" resolution: "acorn-jsx@npm:5.3.2"
@ -5205,12 +5216,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"acorn@npm:^8.5.0, acorn@npm:^8.7.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": "acorn@npm:^8.14.0, acorn@npm:^8.5.0, acorn@npm:^8.8.2, acorn@npm:^8.9.0":
version: 8.13.0 version: 8.14.0
resolution: "acorn@npm:8.13.0" resolution: "acorn@npm:8.14.0"
bin: bin:
acorn: bin/acorn acorn: bin/acorn
checksum: 10/33e3a03114b02b3bc5009463b3d9549b31a90ee38ebccd5e66515830a02acf62a90edcc12abfb6c9fb3837b6c17a3ec9b72b3bf52ac31d8ad8248a4af871e0f5 checksum: 10/6df29c35556782ca9e632db461a7f97947772c6c1d5438a81f0c873a3da3a792487e83e404d1c6c25f70513e91aa18745f6eafb1fcc3a43ecd1920b21dd173d2
languageName: node languageName: node
linkType: hard linkType: hard
@ -5881,7 +5892,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"browserslist@npm:^4.21.10, browserslist@npm:^4.23.3, browserslist@npm:^4.24.0": "browserslist@npm:^4.23.3, browserslist@npm:^4.24.0":
version: 4.24.0 version: 4.24.0
resolution: "browserslist@npm:4.24.0" resolution: "browserslist@npm:4.24.0"
dependencies: dependencies:
@ -6540,10 +6551,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"core-js@npm:3.38.1": "core-js@npm:3.39.0":
version: 3.38.1 version: 3.39.0
resolution: "core-js@npm:3.38.1" resolution: "core-js@npm:3.39.0"
checksum: 10/3c25fdf0b2595ed37ceb305213a61e2cf26185f628455e99d1c736dda5f69e2de4de7126e6a1da136f54260c4fcc982c4215e37b5a618790a597930f854c0a37 checksum: 10/a3d34e669783dfc878e545f1983f60d9ff48a3867cd1d7ff8839b849e053002a208c7c14a5ca354b8e0b54982901e2f83dc87c3d9b95de0a94b4071d1c74e5f6
languageName: node languageName: node
linkType: hard linkType: hard
@ -8716,11 +8727,11 @@ __metadata:
"@babel/runtime": "npm:7.26.0" "@babel/runtime": "npm:7.26.0"
"@braintree/sanitize-url": "npm:7.1.0" "@braintree/sanitize-url": "npm:7.1.0"
"@bundle-stats/plugin-webpack-filter": "npm:4.16.0" "@bundle-stats/plugin-webpack-filter": "npm:4.16.0"
"@codemirror/autocomplete": "npm:6.18.1" "@codemirror/autocomplete": "npm:6.18.2"
"@codemirror/commands": "npm:6.7.1" "@codemirror/commands": "npm:6.7.1"
"@codemirror/language": "npm:6.10.3" "@codemirror/language": "npm:6.10.3"
"@codemirror/legacy-modes": "npm:6.4.1" "@codemirror/legacy-modes": "npm:6.4.1"
"@codemirror/search": "npm:6.5.6" "@codemirror/search": "npm:6.5.7"
"@codemirror/state": "npm:6.4.1" "@codemirror/state": "npm:6.4.1"
"@codemirror/view": "npm:6.34.1" "@codemirror/view": "npm:6.34.1"
"@egjs/hammerjs": "npm:2.0.17" "@egjs/hammerjs": "npm:2.0.17"
@ -8827,7 +8838,7 @@ __metadata:
chart.js: "npm:4.4.6" chart.js: "npm:4.4.6"
color-name: "npm:2.0.0" color-name: "npm:2.0.0"
comlink: "npm:4.4.1" comlink: "npm:4.4.1"
core-js: "npm:3.38.1" core-js: "npm:3.39.0"
cropperjs: "npm:1.6.2" cropperjs: "npm:1.6.2"
date-fns: "npm:4.1.0" date-fns: "npm:4.1.0"
date-fns-tz: "npm:3.2.0" date-fns-tz: "npm:3.2.0"
@ -8877,7 +8888,7 @@ __metadata:
map-stream: "npm:0.0.7" map-stream: "npm:0.0.7"
marked: "npm:14.1.3" marked: "npm:14.1.3"
memoize-one: "npm:6.0.0" memoize-one: "npm:6.0.0"
mocha: "npm:10.7.3" mocha: "npm:10.8.2"
node-vibrant: "npm:3.2.1-alpha.1" node-vibrant: "npm:3.2.1-alpha.1"
object-hash: "npm:3.0.0" object-hash: "npm:3.0.0"
open: "npm:10.1.0" open: "npm:10.1.0"
@ -8913,7 +8924,7 @@ __metadata:
vis-network: "npm:9.1.9" vis-network: "npm:9.1.9"
vue: "npm:2.7.16" vue: "npm:2.7.16"
vue2-daterange-picker: "npm:0.6.8" vue2-daterange-picker: "npm:0.6.8"
webpack: "npm:5.95.0" webpack: "npm:5.96.1"
webpack-cli: "npm:5.1.4" webpack-cli: "npm:5.1.4"
webpack-dev-server: "npm:5.1.0" webpack-dev-server: "npm:5.1.0"
webpack-manifest-plugin: "npm:5.0.0" webpack-manifest-plugin: "npm:5.0.0"
@ -8921,12 +8932,12 @@ __metadata:
webpackbar: "npm:6.0.1" webpackbar: "npm:6.0.1"
weekstart: "npm:2.0.0" weekstart: "npm:2.0.0"
workbox-build: "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch" workbox-build: "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
workbox-cacheable-response: "npm:7.1.0" workbox-cacheable-response: "npm:7.3.0"
workbox-core: "npm:7.1.0" workbox-core: "npm:7.3.0"
workbox-expiration: "npm:7.1.0" workbox-expiration: "npm:7.3.0"
workbox-precaching: "npm:7.1.0" workbox-precaching: "npm:7.3.0"
workbox-routing: "npm:7.1.0" workbox-routing: "npm:7.3.0"
workbox-strategies: "npm:7.1.0" workbox-strategies: "npm:7.3.0"
xss: "npm:1.0.15" xss: "npm:1.0.15"
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@ -10966,9 +10977,9 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"mocha@npm:10.7.3": "mocha@npm:10.8.2":
version: 10.7.3 version: 10.8.2
resolution: "mocha@npm:10.7.3" resolution: "mocha@npm:10.8.2"
dependencies: dependencies:
ansi-colors: "npm:^4.1.3" ansi-colors: "npm:^4.1.3"
browser-stdout: "npm:^1.3.1" browser-stdout: "npm:^1.3.1"
@ -10993,7 +11004,7 @@ __metadata:
bin: bin:
_mocha: bin/_mocha _mocha: bin/_mocha
mocha: bin/mocha.js mocha: bin/mocha.js
checksum: 10/5757aeb320df2507338bfba41731070ce16d27177c5876672fff4bcc4f7b7bcf1afe6ec761bfded43a5d28032d7b797b8b905b5b44c9420203f3ee71457732c1 checksum: 10/903bbffcb195ef9d36b27db54e3462c5486de1397289e0953735b3530397a139336c452bcf5188c663496c660d2285bbb6c7213290d36d536ad647b6145cb917
languageName: node languageName: node
linkType: hard linkType: hard
@ -14678,17 +14689,17 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"webpack@npm:5.95.0": "webpack@npm:5.96.1":
version: 5.95.0 version: 5.96.1
resolution: "webpack@npm:5.95.0" resolution: "webpack@npm:5.96.1"
dependencies: dependencies:
"@types/estree": "npm:^1.0.5" "@types/eslint-scope": "npm:^3.7.7"
"@types/estree": "npm:^1.0.6"
"@webassemblyjs/ast": "npm:^1.12.1" "@webassemblyjs/ast": "npm:^1.12.1"
"@webassemblyjs/wasm-edit": "npm:^1.12.1" "@webassemblyjs/wasm-edit": "npm:^1.12.1"
"@webassemblyjs/wasm-parser": "npm:^1.12.1" "@webassemblyjs/wasm-parser": "npm:^1.12.1"
acorn: "npm:^8.7.1" acorn: "npm:^8.14.0"
acorn-import-attributes: "npm:^1.9.5" browserslist: "npm:^4.24.0"
browserslist: "npm:^4.21.10"
chrome-trace-event: "npm:^1.0.2" chrome-trace-event: "npm:^1.0.2"
enhanced-resolve: "npm:^5.17.1" enhanced-resolve: "npm:^5.17.1"
es-module-lexer: "npm:^1.2.1" es-module-lexer: "npm:^1.2.1"
@ -14710,7 +14721,7 @@ __metadata:
optional: true optional: true
bin: bin:
webpack: bin/webpack.js webpack: bin/webpack.js
checksum: 10/0377ad3a550b041f26237c96fb55754625b0ce6bae83c1c2447e3262ad056b0b0ad770dcbb92b59f188e9a2bd56155ce910add17dcf023cfbe78bdec774380c1 checksum: 10/d3419ffd198252e1d0301bd0c072cee93172f3e47937c745aa8202691d2f5d529d4ba4a1965d1450ad89a1bcd3c1f70ae09e57232b0d01dd38d69c1060e964d5
languageName: node languageName: node
linkType: hard linkType: hard
@ -14990,6 +15001,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"workbox-cacheable-response@npm:7.3.0":
version: 7.3.0
resolution: "workbox-cacheable-response@npm:7.3.0"
dependencies:
workbox-core: "npm:7.3.0"
checksum: 10/44cd7bc26e509ca96b1b84e3ff5964296efa645853f114f39789d21c0a214ca5fc047259910b303e220bb4052155cddc5639993fcee076fac496b4895ff17a15
languageName: node
linkType: hard
"workbox-core@npm:7.1.0": "workbox-core@npm:7.1.0":
version: 7.1.0 version: 7.1.0
resolution: "workbox-core@npm:7.1.0" resolution: "workbox-core@npm:7.1.0"
@ -14997,6 +15017,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"workbox-core@npm:7.3.0":
version: 7.3.0
resolution: "workbox-core@npm:7.3.0"
checksum: 10/228fb7018a0568c329e21d47d84980f93ebfef9b1eb3f40ddc3516ca6ae58d51dc7ca4dddc829332775b59a3079e62d105c5e1c5c312805d177b963f8bf54393
languageName: node
linkType: hard
"workbox-expiration@npm:7.1.0": "workbox-expiration@npm:7.1.0":
version: 7.1.0 version: 7.1.0
resolution: "workbox-expiration@npm:7.1.0" resolution: "workbox-expiration@npm:7.1.0"
@ -15007,6 +15034,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"workbox-expiration@npm:7.3.0":
version: 7.3.0
resolution: "workbox-expiration@npm:7.3.0"
dependencies:
idb: "npm:^7.0.1"
workbox-core: "npm:7.3.0"
checksum: 10/83e021d700e521a65a89907679d1a580aacc0419428286910ec7c6b0a538326f71f05566434f666ebf6c9fbe819ef3ea81428df1d868f9ea92527afe5d11152d
languageName: node
linkType: hard
"workbox-google-analytics@npm:7.1.0": "workbox-google-analytics@npm:7.1.0":
version: 7.1.0 version: 7.1.0
resolution: "workbox-google-analytics@npm:7.1.0" resolution: "workbox-google-analytics@npm:7.1.0"
@ -15039,6 +15076,17 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"workbox-precaching@npm:7.3.0":
version: 7.3.0
resolution: "workbox-precaching@npm:7.3.0"
dependencies:
workbox-core: "npm:7.3.0"
workbox-routing: "npm:7.3.0"
workbox-strategies: "npm:7.3.0"
checksum: 10/d14135c471a45de36438c40eed7cb7157cdb336d4216a775486c6307d1ac316794d64231c2e2d0a4c313bb4a4fec623ab77e391cc458b4f2afa64e2487acb2e8
languageName: node
linkType: hard
"workbox-range-requests@npm:7.1.0": "workbox-range-requests@npm:7.1.0":
version: 7.1.0 version: 7.1.0
resolution: "workbox-range-requests@npm:7.1.0" resolution: "workbox-range-requests@npm:7.1.0"
@ -15071,6 +15119,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"workbox-routing@npm:7.3.0":
version: 7.3.0
resolution: "workbox-routing@npm:7.3.0"
dependencies:
workbox-core: "npm:7.3.0"
checksum: 10/0d729f9c5cfc5754404ac1f7b729c7740ddc806203792701ac642151fbec939b4aa0fb289eab2295e49180e8154ad9bb1380effb7e0f0362163b79db4291dba7
languageName: node
linkType: hard
"workbox-strategies@npm:7.1.0": "workbox-strategies@npm:7.1.0":
version: 7.1.0 version: 7.1.0
resolution: "workbox-strategies@npm:7.1.0" resolution: "workbox-strategies@npm:7.1.0"
@ -15080,6 +15137,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"workbox-strategies@npm:7.3.0":
version: 7.3.0
resolution: "workbox-strategies@npm:7.3.0"
dependencies:
workbox-core: "npm:7.3.0"
checksum: 10/61ba672075ef8aaa70ad9221460dab80a7d8920e324e14137460f26ebe8b137e5589fb75c664e0efeaf4402e3d8435a9b1818f9a9c61f88863c0e0315af337e7
languageName: node
linkType: hard
"workbox-streams@npm:7.1.0": "workbox-streams@npm:7.1.0":
version: 7.1.0 version: 7.1.0
resolution: "workbox-streams@npm:7.1.0" resolution: "workbox-streams@npm:7.1.0"