Compare commits

..

5 Commits

Author SHA1 Message Date
Aidan Timson
bd10e73510 Drop examples 2025-09-26 10:51:24 +01:00
Aidan Timson
3dd4e3fcd6 Manage disposers from manager and document methods 2025-09-25 11:42:00 +01:00
Aidan Timson
3c0430f424 Switch to set and allow removal of all or a list 2025-09-25 11:32:00 +01:00
Aidan Timson
e703b0c3e0 Migrate quick bar mixin to use helper 2025-09-25 11:18:56 +01:00
Aidan Timson
eb5b5275b1 Create keyboard shortcuts helper 2025-09-25 11:17:57 +01:00
27 changed files with 505 additions and 556 deletions

File diff suppressed because one or more lines are too long

View File

@@ -6,4 +6,4 @@ enableGlobalCache: false
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.10.3.cjs
yarnPath: .yarn/releases/yarn-4.10.2.cjs

View File

@@ -5,17 +5,17 @@ const castContext = framework.CastReceiverContext.getInstance();
const playerManager = castContext.getPlayerManager();
playerManager.setMessageInterceptor(
framework.messages.MessageType.LOAD,
"LOAD" as framework.messages.MessageType.LOAD,
(loadRequestData) => {
const media = loadRequestData.media;
// Special handling if it came from Google Assistant
if (media.entity) {
media.contentId = media.entity;
media.streamType = framework.messages.StreamType.LIVE;
media.streamType = "LIVE" as framework.messages.StreamType.LIVE;
media.contentType = "application/vnd.apple.mpegurl";
// @ts-ignore
media.hlsVideoSegmentFormat =
framework.messages.HlsVideoSegmentFormat.FMP4;
"fmp4" as framework.messages.HlsVideoSegmentFormat.FMP4;
}
return loadRequestData;
}

View File

@@ -40,7 +40,8 @@ const playDummyMedia = (viewTitle?: string) => {
loadRequestData.media.contentId =
"https://cast.home-assistant.io/images/google-nest-hub.png";
loadRequestData.media.contentType = "image/jpeg";
loadRequestData.media.streamType = framework.messages.StreamType.NONE;
loadRequestData.media.streamType =
"NONE" as framework.messages.StreamType.NONE;
const metadata = new framework.messages.GenericMediaMetadata();
metadata.title = viewTitle;
loadRequestData.media.metadata = metadata;
@@ -89,7 +90,7 @@ const showMediaPlayer = () => {
const options = new framework.CastReceiverOptions();
options.disableIdleTimeout = true;
options.customNamespaces = {
[CAST_NS]: framework.system.MessageType.JSON,
[CAST_NS]: "json" as framework.system.MessageType.JSON,
};
castContext.addCustomMessageListener(
@@ -97,9 +98,7 @@ castContext.addCustomMessageListener(
// @ts-ignore
(ev: ReceivedMessage<HassMessage>) => {
// We received a show Lovelace command, stop media from playing, hide media player and show Lovelace controller
if (
playerManager.getPlayerState() !== framework.messages.PlayerState.IDLE
) {
if (playerManager.getPlayerState() !== "IDLE") {
playerManager.stop();
} else {
showLovelaceController();
@@ -113,7 +112,7 @@ castContext.addCustomMessageListener(
const playerManager = castContext.getPlayerManager();
playerManager.setMessageInterceptor(
framework.messages.MessageType.LOAD,
"LOAD" as framework.messages.MessageType.LOAD,
(loadRequestData) => {
if (
loadRequestData.media.contentId ===
@@ -127,24 +126,23 @@ playerManager.setMessageInterceptor(
// Special handling if it came from Google Assistant
if (media.entity) {
media.contentId = media.entity;
media.streamType = framework.messages.StreamType.LIVE;
media.streamType = "LIVE" as framework.messages.StreamType.LIVE;
media.contentType = "application/vnd.apple.mpegurl";
// @ts-ignore
media.hlsVideoSegmentFormat =
framework.messages.HlsVideoSegmentFormat.FMP4;
"fmp4" as framework.messages.HlsVideoSegmentFormat.FMP4;
}
return loadRequestData;
}
);
playerManager.addEventListener(
framework.events.EventType.MEDIA_STATUS,
"MEDIA_STATUS" as framework.events.EventType.MEDIA_STATUS,
(event) => {
if (
event.mediaStatus?.playerState === framework.messages.PlayerState.IDLE &&
event.mediaStatus?.playerState === "IDLE" &&
event.mediaStatus?.idleReason &&
event.mediaStatus?.idleReason !==
framework.messages.IdleReason.INTERRUPTED
event.mediaStatus?.idleReason !== "INTERRUPTED"
) {
// media finished or stopped, return to default Lovelace
showLovelaceController();

View File

@@ -34,7 +34,7 @@
"@codemirror/legacy-modes": "6.5.1",
"@codemirror/search": "6.5.11",
"@codemirror/state": "6.5.2",
"@codemirror/view": "6.38.3",
"@codemirror/view": "6.38.2",
"@date-fns/tz": "1.4.1",
"@egjs/hammerjs": "2.0.17",
"@formatjs/intl-datetimeformat": "6.18.0",
@@ -111,7 +111,7 @@
"fuse.js": "7.1.0",
"google-timezones-json": "1.2.0",
"gulp-zopfli-green": "6.0.2",
"hls.js": "1.6.13",
"hls.js": "1.6.12",
"home-assistant-js-websocket": "9.5.0",
"idb-keyval": "6.2.2",
"intl-messageformat": "10.7.16",
@@ -158,10 +158,10 @@
"@octokit/plugin-retry": "8.0.1",
"@octokit/rest": "22.0.0",
"@rsdoctor/rspack-plugin": "1.2.3",
"@rspack/core": "1.5.6",
"@rspack/core": "1.5.5",
"@rspack/dev-server": "1.1.4",
"@types/babel__plugin-transform-runtime": "7.9.5",
"@types/chromecast-caf-receiver": "6.0.22",
"@types/chromecast-caf-receiver": "6.0.24",
"@types/chromecast-caf-sender": "1.0.11",
"@types/color-name": "2.0.0",
"@types/culori": "4.0.1",
@@ -213,11 +213,11 @@
"rspack-manifest-plugin": "5.1.0",
"serve": "14.2.5",
"sinon": "21.0.0",
"tar": "7.5.1",
"tar": "7.4.4",
"terser-webpack-plugin": "5.3.14",
"ts-lit-plugin": "2.0.2",
"typescript": "5.9.2",
"typescript-eslint": "8.44.1",
"typescript-eslint": "8.44.0",
"vite-tsconfig-paths": "5.1.4",
"vitest": "3.2.4",
"webpack-stats-plugin": "1.1.3",
@@ -235,5 +235,5 @@
"tslib": "2.8.1",
"@material/mwc-list@^0.27.0": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch"
},
"packageManager": "yarn@4.10.3"
"packageManager": "yarn@4.10.2"
}

View File

@@ -1,40 +1,23 @@
import { formatHex, parse } from "culori";
/**
* Expands a 3-digit hex color to a 6-digit hex color.
* @param hex - The hex color to expand.
* @returns The expanded hex color.
* @throws If the hex color is invalid.
*/
export const expandHex = (hex: string): string => {
const color = parse(hex);
if (!color) {
throw new Error(`Invalid hex color: ${hex}`);
hex = hex.replace("#", "");
if (hex.length === 6) return hex;
let result = "";
for (const val of hex) {
result += val + val;
}
const formattedColor = formatHex(color);
if (!formattedColor) {
throw new Error(`Could not format hex color: ${hex}`);
}
return formattedColor.replace("#", "");
return result;
};
/**
* Blends two hex colors. c1 is placed over c2, blend is c1's opacity.
* @param c1 - The first hex color.
* @param c2 - The second hex color.
* @param blend - The blend percentage (0-100).
* @returns The blended hex color.
*/
// Blend 2 hex colors: c1 is placed over c2, blend is c1's opacity.
export const hexBlend = (c1: string, c2: string, blend = 50): string => {
let color = "";
c1 = expandHex(c1);
c2 = expandHex(c2);
let color = "";
for (let i = 0; i <= 5; i += 2) {
const h1 = parseInt(c1.substring(i, i + 2), 16);
const h2 = parseInt(c2.substring(i, i + 2), 16);
const hex = Math.floor(h2 + (h1 - h2) * (blend / 100))
.toString(16)
.padStart(2, "0");
let hex = Math.floor(h2 + (h1 - h2) * (blend / 100)).toString(16);
while (hex.length < 2) hex = "0" + hex;
color += hex;
}
return `#${color}`;

View File

@@ -1,49 +1,28 @@
import { wcagLuminance, wcagContrast } from "culori";
export const luminosity = (rgb: [number, number, number]): number => {
// http://www.w3.org/TR/WCAG20/#relativeluminancedef
const lum: [number, number, number] = [0, 0, 0];
for (let i = 0; i < rgb.length; i++) {
const chan = rgb[i] / 255;
lum[i] = chan <= 0.03928 ? chan / 12.92 : ((chan + 0.055) / 1.055) ** 2.4;
}
/**
* Calculates the luminosity of an RGB color.
* @param rgb - The RGB color to calculate the luminosity of.
* @returns The luminosity of the color.
*/
export const luminosity = (rgb: [number, number, number]): number =>
wcagLuminance({
mode: "rgb",
r: rgb[0] / 255,
g: rgb[1] / 255,
b: rgb[2] / 255,
});
return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
};
/**
* Calculates the contrast ratio between two RGB colors.
* @param color1 - The first color to calculate the contrast ratio of.
* @param color2 - The second color to calculate the contrast ratio of.
* @returns The contrast ratio between the two colors.
*/
export const rgbContrast = (
color1: [number, number, number],
color2: [number, number, number]
) =>
wcagContrast(
{
mode: "rgb",
r: color1[0] / 255,
g: color1[1] / 255,
b: color1[2] / 255,
},
{
mode: "rgb",
r: color2[0] / 255,
g: color2[1] / 255,
b: color2[2] / 255,
}
);
) => {
const lum1 = luminosity(color1);
const lum2 = luminosity(color2);
if (lum1 > lum2) {
return (lum1 + 0.05) / (lum2 + 0.05);
}
return (lum2 + 0.05) / (lum1 + 0.05);
};
/**
* Calculates the contrast ratio between two RGB colors.
* @param rgb1 - The first color to calculate the contrast ratio of.
* @param rgb2 - The second color to calculate the contrast ratio of.
* @returns The contrast ratio between the two colors.
*/
export const getRGBContrastRatio = (
rgb1: [number, number, number],
rgb2: [number, number, number]

View File

@@ -0,0 +1,103 @@
import { tinykeys } from "tinykeys";
import { canOverrideAlphanumericInput } from "../dom/can-override-input";
import type { HomeAssistant } from "../../types";
export type ShortcutHandler = (event: KeyboardEvent) => void;
export interface ShortcutManager {
/**
* Add a group of keyboard shortcuts to the manager.
*
* @param shortcuts - Key combinations mapped to handler functions.
* Uses tinykeys syntax. See https://github.com/jamiebuilds/tinykeys#usage.
*/
add: (shortcuts: Record<string, ShortcutHandler>) => void;
/**
* Remove shortcuts from the manager.
*
* @param keys - Optional array of specific key combinations to remove. If provided,
* only shortcuts matching these keys will be removed. If omitted, all shortcuts
* from this manager will be removed.
*/
remove: (keys?: string[]) => void;
}
interface ShortcutEntry {
keys: Set<string>;
disposer: () => void;
}
/**
* Register keyboard shortcuts using tinykeys.
*
* @param shortcuts - Key combinations mapped to handler functions.
* @returns A function to remove the shortcuts.
*/
function registerShortcuts(
shortcuts: Record<string, ShortcutHandler>
): () => void {
const wrappedShortcuts: Record<string, ShortcutHandler> = {};
for (const [key, handler] of Object.entries(shortcuts)) {
wrappedShortcuts[key] = (event: KeyboardEvent) => {
if (!canOverrideAlphanumericInput(event.composedPath())) {
return;
}
if (window.getSelection()?.toString()) {
return;
}
handler(event);
};
}
// Underlying implementation (tinykeys for now)
return tinykeys(window, wrappedShortcuts);
}
/**
* Create a shortcut manager that can add and dispose shortcuts.
*
* @param hass - Home Assistant context to check if shortcuts are enabled.
* @returns A shortcut manager containing the add and remove methods.
*/
export function createShortcutManager(hass?: HomeAssistant): ShortcutManager {
const shortcutEntries: ShortcutEntry[] = [];
return {
add(shortcuts: Record<string, ShortcutHandler>) {
// Skip registration if shortcuts are disabled
if (hass && !hass.enableShortcuts) {
return;
}
const disposer = registerShortcuts(shortcuts);
const keys = new Set(Object.keys(shortcuts));
const entry: ShortcutEntry = { keys, disposer };
shortcutEntries.push(entry);
},
remove(keys?: string[]) {
if (keys) {
const entriesToRemove: ShortcutEntry[] = [];
for (const entry of shortcutEntries) {
if (keys.some((key) => entry.keys.has(key))) {
entry.disposer();
entriesToRemove.push(entry);
}
}
for (const entry of entriesToRemove) {
const index = shortcutEntries.indexOf(entry);
if (index !== -1) {
shortcutEntries.splice(index, 1);
}
}
} else {
shortcutEntries.forEach((entry) => entry.disposer());
shortcutEntries.length = 0;
}
},
};
}

View File

@@ -1,5 +1,5 @@
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import type { LocalizeFunc } from "../common/translations/localize";
@@ -73,18 +73,14 @@ export class HaAnalytics extends LitElement {
.checked=${this.analytics?.preferences[preference]}
.preference=${preference}
name=${preference}
?disabled=${baseEnabled}
>
</ha-switch>
${baseEnabled
? nothing
: html`<ha-tooltip
.for="switch-${preference}"
placement="right"
>
${this.localize(
`ui.panel.${this.translationKeyPanel}.analytics.need_base_enabled`
)}
</ha-tooltip>`}
<ha-tooltip .for="switch-${preference}" placement="right">
${this.localize(
`ui.panel.${this.translationKeyPanel}.analytics.need_base_enabled`
)}
</ha-tooltip>
</span>
</ha-settings-row>
`

View File

@@ -39,24 +39,22 @@ class HaSegmentedBar extends LitElement {
<slot name="extra"></slot>
</div>
<div class="bar">
${this.segments.map(
(segment, index) => html`
${this.hideTooltip || !segment.label
? nothing
: html`
<ha-tooltip for="segment-${index}" placement="top">
${segment.label}
</ha-tooltip>
`}
<div
id="segment-${index}"
style=${styleMap({
width: `${(segment.value / totalValue) * 100}%`,
backgroundColor: segment.color,
})}
></div>
`
)}
${this.segments.map((segment) => {
const bar = html`<div
style=${styleMap({
width: `${(segment.value / totalValue) * 100}%`,
backgroundColor: segment.color,
})}
></div>`;
return this.hideTooltip && !segment.label
? bar
: html`
<ha-tooltip>
<span slot="content">${segment.label}</span>
${bar}
</ha-tooltip>
`;
})}
</div>
${this.hideLegend
? nothing

View File

@@ -82,12 +82,12 @@ export class HaNumberSelector extends LitElement {
labeled
.min=${this.selector.number!.min}
.max=${this.selector.number!.max}
.value=${this.value}
.value=${this.value ?? ""}
.step=${sliderStep}
.disabled=${this.disabled}
.required=${this.required}
@change=${this._handleSliderChange}
.withMarkers=${this.selector.number?.slider_ticks || false}
.ticks=${this.selector.number?.slider_ticks}
>
</ha-slider>
`

View File

@@ -18,8 +18,6 @@ export class HaTabGroupTab extends Tab {
opacity: 0.8;
color: inherit;
--wa-space-l: 16px;
}
:host([active]:not([disabled])) {

View File

@@ -4,7 +4,6 @@ export interface LovelaceBadgeConfig {
type: string;
[key: string]: any;
visibility?: Condition[];
disabled?: boolean;
}
export const ensureBadgeConfig = (

View File

@@ -48,10 +48,10 @@ class MoreInfoMediaPlayer extends LitElement {
@property({ attribute: false }) public stateObj?: MediaPlayerEntity;
private _formatDuration(duration: number) {
private _formateDuration(duration: number) {
const hours = Math.floor(duration / 3600);
const minutes = Math.floor((duration % 3600) / 60);
const seconds = Math.floor(duration % 60);
const seconds = duration % 60;
return formatDurationDigital(this.hass.locale, {
hours,
minutes,
@@ -260,12 +260,12 @@ class MoreInfoMediaPlayer extends LitElement {
const controls = computeMediaControls(stateObj, true);
const coverUrl = stateObj.attributes.entity_picture || "";
const playerObj = new HassMediaPlayerEntity(this.hass, this.stateObj);
const position = Math.max(Math.floor(playerObj.currentProgress || 0), 0);
const duration = Math.max(stateObj.attributes.media_duration || 0, 0);
const remaining = Math.max(duration - position, 0);
const remainingFormatted = this._formatDuration(remaining);
const positionFormatted = this._formatDuration(position);
const position = Math.floor(playerObj.currentProgress) || 0;
const duration = stateObj.attributes.media_duration || 0;
const remaining = duration - position;
const durationFormated =
remaining > 0 ? this._formateDuration(remaining) : 0;
const postionFormated = this._formateDuration(position);
const primaryTitle = playerObj.primaryTitle;
const secondaryTitle = playerObj.secondaryTitle;
const turnOn = controls?.find((c) => c.action === "turn_on");
@@ -323,10 +323,11 @@ class MoreInfoMediaPlayer extends LitElement {
@change=${this._handleMediaSeekChanged}
?disabled=${!stateActive(stateObj) ||
!supportsFeature(stateObj, MediaPlayerEntityFeature.SEEK)}
>
<span slot="reference">${positionFormatted}</span>
<span slot="reference">${remainingFormatted}</span>
</ha-slider>
></ha-slider>
<div class="position-info-row">
<span class="position-time">${postionFormated}</span>
<span class="duration-time">${durationFormated}</span>
</div>
</div>
`
: nothing}
@@ -547,8 +548,13 @@ class MoreInfoMediaPlayer extends LitElement {
flex-direction: column;
}
.position-bar ha-slider::part(references) {
.position-info-row {
display: flex;
flex-direction: row;
justify-content: space-between;
color: var(--secondary-text-color);
padding: 0 8px;
font-size: var(--ha-font-size-s);
}
.media-info-row {

View File

@@ -8,7 +8,6 @@ import { createCloseHeading } from "../../components/ha-dialog";
import "../../components/ha-textarea";
import type { HaTextArea } from "../../components/ha-textarea";
import { convertTextToSpeech } from "../../data/tts";
import { haStyleDialog } from "../../resources/styles";
import type { HomeAssistant } from "../../types";
import { showAlertDialog } from "../generic/show-dialog-box";
import type { TTSTryDialogParams } from "./show-dialog-tts-try";
@@ -150,24 +149,21 @@ export class TTSTryDialog extends LitElement {
});
}
static styles = [
haStyleDialog,
css`
ha-dialog {
--mdc-dialog-max-width: 500px;
}
ha-textarea,
ha-select {
width: 100%;
}
ha-select {
margin-top: 8px;
}
.loading {
height: 36px;
}
`,
];
static styles = css`
ha-dialog {
--mdc-dialog-max-width: 500px;
}
ha-textarea,
ha-select {
width: 100%;
}
ha-select {
margin-top: 8px;
}
.loading {
height: 36px;
}
`;
}
declare global {

View File

@@ -213,7 +213,6 @@ class HaConfigEnergy extends LitElement {
this.hass.states[key],
])
),
issues: this._validationResult,
};
const json = JSON.stringify(data, null, 2);
const blob = new Blob([json], { type: "application/json" });

View File

@@ -143,9 +143,7 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
slot="fab"
@click=${this._importExternalThreadCredentials}
extended
.label=${this.hass.localize(
"ui.panel.config.thread.thread_network_send_credentials_ha"
)}
label="Send credentials to Home Assistant"
><ha-svg-icon slot="icon" .path=${mdiCellphoneKey}></ha-svg-icon
></ha-fab>`
: nothing}
@@ -312,9 +310,7 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
<ha-button
.datasetId=${network.dataset.dataset_id}
@click=${this._setPreferred}
>${this.hass.localize(
"ui.panel.config.thread.thread_network_make_preferred"
)}</ha-button
>Make preferred network</ha-button
>
</div>`
: ""}
@@ -326,9 +322,7 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
size="small"
.networkDataset=${network.dataset}
@click=${this._sendCredentials}
>${this.hass.localize(
"ui.panel.config.thread.thread_network_send_credentials_phone"
)}</ha-button
>Send credentials to phone</ha-button
>
</div>`
: ""}

View File

@@ -161,7 +161,7 @@ export class HuiBadge extends ReactiveElement {
);
}
private _updateVisibility(ignoreConditions?: boolean) {
private _updateVisibility(forceVisible?: boolean) {
if (!this._element || !this.hass) {
return;
}
@@ -171,18 +171,9 @@ export class HuiBadge extends ReactiveElement {
return;
}
if (this.preview) {
this._setElementVisibility(true);
return;
}
if (this.config?.disabled) {
this._setElementVisibility(false);
return;
}
const visible =
ignoreConditions ||
forceVisible ||
this.preview ||
!this.config?.visibility ||
checkConditionsMet(this.config.visibility, this.hass);
this._setElementVisibility(visible);

View File

@@ -1,32 +1,30 @@
import { mdiWaterBoiler } from "@mdi/js";
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import { stopPropagation } from "../../../common/dom/stop_propagation";
import { computeDomain } from "../../../common/entity/compute_domain";
import { stateColorCss } from "../../../common/entity/state_color";
import "../../../components/ha-control-button";
import "../../../components/ha-control-button-group";
import "../../../components/ha-control-select";
import type { ControlSelectOption } from "../../../components/ha-control-select";
import "../../../components/ha-control-select-menu";
import type { HaControlSelectMenu } from "../../../components/ha-control-select-menu";
import "../../../components/ha-list-item";
import "../../../components/ha-control-slider";
import { UNAVAILABLE } from "../../../data/entity";
import type {
OperationMode,
WaterHeaterEntity,
} from "../../../data/water_heater";
import {
computeOperationModeIcon,
compareWaterHeaterOperationMode,
computeOperationModeIcon,
} from "../../../data/water_heater";
import { UNAVAILABLE } from "../../../data/entity";
import type { HomeAssistant } from "../../../types";
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
import { cardFeatureStyles } from "./common/card-feature-styles";
import { filterModes } from "./common/filter-modes";
import type {
WaterHeaterOperationModesCardFeatureConfig,
LovelaceCardFeatureContext,
WaterHeaterOperationModesCardFeatureConfig,
} from "./types";
export const supportsWaterHeaterOperationModesCardFeature = (
@@ -54,9 +52,6 @@ class HuiWaterHeaterOperationModeCardFeature
@state() _currentOperationMode?: OperationMode;
@query("ha-control-select-menu", true)
private _haSelect?: HaControlSelectMenu;
private get _stateObj() {
if (!this.hass || !this.context || !this.context.entity_id) {
return undefined;
@@ -102,23 +97,8 @@ class HuiWaterHeaterOperationModeCardFeature
}
}
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (this._haSelect && changedProps.has("hass")) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (
this.hass &&
this.hass.formatEntityAttributeValue !==
oldHass?.formatEntityAttributeValue
) {
this._haSelect.layoutOptions();
}
}
}
private async _valueChanged(ev: CustomEvent) {
const mode =
(ev.detail as any).value ?? ((ev.target as any).value as OperationMode);
const mode = (ev.detail as any).value as OperationMode;
if (mode === this._stateObj!.state) return;
@@ -163,48 +143,9 @@ class HuiWaterHeaterOperationModeCardFeature
).map<ControlSelectOption>((mode) => ({
value: mode,
label: this.hass!.formatEntityState(this._stateObj!, mode),
icon: html`
<ha-svg-icon
slot="graphic"
.path=${computeOperationModeIcon(mode as OperationMode)}
></ha-svg-icon>
`,
path: computeOperationModeIcon(mode as OperationMode),
}));
if (this._config.style === "dropdown") {
return html`
<ha-control-select-menu
show-arrow
hide-label
.label=${this.hass.localize("ui.card.water_heater.mode")}
.value=${this._currentOperationMode}
.disabled=${this._stateObj.state === UNAVAILABLE}
fixedMenuPosition
naturalMenuWidth
@selected=${this._valueChanged}
@closed=${stopPropagation}
>
${this._currentOperationMode
? html`
<ha-svg-icon
slot="icon"
.path=${computeOperationModeIcon(this._currentOperationMode)}
></ha-svg-icon>
`
: html`
<ha-svg-icon slot="icon" .path=${mdiWaterBoiler}></ha-svg-icon>
`}
${options.map(
(option) => html`
<ha-list-item .value=${option.value} graphic="icon">
${option.icon}${option.label}
</ha-list-item>
`
)}
</ha-control-select-menu>
`;
}
return html`
<ha-control-select
.options=${options}

View File

@@ -140,7 +140,6 @@ export interface ToggleCardFeatureConfig {
export interface WaterHeaterOperationModesCardFeatureConfig {
type: "water-heater-operation-modes";
style?: "dropdown" | "icons";
operation_modes?: OperationMode[];
}

View File

@@ -16,7 +16,6 @@ import type {
} from "../../card-features/types";
import type { LovelaceCardFeatureEditor } from "../../types";
import { compareWaterHeaterOperationMode } from "../../../../data/water_heater";
import type { LocalizeFunc } from "../../../../common/translations/localize";
type WaterHeaterOperationModesCardFeatureData =
WaterHeaterOperationModesCardFeatureConfig & {
@@ -40,27 +39,11 @@ export class HuiWaterHeaterOperationModesCardFeatureEditor
private _schema = memoizeOne(
(
localize: LocalizeFunc,
formatEntityState: FormatEntityStateFunc,
stateObj: HassEntity | undefined,
customizeModes: boolean
) =>
[
{
name: "style",
selector: {
select: {
multiple: false,
mode: "list",
options: ["dropdown", "icons"].map((mode) => ({
value: mode,
label: localize(
`ui.panel.lovelace.editor.features.types.water-heater-operation-modes.style_list.${mode}`
),
})),
},
},
},
{
name: "customize_modes",
selector: {
@@ -102,13 +85,11 @@ export class HuiWaterHeaterOperationModesCardFeatureEditor
: undefined;
const data: WaterHeaterOperationModesCardFeatureData = {
style: "icons",
...this._config,
customize_modes: this._config.operation_modes !== undefined,
};
const schema = this._schema(
this.hass.localize,
this.hass.formatEntityState,
stateObj,
data.customize_modes
@@ -150,7 +131,6 @@ export class HuiWaterHeaterOperationModesCardFeatureEditor
) => {
switch (schema.name) {
case "operation_modes":
case "style":
case "customize_modes":
return this.hass!.localize(
`ui.panel.lovelace.editor.features.types.water-heater-operation-modes.${schema.name}`

View File

@@ -1,7 +1,6 @@
import { object, string, any, optional, boolean } from "superstruct";
import { object, string, any } from "superstruct";
export const baseLovelaceBadgeConfig = object({
type: string(),
visibility: any(),
disabled: optional(boolean()),
});

View File

@@ -1,4 +1,4 @@
import { object, string, any, optional, boolean } from "superstruct";
import { object, string, any } from "superstruct";
export const baseLovelaceCardConfig = object({
type: string(),
@@ -6,5 +6,4 @@ export const baseLovelaceCardConfig = object({
layout_options: any(),
grid_options: any(),
visibility: any(),
disabled: optional(boolean()),
});

View File

@@ -4,14 +4,13 @@ import { isComponentLoaded } from "../../../../common/config/is_component_loaded
import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section";
import { getCommonControlUsagePrediction } from "../../../../data/usage_prediction";
import type { HomeAssistant } from "../../../../types";
import type { HeadingCardConfig, TileCardConfig } from "../../cards/types";
import type { TileCardConfig } from "../../cards/types";
const DEFAULT_LIMIT = 8;
export interface CommonControlSectionStrategyConfig {
type: "common-controls";
title?: string;
icon?: string;
limit?: number;
exclude_entities?: string[];
hide_empty?: boolean;
@@ -32,8 +31,7 @@ export class CommonControlsSectionStrategy extends ReactiveElement {
section.cards?.push({
type: "heading",
heading: config.title,
icon: config.icon,
} satisfies HeadingCardConfig);
});
}
if (!isComponentLoaded(hass, "usage_prediction")) {

View File

@@ -1,5 +1,4 @@
import type { PropertyValues } from "lit";
import { tinykeys } from "tinykeys";
import memoizeOne from "memoize-one";
import { isComponentLoaded } from "../common/config/is_component_loaded";
import { mainWindow } from "../common/dom/get_main_window";
@@ -12,6 +11,7 @@ import type { Constructor, HomeAssistant } from "../types";
import { storeState } from "../util/ha-pref-storage";
import { showToast } from "../util/toast";
import type { HassElement } from "./hass-element";
import { createShortcutManager } from "../common/keyboard/shortcuts";
import { extractSearchParamsObject } from "../common/url/search-params";
import { showVoiceCommandDialog } from "../dialogs/voice-command-dialog/show-ha-voice-command-dialog";
import { canOverrideAlphanumericInput } from "../common/dom/can-override-input";
@@ -62,7 +62,8 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
}
private _registerShortcut() {
tinykeys(window, {
const shortcutManager = createShortcutManager(this.hass);
shortcutManager.add({
// Those are for latin keyboards that have e, c, m keys
e: (ev) => this._showQuickBar(ev),
c: (ev) => this._showQuickBar(ev, QuickBarMode.Command),

View File

@@ -5836,10 +5836,7 @@
"change_channel_range": "Channel must be in the range 11 to 26",
"change_channel_text": "Initiating a channel change for your Home Assistant Thread network should be performed with caution. Some Thread devices may not migrate to the new channel automatically and, if the new channel is congested, your Thread devices may become intermittently unavailable. Some devices may need to be manually re-joined to your Thread network before they show in Home Assistant again. This action cannot be reversed (without performing another channel change).",
"thread_network_info": "Thread network information",
"thread_network_delete_credentials": "Delete Thread network credentials",
"thread_network_send_credentials_ha": "Send credentials to Home Assistant",
"thread_network_send_credentials_phone": "Send credentials to phone",
"thread_network_make_preferred": "Make preferred network"
"thread_network_delete_credentials": "Delete Thread network credentials"
},
"ssdp": {
"name": "Name",
@@ -8220,12 +8217,7 @@
"water-heater-operation-modes": {
"label": "Water heater operation modes",
"operation_modes": "Operation modes",
"customize_modes": "Customize operation modes",
"style": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style%]",
"style_list": {
"dropdown": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::dropdown%]",
"icons": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::icons%]"
}
"customize_modes": "Customize operation modes"
},
"lawn-mower-commands": {
"label": "Lawn mower commands",

286
yarn.lock
View File

@@ -1284,15 +1284,15 @@ __metadata:
languageName: node
linkType: hard
"@codemirror/view@npm:6.38.3, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0":
version: 6.38.3
resolution: "@codemirror/view@npm:6.38.3"
"@codemirror/view@npm:6.38.2, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0":
version: 6.38.2
resolution: "@codemirror/view@npm:6.38.2"
dependencies:
"@codemirror/state": "npm:^6.5.0"
crelt: "npm:^1.0.6"
style-mod: "npm:^4.1.0"
w3c-keyname: "npm:^2.2.4"
checksum: 10/2df41450399cbac0eaf06dba822418dd6926e48344b9255902248075ef040c957dfe97fe842a755e284a2fd4a66dc17b9638385f46ad74e926baac2e797335a2
checksum: 10/300608850a29215d7b47fe8ade183fc2241457a924335bd127e29e1af11da9314369c65ec0da968177086f3529abbcd71a609c1af673ea8951c32a523cab358c
languageName: node
linkType: hard
@@ -4052,92 +4052,92 @@ __metadata:
languageName: node
linkType: hard
"@rspack/binding-darwin-arm64@npm:1.5.6":
version: 1.5.6
resolution: "@rspack/binding-darwin-arm64@npm:1.5.6"
"@rspack/binding-darwin-arm64@npm:1.5.5":
version: 1.5.5
resolution: "@rspack/binding-darwin-arm64@npm:1.5.5"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@rspack/binding-darwin-x64@npm:1.5.6":
version: 1.5.6
resolution: "@rspack/binding-darwin-x64@npm:1.5.6"
"@rspack/binding-darwin-x64@npm:1.5.5":
version: 1.5.5
resolution: "@rspack/binding-darwin-x64@npm:1.5.5"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@rspack/binding-linux-arm64-gnu@npm:1.5.6":
version: 1.5.6
resolution: "@rspack/binding-linux-arm64-gnu@npm:1.5.6"
"@rspack/binding-linux-arm64-gnu@npm:1.5.5":
version: 1.5.5
resolution: "@rspack/binding-linux-arm64-gnu@npm:1.5.5"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard
"@rspack/binding-linux-arm64-musl@npm:1.5.6":
version: 1.5.6
resolution: "@rspack/binding-linux-arm64-musl@npm:1.5.6"
"@rspack/binding-linux-arm64-musl@npm:1.5.5":
version: 1.5.5
resolution: "@rspack/binding-linux-arm64-musl@npm:1.5.5"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard
"@rspack/binding-linux-x64-gnu@npm:1.5.6":
version: 1.5.6
resolution: "@rspack/binding-linux-x64-gnu@npm:1.5.6"
"@rspack/binding-linux-x64-gnu@npm:1.5.5":
version: 1.5.5
resolution: "@rspack/binding-linux-x64-gnu@npm:1.5.5"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard
"@rspack/binding-linux-x64-musl@npm:1.5.6":
version: 1.5.6
resolution: "@rspack/binding-linux-x64-musl@npm:1.5.6"
"@rspack/binding-linux-x64-musl@npm:1.5.5":
version: 1.5.5
resolution: "@rspack/binding-linux-x64-musl@npm:1.5.5"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard
"@rspack/binding-wasm32-wasi@npm:1.5.6":
version: 1.5.6
resolution: "@rspack/binding-wasm32-wasi@npm:1.5.6"
"@rspack/binding-wasm32-wasi@npm:1.5.5":
version: 1.5.5
resolution: "@rspack/binding-wasm32-wasi@npm:1.5.5"
dependencies:
"@napi-rs/wasm-runtime": "npm:^1.0.5"
conditions: cpu=wasm32
languageName: node
linkType: hard
"@rspack/binding-win32-arm64-msvc@npm:1.5.6":
version: 1.5.6
resolution: "@rspack/binding-win32-arm64-msvc@npm:1.5.6"
"@rspack/binding-win32-arm64-msvc@npm:1.5.5":
version: 1.5.5
resolution: "@rspack/binding-win32-arm64-msvc@npm:1.5.5"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@rspack/binding-win32-ia32-msvc@npm:1.5.6":
version: 1.5.6
resolution: "@rspack/binding-win32-ia32-msvc@npm:1.5.6"
"@rspack/binding-win32-ia32-msvc@npm:1.5.5":
version: 1.5.5
resolution: "@rspack/binding-win32-ia32-msvc@npm:1.5.5"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@rspack/binding-win32-x64-msvc@npm:1.5.6":
version: 1.5.6
resolution: "@rspack/binding-win32-x64-msvc@npm:1.5.6"
"@rspack/binding-win32-x64-msvc@npm:1.5.5":
version: 1.5.5
resolution: "@rspack/binding-win32-x64-msvc@npm:1.5.5"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@rspack/binding@npm:1.5.6":
version: 1.5.6
resolution: "@rspack/binding@npm:1.5.6"
"@rspack/binding@npm:1.5.5":
version: 1.5.5
resolution: "@rspack/binding@npm:1.5.5"
dependencies:
"@rspack/binding-darwin-arm64": "npm:1.5.6"
"@rspack/binding-darwin-x64": "npm:1.5.6"
"@rspack/binding-linux-arm64-gnu": "npm:1.5.6"
"@rspack/binding-linux-arm64-musl": "npm:1.5.6"
"@rspack/binding-linux-x64-gnu": "npm:1.5.6"
"@rspack/binding-linux-x64-musl": "npm:1.5.6"
"@rspack/binding-wasm32-wasi": "npm:1.5.6"
"@rspack/binding-win32-arm64-msvc": "npm:1.5.6"
"@rspack/binding-win32-ia32-msvc": "npm:1.5.6"
"@rspack/binding-win32-x64-msvc": "npm:1.5.6"
"@rspack/binding-darwin-arm64": "npm:1.5.5"
"@rspack/binding-darwin-x64": "npm:1.5.5"
"@rspack/binding-linux-arm64-gnu": "npm:1.5.5"
"@rspack/binding-linux-arm64-musl": "npm:1.5.5"
"@rspack/binding-linux-x64-gnu": "npm:1.5.5"
"@rspack/binding-linux-x64-musl": "npm:1.5.5"
"@rspack/binding-wasm32-wasi": "npm:1.5.5"
"@rspack/binding-win32-arm64-msvc": "npm:1.5.5"
"@rspack/binding-win32-ia32-msvc": "npm:1.5.5"
"@rspack/binding-win32-x64-msvc": "npm:1.5.5"
dependenciesMeta:
"@rspack/binding-darwin-arm64":
optional: true
@@ -4159,23 +4159,23 @@ __metadata:
optional: true
"@rspack/binding-win32-x64-msvc":
optional: true
checksum: 10/852113a80ff7396257426a6a3a6c6fd47f5743c7304b90de9d348ed0496e5f335bcc0617f857be2d9e836fa610dd7a3952a1a834b756a228c4913acb78a3d3fa
checksum: 10/65b71796a3e8f1bc5a374253aafc128076cf1b02ac0ae8484eff897420152f1863c153dd9195ba84a8d2c4a41ab8a41d590b0645308f6035ab188c9c3d33b214
languageName: node
linkType: hard
"@rspack/core@npm:1.5.6":
version: 1.5.6
resolution: "@rspack/core@npm:1.5.6"
"@rspack/core@npm:1.5.5":
version: 1.5.5
resolution: "@rspack/core@npm:1.5.5"
dependencies:
"@module-federation/runtime-tools": "npm:0.18.0"
"@rspack/binding": "npm:1.5.6"
"@rspack/binding": "npm:1.5.5"
"@rspack/lite-tapable": "npm:1.0.1"
peerDependencies:
"@swc/helpers": ">=0.5.1"
peerDependenciesMeta:
"@swc/helpers":
optional: true
checksum: 10/50814815c63b611c2e9a7724dfa194e8b52e7232fa18637ef17c6de79c36ceb798f1fd7501e869b24a4f4f4ab6c198c5ce43884be81e7421c82a0e4880dc9600
checksum: 10/864e16e3370ee09cbe26a29220a59392f10e61b8ae1e258139c9939c2ecc20c8899e92357e67bdb81603ce102baa46e5bef916de3b39000828d40c54158ab816
languageName: node
linkType: hard
@@ -4491,10 +4491,10 @@ __metadata:
languageName: node
linkType: hard
"@types/chromecast-caf-receiver@npm:6.0.22":
version: 6.0.22
resolution: "@types/chromecast-caf-receiver@npm:6.0.22"
checksum: 10/6c51cb52527776ddfa187a261b88184c98bdd61c129dd8719cba213894d565cf69073734d6473696ffd60a768f6fb5a3fe9932693f43174fbc5e7af201db8a90
"@types/chromecast-caf-receiver@npm:6.0.24":
version: 6.0.24
resolution: "@types/chromecast-caf-receiver@npm:6.0.24"
checksum: 10/1f2b95e8a15dbb36d5328895229d4a5cb255b33e62d46335bd6ed75e16aa9ea6a7d765a64ae120d19b3134fb3e51e9547d2544c7277f7bffe0bf0b3999f026da
languageName: node
linkType: hard
@@ -5000,106 +5000,106 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:8.44.1":
version: 8.44.1
resolution: "@typescript-eslint/eslint-plugin@npm:8.44.1"
"@typescript-eslint/eslint-plugin@npm:8.44.0":
version: 8.44.0
resolution: "@typescript-eslint/eslint-plugin@npm:8.44.0"
dependencies:
"@eslint-community/regexpp": "npm:^4.10.0"
"@typescript-eslint/scope-manager": "npm:8.44.1"
"@typescript-eslint/type-utils": "npm:8.44.1"
"@typescript-eslint/utils": "npm:8.44.1"
"@typescript-eslint/visitor-keys": "npm:8.44.1"
"@typescript-eslint/scope-manager": "npm:8.44.0"
"@typescript-eslint/type-utils": "npm:8.44.0"
"@typescript-eslint/utils": "npm:8.44.0"
"@typescript-eslint/visitor-keys": "npm:8.44.0"
graphemer: "npm:^1.4.0"
ignore: "npm:^7.0.0"
natural-compare: "npm:^1.4.0"
ts-api-utils: "npm:^2.1.0"
peerDependencies:
"@typescript-eslint/parser": ^8.44.1
"@typescript-eslint/parser": ^8.44.0
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10/e0f69d1d24bbf63c3f2937f85b49994eae907656b01bc9d3563f096750add1085c4b15953b82b750b0da2b8444850558a8bf0d5bcfb8f0410dfd628f4245dc11
checksum: 10/38d0491d96740d1c9f412b0ee1a8a24ed132433347e52810b030e331b27a846b2c70a8424b5684e7ccd7a7ed231e336dd40a0642f5014409ed28e84561fd0757
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:8.44.1":
version: 8.44.1
resolution: "@typescript-eslint/parser@npm:8.44.1"
"@typescript-eslint/parser@npm:8.44.0":
version: 8.44.0
resolution: "@typescript-eslint/parser@npm:8.44.0"
dependencies:
"@typescript-eslint/scope-manager": "npm:8.44.1"
"@typescript-eslint/types": "npm:8.44.1"
"@typescript-eslint/typescript-estree": "npm:8.44.1"
"@typescript-eslint/visitor-keys": "npm:8.44.1"
"@typescript-eslint/scope-manager": "npm:8.44.0"
"@typescript-eslint/types": "npm:8.44.0"
"@typescript-eslint/typescript-estree": "npm:8.44.0"
"@typescript-eslint/visitor-keys": "npm:8.44.0"
debug: "npm:^4.3.4"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10/ff5048c36d9fde27a03f64f3c4ad4739370fde1d744fa7bd1e08280601bd9adfe64c740789fd2adede54dd212a005c59bf1c06c68d05f57b7028332838ed28f8
checksum: 10/8c7ddabf46a94877e3af9b029c5e65cc99ec2983ee14842eea7e8ebccc0e24f589cdf7d2ca63ece87284ea7a4d9d622a22452ac5c66cb6ebda1b91b438e20979
languageName: node
linkType: hard
"@typescript-eslint/project-service@npm:8.44.1":
version: 8.44.1
resolution: "@typescript-eslint/project-service@npm:8.44.1"
"@typescript-eslint/project-service@npm:8.44.0":
version: 8.44.0
resolution: "@typescript-eslint/project-service@npm:8.44.0"
dependencies:
"@typescript-eslint/tsconfig-utils": "npm:^8.44.1"
"@typescript-eslint/types": "npm:^8.44.1"
"@typescript-eslint/tsconfig-utils": "npm:^8.44.0"
"@typescript-eslint/types": "npm:^8.44.0"
debug: "npm:^4.3.4"
peerDependencies:
typescript: ">=4.8.4 <6.0.0"
checksum: 10/4b74d9d1c113b2637b6d65c790bfd2fa15ab1061fe77e68519c3b1939f4b0ee9e15d621ffc946ae2ef457289e830ddea879553868d5c7ff1af4904d7842792e0
checksum: 10/400b4981e6f6bd323592df7803383c0f223256f76b4876c03c2e8fa5e9470ce90a6a39555f759a391ba87bbf79add800eadf42ba06acf0732407a225790722de
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:8.44.1":
version: 8.44.1
resolution: "@typescript-eslint/scope-manager@npm:8.44.1"
"@typescript-eslint/scope-manager@npm:8.44.0":
version: 8.44.0
resolution: "@typescript-eslint/scope-manager@npm:8.44.0"
dependencies:
"@typescript-eslint/types": "npm:8.44.1"
"@typescript-eslint/visitor-keys": "npm:8.44.1"
checksum: 10/f731becce1f79b3add939417e31c7ae38c9150d73de5dec4141376cc64e1bb69f8d6b9f2072f8f442995a1e30eab57fd73c1a4b87220e19abb0f210e2c123096
"@typescript-eslint/types": "npm:8.44.0"
"@typescript-eslint/visitor-keys": "npm:8.44.0"
checksum: 10/5dae4a838637df522f0f71d2df238b5fd5045a374cd036e7485d39fc1b20dd1151185463c8fc35441b12c747b795c88b429a5e229fc9449ae7b15d6e7e1b6caf
languageName: node
linkType: hard
"@typescript-eslint/tsconfig-utils@npm:8.44.1, @typescript-eslint/tsconfig-utils@npm:^8.44.1":
version: 8.44.1
resolution: "@typescript-eslint/tsconfig-utils@npm:8.44.1"
"@typescript-eslint/tsconfig-utils@npm:8.44.0, @typescript-eslint/tsconfig-utils@npm:^8.44.0":
version: 8.44.0
resolution: "@typescript-eslint/tsconfig-utils@npm:8.44.0"
peerDependencies:
typescript: ">=4.8.4 <6.0.0"
checksum: 10/942d4bb9ec3d0f1f6c7fe0dc0fef2ae83a12b43ff3537fbd74007d0c9b80f166db2e5fa2f422f0b10ade348e330204dc70fc50e235ee66dc13ba488ac1490778
checksum: 10/c8535d481d7cda5c846d5b74b7cde7a98b636a558b45510820840dcb9928575c4f3d26b3c021fd7e47b782c6d9a73e9a8adf29191548406ac4b02e1a1dce928f
languageName: node
linkType: hard
"@typescript-eslint/type-utils@npm:8.44.1":
version: 8.44.1
resolution: "@typescript-eslint/type-utils@npm:8.44.1"
"@typescript-eslint/type-utils@npm:8.44.0":
version: 8.44.0
resolution: "@typescript-eslint/type-utils@npm:8.44.0"
dependencies:
"@typescript-eslint/types": "npm:8.44.1"
"@typescript-eslint/typescript-estree": "npm:8.44.1"
"@typescript-eslint/utils": "npm:8.44.1"
"@typescript-eslint/types": "npm:8.44.0"
"@typescript-eslint/typescript-estree": "npm:8.44.0"
"@typescript-eslint/utils": "npm:8.44.0"
debug: "npm:^4.3.4"
ts-api-utils: "npm:^2.1.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10/696747b2a048c281d8cfe74b3f61b7af2e7fa371e9afa58de6d6b49ad7cfa2577d52ddd66fe8b243d2d039b489b6db07bf18a746b14004456c8405842276aa92
checksum: 10/513c6d371922c60522a44a659ca99d3c9978b61781a27831fb60f522190c387f2c48f4759614483fb904953a0c1391fd0a9e2027e30ab1eebfea49f404f1a560
languageName: node
linkType: hard
"@typescript-eslint/types@npm:8.44.1, @typescript-eslint/types@npm:^8.44.1":
version: 8.44.1
resolution: "@typescript-eslint/types@npm:8.44.1"
checksum: 10/acebff929b2c64254c430fff54d8d135c9f47bcc20062fd3e52f64952b0ef973db9582812025f5314940889ae4c4a8798726a477b94fbda31881109687567528
"@typescript-eslint/types@npm:8.44.0, @typescript-eslint/types@npm:^8.44.0":
version: 8.44.0
resolution: "@typescript-eslint/types@npm:8.44.0"
checksum: 10/9e28c95feb0d3b9ae83117a8b3db43db4e9a5d2fcbf8fd0c6e231e0ae604fe894b6725642fe3234733e81e32bec6f03b270d8a27d4258c0ee0b44a8bd8685756
languageName: node
linkType: hard
"@typescript-eslint/typescript-estree@npm:8.44.1":
version: 8.44.1
resolution: "@typescript-eslint/typescript-estree@npm:8.44.1"
"@typescript-eslint/typescript-estree@npm:8.44.0":
version: 8.44.0
resolution: "@typescript-eslint/typescript-estree@npm:8.44.0"
dependencies:
"@typescript-eslint/project-service": "npm:8.44.1"
"@typescript-eslint/tsconfig-utils": "npm:8.44.1"
"@typescript-eslint/types": "npm:8.44.1"
"@typescript-eslint/visitor-keys": "npm:8.44.1"
"@typescript-eslint/project-service": "npm:8.44.0"
"@typescript-eslint/tsconfig-utils": "npm:8.44.0"
"@typescript-eslint/types": "npm:8.44.0"
"@typescript-eslint/visitor-keys": "npm:8.44.0"
debug: "npm:^4.3.4"
fast-glob: "npm:^3.3.2"
is-glob: "npm:^4.0.3"
@@ -5108,32 +5108,32 @@ __metadata:
ts-api-utils: "npm:^2.1.0"
peerDependencies:
typescript: ">=4.8.4 <6.0.0"
checksum: 10/b7b4d177e9339c978a090f1ec23c3f58316845b1cfc4f80a59f481d748b19078ab2cf4fe2d3aa063ad3dc556ea678289e2a9f61e12d7beaeb2bb681599b7481b
checksum: 10/e2e579b15c49e0ef6d2da9d06105ff2c995c785b1bb6cc0a11aa124c1bcc6435ee8897a9d9d6346951a8cc329b8af6410bc982dc5e55272b9af650b11daa536a
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:8.44.1":
version: 8.44.1
resolution: "@typescript-eslint/utils@npm:8.44.1"
"@typescript-eslint/utils@npm:8.44.0":
version: 8.44.0
resolution: "@typescript-eslint/utils@npm:8.44.0"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.7.0"
"@typescript-eslint/scope-manager": "npm:8.44.1"
"@typescript-eslint/types": "npm:8.44.1"
"@typescript-eslint/typescript-estree": "npm:8.44.1"
"@typescript-eslint/scope-manager": "npm:8.44.0"
"@typescript-eslint/types": "npm:8.44.0"
"@typescript-eslint/typescript-estree": "npm:8.44.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10/d7757d400a14bd69272da5e32dc61893ec958a9776b2436e2980d7e638164c88edb4b56c5faff6cf8ea61b1fd8a3f6c78ad4f7fc5c4e7d217d960e08039f7c40
checksum: 10/436e21e3d01e2f6f70fea1d3175de6836f72aa52220c6482c871e5b72f845215db46a8e58e5ab83a197326559d075afb61bedabacbdaa96a243a18f43ac5d6cb
languageName: node
linkType: hard
"@typescript-eslint/visitor-keys@npm:8.44.1":
version: 8.44.1
resolution: "@typescript-eslint/visitor-keys@npm:8.44.1"
"@typescript-eslint/visitor-keys@npm:8.44.0":
version: 8.44.0
resolution: "@typescript-eslint/visitor-keys@npm:8.44.0"
dependencies:
"@typescript-eslint/types": "npm:8.44.1"
"@typescript-eslint/types": "npm:8.44.0"
eslint-visitor-keys: "npm:^4.2.1"
checksum: 10/040f57906265d9ba5ec2230e728eea87bf6af9e0d345017de9e5b05211469457d838435f8b776354b403dad7b2c4527b68863c4ab6750f2668731dd2a3b8f9e8
checksum: 10/09b008b14f46ea14bb5076632745dd527982f231830141a3c789ed0cd3d7bced7340a0ccfb40c640b765de42c267202db83072587144791df4806bd68233596c
languageName: node
linkType: hard
@@ -9355,10 +9355,10 @@ __metadata:
languageName: node
linkType: hard
"hls.js@npm:1.6.13":
version: 1.6.13
resolution: "hls.js@npm:1.6.13"
checksum: 10/4de045fddbeb6edc3859021ff60b268a642aa87e83347a3a764e53299b616d57f36eecef0b91eab240ffde84f75d1ab5f7cc0916a929de265707d0f0b27d1c71
"hls.js@npm:1.6.12":
version: 1.6.12
resolution: "hls.js@npm:1.6.12"
checksum: 10/b0f23fcda44c6a4dc16dc501b3a17829417133079fefb7463a1e3d22ae9da24cb970e9e45165e4223b1e6b3a3d0f253ca680dcc9daf2e27d3d8153390ed3b9be
languageName: node
linkType: hard
@@ -9379,7 +9379,7 @@ __metadata:
"@codemirror/legacy-modes": "npm:6.5.1"
"@codemirror/search": "npm:6.5.11"
"@codemirror/state": "npm:6.5.2"
"@codemirror/view": "npm:6.38.3"
"@codemirror/view": "npm:6.38.2"
"@date-fns/tz": "npm:1.4.1"
"@egjs/hammerjs": "npm:2.0.17"
"@formatjs/intl-datetimeformat": "npm:6.18.0"
@@ -9435,14 +9435,14 @@ __metadata:
"@octokit/rest": "npm:22.0.0"
"@replit/codemirror-indentation-markers": "npm:6.5.3"
"@rsdoctor/rspack-plugin": "npm:1.2.3"
"@rspack/core": "npm:1.5.6"
"@rspack/core": "npm:1.5.5"
"@rspack/dev-server": "npm:1.1.4"
"@swc/helpers": "npm:0.5.17"
"@thomasloven/round-slider": "npm:0.6.0"
"@tsparticles/engine": "npm:3.9.1"
"@tsparticles/preset-links": "npm:3.2.0"
"@types/babel__plugin-transform-runtime": "npm:7.9.5"
"@types/chromecast-caf-receiver": "npm:6.0.22"
"@types/chromecast-caf-receiver": "npm:6.0.24"
"@types/chromecast-caf-sender": "npm:1.0.11"
"@types/color-name": "npm:2.0.0"
"@types/culori": "npm:4.0.1"
@@ -9502,7 +9502,7 @@ __metadata:
gulp-json-transform: "npm:0.5.0"
gulp-rename: "npm:2.1.0"
gulp-zopfli-green: "npm:6.0.2"
hls.js: "npm:1.6.13"
hls.js: "npm:1.6.12"
home-assistant-js-websocket: "npm:9.5.0"
html-minifier-terser: "npm:7.2.0"
husky: "npm:9.1.7"
@@ -9539,12 +9539,12 @@ __metadata:
sortablejs: "patch:sortablejs@npm%3A1.15.6#~/.yarn/patches/sortablejs-npm-1.15.6-3235a8f83b.patch"
stacktrace-js: "npm:2.0.2"
superstruct: "npm:2.0.2"
tar: "npm:7.5.1"
tar: "npm:7.4.4"
terser-webpack-plugin: "npm:5.3.14"
tinykeys: "npm:3.0.0"
ts-lit-plugin: "npm:2.0.2"
typescript: "npm:5.9.2"
typescript-eslint: "npm:8.44.1"
typescript-eslint: "npm:8.44.0"
ua-parser-js: "npm:2.0.5"
vite-tsconfig-paths: "npm:5.1.4"
vitest: "npm:3.2.4"
@@ -14079,16 +14079,16 @@ __metadata:
languageName: node
linkType: hard
"tar@npm:7.5.1, tar@npm:^7.4.3":
version: 7.5.1
resolution: "tar@npm:7.5.1"
"tar@npm:7.4.4, tar@npm:^7.4.3":
version: 7.4.4
resolution: "tar@npm:7.4.4"
dependencies:
"@isaacs/fs-minipass": "npm:^4.0.0"
chownr: "npm:^3.0.0"
minipass: "npm:^7.1.2"
minizlib: "npm:^3.1.0"
yallist: "npm:^5.0.0"
checksum: 10/4848cd2fa2fcaf0734cf54e14bc685056eb43a74d7cc7f954c3ac88fea88c85d95b1d7896619f91aab6f2234c5eec731c18aaa201a78fcf86985bdc824ed7a00
checksum: 10/be7d95e019b029ac507e7cd4b23c243ba896b67d0837c4f53d18c32a5014a24b7b247e982f4d47147b8d637c491b35cc122e19e29246137ecb2b88a495aaf1fb
languageName: node
linkType: hard
@@ -14576,18 +14576,18 @@ __metadata:
languageName: node
linkType: hard
"typescript-eslint@npm:8.44.1":
version: 8.44.1
resolution: "typescript-eslint@npm:8.44.1"
"typescript-eslint@npm:8.44.0":
version: 8.44.0
resolution: "typescript-eslint@npm:8.44.0"
dependencies:
"@typescript-eslint/eslint-plugin": "npm:8.44.1"
"@typescript-eslint/parser": "npm:8.44.1"
"@typescript-eslint/typescript-estree": "npm:8.44.1"
"@typescript-eslint/utils": "npm:8.44.1"
"@typescript-eslint/eslint-plugin": "npm:8.44.0"
"@typescript-eslint/parser": "npm:8.44.0"
"@typescript-eslint/typescript-estree": "npm:8.44.0"
"@typescript-eslint/utils": "npm:8.44.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <6.0.0"
checksum: 10/9bd0601ef67d0fb20b095a722f4286b3b5d27905ff926a9375fc0934746626d72339d74ba40eb9d361af7fda1987294b0533dbed5dddbac79814daf196a301ac
checksum: 10/7c406b064d35f0fcad95c372a73e7cead2452b2f05ddeaef0a512140de9881d402e6a76c997e014e5719de4eac8a1dac64d293d8958e0178108c49b1dc49c5d6
languageName: node
linkType: hard