Compare commits

..

6 Commits

Author SHA1 Message Date
Paulus Schoutsen 854e56947f Update IFRAME_SANDBOX to remove 'allow-same-origin'
Removed 'allow-same-origin' from the IFRAME_SANDBOX settings.
2026-05-18 12:12:21 -04:00
Marcin Bauer 4728eb7231 Remove arrow icon from continue on error indicator (#52092)
The arrow-right icon next to the alert icon was decorative noise.
With automation comments (#52090) adding yet another icon, simplify
to a single mdiAlertCircleCheck indicator.

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-18 17:28:14 +03:00
renovate[bot] d02b92bd32 Update dependency @tsparticles/engine to v4 (#52091)
* Update dependency @tsparticles/engine to v4

* Bump @tsparticles/preset-links to v4 to match engine

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2026-05-18 14:15:54 +00:00
Wendelin 98525d23e6 Lovelace condition live test (#52027)
* Add lovelace condition live test

* Add live card status

* Add empty text
2026-05-18 15:01:56 +02:00
Petar Petrov ec98b21276 Highlight problematic devices in Energy Dashboard list (#52088) 2026-05-18 10:18:27 +01:00
Paulus Schoutsen defad3beca Treat media player unknown state like off instead of unavailable (#52080)
* Show both power buttons for assumed-state media players when unknown

Media players with assumed state report an unknown state when their
actual power state can't be determined. In that case the entity row and
more info should still expose both turn on and turn off controls so the
user can operate the device.

https://claude.ai/code/session_01JyZojNPCCY65HmRVQaASkG

* Treat media player unknown state like off instead of unavailable

The media player controls lumped the "unknown" state in with
"unavailable" and hid all controls. An unknown state is closer to "off":
the device exists but its power state isn't reported, which is common
for assumed-state players. Only "unavailable" should hide the controls,
so an unknown-state player now shows the turn on button (and both power
buttons when it has an assumed state) in the entity row and more info.

https://claude.ai/code/session_01JyZojNPCCY65HmRVQaASkG

* Adjust comments and variable placement for media player state check

https://claude.ai/code/session_01JyZojNPCCY65HmRVQaASkG

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-18 09:58:01 +03:00
49 changed files with 704 additions and 951 deletions
+2 -2
View File
@@ -75,8 +75,8 @@
"@replit/codemirror-indentation-markers": "6.5.3",
"@swc/helpers": "0.5.21",
"@thomasloven/round-slider": "0.6.0",
"@tsparticles/engine": "3.9.1",
"@tsparticles/preset-links": "3.2.0",
"@tsparticles/engine": "4.0.0",
"@tsparticles/preset-links": "4.0.0",
"@vibrant/color": "4.0.4",
"@webcomponents/scoped-custom-element-registry": "0.0.10",
"@webcomponents/webcomponentsjs": "2.8.0",
@@ -0,0 +1,59 @@
import type {
ReactiveController,
ReactiveControllerHost,
} from "@lit/reactive-element/reactive-controller";
import type {
Condition,
ConditionContext,
} from "../../panels/lovelace/common/validate-condition";
import type { HomeAssistant } from "../../types";
import { setupConditionListeners } from "../condition/listeners";
/**
* Reactive controller that manages the media-query and time-based listeners
* needed to keep a set of lovelace visibility conditions evaluated live.
*
* The host is responsible for the actual evaluation (e.g. computing visible /
* hidden / invalid state); the controller only triggers it via the supplied
* `onUpdate` callback when something the conditions depend on changes. Call
* `setup()` whenever the conditions change; the controller clears previous
* listeners and re-subscribes. Listeners are automatically released when the
* host disconnects.
*/
export class ConditionListenersController implements ReactiveController {
private _unsubs: (() => void)[] = [];
constructor(host: ReactiveControllerHost) {
host.addController(this);
}
public hostDisconnected(): void {
this.clear();
}
public setup(
conditions: Condition[],
hass: HomeAssistant,
onUpdate: () => void,
getContext?: () => ConditionContext
): void {
this.clear();
if (!conditions.length) {
return;
}
setupConditionListeners(
conditions,
hass,
(unsub) => this._unsubs.push(unsub),
() => onUpdate(),
getContext
);
}
public clear(): void {
for (const unsub of this._unsubs) {
unsub();
}
this._unsubs = [];
}
}
@@ -0,0 +1,91 @@
import { LitElement, css, html, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import "../ha-tooltip";
export type LiveTestState = "pass" | "fail" | "invalid" | "unknown";
/**
* @element ha-automation-row-live-test
*
* @summary
* Small status indicator dot used in automation/condition rows to surface the
* live evaluation result. Renders an optional tooltip with details on hover.
*
* @attr {"pass"|"fail"|"invalid"|"unknown"} state - The current live-test state. Defaults to `unknown`.
* @attr {string} label - Accessible label announced by assistive technology.
* @attr {string} message - Optional tooltip body shown on hover/focus.
*/
@customElement("ha-automation-row-live-test")
export class HaAutomationRowLiveTest extends LitElement {
@property({ reflect: true }) public state: LiveTestState = "unknown";
@property() public label = "";
@property() public message?: string;
protected render() {
return html`
<div
id="indicator"
role="status"
tabindex="0"
aria-label=${this.label}
></div>
${this.message
? html`<ha-tooltip for="indicator">${this.message}</ha-tooltip>`
: nothing}
`;
}
static styles = css`
:host {
position: absolute;
inset-inline-end: -6px;
display: inline-block;
}
#indicator {
width: 12px;
height: 12px;
border-radius: var(--ha-border-radius-circle);
border: 3px solid;
box-sizing: border-box;
background-color: var(--card-background-color);
transition: all var(--ha-animation-duration-normal) ease-in-out;
}
:host([state="pass"]) #indicator {
background-color: var(--ha-color-fill-success-loud-resting);
border-color: var(--ha-color-fill-success-loud-resting);
}
:host([state="pass"]) #indicator:hover {
background-color: var(--ha-color-fill-success-loud-hover);
border-color: var(--ha-color-fill-success-loud-hover);
}
:host([state="fail"]) #indicator {
border-color: var(--ha-color-fill-warning-loud-resting);
}
:host([state="fail"]) #indicator:hover {
background-color: var(--ha-color-fill-warning-loud-hover);
border-color: var(--ha-color-fill-warning-loud-hover);
}
:host([state="invalid"]) #indicator {
border-color: var(--ha-color-fill-danger-loud-resting);
}
:host([state="invalid"]) #indicator:hover {
background-color: var(--ha-color-fill-danger-loud-hover);
border-color: var(--ha-color-fill-danger-loud-hover);
}
:host([state="unknown"]) #indicator {
border-color: var(--ha-color-fill-neutral-loud-resting);
}
:host([state="unknown"]) #indicator:hover {
background-color: var(--ha-color-fill-neutral-loud-hover);
border-color: var(--ha-color-fill-neutral-loud-hover);
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-automation-row-live-test": HaAutomationRowLiveTest;
}
}
-1
View File
@@ -30,7 +30,6 @@ export class HaSettingsRow extends LitElement {
<slot name="prefix"></slot>
<div
class="body"
part="heading"
?two-line=${!this.threeLine && hasDescription}
?three-line=${this.threeLine}
>
+1 -16
View File
@@ -1,13 +1,12 @@
import "@home-assistant/webawesome/dist/components/textarea/textarea";
import type WaTextarea from "@home-assistant/webawesome/dist/components/textarea/textarea";
import { HasSlotController } from "@home-assistant/webawesome/dist/internal/slot";
import type { PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ifDefined } from "lit/directives/if-defined";
import { stopPropagation } from "../common/dom/stop_propagation";
import { WaInputMixin, waInputStyles } from "./input/wa-input-mixin";
import { stopPropagation } from "../common/dom/stop_propagation";
/**
* Home Assistant textarea component
@@ -85,20 +84,6 @@ export class HaTextArea extends WaInputMixin(LitElement) {
this.removeEventListener("keydown", stopPropagation);
}
protected override async firstUpdated(
changedProperties: PropertyValues<this>
): Promise<void> {
super.firstUpdated(changedProperties);
if (this.autofocus) {
await this._textarea?.updateComplete;
this._textarea?.focus();
}
}
public override focus(options?: FocusOptions): void {
this._textarea?.focus(options);
}
protected render() {
const hasLabelSlot = this.label
? false
-4
View File
@@ -95,7 +95,6 @@ export interface TriggerList {
export interface BaseTrigger {
alias?: string;
comment?: string;
/** @deprecated Use `trigger` instead */
platform?: string;
trigger: string;
@@ -241,7 +240,6 @@ export type Trigger = LegacyTrigger | TriggerList | PlatformTrigger;
interface BaseCondition {
condition: string;
alias?: string;
comment?: string;
enabled?: boolean;
options?: Record<string, unknown>;
}
@@ -609,7 +607,6 @@ export interface AutomationClipboard {
export interface BaseSidebarConfig {
delete: () => void;
close: (focus?: boolean) => void;
editComment: () => void;
}
export interface TriggerSidebarConfig extends BaseSidebarConfig {
@@ -671,7 +668,6 @@ export interface OptionSidebarConfig extends BaseSidebarConfig {
rename: () => void;
duplicate: () => void;
defaultOption?: boolean;
comment?: string;
}
export interface ScriptFieldSidebarConfig extends BaseSidebarConfig {
-1
View File
@@ -12,7 +12,6 @@ import {
export interface DeviceAutomation {
alias?: string;
comment?: string;
device_id: string;
domain: string;
entity_id?: string;
+3 -2
View File
@@ -38,7 +38,7 @@ import { stateActive } from "../common/entity/state_active";
import { supportsFeature } from "../common/entity/supports-feature";
import type { MediaPlayerItemId } from "../components/media-player/ha-media-player-browse";
import type { HomeAssistant, TranslationDict } from "../types";
import { isUnavailableState } from "./entity/entity";
import { UNAVAILABLE } from "./entity/entity";
import { isTTSMediaSource } from "./tts";
interface MediaPlayerEntityAttributes extends HassEntityAttributeBase {
@@ -284,7 +284,8 @@ export const computeMediaControls = (
const state = stateObj.state;
if (isUnavailableState(state)) {
// We only filter out `unavailable`, not `unknown`
if (state === UNAVAILABLE) {
return undefined;
}
-3
View File
@@ -36,7 +36,6 @@ export const isMaxMode = arrayLiteralIncludes(MODES_MAX);
export const baseActionStruct = object({
alias: optional(string()),
comment: optional(string()),
continue_on_error: optional(boolean()),
enabled: optional(boolean()),
});
@@ -106,7 +105,6 @@ export interface Field {
interface BaseAction {
alias?: string;
comment?: string;
continue_on_error?: boolean;
enabled?: boolean;
}
@@ -197,7 +195,6 @@ export interface ForEachRepeat extends BaseRepeat {
export interface Option {
alias?: string;
comment?: string;
conditions: string | Condition[];
sequence: Action | Action[];
}
+3 -16
View File
@@ -9,7 +9,6 @@ import "../../components/ha-dialog";
import "../../components/ha-dialog-footer";
import "../../components/ha-dialog-header";
import "../../components/ha-svg-icon";
import "../../components/ha-textarea";
import "../../components/input/ha-input";
import type { HaInput } from "../../components/input/ha-input";
import type { HomeAssistant } from "../../types";
@@ -29,7 +28,7 @@ class DialogBox extends LitElement {
@state() private _validInput = true;
@query("ha-input, ha-textarea") private _textField?: HaInput;
@query("ha-input") private _textField?: HaInput;
private _closePromise?: Promise<void>;
@@ -110,7 +109,7 @@ class DialogBox extends LitElement {
</ha-dialog-header>
<div id="dialog-box-description">
${this._params.text ? html` <p>${this._params.text}</p> ` : ""}
${this._params.prompt && !this._params.multiline
${this._params.prompt
? html`
<ha-input
autofocus
@@ -132,19 +131,7 @@ class DialogBox extends LitElement {
: nothing}
</ha-input>
`
: this._params.prompt && this._params.multiline
? html`
<ha-textarea
resize="auto"
autofocus
.value=${this._params.defaultValue}
.placeholder=${this._params.placeholder}
.label=${this._params.inputLabel}
.disabled=${this._loading}
@input=${this._validateInput}
></ha-textarea>
`
: nothing}
: nothing}
</div>
<ha-dialog-footer slot="footer">
${confirmPrompt
-1
View File
@@ -33,7 +33,6 @@ export interface PromptDialogParams extends BaseDialogBoxParams {
inputMin?: number | string;
inputMax?: number | string;
action?: (value?: string) => Promise<void>;
multiline?: boolean;
}
export interface DialogBoxParams
@@ -37,7 +37,7 @@ import "../../../components/ha-svg-icon";
import "../../../components/ha-tooltip";
import { showJoinMediaPlayersDialog } from "../../../components/media-player/show-join-media-players-dialog";
import { showMediaBrowserDialog } from "../../../components/media-player/show-media-browser-dialog";
import { isUnavailableState } from "../../../data/entity/entity";
import { UNAVAILABLE } from "../../../data/entity/entity";
import type {
MediaPickedEvent,
MediaPlayerEntity,
@@ -275,7 +275,8 @@ class MoreInfoMediaPlayer extends LitElement {
protected _renderGrouping() {
if (
!this.stateObj ||
isUnavailableState(this.stateObj.state) ||
// Compare against `unavailable` so we allow `unknown`
this.stateObj.state === UNAVAILABLE ||
!supportsFeature(this.stateObj, MediaPlayerEntityFeature.GROUPING)
) {
return nothing;
@@ -315,7 +316,7 @@ class MoreInfoMediaPlayer extends LitElement {
return nothing;
}
if (isUnavailableState(this.stateObj.state)) {
if (this.stateObj.state === UNAVAILABLE) {
return this._renderEmptyCover(this.hass.formatEntityState(this.stateObj));
}
@@ -461,7 +462,7 @@ class MoreInfoMediaPlayer extends LitElement {
: nothing}
${this._renderVolumeControl()}
<div class="controls-row">
${!isUnavailableState(stateObj.state) &&
${stateObj.state !== UNAVAILABLE &&
supportsFeature(stateObj, MediaPlayerEntityFeature.BROWSE_MEDIA)
? this._renderControlButton(
"browse_media",
@@ -107,7 +107,6 @@ export default class HaAutomationActionEditor extends LitElement {
ev.stopPropagation();
const value = {
...(this.action.alias ? { alias: this.action.alias } : {}),
...(this.action.comment ? { comment: this.action.comment } : {}),
...ev.detail.value,
};
fireEvent(this, "value-changed", { value });
@@ -4,12 +4,9 @@ import {
mdiAlertCircleCheck,
mdiAppleKeyboardCommand,
mdiArrowDown,
mdiArrowRightThin,
mdiArrowUp,
mdiCheckboxBlankOutline,
mdiCheckboxOutline,
mdiCommentEditOutline,
mdiCommentTextOutline,
mdiContentCopy,
mdiContentCut,
mdiContentPaste,
@@ -335,10 +332,6 @@ export default class HaAutomationActionRow extends LitElement {
${type !== "condition" &&
(this.action as NonConditionAction).continue_on_error === true
? html`<ha-svg-icon
class="arrow-right"
.path=${mdiArrowRightThin}
></ha-svg-icon
><ha-svg-icon
id="svg-icon"
.path=${mdiAlertCircleCheck}
></ha-svg-icon>
@@ -348,24 +341,6 @@ export default class HaAutomationActionRow extends LitElement {
)}
</ha-tooltip>`
: nothing}
${this.action.comment?.trim()
? html`
<ha-svg-icon
id="comment-icon"
.path=${mdiCommentTextOutline}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.comment.label"
)}
class="comment-indicator"
></ha-svg-icon>
<ha-tooltip for="comment-icon"
>${this.action.comment.substring(0, 250)}${this.action.comment
.length > 250
? "..."
: nothing}</ha-tooltip
>
`
: nothing}
</h3>
<ha-automation-row-event-chip
.show=${this._running}
@@ -409,14 +384,6 @@ export default class HaAutomationActionRow extends LitElement {
)
)}
</ha-dropdown-item>
<ha-dropdown-item value="edit_comment">
<ha-svg-icon slot="icon" .path=${mdiCommentEditOutline}></ha-svg-icon>
${this._renderOverflowLabel(
this.hass.localize(
`ui.panel.config.automation.editor.comment.${this.action.comment ? "edit" : "add"}`
)
)}
</ha-dropdown-item>
<wa-divider></wa-divider>
<ha-dropdown-item value="duplicate" .disabled=${this.disabled}>
<ha-svg-icon
@@ -943,38 +910,6 @@ export default class HaAutomationActionRow extends LitElement {
}
};
private _editCommentAction = async (): Promise<void> => {
const comment = await showPromptDialog(this, {
title: this.hass.localize(
`ui.panel.config.automation.editor.comment.${this.action.comment ? "edit" : "add"}`
),
inputLabel: this.hass.localize(
"ui.panel.config.automation.editor.comment.label"
),
inputType: "string",
defaultValue: this.action.comment,
confirmText: this.hass.localize("ui.common.submit"),
multiline: true,
});
if (comment !== null) {
const value = { ...this.action };
if (comment === "") {
delete value.comment;
} else {
value.comment = comment;
}
fireEvent(this, "value-changed", {
value,
});
if (this._selected && this.optionsInSidebar) {
this.openSidebar(value); // refresh sidebar
} else if (this._yamlMode) {
this._actionEditor?.yamlEditor?.setValue(value);
}
}
};
private _duplicateAction = () => {
fireEvent(this, "duplicate");
};
@@ -1091,7 +1026,6 @@ export default class HaAutomationActionRow extends LitElement {
rename: () => {
this._renameAction();
},
editComment: this._editCommentAction,
toggleYamlMode: () => {
this._toggleYamlMode();
this.openSidebar();
@@ -1187,9 +1121,6 @@ export default class HaAutomationActionRow extends LitElement {
case "rename":
this._renameAction();
break;
case "edit_comment":
this._editCommentAction();
break;
case "duplicate":
this._duplicateAction();
break;
@@ -1227,9 +1158,6 @@ export default class HaAutomationActionRow extends LitElement {
rowStyles,
overflowStyles,
css`
ha-svg-icon.arrow-right {
--icon-primary-color: var(--ha-color-fill-neutral-loud-resting);
}
ha-svg-icon#svg-icon {
--icon-primary-color: var(--ha-color-fill-neutral-loud-active);
}
@@ -186,10 +186,6 @@ export class HaDeviceAction extends LitElement {
}
static styles = css`
:host {
display: block;
margin-bottom: var(--ha-space-3);
}
ha-device-picker {
display: block;
margin-bottom: 24px;
@@ -123,7 +123,6 @@ export default class HaAutomationConditionEditor extends LitElement {
ev.stopPropagation();
const value = {
...(this.condition.alias ? { alias: this.condition.alias } : {}),
...(this.condition.comment ? { comment: this.condition.comment } : {}),
...ev.detail.value,
};
fireEvent(this, "value-changed", { value });
@@ -4,8 +4,6 @@ import {
mdiAppleKeyboardCommand,
mdiArrowDown,
mdiArrowUp,
mdiCommentEditOutline,
mdiCommentTextOutline,
mdiContentCopy,
mdiContentCut,
mdiContentPaste,
@@ -25,7 +23,7 @@ import type {
} from "home-assistant-js-websocket";
import { dump } from "js-yaml";
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { LitElement, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import memoizeOne from "memoize-one";
@@ -41,6 +39,7 @@ import { debounce } from "../../../../common/util/debounce";
import "../../../../components/automation/ha-automation-row";
import type { HaAutomationRow } from "../../../../components/automation/ha-automation-row";
import "../../../../components/automation/ha-automation-row-event-chip";
import "../../../../components/automation/ha-automation-row-live-test";
import "../../../../components/ha-card";
import "../../../../components/ha-condition-icon";
import "../../../../components/ha-dropdown";
@@ -48,7 +47,6 @@ import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
import "../../../../components/ha-dropdown-item";
import "../../../../components/ha-expansion-panel";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-tooltip";
import type {
AutomationClipboard,
Condition,
@@ -219,24 +217,6 @@ export default class HaAutomationConditionRow extends LitElement {
conditionTargetSpec
)
: nothing}
${this.condition.comment?.trim()
? html`
<ha-svg-icon
id="comment-icon"
.path=${mdiCommentTextOutline}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.comment.label"
)}
class="comment-indicator"
></ha-svg-icon>
<ha-tooltip for="comment-icon"
>${this.condition.comment.substring(0, 250)}${this.condition
.comment.length > 250
? "..."
: nothing}</ha-tooltip
>
`
: nothing}
</h3>
<ha-automation-row-event-chip
.show=${this._testing}
@@ -284,14 +264,6 @@ export default class HaAutomationConditionRow extends LitElement {
)
)}
</ha-dropdown-item>
<ha-dropdown-item value="edit_comment">
<ha-svg-icon slot="icon" .path=${mdiCommentEditOutline}></ha-svg-icon>
${this._renderOverflowLabel(
this.hass.localize(
`ui.panel.config.automation.editor.comment.${this.condition.comment ? "edit" : "add"}`
)
)}
</ha-dropdown-item>
<wa-divider></wa-divider>
@@ -526,23 +498,15 @@ export default class HaAutomationConditionRow extends LitElement {
@click=${this._toggleSidebar}
@toggle-collapsed=${this._toggleCollapse}
>${this._renderRow()}
<div
<ha-automation-row-live-test
slot="icons"
id="live-test"
class=${this._liveTestResult.state}
role="status"
tabindex="0"
aria-label=${this.hass.localize(
.state=${this._liveTestResult.state}
.label=${this.hass.localize(
`ui.panel.config.automation.editor.conditions.live_test_state.${this._liveTestResult.state}`
)}
>
${this._liveTestResult.message
? html`<ha-tooltip for="live-test">
${this._liveTestResult.message}
</ha-tooltip>`
: nothing}
</div></ha-automation-row
>`
.message=${this._liveTestResult.message}
></ha-automation-row-live-test
></ha-automation-row>`
: html`
<ha-expansion-panel
left-chevron
@@ -849,38 +813,6 @@ export default class HaAutomationConditionRow extends LitElement {
}
};
private _editCommentCondition = async (): Promise<void> => {
const comment = await showPromptDialog(this, {
title: this.hass.localize(
`ui.panel.config.automation.editor.comment.${this.condition.comment ? "edit" : "add"}`
),
inputLabel: this.hass.localize(
"ui.panel.config.automation.editor.comment.label"
),
inputType: "string",
defaultValue: this.condition.comment,
confirmText: this.hass.localize("ui.common.submit"),
multiline: true,
});
if (comment !== null) {
const value = { ...this.condition };
if (comment === "") {
delete value.comment;
} else {
value.comment = comment;
}
fireEvent(this, "value-changed", {
value,
});
if (this._selected && this.optionsInSidebar) {
this.openSidebar(value); // refresh sidebar
} else if (this._yamlMode) {
this.conditionEditor?.yamlEditor?.setValue(value);
}
}
};
private _duplicateCondition = () => {
fireEvent(this, "duplicate");
};
@@ -1022,7 +954,6 @@ export default class HaAutomationConditionRow extends LitElement {
rename: () => {
this._renameCondition();
},
editComment: this._editCommentCondition,
toggleYamlMode: () => {
this._toggleYamlMode();
this.openSidebar();
@@ -1094,9 +1025,6 @@ export default class HaAutomationConditionRow extends LitElement {
case "rename":
this._renameCondition();
break;
case "edit_comment":
this._editCommentCondition();
break;
case "duplicate":
this._duplicateCondition();
break;
@@ -1128,52 +1056,7 @@ export default class HaAutomationConditionRow extends LitElement {
}
static get styles(): CSSResultGroup {
return [
rowStyles,
overflowStyles,
css`
#live-test {
position: absolute;
inset-inline-end: -6px;
width: 12px;
height: 12px;
border-radius: var(--ha-border-radius-circle);
border: 3px solid;
box-sizing: border-box;
background-color: var(--card-background-color);
transition: all var(--ha-animation-duration-normal) ease-in-out;
}
#live-test.pass {
background-color: var(--ha-color-fill-success-loud-resting);
border-color: var(--ha-color-fill-success-loud-resting);
}
#live-test.pass:hover {
background-color: var(--ha-color-fill-success-loud-hover);
border-color: var(--ha-color-fill-success-loud-hover);
}
#live-test.fail {
border-color: var(--ha-color-fill-warning-loud-resting);
}
#live-test.fail:hover {
background-color: var(--ha-color-fill-warning-loud-hover);
border-color: var(--ha-color-fill-warning-loud-hover);
}
#live-test.invalid {
border-color: var(--ha-color-fill-danger-loud-resting);
}
#live-test.invalid:hover {
background-color: var(--ha-color-fill-danger-loud-hover);
border-color: var(--ha-color-fill-danger-loud-hover);
}
#live-test.unknown {
border-color: var(--ha-color-fill-neutral-loud-resting);
}
#live-test.unknown:hover {
background-color: var(--ha-color-fill-neutral-loud-hover);
border-color: var(--ha-color-fill-neutral-loud-hover);
}
`,
];
return [rowStyles, overflowStyles];
}
}
@@ -188,10 +188,6 @@ export class HaDeviceCondition extends LitElement {
}
static styles = css`
:host {
display: block;
margin-bottom: var(--ha-space-3);
}
ha-device-picker {
display: block;
margin-bottom: 24px;
@@ -1,5 +1,5 @@
import type { PropertyValues } from "lit";
import { css, html, LitElement } from "lit";
import { html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import {
@@ -22,7 +22,6 @@ import type { HomeAssistant } from "../../../../../types";
const numericStateConditionStruct = object({
alias: optional(string()),
comment: optional(string()),
condition: literal("numeric_state"),
entity_id: optional(string()),
attribute: optional(string()),
@@ -256,13 +255,6 @@ export default class HaNumericStateCondition extends LitElement {
);
}
};
static styles = css`
:host {
display: block;
margin-bottom: var(--ha-space-3);
}
`;
}
declare global {
@@ -1,8 +1,7 @@
import type { PropertyValues } from "lit";
import { css, html, LitElement } from "lit";
import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import {
array,
assert,
boolean,
literal,
@@ -11,9 +10,10 @@ import {
optional,
string,
union,
array,
} from "superstruct";
import { ensureArray } from "../../../../../common/array/ensure-array";
import { createDurationData } from "../../../../../common/datetime/create_duration_data";
import { ensureArray } from "../../../../../common/array/ensure-array";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-form/ha-form";
import type { SchemaUnion } from "../../../../../components/ha-form/types";
@@ -25,7 +25,6 @@ import type { ConditionElement } from "../ha-automation-condition-row";
const stateConditionStruct = object({
alias: optional(string()),
comment: optional(string()),
condition: literal("state"),
entity_id: optional(string()),
attribute: optional(string()),
@@ -143,13 +142,6 @@ export class HaStateCondition extends LitElement implements ConditionElement {
);
}
};
static styles = css`
:host {
display: block;
margin-bottom: var(--ha-space-3);
}
`;
}
declare global {
@@ -1,79 +0,0 @@
import "@home-assistant/webawesome/dist/components/divider/divider";
import { consume, type ContextType } from "@lit/context";
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-button";
import "../../../components/ha-settings-row";
import { internationalizationContext } from "../../../data/context";
@customElement("ha-automation-comment")
export class HaAutomationComment extends LitElement {
@property() public comment!: string;
@state()
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
protected render() {
return html`
<ha-settings-row narrow>
<div class="heading" slot="heading">
<span class="title" id="comment-label">
${this._i18n.localize(
"ui.panel.config.automation.editor.comment.label"
)}
</span>
<ha-button
@click=${this._handleClick}
size="small"
appearance="plain"
>
${this._i18n.localize("ui.common.edit")}
</ha-button>
</div>
<p aria-labelledby="comment-label">${this.comment}</p>
</ha-settings-row>
`;
}
private _handleClick() {
fireEvent(this, "edit-comment");
}
static styles = css`
ha-settings-row {
margin-inline: calc(-1 * var(--ha-space-4));
}
ha-settings-row::part(heading) {
padding-inline-end: 0;
overflow: visible;
}
.heading {
display: flex;
justify-content: space-between;
align-items: center;
}
p {
margin: var(--ha-space-2) 0 0;
border: var(--ha-border-width-sm) solid
var(--ha-color-border-neutral-quiet);
padding: var(--ha-space-1) var(--ha-space-3);
border-radius: var(--ha-border-radius-lg);
background-color: var(--ha-color-fill-neutral-quiet-resting);
}
ha-button {
margin-inline-end: calc(-1 * var(--ha-space-3));
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-automation-comment": HaAutomationComment;
}
interface HASSDomEvents {
"edit-comment": undefined;
}
}
@@ -3,8 +3,6 @@ import {
mdiAppleKeyboardCommand,
mdiArrowDown,
mdiArrowUp,
mdiCommentEditOutline,
mdiCommentTextOutline,
mdiDelete,
mdiDotsVertical,
mdiPlusCircleMultipleOutline,
@@ -39,11 +37,11 @@ import type { Action, Option } from "../../../../data/script";
import { showPromptDialog } from "../../../../dialogs/generic/show-dialog-box";
import type { HomeAssistant } from "../../../../types";
import { isMac } from "../../../../util/is_mac";
import { showEditorToast } from "../editor-toast";
import "../action/ha-automation-action";
import type HaAutomationAction from "../action/ha-automation-action";
import "../condition/ha-automation-condition";
import type HaAutomationCondition from "../condition/ha-automation-condition";
import { showEditorToast } from "../editor-toast";
import {
editorStyles,
indentStyle,
@@ -152,24 +150,6 @@ export default class HaAutomationOptionRow extends LitElement {
: this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.default"
)}
${this.option?.comment?.trim()
? html`
<ha-svg-icon
id="comment-icon"
.path=${mdiCommentTextOutline}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.comment.label"
)}
class="comment-indicator"
></ha-svg-icon>
<ha-tooltip for="comment-icon"
>${this.option.comment.substring(0, 250)}${this.option.comment
.length > 250
? "..."
: nothing}</ha-tooltip
>
`
: nothing}
</h3>
<slot name="icons" slot="icons"></slot>
@@ -197,17 +177,6 @@ export default class HaAutomationOptionRow extends LitElement {
)
)}
</ha-dropdown-item>
<ha-dropdown-item value="edit_comment">
<ha-svg-icon
slot="icon"
.path=${mdiCommentEditOutline}
></ha-svg-icon>
${this._renderOverflowLabel(
this.hass.localize(
`ui.panel.config.automation.editor.comment.${this.option?.comment ? "edit" : "add"}`
)
)}
</ha-dropdown-item>
<ha-dropdown-item value="duplicate" .disabled=${this.disabled}>
<ha-svg-icon
@@ -392,9 +361,6 @@ export default class HaAutomationOptionRow extends LitElement {
case "rename":
this._renameOption();
break;
case "edit_comment":
this._editCommentOption();
break;
case "delete":
this._removeOption();
break;
@@ -458,39 +424,6 @@ export default class HaAutomationOptionRow extends LitElement {
}
};
private _editCommentOption = async (): Promise<void> => {
if (!this.option) {
return;
}
const comment = await showPromptDialog(this, {
title: this.hass.localize(
`ui.panel.config.automation.editor.comment.${this.option.comment ? "edit" : "add"}`
),
inputLabel: this.hass.localize(
"ui.panel.config.automation.editor.comment.label"
),
inputType: "string",
defaultValue: this.option.comment,
confirmText: this.hass.localize("ui.common.submit"),
multiline: true,
});
if (comment !== null) {
const value: Option = { ...this.option };
if (comment === "") {
delete value.comment;
} else {
value.comment = comment;
}
fireEvent(this, "value-changed", {
value,
});
if (this._selected) {
this.openSidebar(value); // refresh sidebar
}
}
};
private _conditionChanged(ev: CustomEvent) {
ev.stopPropagation();
const conditions = ev.detail.value as Condition[];
@@ -522,8 +455,7 @@ export default class HaAutomationOptionRow extends LitElement {
this.openSidebar();
}
public openSidebar(option?: Option): void {
const sidebarOption = option ?? this.option;
public openSidebar(): void {
fireEvent(this, "open-sidebar", {
close: (focus?: boolean) => {
this._selected = false;
@@ -535,11 +467,9 @@ export default class HaAutomationOptionRow extends LitElement {
rename: () => {
this._renameOption();
},
editComment: this._editCommentOption,
delete: this._removeOption,
duplicate: this._duplicateOption,
defaultOption: !!this.defaultActions,
comment: sidebarOption?.comment,
} satisfies OptionSidebarConfig);
this._selected = true;
this._collapsed = false;
@@ -3,7 +3,6 @@ import {
mdiAppleKeyboardCommand,
mdiCheckboxBlankOutline,
mdiCheckboxOutline,
mdiCommentEditOutline,
mdiContentCopy,
mdiContentCut,
mdiContentPaste,
@@ -27,8 +26,8 @@ import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
import "../../../../components/ha-dropdown-item";
import { ACTION_BUILDING_BLOCKS } from "../../../../data/action";
import type { ActionSidebarConfig } from "../../../../data/automation";
import type { DomainManifestLookup } from "../../../../data/integration";
import { domainToName } from "../../../../data/integration";
import type { DomainManifestLookup } from "../../../../data/integration";
import type {
NonConditionAction,
RepeatAction,
@@ -39,7 +38,6 @@ import { isMac } from "../../../../util/is_mac";
import type HaAutomationConditionEditor from "../action/ha-automation-action-editor";
import { getAutomationActionType } from "../action/ha-automation-action-row";
import { getRepeatType } from "../action/types/ha-automation-action-repeat";
import "../ha-automation-comment";
import { overflowStyles, sidebarEditorStyles } from "../styles";
import "./ha-automation-sidebar-card";
@@ -177,15 +175,6 @@ export default class HaAutomationSidebarAction extends LitElement {
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
</div>
</ha-dropdown-item>
<ha-dropdown-item slot="menu-items" value="edit_comment">
<ha-svg-icon slot="icon" .path=${mdiCommentEditOutline}></ha-svg-icon>
<div class="overflow-label">
${this.hass.localize(
`ui.panel.config.automation.editor.comment.${this.config.config.action.comment ? "edit" : "add"}`
)}
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
</div>
</ha-dropdown-item>
<wa-divider slot="menu-items"></wa-divider>
<ha-dropdown-item
@@ -388,12 +377,6 @@ export default class HaAutomationSidebarAction extends LitElement {
@ui-mode-not-available=${this._handleUiModeNotAvailable}
></ha-automation-action-editor>`
)}
${this.config.config.action.comment?.trim() && !this.yamlMode
? html`<ha-automation-comment
@edit-comment=${this.config.editComment}
.comment=${this.config.config.action.comment}
></ha-automation-comment>`
: nothing}
</ha-automation-sidebar-card>`;
}
@@ -442,9 +425,6 @@ export default class HaAutomationSidebarAction extends LitElement {
case "rename":
this.config.rename();
break;
case "edit_comment":
this.config.editComment();
break;
case "run":
this.config.run();
break;
@@ -1,7 +1,6 @@
import "@home-assistant/webawesome/dist/components/divider/divider";
import {
mdiAppleKeyboardCommand,
mdiCommentEditOutline,
mdiContentCopy,
mdiContentCut,
mdiContentPaste,
@@ -35,7 +34,6 @@ import type { HomeAssistant } from "../../../../types";
import { isMac } from "../../../../util/is_mac";
import "../condition/ha-automation-condition-editor";
import type HaAutomationConditionEditor from "../condition/ha-automation-condition-editor";
import "../ha-automation-comment";
import { overflowStyles, sidebarEditorStyles } from "../styles";
import "./ha-automation-sidebar-card";
@@ -151,19 +149,6 @@ export default class HaAutomationSidebarCondition extends LitElement {
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
</div>
</ha-dropdown-item>
<ha-dropdown-item
slot="menu-items"
value="edit_comment"
.disabled=${this.disabled}
>
<ha-svg-icon slot="icon" .path=${mdiCommentEditOutline}></ha-svg-icon>
<div class="overflow-label">
${this.hass.localize(
`ui.panel.config.automation.editor.comment.${this.config.config.comment ? "edit" : "add"}`
)}
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
</div>
</ha-dropdown-item>
<wa-divider slot="menu-items"></wa-divider>
@@ -347,12 +332,6 @@ export default class HaAutomationSidebarCondition extends LitElement {
sidebar
></ha-automation-condition-editor>`
)}
${this.config.config.comment?.trim() && !this.yamlMode
? html`<ha-automation-comment
@edit-comment=${this.config.editComment}
.comment=${this.config.config.comment}
></ha-automation-comment>`
: nothing}
<div class="testing-wrapper">
<div
class="testing ${classMap({
@@ -417,9 +396,6 @@ export default class HaAutomationSidebarCondition extends LitElement {
case "rename":
this.config.rename();
break;
case "edit_comment":
this.config.editComment();
break;
case "test":
this.config.test();
break;
@@ -1,7 +1,6 @@
import "@home-assistant/webawesome/dist/components/divider/divider";
import {
mdiAppleKeyboardCommand,
mdiCommentEditOutline,
mdiDelete,
mdiPlusCircleMultipleOutline,
mdiRenameBox,
@@ -15,7 +14,6 @@ import type { OptionSidebarConfig } from "../../../../data/automation";
import type { HomeAssistant } from "../../../../types";
import { isMac } from "../../../../util/is_mac";
import type HaAutomationConditionEditor from "../action/ha-automation-action-editor";
import "../ha-automation-comment";
import { overflowStyles, sidebarEditorStyles } from "../styles";
import "./ha-automation-sidebar-card";
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
@@ -74,22 +72,6 @@ export default class HaAutomationSidebarOption extends LitElement {
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
</div>
</ha-dropdown-item>
<ha-dropdown-item
slot="menu-items"
value="edit_comment"
.disabled=${!!disabled}
>
<ha-svg-icon
slot="icon"
.path=${mdiCommentEditOutline}
></ha-svg-icon>
<div class="overflow-label">
${this.hass.localize(
`ui.panel.config.automation.editor.comment.${this.config.comment ? "edit" : "add"}`
)}
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
</div>
</ha-dropdown-item>
<ha-dropdown-item
slot="menu-items"
@@ -144,12 +126,6 @@ export default class HaAutomationSidebarOption extends LitElement {
`}
<div class="description">${description}</div>
${!this.config.defaultOption && this.config.comment?.trim()
? html`<ha-automation-comment
@edit-comment=${this.config.editComment}
.comment=${this.config.comment}
></ha-automation-comment>`
: nothing}
</ha-automation-sidebar-card>`;
}
@@ -164,9 +140,6 @@ export default class HaAutomationSidebarOption extends LitElement {
case "rename":
this.config.rename();
break;
case "edit_comment":
this.config.editComment();
break;
case "duplicate":
this.config.duplicate();
break;
@@ -1,24 +1,18 @@
import {
mdiAppleKeyboardCommand,
mdiCommentEditOutline,
mdiDelete,
mdiPlaylistEdit,
} from "@mdi/js";
import { mdiAppleKeyboardCommand, mdiDelete, mdiPlaylistEdit } from "@mdi/js";
import type { PropertyValues } from "lit";
import { html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { keyed } from "lit/directives/keyed";
import { fireEvent } from "../../../../common/dom/fire_event";
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
import "../../../../components/ha-dropdown-item";
import type { ScriptFieldSidebarConfig } from "../../../../data/automation";
import type { HomeAssistant } from "../../../../types";
import { isMac } from "../../../../util/is_mac";
import "../../script/ha-script-field-editor";
import type HaAutomationConditionEditor from "../action/ha-automation-action-editor";
import "../ha-automation-comment";
import { overflowStyles, sidebarEditorStyles } from "../styles";
import "./ha-automation-sidebar-card";
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
@customElement("ha-automation-sidebar-script-field")
export default class HaAutomationSidebarScriptField extends LitElement {
@@ -68,15 +62,6 @@ export default class HaAutomationSidebarScriptField extends LitElement {
@wa-select=${this._handleDropdownSelect}
>
<span slot="title">${title}</span>
<ha-dropdown-item slot="menu-items" value="edit_comment">
<ha-svg-icon slot="icon" .path=${mdiCommentEditOutline}></ha-svg-icon>
<div class="overflow-label">
${this.hass.localize(
`ui.panel.config.automation.editor.comment.${this.config.config.field.description ? "edit" : "add"}`
)}
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
</div>
</ha-dropdown-item>
<ha-dropdown-item
slot="menu-items"
value="toggle_yaml_mode"
@@ -136,12 +121,6 @@ export default class HaAutomationSidebarScriptField extends LitElement {
@yaml-changed=${this._yamlChangedSidebar}
></ha-script-field-editor>`
)}
${this.config.config.field.description?.trim() && !this.yamlMode
? html`<ha-automation-comment
@edit-comment=${this.config.editComment}
.comment=${this.config.config.field.description}
></ha-automation-comment>`
: nothing}
</ha-automation-sidebar-card>`;
}
@@ -189,9 +168,6 @@ export default class HaAutomationSidebarScriptField extends LitElement {
case "toggle_yaml_mode":
this._toggleYamlMode();
break;
case "edit_comment":
this.config.editComment();
break;
case "delete":
this.config.delete();
break;
@@ -1,7 +1,6 @@
import "@home-assistant/webawesome/dist/components/divider/divider";
import {
mdiAppleKeyboardCommand,
mdiCommentEditOutline,
mdiContentCopy,
mdiContentCut,
mdiContentPaste,
@@ -19,12 +18,9 @@ import { customElement, property, query, state } from "lit/decorators";
import { keyed } from "lit/directives/keyed";
import { fireEvent } from "../../../../common/dom/fire_event";
import { handleStructError } from "../../../../common/structs/handle-errors";
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
import "../../../../components/ha-dropdown-item";
import type {
LegacyTrigger,
Trigger,
TriggerList,
TriggerSidebarConfig,
} from "../../../../data/automation";
import {
@@ -34,11 +30,11 @@ import {
} from "../../../../data/trigger";
import type { HomeAssistant } from "../../../../types";
import { isMac } from "../../../../util/is_mac";
import "../ha-automation-comment";
import { overflowStyles, sidebarEditorStyles } from "../styles";
import "../trigger/ha-automation-trigger-editor";
import type HaAutomationTriggerEditor from "../trigger/ha-automation-trigger-editor";
import "./ha-automation-sidebar-card";
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
@customElement("ha-automation-sidebar-trigger")
export default class HaAutomationSidebarTrigger extends LitElement {
@@ -129,24 +125,7 @@ export default class HaAutomationSidebarTrigger extends LitElement {
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
</div>
</ha-dropdown-item>
${type !== "list"
? html`<ha-dropdown-item
slot="menu-items"
value="edit_comment"
.disabled=${this.disabled}
>
<ha-svg-icon
slot="icon"
.path=${mdiCommentEditOutline}
></ha-svg-icon>
<div class="overflow-label">
${this.hass.localize(
`ui.panel.config.automation.editor.comment.${(this.config.config as Exclude<Trigger, TriggerList>).comment ? "edit" : "add"}`
)}
<span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
</div>
</ha-dropdown-item>`
: nothing}
${!this.yamlMode &&
!("id" in this.config.config) &&
!this._requestShowId
@@ -342,14 +321,6 @@ export default class HaAutomationSidebarTrigger extends LitElement {
sidebar
></ha-automation-trigger-editor>`
)}
${!isTriggerList(this.config.config) &&
this.config.config.comment?.trim() &&
!this.yamlMode
? html`<ha-automation-comment
@edit-comment=${this.config.editComment}
.comment=${this.config.config.comment}
></ha-automation-comment>`
: nothing}
</ha-automation-sidebar-card>
`;
}
@@ -401,9 +372,6 @@ export default class HaAutomationSidebarTrigger extends LitElement {
case "rename":
this.config.rename();
break;
case "edit_comment":
this.config.editComment();
break;
case "show_id":
this._showTriggerId();
break;
-1
View File
@@ -4,7 +4,6 @@ export const baseTriggerStruct = object({
trigger: string(),
id: optional(string()),
enabled: optional(boolean()),
comment: optional(string()),
});
export const forDictStruct = object({
-8
View File
@@ -52,14 +52,6 @@ export const rowStyles = css`
ha-automation-row-event-chip.event-chip {
position: absolute;
}
.comment-indicator {
color: var(--ha-color-on-neutral-normal);
}
.comment-indicator + ha-tooltip::part(body) {
cursor: default;
max-width: 300px;
}
`;
export const editorStyles = css`
@@ -141,7 +141,6 @@ export default class HaAutomationTriggerEditor extends LitElement {
ev.stopPropagation();
const value = {
...(this.trigger.alias ? { alias: this.trigger.alias } : {}),
...(this.trigger.comment ? { comment: this.trigger.comment } : {}),
...ev.detail.value,
};
fireEvent(this, "value-changed", { value });
@@ -4,8 +4,6 @@ import {
mdiAppleKeyboardCommand,
mdiArrowDown,
mdiArrowUp,
mdiCommentEditOutline,
mdiCommentTextOutline,
mdiContentCopy,
mdiContentCut,
mdiContentPaste,
@@ -244,28 +242,6 @@ export default class HaAutomationTriggerRow extends LitElement {
triggerTargetSpec
)
: nothing}
${type !== "list" &&
(this.trigger as Exclude<Trigger, TriggerList>).comment?.trim()
? html`
<ha-svg-icon
id="comment-icon"
.path=${mdiCommentTextOutline}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.comment.label"
)}
class="comment-indicator"
></ha-svg-icon>
<ha-tooltip for="comment-icon"
>${(
this.trigger as Exclude<Trigger, TriggerList>
).comment!.substring(0, 250)}${(
this.trigger as Exclude<Trigger, TriggerList>
).comment!.length > 250
? "..."
: nothing}</ha-tooltip
>
`
: nothing}
</h3>
<ha-automation-row-event-chip
.show=${this._triggered}
@@ -306,19 +282,7 @@ export default class HaAutomationTriggerRow extends LitElement {
)
)}
</ha-dropdown-item>
${type !== "list"
? html`<ha-dropdown-item value="edit_comment">
<ha-svg-icon
slot="icon"
.path=${mdiCommentEditOutline}
></ha-svg-icon>
${this._renderOverflowLabel(
this.hass.localize(
`ui.panel.config.automation.editor.comment.${(this.trigger as Exclude<Trigger, TriggerList>).comment ? "edit" : "add"}`
)
)}
</ha-dropdown-item>`
: nothing}
<wa-divider></wa-divider>
<ha-dropdown-item value="duplicate" .disabled=${this.disabled}>
@@ -695,7 +659,6 @@ export default class HaAutomationTriggerRow extends LitElement {
rename: () => {
this._renameTrigger();
},
editComment: this._editCommentTrigger,
toggleYamlMode: () => {
this._toggleYamlMode();
this.openSidebar();
@@ -839,40 +802,6 @@ export default class HaAutomationTriggerRow extends LitElement {
}
};
private _editCommentTrigger = async (): Promise<void> => {
if (isTriggerList(this.trigger)) return;
const trigger = this.trigger;
const comment = await showPromptDialog(this, {
title: this.hass.localize(
`ui.panel.config.automation.editor.comment.${trigger.comment ? "edit" : "add"}`
),
inputLabel: this.hass.localize(
"ui.panel.config.automation.editor.comment.label"
),
inputType: "string",
defaultValue: trigger.comment,
confirmText: this.hass.localize("ui.common.submit"),
multiline: true,
});
if (comment !== null) {
const value = { ...trigger };
if (comment === "") {
delete value.comment;
} else {
value.comment = comment;
}
fireEvent(this, "value-changed", {
value,
});
if (this._selected && this.optionsInSidebar) {
this.openSidebar(value); // refresh sidebar
} else if (this._yamlMode) {
this.triggerEditor?.yamlEditor?.setValue(value);
}
}
};
private _duplicateTrigger = () => {
fireEvent(this, "duplicate");
};
@@ -984,9 +913,6 @@ export default class HaAutomationTriggerRow extends LitElement {
case "rename":
this._renameTrigger();
break;
case "edit_comment":
this._editCommentTrigger();
break;
case "duplicate":
this._duplicateTrigger();
break;
@@ -212,10 +212,6 @@ export class HaDeviceTrigger extends LitElement {
}
static styles = css`
:host {
display: block;
margin-bottom: var(--ha-space-3);
}
ha-device-picker {
display: block;
margin-bottom: 24px;
@@ -1,8 +1,7 @@
import type { PropertyValues } from "lit";
import { css, html, LitElement } from "lit";
import { html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { ensureArray } from "../../../../../common/array/ensure-array";
import { createDurationData } from "../../../../../common/datetime/create_duration_data";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { hasTemplate } from "../../../../../common/string/has-template";
@@ -11,6 +10,7 @@ import "../../../../../components/ha-form/ha-form";
import type { SchemaUnion } from "../../../../../components/ha-form/types";
import type { NumericStateTrigger } from "../../../../../data/automation";
import type { HomeAssistant } from "../../../../../types";
import { ensureArray } from "../../../../../common/array/ensure-array";
@customElement("ha-automation-trigger-numeric_state")
export class HaNumericStateTrigger extends LitElement {
@@ -333,13 +333,6 @@ export class HaNumericStateTrigger extends LitElement {
);
}
};
static styles = css`
:host {
display: block;
margin-bottom: var(--ha-space-3);
}
`;
}
declare global {
@@ -29,7 +29,6 @@ const DEFAULT_KEYS: (keyof PlatformTrigger)[] = [
"trigger",
"target",
"alias",
"comment",
"id",
"variables",
"enabled",
@@ -1,7 +1,6 @@
import type { PropertyValues } from "lit";
import { css, html, LitElement } from "lit";
import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import {
array,
assert,
@@ -14,21 +13,22 @@ import {
string,
union,
} from "superstruct";
import memoizeOne from "memoize-one";
import type { LocalizeFunc } from "../../../../../common/translations/localize";
import { ensureArray } from "../../../../../common/array/ensure-array";
import { createDurationData } from "../../../../../common/datetime/create_duration_data";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { hasTemplate } from "../../../../../common/string/has-template";
import type { LocalizeFunc } from "../../../../../common/translations/localize";
import type { StateTrigger } from "../../../../../data/automation";
import { ANY_STATE_VALUE } from "../../../../../components/entity/const";
import type { HomeAssistant } from "../../../../../types";
import { baseTriggerStruct, forDictStruct } from "../../structs";
import type { TriggerElement } from "../ha-automation-trigger-row";
import "../../../../../components/ha-form/ha-form";
import { createDurationData } from "../../../../../common/datetime/create_duration_data";
import type {
HaFormSchema,
SchemaUnion,
} from "../../../../../components/ha-form/types";
import type { StateTrigger } from "../../../../../data/automation";
import type { HomeAssistant } from "../../../../../types";
import { baseTriggerStruct, forDictStruct } from "../../structs";
import type { TriggerElement } from "../ha-automation-trigger-row";
const stateTriggerStruct = assign(
baseTriggerStruct,
@@ -303,13 +303,6 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
? "ui.components.entity.entity-picker.entity"
: `ui.panel.config.automation.editor.triggers.type.state.${schema.name}`
);
static styles = css`
:host {
display: block;
margin-bottom: var(--ha-space-3);
}
`;
}
declare global {
@@ -1,4 +1,5 @@
import {
mdiAlertCircle,
mdiDelete,
mdiWater,
mdiDragHorizontalVariant,
@@ -6,7 +7,7 @@ import {
mdiPlus,
} from "@mdi/js";
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { repeat } from "lit/directives/repeat";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
@@ -15,10 +16,12 @@ import "../../../../components/ha-button";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-sortable";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-tooltip";
import type {
DeviceConsumptionEnergyPreference,
EnergyPreferences,
EnergyPreferencesValidation,
EnergyValidationIssue,
} from "../../../../data/energy";
import { saveEnergyPreferences } from "../../../../data/energy";
import type { StatisticsMetaData } from "../../../../data/recorder";
@@ -93,7 +96,7 @@ export class EnergyDeviceSettingsWater extends LitElement {
${repeat(
this.preferences.device_consumption_water,
(device) => device.stat_consumption,
(device) => html`
(device, index) => html`
<div class="row" .device=${device}>
<div class="handle">
<ha-svg-icon
@@ -108,6 +111,12 @@ export class EnergyDeviceSettingsWater extends LitElement {
this.statsMetadata?.[device.stat_consumption]
)}</span
>
${this._renderIssueIndicator(
this.validationResult?.device_consumption_water[
index
],
index
)}
<ha-icon-button
.label=${this.hass.localize("ui.common.edit")}
@click=${this._editDevice}
@@ -144,6 +153,31 @@ export class EnergyDeviceSettingsWater extends LitElement {
`;
}
private _renderIssueIndicator(
issues: EnergyValidationIssue[] | undefined,
index: number
) {
if (!issues?.length) {
return nothing;
}
const titles = issues.map(
(issue) =>
this.hass.localize(`component.energy.issues.${issue.type}.title`) ||
issue.type
);
const label = titles.join("\n");
const id = `issue-icon-${index}`;
return html`
<ha-svg-icon
id=${id}
class="issue-icon"
.path=${mdiAlertCircle}
aria-label=${label}
></ha-svg-icon>
<ha-tooltip .for=${id} placement="top">${label}</ha-tooltip>
`;
}
private _itemMoved(ev: CustomEvent): void {
ev.stopPropagation();
const { oldIndex, newIndex } = ev.detail;
@@ -248,6 +282,9 @@ export class EnergyDeviceSettingsWater extends LitElement {
cursor: move; /* fallback if grab cursor is unsupported */
cursor: grab;
}
.issue-icon {
color: var(--warning-color);
}
`,
];
}
@@ -1,4 +1,5 @@
import {
mdiAlertCircle,
mdiDelete,
mdiDevices,
mdiDragHorizontalVariant,
@@ -6,7 +7,7 @@ import {
mdiPlus,
} from "@mdi/js";
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { repeat } from "lit/directives/repeat";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
@@ -15,10 +16,12 @@ import "../../../../components/ha-button";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-sortable";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-tooltip";
import type {
DeviceConsumptionEnergyPreference,
EnergyPreferences,
EnergyPreferencesValidation,
EnergyValidationIssue,
} from "../../../../data/energy";
import { saveEnergyPreferences } from "../../../../data/energy";
import type { StatisticsMetaData } from "../../../../data/recorder";
@@ -93,7 +96,7 @@ export class EnergyDeviceSettings extends LitElement {
${repeat(
this.preferences.device_consumption,
(device) => device.stat_consumption,
(device) => html`
(device, index) => html`
<div class="row" .device=${device}>
<div class="handle">
<ha-svg-icon
@@ -108,6 +111,10 @@ export class EnergyDeviceSettings extends LitElement {
this.statsMetadata?.[device.stat_consumption]
)}</span
>
${this._renderIssueIndicator(
this.validationResult?.device_consumption[index],
index
)}
<ha-icon-button
.label=${this.hass.localize("ui.common.edit")}
@click=${this._editDevice}
@@ -144,6 +151,31 @@ export class EnergyDeviceSettings extends LitElement {
`;
}
private _renderIssueIndicator(
issues: EnergyValidationIssue[] | undefined,
index: number
) {
if (!issues?.length) {
return nothing;
}
const titles = issues.map(
(issue) =>
this.hass.localize(`component.energy.issues.${issue.type}.title`) ||
issue.type
);
const label = titles.join("\n");
const id = `issue-icon-${index}`;
return html`
<ha-svg-icon
id=${id}
class="issue-icon"
.path=${mdiAlertCircle}
aria-label=${label}
></ha-svg-icon>
<ha-tooltip .for=${id} placement="top">${label}</ha-tooltip>
`;
}
private _itemMoved(ev: CustomEvent): void {
ev.stopPropagation();
const { oldIndex, newIndex } = ev.detail;
@@ -244,6 +276,9 @@ export class EnergyDeviceSettings extends LitElement {
cursor: move; /* fallback if grab cursor is unsupported */
cursor: grab;
}
.issue-icon {
color: var(--warning-color);
}
`,
];
}
@@ -42,6 +42,10 @@ export default class HaScriptFieldEditor extends LitElement {
name: "key",
selector: { text: {} },
},
{
name: "description",
selector: { text: {} },
},
{
name: "required",
selector: { boolean: {} },
@@ -1,7 +1,5 @@
import {
mdiAppleKeyboardCommand,
mdiCommentEditOutline,
mdiCommentTextOutline,
mdiDelete,
mdiDotsVertical,
mdiPlaylistEdit,
@@ -23,7 +21,6 @@ import "../../../components/ha-dropdown-item";
import type { ScriptFieldSidebarConfig } from "../../../data/automation";
import type { Field } from "../../../data/script";
import { SELECTOR_SELECTOR_BUILDING_BLOCKS } from "../../../data/selector/selector_selector";
import { showPromptDialog } from "../../../dialogs/generic/show-dialog-box";
import type { HomeAssistant } from "../../../types";
import { isMac } from "../../../util/is_mac";
import { showEditorToast } from "../automation/editor-toast";
@@ -93,16 +90,6 @@ export default class HaScriptFieldRow extends LitElement {
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<ha-dropdown-item value="edit_comment">
<ha-svg-icon
slot="icon"
.path=${mdiCommentEditOutline}
></ha-svg-icon>
${this.hass.localize(
`ui.panel.config.automation.editor.comment.${this.field.description ? "edit" : "add"}`
)}
</ha-dropdown-item>
<ha-dropdown-item value="toggle_yaml_mode">
<ha-svg-icon slot="icon" .path=${mdiPlaylistEdit}></ha-svg-icon>
<div class="overflow-label">
@@ -145,27 +132,7 @@ export default class HaScriptFieldRow extends LitElement {
</ha-dropdown-item>
</ha-dropdown>
<h3 slot="header">
${this.field.name ?? this.key}
${this.field.description?.trim()
? html`
<ha-svg-icon
id="comment-icon"
.path=${mdiCommentTextOutline}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.comment.label"
)}
class="comment-indicator"
></ha-svg-icon>
<ha-tooltip for="comment-icon"
>${this.field.description.substring(0, 250)}${this.field
.description.length > 250
? "..."
: nothing}</ha-tooltip
>
`
: nothing}
</h3>
<h3 slot="header">${this.field.name ?? this.key}</h3>
<slot name="icons" slot="icons"></slot>
</ha-automation-row>
@@ -357,46 +324,11 @@ export default class HaScriptFieldRow extends LitElement {
});
}
private _editComment = async (): Promise<void> => {
const comment = await showPromptDialog(this, {
title: this.hass.localize(
`ui.panel.config.automation.editor.comment.${this.field.description ? "edit" : "add"}`
),
inputLabel: this.hass.localize(
"ui.panel.config.automation.editor.comment.label"
),
inputType: "string",
defaultValue: this.field.description,
confirmText: this.hass.localize("ui.common.submit"),
multiline: true,
});
if (comment !== null) {
const value = { ...this.field };
if (comment === "") {
delete value.description;
} else {
value.description = comment;
}
fireEvent(this, "value-changed", {
value,
});
if (this._selected) {
this.openSidebar(false, value); // refresh sidebar
}
}
};
public openSidebar(
selectorEditor = false,
fieldValue?: HaScriptFieldRow["field"]
): void {
public openSidebar(selectorEditor = false): void {
if (!selectorEditor) {
this._selected = true;
}
const field = fieldValue ?? this.field;
fireEvent(this, "open-sidebar", {
save: (value) => {
fireEvent(this, "value-changed", { value });
@@ -421,13 +353,12 @@ export default class HaScriptFieldRow extends LitElement {
},
delete: this._onDelete,
config: {
field,
field: this.field,
selector: selectorEditor,
key: this.key,
excludeKeys: this.excludeKeys,
},
yamlMode: this._yamlMode,
editComment: this._editComment,
} satisfies ScriptFieldSidebarConfig);
if (this.narrow) {
@@ -488,9 +419,6 @@ export default class HaScriptFieldRow extends LitElement {
case "delete":
this._onDelete();
break;
case "edit_comment":
this._editComment();
break;
}
}
@@ -3,12 +3,12 @@ import type { PropertyValues } from "lit";
import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-alert";
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import type { HomeAssistant } from "../../../../types";
import type { Condition } from "../../common/validate-condition";
import { conditionsEntityContext } from "../conditions/context";
import "../conditions/ha-card-conditions-editor";
import "../conditions/ha-visibility-status";
@customElement("hui-badge-visibility-editor")
export class HuiBadgeVisibilityEditor extends LitElement {
@@ -34,11 +34,10 @@ export class HuiBadgeVisibilityEditor extends LitElement {
render() {
const conditions = this.config.visibility ?? [];
return html`
<p class="intro">
${this.hass.localize(
`ui.panel.lovelace.editor.edit_badge.visibility.explanation`
)}
</p>
<ha-visibility-status
.hass=${this.hass}
.conditions=${conditions}
></ha-visibility-status>
<ha-card-conditions-editor
.hass=${this.hass}
.conditions=${conditions}
@@ -62,10 +61,8 @@ export class HuiBadgeVisibilityEditor extends LitElement {
}
static styles = css`
.intro {
margin: 0;
color: var(--secondary-text-color);
margin-bottom: 8px;
ha-visibility-status {
margin-bottom: var(--ha-space-3);
}
`;
}
@@ -3,12 +3,12 @@ import type { PropertyValues } from "lit";
import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-alert";
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import type { HomeAssistant } from "../../../../types";
import type { Condition } from "../../common/validate-condition";
import { conditionsEntityContext } from "../conditions/context";
import "../conditions/ha-card-conditions-editor";
import "../conditions/ha-visibility-status";
@customElement("hui-card-visibility-editor")
export class HuiCardVisibilityEditor extends LitElement {
@@ -34,11 +34,10 @@ export class HuiCardVisibilityEditor extends LitElement {
render() {
const conditions = this.config.visibility ?? [];
return html`
<p class="intro">
${this.hass.localize(
`ui.panel.lovelace.editor.edit_card.visibility.explanation`
)}
</p>
<ha-visibility-status
.hass=${this.hass}
.conditions=${conditions}
></ha-visibility-status>
<ha-card-conditions-editor
.hass=${this.hass}
.conditions=${conditions}
@@ -62,10 +61,8 @@ export class HuiCardVisibilityEditor extends LitElement {
}
static styles = css`
.intro {
margin: 0;
color: var(--secondary-text-color);
margin-bottom: 8px;
ha-visibility-status {
margin-bottom: var(--ha-space-3);
}
`;
}
@@ -13,12 +13,14 @@ import deepClone from "deep-clone-simple";
import type { PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ConditionListenersController } from "../../../../common/controllers/condition-listeners-controller";
import { storage } from "../../../../common/decorators/storage";
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../../../common/dom/fire_event";
import { stopPropagation } from "../../../../common/dom/stop_propagation";
import { handleStructError } from "../../../../common/structs/handle-errors";
import "../../../../components/automation/ha-automation-row-event-chip";
import "../../../../components/automation/ha-automation-row-live-test";
import "../../../../components/ha-alert";
import "../../../../components/ha-card";
import "../../../../components/ha-dropdown";
@@ -33,11 +35,11 @@ import { haStyle } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import { ICON_CONDITION } from "../../common/icon-condition";
import type {
AndCondition,
Condition,
LegacyCondition,
OrCondition,
AndCondition,
NotCondition,
OrCondition,
} from "../../common/validate-condition";
import {
checkConditionsMet,
@@ -103,6 +105,13 @@ export class HaCardConditionEditor extends LitElement {
@state() private _testingResult?: boolean;
@state() private _liveTestResult: {
state: "pass" | "fail" | "invalid" | "unknown";
message?: string;
} = { state: "unknown" };
private _listeners = new ConditionListenersController(this);
private get _editor() {
if (!this._condition) return undefined;
return customElements.get(
@@ -116,6 +125,14 @@ export class HaCardConditionEditor extends LitElement {
});
}
private _setupConditionListeners() {
this._listeners.setup(
this.condition ? [this.condition as Condition] : [],
this.hass,
() => this._evaluateLiveTest()
);
}
protected willUpdate(changedProperties: PropertyValues<this>): void {
if (changedProperties.has("condition")) {
this._condition = {
@@ -143,7 +160,61 @@ export class HaCardConditionEditor extends LitElement {
if (!this._uiAvailable && !this._yamlMode) {
this._yamlMode = true;
}
this._setupConditionListeners();
}
if (changedProperties.has("condition") || changedProperties.has("hass")) {
this._evaluateLiveTest();
}
}
protected updated(changedProperties: PropertyValues<this>): void {
if ((changedProperties as Map<string, unknown>).has("_entityContext")) {
this._evaluateLiveTest();
}
}
private _evaluateLiveTest() {
if (!this.condition || !this._condition) {
this._liveTestResult = { state: "unknown" };
return;
}
if (
isNoEntityCondition(this._condition.condition, this._noEntity) ||
containsNoEntityCondition(this._condition, this._noEntity)
) {
this._liveTestResult = {
state: "unknown",
message: this.hass.localize(
"ui.panel.lovelace.editor.condition-editor.live_test_state.unknown"
),
};
return;
}
if (!validateConditionalConfig([this.condition])) {
this._liveTestResult = {
state: "invalid",
message: this.hass.localize(
"ui.panel.lovelace.editor.condition-editor.live_test_state.invalid"
),
};
return;
}
const testContext =
this._entityContext?.mode === "current"
? { entity_id: this._entityContext.entityId }
: {};
const pass = checkConditionsMet([this.condition], this.hass, testContext);
this._liveTestResult = {
state: pass ? "pass" : "fail",
message: this.hass.localize(
`ui.panel.lovelace.editor.condition-editor.live_test_state.${pass ? "pass" : "fail"}`
),
};
}
protected render() {
@@ -151,6 +222,10 @@ export class HaCardConditionEditor extends LitElement {
if (!condition) return nothing;
const hideLiveTest =
isNoEntityCondition(condition.condition, this._noEntity) ||
containsNoEntityCondition(condition, this._noEntity);
return html`
<div class="container">
<ha-expansion-panel left-chevron>
@@ -164,6 +239,33 @@ export class HaCardConditionEditor extends LitElement {
`ui.panel.lovelace.editor.condition-editor.condition.${condition.condition}.label`
) || condition.condition}
</h3>
<ha-automation-row-event-chip
.show=${this._testingResult !== undefined}
.variant=${this._testingResult ? "success" : "warning"}
slot="event"
class="event-chip"
aria-live="polite"
>
${this._testingResult
? this.hass.localize(
"ui.panel.lovelace.editor.condition-editor.testing_pass"
)
: this.hass.localize(
"ui.panel.lovelace.editor.condition-editor.testing_error"
)}
</ha-automation-row-event-chip>
${hideLiveTest
? nothing
: html`
<ha-automation-row-live-test
slot="icons"
.state=${this._liveTestResult.state}
.label=${this.hass.localize(
`ui.panel.lovelace.editor.condition-editor.live_test_state.${this._liveTestResult.state}`
)}
.message=${this._liveTestResult.message}
></ha-automation-row-live-test>
`}
<ha-dropdown
slot="icons"
@wa-select=${this._handleAction}
@@ -267,23 +369,6 @@ export class HaCardConditionEditor extends LitElement {
`}
</div>
</ha-expansion-panel>
<div
class="testing ${classMap({
active: this._testingResult !== undefined,
pass: this._testingResult === true,
error: this._testingResult === false,
})}"
>
${this._testingResult
? this.hass.localize(
"ui.panel.lovelace.editor.condition-editor.testing_pass"
)
: this._testingResult === false
? this.hass.localize(
"ui.panel.lovelace.editor.condition-editor.testing_error"
)
: nothing}
</div>
</div>
`;
}
@@ -418,41 +503,9 @@ export class HaCardConditionEditor extends LitElement {
opacity: 0.5;
pointer-events: none;
}
.testing {
.event-chip {
position: absolute;
top: 0px;
right: 0px;
left: 0px;
text-transform: uppercase;
font-size: var(--ha-font-size-m);
font-weight: var(--ha-font-weight-bold);
background-color: var(--divider-color, #e0e0e0);
color: var(--text-primary-color);
max-height: 0px;
overflow: hidden;
transition: max-height 0.3s;
text-align: center;
border-top-right-radius: calc(
var(--ha-card-border-radius, var(--ha-border-radius-lg)) - var(
--ha-card-border-width,
1px
)
);
border-top-left-radius: calc(
var(--ha-card-border-radius, var(--ha-border-radius-lg)) - var(
--ha-card-border-width,
1px
)
);
}
.testing.active {
max-height: 100px;
}
.testing.error {
background-color: var(--accent-color);
}
.testing.pass {
background-color: var(--success-color);
inset-inline-end: 40px;
}
.container {
position: relative;
@@ -0,0 +1,160 @@
import { consume } from "@lit/context";
import { mdiAlertCircle, mdiEye, mdiEyeOff } from "@mdi/js";
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { css, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import { ConditionListenersController } from "../../../../common/controllers/condition-listeners-controller";
import "../../../../components/ha-svg-icon";
import { HaRowItem } from "../../../../components/item/ha-row-item";
import type { HomeAssistant } from "../../../../types";
import type {
Condition,
LegacyCondition,
} from "../../common/validate-condition";
import {
checkConditionsMet,
validateConditionalConfig,
} from "../../common/validate-condition";
import type { ConditionsEntityContext } from "./context";
import { conditionsEntityContext } from "./context";
type VisibilityState = "visible" | "hidden" | "invalid";
const STATE_ICONS: Record<VisibilityState, string> = {
visible: mdiEye,
hidden: mdiEyeOff,
invalid: mdiAlertCircle,
};
/**
* @element ha-visibility-status
* @extends {HaRowItem}
*
* @summary
* Row-style banner that surfaces the live visibility result for a set of
* lovelace conditions. Replaces the static explanation alert at the top of
* card / section / badge / conditional-card visibility editors.
*
* @attr {"visible"|"hidden"|"invalid"} state - Computed visibility state (reflected for styling).
*/
@customElement("ha-visibility-status")
export class HaVisibilityStatus extends HaRowItem {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false })
public conditions: (Condition | LegacyCondition)[] = [];
@state()
@consume({ context: conditionsEntityContext, subscribe: true })
private _entityContext?: ConditionsEntityContext;
@property({ reflect: true })
public state: VisibilityState = "visible";
private _listeners = new ConditionListenersController(this);
protected willUpdate(changedProperties: PropertyValues<this>): void {
super.willUpdate(changedProperties);
if (changedProperties.has("conditions") || changedProperties.has("hass")) {
this._listeners.setup(
(this.conditions ?? []) as Condition[],
this.hass,
() => this._evaluate()
);
}
if (
changedProperties.has("hass") ||
changedProperties.has("conditions") ||
(changedProperties as Map<string, unknown>).has("_entityContext")
) {
this._evaluate();
}
}
protected override _renderInner(): TemplateResult {
return html`
<div part="start" class="start">
<ha-svg-icon .path=${STATE_ICONS[this.state]}></ha-svg-icon>
</div>
<div part="content" class="content">
<div part="headline" class="headline">
${this.hass?.localize(
`ui.panel.lovelace.editor.condition-editor.visibility_status.${this.state}.headline`
)}
</div>
<div part="supporting-text" class="supporting">
${this.hass?.localize(
`ui.panel.lovelace.editor.condition-editor.visibility_status.${this.state}.supporting${(this.conditions?.length ?? 0) === 0 ? "_empty" : ""}`
)}
</div>
</div>
`;
}
private _evaluate() {
const conditions = this.conditions ?? [];
let newState: VisibilityState;
if (conditions.length === 0) {
newState = "visible";
} else if (!validateConditionalConfig(conditions)) {
newState = "invalid";
} else {
const context =
this._entityContext?.mode === "current"
? { entity_id: this._entityContext.entityId }
: {};
newState = checkConditionsMet(conditions, this.hass, context)
? "visible"
: "hidden";
}
if (newState === this.state) {
return;
}
this.state = newState;
}
static styles: CSSResultGroup = [
HaRowItem.styles,
css`
:host {
display: block;
border-radius: var(--ha-border-radius-xl);
transition: background-color var(--ha-animation-duration-normal)
ease-in-out;
}
.base {
padding: var(--ha-space-4);
}
:host([state="visible"]) {
background-color: var(--ha-color-fill-success-quiet-resting);
--visibility-status-color: var(--ha-color-on-success-normal);
}
:host([state="hidden"]) {
background-color: var(--ha-color-fill-warning-quiet-resting);
--visibility-status-color: var(--ha-color-on-warning-normal);
}
:host([state="invalid"]) {
background-color: var(--ha-color-fill-danger-quiet-resting);
--visibility-status-color: var(--ha-color-on-danger-normal);
}
.start {
align-self: start;
}
.start ha-svg-icon {
color: var(--visibility-status-color);
--mdc-icon-size: 24px;
}
.headline {
font-weight: var(--ha-font-weight-medium);
white-space: normal;
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"ha-visibility-status": HaVisibilityStatus;
}
}
@@ -7,7 +7,6 @@ import { any, array, assert, assign, object, optional } from "superstruct";
import { storage } from "../../../../common/decorators/storage";
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-alert";
import "../../../../components/ha-button";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-tab-group";
@@ -21,6 +20,7 @@ import "../card-editor/hui-card-element-editor";
import type { HuiCardElementEditor } from "../card-editor/hui-card-element-editor";
import "../card-editor/hui-card-picker";
import "../conditions/ha-card-conditions-editor";
import "../conditions/ha-visibility-status";
import "../hui-element-editor";
import type { ConfigChangedEvent } from "../hui-element-editor";
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
@@ -147,11 +147,10 @@ export class HuiConditionalCardEditor
</div>
`
: html`
<ha-alert alert-type="info">
${this.hass!.localize(
"ui.panel.lovelace.editor.condition-editor.explanation"
)}
</ha-alert>
<ha-visibility-status
.hass=${this.hass}
.conditions=${this._config.conditions ?? []}
></ha-visibility-status>
<ha-card-conditions-editor
.hass=${this.hass}
.conditions=${this._config.conditions}
@@ -246,9 +245,9 @@ export class HuiConditionalCardEditor
width: 100%;
justify-content: center;
}
ha-alert {
display: block;
margin-top: 12px;
ha-visibility-status {
margin-top: var(--ha-space-3);
margin-bottom: var(--ha-space-3);
}
.card {
margin-top: 8px;
@@ -1,11 +1,11 @@
import { LitElement, html } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-alert";
import type { LovelaceSectionRawConfig } from "../../../../data/lovelace/config/section";
import type { HomeAssistant } from "../../../../types";
import type { Condition } from "../../common/validate-condition";
import "../conditions/ha-card-conditions-editor";
import "../conditions/ha-visibility-status";
@customElement("hui-section-visibility-editor")
export class HuiDialogEditSection extends LitElement {
@@ -16,11 +16,10 @@ export class HuiDialogEditSection extends LitElement {
render() {
const conditions = this.config.visibility ?? [];
return html`
<ha-alert alert-type="info">
${this.hass.localize(
`ui.panel.lovelace.editor.edit_section.visibility.explanation`
)}
</ha-alert>
<ha-visibility-status
.hass=${this.hass}
.conditions=${conditions}
></ha-visibility-status>
<ha-card-conditions-editor
.hass=${this.hass}
.conditions=${conditions}
@@ -30,6 +29,12 @@ export class HuiDialogEditSection extends LitElement {
`;
}
static styles = css`
ha-visibility-status {
margin-bottom: var(--ha-space-3);
}
`;
private _valueChanged(ev: CustomEvent): void {
ev.stopPropagation();
const conditions = ev.detail.value as Condition[];
@@ -22,7 +22,7 @@ import { supportsFeature } from "../../../common/entity/supports-feature";
import { debounce } from "../../../common/util/debounce";
import "../../../components/ha-icon-button";
import "../../../components/ha-slider";
import { isUnavailableState } from "../../../data/entity/entity";
import { UNAVAILABLE } from "../../../data/entity/entity";
import type {
ControlButton,
MediaPlayerEntity,
@@ -195,7 +195,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
<div class="controls">
${supportsFeature(stateObj, MediaPlayerEntityFeature.TURN_ON) &&
(!stateActive(stateObj) || assumedState) &&
!isUnavailableState(entityState)
entityState !== UNAVAILABLE
? html`
<ha-icon-button
.path=${assumedState ? mdiPowerOn : mdiPower}
@@ -209,7 +209,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
(stateActive(stateObj) ||
assumedState ||
!supportsFeature(stateObj, MediaPlayerEntityFeature.TURN_ON) ||
isUnavailableState(entityState))
entityState === UNAVAILABLE)
? buttons
: ""}
${supportsFeature(stateObj, MediaPlayerEntityFeature.TURN_OFF) &&
+22 -16
View File
@@ -5043,11 +5043,6 @@
"placeholder": "Optional description",
"add": "Add description"
},
"comment": {
"label": "Comment",
"edit": "Edit comment",
"add": "Add comment"
},
"leave": {
"unsaved_new_title": "Save new automation?",
"unsaved_new_text": "You can save your changes, or delete this automation. You can't undo this action.",
@@ -8934,9 +8929,6 @@
"tab_visibility": "Visibility",
"tab_layout": "Layout",
"paste_condition": "Paste condition",
"visibility": {
"explanation": "The card will be shown when ALL conditions below are fulfilled. If no conditions are set, the card will always be shown."
},
"layout": {
"full_width": "Full width",
"full_width_helper": "Take up the full width of the section whatever its size",
@@ -8967,10 +8959,7 @@
"cut": "[%key:ui::panel::lovelace::editor::edit_card::cut%]",
"duplicate": "[%key:ui::panel::lovelace::editor::edit_card::duplicate%]",
"tab_config": "[%key:ui::panel::lovelace::editor::edit_card::tab_config%]",
"tab_visibility": "[%key:ui::panel::lovelace::editor::edit_card::tab_visibility%]",
"visibility": {
"explanation": "The badge will be shown when ALL conditions below are fulfilled. If no conditions are set, the badge will always be shown."
}
"tab_visibility": "[%key:ui::panel::lovelace::editor::edit_card::tab_visibility%]"
},
"suggest_badge": {
"header": "[%key:ui::panel::lovelace::editor::suggest_card::header%]",
@@ -9042,9 +9031,6 @@
"background_opacity": "Opacity",
"theme": "Theme",
"theme_helper": "Apply a specific theme to this section, overriding the view theme"
},
"visibility": {
"explanation": "The section will be shown when ALL conditions below are fulfilled. If no conditions are set, the section will always be shown."
}
},
"suggest_card": {
@@ -9086,11 +9072,31 @@
}
},
"condition-editor": {
"explanation": "The card will be shown when ALL conditions below are fulfilled.",
"add": "Add condition",
"test": "[%key:ui::panel::config::automation::editor::conditions::test%]",
"testing_pass": "[%key:ui::panel::config::automation::editor::conditions::testing_pass%]",
"testing_error": "[%key:ui::panel::config::automation::editor::conditions::testing_error%]",
"live_test_state": {
"pass": "[%key:ui::panel::config::automation::editor::conditions::testing_pass%]",
"fail": "[%key:ui::panel::config::automation::editor::conditions::testing_error%]",
"invalid": "[%key:ui::panel::config::automation::editor::conditions::live_test_state::invalid%]",
"unknown": "[%key:ui::panel::config::automation::editor::conditions::live_test_state::unknown%]"
},
"visibility_status": {
"visible": {
"headline": "Current visibility: Visible",
"supporting": "All visibility conditions are met",
"supporting_empty": "No visibility conditions are set"
},
"hidden": {
"headline": "Current visibility: Hidden",
"supporting": "Not all visibility conditions are met"
},
"invalid": {
"headline": "Visibility status unknown",
"supporting": "One or more conditions have an invalid configuration"
}
},
"invalid_config_title": "Invalid configuration",
"invalid_config_text": "The condition cannot be tested because the configuration is not valid.",
"condition": {
+1 -1
View File
@@ -1,2 +1,2 @@
export const IFRAME_SANDBOX =
"allow-forms allow-popups allow-pointer-lock allow-same-origin allow-scripts allow-modals allow-downloads";
"allow-forms allow-popups allow-pointer-lock allow-scripts allow-modals allow-downloads";
+99 -87
View File
@@ -4335,129 +4335,141 @@ __metadata:
languageName: node
linkType: hard
"@tsparticles/basic@npm:^3.7.1":
version: 3.9.1
resolution: "@tsparticles/basic@npm:3.9.1"
"@tsparticles/basic@npm:4.0.0":
version: 4.0.0
resolution: "@tsparticles/basic@npm:4.0.0"
dependencies:
"@tsparticles/engine": "npm:3.9.1"
"@tsparticles/move-base": "npm:3.9.1"
"@tsparticles/plugin-hex-color": "npm:3.9.1"
"@tsparticles/plugin-hsl-color": "npm:3.9.1"
"@tsparticles/plugin-rgb-color": "npm:3.9.1"
"@tsparticles/shape-circle": "npm:3.9.1"
"@tsparticles/updater-color": "npm:3.9.1"
"@tsparticles/updater-opacity": "npm:3.9.1"
"@tsparticles/updater-out-modes": "npm:3.9.1"
"@tsparticles/updater-size": "npm:3.9.1"
checksum: 10/a3d0c926e5822931df9762b2038955093ecfb558715807482f691d54efb848286a5d78a55a184885b2d4f46a005bf52c3c54e0013e29e71158ae1ccb5dce08d3
"@tsparticles/engine": "npm:4.0.0"
"@tsparticles/plugin-hex-color": "npm:4.0.0"
"@tsparticles/plugin-hsl-color": "npm:4.0.0"
"@tsparticles/plugin-move": "npm:4.0.0"
"@tsparticles/plugin-rgb-color": "npm:4.0.0"
"@tsparticles/shape-circle": "npm:4.0.0"
"@tsparticles/updater-opacity": "npm:4.0.0"
"@tsparticles/updater-out-modes": "npm:4.0.0"
"@tsparticles/updater-paint": "npm:4.0.0"
"@tsparticles/updater-size": "npm:4.0.0"
checksum: 10/5e455beb0663019d719bc111928a9e22a0f471450414391eed4be2f00455019c59ec85e1e8b7fadf8da2dc258c803d555ebb9b2e0a497a8f53b5922fffab314e
languageName: node
linkType: hard
"@tsparticles/engine@npm:3.9.1, @tsparticles/engine@npm:^3.7.1":
version: 3.9.1
resolution: "@tsparticles/engine@npm:3.9.1"
checksum: 10/91e95f33d526558e0f7251a75dc2971873a7854bb903b61aab95d394c954d3d5f6c2429c151483ebe83445e14e2a7ed9ceadb0fd9c0b7e8c11ec316e4bfd04fa
"@tsparticles/engine@npm:4.0.0":
version: 4.0.0
resolution: "@tsparticles/engine@npm:4.0.0"
checksum: 10/05fd84ad82f75c9a9d44280ed948d9340cbfb5b18a18f112ee08c0c7d70ba35687197c9ce7508f7c2600410f42075369f2708504485912eb57e5e6e1cf8394ef
languageName: node
linkType: hard
"@tsparticles/interaction-particles-links@npm:^3.7.1":
version: 3.9.1
resolution: "@tsparticles/interaction-particles-links@npm:3.9.1"
dependencies:
"@tsparticles/engine": "npm:3.9.1"
checksum: 10/be3925f0892de0eb9a4bc35b1ad402a462874272174379625bccce4c162a528d4f2a4526398f1b4a8b53ff27dae95e2b42a799a9c81ab99e233a9d16c7123774
"@tsparticles/interaction-particles-links@npm:4.0.0":
version: 4.0.0
resolution: "@tsparticles/interaction-particles-links@npm:4.0.0"
peerDependencies:
"@tsparticles/canvas-utils": 4.0.0
"@tsparticles/engine": 4.0.0
"@tsparticles/plugin-interactivity": 4.0.0
checksum: 10/98d7defc0af362775339f4d7016fa096d352065579036837c99c1a721d88f3207a074075d78130b95236e60a77f66dd29fe957b42e26b7421bdfb797e3271a28
languageName: node
linkType: hard
"@tsparticles/move-base@npm:3.9.1":
version: 3.9.1
resolution: "@tsparticles/move-base@npm:3.9.1"
dependencies:
"@tsparticles/engine": "npm:3.9.1"
checksum: 10/d03795bb4d789295ce4179e1b22d618658a15c31915cba5c8f137bf4a8f183186e3969145ef3951df07fddea0e9d1830a4e25a22baa70d904769c488041da40c
"@tsparticles/plugin-hex-color@npm:4.0.0":
version: 4.0.0
resolution: "@tsparticles/plugin-hex-color@npm:4.0.0"
peerDependencies:
"@tsparticles/engine": 4.0.0
checksum: 10/356310741b0019bcdd352e2affc9090662d7191aff932a1e2743c8d1911f87bd8a49a623a3733cf032911884d52b4e65a291267f37e28513c597d5cd4cadba1e
languageName: node
linkType: hard
"@tsparticles/plugin-hex-color@npm:3.9.1":
version: 3.9.1
resolution: "@tsparticles/plugin-hex-color@npm:3.9.1"
dependencies:
"@tsparticles/engine": "npm:3.9.1"
checksum: 10/726a2ae6182bc6e40ed443e1d664bae7ddb4e606a108e92a0c5fc50f0623105144672295720c06e915ef0e36c2a2455ed80dc335a850f11e3a1de1ad44e4ed08
"@tsparticles/plugin-hsl-color@npm:4.0.0":
version: 4.0.0
resolution: "@tsparticles/plugin-hsl-color@npm:4.0.0"
peerDependencies:
"@tsparticles/engine": 4.0.0
checksum: 10/cb04a297a40fdad04f25dfaa10d77a04c70dceded6c24e6f3ab766d5cc70514464011527d822fc883bb5da5003fdd5d8ab92b71046cb290a3c8ca60f7a40483f
languageName: node
linkType: hard
"@tsparticles/plugin-hsl-color@npm:3.9.1":
version: 3.9.1
resolution: "@tsparticles/plugin-hsl-color@npm:3.9.1"
dependencies:
"@tsparticles/engine": "npm:3.9.1"
checksum: 10/f81aaed365045e437c8f1627c03f3d255dd2bba2d5ff5231b5e90d576b24fbb5dc110f3b860e13d8696ef820feb465414c0e62962a59e55e6e4a86883cb0f003
"@tsparticles/plugin-interactivity@npm:4.0.0":
version: 4.0.0
resolution: "@tsparticles/plugin-interactivity@npm:4.0.0"
peerDependencies:
"@tsparticles/engine": 4.0.0
checksum: 10/23b089e537f5fed67fe7e3cf6b67aa639d9586ebda6838d349d9a02095449ca7bcfe164040a9fa08bdfd779f44ae5917434a335e5c67db5df6d6a6bfd521819e
languageName: node
linkType: hard
"@tsparticles/plugin-rgb-color@npm:3.9.1":
version: 3.9.1
resolution: "@tsparticles/plugin-rgb-color@npm:3.9.1"
dependencies:
"@tsparticles/engine": "npm:3.9.1"
checksum: 10/17352010973ad83e6c9292722896dd0eef0b9f4411684d3fbcf110363bd2aa41594a77b28709dbf1ee9945624567d945ef71900cb37630b0da68714375333c6f
"@tsparticles/plugin-move@npm:4.0.0":
version: 4.0.0
resolution: "@tsparticles/plugin-move@npm:4.0.0"
peerDependencies:
"@tsparticles/engine": 4.0.0
checksum: 10/abc5d175105243171ce440d88ae322f2412fb1c390bd39859fde1dc201b528e5d6099f38fa4a87a47ff110eaf65a930306922c409b1404271ba61d222ca9e48d
languageName: node
linkType: hard
"@tsparticles/preset-links@npm:3.2.0":
version: 3.2.0
resolution: "@tsparticles/preset-links@npm:3.2.0"
dependencies:
"@tsparticles/basic": "npm:^3.7.1"
"@tsparticles/engine": "npm:^3.7.1"
"@tsparticles/interaction-particles-links": "npm:^3.7.1"
checksum: 10/1cae6c097d3cac1ba210ed681a40626a79f8579a4e82b1827e0b5864b1cb1fb737471f699800447a7a2bd6e17c706b05db36f84741d9f0c9600bd638e7e29999
"@tsparticles/plugin-rgb-color@npm:4.0.0":
version: 4.0.0
resolution: "@tsparticles/plugin-rgb-color@npm:4.0.0"
peerDependencies:
"@tsparticles/engine": 4.0.0
checksum: 10/7d2255d5f428c56cd56fcf00839707ca3fa554a25fa59b2353e30c1c275ca24f06b9f2eee28a869633a740c56b3609cb6540135fb7297ec54d5fcc140e2ce575
languageName: node
linkType: hard
"@tsparticles/shape-circle@npm:3.9.1":
version: 3.9.1
resolution: "@tsparticles/shape-circle@npm:3.9.1"
"@tsparticles/preset-links@npm:4.0.0":
version: 4.0.0
resolution: "@tsparticles/preset-links@npm:4.0.0"
dependencies:
"@tsparticles/engine": "npm:3.9.1"
checksum: 10/1f0e5add252ee6e59b32b018b585106189a8938798e879a5a09b42434091c82f748b7656206d316705d65821f45e87bb9ef4c7a240c33be4384dbef0def1e2f5
"@tsparticles/basic": "npm:4.0.0"
"@tsparticles/engine": "npm:4.0.0"
"@tsparticles/interaction-particles-links": "npm:4.0.0"
"@tsparticles/plugin-interactivity": "npm:4.0.0"
checksum: 10/67d8c4c90c44a9f3f940aa249e2b86acef4485a54ac02005de5b947cc3c2d01d4b6b00695dc5ff47ab0a8594faa18ba2ccf05b7510f7fd84993939f7d137121e
languageName: node
linkType: hard
"@tsparticles/updater-color@npm:3.9.1":
version: 3.9.1
resolution: "@tsparticles/updater-color@npm:3.9.1"
dependencies:
"@tsparticles/engine": "npm:3.9.1"
checksum: 10/5c4cb7fc7f4767461abffd3ba90ee2c5dba8a7cd3a38d6278314bb9d074c7e5f4977844f4c84448af00e24c923e4635f9392b320fffbac644ae59f157c6ed5b0
"@tsparticles/shape-circle@npm:4.0.0":
version: 4.0.0
resolution: "@tsparticles/shape-circle@npm:4.0.0"
peerDependencies:
"@tsparticles/engine": 4.0.0
checksum: 10/37f010c44ba9b82712532da32c2cb6e482d5f5205aaf3bcd7fb04c87925218cd732a31d69ae424613810f11b2651a80fbbd9ec9d6b9d0e58884022ec68cc9d5b
languageName: node
linkType: hard
"@tsparticles/updater-opacity@npm:3.9.1":
version: 3.9.1
resolution: "@tsparticles/updater-opacity@npm:3.9.1"
dependencies:
"@tsparticles/engine": "npm:3.9.1"
checksum: 10/c0ecfd623bdb9cf6ece47098403cb19ce4d9c6204b19ca65357c106f400b4faf2e8f3a51a7ae518b9f708ba549073b4ea8ad8f2282dc298016437a62659298f4
"@tsparticles/updater-opacity@npm:4.0.0":
version: 4.0.0
resolution: "@tsparticles/updater-opacity@npm:4.0.0"
peerDependencies:
"@tsparticles/engine": 4.0.0
checksum: 10/b078a28175372246d6861562bcc013c03dff88f6aeaf9b16558e533477340e1b3cd07d57997bdf11b730636df26ef0117bef9ba4ab1b791a9b9a4cd1d2e6623e
languageName: node
linkType: hard
"@tsparticles/updater-out-modes@npm:3.9.1":
version: 3.9.1
resolution: "@tsparticles/updater-out-modes@npm:3.9.1"
dependencies:
"@tsparticles/engine": "npm:3.9.1"
checksum: 10/b74bb0987aacabaeabc981fbbeffb362263ad69cea2f1733b0f5b8753a5c7338ca51eb02164a41d0a70b75ae0bd6e2f2c16c0bc0b4805b367122536df110f21a
"@tsparticles/updater-out-modes@npm:4.0.0":
version: 4.0.0
resolution: "@tsparticles/updater-out-modes@npm:4.0.0"
peerDependencies:
"@tsparticles/engine": 4.0.0
checksum: 10/3b27af0a68a7f320ae2481142ff9d6e342b75f245b94e875ea2bdc3f9f47c0b8e79b7ddbf45fea5eeaa9e4610dbdb7130e10056b1f26618958e7e6c28e25dbbe
languageName: node
linkType: hard
"@tsparticles/updater-size@npm:3.9.1":
version: 3.9.1
resolution: "@tsparticles/updater-size@npm:3.9.1"
dependencies:
"@tsparticles/engine": "npm:3.9.1"
checksum: 10/211038b9cadd1df1a0fb747d2e1eeff9f1ce57fd4828e838ddfe6b5365feb3f625e7f713380bc819601d88c50ba4b122a401eae627449760d94c95cff85efdb3
"@tsparticles/updater-paint@npm:4.0.0":
version: 4.0.0
resolution: "@tsparticles/updater-paint@npm:4.0.0"
peerDependencies:
"@tsparticles/engine": 4.0.0
checksum: 10/22f9c275ee3eb1409923c74b63fc5984d978b68473bd0f58418d6c28a9be0e9e2e010e338adb7deb482021ab444fab85b962745c06d94066dd40265cf016395d
languageName: node
linkType: hard
"@tsparticles/updater-size@npm:4.0.0":
version: 4.0.0
resolution: "@tsparticles/updater-size@npm:4.0.0"
peerDependencies:
"@tsparticles/engine": 4.0.0
checksum: 10/4664fc5c4c961331d733d98287e9fef0079077def528b814034aea163eb95e2f6c0549e1b87e95eef5f376a7cd0983fbf18c2d77023b9f1f1b163698b03ec504
languageName: node
linkType: hard
@@ -8762,8 +8774,8 @@ __metadata:
"@rspack/dev-server": "npm:2.0.1"
"@swc/helpers": "npm:0.5.21"
"@thomasloven/round-slider": "npm:0.6.0"
"@tsparticles/engine": "npm:3.9.1"
"@tsparticles/preset-links": "npm:3.2.0"
"@tsparticles/engine": "npm:4.0.0"
"@tsparticles/preset-links": "npm:4.0.0"
"@types/babel__plugin-transform-runtime": "npm:7.9.5"
"@types/chromecast-caf-receiver": "npm:6.0.26"
"@types/chromecast-caf-sender": "npm:1.0.11"