diff --git a/build-scripts/env.js b/build-scripts/env.js index 101858a367..9f10fc5838 100644 --- a/build-scripts/env.js +++ b/build-scripts/env.js @@ -1,6 +1,14 @@ module.exports = { - isProdBuild: process.env.NODE_ENV === "production", - isStatsBuild: process.env.STATS === "1", - isTravis: process.env.TRAVIS === "true", - isNetlify: process.env.NETLIFY === "true", + isProdBuild() { + return process.env.NODE_ENV === "production"; + }, + isStatsBuild() { + return process.env.STATS === "1"; + }, + isTravis() { + return process.env.TRAVIS === "true"; + }, + isNetlify() { + return process.env.NETLIFY === "true"; + }, }; diff --git a/build-scripts/gulp/app.js b/build-scripts/gulp/app.js index 9fdf96cc04..60540172f8 100644 --- a/build-scripts/gulp/app.js +++ b/build-scripts/gulp/app.js @@ -42,7 +42,7 @@ gulp.task( "copy-static", "webpack-prod-app", ...// Don't compress running tests - (envVars.isTravis ? [] : ["compress-app"]), + (envVars.isTravis() ? [] : ["compress-app"]), gulp.parallel( "gen-pages-prod", "gen-index-app-prod", diff --git a/build-scripts/gulp/hassio.js b/build-scripts/gulp/hassio.js index 164a5b9deb..01f165bc29 100644 --- a/build-scripts/gulp/hassio.js +++ b/build-scripts/gulp/hassio.js @@ -29,6 +29,6 @@ gulp.task( gulp.parallel("gen-icons-hassio", "gen-icons-mdi"), "webpack-prod-hassio", ...// Don't compress running tests - (envVars.isTravis ? [] : ["compress-hassio"]) + (envVars.isTravis() ? [] : ["compress-hassio"]) ) ); diff --git a/build-scripts/gulp/translations.js b/build-scripts/gulp/translations.js index 4b0d203448..ded39269b3 100755 --- a/build-scripts/gulp/translations.js +++ b/build-scripts/gulp/translations.js @@ -292,10 +292,11 @@ gulp.task( function fingerprintTranslationFiles() { // Fingerprint full file of each language const files = fs.readdirSync(fullDir); + for (let i = 0; i < files.length; i++) { fingerprints[files[i].split(".")[0]] = { // In dev we create fake hashes - hash: env.isProdBuild + hash: env.isProdBuild() ? crypto .createHash("md5") .update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8")) @@ -332,7 +333,7 @@ gulp.task( gulp.series( "clean-translations", "ensure-translations-build-dir", - env.isProdBuild ? (done) => done() : "create-test-translation", + env.isProdBuild() ? (done) => done() : "create-test-translation", "build-master-translation", "build-merged-translations", gulp.parallel(...splitTasks), diff --git a/build-scripts/webpack.js b/build-scripts/webpack.js index 88ad6ac1dc..7e86af6e39 100644 --- a/build-scripts/webpack.js +++ b/build-scripts/webpack.js @@ -155,7 +155,7 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => { `/static/translations/${englishFilename}` ] = `build-translations/output/${englishFilename}`; - Object.keys(translationMetadata.fragments).forEach((fragment) => { + translationMetadata.fragments.forEach((fragment) => { workBoxTranslationsTemplatedURLs[ `/static/translations/${fragment}/${englishFilename}` ] = `build-translations/output/${fragment}/${englishFilename}`; diff --git a/cast/webpack.config.js b/cast/webpack.config.js index bb9746cd11..fe65fcd4ac 100644 --- a/cast/webpack.config.js +++ b/cast/webpack.config.js @@ -6,6 +6,6 @@ const { isProdBuild } = require("../build-scripts/env.js"); const latestBuild = true; module.exports = createCastConfig({ - isProdBuild, + isProdBuild: isProdBuild(), latestBuild, }); diff --git a/demo/public/stub_config/bedroom.png b/demo/public/stub_config/bedroom.png new file mode 100644 index 0000000000..2d1e549fa1 Binary files /dev/null and b/demo/public/stub_config/bedroom.png differ diff --git a/demo/public/stub_config/floorplan.png b/demo/public/stub_config/floorplan.png new file mode 100644 index 0000000000..859842ea44 Binary files /dev/null and b/demo/public/stub_config/floorplan.png differ diff --git a/demo/public/stub_config/kitchen.png b/demo/public/stub_config/kitchen.png new file mode 100644 index 0000000000..5be0f46e98 Binary files /dev/null and b/demo/public/stub_config/kitchen.png differ diff --git a/demo/public/stub_config/t-shirt-promo.png b/demo/public/stub_config/t-shirt-promo.png new file mode 100644 index 0000000000..84ae9d5386 Binary files /dev/null and b/demo/public/stub_config/t-shirt-promo.png differ diff --git a/demo/webpack.config.js b/demo/webpack.config.js index 9ccb790f8b..a297932311 100644 --- a/demo/webpack.config.js +++ b/demo/webpack.config.js @@ -6,7 +6,7 @@ const { isProdBuild, isStatsBuild } = require("../build-scripts/env.js"); const latestBuild = true; module.exports = createDemoConfig({ - isProdBuild, - isStatsBuild, + isProdBuild: isProdBuild(), + isStatsBuild: isStatsBuild(), latestBuild, }); diff --git a/gallery/public/images/album_cover_2.jpg b/gallery/public/images/album_cover_2.jpg new file mode 100644 index 0000000000..4a2339f733 Binary files /dev/null and b/gallery/public/images/album_cover_2.jpg differ diff --git a/gallery/src/data/media_players.ts b/gallery/src/data/media_players.ts index 4ca2551783..f15e6949b6 100644 --- a/gallery/src/data/media_players.ts +++ b/gallery/src/data/media_players.ts @@ -7,7 +7,7 @@ export const createMediaPlayerEntities = () => [ media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)", media_artist: "Technohead", supported_features: 64063, - entity_picture: "/images/album_cover.jpg", + entity_picture: "/images/album_cover_2.jpg", media_duration: 300, media_position: 50, media_position_updated_at: new Date( diff --git a/gallery/src/demos/demo-hui-media-control-card.ts b/gallery/src/demos/demo-hui-media-control-card.ts index f79284a13f..aca2612dd5 100644 --- a/gallery/src/demos/demo-hui-media-control-card.ts +++ b/gallery/src/demos/demo-hui-media-control-card.ts @@ -4,6 +4,7 @@ import { PolymerElement } from "@polymer/polymer/polymer-element"; import { provideHass } from "../../../src/fake_data/provide_hass"; import "../components/demo-cards"; import { createMediaPlayerEntities } from "../data/media_players"; +import "../../../src/panels/lovelace/cards/hui-media-control-card"; const CONFIGS = [ { diff --git a/hassio/webpack.config.js b/hassio/webpack.config.js index 02e44c0d74..222b20ef84 100644 --- a/hassio/webpack.config.js +++ b/hassio/webpack.config.js @@ -6,6 +6,6 @@ const { isProdBuild } = require("../build-scripts/env.js"); const latestBuild = false; module.exports = createHassioConfig({ - isProdBuild, + isProdBuild: isProdBuild(), latestBuild, }); diff --git a/setup.py b/setup.py index 4f160813c1..97a90b9da4 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20200312.0", + version="20200313.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/home-assistant-polymer", author="The Home Assistant Authors", diff --git a/src/data/media-player.ts b/src/data/media-player.ts index 2527731956..1f716cbee1 100644 --- a/src/data/media-player.ts +++ b/src/data/media-player.ts @@ -14,7 +14,7 @@ export const SUPPORT_SELECT_SOURCE = 2048; export const SUPPORT_STOP = 4096; export const SUPPORTS_PLAY = 16384; export const SUPPORT_SELECT_SOUND_MODE = 65536; -export const CONTRAST_RATIO = 3.5; +export const CONTRAST_RATIO = 4.5; export interface MediaPlayerThumbnail { content_type: string; diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts index 6d38ae561d..c9dd5f902e 100644 --- a/src/panels/lovelace/cards/hui-button-card.ts +++ b/src/panels/lovelace/cards/hui-button-card.ts @@ -56,7 +56,8 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { lovelaceConfig, maxEntities, entities, - entitiesFill + entitiesFill, + ["light", "switch"] ); return { diff --git a/src/panels/lovelace/cards/hui-entities-card.ts b/src/panels/lovelace/cards/hui-entities-card.ts index 072e6eb1ba..a68b82e015 100644 --- a/src/panels/lovelace/cards/hui-entities-card.ts +++ b/src/panels/lovelace/cards/hui-entities-card.ts @@ -51,10 +51,11 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard { lovelaceConfig, maxEntities, entities, - entitiesFill + entitiesFill, + ["light", "switch", "sensor"] ); - return { entities: foundEntities }; + return { title: "My Title", entities: foundEntities }; } @property() private _config?: EntitiesCardConfig; diff --git a/src/panels/lovelace/cards/hui-gauge-card.ts b/src/panels/lovelace/cards/hui-gauge-card.ts index def7bf77f8..a7e9faae09 100644 --- a/src/panels/lovelace/cards/hui-gauge-card.ts +++ b/src/panels/lovelace/cards/hui-gauge-card.ts @@ -312,8 +312,8 @@ class HuiGaugeCard extends LitElement implements LovelaceCard { line-height: calc(var(--base-unit) * 0.3); position: absolute; width: calc(var(--base-unit) * 4); - height: calc(var(--base-unit) * 2.1); - top: calc(var(--base-unit) * 1.2); + height: var(--base-unit); + top: var(--base-unit); margin-left: auto; margin-right: auto; } @@ -322,6 +322,7 @@ class HuiGaugeCard extends LitElement implements LovelaceCard { } .gauge-data #percent { font-size: calc(var(--base-unit) * 0.55); + line-height: calc(var(--base-unit) * 0.55); } .gauge-data #name { padding-top: calc(var(--base-unit) * 0.15); diff --git a/src/panels/lovelace/cards/hui-glance-card.ts b/src/panels/lovelace/cards/hui-glance-card.ts index b1288f4276..ff789b1e1d 100644 --- a/src/panels/lovelace/cards/hui-glance-card.ts +++ b/src/panels/lovelace/cards/hui-glance-card.ts @@ -47,7 +47,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard { lovelaceConfig: LovelaceConfig, entities?: string[], entitiesFill?: string[] - ): object { + ): GlanceCardConfig { const includeDomains = ["sensor"]; const maxEntities = 3; const foundEntities = findEntities( @@ -59,7 +59,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard { includeDomains ); - return { entities: foundEntities }; + return { type: "glance", entities: foundEntities }; } @property() public hass?: HomeAssistant; diff --git a/src/panels/lovelace/cards/hui-media-control-card.ts b/src/panels/lovelace/cards/hui-media-control-card.ts index e8ea6d4722..3d44e0895a 100644 --- a/src/panels/lovelace/cards/hui-media-control-card.ts +++ b/src/panels/lovelace/cards/hui-media-control-card.ts @@ -12,7 +12,7 @@ import { import { classMap } from "lit-html/directives/class-map"; import { styleMap } from "lit-html/directives/style-map"; import Vibrant from "node-vibrant"; -import { Palette } from "node-vibrant/lib/color"; +import { Swatch } from "node-vibrant/lib/color"; import "@polymer/paper-icon-button/paper-icon-button"; import "@polymer/paper-progress/paper-progress"; // tslint:disable-next-line: no-duplicate-imports @@ -59,6 +59,107 @@ function getContrastRatio( return Math.round((contrast(rgb1, rgb2) + Number.EPSILON) * 100) / 100; } +// How much the total diff between 2 RGB colors can be +// to be considered similar. +const COLOR_SIMILARITY_THRESHOLD = 150; + +// For debug purposes, is being tree shaken. +const DEBUG_COLOR = __DEV__ && false; + +const logColor = ( + color: Swatch, + label: string = `${color.getHex()} - ${color.getPopulation()}` +) => + // tslint:disable-next-line:no-console + console.log( + `%c${label}`, + `color: ${color.getBodyTextColor()}; background-color: ${color.getHex()}` + ); + +const customGenerator = (colors: Swatch[]) => { + colors.sort((colorA, colorB) => colorB.population - colorA.population); + + const backgroundColor = colors[0]; + let foregroundColor: string | undefined; + + const contrastRatios = new Map(); + const approvedContrastRatio = (color: Swatch) => { + if (!contrastRatios.has(color)) { + contrastRatios.set( + color, + getContrastRatio(backgroundColor.rgb, color.rgb) + ); + } + + return contrastRatios.get(color)! > CONTRAST_RATIO; + }; + + // We take each next color and find one that has better contrast. + for (let i = 1; i < colors.length && foregroundColor === undefined; i++) { + // If this color matches, score, take it. + if (approvedContrastRatio(colors[i])) { + if (DEBUG_COLOR) { + logColor(colors[i], "PICKED"); + } + foregroundColor = colors[i].hex; + break; + } + + // This color has the wrong contrast ratio, but it is the right color. + // Let's find similar colors that might have the right contrast ratio + + const currentColor = colors[i]; + if (DEBUG_COLOR) { + logColor(colors[i], "Finding similar color with better contrast"); + } + + for (let j = i + 1; j < colors.length; j++) { + const compareColor = colors[j]; + + // difference. 0 is same, 765 max difference + const diffScore = + Math.abs(currentColor.rgb[0] - compareColor.rgb[0]) + + Math.abs(currentColor.rgb[1] - compareColor.rgb[1]) + + Math.abs(currentColor.rgb[2] - compareColor.rgb[2]); + + if (DEBUG_COLOR) { + logColor(colors[j], `${colors[j].hex} - ${diffScore}`); + } + + if (diffScore > COLOR_SIMILARITY_THRESHOLD) { + continue; + } + + if (approvedContrastRatio(compareColor)) { + if (DEBUG_COLOR) { + logColor(compareColor, "PICKED"); + } + foregroundColor = compareColor.hex; + break; + } + } + } + + if (foregroundColor === undefined) { + foregroundColor = backgroundColor.bodyTextColor; + } + + if (DEBUG_COLOR) { + // tslint:disable-next-line:no-console + console.log(); + // tslint:disable-next-line:no-console + console.log( + "%cPicked colors", + `color: ${foregroundColor}; background-color: ${backgroundColor.hex}; font-weight: bold; padding: 16px;` + ); + colors.forEach((color) => logColor(color)); + // tslint:disable-next-line:no-console + console.log(); + } + + return [foregroundColor, backgroundColor.hex]; +}; + interface ControlButton { icon: string; action: string; @@ -594,50 +695,11 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard { } Vibrant.from(this._image) - .quality(1) + .useGenerator(customGenerator) .getPalette() - .then((palette: Palette) => { - const paletteColors: any[] = []; - - Object.keys(palette).forEach((color) => { - paletteColors.push({ - hex: palette[color]!.getHex(), - rgb: palette[color]!.getRgb(), - textColor: palette[color]!.getBodyTextColor(), - population: palette[color]!.getPopulation(), - }); - }); - - if (!paletteColors.length) { - this._foregroundColor = undefined; - this._backgroundColor = undefined; - return; - } - - paletteColors.sort((colorA, colorB) => { - if (colorA.population > colorB.population) { - return -1; - } - if (colorA.population < colorB.population) { - return 1; - } - - return 0; - }); - - this._backgroundColor = paletteColors[0].hex; - - for (let i = 1; i < paletteColors.length; i++) { - if ( - getContrastRatio(paletteColors[0].rgb, paletteColors[i].rgb) >= - CONTRAST_RATIO - ) { - this._foregroundColor = paletteColors[i].hex; - return; - } - } - - this._foregroundColor = paletteColors[0].textColor; + .then(([foreground, background]: [string, string]) => { + this._backgroundColor = background; + this._foregroundColor = foreground; }) .catch((err: any) => { // tslint:disable-next-line:no-console diff --git a/src/panels/lovelace/cards/hui-picture-card.ts b/src/panels/lovelace/cards/hui-picture-card.ts index f21b24b778..aec27be4ac 100644 --- a/src/panels/lovelace/cards/hui-picture-card.ts +++ b/src/panels/lovelace/cards/hui-picture-card.ts @@ -32,8 +32,7 @@ export class HuiPictureCard extends LitElement implements LovelaceCard { } public static getStubConfig(): object { return { - image: - "https://www.home-assistant.io/images/merchandise/shirt-frontpage.png", + image: "https://demo.home-assistant.io/stub_config/t-shirt-promo.png", tap_action: { action: "none" }, hold_action: { action: "none" }, }; diff --git a/src/panels/lovelace/cards/hui-picture-elements-card.ts b/src/panels/lovelace/cards/hui-picture-elements-card.ts index d8408bb55f..001d50ecd1 100644 --- a/src/panels/lovelace/cards/hui-picture-elements-card.ts +++ b/src/panels/lovelace/cards/hui-picture-elements-card.ts @@ -25,26 +25,30 @@ class HuiPictureElementsCard extends LitElement implements LovelaceCard { lovelaceConfig: LovelaceConfig, entities?: string[], entitiesFill?: string[] - ): object { + ): PictureElementsCardConfig { const maxEntities = 1; const foundEntities = findEntities( hass, lovelaceConfig, maxEntities, entities, - entitiesFill + entitiesFill, + ["sensor", "binary_sensor"] ); return { + type: "picture-elements", elements: [ { type: "state-badge", entity: foundEntities[0] || "", - style: "position: absolute, transform: translate(-50%, -50%)", + style: { + top: "32%", + left: "40%", + }, }, ], - image: - "https://www.home-assistant.io/images/merchandise/shirt-frontpage.png", + image: "https://demo.home-assistant.io/stub_config/floorplan.png", }; } diff --git a/src/panels/lovelace/cards/hui-picture-entity-card.ts b/src/panels/lovelace/cards/hui-picture-entity-card.ts index 047cc84e6d..4a409da1b5 100644 --- a/src/panels/lovelace/cards/hui-picture-entity-card.ts +++ b/src/panels/lovelace/cards/hui-picture-entity-card.ts @@ -52,13 +52,13 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard { lovelaceConfig, maxEntities, entities, - entitiesFill + entitiesFill, + ["light", "switch"] ); return { entity: foundEntities[0] || "", - image: - "https://www.home-assistant.io/images/merchandise/shirt-frontpage.png", + image: "https://demo.home-assistant.io/stub_config/bedroom.png", }; } diff --git a/src/panels/lovelace/cards/hui-picture-glance-card.ts b/src/panels/lovelace/cards/hui-picture-glance-card.ts index a175ccec9c..7c1c1f027d 100644 --- a/src/panels/lovelace/cards/hui-picture-glance-card.ts +++ b/src/panels/lovelace/cards/hui-picture-glance-card.ts @@ -49,19 +49,21 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard { lovelaceConfig: LovelaceConfig, entities?: string[], entitiesFill?: string[] - ): object { + ): PictureGlanceCardConfig { const maxEntities = 2; const foundEntities = findEntities( hass, lovelaceConfig, maxEntities, entities, - entitiesFill + entitiesFill, + ["sensor", "binary_sensor"] ); return { - image: - "https://www.home-assistant.io/images/merchandise/shirt-frontpage.png", + type: "picture-glance", + title: "Kitchen", + image: "https://demo.home-assistant.io/stub_config/kitchen.png", entities: foundEntities, }; } diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts index a67977a80c..7bda4a1119 100644 --- a/src/panels/lovelace/cards/types.ts +++ b/src/panels/lovelace/cards/types.ts @@ -115,7 +115,7 @@ export interface GlanceCardConfig extends LovelaceCardConfig { show_icon?: boolean; title?: string; theme?: string; - entities: ConfigEntity[]; + entities: Array; columns?: number; state_color?: boolean; } @@ -177,7 +177,7 @@ export interface PictureElementsCardConfig extends LovelaceCardConfig { camera_image?: string; camera_view?: HuiImage["cameraView"]; state_image?: {}; - state_filter: string[]; + state_filter?: string[]; aspect_ratio?: string; entity?: string; elements: LovelaceElementConfig[]; @@ -202,13 +202,13 @@ export interface PictureEntityCardConfig extends LovelaceCardConfig { } export interface PictureGlanceCardConfig extends LovelaceCardConfig { - entities: PictureGlanceEntityConfig[]; + entities: Array; title?: string; image?: string; camera_image?: string; camera_view?: HuiImage["cameraView"]; state_image?: {}; - state_filter: string[]; + state_filter?: string[]; aspect_ratio?: string; entity?: string; tap_action?: ActionConfig; diff --git a/src/panels/lovelace/common/compute-unused-entities.ts b/src/panels/lovelace/common/compute-unused-entities.ts index af91701e78..abedb58454 100755 --- a/src/panels/lovelace/common/compute-unused-entities.ts +++ b/src/panels/lovelace/common/compute-unused-entities.ts @@ -44,7 +44,7 @@ const addEntities = (entities: Set, obj) => { if (obj.entity) { addEntityId(entities, obj.entity); } - if (obj.entities) { + if (obj.entities && Array.isArray(obj.entities)) { obj.entities.forEach((entity) => addEntityId(entities, entity)); } if (obj.card) { diff --git a/src/panels/lovelace/components/hui-marquee.ts b/src/panels/lovelace/components/hui-marquee.ts index cc59cbf700..967ae88277 100644 --- a/src/panels/lovelace/components/hui-marquee.ts +++ b/src/panels/lovelace/components/hui-marquee.ts @@ -30,6 +30,10 @@ class HuiMarquee extends LitElement { protected updated(changedProperties: PropertyValues): void { super.updated(changedProperties); + if (changedProperties.has("text") && this._animating) { + this._animating = false; + } + if ( changedProperties.has("active") && this.active && @@ -69,6 +73,7 @@ class HuiMarquee extends LitElement { position: relative; align-items: center; height: 1em; + contain: strict; } .marquee-inner { @@ -77,7 +82,6 @@ class HuiMarquee extends LitElement { right: 0; text-overflow: ellipsis; overflow: hidden; - animation: marquee 10s linear infinite paused; } :host(.hovering) .marquee-inner { @@ -88,13 +92,10 @@ class HuiMarquee extends LitElement { :host([animating]) .marquee-inner { left: initial; right: initial; + animation: marquee 10s linear infinite; } - :host([animating]) > div { - animation-play-state: running; - } - - span { + :host([animating]) .marquee-inner span { padding-right: 16px; } diff --git a/src/panels/lovelace/create-element/create-card-element.ts b/src/panels/lovelace/create-element/create-card-element.ts index 3669bba1f4..2fab4496c0 100644 --- a/src/panels/lovelace/create-element/create-card-element.ts +++ b/src/panels/lovelace/create-element/create-card-element.ts @@ -5,7 +5,6 @@ import "../cards/hui-glance-card"; import "../cards/hui-history-graph-card"; import "../cards/hui-horizontal-stack-card"; import "../cards/hui-light-card"; -import "../cards/hui-media-control-card"; import "../cards/hui-sensor-card"; import "../cards/hui-thermostat-card"; import "../cards/hui-vertical-stack-card"; @@ -25,7 +24,6 @@ const ALWAYS_LOADED_TYPES = new Set([ "history-graph", "horizontal-stack", "light", - "media-control", "sensor", "thermostat", "vertical-stack", @@ -36,6 +34,7 @@ const LAZY_LOAD_TYPES = { "alarm-panel": () => import("../cards/hui-alarm-panel-card"), "empty-state": () => import("../cards/hui-empty-state-card"), "entity-filter": () => import("../cards/hui-entity-filter-card"), + "media-control": () => import("../cards/hui-media-control-card"), "picture-elements": () => import("../cards/hui-picture-elements-card"), "picture-entity": () => import("../cards/hui-picture-entity-card"), "picture-glance": () => import("../cards/hui-picture-glance-card"), diff --git a/src/panels/lovelace/elements/types.ts b/src/panels/lovelace/elements/types.ts index 08ca175fa7..f12137d58f 100644 --- a/src/panels/lovelace/elements/types.ts +++ b/src/panels/lovelace/elements/types.ts @@ -2,22 +2,31 @@ import { HomeAssistant } from "../../../types"; import { Condition } from "../common/validate-condition"; import { ActionConfig } from "../../../data/lovelace"; -export interface LovelaceElementConfig { +interface LovelaceElementConfigBase { type: string; style: object; } +export type LovelaceElementConfig = + | ConditionalElementConfig + | IconElementConfig + | ImageElementConfig + | ServiceButtonElementConfig + | StateBadgeElementConfig + | StateIconElementConfig + | StateLabelElementConfig; + export interface LovelaceElement extends HTMLElement { hass?: HomeAssistant; setConfig(config: LovelaceElementConfig): void; } -export interface ConditionalElementConfig extends LovelaceElementConfig { +export interface ConditionalElementConfig extends LovelaceElementConfigBase { conditions: Condition[]; - elements: LovelaceElementConfig[]; + elements: LovelaceElementConfigBase[]; } -export interface IconElementConfig extends LovelaceElementConfig { +export interface IconElementConfig extends LovelaceElementConfigBase { entity?: string; name?: string; tap_action?: ActionConfig; @@ -26,7 +35,7 @@ export interface IconElementConfig extends LovelaceElementConfig { icon: string; } -export interface ImageElementConfig extends LovelaceElementConfig { +export interface ImageElementConfig extends LovelaceElementConfigBase { entity?: string; tap_action?: ActionConfig; hold_action?: ActionConfig; @@ -39,13 +48,13 @@ export interface ImageElementConfig extends LovelaceElementConfig { aspect_ratio?: string; } -export interface ServiceButtonElementConfig extends LovelaceElementConfig { +export interface ServiceButtonElementConfig extends LovelaceElementConfigBase { title?: string; service?: string; service_data?: object; } -export interface StateBadgeElementConfig extends LovelaceElementConfig { +export interface StateBadgeElementConfig extends LovelaceElementConfigBase { entity: string; title?: string; tap_action?: ActionConfig; @@ -53,7 +62,7 @@ export interface StateBadgeElementConfig extends LovelaceElementConfig { double_tap_action?: ActionConfig; } -export interface StateIconElementConfig extends LovelaceElementConfig { +export interface StateIconElementConfig extends LovelaceElementConfigBase { entity: string; tap_action?: ActionConfig; hold_action?: ActionConfig; @@ -62,7 +71,7 @@ export interface StateIconElementConfig extends LovelaceElementConfig { state_color?: boolean; } -export interface StateLabelElementConfig extends LovelaceElementConfig { +export interface StateLabelElementConfig extends LovelaceElementConfigBase { entity: string; attribute?: string; prefix?: string; diff --git a/translations/ca.json b/translations/ca.json index 11056a905f..fbe2022641 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -649,8 +649,12 @@ "name": "Nom" }, "input_datetime": { + "date": "Data", + "datetime": "Data i hora", "has_date": "Data", - "has_time": "Hora" + "has_time": "Hora", + "mode": "Què vols introduir", + "time": "Hora" }, "input_number": { "max": "Valor màxim", @@ -1299,7 +1303,8 @@ "device": "Dispositiu", "integration": "Integració", "manufacturer": "Fabricant", - "model": "Model" + "model": "Model", + "no_devices": "Sense dispositius" }, "description": "Gestiona els dispositius connectats", "details": "Aquí tens tots els detalls del dispositiu.", @@ -1395,6 +1400,7 @@ }, "picker": { "headers": { + "entity_id": "ID de l'entitat", "name": "Nom", "type": "Tipus" } @@ -1487,6 +1493,7 @@ "require_admin": "Només administrador", "show_sidebar": "Mostra a la barra lateral", "title": "Títol de la barra lateral", + "title_required": "El títol és necessari.", "update": "Actualitza", "url": "URL" }, @@ -1494,6 +1501,7 @@ "add_dashboard": "Afegeix panell", "headers": { "conf_mode": "Mètode de configuració", + "default": "Per defecte", "filename": "Nom de l'arxiu", "require_admin": "Només administrador", "sidebar": "Mostra a la barra lateral", @@ -1524,7 +1532,8 @@ "headers": { "type": "Tipus", "url": "URL" - } + }, + "no_resources": "Sense recursos" }, "refresh_body": "Has d’actualitzar la pàgina per completar l’eliminació. Vols actualitzar-la ara?", "refresh_header": "Vols actualitzar?", @@ -2060,13 +2069,17 @@ "name": "Panell d'alarma" }, "button": { + "description": "La targeta botó et permet afegir botons per realitzar diferents tasques.", "name": "Botó" }, "conditional": { "card": "Targeta", "conditions": "Condicions", "current_state": "actual", - "name": "Condicional" + "description": "La targeta condicional mostra una altra targeta en funció dels estats d'una entitat.", + "name": "Condicional", + "state_equal": "L'estat és igual a", + "state_not_equal": "L'estat NO és igual a" }, "config": { "optional": "Opcional", @@ -2103,6 +2116,7 @@ "icon": "Icona", "icon_height": "Alçada de la icona", "image": "Ruta de la imatge", + "manual": "Manual", "maximum": "Màxim", "minimum": "Mínim", "name": "Nom", @@ -2216,7 +2230,10 @@ "move_right": "Desplaça la visualització cap a la dreta", "tab_badges": "Etiquetes", "tab_settings": "Configuració", - "tab_visibility": "Visibilitat" + "tab_visibility": "Visibilitat", + "visibility": { + "select_users": "Selecciona quins usuaris podran veure aquesta vista a la navegació" + } }, "header": "Editar la interfície d'usuari (UI)", "menu": { @@ -2287,9 +2304,12 @@ }, "views": { "confirm_delete": "Estàs segur que vols eliminar aquesta visualització?", + "confirm_delete_existing_cards": "Si elimines aquesta vista, també s'eliminaran les targetes que conté", + "confirm_delete_text": "Estàs segur que vols eliminar la vista '{name}'?", "existing_cards": "No pots suprimir una visualització si conté targetes, elimina-les primer." }, "warning": { + "attribute_not_found": "L'atribut {attribute} de {entity} no està disponible.", "entity_non_numeric": "Entitat no numèrica: {entity}", "entity_not_found": "Entitat no disponible: {entity}" } diff --git a/translations/de.json b/translations/de.json index 90e0060373..ab687133d0 100644 --- a/translations/de.json +++ b/translations/de.json @@ -1317,7 +1317,8 @@ "device": "Gerät", "integration": "Integration", "manufacturer": "Hersteller", - "model": "Modell" + "model": "Modell", + "no_devices": "keine Geräte" }, "description": "Verwalte verbundene Geräte", "details": "Hier sind alle Details deines Geräts.", @@ -1559,7 +1560,8 @@ "headers": { "type": "Typ", "url": "Url" - } + }, + "no_resources": "keine Ressourcen" }, "refresh_body": "Sie müssen die Seite aktualisieren, um das Entfernen abzuschließen. Möchten Sie jetzt aktualisieren?", "refresh_header": "Möchten Sie aktualisieren?", @@ -2275,7 +2277,10 @@ "move_right": "Ansicht nach rechts verschieben", "tab_badges": "Abzeichen", "tab_settings": "Einstellungen", - "tab_visibility": "Sichtweite" + "tab_visibility": "Sichtweite", + "visibility": { + "select_users": "Wähle aus, welche Benutzer diese Ansicht in der Navigation sehen sollen" + } }, "header": "Benutzeroberfläche bearbeiten", "menu": { @@ -2352,6 +2357,7 @@ "existing_cards": "Sie können eine Ansicht mit Karten nicht löschen. Entfernen Sie zuerst die Karten." }, "warning": { + "attribute_not_found": "Attribut {attribute} nicht verfügbar in: {entity}", "entity_non_numeric": "Die Entität ist nicht-numerisch: {entity}", "entity_not_found": "Entität nicht verfügbar: {entity}" } diff --git a/translations/en.json b/translations/en.json index 38b0d26e9e..c649ce3965 100644 --- a/translations/en.json +++ b/translations/en.json @@ -674,8 +674,8 @@ "options": "Options" }, "input_text": { - "max": "Maximum lenght", - "min": "Minimum lenght", + "max": "Maximum length", + "min": "Minimum length", "mode": "Display mode", "password": "Password", "pattern": "Regex pattern for client-side validation", @@ -1508,18 +1508,19 @@ "yaml": "YAML file" }, "confirm_delete": "Are you sure you want to delete this dashboard?", + "default_dashboard": "This is the default dashboard", "detail": { "create": "Create", "delete": "Delete", "dismiss": "Close", "edit_dashboard": "Edit dashboard", - "icon": "Sidebar icon", + "icon": "Icon", "new_dashboard": "Add new dashboard", "remove_default": "Remove as default on this device", "require_admin": "Admin only", "set_default": "Set as default on this device", "show_sidebar": "Show in sidebar", - "title": "Sidebar title", + "title": "Title", "title_required": "Title is required.", "update": "Update", "url": "Url", diff --git a/translations/es.json b/translations/es.json index d96ccab496..c4453d5162 100644 --- a/translations/es.json +++ b/translations/es.json @@ -1317,7 +1317,8 @@ "device": "Dispositivo", "integration": "Integración", "manufacturer": "Fabricante", - "model": "Modelo" + "model": "Modelo", + "no_devices": "Sin dispositivos" }, "description": "Administrar dispositivos conectados", "details": "Aquí están todos los detalles de tu dispositivo.", @@ -1559,7 +1560,8 @@ "headers": { "type": "Tipo", "url": "Url" - } + }, + "no_resources": "Sin recursos" }, "refresh_body": "Tienes que actualizar la página para completar la eliminación, ¿deseas actualizar ahora?", "refresh_header": "¿Quieres actualizar?", @@ -2275,7 +2277,10 @@ "move_right": "Mover vista a la derecha", "tab_badges": "Insignias", "tab_settings": "Configuración", - "tab_visibility": "Visibilidad" + "tab_visibility": "Visibilidad", + "visibility": { + "select_users": "Selecciona qué usuarios deberían ver esta vista en la navegación" + } }, "header": "Editar la interfaz de usuario", "menu": { @@ -2352,6 +2357,7 @@ "existing_cards": "No puedes eliminar una vista que tiene tarjetas. Elimina las tarjetas primero." }, "warning": { + "attribute_not_found": "El atributo {attribute} no está disponible en: {entity}", "entity_non_numeric": "La entidad no es numérica: {entity}", "entity_not_found": "La entidad no está disponible: {entity}" } diff --git a/translations/fr.json b/translations/fr.json index b0e5ab96d7..cd5b09c874 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -2200,6 +2200,7 @@ "existing_cards": "Vous ne pouvez pas supprimer une vue contenant des cartes. Retirez d'abord les cartes." }, "warning": { + "attribute_not_found": "Attribut {attribute} non disponible dans: {entity}", "entity_non_numeric": "L'entité est non numérique: {entity}", "entity_not_found": "Entité non disponible: {entity}" } diff --git a/translations/nb.json b/translations/nb.json index 059bcfd969..9202499253 100644 --- a/translations/nb.json +++ b/translations/nb.json @@ -1317,7 +1317,8 @@ "device": "Enhet", "integration": "Integrering", "manufacturer": "Produsent", - "model": "Modell" + "model": "Modell", + "no_devices": "Ingen enheter" }, "description": "Administrer tilkoblede enheter", "details": "Her er alle detaljer for enheten.", @@ -1559,7 +1560,8 @@ "headers": { "type": "Type", "url": "Url" - } + }, + "no_resources": "Ingen ressurser" }, "refresh_body": "Du må oppdatere siden for å fullføre fjerningen, vil du oppdatere nå?", "refresh_header": "Vil du oppdatere?", @@ -2275,7 +2277,10 @@ "move_right": "Flytt visning til høyre", "tab_badges": "Merker", "tab_settings": "Innstillinger", - "tab_visibility": "Synlighet" + "tab_visibility": "Synlighet", + "visibility": { + "select_users": "Velg hvilke brukere som skal se denne visningen i navigasjonen" + } }, "header": "Rediger brukergrensesnitt", "menu": { @@ -2352,6 +2357,7 @@ "existing_cards": "Du kan ikke slette en visning som har kort i den. Fjern kortene først." }, "warning": { + "attribute_not_found": "Attributtet {attributt} er ikke tilgjengelig i: {entity}", "entity_non_numeric": "Entiteten er ikke-numerisk: {entity}", "entity_not_found": "Entitet ikke tilgjengelig: {entity}" } diff --git a/translations/pl.json b/translations/pl.json index 51baa03ee5..df6bf25af4 100644 --- a/translations/pl.json +++ b/translations/pl.json @@ -651,9 +651,12 @@ "name": "Nazwa" }, "input_datetime": { - "datetime": "Data i godzina", + "date": "Data", + "datetime": "Data i czas", "has_date": "Data", - "has_time": "Czas" + "has_time": "Czas", + "mode": "Co chcesz wprowadzić?", + "time": "Czas" }, "input_number": { "box": "Pole wprowadzania danych", @@ -1314,7 +1317,8 @@ "device": "Urządzenie", "integration": "Integracja", "manufacturer": "Producent", - "model": "Model" + "model": "Model", + "no_devices": "Brak urządzeń" }, "description": "Zarządzaj podłączonymi urządzeniami", "details": "Oto wszystkie szczegóły dotyczące twojego urządzenia.", @@ -1494,11 +1498,11 @@ }, "introduction": "Tutaj możesz skonfigurować Home Assistant'a i jego komponenty. Nie wszystkie opcje można konfigurować z interfejsu użytkownika, ale pracujemy nad tym.", "lovelace": { - "caption": "Dashboard'y interfejsu użytkownika Lovelace", + "caption": "Dashboardy Lovelace", "dashboards": { "cant_edit_default": "Standardowego dashboardu Lovelace nie można edytować za pomocą interfejsu użytkownika. Możesz go ukryć, ustawiając jako domyślny inny dashboard.", "cant_edit_yaml": "Dasboard'y zdefiniowane w plikach YAML nie mogą być edytowane z poziomu interfejsu użytkownika. Zmień je w pliku configuration.yaml.", - "caption": "Dashboard'y", + "caption": "Dashboardy", "conf_mode": { "storage": "Konfigurowalny z interfejsu użytkownika", "yaml": "Plik YAML" @@ -1512,9 +1516,9 @@ "icon": "Ikona w pasku bocznym", "new_dashboard": "Dodaj nowy dashboard", "remove_default": "Nie używaj jako domyślny na tym urządzeniu", - "require_admin": "Tylko administrator", + "require_admin": "Tylko dla administratorów", "set_default": "Ustaw jako domyślny na tym urządzeniu", - "show_sidebar": "Pokaż na pasku bocznym", + "show_sidebar": "Wyświetlanie na pasku bocznym", "title": "Tytuł w pasku bocznym", "title_required": "Tytuł jest wymagany.", "update": "Aktualizuj", @@ -1528,13 +1532,13 @@ "default": "Domyślny", "filename": "Nazwa pliku", "require_admin": "Tylko administrator", - "sidebar": "Pokaż na pasku bocznym", + "sidebar": "Na pasku bocznym", "title": "Tytuł" }, - "open": "Otwórz dashboard" + "open": "Otwórz" } }, - "description": "Konfiguruj dashboard'y interfejsu użytkownika Lovelace", + "description": "Konfiguruj dashboardy interfejsu użytkownika Lovelace", "resources": { "cant_edit_yaml": "Korzystasz z interfejsu użytkownika Lovelace w trybie YAML, dlatego nie możesz zarządzać zasobami za pomocą interfejsu użytkownika. Zarządzaj nimi w pliku configuration.yaml.", "caption": "Zasoby", @@ -1556,7 +1560,8 @@ "headers": { "type": "Typ", "url": "URL" - } + }, + "no_resources": "Brak zasobów" }, "refresh_body": "Musisz odświeżyć stronę, aby zakończyć usuwanie, czy chcesz teraz odświeżyć?", "refresh_header": "Czy chcesz odświeżyć?", @@ -2272,7 +2277,10 @@ "move_right": "Przesuń widok w prawo", "tab_badges": "Odznaki", "tab_settings": "Ustawienia", - "tab_visibility": "Widoczność" + "tab_visibility": "Widoczność", + "visibility": { + "select_users": "Wybierz, którzy użytkownicy powinni mieć dostęp do tego widoku" + } }, "header": "Edycja interfejsu użytkownika", "menu": { @@ -2307,8 +2315,8 @@ "para": "Domyślnie Home Assistant będzie zarządzać interfejsem użytkownika, aktualizując go, gdy pojawią się nowe encje lub komponenty Lovelace. Jeśli przejmiesz kontrolę, Home Assistant nie będzie już automatycznie wprowadzać dla ciebie zmian.", "para_sure": "Na pewno chcesz przejąć kontrolę nad interfejsem użytkownika?", "save": "Przejmuję kontrolę", - "yaml_config": "Aby rozpocząć, zapoznaj się z aktualną konfiguracją tego dashboard'u:", - "yaml_control": "Aby zarządzać w trybie YAML, utwórz plik o nazwie podanej w konfiguracji dla tego dashboard'u lub domyślny 'ui-lovelace.yaml'.", + "yaml_config": "Aby rozpocząć, zapoznaj się z aktualną konfiguracją tego dashboardu:", + "yaml_control": "Aby zarządzać w trybie YAML, utwórz plik o nazwie podanej w konfiguracji dla tego dashboardu lub domyślny 'ui-lovelace.yaml'.", "yaml_mode": "Używasz trybu YAML, co oznacza, że nie możesz zmienić konfiguracji Lovelace z poziomu interfejsu użytkownika. Jeśli chcesz zarządzać Lovelace z poziomu interfejsu użytkownika, usuń 'mode: yaml' z konfiguracji Lovelace w pliku 'configuration.yaml'." }, "suggest_card": { @@ -2349,6 +2357,7 @@ "existing_cards": "Nie można usunąć widoku zawierającego karty. Najpierw usuń karty." }, "warning": { + "attribute_not_found": "Atrybut {attribute} jest niedostępny dla: {entity}", "entity_non_numeric": "Encja nie jest numeryczna: {entity}", "entity_not_found": "Encja niedostępna: {entity}" } diff --git a/translations/ru.json b/translations/ru.json index 62b47f1edf..93a27923ef 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -1316,7 +1316,8 @@ "device": "Устройство", "integration": "Интеграция", "manufacturer": "Производитель", - "model": "Модель" + "model": "Модель", + "no_devices": "Нет устройств" }, "description": "Управляйте подключенными устройствами", "details": "Здесь приведена вся доступная информация об этом устройстве.", @@ -1518,7 +1519,7 @@ "set_default": "Установить по умолчанию на этом устройстве", "show_sidebar": "Показывать на боковой панели", "title": "Название для боковой панели", - "title_required": "Название указывается обязательно.", + "title_required": "Обязательное поле", "update": "Обновить", "url": "URL-адрес", "url_error_msg": "URL не может содержать пробелы или специальные символы, кроме символов _ и -" @@ -1549,7 +1550,7 @@ "type": "Тип ресурса", "update": "Обновить", "url": "URL-адрес", - "url_error_msg": "URL-адрес является обязательным полем.", + "url_error_msg": "Обязательное поле", "warning_header": "Будьте осторожны!", "warning_text": "Добавление ресурсов может быть опасным. Убедитесь, что Вы знаете источник ресурса и доверяете ему. Плохие ресурсы могут нанести серьёзный вред Вашей системе." }, @@ -1558,7 +1559,8 @@ "headers": { "type": "Тип", "url": "URL-адрес" - } + }, + "no_resources": "Нет ресурсов" }, "refresh_body": "Вы должны обновить страницу, чтобы завершить удаление. Обновить сейчас?", "refresh_header": "Обновить страницу?", @@ -2267,7 +2269,10 @@ "move_right": "Переместить вкладку вправо", "tab_badges": "Значки", "tab_settings": "Настройки", - "tab_visibility": "Видимость" + "tab_visibility": "Видимость", + "visibility": { + "select_users": "Выберите, какие пользователи должны видеть эту вкладку" + } }, "header": "Редактирование интерфейса", "menu": { @@ -2343,6 +2348,7 @@ "existing_cards": "Прежде чем удалять вкладку, удалите из нее все карточки." }, "warning": { + "attribute_not_found": "Атрибут {attribute} недоступен в {entity}", "entity_non_numeric": "Объект не является числом: {entity}", "entity_not_found": "Объект {entity} недоступен." } diff --git a/translations/zh-Hans.json b/translations/zh-Hans.json index 6b7457d22c..2b0983153f 100644 --- a/translations/zh-Hans.json +++ b/translations/zh-Hans.json @@ -1317,7 +1317,8 @@ "device": "设备", "integration": "集成", "manufacturer": "制造商", - "model": "型号" + "model": "型号", + "no_devices": "没有设备" }, "description": "管理已连接的设备", "details": "这是设备的所有详细信息。", @@ -1559,7 +1560,8 @@ "headers": { "type": "类型", "url": "网址" - } + }, + "no_resources": "没有资源" }, "refresh_body": "您必须刷新页面才能完成删除,是否要立即刷新?", "refresh_header": "要刷新吗?", @@ -2275,7 +2277,10 @@ "move_right": "向右移动视图", "tab_badges": "徽章", "tab_settings": "设置", - "tab_visibility": "可见性" + "tab_visibility": "可见性", + "visibility": { + "select_users": "选择要让哪些用户在导航中看到此视图" + } }, "header": "编辑 UI", "menu": { @@ -2352,6 +2357,7 @@ "existing_cards": "无法删除包含卡片的视图。请先移除卡片。" }, "warning": { + "attribute_not_found": "属性 {attribute} 对 {entity} 不可用", "entity_non_numeric": "实体 {entity} 非数值", "entity_not_found": "实体 {entity} 不可用" } diff --git a/translations/zh-Hant.json b/translations/zh-Hant.json index ce04a48a3f..6a6964b503 100644 --- a/translations/zh-Hant.json +++ b/translations/zh-Hant.json @@ -510,7 +510,7 @@ "air_pressure": "大氣壓", "humidity": "濕度", "temperature": "溫度", - "visibility": "能見度", + "visibility": "顯示選項", "wind_speed": "風速" }, "cardinal_direction": { @@ -770,7 +770,7 @@ "unknown": "未知", "zha_device_card": { "area_picker_label": "分區", - "device_name_placeholder": "使用者姓氏", + "device_name_placeholder": "設備命名", "update_name_button": "更新名稱" } } @@ -1317,7 +1317,8 @@ "device": "設備", "integration": "整合", "manufacturer": "廠牌", - "model": "型號" + "model": "型號", + "no_devices": "沒有設備" }, "description": "管理已連線設備", "details": "此處顯示設備所有詳細資訊。", @@ -1412,7 +1413,7 @@ "header": "設定 Home Assistant", "helpers": { "caption": "助手", - "description": "可協助建立自動化的元素。", + "description": "可協助建立自動化的元素", "dialog": { "add_helper": "新增助手", "add_platform": "新增 {platform}", @@ -1503,7 +1504,7 @@ "cant_edit_yaml": "於 YAML 中定義的主面板無法藉由 UI 編輯,請於 Configuration.yaml 進行更改。", "caption": "主面板", "conf_mode": { - "storage": "UI 已控制", + "storage": "由 UI 控制", "yaml": "YAML 檔案" }, "confirm_delete": "確定要刪除此主面板?", @@ -1559,14 +1560,15 @@ "headers": { "type": "類別", "url": "網址" - } + }, + "no_resources": "沒有資源" }, "refresh_body": "必須更新頁面以完成移除、要立即更新?", "refresh_header": "是否要更新?", "types": { "css": "Stylesheet", - "html": "HTML(已棄用)", - "js": "JavaScript 檔案(已棄用)", + "html": "HTML(不再適用)", + "js": "JavaScript 檔案(不再適用)", "module": "JavaScript 模組" } } @@ -2273,9 +2275,12 @@ "header_name": "{name}面板設定", "move_left": "向左移動視圖", "move_right": "向右移動視圖", - "tab_badges": "園標圖示", + "tab_badges": "圓標圖示", "tab_settings": "設定", - "tab_visibility": "能見度" + "tab_visibility": "顯示選項", + "visibility": { + "select_users": "選擇可觀看此面板的使用者" + } }, "header": "編輯 UI", "menu": { @@ -2321,7 +2326,7 @@ }, "view": { "panel_mode": { - "description": "將會以全寬繪製第一張卡,視圖中其他面板將不進行呈現。", + "description": "將會以全寬度製作第一張面板,視圖中其他面板將不進行呈現。", "title": "面板模式?" } } @@ -2352,6 +2357,7 @@ "existing_cards": "無法刪除內含面板的視圖,請先移除面板。" }, "warning": { + "attribute_not_found": "無法使用屬性 {attribute} 之物件:{entity}", "entity_non_numeric": "物件為非數字:{entity}", "entity_not_found": "物件不可用:{entity}" } diff --git a/webpack.config.js b/webpack.config.js index ff1b7462be..fdd9227cb9 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,8 +5,8 @@ const { isProdBuild, isStatsBuild } = require("./build-scripts/env.js"); const configs = [ createAppConfig({ - isProdBuild, - isStatsBuild, + isProdBuild: isProdBuild(), + isStatsBuild: isStatsBuild(), latestBuild: true, }), ]; @@ -14,8 +14,8 @@ const configs = [ if (isProdBuild && !isStatsBuild) { configs.push( createAppConfig({ - isProdBuild, - isStatsBuild, + isProdBuild: isProdBuild(), + isStatsBuild: isStatsBuild(), latestBuild: false, }) );