diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7fb68b29a0..84d8995cc5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -89,7 +89,7 @@ jobs: env: IS_TEST: "true" - name: Upload bundle stats - uses: actions/upload-artifact@v4.3.6 + uses: actions/upload-artifact@v4.4.0 with: name: frontend-bundle-stats path: build/stats/*.json @@ -113,7 +113,7 @@ jobs: env: IS_TEST: "true" - name: Upload bundle stats - uses: actions/upload-artifact@v4.3.6 + uses: actions/upload-artifact@v4.4.0 with: name: supervisor-bundle-stats path: build/stats/*.json diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 530361e404..805f7c5e94 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -57,14 +57,14 @@ jobs: run: tar -czvf translations.tar.gz translations - name: Upload build artifacts - uses: actions/upload-artifact@v4.3.6 + uses: actions/upload-artifact@v4.4.0 with: name: wheels path: dist/home_assistant_frontend*.whl if-no-files-found: error - name: Upload translations - uses: actions/upload-artifact@v4.3.6 + uses: actions/upload-artifact@v4.4.0 with: name: translations path: translations.tar.gz diff --git a/gallery/src/pages/misc/ha-markdown.markdown b/gallery/src/pages/misc/ha-markdown.markdown new file mode 100644 index 0000000000..4e1bd1b608 --- /dev/null +++ b/gallery/src/pages/misc/ha-markdown.markdown @@ -0,0 +1,3 @@ +--- +title: Markdown +--- diff --git a/gallery/src/pages/misc/ha-markdown.ts b/gallery/src/pages/misc/ha-markdown.ts new file mode 100644 index 0000000000..600a9a4f02 --- /dev/null +++ b/gallery/src/pages/misc/ha-markdown.ts @@ -0,0 +1,93 @@ +import { css, html, LitElement } from "lit"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-markdown"; + +import { customElement } from "lit/decorators"; + +interface MarkdownContent { + content: string; + breaks: boolean; + allowSvg: boolean; + lazyImages: boolean; +} + +const mdContentwithDefaults = (md: Partial) => + ({ + breaks: false, + allowSvg: false, + lazyImages: false, + ...md, + }) as MarkdownContent; + +const generateContent = (md) => ` +\`\`\`json +${JSON.stringify({ ...md, content: undefined })} +\`\`\` + +--- + +${md.content} +`; + +const markdownContents: MarkdownContent[] = [ + mdContentwithDefaults({ + content: "_Hello_ **there** 👋, ~~nice~~ of you ||to|| show up.", + }), + ...[true, false].map((breaks) => + mdContentwithDefaults({ + breaks, + content: ` +![image](https://img.shields.io/badge/markdown-rendering-brightgreen) +![image](https://img.shields.io/badge/markdown-rendering-blue) + +> [!TIP] +> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer dictum quis ante eu eleifend. Integer sed [consectetur est, nec elementum magna](#). Fusce lobortis lectus ac rutrum tincidunt. Quisque suscipit gravida ante, in convallis risus vulputate non. + +key | description +-- | -- +lorem | ipsum + +- list item 1 +- list item 2 + + + `, + }) + ), +]; + +@customElement("demo-misc-ha-markdown") +export class DemoMiscMarkdown extends LitElement { + protected render() { + return html` +
+ ${markdownContents.map( + (md) => + html` + + ` + )} +
+ `; + } + + static get styles() { + return css` + ha-card { + margin: 12px; + padding: 12px; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-misc-ha-markdown": DemoMiscMarkdown; + } +} diff --git a/package.json b/package.json index 38c7ae5a33..259a19d815 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "leaflet-draw": "1.0.4", "lit": "2.8.0", "luxon": "3.5.0", - "marked": "14.0.0", + "marked": "14.1.0", "memoize-one": "6.0.0", "node-vibrant": "3.2.1-alpha.1", "proxy-polyfill": "0.3.2", @@ -155,7 +155,7 @@ "@babel/plugin-transform-runtime": "7.25.4", "@babel/preset-env": "7.25.4", "@babel/preset-typescript": "7.24.7", - "@bundle-stats/plugin-webpack-filter": "4.14.2", + "@bundle-stats/plugin-webpack-filter": "4.15.0", "@koa/cors": "5.0.0", "@lokalise/node-api": "12.7.0", "@octokit/auth-oauth-device": "7.1.1", diff --git a/pyproject.toml b/pyproject.toml index 7bcb25bd93..51e7b1af14 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20240829.0" +version = "20240902.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" diff --git a/src/components/ha-markdown-element.ts b/src/components/ha-markdown-element.ts index 5ca7fa66b2..e291a54cce 100644 --- a/src/components/ha-markdown-element.ts +++ b/src/components/ha-markdown-element.ts @@ -96,7 +96,25 @@ class HaMarkdownElement extends ReactiveElement { haAlertNode.append( ...Array.from(node.childNodes) - .map((child) => Array.from(child.childNodes)) + .map((child) => { + const arr = Array.from(child.childNodes); + if (!this.breaks && arr.length) { + // When we are not breaking, the first line of the blockquote is not considered, + // so we need to adjust the first child text content + const firstChild = arr[0]; + if ( + firstChild.nodeType === Node.TEXT_NODE && + firstChild.textContent === gitHubAlertMatch.input && + firstChild.textContent?.includes("\n") + ) { + firstChild.textContent = firstChild.textContent + .split("\n") + .slice(1) + .join("\n"); + } + } + return arr; + }) .reduce((acc, val) => acc.concat(val), []) .filter( (childNode) => diff --git a/src/data/script_i18n.ts b/src/data/script_i18n.ts index 150afc4220..64852bc6e7 100644 --- a/src/data/script_i18n.ts +++ b/src/data/script_i18n.ts @@ -127,6 +127,12 @@ const tryDescribeAction = ( targets.push( computeEntityRegistryName(hass, entityReg) || targetThing ); + } else if (targetThing === "all") { + targets.push( + hass.localize( + `${actionTranslationBaseKey}.service.description.target_every_entity` + ) + ); } else { targets.push( hass.localize( diff --git a/src/panels/lovelace/badges/hui-entity-badge.ts b/src/panels/lovelace/badges/hui-entity-badge.ts index af5b05e3e9..39aa9a193f 100644 --- a/src/panels/lovelace/badges/hui-entity-badge.ts +++ b/src/panels/lovelace/badges/hui-entity-badge.ts @@ -351,7 +351,7 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge { .badge.no-info { padding: 0; } - .badge:not(.no-icon) img { + .badge:not(.no-icon):not(.no-info) img { margin-left: -6px; margin-inline-start: -6px; margin-inline-end: initial; diff --git a/src/panels/lovelace/sections/hui-grid-section.ts b/src/panels/lovelace/sections/hui-grid-section.ts index 349521228b..8378b3e173 100644 --- a/src/panels/lovelace/sections/hui-grid-section.ts +++ b/src/panels/lovelace/sections/hui-grid-section.ts @@ -48,7 +48,10 @@ export class GridSection extends LitElement implements LovelaceSectionElement { private _cardConfigKeys = new WeakMap(); private _getKey(cardConfig: LovelaceCardConfig) { - if (!this._cardConfigKeys.has(cardConfig)) { + if ( + !this._cardConfigKeys.has(cardConfig) && + typeof cardConfig === "object" + ) { this._cardConfigKeys.set(cardConfig, Math.random().toString()); } return this._cardConfigKeys.get(cardConfig)!; @@ -62,20 +65,6 @@ export class GridSection extends LitElement implements LovelaceSectionElement { const editMode = Boolean(this.lovelace?.editMode && !this.isStrategy); return html` - ${this._config.title || this.lovelace?.editMode - ? html` -

- ${this._config.title || - this.hass.localize( - "ui.panel.lovelace.editor.section.unnamed_section" - )} -

- ` - : nothing} - ${editMode - ? html` -
-
- - - -
-
- ` - : nothing} - ${section} + ${ + sectionConfig?.title || this.lovelace?.editMode + ? html` +
+

+ ${sectionConfig?.title || + this.hass.localize( + "ui.panel.lovelace.editor.section.unnamed_section" + )} +

+ ${editMode + ? html` +
+ + + +
+ ` + : nothing} +
+ ` + : nothing + } + ${section} + `; } @@ -346,25 +367,6 @@ export class SectionsView extends LitElement implements LovelaceViewElement { } } - .section-actions { - position: absolute; - top: 0; - right: 0; - inset-inline-end: 0; - inset-inline-start: initial; - opacity: 1; - display: flex; - align-items: center; - justify-content: center; - transition: opacity 0.2s ease-in-out; - background-color: rgba(var(--rgb-card-background-color), 0.3); - border-radius: 18px; - background: var(--secondary-background-color); - --mdc-icon-button-size: 36px; - --mdc-icon-size: 20px; - color: var(--primary-text-color); - } - .handle { cursor: grab; padding: 8px; @@ -396,6 +398,55 @@ export class SectionsView extends LitElement implements LovelaceViewElement { margin: 16px 8px; text-align: center; } + + .section-header { + position: relative; + height: var(--row-height); + margin-bottom: var(--row-gap); + display: flex; + flex-direction: column; + justify-content: flex-end; + } + + .section-title { + color: var(--primary-text-color); + font-size: 20px; + font-weight: normal; + margin: 0px; + letter-spacing: 0.1px; + line-height: 32px; + text-align: var(--ha-view-sections-title-text-align, start); + min-height: 32px; + box-sizing: border-box; + padding: 0 10px 10px; + } + + .section-title.placeholder { + color: var(--secondary-text-color); + font-style: italic; + } + + .section-actions { + position: absolute; + height: 36px; + bottom: calc(-1 * var(--row-gap) - 2px); + right: 0; + inset-inline-end: 0; + inset-inline-start: initial; + opacity: 1; + display: flex; + align-items: center; + justify-content: center; + transition: opacity 0.2s ease-in-out; + background-color: rgba(var(--rgb-card-background-color), 0.3); + border-radius: var(--ha-card-border-radius, 12px); + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; + background: var(--secondary-background-color); + --mdc-icon-button-size: 36px; + --mdc-icon-size: 20px; + color: var(--primary-text-color); + } `; } } diff --git a/src/translations/en.json b/src/translations/en.json index 97fd46952d..19e2c300ff 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3311,6 +3311,7 @@ "service_name_no_targets": "{domain} ''{name}''", "service": "Perform an action", "target_template": "templated {name}", + "target_every_entity": "every entity", "target_unknown_entity": "unknown entity", "target_unknown_device": "unknown device", "target_unknown_area": "unknown area", diff --git a/yarn.lock b/yarn.lock index a630b71858..0d2e5b0db9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1432,12 +1432,12 @@ __metadata: languageName: node linkType: hard -"@bundle-stats/plugin-webpack-filter@npm:4.14.2": - version: 4.14.2 - resolution: "@bundle-stats/plugin-webpack-filter@npm:4.14.2" +"@bundle-stats/plugin-webpack-filter@npm:4.15.0": + version: 4.15.0 + resolution: "@bundle-stats/plugin-webpack-filter@npm:4.15.0" peerDependencies: core-js: ^3.0.0 - checksum: 10/5024a0babd398e66f4a973f41ee521ab756bdb3d35ea994c2a6e4bf9e6fca28715a53e1431e1acd5837debc129ea9e784509d8afafb3ba082a397caaed570225 + checksum: 10/5b3bbc133ccc50a4f2e6e8c7e6e10dacc1c85cb176e934965777a8f1ca7b137707b704b77858d8c4fa9d665a92e0cfc99b1d07791d08c522e023fb3c39d8a705 languageName: node linkType: hard @@ -8920,7 +8920,7 @@ __metadata: "@babel/preset-typescript": "npm:7.24.7" "@babel/runtime": "npm:7.25.4" "@braintree/sanitize-url": "npm:7.1.0" - "@bundle-stats/plugin-webpack-filter": "npm:4.14.2" + "@bundle-stats/plugin-webpack-filter": "npm:4.15.0" "@codemirror/autocomplete": "npm:6.18.0" "@codemirror/commands": "npm:6.6.0" "@codemirror/language": "npm:6.10.2" @@ -9078,7 +9078,7 @@ __metadata: luxon: "npm:3.5.0" magic-string: "npm:0.30.11" map-stream: "npm:0.0.7" - marked: "npm:14.0.0" + marked: "npm:14.1.0" memoize-one: "npm:6.0.0" mocha: "npm:10.5.0" node-vibrant: "npm:3.2.1-alpha.1" @@ -10858,12 +10858,12 @@ __metadata: languageName: node linkType: hard -"marked@npm:14.0.0": - version: 14.0.0 - resolution: "marked@npm:14.0.0" +"marked@npm:14.1.0": + version: 14.1.0 + resolution: "marked@npm:14.1.0" bin: marked: bin/marked.js - checksum: 10/5f69e58e177bde75fb6145127c939c096d05494c5dff92d73af8a550097d6631c82e919f8a375e3743379a2623418151086b925a902a2be123590b887716f2bb + checksum: 10/1a930dd87a3994cc4fcc72c4668c548429d0e6363b8f7660193c106fa1cadcde5c813cfc3fe4be42b9f18b3652ba73469fb6c718215b0dbf8ddb9de2d1f5ab38 languageName: node linkType: hard