Compare commits

..

23 Commits

Author SHA1 Message Date
Bram Kragten
38b7bd18bb Bumped version to 20251127.0 2025-11-27 17:06:57 +01:00
Wendelin
a00e944a35 Add TCA by target sort like item collections (#28192) 2025-11-27 17:06:30 +01:00
Petar Petrov
481569804e Fix water sankey calculation to include total supply from sources (#28191) 2025-11-27 17:06:29 +01:00
Paul Bottein
a1d7e270ff Add hint to reorder areas and floors (#28189) 2025-11-27 17:06:28 +01:00
Wendelin
225ccf1d2f Fix lab automations icons and sidebar width (#28184)
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2025-11-27 17:06:27 +01:00
Wendelin
4a5e1f9f3f "Add TCA" dialog desktop height to 800px (#28182) 2025-11-27 17:06:26 +01:00
Wendelin
b27b7210fd Show hidden entities in target tree (#28181)
* Show hidden entities in target tree

* Fix types
2025-11-27 17:06:25 +01:00
Petar Petrov
acd5181449 Fix sankey chart resizing (#28180) 2025-11-27 17:06:24 +01:00
Bram Kragten
b6b2d03a80 Always store token when using develop and serve (#28179) 2025-11-27 17:06:22 +01:00
Paul Bottein
7aee2b7cb7 Fix labs back button (#28174) 2025-11-27 17:06:21 +01:00
Paul Bottein
df1914cb7a Fix disabled dashboard picker when no custom dashboard (#28172) 2025-11-27 17:06:20 +01:00
Paul Bottein
6706d5904d Fix box shadow for sidebar tabs (#28170) 2025-11-27 17:06:19 +01:00
Wendelin
221aefd764 Fix automation add TCA autofocus (#28168)
Fix automation add tca autofocus
2025-11-27 17:06:18 +01:00
Paul Bottein
670057e8e6 Restore sidebar view when clicking back (#28167) 2025-11-27 17:06:17 +01:00
Wendelin
427e46201c Fix add condition default tab and blank styles (#28166) 2025-11-27 17:06:16 +01:00
Petar Petrov
fd1240f335 Refactor power sankey hierarchy to handle devices with not power sensor (#28164) 2025-11-27 17:06:15 +01:00
Petar Petrov
aa7670cb59 Disable axis pointer on the energy devices bar chart to fix refresh issues on touch devices (#28163) 2025-11-27 17:06:14 +01:00
Petar Petrov
468139229c Handle grouping by floor and area in power sankey card (#28162) 2025-11-27 17:06:13 +01:00
Simon Lamon
39752f0e3f Don't show more info for untracked consumption (#28151) 2025-11-27 17:06:12 +01:00
Petar Petrov
4d850d067f Replace gauges with energy usage graph in energy overview (#28150) 2025-11-27 17:06:10 +01:00
Paul Bottein
bcae64df88 Use hui-root for panel energy (#28149)
* Use hui-root for panel energy

* Review feedback

* Set empty prefs
2025-11-27 17:06:09 +01:00
Iván Pereira
690fd5a061 Fix hide sidebar tooltip on touchend events (#28042)
* fix: hide sidebar tooltip on touchend events

* Add a comment recommended by Copilot

* Clear timeouts id in disconnectedCallback
2025-11-27 17:06:08 +01:00
Bram Kragten
ac56c6df9a Bumped version to 20251126.0 2025-11-26 16:11:20 +01:00
18 changed files with 386 additions and 459 deletions

View File

@@ -89,8 +89,8 @@
"@thomasloven/round-slider": "0.6.0",
"@tsparticles/engine": "3.9.1",
"@tsparticles/preset-links": "3.2.0",
"@vaadin/combo-box": "24.9.6",
"@vaadin/vaadin-themable-mixin": "24.9.6",
"@vaadin/combo-box": "24.9.5",
"@vaadin/vaadin-themable-mixin": "24.9.5",
"@vibrant/color": "4.0.0",
"@vue/web-component-wrapper": "1.3.0",
"@webcomponents/scoped-custom-element-registry": "0.0.10",
@@ -152,13 +152,13 @@
"@babel/helper-define-polyfill-provider": "0.6.5",
"@babel/plugin-transform-runtime": "7.28.5",
"@babel/preset-env": "7.28.5",
"@bundle-stats/plugin-webpack-filter": "4.21.7",
"@bundle-stats/plugin-webpack-filter": "4.21.6",
"@lokalise/node-api": "15.4.0",
"@octokit/auth-oauth-device": "8.0.3",
"@octokit/plugin-retry": "8.0.3",
"@octokit/rest": "22.0.1",
"@rsdoctor/rspack-plugin": "1.3.11",
"@rspack/core": "1.6.5",
"@rspack/core": "1.6.4",
"@rspack/dev-server": "1.1.4",
"@types/babel__plugin-transform-runtime": "7.9.5",
"@types/chromecast-caf-receiver": "6.0.22",
@@ -178,7 +178,7 @@
"@types/tar": "6.1.13",
"@types/ua-parser-js": "0.7.39",
"@types/webspeechapi": "0.0.29",
"@vitest/coverage-v8": "4.0.14",
"@vitest/coverage-v8": "4.0.13",
"babel-loader": "10.0.0",
"babel-plugin-template-html-minifier": "4.1.0",
"browserslist-useragent-regexp": "4.1.3",
@@ -194,7 +194,7 @@
"eslint-plugin-wc": "3.0.2",
"fancy-log": "2.0.0",
"fs-extra": "11.3.2",
"glob": "13.0.0",
"glob": "12.0.0",
"gulp": "5.0.1",
"gulp-brotli": "3.0.0",
"gulp-json-transform": "0.5.0",
@@ -217,9 +217,9 @@
"terser-webpack-plugin": "5.3.14",
"ts-lit-plugin": "2.0.2",
"typescript": "5.9.3",
"typescript-eslint": "8.48.0",
"typescript-eslint": "8.47.0",
"vite-tsconfig-paths": "5.1.4",
"vitest": "4.0.14",
"vitest": "4.0.13",
"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"

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
version = "20251029.0"
version = "20251127.0"
license = "Apache-2.0"
license-files = ["LICENSE*"]
description = "The Home Assistant frontend"

View File

@@ -838,10 +838,10 @@ export class HaDataTable extends LitElement {
} else if (this.sortDirection === "asc") {
this.sortDirection = "desc";
} else {
this.sortDirection = "asc";
this.sortDirection = null;
}
this.sortColumn = columnId;
this.sortColumn = this.sortDirection === null ? undefined : columnId;
fireEvent(this, "sorting-changed", {
column: columnId,

View File

@@ -186,6 +186,7 @@ export class HaIcon extends LitElement {
static styles = css`
:host {
display: flex;
fill: currentcolor;
}
`;

View File

@@ -73,8 +73,6 @@ export class HaLanguagePicker extends LitElement {
@property({ type: Boolean }) public required = false;
@property() public helper?: string;
@property({ attribute: "native-name", type: Boolean })
public nativeName = false;
@@ -137,7 +135,6 @@ export class HaLanguagePicker extends LitElement {
.value=${value}
.valueRenderer=${this._valueRenderer}
.disabled=${this.disabled}
.helper=${this.helper}
.getItems=${this._getItems}
@value-changed=${this._changed}
hide-clear-icon

View File

@@ -88,7 +88,8 @@ export class HaMarkdown extends LitElement {
}
ol,
ul {
padding-inline-start: 1rem;
list-style-position: inside;
padding-inline-start: 0;
}
li {
&:has(input[type="checkbox"]) {
@@ -139,19 +140,16 @@ export class HaMarkdown extends LitElement {
margin: var(--ha-space-4) 0;
}
table {
border-collapse: var(--markdown-table-border-collapse, collapse);
}
div:has(> table) {
overflow: auto;
border-collapse: collapse;
display: block;
overflow-x: auto;
}
th {
text-align: start;
}
td,
th {
border-width: var(--markdown-table-border-width, 1px);
border-style: var(--markdown-table-border-style, solid);
border-color: var(--markdown-table-border-color, var(--divider-color));
border: 1px solid var(--markdown-table-border-color, transparent);
padding: 0.25em 0.5em;
}
blockquote {

View File

@@ -103,8 +103,8 @@ export class HaPickerField extends LitElement {
--md-list-item-two-line-container-height: 56px;
--md-list-item-top-space: 0px;
--md-list-item-bottom-space: 0px;
--md-list-item-leading-space: var(--ha-space-4);
--md-list-item-trailing-space: var(--ha-space-2);
--md-list-item-leading-space: 8px;
--md-list-item-trailing-space: 8px;
--ha-md-list-item-gap: var(--ha-space-2);
/* Remove the default focus ring */
--md-focus-ring-width: 0px;

View File

@@ -53,7 +53,6 @@ export type DialogWidth = "small" | "medium" | "large" | "full";
* @cssprop --dialog-surface-margin-top - Top margin for the dialog surface.
*
* @attr {boolean} open - Controls the dialog open state.
* @attr {("alert"|"standard")} type - Dialog type. Defaults to "standard".
* @attr {("small"|"medium"|"large"|"full")} width - Preferred dialog width preset. Defaults to "medium".
* @attr {boolean} prevent-scrim-close - Prevents closing the dialog by clicking the scrim/overlay. Defaults to false.
* @attr {string} header-title - Header title text. If not set, the headerTitle slot is used.
@@ -85,9 +84,6 @@ export class HaWaDialog extends LitElement {
@property({ type: Boolean, reflect: true })
public open = false;
@property({ reflect: true })
public type: "alert" | "standard" = "standard";
@property({ type: String, reflect: true, attribute: "width" })
public width: DialogWidth = "medium";
@@ -204,7 +200,18 @@ export class HaWaDialog extends LitElement {
haStyleScrollbar,
css`
wa-dialog {
--full-width: var(--ha-dialog-width-full, min(95vw, var(--safe-width)));
--full-width: var(
--ha-dialog-width-full,
min(
95vw,
calc(
100vw - var(--safe-area-inset-left, var(--ha-space-0)) - var(
--safe-area-inset-right,
var(--ha-space-0)
)
)
)
);
--width: min(var(--ha-dialog-width-md, 580px), var(--full-width));
--spacing: var(--dialog-content-padding, var(--ha-space-6));
--show-duration: var(--ha-dialog-show-duration, 200ms);
@@ -221,7 +228,8 @@ export class HaWaDialog extends LitElement {
--ha-dialog-border-radius,
var(--ha-border-radius-3xl)
);
max-width: var(--ha-dialog-max-width, var(--safe-width));
max-width: var(--ha-dialog-max-width, 100vw);
max-width: var(--ha-dialog-max-width, 100svw);
}
:host([width="small"]) wa-dialog {
@@ -241,57 +249,34 @@ export class HaWaDialog extends LitElement {
max-width: var(--width, var(--full-width));
max-height: var(
--ha-dialog-max-height,
calc(var(--safe-height) - var(--ha-space-20))
calc(100% - var(--ha-space-20))
);
min-height: var(--ha-dialog-min-height);
position: var(--dialog-surface-position, relative);
margin-top: var(--dialog-surface-margin-top, auto);
/* Used to offset the dialog from the safe areas when space is limited */
transform: translate(
calc(
var(--safe-area-offset-left, var(--ha-space-0)) - var(
--safe-area-offset-right,
var(--ha-space-0)
)
),
calc(
var(--safe-area-offset-top, var(--ha-space-0)) - var(
--safe-area-offset-bottom,
var(--ha-space-0)
)
)
);
display: flex;
flex-direction: column;
overflow: hidden;
}
@media all and (max-width: 450px), all and (max-height: 500px) {
:host([type="standard"]) {
:host {
--ha-dialog-border-radius: var(--ha-space-0);
}
wa-dialog {
/* Make the container fill the whole screen width and not the safe width */
--full-width: var(--ha-dialog-width-full, 100vw);
--width: var(--full-width);
}
wa-dialog {
--full-width: var(--ha-dialog-width-full, 100vw);
}
wa-dialog::part(dialog) {
/* Make the dialog fill the whole screen height and not the safe height */
min-height: var(--ha-dialog-min-height, 100vh);
min-height: var(--ha-dialog-min-height, 100dvh);
max-height: var(--ha-dialog-max-height, 100vh);
max-height: var(--ha-dialog-max-height, 100dvh);
margin-top: 0;
margin-bottom: 0;
/* Use safe area as padding instead of the container size */
padding-top: var(--safe-area-inset-top);
padding-bottom: var(--safe-area-inset-bottom);
padding-left: var(--safe-area-inset-left);
padding-right: var(--safe-area-inset-right);
/* Reset the transform to center the dialog */
transform: none;
}
wa-dialog::part(dialog) {
min-height: var(--ha-dialog-min-height, 100vh);
min-height: var(--ha-dialog-min-height, 100svh);
max-height: var(--ha-dialog-max-height, 100vh);
max-height: var(--ha-dialog-max-height, 100svh);
padding-top: var(--safe-area-inset-top, var(--ha-space-0));
padding-bottom: var(--safe-area-inset-bottom, var(--ha-space-0));
padding-left: var(--safe-area-inset-left, var(--ha-space-0));
padding-right: var(--safe-area-inset-right, var(--ha-space-0));
}
}

View File

@@ -1,7 +1,6 @@
import { mdiAlertOutline, mdiClose } from "@mdi/js";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ifDefined } from "lit/directives/if-defined";
import { fireEvent } from "../../common/dom/fire_event";
import "../../components/ha-button";
@@ -65,7 +64,6 @@ class DialogBox extends LitElement {
<ha-wa-dialog
.hass=${this.hass}
.open=${this._open}
type=${confirmPrompt ? "alert" : "standard"}
?prevent-scrim-close=${confirmPrompt}
@closed=${this._dialogClosed}
aria-labelledby="dialog-box-title"
@@ -81,11 +79,7 @@ class DialogBox extends LitElement {
></ha-icon-button
></slot>`
: nothing}
<span
class=${classMap({ title: true, alert: confirmPrompt })}
slot="title"
id="dialog-box-title"
>
<span slot="title" id="dialog-box-title">
${this._params.warning
? html`<ha-svg-icon
.path=${mdiAlertOutline}
@@ -205,14 +199,6 @@ class DialogBox extends LitElement {
ha-textfield {
width: 100%;
}
.title.alert {
padding: 0 var(--ha-space-2);
}
@media all and (min-width: 450px) and (min-height: 500px) {
.title.alert {
padding: 0 var(--ha-space-1);
}
}
`;
}

View File

@@ -621,9 +621,9 @@ export class HaTabsSubpageDataTable extends KeyboardShortcutMixin(LitElement) {
} else if (this._sortDirection === "asc") {
this._sortDirection = "desc";
} else {
this._sortDirection = "asc";
this._sortDirection = null;
}
this._sortColumn = columnId;
this._sortColumn = this._sortDirection === null ? undefined : columnId;
fireEvent(this, "sorting-changed", {
column: columnId,

View File

@@ -35,8 +35,6 @@ import type { HASSDomEvent } from "../../../common/dom/fire_event";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { navigate } from "../../../common/navigate";
import { slugify } from "../../../common/string/slugify";
import "../../../components/ha-tooltip";
import type { LocalizeFunc } from "../../../common/translations/localize";
import {
hasRejectedItems,
@@ -329,19 +327,14 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
const date = new Date(automation.last_triggered);
const now = new Date();
const dayDifference = differenceInDays(now, date);
const formattedTime = formatShortDateTimeWithConditionalYear(
date,
this.hass.locale,
this.hass.config
);
const elementId = "last-triggered-" + slugify(automation.entity_id);
return html`
${dayDifference > 3
? formattedTime
: html`
<ha-tooltip for=${elementId}>${formattedTime}</ha-tooltip>
<span id=${elementId}>${relativeTime(date, locale)}</span>
`}
? formatShortDateTimeWithConditionalYear(
date,
this.hass.locale,
this.hass.config
)
: relativeTime(date, locale)}
`;
},
},

View File

@@ -24,7 +24,7 @@ import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { computeCssColor } from "../../../common/color/compute-color";
import { formatShortDateTimeWithConditionalYear } from "../../../common/datetime/format_date_time";
import { formatShortDateTime } from "../../../common/datetime/format_date_time";
import { relativeTime } from "../../../common/datetime/relative_time";
import { storage } from "../../../common/decorators/storage";
import type { HASSDomEvent } from "../../../common/dom/fire_event";
@@ -301,21 +301,10 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
const date = new Date(scene.state);
const now = new Date();
const dayDifference = differenceInDays(now, date);
const formattedTime = formatShortDateTimeWithConditionalYear(
date,
this.hass.locale,
this.hass.config
);
const elementId = "last-activated-" + slugify(scene.entity_id);
return html`
${dayDifference > 3
? formattedTime
: html`
<ha-tooltip for=${elementId}>${formattedTime}</ha-tooltip>
<span id=${elementId}
>${relativeTime(date, this.hass.locale)}</span
>
`}
? formatShortDateTime(date, this.hass.locale, this.hass.config)
: relativeTime(date, this.hass.locale)}
`;
},
},

View File

@@ -33,8 +33,6 @@ import type { HASSDomEvent } from "../../../common/dom/fire_event";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { navigate } from "../../../common/navigate";
import { slugify } from "../../../common/string/slugify";
import "../../../components/ha-tooltip";
import type { LocalizeFunc } from "../../../common/translations/localize";
import {
hasRejectedItems,
@@ -304,27 +302,19 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
sortable: true,
title: localize("ui.card.automation.last_triggered"),
template: (script) => {
if (!script.last_triggered) {
return this.hass.localize("ui.components.relative_time.never");
}
const date = new Date(script.last_triggered);
const now = new Date();
const dayDifference = differenceInDays(now, date);
const formattedTime = formatShortDateTimeWithConditionalYear(
date,
this.hass.locale,
this.hass.config
);
const elementId = "last-triggered-" + slugify(script.entity_id);
return html`
${dayDifference > 3
? formattedTime
: html`
<ha-tooltip for=${elementId}>${formattedTime}</ha-tooltip>
<span id=${elementId}
>${relativeTime(date, this.hass.locale)}</span
>
`}
${script.last_triggered
? dayDifference > 3
? formatShortDateTimeWithConditionalYear(
date,
this.hass.locale,
this.hass.config
)
: relativeTime(date, this.hass.locale)
: this.hass.localize("ui.components.relative_time.never")}
`;
},
},

View File

@@ -547,9 +547,9 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
} else if (this._sortDirection === "asc") {
this._sortDirection = "desc";
} else {
this._sortDirection = "asc";
this._sortDirection = null;
}
this._sortColumn = columnId;
this._sortColumn = this._sortDirection === null ? undefined : columnId;
}
private _handleGroupBy(ev) {

View File

@@ -514,7 +514,8 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
.wrapper.narrow hui-view-sidebar {
grid-column: 1 / -1;
padding-bottom: calc(
var(--ha-space-14) + var(--ha-space-3) + var(--safe-area-inset-bottom)
var(--ha-space-4) + 56px + var(--ha-space-4) +
env(safe-area-inset-bottom)
);
}
@@ -524,7 +525,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
.mobile-tabs {
position: fixed;
bottom: calc(var(--ha-space-3) + var(--safe-area-inset-bottom));
bottom: calc(var(--ha-space-3) + env(safe-area-inset-bottom));
left: 50%;
transform: translateX(-50%);
padding: 0;
@@ -565,7 +566,8 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
.wrapper.narrow.has-sidebar .content {
padding-bottom: calc(
var(--ha-space-14) + var(--ha-space-3) + var(--safe-area-inset-bottom)
var(--ha-space-4) + 56px + var(--ha-space-4) +
env(safe-area-inset-bottom)
);
}

View File

@@ -55,18 +55,6 @@ const renderMarkdown = async (
marked.setOptions(markedOptions);
marked.use({
renderer: {
table(...args) {
const defaultRenderer = new marked.Renderer();
// Wrap the table with block element because the property 'overflow'
// cannot be applied to elements of display type 'table'.
// https://www.w3.org/TR/css-overflow-3/#overflow-control
return `<div>${defaultRenderer.table.apply(this, args)}</div>`;
},
},
});
const tokens = marked.lexer(content);
return tokens.map((token) =>
filterXSS(marked.parser([token]), {

View File

@@ -27,27 +27,14 @@ export const mainStyles = css`
--margin-title-ltr: 0 0 0 24px;
--margin-title-rtl: 0 24px 0 0;
/* Safe area insets */
--safe-area-inset-top: var(--app-safe-area-inset-top, env(safe-area-inset-top, 0px));
--safe-area-inset-bottom: var(--app-safe-area-inset-bottom, env(safe-area-inset-bottom, 0px));
--safe-area-inset-left: var(--app-safe-area-inset-left, env(safe-area-inset-left, 0px));
--safe-area-inset-right: var(--app-safe-area-inset-right, env(safe-area-inset-right, 0px));
/* safe-area-insets */
--safe-area-inset-top: var(--app-safe-area-inset-top, env(safe-area-inset-top, 0));
--safe-area-inset-bottom: var(--app-safe-area-inset-bottom, env(safe-area-inset-bottom, 0));
--safe-area-inset-left: var(--app-safe-area-inset-left, env(safe-area-inset-left, 0));
--safe-area-inset-right: var(--app-safe-area-inset-right, env(safe-area-inset-right, 0));
/* Safe area inset x and y */
--safe-area-inset-x: calc(var(--safe-area-inset-left, 0px) + var(--safe-area-inset-right, 0px));
--safe-area-inset-y: calc(var(--safe-area-inset-top, 0px) + var(--safe-area-inset-bottom, 0px));
/* Offsets for centering elements within asymmetric safe areas */
--safe-area-offset-left: calc(max(var(--safe-area-inset-left, 0px) - var(--safe-area-inset-right, 0px), 0px) / 2);
--safe-area-offset-right: calc(max(var(--safe-area-inset-right, 0px) - var(--safe-area-inset-left, 0px), 0px) / 2);
--safe-area-offset-top: calc(max(var(--safe-area-inset-top, 0px) - var(--safe-area-inset-bottom, 0px), 0px) / 2);
--safe-area-offset-bottom: calc(max(var(--safe-area-inset-bottom, 0px) - var(--safe-area-inset-top, 0px), 0px) / 2);
/* Safe width and height for use instead of 100vw and 100vh
* when working with areas like dialogs which need to fill the entire safe area.
*/
--safe-width: calc(100vw - var(--safe-area-inset-left) - var(--safe-area-inset-right));
--safe-height: calc(100vh - var(--safe-area-inset-top) - var(--safe-area-inset-bottom));
--safe-area-inset-x: calc(var(--safe-area-inset-left, 0px) + var(--safe-area-inset-right, 0px));
}
`;

595
yarn.lock

File diff suppressed because it is too large Load Diff