mirror of
https://github.com/home-assistant/frontend.git
synced 2026-01-16 04:07:38 +00:00
Compare commits
11 Commits
add-pr-art
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db5f823b6b | ||
|
|
1d241aa49a | ||
|
|
fece231faf | ||
|
|
fffb3c3a28 | ||
|
|
fe14d436ff | ||
|
|
42e02be928 | ||
|
|
6213b6cd2a | ||
|
|
cd75c55392 | ||
|
|
ca325020d7 | ||
|
|
6250402661 | ||
|
|
0bfca79851 |
13
.github/workflows/release.yaml
vendored
13
.github/workflows/release.yaml
vendored
@@ -19,8 +19,11 @@ jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
environment: pypi
|
||||
permissions:
|
||||
contents: write # Required to upload release assets
|
||||
id-token: write # For "Trusted Publisher" to PyPi
|
||||
if: github.repository_owner == 'home-assistant'
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
@@ -46,14 +49,18 @@ jobs:
|
||||
run: ./script/translations_download
|
||||
env:
|
||||
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }}
|
||||
|
||||
- name: Build and release package
|
||||
run: |
|
||||
python3 -m pip install twine build
|
||||
export TWINE_USERNAME="__token__"
|
||||
export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}"
|
||||
python3 -m pip install build
|
||||
export SKIP_FETCH_NIGHTLY_TRANSLATIONS=1
|
||||
script/release
|
||||
|
||||
- name: Publish to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
|
||||
with:
|
||||
skip-existing: true
|
||||
|
||||
- name: Upload release assets
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
with:
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
"@types/tar": "6.1.13",
|
||||
"@types/ua-parser-js": "0.7.39",
|
||||
"@types/webspeechapi": "0.0.29",
|
||||
"@vitest/coverage-v8": "4.0.16",
|
||||
"@vitest/coverage-v8": "4.0.17",
|
||||
"babel-loader": "10.0.0",
|
||||
"babel-plugin-template-html-minifier": "4.1.0",
|
||||
"browserslist-useragent-regexp": "4.1.3",
|
||||
@@ -217,7 +217,7 @@
|
||||
"typescript": "5.9.3",
|
||||
"typescript-eslint": "8.52.0",
|
||||
"vite-tsconfig-paths": "6.0.4",
|
||||
"vitest": "4.0.16",
|
||||
"vitest": "4.0.17",
|
||||
"webpack-stats-plugin": "1.1.3",
|
||||
"webpackbar": "7.0.0",
|
||||
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/bin/sh
|
||||
# Pushes a new version to PyPi.
|
||||
|
||||
# Stop on errors
|
||||
set -e
|
||||
@@ -12,5 +11,4 @@ yarn install
|
||||
script/build_frontend
|
||||
|
||||
rm -rf dist home_assistant_frontend.egg-info
|
||||
python3 -m build
|
||||
python3 -m twine upload dist/*.whl --skip-existing
|
||||
python3 -m build -q
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { expose } from "comlink";
|
||||
import Fuse, { type FuseOptionKey } from "fuse.js";
|
||||
import type { FuseOptionKey, IFuseOptions } from "fuse.js";
|
||||
import Fuse from "fuse.js";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { ipCompare, stringCompare } from "../../common/string/compare";
|
||||
import { stripDiacritics } from "../../common/string/strip-diacritics";
|
||||
import { multiTermSearch } from "../../resources/fuseMultiTerm";
|
||||
import type {
|
||||
ClonedDataTableColumnData,
|
||||
DataTableRowData,
|
||||
@@ -11,46 +11,159 @@ import type {
|
||||
SortingDirection,
|
||||
} from "./ha-data-table";
|
||||
|
||||
const getSearchKeys = memoizeOne(
|
||||
(columns: SortableColumnContainer): FuseOptionKey<DataTableRowData>[] => {
|
||||
const searchKeys = new Set<string>();
|
||||
interface FilterKeyConfig {
|
||||
key: string;
|
||||
filterKey?: string;
|
||||
}
|
||||
|
||||
Object.entries(columns).forEach(([key, column]) => {
|
||||
if (column.filterable) {
|
||||
searchKeys.add(
|
||||
column.filterKey
|
||||
? `${column.valueColumn || key}.${column.filterKey}`
|
||||
: key
|
||||
);
|
||||
}
|
||||
});
|
||||
return Array.from(searchKeys);
|
||||
const getFilterKeys = memoizeOne(
|
||||
(columns: SortableColumnContainer): FilterKeyConfig[] =>
|
||||
Object.entries(columns)
|
||||
.filter(([, column]) => column.filterable)
|
||||
.map(([key, column]) => ({
|
||||
key: column.valueColumn || key,
|
||||
filterKey: column.filterKey,
|
||||
}))
|
||||
);
|
||||
|
||||
const getSearchableValue = (
|
||||
row: DataTableRowData,
|
||||
{ key, filterKey }: FilterKeyConfig
|
||||
): string => {
|
||||
let value = row[key];
|
||||
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
);
|
||||
|
||||
const fuseIndex = memoizeOne(
|
||||
(data: DataTableRowData[], keys: FuseOptionKey<DataTableRowData>[]) =>
|
||||
Fuse.createIndex(keys, data)
|
||||
);
|
||||
if (filterKey && typeof value === "object" && !Array.isArray(value)) {
|
||||
value = value[filterKey];
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
const stringValues = value
|
||||
.filter((item) => item != null && typeof item !== "object")
|
||||
.map(String);
|
||||
return stripDiacritics(stringValues.join(" ").toLowerCase());
|
||||
}
|
||||
|
||||
return stripDiacritics(String(value).toLowerCase());
|
||||
};
|
||||
|
||||
/** Filters data using exact substring matching (all terms must match). */
|
||||
const filterDataExact = (
|
||||
data: DataTableRowData[],
|
||||
filterKeys: FilterKeyConfig[],
|
||||
terms: string[]
|
||||
): DataTableRowData[] => {
|
||||
if (terms.length === 1) {
|
||||
const term = terms[0];
|
||||
return data.filter((row) =>
|
||||
filterKeys.some((config) =>
|
||||
getSearchableValue(row, config).includes(term)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return data.filter((row) => {
|
||||
const searchString = filterKeys
|
||||
.map((config) => getSearchableValue(row, config))
|
||||
.join(" ");
|
||||
return terms.every((term) => searchString.includes(term));
|
||||
});
|
||||
};
|
||||
|
||||
const FUZZY_OPTIONS: IFuseOptions<DataTableRowData> = {
|
||||
ignoreDiacritics: true,
|
||||
isCaseSensitive: false,
|
||||
threshold: 0.2, // Stricter than default 0.3
|
||||
minMatchCharLength: 2,
|
||||
ignoreLocation: true,
|
||||
shouldSort: false,
|
||||
};
|
||||
|
||||
interface FuseKeyConfig {
|
||||
name: string | string[];
|
||||
getFn: (row: DataTableRowData) => string;
|
||||
}
|
||||
|
||||
/** Filters data using fuzzy matching with Fuse.js (all terms must match). */
|
||||
const filterDataFuzzy = (
|
||||
data: DataTableRowData[],
|
||||
filterKeys: FilterKeyConfig[],
|
||||
terms: string[]
|
||||
): DataTableRowData[] => {
|
||||
// Build Fuse.js search keys from filter keys
|
||||
const fuseKeys: FuseKeyConfig[] = filterKeys.map((config) => ({
|
||||
name: config.filterKey ? [config.key, config.filterKey] : config.key,
|
||||
getFn: (row: DataTableRowData) => getSearchableValue(row, config),
|
||||
}));
|
||||
|
||||
// Find minimum term length to adjust minMatchCharLength
|
||||
const minTermLength = Math.min(...terms.map((t) => t.length));
|
||||
const minMatchCharLength = Math.min(minTermLength, 2);
|
||||
|
||||
const fuse = new Fuse<DataTableRowData>(data, {
|
||||
...FUZZY_OPTIONS,
|
||||
keys: fuseKeys as FuseOptionKey<DataTableRowData>[],
|
||||
minMatchCharLength,
|
||||
});
|
||||
|
||||
// For single term, simple search
|
||||
if (terms.length === 1) {
|
||||
return fuse.search(terms[0]).map((r) => r.item);
|
||||
}
|
||||
|
||||
// For multiple terms, all must match (AND logic)
|
||||
const expression = {
|
||||
$and: terms.map((term) => ({
|
||||
$or: fuseKeys.map((key) => ({
|
||||
$path: Array.isArray(key.name) ? key.name : [key.name],
|
||||
$val: term,
|
||||
})),
|
||||
})),
|
||||
};
|
||||
|
||||
return fuse.search(expression).map((r) => r.item);
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters data with exact match priority and fuzzy fallback.
|
||||
* - First tries exact substring matching
|
||||
* - If exact matches found, returns only those
|
||||
* - If no exact matches, falls back to fuzzy search with strict scoring
|
||||
*/
|
||||
const filterData = (
|
||||
data: DataTableRowData[],
|
||||
columns: SortableColumnContainer,
|
||||
filter: string
|
||||
) => {
|
||||
filter = stripDiacritics(filter.toLowerCase());
|
||||
): DataTableRowData[] => {
|
||||
const normalizedFilter = stripDiacritics(filter.toLowerCase().trim());
|
||||
|
||||
if (filter === "") {
|
||||
if (!normalizedFilter) {
|
||||
return data;
|
||||
}
|
||||
|
||||
const keys = getSearchKeys(columns);
|
||||
const filterKeys = getFilterKeys(columns);
|
||||
|
||||
const index = fuseIndex(data, keys);
|
||||
if (!filterKeys.length) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return multiTermSearch<DataTableRowData>(data, filter, keys, index, {
|
||||
threshold: 0.2, // reduce fuzzy matches in data tables
|
||||
});
|
||||
const terms = normalizedFilter.split(/\s+/);
|
||||
|
||||
// First, try exact substring matching
|
||||
const exactMatches = filterDataExact(data, filterKeys, terms);
|
||||
|
||||
if (exactMatches.length > 0) {
|
||||
return exactMatches;
|
||||
}
|
||||
|
||||
// No exact matches, fall back to fuzzy search
|
||||
return filterDataFuzzy(data, filterKeys, terms);
|
||||
};
|
||||
|
||||
const sortData = (
|
||||
|
||||
@@ -138,10 +138,10 @@ export class HaMarkdown extends LitElement {
|
||||
--markdown-table-padding-inline: 0;
|
||||
--markdown-table-padding-block: 0;
|
||||
th {
|
||||
vertical-align: attr(align, center);
|
||||
vertical-align: attr(valign, middle);
|
||||
}
|
||||
td {
|
||||
vertical-align: attr(align, left);
|
||||
vertical-align: attr(valign, middle);
|
||||
}
|
||||
}
|
||||
table {
|
||||
|
||||
@@ -223,7 +223,6 @@ export class HaRelatedItems extends LitElement {
|
||||
.src=${brandsUrl({
|
||||
domain: entry.domain,
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
crossorigin="anonymous"
|
||||
@@ -249,7 +248,6 @@ export class HaRelatedItems extends LitElement {
|
||||
.src=${brandsUrl({
|
||||
domain: integration,
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
crossorigin="anonymous"
|
||||
|
||||
@@ -87,7 +87,6 @@ export class HaMediaSelector extends LitElement {
|
||||
this._thumbnailUrl = brandsUrl({
|
||||
domain: extractDomainFromBrandUrl(thumbnail),
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -589,10 +589,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
// On keypresses on the listbox, we're going to ignore mouse enter events
|
||||
// for 100ms so that we ignore it when pressing down arrow scrolls the
|
||||
// sidebar causing the mouse to hover a new icon
|
||||
if (
|
||||
this.alwaysExpand ||
|
||||
new Date().getTime() < this._recentKeydownActiveUntil
|
||||
) {
|
||||
if (new Date().getTime() < this._recentKeydownActiveUntil) {
|
||||
return;
|
||||
}
|
||||
if (this._mouseLeaveTimeout) {
|
||||
@@ -612,7 +609,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
|
||||
private _listboxFocusIn(ev) {
|
||||
if (this.alwaysExpand || ev.target.localName !== "ha-md-list-item") {
|
||||
if (ev.target.localName !== "ha-md-list-item") {
|
||||
return;
|
||||
}
|
||||
this._showTooltip(ev.target);
|
||||
@@ -652,6 +649,14 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
clearTimeout(this._tooltipHideTimeout);
|
||||
this._tooltipHideTimeout = undefined;
|
||||
}
|
||||
const itemText = item.querySelector(".item-text") as HTMLElement | null;
|
||||
if (this.hasAttribute("expanded") && itemText) {
|
||||
const isTruncated = itemText.scrollWidth > itemText.clientWidth;
|
||||
if (!isTruncated) {
|
||||
this._hideTooltip();
|
||||
return;
|
||||
}
|
||||
}
|
||||
const tooltip = this._tooltip;
|
||||
const allListbox = this.shadowRoot!.querySelectorAll("ha-md-list")!;
|
||||
const listbox = [...allListbox].find((lb) => lb.contains(item));
|
||||
@@ -662,9 +667,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
(listbox?.offsetTop ?? 0) -
|
||||
(listbox?.scrollTop ?? 0);
|
||||
|
||||
tooltip.innerText = (
|
||||
item.querySelector(".item-text") as HTMLElement
|
||||
).innerText;
|
||||
tooltip.innerText = itemText?.innerText ?? "";
|
||||
tooltip.style.display = "block";
|
||||
tooltip.style.position = "fixed";
|
||||
tooltip.style.top = `${top}px`;
|
||||
@@ -846,6 +849,9 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
:host([expanded]) ha-md-list-item .item-text {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.divider {
|
||||
@@ -913,7 +919,9 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
position: absolute;
|
||||
opacity: 0.9;
|
||||
border-radius: var(--ha-border-radius-sm);
|
||||
white-space: nowrap;
|
||||
max-width: calc(var(--ha-space-20) * 3);
|
||||
white-space: normal;
|
||||
overflow-wrap: break-word;
|
||||
color: var(--sidebar-background-color);
|
||||
background-color: var(--sidebar-text-color);
|
||||
padding: var(--ha-space-1);
|
||||
|
||||
@@ -793,7 +793,6 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
thumbnailUrl = brandsUrl({
|
||||
domain: extractDomainFromBrandUrl(thumbnailUrl),
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
mdiViewDashboard,
|
||||
} from "@mdi/js";
|
||||
import type { HomeAssistant, PanelInfo } from "../types";
|
||||
import type { PageNavigation } from "../layouts/hass-tabs-subpage";
|
||||
import type { LocalizeKeys } from "../common/translations/localize";
|
||||
|
||||
/** Panel to show when no panel is picked. */
|
||||
export const DEFAULT_PANEL = "lovelace";
|
||||
@@ -72,6 +74,40 @@ export const getPanelTitleFromUrlPath = (
|
||||
return getPanelTitle(hass, panel);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get subpage title for config panel routes.
|
||||
* Returns the specific subpage title (e.g., "Automations") if found,
|
||||
* or undefined to fall back to the panel title (e.g., "Settings").
|
||||
*
|
||||
* @param hass HomeAssistant instance
|
||||
* @param path Full route path (e.g., "/config/automation/dashboard")
|
||||
* @param configSections Config sections metadata for resolving subpage titles
|
||||
* @returns Localized subpage title, or undefined if not found
|
||||
*/
|
||||
export const getConfigSubpageTitle = (
|
||||
hass: HomeAssistant,
|
||||
path: string,
|
||||
configSections: Record<string, PageNavigation[]>
|
||||
): string | undefined => {
|
||||
// Search through all config section groups for a matching path
|
||||
for (const sectionGroup of Object.values(configSections)) {
|
||||
const pageNav = sectionGroup.find((nav) => path.startsWith(nav.path));
|
||||
if (pageNav) {
|
||||
if (pageNav.translationKey) {
|
||||
const localized = hass.localize(pageNav.translationKey as LocalizeKeys);
|
||||
if (localized) {
|
||||
return localized;
|
||||
}
|
||||
}
|
||||
|
||||
if (pageNav.name) {
|
||||
return pageNav.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const getPanelIcon = (panel: PanelInfo): string | undefined => {
|
||||
if (!panel.icon) {
|
||||
switch (panel.component_name) {
|
||||
|
||||
@@ -863,6 +863,7 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
}
|
||||
|
||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
||||
ev.stopPropagation();
|
||||
const action = ev.detail?.item?.value;
|
||||
|
||||
if (!action) {
|
||||
|
||||
@@ -436,6 +436,24 @@ class DialogAddAutomationElement
|
||||
|
||||
// #region render
|
||||
|
||||
private _getEmptyNote(automationElementType: string) {
|
||||
if (
|
||||
automationElementType !== "trigger" &&
|
||||
automationElementType !== "condition"
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.hass.localize(
|
||||
`ui.panel.config.automation.editor.${automationElementType}s.no_items_for_target_note`,
|
||||
{
|
||||
labs_link: html`<a href="/config/labs" @click=${this._close}
|
||||
>${this.hass.localize("ui.panel.config.labs.caption")}</a
|
||||
>`,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._params) {
|
||||
return nothing;
|
||||
@@ -701,6 +719,7 @@ class DialogAddAutomationElement
|
||||
.emptyLabel=${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.${automationElementType}s.no_items_for_target`
|
||||
)}
|
||||
.emptyNote=${this._getEmptyNote(automationElementType)}
|
||||
.tooltipDescription=${this._tab === "targets"}
|
||||
.target=${(this._tab === "targets" &&
|
||||
this._selectedTarget &&
|
||||
@@ -1696,9 +1715,9 @@ class DialogAddAutomationElement
|
||||
|
||||
// #region interaction
|
||||
|
||||
private _close() {
|
||||
private _close = () => {
|
||||
this._open = false;
|
||||
}
|
||||
};
|
||||
|
||||
private _back() {
|
||||
mainWindow.history.back();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { mdiInformationOutline, mdiPlus } from "@mdi/js";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { LitElement, css, html, nothing, type TemplateResult } from "lit";
|
||||
import {
|
||||
customElement,
|
||||
eventOptions,
|
||||
@@ -39,6 +39,8 @@ export class HaAutomationAddItems extends LitElement {
|
||||
|
||||
@property({ attribute: "empty-label" }) public emptyLabel!: string;
|
||||
|
||||
@property({ attribute: false }) public emptyNote?: string | TemplateResult;
|
||||
|
||||
@property({ attribute: false }) public target?: Target;
|
||||
|
||||
@property({ attribute: false }) public getLabel!: (
|
||||
@@ -79,6 +81,9 @@ export class HaAutomationAddItems extends LitElement {
|
||||
? html`${this.emptyLabel}
|
||||
${this.target
|
||||
? html`<div>${this._renderTarget(this.target)}</div>`
|
||||
: nothing}
|
||||
${this.emptyNote
|
||||
? html`<div class="empty-note">${this.emptyNote}</div>`
|
||||
: nothing}`
|
||||
: repeat(
|
||||
this.items,
|
||||
@@ -199,6 +204,7 @@ export class HaAutomationAddItems extends LitElement {
|
||||
static styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
}
|
||||
:host([scrollable]) .items {
|
||||
overflow: auto;
|
||||
@@ -213,13 +219,24 @@ export class HaAutomationAddItems extends LitElement {
|
||||
background-color: var(--ha-color-surface-default);
|
||||
align-items: center;
|
||||
color: var(--ha-color-text-secondary);
|
||||
padding: 0;
|
||||
padding: var(--ha-space-4);
|
||||
margin: 0 var(--ha-space-4)
|
||||
max(var(--safe-area-inset-bottom), var(--ha-space-3));
|
||||
line-height: var(--ha-line-height-expanded);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.empty-note {
|
||||
color: var(--ha-color-text-secondary);
|
||||
margin-top: var(--ha-space-2);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-note a {
|
||||
color: currentColor;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.items.error {
|
||||
background-color: var(--ha-color-fill-danger-quiet-resting);
|
||||
color: var(--ha-color-on-danger-normal);
|
||||
|
||||
@@ -839,6 +839,7 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
}
|
||||
|
||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
||||
ev.stopPropagation();
|
||||
const action = ev.detail?.item?.value;
|
||||
|
||||
if (!action) {
|
||||
|
||||
@@ -350,6 +350,7 @@ export default class HaAutomationOptionRow extends LitElement {
|
||||
}
|
||||
|
||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
||||
ev.stopPropagation();
|
||||
const action = ev.detail?.item?.value;
|
||||
|
||||
if (!action) {
|
||||
|
||||
@@ -815,6 +815,7 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
}
|
||||
|
||||
private _handleDropdownSelect(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
||||
ev.stopPropagation();
|
||||
const action = ev.detail?.item?.value;
|
||||
|
||||
if (!action) {
|
||||
|
||||
@@ -152,7 +152,6 @@ class HaBackupConfigAgents extends LitElement {
|
||||
.src=${brandsUrl({
|
||||
domain,
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
crossorigin="anonymous"
|
||||
|
||||
@@ -66,7 +66,6 @@ class HaBackupAgentsPicker extends LitElement {
|
||||
.src=${brandsUrl({
|
||||
domain,
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
crossorigin="anonymous"
|
||||
|
||||
@@ -226,7 +226,6 @@ class HaConfigBackupBackups extends SubscribeMixin(LitElement) {
|
||||
.src=${brandsUrl({
|
||||
domain,
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
height="24"
|
||||
|
||||
@@ -207,7 +207,6 @@ class HaConfigBackupDetails extends LitElement {
|
||||
.src=${brandsUrl({
|
||||
domain,
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized:
|
||||
this.hass.themes?.darkMode,
|
||||
})}
|
||||
|
||||
@@ -252,7 +252,6 @@ class HaConfigBackupSettings extends LitElement {
|
||||
.src=${brandsUrl({
|
||||
domain: "cloud",
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
crossorigin="anonymous"
|
||||
|
||||
@@ -4,10 +4,8 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-button";
|
||||
import { createCloseHeading } from "../../../components/ha-dialog";
|
||||
import "../../../components/ha-icon-picker";
|
||||
import "../../../components/ha-settings-row";
|
||||
import "../../../components/ha-textfield";
|
||||
import "../../../components/ha-wa-dialog";
|
||||
import "../../../components/ha-dialog-footer";
|
||||
import { updateEntityRegistryEntry } from "../../../data/entity/entity_registry";
|
||||
import { haStyleDialog } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
@@ -28,14 +26,21 @@ class DialogAssignCategory extends LitElement {
|
||||
|
||||
@state() private _submitting?: boolean;
|
||||
|
||||
@state() private _open = false;
|
||||
|
||||
public showDialog(params: AssignCategoryDialogParams): void {
|
||||
this._params = params;
|
||||
this._scope = params.scope;
|
||||
this._category = params.entityReg.categories[params.scope];
|
||||
this._error = undefined;
|
||||
this._open = true;
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._open = false;
|
||||
}
|
||||
|
||||
private _dialogClosed(): void {
|
||||
this._error = "";
|
||||
this._params = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
@@ -47,47 +52,46 @@ class DialogAssignCategory extends LitElement {
|
||||
}
|
||||
const entry = this._params.entityReg.categories[this._params.scope];
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${createCloseHeading(
|
||||
this.hass,
|
||||
entry
|
||||
? this.hass.localize("ui.panel.config.category.assign.edit")
|
||||
: this.hass.localize("ui.panel.config.category.assign.assign")
|
||||
)}
|
||||
<ha-wa-dialog
|
||||
.hass=${this.hass}
|
||||
.open=${this._open}
|
||||
header-title=${entry
|
||||
? this.hass.localize("ui.panel.config.category.assign.edit")
|
||||
: this.hass.localize("ui.panel.config.category.assign.assign")}
|
||||
@closed=${this._dialogClosed}
|
||||
>
|
||||
<div>
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
<div class="form">
|
||||
<ha-category-picker
|
||||
.hass=${this.hass}
|
||||
.scope=${this._scope}
|
||||
.label=${this.hass.localize(
|
||||
"ui.components.category-picker.category"
|
||||
)}
|
||||
.value=${this._category}
|
||||
@value-changed=${this._categoryChanged}
|
||||
></ha-category-picker>
|
||||
</div>
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
<div class="form">
|
||||
<ha-category-picker
|
||||
.hass=${this.hass}
|
||||
.scope=${this._scope}
|
||||
.label=${this.hass.localize(
|
||||
"ui.components.category-picker.category"
|
||||
)}
|
||||
.value=${this._category}
|
||||
@value-changed=${this._categoryChanged}
|
||||
autofocus
|
||||
></ha-category-picker>
|
||||
</div>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
slot="primaryAction"
|
||||
@click=${this.closeDialog}
|
||||
>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
slot="primaryAction"
|
||||
@click=${this._updateEntry}
|
||||
.disabled=${!!this._submitting}
|
||||
>
|
||||
${this.hass.localize("ui.common.save")}
|
||||
</ha-button>
|
||||
</ha-dialog>
|
||||
<ha-dialog-footer slot="footer">
|
||||
<ha-button
|
||||
slot="secondaryAction"
|
||||
appearance="plain"
|
||||
@click=${this.closeDialog}
|
||||
>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
slot="primaryAction"
|
||||
@click=${this._updateEntry}
|
||||
.disabled=${!!this._submitting}
|
||||
>
|
||||
${this.hass.localize("ui.common.save")}
|
||||
</ha-button>
|
||||
</ha-dialog-footer>
|
||||
</ha-wa-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-alert";
|
||||
import { createCloseHeading } from "../../../components/ha-dialog";
|
||||
import "../../../components/ha-wa-dialog";
|
||||
import "../../../components/ha-dialog-footer";
|
||||
import "../../../components/ha-icon-picker";
|
||||
import "../../../components/ha-settings-row";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-textfield";
|
||||
import type {
|
||||
@@ -30,11 +30,14 @@ class DialogCategoryDetail extends LitElement {
|
||||
|
||||
@state() private _submitting?: boolean;
|
||||
|
||||
@state() private _open = false;
|
||||
|
||||
public async showDialog(
|
||||
params: CategoryRegistryDetailDialogParams
|
||||
): Promise<void> {
|
||||
this._params = params;
|
||||
this._error = undefined;
|
||||
this._open = true;
|
||||
if (this._params.entry) {
|
||||
this._name = this._params.entry.name || "";
|
||||
this._icon = this._params.entry.icon || null;
|
||||
@@ -46,6 +49,10 @@ class DialogCategoryDetail extends LitElement {
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._open = false;
|
||||
}
|
||||
|
||||
private _dialogClosed(): void {
|
||||
this._error = "";
|
||||
this._params = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
@@ -58,61 +65,55 @@ class DialogCategoryDetail extends LitElement {
|
||||
const entry = this._params.entry;
|
||||
const nameInvalid = !this._isNameValid();
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${createCloseHeading(
|
||||
this.hass,
|
||||
entry
|
||||
? this.hass.localize("ui.panel.config.category.editor.edit")
|
||||
: this.hass.localize("ui.panel.config.category.editor.create")
|
||||
)}
|
||||
<ha-wa-dialog
|
||||
.hass=${this.hass}
|
||||
.open=${this._open}
|
||||
header-title=${entry
|
||||
? this.hass.localize("ui.panel.config.category.editor.edit")
|
||||
: this.hass.localize("ui.panel.config.category.editor.create")}
|
||||
@closed=${this._dialogClosed}
|
||||
>
|
||||
<div>
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
<div class="form">
|
||||
<ha-textfield
|
||||
.value=${this._name}
|
||||
@input=${this._nameChanged}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.category.editor.name"
|
||||
)}
|
||||
.validationMessage=${this.hass.localize(
|
||||
"ui.panel.config.category.editor.required_error_msg"
|
||||
)}
|
||||
required
|
||||
dialogInitialFocus
|
||||
></ha-textfield>
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
<div class="form">
|
||||
<ha-textfield
|
||||
.value=${this._name}
|
||||
@input=${this._nameChanged}
|
||||
.label=${this.hass.localize("ui.panel.config.category.editor.name")}
|
||||
.validationMessage=${this.hass.localize(
|
||||
"ui.panel.config.category.editor.required_error_msg"
|
||||
)}
|
||||
required
|
||||
autofocus
|
||||
></ha-textfield>
|
||||
|
||||
<ha-icon-picker
|
||||
.hass=${this.hass}
|
||||
.value=${this._icon}
|
||||
@value-changed=${this._iconChanged}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.category.editor.icon"
|
||||
)}
|
||||
></ha-icon-picker>
|
||||
</div>
|
||||
<ha-icon-picker
|
||||
.hass=${this.hass}
|
||||
.value=${this._icon}
|
||||
@value-changed=${this._iconChanged}
|
||||
.label=${this.hass.localize("ui.panel.config.category.editor.icon")}
|
||||
></ha-icon-picker>
|
||||
</div>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
slot="secondaryAction"
|
||||
@click=${this.closeDialog}
|
||||
>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
slot="primaryAction"
|
||||
@click=${this._updateEntry}
|
||||
.disabled=${nameInvalid || !!this._submitting}
|
||||
>
|
||||
${entry
|
||||
? this.hass.localize("ui.common.save")
|
||||
: this.hass.localize("ui.common.add")}
|
||||
</ha-button>
|
||||
</ha-dialog>
|
||||
<ha-dialog-footer slot="footer">
|
||||
<ha-button
|
||||
slot="secondaryAction"
|
||||
appearance="plain"
|
||||
@click=${this.closeDialog}
|
||||
>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
slot="primaryAction"
|
||||
@click=${this._updateEntry}
|
||||
.disabled=${nameInvalid || !!this._submitting}
|
||||
>
|
||||
${entry
|
||||
? this.hass.localize("ui.common.save")
|
||||
: this.hass.localize("ui.common.add")}
|
||||
</ha-button>
|
||||
</ha-dialog-footer>
|
||||
</ha-wa-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +227,6 @@ export class DialogHelperDetail extends LitElement {
|
||||
src=${brandsUrl({
|
||||
domain,
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
crossorigin="anonymous"
|
||||
|
||||
@@ -60,7 +60,6 @@ class HaDomainIntegrations extends LitElement {
|
||||
src=${brandsUrl({
|
||||
domain: flow.handler,
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
crossorigin="anonymous"
|
||||
@@ -106,7 +105,6 @@ class HaDomainIntegrations extends LitElement {
|
||||
src=${brandsUrl({
|
||||
domain,
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
crossorigin="anonymous"
|
||||
@@ -170,7 +168,6 @@ class HaDomainIntegrations extends LitElement {
|
||||
src=${brandsUrl({
|
||||
domain: this.domain,
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
crossorigin="anonymous"
|
||||
|
||||
@@ -57,7 +57,6 @@ export class HaIntegrationListItem extends ListItemBase {
|
||||
src=${brandsUrl({
|
||||
domain: this.integration.domain,
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
brand: this.brand,
|
||||
})}
|
||||
|
||||
@@ -203,7 +203,6 @@ class HaConfigLabs extends SubscribeMixin(LitElement) {
|
||||
src=${brandsUrl({
|
||||
domain: preview_feature.domain,
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
crossorigin="anonymous"
|
||||
|
||||
@@ -175,24 +175,31 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
template: narrow
|
||||
? undefined
|
||||
: (dashboard) => html`
|
||||
${dashboard.title}
|
||||
${dashboard.default
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
.id="default-icon-${dashboard.title}"
|
||||
style="padding-left: 10px; padding-inline-start: 10px; padding-inline-end: initial; direction: var(--direction);"
|
||||
.path=${mdiHomeCircleOutline}
|
||||
></ha-svg-icon>
|
||||
<ha-tooltip
|
||||
.for="default-icon-${dashboard.title}"
|
||||
placement="right"
|
||||
>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.lovelace.dashboards.default_dashboard`
|
||||
)}
|
||||
</ha-tooltip>
|
||||
`
|
||||
: nothing}
|
||||
<span
|
||||
style="display:flex; align-items:center; gap: var(--ha-space-2); min-width:0; width:100%;"
|
||||
>
|
||||
<span
|
||||
style="min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; flex:1;"
|
||||
>${dashboard.title}</span
|
||||
>
|
||||
${dashboard.default
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
.id="default-icon-${dashboard.title}"
|
||||
style="flex-shrink:0;"
|
||||
.path=${mdiHomeCircleOutline}
|
||||
></ha-svg-icon>
|
||||
<ha-tooltip
|
||||
.for="default-icon-${dashboard.title}"
|
||||
placement="right"
|
||||
>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.lovelace.dashboards.default_dashboard`
|
||||
)}
|
||||
</ha-tooltip>
|
||||
`
|
||||
: nothing}
|
||||
</span>
|
||||
`,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -75,7 +75,6 @@ class HaConfigRepairs extends LitElement {
|
||||
src=${brandsUrl({
|
||||
domain: issue.issue_domain || issue.domain,
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
.title=${domainName}
|
||||
|
||||
@@ -58,7 +58,6 @@ class IntegrationsStartupTime extends LitElement {
|
||||
src=${brandsUrl({
|
||||
domain: setup.domain,
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
crossorigin="anonymous"
|
||||
|
||||
@@ -8,6 +8,8 @@ import { ifDefined } from "lit/directives/if-defined";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { computeEntityNameList } from "../../../common/entity/compute_entity_name_display";
|
||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||
import "../../../components/ha-check-list-item";
|
||||
import "../../../components/search-input";
|
||||
import "../../../components/ha-dialog";
|
||||
@@ -18,10 +20,16 @@ import "../../../components/ha-list";
|
||||
import type { ExposeEntitySettings } from "../../../data/expose";
|
||||
import { voiceAssistants } from "../../../data/expose";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { loadVirtualizer } from "../../../resources/virtualizer";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import "./entity-voice-settings";
|
||||
import type { ExposeEntityDialogParams } from "./show-dialog-expose-entity";
|
||||
|
||||
interface FilteredEntity {
|
||||
entity: HassEntity;
|
||||
nameList: (string | undefined)[];
|
||||
}
|
||||
|
||||
@customElement("dialog-expose-entity")
|
||||
class DialogExposeEntity extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@@ -32,6 +40,12 @@ class DialogExposeEntity extends LitElement {
|
||||
|
||||
@state() private _selected: string[] = [];
|
||||
|
||||
public willUpdate(): void {
|
||||
if (!this.hasUpdated) {
|
||||
loadVirtualizer();
|
||||
}
|
||||
}
|
||||
|
||||
public async showDialog(params: ExposeEntityDialogParams): Promise<void> {
|
||||
this._params = params;
|
||||
}
|
||||
@@ -141,38 +155,101 @@ class DialogExposeEntity extends LitElement {
|
||||
(
|
||||
exposedEntities: Record<string, ExposeEntitySettings>,
|
||||
filter?: string
|
||||
) => {
|
||||
): FilteredEntity[] => {
|
||||
const lowerFilter = filter?.toLowerCase();
|
||||
return Object.values(this.hass.states).filter(
|
||||
(entity) =>
|
||||
this._params!.filterAssistants.some(
|
||||
(ass) => !exposedEntities[entity.entity_id]?.[ass]
|
||||
) &&
|
||||
(!lowerFilter ||
|
||||
entity.entity_id.toLowerCase().includes(lowerFilter) ||
|
||||
computeStateName(entity)?.toLowerCase().includes(lowerFilter))
|
||||
);
|
||||
const result: FilteredEntity[] = [];
|
||||
|
||||
for (const entity of Object.values(this.hass.states)) {
|
||||
if (
|
||||
this._params!.filterAssistants.every(
|
||||
(ass) => exposedEntities[entity.entity_id]?.[ass]
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const nameList = computeEntityNameList(
|
||||
entity,
|
||||
[{ type: "entity" }, { type: "device" }, { type: "area" }],
|
||||
this.hass.entities,
|
||||
this.hass.devices,
|
||||
this.hass.areas,
|
||||
this.hass.floors
|
||||
);
|
||||
|
||||
if (!lowerFilter) {
|
||||
result.push({ entity, nameList });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entity.entity_id.toLowerCase().includes(lowerFilter)) {
|
||||
result.push({ entity, nameList });
|
||||
continue;
|
||||
}
|
||||
|
||||
const entityName = computeStateName(entity);
|
||||
if (entityName?.toLowerCase().includes(lowerFilter)) {
|
||||
result.push({ entity, nameList });
|
||||
continue;
|
||||
}
|
||||
|
||||
const [, deviceName, areaName] = nameList;
|
||||
|
||||
if (deviceName?.toLowerCase().includes(lowerFilter)) {
|
||||
result.push({ entity, nameList });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (areaName?.toLowerCase().includes(lowerFilter)) {
|
||||
result.push({ entity, nameList });
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
);
|
||||
|
||||
private _renderItem = (entityState: HassEntity) => html`
|
||||
<ha-check-list-item
|
||||
graphic="icon"
|
||||
twoLine
|
||||
.value=${entityState.entity_id}
|
||||
.selected=${this._selected.includes(entityState.entity_id)}
|
||||
@request-selected=${this._handleSelected}
|
||||
>
|
||||
<ha-state-icon
|
||||
title=${ifDefined(entityState?.state)}
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${entityState}
|
||||
></ha-state-icon>
|
||||
${computeStateName(entityState)}
|
||||
<span slot="secondary">${entityState.entity_id}</span>
|
||||
</ha-check-list-item>
|
||||
`;
|
||||
private _renderItem = (item: FilteredEntity) => {
|
||||
const { entity: entityState, nameList } = item;
|
||||
const [entityName, deviceName, areaName] = nameList;
|
||||
|
||||
const isRTL = computeRTL(this.hass);
|
||||
const primary = entityName || deviceName || entityState.entity_id;
|
||||
const context = [areaName, entityName ? deviceName : undefined]
|
||||
.filter(Boolean)
|
||||
.join(isRTL ? " ◂ " : " ▸ ");
|
||||
const showEntityId = this.hass.userData?.showEntityIdPicker;
|
||||
|
||||
return html`
|
||||
<ha-check-list-item
|
||||
graphic="icon"
|
||||
?twoLine=${context}
|
||||
?threeLine=${showEntityId}
|
||||
.value=${entityState.entity_id}
|
||||
.selected=${this._selected.includes(entityState.entity_id)}
|
||||
@request-selected=${this._handleSelected}
|
||||
>
|
||||
<ha-state-icon
|
||||
title=${ifDefined(entityState?.state)}
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${entityState}
|
||||
></ha-state-icon>
|
||||
${primary}
|
||||
${context || showEntityId
|
||||
? html`<span slot="secondary">
|
||||
${context}
|
||||
${showEntityId
|
||||
? html`<br /><span class="entity-id"
|
||||
>${entityState.entity_id}</span
|
||||
>`
|
||||
: nothing}
|
||||
</span>`
|
||||
: nothing}
|
||||
</ha-check-list-item>
|
||||
`;
|
||||
};
|
||||
|
||||
private _expose() {
|
||||
this._params!.exposeEntities(this._selected);
|
||||
@@ -198,6 +275,7 @@ class DialogExposeEntity extends LitElement {
|
||||
width: 100%;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
margin-top: var(--ha-space-2);
|
||||
--text-field-suffix-padding-left: 8px;
|
||||
}
|
||||
.header {
|
||||
@@ -210,7 +288,7 @@ class DialogExposeEntity extends LitElement {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: -4px 0;
|
||||
margin: calc(var(--ha-space-1) * -1) 0;
|
||||
}
|
||||
.subtitle {
|
||||
color: var(--secondary-text-color);
|
||||
@@ -225,9 +303,17 @@ class DialogExposeEntity extends LitElement {
|
||||
width: 100%;
|
||||
height: 72px;
|
||||
}
|
||||
ha-check-list-item[threeLine] {
|
||||
height: 88px;
|
||||
}
|
||||
ha-check-list-item .entity-id {
|
||||
line-height: var(--ha-line-height-normal);
|
||||
padding-left: var(--ha-space-1);
|
||||
font-size: var(--ha-font-size-xs);
|
||||
}
|
||||
ha-check-list-item ha-state-icon {
|
||||
margin-left: 24px;
|
||||
margin-inline-start: 24px;
|
||||
margin-left: var(--ha-space-6);
|
||||
margin-inline-start: var(--ha-space-6);
|
||||
margin-inline-end: initial;
|
||||
}
|
||||
@media all and (max-height: 800px) {
|
||||
@@ -262,8 +348,8 @@ class DialogExposeEntity extends LitElement {
|
||||
--text-field-suffix-padding-left: unset;
|
||||
}
|
||||
ha-check-list-item ha-state-icon {
|
||||
margin-left: 8px;
|
||||
margin-inline-start: 8px;
|
||||
margin-left: var(--ha-space-2);
|
||||
margin-inline-start: var(--ha-space-2);
|
||||
margin-inline-end: initial;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,20 +167,21 @@ export class VoiceAssistantsExpose extends LitElement {
|
||||
filterable: true,
|
||||
direction: "asc",
|
||||
flex: 2,
|
||||
template: narrow
|
||||
? undefined
|
||||
: (entry) => html`
|
||||
${entry.name}<br />
|
||||
<div class="secondary">${entry.entity_id}</div>
|
||||
`,
|
||||
},
|
||||
// For search & narrow
|
||||
area: {
|
||||
title: localize("ui.panel.config.voice_assistants.expose.headers.area"),
|
||||
sortable: true,
|
||||
groupable: true,
|
||||
filterable: true,
|
||||
template: (entry) => entry.area || "—",
|
||||
},
|
||||
entity_id: {
|
||||
title: localize(
|
||||
"ui.panel.config.voice_assistants.expose.headers.entity_id"
|
||||
),
|
||||
hidden: !narrow,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
defaultHidden: true,
|
||||
},
|
||||
domain: {
|
||||
title: localize(
|
||||
@@ -191,13 +192,6 @@ export class VoiceAssistantsExpose extends LitElement {
|
||||
filterable: true,
|
||||
groupable: true,
|
||||
},
|
||||
area: {
|
||||
title: localize("ui.panel.config.voice_assistants.expose.headers.area"),
|
||||
sortable: true,
|
||||
groupable: true,
|
||||
filterable: true,
|
||||
template: (entry) => entry.area || "—",
|
||||
},
|
||||
assistants: {
|
||||
title: localize(
|
||||
"ui.panel.config.voice_assistants.expose.headers.assistants"
|
||||
@@ -819,8 +813,8 @@ export class VoiceAssistantsExpose extends LitElement {
|
||||
}
|
||||
.selected-txt {
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
padding-left: 16px;
|
||||
padding-inline-start: 16px;
|
||||
padding-left: var(--ha-space-4);
|
||||
padding-inline-start: var(--ha-space-4);
|
||||
direction: var(--direction);
|
||||
}
|
||||
.table-header .selected-txt {
|
||||
@@ -830,8 +824,8 @@ export class VoiceAssistantsExpose extends LitElement {
|
||||
font-size: var(--ha-font-size-l);
|
||||
}
|
||||
.header-toolbar .header-btns {
|
||||
margin-right: -12px;
|
||||
margin-inline-end: -12px;
|
||||
margin-right: calc(var(--ha-space-3) * -1);
|
||||
margin-inline-end: calc(var(--ha-space-3) * -1);
|
||||
direction: var(--direction);
|
||||
}
|
||||
.header-btns {
|
||||
@@ -839,17 +833,17 @@ export class VoiceAssistantsExpose extends LitElement {
|
||||
}
|
||||
.header-btns > ha-button,
|
||||
.header-btns > ha-icon-button {
|
||||
margin: 8px;
|
||||
margin: var(--ha-space-2);
|
||||
}
|
||||
ha-button-menu {
|
||||
margin-left: 8px;
|
||||
margin-inline-start: 8px;
|
||||
margin-left: var(--ha-space-2);
|
||||
margin-inline-start: var(--ha-space-2);
|
||||
margin-inline-end: initial;
|
||||
}
|
||||
.clear {
|
||||
color: var(--primary-color);
|
||||
padding-left: 8px;
|
||||
padding-inline-start: 8px;
|
||||
padding-left: var(--ha-space-2);
|
||||
padding-inline-start: var(--ha-space-2);
|
||||
text-transform: uppercase;
|
||||
direction: var(--direction);
|
||||
}
|
||||
|
||||
@@ -160,7 +160,6 @@ class HaLogbookRenderer extends LitElement {
|
||||
? brandsUrl({
|
||||
domain: domain!,
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
@@ -1,30 +1,55 @@
|
||||
import { getPanelTitleFromUrlPath } from "../data/panel";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { getConfigSubpageTitle, getPanelTitleFromUrlPath } from "../data/panel";
|
||||
import { configSections } from "../panels/config/ha-panel-config";
|
||||
import type { Constructor, HomeAssistant } from "../types";
|
||||
import type { HassBaseEl } from "./hass-base-mixin";
|
||||
|
||||
const setTitle = (title: string | undefined) => {
|
||||
const setPageTitle = (title: string | undefined) => {
|
||||
document.title = title ? `${title} – Home Assistant` : "Home Assistant";
|
||||
};
|
||||
|
||||
const getRoutePath = (): string =>
|
||||
// In demo mode, use hash; otherwise use pathname
|
||||
__DEMO__ ? window.location.hash.substring(1) : window.location.pathname;
|
||||
|
||||
export const panelTitleMixin = <T extends Constructor<HassBaseEl>>(
|
||||
superClass: T
|
||||
) =>
|
||||
class extends superClass {
|
||||
protected updated(changedProps) {
|
||||
private _previousPath?: string;
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
super.updated(changedProps);
|
||||
if (!changedProps.has("hass") || !this.hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
const currentPath = getRoutePath();
|
||||
|
||||
// Update title when panel, localize, or route path changes
|
||||
if (
|
||||
!oldHass ||
|
||||
oldHass.panels !== this.hass.panels ||
|
||||
oldHass.panelUrl !== this.hass.panelUrl ||
|
||||
oldHass.localize !== this.hass.localize
|
||||
oldHass.localize !== this.hass.localize ||
|
||||
this._previousPath !== currentPath
|
||||
) {
|
||||
setTitle(getPanelTitleFromUrlPath(this.hass, this.hass.panelUrl));
|
||||
this._previousPath = currentPath;
|
||||
|
||||
let title: string | undefined;
|
||||
|
||||
// Try to get specific subpage title for config panel
|
||||
if (this.hass.panelUrl === "config") {
|
||||
title = getConfigSubpageTitle(this.hass, currentPath, configSections);
|
||||
}
|
||||
|
||||
// Fall back to panel title
|
||||
if (!title) {
|
||||
title = getPanelTitleFromUrlPath(this.hass, this.hass.panelUrl);
|
||||
}
|
||||
|
||||
setPageTitle(title);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4162,6 +4162,7 @@
|
||||
"cut_to_clipboard": "Trigger cut to clipboard",
|
||||
"select": "Select a trigger",
|
||||
"no_items_for_target": "No triggers available for",
|
||||
"no_items_for_target_note": "This is a {labs_link} feature. More triggers will be added in future updates.",
|
||||
"groups": {
|
||||
"device": {
|
||||
"label": "Device"
|
||||
@@ -4433,6 +4434,7 @@
|
||||
"cut_to_clipboard": "Condition cut to clipboard",
|
||||
"select": "Select a condition",
|
||||
"no_items_for_target": "No conditions available for",
|
||||
"no_items_for_target_note": "This is a {labs_link} feature. More conditions will be added in future updates.",
|
||||
"groups": {
|
||||
"device": {
|
||||
"label": "Device"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
export interface BrandsOptions {
|
||||
domain: string;
|
||||
type: "icon" | "logo" | "icon@2x" | "logo@2x";
|
||||
useFallback?: boolean;
|
||||
darkOptimized?: boolean;
|
||||
brand?: boolean;
|
||||
}
|
||||
@@ -14,11 +13,9 @@ export interface HardwareBrandsOptions {
|
||||
}
|
||||
|
||||
export const brandsUrl = (options: BrandsOptions): string =>
|
||||
`https://brands.home-assistant.io/${options.brand ? "brands/" : ""}${
|
||||
options.useFallback ? "_/" : ""
|
||||
}${options.domain}/${options.darkOptimized ? "dark_" : ""}${
|
||||
options.type
|
||||
}.png`;
|
||||
`https://brands.home-assistant.io/${options.brand ? "brands/" : ""}_/${options.domain}/${
|
||||
options.darkOptimized ? "dark_" : ""
|
||||
}${options.type}.png`;
|
||||
|
||||
export const hardwareBrandsUrl = (options: HardwareBrandsOptions): string =>
|
||||
`https://brands.home-assistant.io/hardware/${options.category}/${
|
||||
|
||||
@@ -2,40 +2,26 @@ import { assert, describe, it } from "vitest";
|
||||
import { brandsUrl } from "../../src/util/brands-url";
|
||||
|
||||
describe("Generate brands Url", () => {
|
||||
it("Generate logo brands url for cloud component without fallback", () => {
|
||||
it("Generate logo brands url for cloud component", () => {
|
||||
assert.strictEqual(
|
||||
// @ts-ignore
|
||||
brandsUrl({ domain: "cloud", type: "logo" }),
|
||||
"https://brands.home-assistant.io/cloud/logo.png"
|
||||
);
|
||||
});
|
||||
it("Generate icon brands url for cloud component without fallback", () => {
|
||||
assert.strictEqual(
|
||||
// @ts-ignore
|
||||
brandsUrl({ domain: "cloud", type: "icon" }),
|
||||
"https://brands.home-assistant.io/cloud/icon.png"
|
||||
);
|
||||
});
|
||||
it("Generate logo brands url for cloud component with fallback", () => {
|
||||
assert.strictEqual(
|
||||
// @ts-ignore
|
||||
brandsUrl({ domain: "cloud", type: "logo", useFallback: true }),
|
||||
"https://brands.home-assistant.io/_/cloud/logo.png"
|
||||
);
|
||||
});
|
||||
it("Generate icon brands url for cloud component with fallback", () => {
|
||||
it("Generate icon brands url for cloud component", () => {
|
||||
assert.strictEqual(
|
||||
// @ts-ignore
|
||||
brandsUrl({ domain: "cloud", type: "icon", useFallback: true }),
|
||||
brandsUrl({ domain: "cloud", type: "icon" }),
|
||||
"https://brands.home-assistant.io/_/cloud/icon.png"
|
||||
);
|
||||
});
|
||||
|
||||
it("Generate dark theme optimized logo brands url for cloud component without fallback", () => {
|
||||
it("Generate dark theme optimized logo brands url for cloud component", () => {
|
||||
assert.strictEqual(
|
||||
// @ts-ignore
|
||||
brandsUrl({ domain: "cloud", type: "logo", darkOptimized: true }),
|
||||
"https://brands.home-assistant.io/cloud/dark_logo.png"
|
||||
"https://brands.home-assistant.io/_/cloud/dark_logo.png"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
140
yarn.lock
140
yarn.lock
@@ -2191,7 +2191,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.28, @jridgewell/trace-mapping@npm:^0.3.31":
|
||||
"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.28, @jridgewell/trace-mapping@npm:^0.3.31":
|
||||
version: 0.3.31
|
||||
resolution: "@jridgewell/trace-mapping@npm:0.3.31"
|
||||
dependencies:
|
||||
@@ -5192,50 +5192,49 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vitest/coverage-v8@npm:4.0.16":
|
||||
version: 4.0.16
|
||||
resolution: "@vitest/coverage-v8@npm:4.0.16"
|
||||
"@vitest/coverage-v8@npm:4.0.17":
|
||||
version: 4.0.17
|
||||
resolution: "@vitest/coverage-v8@npm:4.0.17"
|
||||
dependencies:
|
||||
"@bcoe/v8-coverage": "npm:^1.0.2"
|
||||
"@vitest/utils": "npm:4.0.16"
|
||||
ast-v8-to-istanbul: "npm:^0.3.8"
|
||||
"@vitest/utils": "npm:4.0.17"
|
||||
ast-v8-to-istanbul: "npm:^0.3.10"
|
||||
istanbul-lib-coverage: "npm:^3.2.2"
|
||||
istanbul-lib-report: "npm:^3.0.1"
|
||||
istanbul-lib-source-maps: "npm:^5.0.6"
|
||||
istanbul-reports: "npm:^3.2.0"
|
||||
magicast: "npm:^0.5.1"
|
||||
obug: "npm:^2.1.1"
|
||||
std-env: "npm:^3.10.0"
|
||||
tinyrainbow: "npm:^3.0.3"
|
||||
peerDependencies:
|
||||
"@vitest/browser": 4.0.16
|
||||
vitest: 4.0.16
|
||||
"@vitest/browser": 4.0.17
|
||||
vitest: 4.0.17
|
||||
peerDependenciesMeta:
|
||||
"@vitest/browser":
|
||||
optional: true
|
||||
checksum: 10/cfb0095db60baa7ec8f76d4a9d09a92f1d9f2bfe83adfbbfca32a9b1a6dd76447db33bbba2fd8882f551ca206ef86f74f6ed436643a036537ecea6398a6825f1
|
||||
checksum: 10/aab6340670dbf42a5bf4a28b49a4d4c8819e842edac45567bae50af27b9e89264406945e57dd115b833190a6c25ba8f716c2eabaa23d2e249a185e3acc97ec1a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vitest/expect@npm:4.0.16":
|
||||
version: 4.0.16
|
||||
resolution: "@vitest/expect@npm:4.0.16"
|
||||
"@vitest/expect@npm:4.0.17":
|
||||
version: 4.0.17
|
||||
resolution: "@vitest/expect@npm:4.0.17"
|
||||
dependencies:
|
||||
"@standard-schema/spec": "npm:^1.0.0"
|
||||
"@types/chai": "npm:^5.2.2"
|
||||
"@vitest/spy": "npm:4.0.16"
|
||||
"@vitest/utils": "npm:4.0.16"
|
||||
"@vitest/spy": "npm:4.0.17"
|
||||
"@vitest/utils": "npm:4.0.17"
|
||||
chai: "npm:^6.2.1"
|
||||
tinyrainbow: "npm:^3.0.3"
|
||||
checksum: 10/1da98c86d394a4955bef381ac2c63a52d2eec0086f55e18858083da928cfdf51e7a30bfd88b1814e861906dae44d089aeab0fcc67b2597a4a8073c70cd14bdf7
|
||||
checksum: 10/f260fefea527aae652be8d71ff188d45f958b7299a4577d1c3ed15bc87e6b20a6abb30ec6419c826259863d8bdbc1122e82cc499fb9eb63aaa43d3a5be1b7f76
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vitest/mocker@npm:4.0.16":
|
||||
version: 4.0.16
|
||||
resolution: "@vitest/mocker@npm:4.0.16"
|
||||
"@vitest/mocker@npm:4.0.17":
|
||||
version: 4.0.17
|
||||
resolution: "@vitest/mocker@npm:4.0.17"
|
||||
dependencies:
|
||||
"@vitest/spy": "npm:4.0.16"
|
||||
"@vitest/spy": "npm:4.0.17"
|
||||
estree-walker: "npm:^3.0.3"
|
||||
magic-string: "npm:^0.30.21"
|
||||
peerDependencies:
|
||||
@@ -5246,54 +5245,54 @@ __metadata:
|
||||
optional: true
|
||||
vite:
|
||||
optional: true
|
||||
checksum: 10/3a34c6571ef278b80d33feabb8389d6cf7cfd248fe592b8b2a373650ab460b95805fde65e6bd76aebc75729fc0c94b4d8b9bba25fa55e21c2745ae03c10316bf
|
||||
checksum: 10/4d938c298dd7e63d23efc56a81e254a8a453b0157b378d4b7af57a40dd2687c24a0e1f2e2499f8d17fe302e6d6d515e67c6a5fbfbff75dee2cfd51c37cf4c7dc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vitest/pretty-format@npm:4.0.16":
|
||||
version: 4.0.16
|
||||
resolution: "@vitest/pretty-format@npm:4.0.16"
|
||||
"@vitest/pretty-format@npm:4.0.17":
|
||||
version: 4.0.17
|
||||
resolution: "@vitest/pretty-format@npm:4.0.17"
|
||||
dependencies:
|
||||
tinyrainbow: "npm:^3.0.3"
|
||||
checksum: 10/914d5d35fb3b0aa67f8e6065ac3d1f1798b7774e1ad9d1e873e7c6efdc7925c98e0f8188bb13c4f3feb4d80b756c337f7a55cd4f78c50fe786330d0aaede7cfd
|
||||
checksum: 10/e50925f44168b8108a5094e44fd739b7183457c101eb020e88b5556a2f857808d0c9d045113aec83815a20d4aaaf9b7a522a1c651ce111de18daa686891b37a0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vitest/runner@npm:4.0.16":
|
||||
version: 4.0.16
|
||||
resolution: "@vitest/runner@npm:4.0.16"
|
||||
"@vitest/runner@npm:4.0.17":
|
||||
version: 4.0.17
|
||||
resolution: "@vitest/runner@npm:4.0.17"
|
||||
dependencies:
|
||||
"@vitest/utils": "npm:4.0.16"
|
||||
"@vitest/utils": "npm:4.0.17"
|
||||
pathe: "npm:^2.0.3"
|
||||
checksum: 10/2aed39bb46ba747bd4fd5acf081e9e500192fec19c1887399f6a1701bbfdab05f3d3b45c00e4af5b90a0832853c959a0f64e676b05c67f5457b7c6984f844aa2
|
||||
checksum: 10/75c62ac09b506d2707baad72c9a8ca6addb9bb179548d9ec9af3f7f2303b2e03f4001480c9657325718b15f2997fc39168c027d8d88794c0f8c04800c640c055
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vitest/snapshot@npm:4.0.16":
|
||||
version: 4.0.16
|
||||
resolution: "@vitest/snapshot@npm:4.0.16"
|
||||
"@vitest/snapshot@npm:4.0.17":
|
||||
version: 4.0.17
|
||||
resolution: "@vitest/snapshot@npm:4.0.17"
|
||||
dependencies:
|
||||
"@vitest/pretty-format": "npm:4.0.16"
|
||||
"@vitest/pretty-format": "npm:4.0.17"
|
||||
magic-string: "npm:^0.30.21"
|
||||
pathe: "npm:^2.0.3"
|
||||
checksum: 10/30f2977c96645c018b9d1f658e758f4f886ac63966dca909e9f736d6c9d6d0a6dabdeaedf9abcc13e1000458e4069283632c0140033972847dc1f4b4ac38e076
|
||||
checksum: 10/0cda8970f484bdc5777347cc317f020dc7773ddf0cea996ab5fff453966310c64e9a97854b04998cf0635e8118c12e2235c7a5f921fdfc288dc63dc27c3116d8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vitest/spy@npm:4.0.16":
|
||||
version: 4.0.16
|
||||
resolution: "@vitest/spy@npm:4.0.16"
|
||||
checksum: 10/76cbabfdd77adf16904d5c128de67abca650bbc2ed36acc68fca548dc51844c7fc1ac516e384d07341b25ae39318c7c2feb499ffa7283a1a838f762cb0cda6ab
|
||||
"@vitest/spy@npm:4.0.17":
|
||||
version: 4.0.17
|
||||
resolution: "@vitest/spy@npm:4.0.17"
|
||||
checksum: 10/23313980c512b00c08a1c64f6ed15dc7c295bb7b09feab571a3cc96536de2f07432109256717f9deb7f1b8c9ba9ac28f7e617cf639654bc564f6ea5a341ad8f4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vitest/utils@npm:4.0.16":
|
||||
version: 4.0.16
|
||||
resolution: "@vitest/utils@npm:4.0.16"
|
||||
"@vitest/utils@npm:4.0.17":
|
||||
version: 4.0.17
|
||||
resolution: "@vitest/utils@npm:4.0.17"
|
||||
dependencies:
|
||||
"@vitest/pretty-format": "npm:4.0.16"
|
||||
"@vitest/pretty-format": "npm:4.0.17"
|
||||
tinyrainbow: "npm:^3.0.3"
|
||||
checksum: 10/07fb3c96867656ff080df7ae6056a8dc23931d0f8bc16e15994c576c580dc6e2dcf71af0964fee197ea7eea4f4ad72c256f56cd3b81599f9e0ba63a228968d50
|
||||
checksum: 10/b8b96f8c2c4fee13f4ef4927e56bbf98c2d4f3a61428d9721c5578c96e2a0953892dfccfad3e0c1a7b3105e3d24f93f826f8338c82c72b9f8bc32b50bc9072a1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -5775,14 +5774,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ast-v8-to-istanbul@npm:^0.3.8":
|
||||
version: 0.3.9
|
||||
resolution: "ast-v8-to-istanbul@npm:0.3.9"
|
||||
"ast-v8-to-istanbul@npm:^0.3.10":
|
||||
version: 0.3.10
|
||||
resolution: "ast-v8-to-istanbul@npm:0.3.10"
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping": "npm:^0.3.31"
|
||||
estree-walker: "npm:^3.0.3"
|
||||
js-tokens: "npm:^9.0.1"
|
||||
checksum: 10/7fd175232b898e7dfa9c576dd765fb60cce3a057ea7ab108a64c745c6fd201cddc7a529e6ab7967b294627e63e920e5a0e72dbc2348aa8ed53030da7c61d93b6
|
||||
checksum: 10/240a5e2c24776b355f2442fa93564a528b8df4b8d94e9bc3234f25020ffac745886865a3a92e5e9dc67ee9720739ec078f04790a3607a7ad98d8349cf75ddf04
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -9091,7 +9090,7 @@ __metadata:
|
||||
"@types/ua-parser-js": "npm:0.7.39"
|
||||
"@types/webspeechapi": "npm:0.0.29"
|
||||
"@vibrant/color": "npm:4.0.0"
|
||||
"@vitest/coverage-v8": "npm:4.0.16"
|
||||
"@vitest/coverage-v8": "npm:4.0.17"
|
||||
"@vue/web-component-wrapper": "npm:1.3.0"
|
||||
"@webcomponents/scoped-custom-element-registry": "npm:0.0.10"
|
||||
"@webcomponents/webcomponentsjs": "npm:2.8.0"
|
||||
@@ -9176,7 +9175,7 @@ __metadata:
|
||||
typescript-eslint: "npm:8.52.0"
|
||||
ua-parser-js: "npm:2.0.7"
|
||||
vite-tsconfig-paths: "npm:6.0.4"
|
||||
vitest: "npm:4.0.16"
|
||||
vitest: "npm:4.0.17"
|
||||
vue: "npm:2.7.16"
|
||||
vue2-daterange-picker: "npm:0.6.8"
|
||||
webpack-stats-plugin: "npm:1.1.3"
|
||||
@@ -10113,17 +10112,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"istanbul-lib-source-maps@npm:^5.0.6":
|
||||
version: 5.0.6
|
||||
resolution: "istanbul-lib-source-maps@npm:5.0.6"
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping": "npm:^0.3.23"
|
||||
debug: "npm:^4.1.1"
|
||||
istanbul-lib-coverage: "npm:^3.0.0"
|
||||
checksum: 10/569dd0a392ee3464b1fe1accbaef5cc26de3479eacb5b91d8c67ebb7b425d39fd02247d85649c3a0e9c29b600809fa60b5af5a281a75a89c01f385b1e24823a2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"istanbul-reports@npm:^3.2.0":
|
||||
version: 3.2.0
|
||||
resolution: "istanbul-reports@npm:3.2.0"
|
||||
@@ -14498,17 +14486,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vitest@npm:4.0.16":
|
||||
version: 4.0.16
|
||||
resolution: "vitest@npm:4.0.16"
|
||||
"vitest@npm:4.0.17":
|
||||
version: 4.0.17
|
||||
resolution: "vitest@npm:4.0.17"
|
||||
dependencies:
|
||||
"@vitest/expect": "npm:4.0.16"
|
||||
"@vitest/mocker": "npm:4.0.16"
|
||||
"@vitest/pretty-format": "npm:4.0.16"
|
||||
"@vitest/runner": "npm:4.0.16"
|
||||
"@vitest/snapshot": "npm:4.0.16"
|
||||
"@vitest/spy": "npm:4.0.16"
|
||||
"@vitest/utils": "npm:4.0.16"
|
||||
"@vitest/expect": "npm:4.0.17"
|
||||
"@vitest/mocker": "npm:4.0.17"
|
||||
"@vitest/pretty-format": "npm:4.0.17"
|
||||
"@vitest/runner": "npm:4.0.17"
|
||||
"@vitest/snapshot": "npm:4.0.17"
|
||||
"@vitest/spy": "npm:4.0.17"
|
||||
"@vitest/utils": "npm:4.0.17"
|
||||
es-module-lexer: "npm:^1.7.0"
|
||||
expect-type: "npm:^1.2.2"
|
||||
magic-string: "npm:^0.30.21"
|
||||
@@ -14526,10 +14514,10 @@ __metadata:
|
||||
"@edge-runtime/vm": "*"
|
||||
"@opentelemetry/api": ^1.9.0
|
||||
"@types/node": ^20.0.0 || ^22.0.0 || >=24.0.0
|
||||
"@vitest/browser-playwright": 4.0.16
|
||||
"@vitest/browser-preview": 4.0.16
|
||||
"@vitest/browser-webdriverio": 4.0.16
|
||||
"@vitest/ui": 4.0.16
|
||||
"@vitest/browser-playwright": 4.0.17
|
||||
"@vitest/browser-preview": 4.0.17
|
||||
"@vitest/browser-webdriverio": 4.0.17
|
||||
"@vitest/ui": 4.0.17
|
||||
happy-dom: "*"
|
||||
jsdom: "*"
|
||||
peerDependenciesMeta:
|
||||
@@ -14553,7 +14541,7 @@ __metadata:
|
||||
optional: true
|
||||
bin:
|
||||
vitest: vitest.mjs
|
||||
checksum: 10/22b3806988ab186be4a6a133903a70c62835198e8e749f6ed751957d23bc1e3f0466e310a1a79d0b70a354b2e308e574486191eb39711257b3fe61e4fe00d1c8
|
||||
checksum: 10/792cf5ecdb2c0c2a61fc7beacec800413dcc5b68ad5e18f74795cdbfe513d58e3b6e437571c728c9992920f52d0640a5264aaf8c3702454b2637ff93451cf567
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user