mirror of
https://github.com/home-assistant/frontend.git
synced 2026-08-29 02:16:15 +00:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
543cfad738 | ||
|
|
328eb62ab1 | ||
|
|
c388068782 | ||
|
|
2b8c57481d | ||
|
|
cfa0d62afc | ||
|
|
b1d8a21aaa | ||
|
|
cde2e66229 | ||
|
|
71d5500ed8 | ||
|
|
462321a3c8 | ||
|
|
afc0c36dd9 |
+3
-3
@@ -117,7 +117,7 @@
|
||||
"lit-html": "3.3.3",
|
||||
"luxon": "3.7.2",
|
||||
"maplibre-gl": "5.24.0",
|
||||
"marked": "18.0.10",
|
||||
"marked": "18.0.11",
|
||||
"memoize-one": "6.0.0",
|
||||
"node-vibrant": "4.0.4",
|
||||
"object-hash": "3.0.0",
|
||||
@@ -179,7 +179,7 @@
|
||||
"browserslist": "4.28.8",
|
||||
"browserslist-useragent-regexp": "4.1.4",
|
||||
"del": "8.0.1",
|
||||
"eslint": "10.9.0",
|
||||
"eslint": "10.9.1",
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"eslint-import-resolver-webpack": "0.13.11",
|
||||
"eslint-plugin-import-x": "4.17.1",
|
||||
@@ -216,7 +216,7 @@
|
||||
"terser-webpack-plugin": "5.6.1",
|
||||
"ts-lit-plugin": "2.0.2",
|
||||
"typescript": "6.0.3",
|
||||
"typescript-eslint": "8.67.0",
|
||||
"typescript-eslint": "8.68.0",
|
||||
"vite-tsconfig-paths": "6.1.1",
|
||||
"vitest": "4.1.11",
|
||||
"webpack-stats-plugin": "1.1.3",
|
||||
|
||||
@@ -95,6 +95,10 @@ export class HaDrawer extends LitElement {
|
||||
}
|
||||
|
||||
private _handleAfterHide(ev: Event) {
|
||||
// Ignore wa-after-hide from nested Web Awesome components (e.g. tooltips)
|
||||
if (ev.target !== ev.currentTarget) {
|
||||
return;
|
||||
}
|
||||
ev.stopPropagation();
|
||||
this.open = false;
|
||||
fireEvent(this, "hass-drawer-closed");
|
||||
|
||||
@@ -7,6 +7,7 @@ import { styleMap } from "lit/directives/style-map";
|
||||
import { atLeastVersion } from "../../common/config/version";
|
||||
import { navigate } from "../../common/navigate";
|
||||
import { debounce } from "../../common/util/debounce";
|
||||
import { deepEqual } from "../../common/util/deep-equal";
|
||||
import "../../components/ha-button";
|
||||
import "../../components/ha-svg-icon";
|
||||
import { updateAreaRegistryEntry } from "../../data/area/area_registry";
|
||||
@@ -14,9 +15,12 @@ import { updateDeviceRegistryEntry } from "../../data/device/device_registry";
|
||||
import {
|
||||
fetchFrontendSystemData,
|
||||
saveFrontendSystemData,
|
||||
subscribeFrontendSystemData,
|
||||
type HomeFrontendSystemData,
|
||||
type SecurityFrontendSystemData,
|
||||
} from "../../data/frontend";
|
||||
import type { LovelaceDashboardStrategyConfig } from "../../data/lovelace/config/types";
|
||||
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
||||
import { mdiHomeAssistant } from "../../resources/home-assistant-logo-svg";
|
||||
import type { HomeAssistant, PanelInfo, Route } from "../../types";
|
||||
import { showToast } from "../../util/toast";
|
||||
@@ -35,7 +39,7 @@ import { showNewOverviewDialog } from "./dialogs/show-dialog-new-overview";
|
||||
import { hasLegacyOverviewPanel } from "../../data/panel";
|
||||
|
||||
@customElement("ha-panel-home")
|
||||
class PanelHome extends LitElement {
|
||||
class PanelHome extends SubscribeMixin(LitElement) {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public narrow = false;
|
||||
@@ -48,12 +52,36 @@ class PanelHome extends LitElement {
|
||||
|
||||
@state() private _config: FrontendSystemData["home"] = {};
|
||||
|
||||
@state() private _securityConfig: SecurityFrontendSystemData = {};
|
||||
|
||||
@state() private _extraActionItems?: ExtraActionItem[];
|
||||
|
||||
@query(".banner") private _banner?: HTMLElement;
|
||||
|
||||
private _loadConfigPromise?: Promise<void>;
|
||||
|
||||
private _securityConfigRevision = 0;
|
||||
|
||||
public hassSubscribe() {
|
||||
return [
|
||||
subscribeFrontendSystemData(
|
||||
this.hass.connection,
|
||||
"security",
|
||||
({ value }) => {
|
||||
this._securityConfigRevision++;
|
||||
const config = value || {};
|
||||
if (deepEqual(config, this._securityConfig)) {
|
||||
return;
|
||||
}
|
||||
this._securityConfig = config;
|
||||
if (this.hasUpdated) {
|
||||
this._setLovelace();
|
||||
}
|
||||
}
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
private get _showBanner(): boolean {
|
||||
// Don't show if already dismissed
|
||||
if (this._config.welcome_banner_dismissed) {
|
||||
@@ -150,16 +178,41 @@ class PanelHome extends LitElement {
|
||||
}
|
||||
|
||||
private async _loadConfig() {
|
||||
try {
|
||||
const [_, data] = await Promise.all([
|
||||
const securityConfigRevision = this._securityConfigRevision;
|
||||
const [translationsResult, homeResult, securityResult] =
|
||||
await Promise.allSettled([
|
||||
this.hass.loadFragmentTranslation("lovelace"),
|
||||
fetchFrontendSystemData(this.hass.connection, "home"),
|
||||
fetchFrontendSystemData(this.hass.connection, "security"),
|
||||
]);
|
||||
this._config = data || {};
|
||||
} catch (err) {
|
||||
|
||||
if (translationsResult.status === "rejected") {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Failed to load favorites:", err);
|
||||
console.error(
|
||||
"Failed to load Lovelace translations:",
|
||||
translationsResult.reason
|
||||
);
|
||||
}
|
||||
|
||||
if (homeResult.status === "rejected") {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Failed to load home configuration:", homeResult.reason);
|
||||
this._config = {};
|
||||
} else {
|
||||
this._config = homeResult.value || {};
|
||||
}
|
||||
|
||||
if (securityResult.status === "rejected") {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
"Failed to load security configuration:",
|
||||
securityResult.reason
|
||||
);
|
||||
if (securityConfigRevision === this._securityConfigRevision) {
|
||||
this._securityConfig = {};
|
||||
}
|
||||
} else if (securityConfigRevision === this._securityConfigRevision) {
|
||||
this._securityConfig = securityResult.value || {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,6 +396,7 @@ class PanelHome extends LitElement {
|
||||
return {
|
||||
strategy: {
|
||||
type: "home",
|
||||
alert_entities: this._securityConfig.alert_entities,
|
||||
favorite_entities: this._config.favorite_entities,
|
||||
home_panel: true,
|
||||
hide_welcome_message: this._config.hide_welcome_message,
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
|
||||
import type {
|
||||
PictureEntityCardConfig,
|
||||
TileCardConfig,
|
||||
} from "../../cards/types";
|
||||
|
||||
export const computeFavoriteCardConfig = (
|
||||
entityId: string
|
||||
): LovelaceCardConfig => {
|
||||
// A camera tile would only show a thumbnail, so give the picture the room.
|
||||
if (computeDomain(entityId) === "camera") {
|
||||
return {
|
||||
type: "picture-entity",
|
||||
entity: entityId,
|
||||
show_name: false,
|
||||
show_state: false,
|
||||
grid_options: {
|
||||
columns: 6,
|
||||
rows: 2,
|
||||
},
|
||||
} satisfies PictureEntityCardConfig;
|
||||
}
|
||||
|
||||
return {
|
||||
type: "tile",
|
||||
entity: entityId,
|
||||
// Favorites come from all over the home, so name the area.
|
||||
state_content: ["state", "area_name"],
|
||||
show_entity_picture: true,
|
||||
} satisfies TileCardConfig;
|
||||
};
|
||||
@@ -1,6 +1,8 @@
|
||||
import { STATE_NOT_RUNNING } from "home-assistant-js-websocket";
|
||||
import { ReactiveElement } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import type { SecurityAlertEntityConfig } from "../../../../data/frontend";
|
||||
import type { ShortcutItem } from "../../../../data/home_shortcuts";
|
||||
import type { LovelaceConfig } from "../../../../data/lovelace/config/types";
|
||||
import type { LovelaceViewRawConfig } from "../../../../data/lovelace/config/view";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
@@ -14,11 +16,11 @@ import {
|
||||
} from "./helpers/home-summaries";
|
||||
import type { HomeAreaViewStrategyConfig } from "./home-area-view-strategy";
|
||||
import type { HomeOtherDevicesViewStrategyConfig } from "./home-other-devices-view-strategy";
|
||||
import type { ShortcutItem } from "../../../../data/home_shortcuts";
|
||||
import type { HomeOverviewViewStrategyConfig } from "./home-overview-view-strategy";
|
||||
|
||||
export interface HomeDashboardStrategyConfig {
|
||||
type: "home";
|
||||
alert_entities?: SecurityAlertEntityConfig[];
|
||||
favorite_entities?: string[];
|
||||
home_panel?: boolean;
|
||||
hide_welcome_message?: boolean;
|
||||
@@ -103,6 +105,7 @@ export class HomeDashboardStrategy extends ReactiveElement {
|
||||
path: "overview",
|
||||
strategy: {
|
||||
type: "home-overview",
|
||||
alert_entities: config.alert_entities,
|
||||
favorite_entities: config.favorite_entities,
|
||||
home_panel: config.home_panel,
|
||||
hide_welcome_message: config.hide_welcome_message,
|
||||
|
||||
@@ -11,6 +11,7 @@ import { floorDefaultIcon } from "../../../../components/ha-floor-icon";
|
||||
import type { AreaRegistryEntry } from "../../../../data/area/area_registry";
|
||||
import type { EnergyPreferences } from "../../../../data/energy";
|
||||
import { getEnergyPreferences } from "../../../../data/energy";
|
||||
import type { SecurityAlertEntityConfig } from "../../../../data/frontend";
|
||||
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
|
||||
import type {
|
||||
LovelaceSectionConfig,
|
||||
@@ -24,6 +25,7 @@ import type { HomeAssistant } from "../../../../types";
|
||||
import { hasClimateEntities } from "../../../climate/strategies/climate-view-strategy";
|
||||
import type {
|
||||
AreaCardConfig,
|
||||
ConditionalCardConfig,
|
||||
DiscoveredDevicesCardConfig,
|
||||
EmptyStateCardConfig,
|
||||
HeadingCardConfig,
|
||||
@@ -34,17 +36,21 @@ import type {
|
||||
TileCardConfig,
|
||||
UpdatesCardConfig,
|
||||
} from "../../cards/types";
|
||||
import { computeFavoriteCardConfig } from "../helpers/favorite-cards";
|
||||
import { computeSecurityAlertCardConfig } from "../../../security/strategies/security-alerts";
|
||||
import {
|
||||
LARGE_SCREEN_CONDITION,
|
||||
SMALL_SCREEN_CONDITION,
|
||||
} from "../helpers/view-columns-conditions";
|
||||
import type { LovelaceStrategyDependency } from "../types";
|
||||
import type { CommonControlsSectionStrategyConfig } from "../usage_prediction/common-controls-section-strategy";
|
||||
import { generateLovelaceSectionStrategy } from "../get-strategy";
|
||||
import { HOME_SUMMARIES_FILTERS } from "./helpers/home-summaries";
|
||||
import { OTHER_DEVICES_FILTERS } from "./helpers/other-devices-filters";
|
||||
|
||||
export interface HomeOverviewViewStrategyConfig {
|
||||
type: "home-overview";
|
||||
alert_entities?: SecurityAlertEntityConfig[];
|
||||
favorite_entities?: string[];
|
||||
home_panel?: boolean;
|
||||
hide_welcome_message?: boolean;
|
||||
@@ -255,31 +261,32 @@ export class HomeOverviewViewStrategy extends ReactiveElement {
|
||||
|
||||
let favoritesSection: LovelaceSectionRawConfig | undefined;
|
||||
if (!config.hide_suggested_entities) {
|
||||
favoritesSection = {
|
||||
strategy: {
|
||||
type: "common-controls",
|
||||
limit: maxCommonControls,
|
||||
include_entities: favoriteEntities,
|
||||
hide_empty: true,
|
||||
heading: favoritesHeadingCard,
|
||||
} satisfies CommonControlsSectionStrategyConfig,
|
||||
column_span: maxColumns,
|
||||
} satisfies LovelaceStrategySectionConfig;
|
||||
const generatedFavoritesSection = await generateLovelaceSectionStrategy(
|
||||
{
|
||||
strategy: {
|
||||
type: "common-controls",
|
||||
limit: maxCommonControls,
|
||||
include_entities: favoriteEntities,
|
||||
hide_empty: true,
|
||||
heading: favoritesHeadingCard,
|
||||
} satisfies CommonControlsSectionStrategyConfig,
|
||||
column_span: maxColumns,
|
||||
} satisfies LovelaceStrategySectionConfig,
|
||||
hass
|
||||
);
|
||||
if (!generatedFavoritesSection.disabled) {
|
||||
favoritesSection = {
|
||||
...generatedFavoritesSection,
|
||||
column_span: maxColumns,
|
||||
};
|
||||
}
|
||||
} else if (favoriteEntities.length > 0) {
|
||||
favoritesSection = {
|
||||
type: "grid",
|
||||
column_span: maxColumns,
|
||||
cards: [
|
||||
favoritesHeadingCard,
|
||||
...favoriteEntities.map(
|
||||
(entityId) =>
|
||||
({
|
||||
type: "tile",
|
||||
entity: entityId,
|
||||
state_content: ["state", "area_name"],
|
||||
show_entity_picture: true,
|
||||
}) satisfies TileCardConfig
|
||||
),
|
||||
...favoriteEntities.map(computeFavoriteCardConfig),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -461,6 +468,10 @@ export class HomeOverviewViewStrategy extends ReactiveElement {
|
||||
}
|
||||
}
|
||||
|
||||
const hasVisibleSummaryCards = summaryCards.some(
|
||||
(card) => !("hide_empty" in card && card.hide_empty)
|
||||
);
|
||||
|
||||
// Build summary cards for sidebar (full width: columns 12)
|
||||
const sidebarSummaryCards = summaryCards.map((card) => ({
|
||||
...card,
|
||||
@@ -507,62 +518,127 @@ export class HomeOverviewViewStrategy extends ReactiveElement {
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const alertCards = config.alert_entities?.map((alertEntity) =>
|
||||
computeSecurityAlertCardConfig(
|
||||
hass.states[alertEntity.entity],
|
||||
alertEntity
|
||||
)
|
||||
);
|
||||
|
||||
const alertsSection: LovelaceSectionConfig | undefined = alertCards?.length
|
||||
? {
|
||||
type: "grid",
|
||||
column_span: maxColumns,
|
||||
visibility: [
|
||||
{
|
||||
condition: "or",
|
||||
conditions: alertCards.map((alertCard) => ({
|
||||
condition: "and",
|
||||
conditions: alertCard.visibility!,
|
||||
})),
|
||||
},
|
||||
],
|
||||
cards: [
|
||||
{
|
||||
type: "heading",
|
||||
heading: hass.localize(
|
||||
"ui.panel.lovelace.strategy.security.active_alerts"
|
||||
),
|
||||
heading_style: "title",
|
||||
grid_options: { columns: "full" },
|
||||
},
|
||||
...alertCards,
|
||||
],
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const emptyStateCard = {
|
||||
type: "empty-state",
|
||||
icon: "mdi:home-assistant",
|
||||
content_only: true,
|
||||
title: hass.localize("ui.panel.lovelace.strategy.home.welcome_title"),
|
||||
content: hass.localize("ui.panel.lovelace.strategy.home.welcome_content"),
|
||||
...(config.home_panel && hass.user?.is_admin
|
||||
? {
|
||||
buttons: [
|
||||
{
|
||||
icon: "mdi:plus",
|
||||
text: hass.localize(
|
||||
"ui.panel.lovelace.strategy.home.welcome_add_device"
|
||||
),
|
||||
appearance: "filled" as const,
|
||||
variant: "brand" as const,
|
||||
tap_action: {
|
||||
action: "fire-dom-event" as const,
|
||||
home_panel: {
|
||||
type: "add_integration",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "mdi:home-edit",
|
||||
text: hass.localize(
|
||||
"ui.panel.lovelace.strategy.home.welcome_edit_areas"
|
||||
),
|
||||
appearance: "plain" as const,
|
||||
variant: "brand" as const,
|
||||
tap_action: {
|
||||
action: "navigate" as const,
|
||||
navigation_path: "/config/areas/dashboard",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
} as EmptyStateCardConfig;
|
||||
|
||||
// No sections, show empty state
|
||||
if (floorsSections.length === 0) {
|
||||
if (floorsSections.length === 0 && !alertsSection) {
|
||||
return {
|
||||
type: "panel",
|
||||
cards: [
|
||||
{
|
||||
type: "empty-state",
|
||||
icon: "mdi:home-assistant",
|
||||
content_only: true,
|
||||
title: hass.localize(
|
||||
"ui.panel.lovelace.strategy.home.welcome_title"
|
||||
),
|
||||
content: hass.localize(
|
||||
"ui.panel.lovelace.strategy.home.welcome_content"
|
||||
),
|
||||
...(config.home_panel && hass.user?.is_admin
|
||||
? {
|
||||
buttons: [
|
||||
{
|
||||
icon: "mdi:plus",
|
||||
text: hass.localize(
|
||||
"ui.panel.lovelace.strategy.home.welcome_add_device"
|
||||
),
|
||||
appearance: "filled",
|
||||
variant: "brand",
|
||||
tap_action: {
|
||||
action: "fire-dom-event",
|
||||
home_panel: {
|
||||
type: "add_integration",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "mdi:home-edit",
|
||||
text: hass.localize(
|
||||
"ui.panel.lovelace.strategy.home.welcome_edit_areas"
|
||||
),
|
||||
appearance: "plain",
|
||||
variant: "brand",
|
||||
tap_action: {
|
||||
action: "navigate",
|
||||
navigation_path: "/config/areas/dashboard",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
} as EmptyStateCardConfig,
|
||||
],
|
||||
cards: [emptyStateCard],
|
||||
};
|
||||
}
|
||||
|
||||
const emptyStateSection: LovelaceSectionConfig | undefined =
|
||||
floorsSections.length === 0 &&
|
||||
!favoritesSection &&
|
||||
!hasVisibleSummaryCards &&
|
||||
alertCards?.length
|
||||
? {
|
||||
type: "grid",
|
||||
column_span: maxColumns,
|
||||
cards: [
|
||||
{
|
||||
type: "conditional",
|
||||
conditions: [
|
||||
{
|
||||
condition: "not",
|
||||
conditions: [
|
||||
{
|
||||
condition: "or",
|
||||
conditions: alertCards.map((alertCard) => ({
|
||||
condition: "and",
|
||||
conditions: alertCard.visibility!,
|
||||
})),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
card: emptyStateCard,
|
||||
} satisfies ConditionalCardConfig,
|
||||
],
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const sections = (
|
||||
[favoritesSection, mobileSummarySection, ...floorsSections] satisfies (
|
||||
LovelaceSectionRawConfig | undefined
|
||||
)[]
|
||||
[
|
||||
alertsSection,
|
||||
emptyStateSection,
|
||||
favoritesSection,
|
||||
mobileSummarySection,
|
||||
...floorsSections,
|
||||
] satisfies (LovelaceSectionRawConfig | undefined)[]
|
||||
).filter(Boolean) as LovelaceSectionRawConfig[];
|
||||
|
||||
return {
|
||||
|
||||
+10
-10
@@ -4,8 +4,9 @@ import { isComponentLoaded } from "../../../../common/config/is_component_loaded
|
||||
import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section";
|
||||
import { getCommonControlsUsagePrediction } from "../../../../data/usage_prediction";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { HeadingCardConfig, TileCardConfig } from "../../cards/types";
|
||||
import type { HeadingCardConfig } from "../../cards/types";
|
||||
import type { Condition } from "../../common/validate-condition";
|
||||
import { computeFavoriteCardConfig } from "../helpers/favorite-cards";
|
||||
import type { LovelaceStrategyDependency } from "../types";
|
||||
|
||||
const DEFAULT_LIMIT = 8;
|
||||
@@ -25,13 +26,6 @@ export interface CommonControlsSectionStrategyConfig {
|
||||
title_visibilty?: Condition[];
|
||||
}
|
||||
|
||||
const toTileCard = (entity: string): TileCardConfig => ({
|
||||
type: "tile",
|
||||
entity,
|
||||
state_content: ["state", "area_name"],
|
||||
show_entity_picture: true,
|
||||
});
|
||||
|
||||
@customElement("common-controls-section-strategy")
|
||||
export class CommonControlsSectionStrategy extends ReactiveElement {
|
||||
static registryDependencies: readonly LovelaceStrategyDependency[] = [];
|
||||
@@ -63,11 +57,17 @@ export class CommonControlsSectionStrategy extends ReactiveElement {
|
||||
|
||||
// Pinned entities already fill the section, skip the prediction call.
|
||||
if (includedEntities.length >= limit) {
|
||||
section.cards!.push(...includedEntities.slice(0, limit).map(toTileCard));
|
||||
section.cards!.push(
|
||||
...includedEntities.slice(0, limit).map(computeFavoriteCardConfig)
|
||||
);
|
||||
return section;
|
||||
}
|
||||
|
||||
if (!isComponentLoaded(hass.config, "usage_prediction")) {
|
||||
if (includedEntities.length > 0) {
|
||||
section.cards!.push(...includedEntities.map(toTileCard));
|
||||
return section;
|
||||
}
|
||||
section.cards!.push({
|
||||
type: "markdown",
|
||||
content: hass.localize(
|
||||
@@ -110,7 +110,7 @@ export class CommonControlsSectionStrategy extends ReactiveElement {
|
||||
return section;
|
||||
}
|
||||
|
||||
section.cards!.push(...entities.map(toTileCard));
|
||||
section.cards!.push(...entities.map(computeFavoriteCardConfig));
|
||||
return section;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,9 @@ import type {
|
||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { SecurityAlertEntityConfig } from "../../../data/frontend";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type {
|
||||
LogbookCardConfig,
|
||||
TileCardConfig,
|
||||
} from "../../lovelace/cards/types";
|
||||
import type { LogbookCardConfig } from "../../lovelace/cards/types";
|
||||
import { computeAreaTileCardConfig } from "../../lovelace/strategies/areas/helpers/areas-strategy-helper";
|
||||
import { computeFavoriteCardConfig } from "../../lovelace/strategies/helpers/favorite-cards";
|
||||
import { computeSecurityAlertCardConfig } from "./security-alerts";
|
||||
|
||||
export interface SecurityViewStrategyConfig {
|
||||
@@ -186,15 +184,7 @@ export class SecurityViewStrategy extends ReactiveElement {
|
||||
),
|
||||
heading_style: "title",
|
||||
},
|
||||
...favoriteEntities.map(
|
||||
(entityId) =>
|
||||
({
|
||||
type: "tile",
|
||||
entity: entityId,
|
||||
state_content: ["state", "area_name"],
|
||||
show_entity_picture: true,
|
||||
}) satisfies TileCardConfig
|
||||
),
|
||||
...favoriteEntities.map(computeFavoriteCardConfig),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import type { Connection } from "home-assistant-js-websocket";
|
||||
import type {
|
||||
HomeFrontendSystemData,
|
||||
SecurityFrontendSystemData,
|
||||
} from "../../../src/data/frontend";
|
||||
import type { HomeAssistant } from "../../../src/types";
|
||||
import "../../../src/panels/home/ha-panel-home";
|
||||
import { createMockHass } from "../../fixtures/hass";
|
||||
|
||||
vi.hoisted(() => {
|
||||
Object.assign(globalThis, {
|
||||
__STATIC_PATH__: "/",
|
||||
__HASS_URL__: "",
|
||||
__BUILD__: "modern",
|
||||
__VERSION__: "test",
|
||||
__BACKWARDS_COMPAT__: false,
|
||||
__SUPERVISOR__: false,
|
||||
__NAMESPACE__: "frontend",
|
||||
});
|
||||
});
|
||||
|
||||
type TestPanelHome = HTMLElement & {
|
||||
hass: HomeAssistant;
|
||||
hassSubscribe(): unknown[];
|
||||
_config: HomeFrontendSystemData;
|
||||
_securityConfig: SecurityFrontendSystemData;
|
||||
} & Record<"_loadConfig", () => Promise<void>>;
|
||||
|
||||
const deferred = <T>() => {
|
||||
let resolve!: (value: T) => void;
|
||||
const promise = new Promise<T>((promiseResolve) => {
|
||||
resolve = promiseResolve;
|
||||
});
|
||||
return { promise, resolve };
|
||||
};
|
||||
|
||||
describe("ha-panel-home configuration loading", () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("does not overwrite a newer security subscription update", async () => {
|
||||
const translations = deferred<HomeAssistant["localize"] | undefined>();
|
||||
let securityChanged:
|
||||
| ((data: { value: SecurityFrontendSystemData | null }) => void)
|
||||
| undefined;
|
||||
const hass = createMockHass();
|
||||
hass.loadFragmentTranslation = () => translations.promise;
|
||||
hass.connection = {
|
||||
sendMessagePromise: vi.fn(async (message: { key: string }) => ({
|
||||
value:
|
||||
message.key === "home"
|
||||
? {}
|
||||
: { alert_entities: [{ entity: "binary_sensor.old" }] },
|
||||
})),
|
||||
subscribeMessage: vi.fn(
|
||||
(
|
||||
callback: (data: { value: SecurityFrontendSystemData | null }) => void
|
||||
) => {
|
||||
securityChanged = callback;
|
||||
return Promise.resolve(() => undefined);
|
||||
}
|
||||
),
|
||||
} as unknown as Connection;
|
||||
|
||||
const element = document.createElement(
|
||||
"ha-panel-home"
|
||||
) as unknown as TestPanelHome;
|
||||
element.hass = hass;
|
||||
element.hassSubscribe();
|
||||
const loading = element._loadConfig();
|
||||
|
||||
securityChanged!({
|
||||
value: { alert_entities: [{ entity: "binary_sensor.new" }] },
|
||||
});
|
||||
translations.resolve(undefined);
|
||||
await loading;
|
||||
|
||||
expect(element._securityConfig.alert_entities).toEqual([
|
||||
{ entity: "binary_sensor.new" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("preserves loaded Home config when security loading fails", async () => {
|
||||
vi.spyOn(console, "error").mockImplementation(() => undefined);
|
||||
const homeConfig: HomeFrontendSystemData = {
|
||||
favorite_entities: ["light.kitchen"],
|
||||
hide_suggested_entities: true,
|
||||
shortcuts: [{ type: "custom", path: "/test", label: "Test" }],
|
||||
};
|
||||
const hass = createMockHass();
|
||||
hass.loadFragmentTranslation = async () => undefined;
|
||||
hass.connection = {
|
||||
sendMessagePromise: vi.fn(async (message: { key: string }) => {
|
||||
if (message.key === "security") {
|
||||
throw new Error("Security unavailable");
|
||||
}
|
||||
return { value: homeConfig };
|
||||
}),
|
||||
} as unknown as Connection;
|
||||
|
||||
const element = document.createElement(
|
||||
"ha-panel-home"
|
||||
) as unknown as TestPanelHome;
|
||||
element.hass = hass;
|
||||
element._securityConfig = {
|
||||
alert_entities: [{ entity: "binary_sensor.old" }],
|
||||
};
|
||||
await element._loadConfig();
|
||||
|
||||
expect(element._config).toEqual(homeConfig);
|
||||
expect(element._securityConfig).toEqual({});
|
||||
});
|
||||
|
||||
it("preserves loaded Home config when translations fail", async () => {
|
||||
vi.spyOn(console, "error").mockImplementation(() => undefined);
|
||||
const homeConfig: HomeFrontendSystemData = {
|
||||
favorite_entities: ["light.kitchen"],
|
||||
};
|
||||
const hass = createMockHass();
|
||||
hass.loadFragmentTranslation = async () => {
|
||||
throw new Error("Translations unavailable");
|
||||
};
|
||||
hass.connection = {
|
||||
sendMessagePromise: vi.fn(async (message: { key: string }) => ({
|
||||
value: message.key === "home" ? homeConfig : {},
|
||||
})),
|
||||
} as unknown as Connection;
|
||||
|
||||
const element = document.createElement(
|
||||
"ha-panel-home"
|
||||
) as unknown as TestPanelHome;
|
||||
element.hass = hass;
|
||||
await element._loadConfig();
|
||||
|
||||
expect(element._config).toEqual(homeConfig);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,182 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { LovelaceSectionConfig } from "../../../../../src/data/lovelace/config/section";
|
||||
import type { LovelaceViewConfig } from "../../../../../src/data/lovelace/config/view";
|
||||
import { checkConditionsMet } from "../../../../../src/panels/lovelace/common/validate-condition";
|
||||
import type { ConditionalCardConfig } from "../../../../../src/panels/lovelace/cards/types";
|
||||
import { HomeOverviewViewStrategy } from "../../../../../src/panels/lovelace/strategies/home/home-overview-view-strategy";
|
||||
import type { HomeAssistant } from "../../../../../src/types";
|
||||
import {
|
||||
createMockEntityState,
|
||||
createMockHass,
|
||||
} from "../../../../fixtures/hass";
|
||||
|
||||
const createHass = (state: string): HomeAssistant => {
|
||||
const hass = createMockHass({
|
||||
"binary_sensor.front_door": createMockEntityState(
|
||||
"binary_sensor.front_door",
|
||||
state,
|
||||
{ device_class: "door" }
|
||||
),
|
||||
});
|
||||
return {
|
||||
...hass,
|
||||
config: { ...hass.config, components: [] },
|
||||
panels: {},
|
||||
};
|
||||
};
|
||||
|
||||
const sections = (view: LovelaceViewConfig): LovelaceSectionConfig[] => {
|
||||
expect(view.type).toBe("sections");
|
||||
return view.sections as LovelaceSectionConfig[];
|
||||
};
|
||||
|
||||
describe("HomeOverviewViewStrategy security alerts", () => {
|
||||
it("maps configured severity to the security alert card config", async () => {
|
||||
const view = await HomeOverviewViewStrategy.generate(
|
||||
{
|
||||
type: "home-overview",
|
||||
alert_entities: [
|
||||
{ entity: "binary_sensor.front_door", severity: "alert" },
|
||||
],
|
||||
},
|
||||
createHass("on")
|
||||
);
|
||||
const alertSection = sections(view)[0];
|
||||
|
||||
expect(alertSection.cards).toEqual([
|
||||
{
|
||||
type: "heading",
|
||||
heading: "ui.panel.lovelace.strategy.security.active_alerts",
|
||||
heading_style: "title",
|
||||
grid_options: { columns: "full" },
|
||||
},
|
||||
{
|
||||
type: "alert",
|
||||
entity: "binary_sensor.front_door",
|
||||
color: "red",
|
||||
visibility: [
|
||||
{
|
||||
condition: "state",
|
||||
entity: "binary_sensor.front_door",
|
||||
state: "on",
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("reactively shows the area-less empty state only without active alerts", async () => {
|
||||
const inactiveHass = createHass("off");
|
||||
const view = await HomeOverviewViewStrategy.generate(
|
||||
{
|
||||
type: "home-overview",
|
||||
alert_entities: [{ entity: "binary_sensor.front_door" }],
|
||||
hide_suggested_entities: true,
|
||||
},
|
||||
inactiveHass
|
||||
);
|
||||
const emptyStateCard = sections(view)[1]
|
||||
?.cards?.[0] as ConditionalCardConfig;
|
||||
|
||||
expect(
|
||||
checkConditionsMet(emptyStateCard.conditions, inactiveHass, {})
|
||||
).toBe(true);
|
||||
expect(
|
||||
checkConditionsMet(emptyStateCard.conditions, createHass("on"), {})
|
||||
).toBe(false);
|
||||
expect(emptyStateCard.card.type).toBe("empty-state");
|
||||
});
|
||||
|
||||
it("shows the area-less empty state when prediction returns no controls", async () => {
|
||||
const hass = createHass("off");
|
||||
hass.config = { ...hass.config, components: ["usage_prediction"] };
|
||||
hass.callWS = vi.fn().mockResolvedValue({ entities: [] });
|
||||
const view = await HomeOverviewViewStrategy.generate(
|
||||
{
|
||||
type: "home-overview",
|
||||
alert_entities: [{ entity: "binary_sensor.front_door" }],
|
||||
},
|
||||
hass
|
||||
);
|
||||
|
||||
expect(
|
||||
sections(view).some((section) =>
|
||||
section.cards?.some((card) => card.type === "conditional")
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("does not show the area-less empty state with a predicted control", async () => {
|
||||
const hass = createHass("off");
|
||||
hass.states["light.kitchen"] = createMockEntityState("light.kitchen", "on");
|
||||
hass.config = { ...hass.config, components: ["usage_prediction"] };
|
||||
hass.callWS = vi.fn().mockResolvedValue({ entities: ["light.kitchen"] });
|
||||
const view = await HomeOverviewViewStrategy.generate(
|
||||
{
|
||||
type: "home-overview",
|
||||
alert_entities: [{ entity: "binary_sensor.front_door" }],
|
||||
},
|
||||
hass
|
||||
);
|
||||
|
||||
expect(
|
||||
sections(view).some((section) =>
|
||||
section.cards?.some((card) => card.type === "conditional")
|
||||
)
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps the Home view when prediction loading fails", async () => {
|
||||
vi.spyOn(console, "error").mockImplementation(() => undefined);
|
||||
const hass = createHass("off");
|
||||
hass.config = { ...hass.config, components: ["usage_prediction"] };
|
||||
hass.callWS = vi
|
||||
.fn()
|
||||
.mockRejectedValue(new Error("Prediction unavailable"));
|
||||
const view = await HomeOverviewViewStrategy.generate(
|
||||
{
|
||||
type: "home-overview",
|
||||
alert_entities: [{ entity: "binary_sensor.front_door" }],
|
||||
},
|
||||
hass
|
||||
);
|
||||
|
||||
const alertCard = sections(view)[0]?.cards?.[1];
|
||||
expect(alertCard?.type).toBe("alert");
|
||||
});
|
||||
|
||||
it("does not show the area-less empty state with configured favorites", async () => {
|
||||
const view = await HomeOverviewViewStrategy.generate(
|
||||
{
|
||||
type: "home-overview",
|
||||
alert_entities: [{ entity: "binary_sensor.front_door" }],
|
||||
favorite_entities: ["binary_sensor.front_door"],
|
||||
},
|
||||
createHass("off")
|
||||
);
|
||||
|
||||
expect(
|
||||
sections(view).some((section) =>
|
||||
section.cards?.some((card) => card.type === "conditional")
|
||||
)
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("does not show the area-less empty state with a visible shortcut", async () => {
|
||||
const view = await HomeOverviewViewStrategy.generate(
|
||||
{
|
||||
type: "home-overview",
|
||||
alert_entities: [{ entity: "binary_sensor.front_door" }],
|
||||
hide_suggested_entities: true,
|
||||
shortcuts: [{ type: "custom", path: "/test", label: "Test" }],
|
||||
},
|
||||
createHass("off")
|
||||
);
|
||||
|
||||
expect(
|
||||
sections(view).some((section) =>
|
||||
section.cards?.some((card) => card.type === "conditional")
|
||||
)
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -5951,105 +5951,105 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/eslint-plugin@npm:8.67.0":
|
||||
version: 8.67.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.67.0"
|
||||
"@typescript-eslint/eslint-plugin@npm:8.68.0":
|
||||
version: 8.68.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.68.0"
|
||||
dependencies:
|
||||
"@eslint-community/regexpp": "npm:^4.12.2"
|
||||
"@typescript-eslint/scope-manager": "npm:8.67.0"
|
||||
"@typescript-eslint/type-utils": "npm:8.67.0"
|
||||
"@typescript-eslint/utils": "npm:8.67.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.67.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.68.0"
|
||||
"@typescript-eslint/type-utils": "npm:8.68.0"
|
||||
"@typescript-eslint/utils": "npm:8.68.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.68.0"
|
||||
ignore: "npm:^7.0.5"
|
||||
natural-compare: "npm:^1.4.0"
|
||||
ts-api-utils: "npm:^2.5.0"
|
||||
peerDependencies:
|
||||
"@typescript-eslint/parser": ^8.67.0
|
||||
"@typescript-eslint/parser": ^8.68.0
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/d53f9e51c6be98ff4cdcb94de24e2951f5e2b7a1dfc56c4f8a2d81e61346c511c0478117626d9cfd5d5d63f5e7a0e0653dc1abe1e7d926a3f9806aaf1fa11bd2
|
||||
checksum: 10/8776fd8ffde8eceae3b1aeca57e528e8ee61a683223c3bef455ca3c5e28e52275e6cd6ebf8ee284a46883e6220bbf297312084e1ee85e00cef017a78c169a674
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/parser@npm:8.67.0":
|
||||
version: 8.67.0
|
||||
resolution: "@typescript-eslint/parser@npm:8.67.0"
|
||||
"@typescript-eslint/parser@npm:8.68.0":
|
||||
version: 8.68.0
|
||||
resolution: "@typescript-eslint/parser@npm:8.68.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager": "npm:8.67.0"
|
||||
"@typescript-eslint/types": "npm:8.67.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.67.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.67.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.68.0"
|
||||
"@typescript-eslint/types": "npm:8.68.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.68.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.68.0"
|
||||
debug: "npm:^4.4.3"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/fa156ade066d0886ea0daa0f872940cdb450a2b5dbd1687e0df64ec19aac86711dde51e17e6b20e72387aa38daec811736821e9db950e8557affa7faea63d65c
|
||||
checksum: 10/979ae0d2c6a391fd5b9b0218189fda5112ff0b5ab2538eef1da4cd77ac8ae46f9b590db9779f2614bd193105b40cf12635b565c840f4cdbbb4a683e24cf5ec4b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/project-service@npm:8.67.0":
|
||||
version: 8.67.0
|
||||
resolution: "@typescript-eslint/project-service@npm:8.67.0"
|
||||
"@typescript-eslint/project-service@npm:8.68.0":
|
||||
version: 8.68.0
|
||||
resolution: "@typescript-eslint/project-service@npm:8.68.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/tsconfig-utils": "npm:^8.67.0"
|
||||
"@typescript-eslint/types": "npm:^8.67.0"
|
||||
"@typescript-eslint/tsconfig-utils": "npm:^8.68.0"
|
||||
"@typescript-eslint/types": "npm:^8.68.0"
|
||||
debug: "npm:^4.4.3"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/3beccdee1e74060eac4e6c5e7edd14a800c8c98e3313dbfe5838a357e83babc22c41c3ebe637c331c4b93db2da50f5479ccf4677db7b6b1a760f3cd27795b36f
|
||||
checksum: 10/37521df3731f9069c7df55362342dd0027f43947241de33c2a1c029b4553fe092e2ac69a534153aaf08c862e6b2390d864cb6c88a0714fc276395390df276fb6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/scope-manager@npm:8.67.0":
|
||||
version: 8.67.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:8.67.0"
|
||||
"@typescript-eslint/scope-manager@npm:8.68.0":
|
||||
version: 8.68.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:8.68.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.67.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.67.0"
|
||||
checksum: 10/a0ca8d52e6c5b2538f7df860acd5a36437cce8e6d5bcededdfde04c02489f4b523cb178b502c266d138cff6758ea375ebe311b2b6af373b127ded4876a697ffb
|
||||
"@typescript-eslint/types": "npm:8.68.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.68.0"
|
||||
checksum: 10/494d499315b4f3ab8eaa114aa53a4b2b9f533b82eb007cd5b5617cececfd8ad6938edb74f734479ae6e40a4086f510b6b984de8e66d06c518cb47817549db780
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/tsconfig-utils@npm:8.67.0, @typescript-eslint/tsconfig-utils@npm:^8.67.0":
|
||||
version: 8.67.0
|
||||
resolution: "@typescript-eslint/tsconfig-utils@npm:8.67.0"
|
||||
"@typescript-eslint/tsconfig-utils@npm:8.68.0, @typescript-eslint/tsconfig-utils@npm:^8.68.0":
|
||||
version: 8.68.0
|
||||
resolution: "@typescript-eslint/tsconfig-utils@npm:8.68.0"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/736d0ab8a273b4e4e17d412b390386eec0171cd3f7ba451bacd92c4f931051bba2049e040fdae84603ea0e07430afb05b49c6d8b6d429f9012d3c6b03eb69f54
|
||||
checksum: 10/b527a7c5deaad40bb72f78eec1bfea5997a7e792e64666984605e9a167246cf856c8d40290a7b3d39cefb3a242538e22a3a4fc136132e4669a314887e2de5f08
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/type-utils@npm:8.67.0":
|
||||
version: 8.67.0
|
||||
resolution: "@typescript-eslint/type-utils@npm:8.67.0"
|
||||
"@typescript-eslint/type-utils@npm:8.68.0":
|
||||
version: 8.68.0
|
||||
resolution: "@typescript-eslint/type-utils@npm:8.68.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.67.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.67.0"
|
||||
"@typescript-eslint/utils": "npm:8.67.0"
|
||||
"@typescript-eslint/types": "npm:8.68.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.68.0"
|
||||
"@typescript-eslint/utils": "npm:8.68.0"
|
||||
debug: "npm:^4.4.3"
|
||||
ts-api-utils: "npm:^2.5.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/a0b943e5a8f3c2e8490ec28599eb94561d7ad029fcf42f2e9a0e8d2685ce625898371cd0a7d3a906fe8b4077d72fee8329cad512a5b0521bc2167f7d88a984fb
|
||||
checksum: 10/ed6b6fe74eee017a79c9dbfa72b3a08748dac8de5702314494d8849feee516c20e2907ad730fd82e50be81c5984c575b29e35eb8b5c99091445b4bc5102ce836
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/types@npm:8.67.0, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.67.0":
|
||||
version: 8.67.0
|
||||
resolution: "@typescript-eslint/types@npm:8.67.0"
|
||||
checksum: 10/8edc1a14a52c7566409c19b34fb72ba3a62a3b409c98b1d496de9fcd83179fb128c8df2fde391c199316a54cdc5d314ba70ce7b440161dd850b73a44f3e54e6d
|
||||
"@typescript-eslint/types@npm:8.68.0, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.68.0":
|
||||
version: 8.68.0
|
||||
resolution: "@typescript-eslint/types@npm:8.68.0"
|
||||
checksum: 10/f9337e0263d95e6edca6e364f9b1f70d5840b9648b5aa0d02065bcee5809a50c92d024ba17b458182ec4e7fffe5efd05584f1667b1e43ff3e7121b218283a207
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:8.67.0":
|
||||
version: 8.67.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:8.67.0"
|
||||
"@typescript-eslint/typescript-estree@npm:8.68.0":
|
||||
version: 8.68.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:8.68.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/project-service": "npm:8.67.0"
|
||||
"@typescript-eslint/tsconfig-utils": "npm:8.67.0"
|
||||
"@typescript-eslint/types": "npm:8.67.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.67.0"
|
||||
"@typescript-eslint/project-service": "npm:8.68.0"
|
||||
"@typescript-eslint/tsconfig-utils": "npm:8.68.0"
|
||||
"@typescript-eslint/types": "npm:8.68.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.68.0"
|
||||
debug: "npm:^4.4.3"
|
||||
minimatch: "npm:^10.2.2"
|
||||
semver: "npm:^7.7.3"
|
||||
@@ -6057,32 +6057,32 @@ __metadata:
|
||||
ts-api-utils: "npm:^2.5.0"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/31c5be812f67c997b9d1e9324c4289b6268a12e9948492403fdd9c9b5df12dcc00f845ae93ae37c78f717583519060175f2147f165b65112eef1e40acf44ad1d
|
||||
checksum: 10/47d75cbc9c10842aeb3c1cc8d0625bd578b3b43b75d3dc86d3543a739b2fc8d0b524fd86b5af1f1493a3f0ae1b9e65c3200bf01a6bde0162efe6414708eac553
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/utils@npm:8.67.0":
|
||||
version: 8.67.0
|
||||
resolution: "@typescript-eslint/utils@npm:8.67.0"
|
||||
"@typescript-eslint/utils@npm:8.68.0":
|
||||
version: 8.68.0
|
||||
resolution: "@typescript-eslint/utils@npm:8.68.0"
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils": "npm:^4.9.1"
|
||||
"@typescript-eslint/scope-manager": "npm:8.67.0"
|
||||
"@typescript-eslint/types": "npm:8.67.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.67.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.68.0"
|
||||
"@typescript-eslint/types": "npm:8.68.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.68.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/8703468cdea3630275c6faacc031e7e4bd7fb8f8988e80ab987811ae3c63a9a9677496cee2b4811f49cb9bc9e6f6c0bdb91cb213353019ce31df0a2b351cea51
|
||||
checksum: 10/6581a1e7ced23cce82279df001d643844c97408bc84826da77ec77d52b77047004b202ff8cfcfbc0b0fef15bf2cafb5ae21bafbda24c9b58102ce634f66c2af9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/visitor-keys@npm:8.67.0":
|
||||
version: 8.67.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:8.67.0"
|
||||
"@typescript-eslint/visitor-keys@npm:8.68.0":
|
||||
version: 8.68.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:8.68.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.67.0"
|
||||
"@typescript-eslint/types": "npm:8.68.0"
|
||||
eslint-visitor-keys: "npm:^5.0.0"
|
||||
checksum: 10/c92c9b202105a8f398ab2ad4be58c8c8fd9af6b5ddd28cdcd57d75f7f04f69561a56a05aac9a008668c64faa7222ebb3fefe19097923a18eb2b9f7a90809a5be
|
||||
checksum: 10/d802bc2c4569495f3d83f9bcf169e3a432ac1c6672e243399886409dbc55d7fa30375d9d0b2cf0d9466b81fc2b01b991483eb56e8cdb8141721009978d3b078b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -9097,9 +9097,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint@npm:10.9.0":
|
||||
version: 10.9.0
|
||||
resolution: "eslint@npm:10.9.0"
|
||||
"eslint@npm:10.9.1":
|
||||
version: 10.9.1
|
||||
resolution: "eslint@npm:10.9.1"
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils": "npm:^4.8.0"
|
||||
"@eslint-community/regexpp": "npm:^4.12.2"
|
||||
@@ -9138,7 +9138,7 @@ __metadata:
|
||||
optional: true
|
||||
bin:
|
||||
eslint: bin/eslint.js
|
||||
checksum: 10/1cd63bbf4f2c003ed759ab6ae9e2030be0abafdcd7b9ffb5be9e9c3aa3ea6142e6af0a337aac21044957750807fde06c9d052e2c9734cca6792dc2d095f9c180
|
||||
checksum: 10/4cee237c7d2c445a05330e595b18521d7452fd3f419241bf1ccf81c769a6761c10ca5a06988197482d4842ed1a26b1dcb51b9526408990cee84559e6c08cb3ab
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -10206,7 +10206,7 @@ __metadata:
|
||||
echarts: "npm:6.1.0"
|
||||
echarts-extension-chart2music: "npm:0.1.1"
|
||||
element-internals-polyfill: "npm:3.0.2"
|
||||
eslint: "npm:10.9.0"
|
||||
eslint: "npm:10.9.1"
|
||||
eslint-config-prettier: "npm:10.1.8"
|
||||
eslint-import-resolver-webpack: "npm:0.13.11"
|
||||
eslint-plugin-import-x: "npm:4.17.1"
|
||||
@@ -10246,7 +10246,7 @@ __metadata:
|
||||
luxon: "npm:3.7.2"
|
||||
map-stream: "npm:0.0.7"
|
||||
maplibre-gl: "npm:5.24.0"
|
||||
marked: "npm:18.0.10"
|
||||
marked: "npm:18.0.11"
|
||||
memoize-one: "npm:6.0.0"
|
||||
minify-literals: "npm:2.2.0"
|
||||
node-vibrant: "npm:4.0.4"
|
||||
@@ -10269,7 +10269,7 @@ __metadata:
|
||||
tinykeys: "patch:tinykeys@npm%3A4.0.0#~/.yarn/patches/tinykeys-npm-4.0.0-a6ca3fd771.patch"
|
||||
ts-lit-plugin: "npm:2.0.2"
|
||||
typescript: "npm:6.0.3"
|
||||
typescript-eslint: "npm:8.67.0"
|
||||
typescript-eslint: "npm:8.68.0"
|
||||
vite-tsconfig-paths: "npm:6.1.1"
|
||||
vitest: "npm:4.1.11"
|
||||
webpack-stats-plugin: "npm:1.1.3"
|
||||
@@ -11944,12 +11944,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"marked@npm:18.0.10":
|
||||
version: 18.0.10
|
||||
resolution: "marked@npm:18.0.10"
|
||||
"marked@npm:18.0.11":
|
||||
version: 18.0.11
|
||||
resolution: "marked@npm:18.0.11"
|
||||
bin:
|
||||
marked: bin/marked.js
|
||||
checksum: 10/0d4b560e0773fd6ba30a4e7560ba7ae1f05d47b23926ad8337d9f80c598dfdbffdf2f81e773a1789e8bc9d9c2c2d3d0c9143a54d32581c336a3bbb1bad80461c
|
||||
checksum: 10/6da69f17820c7442358d751e902d3e0e06fef957e1178d634db938cf339c4504399c9f11a953b0a116b1e5bebc1c7fb52726777cc6c351f2c9f39e4a8276e21a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -15232,18 +15232,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript-eslint@npm:8.67.0":
|
||||
version: 8.67.0
|
||||
resolution: "typescript-eslint@npm:8.67.0"
|
||||
"typescript-eslint@npm:8.68.0":
|
||||
version: 8.68.0
|
||||
resolution: "typescript-eslint@npm:8.68.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/eslint-plugin": "npm:8.67.0"
|
||||
"@typescript-eslint/parser": "npm:8.67.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.67.0"
|
||||
"@typescript-eslint/utils": "npm:8.67.0"
|
||||
"@typescript-eslint/eslint-plugin": "npm:8.68.0"
|
||||
"@typescript-eslint/parser": "npm:8.68.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.68.0"
|
||||
"@typescript-eslint/utils": "npm:8.68.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/4efc2cea5b067f65d58e010fda3888ec94f2e67ebfa289be78c3a02458787b7125cf8f6095776b912005fb69e8748ad4018777b16436a0658957c163ef213bf9
|
||||
checksum: 10/451c64296ac5f48e4621a03324f5aaef4a26f1dd9d4df63bd3cb07f136e8379185a800731f83634eb5d909c696fad820ba74d31ab3d7cf263328504275d1305b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user