diff --git a/build-scripts/gulp/compress.js b/build-scripts/gulp/compress.js
index 1a6032c35c..fa171c9154 100644
--- a/build-scripts/gulp/compress.js
+++ b/build-scripts/gulp/compress.js
@@ -15,23 +15,29 @@ const brotliOptions = {
};
const zopfliOptions = { threshold: 150 };
-const compressDistBrotli = (rootDir, modernDir) =>
+const compressDistBrotli = (rootDir, modernDir, compressServiceWorker = true) =>
gulp
- .src([`${modernDir}/**/${filesGlob}`, `${rootDir}/sw-modern.js`], {
- base: rootDir,
- })
+ .src(
+ [
+ `${modernDir}/**/${filesGlob}`,
+ compressServiceWorker ? `${rootDir}/sw-modern.js` : undefined,
+ ].filter(Boolean),
+ {
+ base: rootDir,
+ }
+ )
.pipe(brotli(brotliOptions))
.pipe(gulp.dest(rootDir));
-const compressDistZopfli = (rootDir, modernDir) =>
+const compressDistZopfli = (rootDir, modernDir, compressModern = false) =>
gulp
.src(
[
`${rootDir}/**/${filesGlob}`,
- `!${modernDir}/**/${filesGlob}`,
+ compressModern ? undefined : `!${modernDir}/**/${filesGlob}`,
`!${rootDir}/{sw-modern,service_worker}.js`,
`${rootDir}/{authorize,onboarding}.html`,
- ],
+ ].filter(Boolean),
{ base: rootDir }
)
.pipe(zopfli(zopfliOptions))
@@ -40,12 +46,20 @@ const compressDistZopfli = (rootDir, modernDir) =>
const compressAppBrotli = () =>
compressDistBrotli(paths.app_output_root, paths.app_output_latest);
const compressHassioBrotli = () =>
- compressDistBrotli(paths.hassio_output_root, paths.hassio_output_latest);
+ compressDistBrotli(
+ paths.hassio_output_root,
+ paths.hassio_output_latest,
+ false
+ );
const compressAppZopfli = () =>
compressDistZopfli(paths.app_output_root, paths.app_output_latest);
const compressHassioZopfli = () =>
- compressDistZopfli(paths.hassio_output_root, paths.hassio_output_latest);
+ compressDistZopfli(
+ paths.hassio_output_root,
+ paths.hassio_output_latest,
+ true
+ );
gulp.task("compress-app", gulp.parallel(compressAppBrotli, compressAppZopfli));
gulp.task(
diff --git a/package.json b/package.json
index a9e4c71f01..7d0cd25dbe 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
"@babel/runtime": "7.25.6",
"@braintree/sanitize-url": "7.1.0",
"@codemirror/autocomplete": "6.18.0",
- "@codemirror/commands": "6.6.0",
+ "@codemirror/commands": "6.6.1",
"@codemirror/language": "6.10.2",
"@codemirror/legacy-modes": "6.4.1",
"@codemirror/search": "6.5.6",
diff --git a/pyproject.toml b/pyproject.toml
index 8b0f85c495..8fc228d388 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
-version = "20240903.1"
+version = "20240904.0"
license = {text = "Apache-2.0"}
description = "The Home Assistant frontend"
readme = "README.md"
diff --git a/src/components/data-table/ha-data-table.ts b/src/components/data-table/ha-data-table.ts
index 60faa4dcaf..77550488c9 100644
--- a/src/components/data-table/ha-data-table.ts
+++ b/src/components/data-table/ha-data-table.ts
@@ -25,7 +25,6 @@ import { fireEvent } from "../../common/dom/fire_event";
import { stringCompare } from "../../common/string/compare";
import { debounce } from "../../common/util/debounce";
import { groupBy } from "../../common/util/group-by";
-import { nextRender } from "../../common/util/render-status";
import { haStyleScrollbar } from "../../resources/styles";
import { loadVirtualizer } from "../../resources/virtualizer";
import { HomeAssistant } from "../../types";
@@ -35,6 +34,7 @@ import "../ha-svg-icon";
import "../search-input";
import { filterData, sortData } from "./sort-filter";
import { LocalizeFunc } from "../../common/translations/localize";
+import { nextRender } from "../../common/util/render-status";
export interface RowClickedEvent {
id: string;
@@ -169,8 +169,6 @@ export class HaDataTable extends LitElement {
@query("slot[name='header']") private _header!: HTMLSlotElement;
- @state() private _items: DataTableRowData[] = [];
-
@state() private _collapsedGroups: string[] = [];
private _checkableRowsCount?: number;
@@ -179,7 +177,9 @@ export class HaDataTable extends LitElement {
private _sortColumns: SortableColumnContainer = {};
- private curRequest = 0;
+ private _curRequest = 0;
+
+ private _lastUpdate = 0;
// @ts-ignore
@restoreScroll(".scroller") private _savedScrollPos?: number;
@@ -206,9 +206,9 @@ export class HaDataTable extends LitElement {
public connectedCallback() {
super.connectedCallback();
- if (this._items.length) {
+ if (this._filteredData.length) {
// Force update of location of rows
- this._items = [...this._items];
+ this._filteredData = [...this._filteredData];
}
}
@@ -291,16 +291,13 @@ export class HaDataTable extends LitElement {
properties.has("columns") ||
properties.has("_filter") ||
properties.has("sortColumn") ||
- properties.has("sortDirection") ||
- properties.has("groupColumn") ||
- properties.has("groupOrder") ||
- properties.has("_collapsedGroups")
+ properties.has("sortDirection")
) {
this._sortFilterData();
}
if (properties.has("selectable") || properties.has("hiddenColumns")) {
- this._items = [...this._items];
+ this._filteredData = [...this._filteredData];
}
}
@@ -467,7 +464,15 @@ export class HaDataTable extends LitElement {
scroller
class="mdc-data-table__content scroller ha-scrollbar"
@scroll=${this._saveScrollPos}
- .items=${this._items}
+ .items=${this._groupData(
+ this._filteredData,
+ localize,
+ this.appendRow,
+ this.hasFab,
+ this.groupColumn,
+ this.groupOrder,
+ this._collapsedGroups
+ )}
.keyFunction=${this._keyFunction}
.renderItem=${renderRow}
>
@@ -602,8 +607,13 @@ export class HaDataTable extends LitElement {
private async _sortFilterData() {
const startTime = new Date().getTime();
- this.curRequest++;
- const curRequest = this.curRequest;
+ const timeBetweenUpdate = startTime - this._lastUpdate;
+ const timeBetweenRequest = startTime - this._curRequest;
+ this._curRequest = startTime;
+
+ const forceUpdate =
+ !this._lastUpdate ||
+ (timeBetweenUpdate > 500 && timeBetweenRequest < 500);
let filteredData = this.data;
if (this._filter) {
@@ -614,6 +624,10 @@ export class HaDataTable extends LitElement {
);
}
+ if (!forceUpdate && this._curRequest !== startTime) {
+ return;
+ }
+
const prom = this.sortColumn
? sortData(
filteredData,
@@ -634,91 +648,103 @@ export class HaDataTable extends LitElement {
setTimeout(resolve, 100 - elapsed);
});
}
- if (this.curRequest !== curRequest) {
+
+ if (!forceUpdate && this._curRequest !== startTime) {
return;
}
- const localize = this.localizeFunc || this.hass.localize;
-
- if (this.appendRow || this.hasFab || this.groupColumn) {
- let items = [...data];
-
- if (this.groupColumn) {
- const grouped = groupBy(items, (item) => item[this.groupColumn!]);
- if (grouped.undefined) {
- // make sure ungrouped items are at the bottom
- grouped[UNDEFINED_GROUP_KEY] = grouped.undefined;
- delete grouped.undefined;
- }
- const sorted: {
- [key: string]: DataTableRowData[];
- } = Object.keys(grouped)
- .sort((a, b) => {
- const orderA = this.groupOrder?.indexOf(a) ?? -1;
- const orderB = this.groupOrder?.indexOf(b) ?? -1;
- if (orderA !== orderB) {
- if (orderA === -1) {
- return 1;
- }
- if (orderB === -1) {
- return -1;
- }
- return orderA - orderB;
- }
- return stringCompare(
- ["", "-", "—"].includes(a) ? "zzz" : a,
- ["", "-", "—"].includes(b) ? "zzz" : b,
- this.hass.locale.language
- );
- })
- .reduce((obj, key) => {
- obj[key] = grouped[key];
- return obj;
- }, {});
- const groupedItems: DataTableRowData[] = [];
- Object.entries(sorted).forEach(([groupName, rows]) => {
- groupedItems.push({
- append: true,
- content: html`
`,
- });
- if (!this._collapsedGroups.includes(groupName)) {
- groupedItems.push(...rows);
- }
- });
- items = groupedItems;
- }
-
- if (this.appendRow) {
- items.push({ append: true, content: this.appendRow });
- }
-
- if (this.hasFab) {
- items.push({ empty: true });
- }
-
- this._items = items;
- } else {
- this._items = data;
- }
+ this._lastUpdate = startTime;
this._filteredData = data;
}
+ private _groupData = memoizeOne(
+ (
+ data: DataTableRowData[],
+ localize: LocalizeFunc,
+ appendRow,
+ hasFab: boolean,
+ groupColumn: string | undefined,
+ groupOrder: string[] | undefined,
+ collapsedGroups: string[]
+ ) => {
+ if (appendRow || hasFab || groupColumn) {
+ let items = [...data];
+
+ if (groupColumn) {
+ const grouped = groupBy(items, (item) => item[groupColumn]);
+ if (grouped.undefined) {
+ // make sure ungrouped items are at the bottom
+ grouped[UNDEFINED_GROUP_KEY] = grouped.undefined;
+ delete grouped.undefined;
+ }
+ const sorted: {
+ [key: string]: DataTableRowData[];
+ } = Object.keys(grouped)
+ .sort((a, b) => {
+ const orderA = groupOrder?.indexOf(a) ?? -1;
+ const orderB = groupOrder?.indexOf(b) ?? -1;
+ if (orderA !== orderB) {
+ if (orderA === -1) {
+ return 1;
+ }
+ if (orderB === -1) {
+ return -1;
+ }
+ return orderA - orderB;
+ }
+ return stringCompare(
+ ["", "-", "—"].includes(a) ? "zzz" : a,
+ ["", "-", "—"].includes(b) ? "zzz" : b,
+ this.hass.locale.language
+ );
+ })
+ .reduce((obj, key) => {
+ obj[key] = grouped[key];
+ return obj;
+ }, {});
+ const groupedItems: DataTableRowData[] = [];
+ Object.entries(sorted).forEach(([groupName, rows]) => {
+ groupedItems.push({
+ append: true,
+ content: html``,
+ });
+ if (!collapsedGroups.includes(groupName)) {
+ groupedItems.push(...rows);
+ }
+ });
+ items = groupedItems;
+ }
+
+ if (appendRow) {
+ items.push({ append: true, content: appendRow });
+ }
+
+ if (hasFab) {
+ items.push({ empty: true });
+ }
+
+ return items;
+ }
+ return data;
+ }
+ );
+
private _memFilterData = memoizeOne(
(
data: DataTableRowData[],
@@ -802,8 +828,8 @@ export class HaDataTable extends LitElement {
private _checkedRowsChanged() {
// force scroller to update, change it's items
- if (this._items.length) {
- this._items = [...this._items];
+ if (this._filteredData.length) {
+ this._filteredData = [...this._filteredData];
}
fireEvent(this, "selection-changed", {
value: this._checkedRows,
diff --git a/src/components/ha-badge.ts b/src/components/ha-badge.ts
new file mode 100644
index 0000000000..66a56b63fc
--- /dev/null
+++ b/src/components/ha-badge.ts
@@ -0,0 +1,155 @@
+import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
+import { customElement, property } from "lit/decorators";
+import { classMap } from "lit/directives/class-map";
+import { ifDefined } from "lit/directives/if-defined";
+import "./ha-ripple";
+
+type BadgeType = "badge" | "button";
+
+@customElement("ha-badge")
+export class HaBadge extends LitElement {
+ @property() public type: BadgeType = "badge";
+
+ @property() public label?: string;
+
+ @property({ type: Boolean, attribute: "icon-only" }) iconOnly = false;
+
+ protected render() {
+ const label = this.label;
+
+ return html`
+
+
+
+ ${this.iconOnly
+ ? nothing
+ : html`
+ ${label ? html`${label}` : nothing}
+
+ `}
+
+ `;
+ }
+
+ static get styles(): CSSResultGroup {
+ return css`
+ :host {
+ --badge-color: var(--secondary-text-color);
+ -webkit-tap-highlight-color: transparent;
+ }
+ .badge {
+ position: relative;
+ --ha-ripple-color: var(--badge-color);
+ --ha-ripple-hover-opacity: 0.04;
+ --ha-ripple-pressed-opacity: 0.12;
+ transition:
+ box-shadow 180ms ease-in-out,
+ border-color 180ms ease-in-out;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ height: var(--ha-badge-size, 36px);
+ min-width: var(--ha-badge-size, 36px);
+ padding: 0px 12px;
+ box-sizing: border-box;
+ width: auto;
+ border-radius: var(
+ --ha-badge-border-radius,
+ calc(var(--ha-badge-size, 36px) / 2)
+ );
+ background: var(
+ --ha-card-background,
+ var(--card-background-color, white)
+ );
+ -webkit-backdrop-filter: var(--ha-card-backdrop-filter, none);
+ backdrop-filter: var(--ha-card-backdrop-filter, none);
+ border-width: var(--ha-card-border-width, 1px);
+ box-shadow: var(--ha-card-box-shadow, none);
+ border-style: solid;
+ border-color: var(
+ --ha-card-border-color,
+ var(--divider-color, #e0e0e0)
+ );
+ }
+ .badge:focus-visible {
+ --shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent);
+ --shadow-focus: 0 0 0 1px var(--badge-color);
+ border-color: var(--badge-color);
+ box-shadow: var(--shadow-default), var(--shadow-focus);
+ }
+ [role="button"] {
+ cursor: pointer;
+ }
+ [role="button"]:focus {
+ outline: none;
+ }
+ .info {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ padding-inline-start: initial;
+ text-align: center;
+ font-family: Roboto;
+ }
+ .label {
+ font-size: 10px;
+ font-style: normal;
+ font-weight: 500;
+ line-height: 10px;
+ letter-spacing: 0.1px;
+ color: var(--secondary-text-color);
+ }
+ .content {
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 500;
+ line-height: 16px;
+ letter-spacing: 0.1px;
+ color: var(--primary-text-color);
+ }
+ ::slotted([slot="icon"]) {
+ --mdc-icon-size: 18px;
+ color: var(--badge-color);
+ line-height: 0;
+ margin-left: -4px;
+ margin-right: 0;
+ margin-inline-start: -4px;
+ margin-inline-end: 0;
+ }
+ ::slotted(img[slot="icon"]) {
+ width: 30px;
+ height: 30px;
+ border-radius: 50%;
+ object-fit: cover;
+ overflow: hidden;
+ margin-left: -10px;
+ margin-right: 0;
+ margin-inline-start: -10px;
+ margin-inline-end: 0;
+ }
+ .badge.icon-only {
+ padding: 0;
+ }
+ .badge.icon-only ::slotted([slot="icon"]) {
+ margin-left: 0;
+ margin-right: 0;
+ margin-inline-start: 0;
+ margin-inline-end: 0;
+ }
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-badge": HaBadge;
+ }
+}
diff --git a/src/panels/lovelace/badges/hui-entity-badge.ts b/src/panels/lovelace/badges/hui-entity-badge.ts
index 39aa9a193f..5bf3b26d13 100644
--- a/src/panels/lovelace/badges/hui-entity-badge.ts
+++ b/src/panels/lovelace/badges/hui-entity-badge.ts
@@ -3,7 +3,6 @@ import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
-import { ifDefined } from "lit/directives/if-defined";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
import { computeCssColor } from "../../../common/color/compute-color";
@@ -12,6 +11,7 @@ import { computeDomain } from "../../../common/entity/compute_domain";
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
import { stateActive } from "../../../common/entity/state_active";
import { stateColorCss } from "../../../common/entity/state_color";
+import "../../../components/ha-badge";
import "../../../components/ha-ripple";
import "../../../components/ha-state-icon";
import "../../../components/ha-svg-icon";
@@ -160,15 +160,14 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
if (!stateObj) {
return html`
-
-
-
- ${entityId}
-
- ${this.hass.localize("ui.badge.entity.not_found")}
-
-
-
+
+
+ ${this.hass.localize("ui.badge.entity.not_found")}
+
`;
}
@@ -204,42 +203,32 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
const content = showState ? stateDisplay : showName ? name : undefined;
return html`
-
-
${showIcon
? imageUrl
- ? html`

`
+ ? html`

`
: html`
`
: nothing}
- ${content
- ? html`
-
- ${label ? html`${name}` : nothing}
- ${content}
-
- `
- : nothing}
-
+ ${content}
+
`;
}
@@ -249,119 +238,15 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
static get styles(): CSSResultGroup {
return css`
- :host {
+ ha-badge {
--badge-color: var(--state-inactive-color);
- -webkit-tap-highlight-color: transparent;
}
- .badge.error {
+ ha-badge.error {
--badge-color: var(--red-color);
}
- .badge {
- position: relative;
- --ha-ripple-color: var(--badge-color);
- --ha-ripple-hover-opacity: 0.04;
- --ha-ripple-pressed-opacity: 0.12;
- transition:
- box-shadow 180ms ease-in-out,
- border-color 180ms ease-in-out;
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: center;
- gap: 8px;
- height: var(--ha-badge-size, 36px);
- min-width: var(--ha-badge-size, 36px);
- padding: 0px 8px;
- box-sizing: border-box;
- width: auto;
- border-radius: var(
- --ha-badge-border-radius,
- calc(var(--ha-badge-size, 36px) / 2)
- );
- background: var(
- --ha-card-background,
- var(--card-background-color, white)
- );
- -webkit-backdrop-filter: var(--ha-card-backdrop-filter, none);
- backdrop-filter: var(--ha-card-backdrop-filter, none);
- border-width: var(--ha-card-border-width, 1px);
- box-shadow: var(--ha-card-box-shadow, none);
- border-style: solid;
- border-color: var(
- --ha-card-border-color,
- var(--divider-color, #e0e0e0)
- );
- --mdc-icon-size: 18px;
- text-align: center;
- font-family: Roboto;
- }
- .badge:focus-visible {
- --shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent);
- --shadow-focus: 0 0 0 1px var(--badge-color);
- border-color: var(--badge-color);
- box-shadow: var(--shadow-default), var(--shadow-focus);
- }
- button,
- [role="button"] {
- cursor: pointer;
- }
- button:focus,
- [role="button"]:focus {
- outline: none;
- }
- .badge.active {
+ ha-badge.active {
--badge-color: var(--primary-color);
}
- .info {
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- padding-right: 4px;
- padding-inline-end: 4px;
- padding-inline-start: initial;
- }
- .label {
- font-size: 10px;
- font-style: normal;
- font-weight: 500;
- line-height: 10px;
- letter-spacing: 0.1px;
- color: var(--secondary-text-color);
- }
- .content {
- font-size: 12px;
- font-style: normal;
- font-weight: 500;
- line-height: 16px;
- letter-spacing: 0.1px;
- color: var(--primary-text-color);
- }
- ha-state-icon,
- ha-svg-icon {
- color: var(--badge-color);
- line-height: 0;
- }
- img {
- width: 30px;
- height: 30px;
- border-radius: 50%;
- object-fit: cover;
- overflow: hidden;
- }
- .badge.no-info {
- padding: 0;
- }
- .badge:not(.no-icon):not(.no-info) img {
- margin-left: -6px;
- margin-inline-start: -6px;
- margin-inline-end: initial;
- }
- .badge.no-icon .info {
- padding-right: 4px;
- padding-left: 4px;
- padding-inline-end: 4px;
- padding-inline-start: 4px;
- }
`;
}
}
diff --git a/src/panels/lovelace/badges/hui-error-badge.ts b/src/panels/lovelace/badges/hui-error-badge.ts
index 19c6e95826..1df21d7d04 100644
--- a/src/panels/lovelace/badges/hui-error-badge.ts
+++ b/src/panels/lovelace/badges/hui-error-badge.ts
@@ -2,12 +2,11 @@ import { mdiAlertCircle } from "@mdi/js";
import { dump } from "js-yaml";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, state } from "lit/decorators";
-import "../../../components/ha-label-badge";
+import "../../../components/ha-badge";
import "../../../components/ha-svg-icon";
import { HomeAssistant } from "../../../types";
import { showAlertDialog } from "../custom-card-helpers";
import { LovelaceBadge } from "../types";
-import { HuiEntityBadge } from "./hui-entity-badge";
import { ErrorBadgeConfig } from "./types";
export const createErrorBadgeElement = (config) => {
@@ -55,41 +54,36 @@ export class HuiErrorBadge extends LitElement implements LovelaceBadge {
}
return html`
-
+
+
+ ${this._config.error}
+
`;
}
static get styles(): CSSResultGroup {
- return [
- HuiEntityBadge.styles,
- css`
- .badge.error {
- --badge-color: var(--error-color);
- border-color: var(--badge-color);
- }
- ha-svg-icon {
- color: var(--badge-color);
- }
- .state {
- max-width: 100px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- pre {
- font-family: var(--code-font-family, monospace);
- white-space: break-spaces;
- user-select: text;
- }
- `,
- ];
+ return css`
+ ha-badge {
+ --badge-color: var(--error-color);
+ --ha-card-border-color: var(--error-color);
+ }
+ .content {
+ max-width: 100px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ pre {
+ font-family: var(--code-font-family, monospace);
+ white-space: break-spaces;
+ user-select: text;
+ }
+ `;
}
}
diff --git a/yarn.lock b/yarn.lock
index ca63111cf0..d190635684 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1458,15 +1458,15 @@ __metadata:
languageName: node
linkType: hard
-"@codemirror/commands@npm:6.6.0":
- version: 6.6.0
- resolution: "@codemirror/commands@npm:6.6.0"
+"@codemirror/commands@npm:6.6.1":
+ version: 6.6.1
+ resolution: "@codemirror/commands@npm:6.6.1"
dependencies:
"@codemirror/language": "npm:^6.0.0"
"@codemirror/state": "npm:^6.4.0"
"@codemirror/view": "npm:^6.27.0"
"@lezer/common": "npm:^1.1.0"
- checksum: 10/e984511b8ac06a1afe01005a50af7a0b6765cf350547d281a13aedbcccbc8620187c6dce55caa0f7c8ba409771e4abb4aa3bae824b6870249c8176b57cc4a42e
+ checksum: 10/533f33db08a4ff7e400ff61e48d34525afdc894f92442a5bf469ed24b1992a61bf726b61647e2faa552a2f4703c149c92026d9a1f9169f615582cdfc36fcb010
languageName: node
linkType: hard
@@ -8922,7 +8922,7 @@ __metadata:
"@braintree/sanitize-url": "npm:7.1.0"
"@bundle-stats/plugin-webpack-filter": "npm:4.15.0"
"@codemirror/autocomplete": "npm:6.18.0"
- "@codemirror/commands": "npm:6.6.0"
+ "@codemirror/commands": "npm:6.6.1"
"@codemirror/language": "npm:6.10.2"
"@codemirror/legacy-modes": "npm:6.4.1"
"@codemirror/search": "npm:6.5.6"