mirror of
				https://github.com/home-assistant/frontend.git
				synced 2025-10-31 06:29:43 +00:00 
			
		
		
		
	Compare commits
	
		
			8 Commits
		
	
	
		
			websocket_
			...
			card_edito
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 76380c189b | ||
|   | c8b7f373c3 | ||
|   | 4f2652abd2 | ||
|   | 6d8b7f6995 | ||
|   | bbf8a8e3e7 | ||
|   | 14308c9057 | ||
|   | b87f44ff74 | ||
|   | 36540aa8fb | 
| @@ -4,12 +4,13 @@ | ||||
| # - released in the last year + current alpha/beta versions | ||||
| # - Firefox extended support release (ESR) | ||||
| # - with global utilization at or above 0.5% | ||||
| # - exclude dead browsers (no security maintenance for 2+ years) | ||||
| # - must support dynamic import of ES modules | ||||
| # - exclude browsers no longer being maintained | ||||
| # - exclude KaiOS, QQ, and UC browsers due to lack of sufficient feature support data | ||||
| unreleased versions | ||||
| last 1 year | ||||
| Firefox ESR | ||||
| >= 0.5% | ||||
| >= 0.5% and supports es6-module-dynamic-import | ||||
| not dead | ||||
| not KaiOS > 0 | ||||
| not QQAndroid > 0 | ||||
| @@ -19,18 +20,23 @@ not UCAndroid > 0 | ||||
| # Legacy builds are served when modern requirements are not met and support browsers: | ||||
| # - released in the last 7 years + current alpha/beta versionss | ||||
| # - with global utilization at or above 0.05% | ||||
| # - exclude dead browsers (no security maintenance for 2+ years) | ||||
| # - exclude Opera Mini which does not support web sockets | ||||
| # The lattermost query ensures that support for popular old browsers is not dropped too early | ||||
| # (e.g. IE 11, Android 4.4, or Samsung 4). | ||||
| # | ||||
| # In addition, legacy browsers must support some minimum features that cannot be polyfilled: | ||||
| # - ES5 (strict mode) | ||||
| # - web sockets to communicate with backend | ||||
| # - inline SVG used widely in buttons, widgets, etc. | ||||
| # - custom events used for most user interactions | ||||
| # - CSS flexbox used in the majority of the layout | ||||
| # Nearly all of these are redundant with the above rules. | ||||
| # As of May 2023, only web sockets must be added to the query. | ||||
| unreleased versions | ||||
| last 7 years | ||||
| >= 0.05% | ||||
| not dead | ||||
| not op_mini all | ||||
| >= 0.05% and supports websockets | ||||
|  | ||||
| [legacy-sw] | ||||
| # Same as legacy plus supports service workers | ||||
| unreleased versions | ||||
| last 7 years | ||||
| >= 0.05% and supports serviceworkers | ||||
| not dead | ||||
| not op_mini all | ||||
| >= 0.05% and supports websockets and supports serviceworkers | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| FROM mcr.microsoft.com/devcontainers/python:1-3.13 | ||||
| # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.148.1/containers/python-3/.devcontainer/base.Dockerfile | ||||
| FROM mcr.microsoft.com/devcontainers/python:3.12 | ||||
|  | ||||
| ENV \ | ||||
|   DEBIAN_FRONTEND=noninteractive \ | ||||
|   | ||||
							
								
								
									
										130
									
								
								.eslintrc.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								.eslintrc.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | ||||
| { | ||||
|   "extends": [ | ||||
|     "airbnb-base", | ||||
|     "airbnb-typescript/base", | ||||
|     "plugin:@typescript-eslint/recommended", | ||||
|     "plugin:wc/recommended", | ||||
|     "plugin:lit/all", | ||||
|     "plugin:lit-a11y/recommended", | ||||
|     "prettier" | ||||
|   ], | ||||
|   "parser": "@typescript-eslint/parser", | ||||
|   "parserOptions": { | ||||
|     "ecmaVersion": 2020, | ||||
|     "ecmaFeatures": { | ||||
|       "modules": true | ||||
|     }, | ||||
|     "sourceType": "module", | ||||
|     "project": "./tsconfig.json" | ||||
|   }, | ||||
|   "settings": { | ||||
|     "import/resolver": { | ||||
|       "webpack": { | ||||
|         "config": "./webpack.config.cjs" | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "globals": { | ||||
|     "__DEV__": false, | ||||
|     "__DEMO__": false, | ||||
|     "__BUILD__": false, | ||||
|     "__VERSION__": false, | ||||
|     "__STATIC_PATH__": false, | ||||
|     "__SUPERVISOR__": false, | ||||
|     "Polymer": true | ||||
|   }, | ||||
|   "env": { | ||||
|     "browser": true, | ||||
|     "es6": true | ||||
|   }, | ||||
|   "rules": { | ||||
|     "class-methods-use-this": "off", | ||||
|     "new-cap": "off", | ||||
|     "prefer-template": "off", | ||||
|     "object-shorthand": "off", | ||||
|     "func-names": "off", | ||||
|     "no-underscore-dangle": "off", | ||||
|     "strict": "off", | ||||
|     "no-plusplus": "off", | ||||
|     "no-bitwise": "error", | ||||
|     "comma-dangle": "off", | ||||
|     "vars-on-top": "off", | ||||
|     "no-continue": "off", | ||||
|     "no-param-reassign": "off", | ||||
|     "no-multi-assign": "off", | ||||
|     "no-console": "error", | ||||
|     "radix": "off", | ||||
|     "no-alert": "off", | ||||
|     "no-nested-ternary": "off", | ||||
|     "prefer-destructuring": "off", | ||||
|     "no-restricted-globals": [2, "event"], | ||||
|     "prefer-promise-reject-errors": "off", | ||||
|     "import/prefer-default-export": "off", | ||||
|     "import/no-default-export": "off", | ||||
|     "import/no-unresolved": "off", | ||||
|     "import/no-cycle": "off", | ||||
|     "import/extensions": [ | ||||
|       "error", | ||||
|       "ignorePackages", | ||||
|       { | ||||
|         "ts": "never", | ||||
|         "js": "never" | ||||
|       } | ||||
|     ], | ||||
|     "no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"], | ||||
|     "object-curly-newline": "off", | ||||
|     "default-case": "off", | ||||
|     "wc/no-self-class": "off", | ||||
|     "no-shadow": "off", | ||||
|     "@typescript-eslint/camelcase": "off", | ||||
|     "@typescript-eslint/ban-ts-comment": "off", | ||||
|     "@typescript-eslint/no-use-before-define": "off", | ||||
|     "@typescript-eslint/no-non-null-assertion": "off", | ||||
|     "@typescript-eslint/no-explicit-any": "off", | ||||
|     "@typescript-eslint/explicit-function-return-type": "off", | ||||
|     "@typescript-eslint/explicit-module-boundary-types": "off", | ||||
|     "@typescript-eslint/no-shadow": ["error"], | ||||
|     "@typescript-eslint/naming-convention": [ | ||||
|       "off", | ||||
|       { | ||||
|         "selector": "default", | ||||
|         "format": ["camelCase", "snake_case"], | ||||
|         "leadingUnderscore": "allow", | ||||
|         "trailingUnderscore": "allow" | ||||
|       }, | ||||
|       { | ||||
|         "selector": ["variable"], | ||||
|         "format": ["camelCase", "snake_case", "UPPER_CASE"], | ||||
|         "leadingUnderscore": "allow", | ||||
|         "trailingUnderscore": "allow" | ||||
|       }, | ||||
|       { | ||||
|         "selector": "typeLike", | ||||
|         "format": ["PascalCase"] | ||||
|       } | ||||
|     ], | ||||
|     "@typescript-eslint/no-unused-vars": "off", | ||||
|     "unused-imports/no-unused-vars": [ | ||||
|       "error", | ||||
|       { | ||||
|         "vars": "all", | ||||
|         "varsIgnorePattern": "^_", | ||||
|         "args": "after-used", | ||||
|         "argsIgnorePattern": "^_", | ||||
|         "ignoreRestSiblings": true | ||||
|       } | ||||
|     ], | ||||
|     "unused-imports/no-unused-imports": "error", | ||||
|     "lit/attribute-names": "warn", | ||||
|     "lit/attribute-value-entities": "off", | ||||
|     "lit/no-template-map": "off", | ||||
|     "lit/no-native-attributes": "warn", | ||||
|     "lit/no-this-assign-in-render": "warn", | ||||
|     "lit-a11y/click-events-have-key-events": ["off"], | ||||
|     "lit-a11y/no-autofocus": "off", | ||||
|     "lit-a11y/alt-text": "warn", | ||||
|     "lit-a11y/anchor-is-valid": "warn", | ||||
|     "lit-a11y/role-has-required-aria-attrs": "warn" | ||||
|   }, | ||||
|   "plugins": ["unused-imports"] | ||||
| } | ||||
							
								
								
									
										4
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
								
							| @@ -7,7 +7,7 @@ body: | ||||
|       value: | | ||||
|         Make sure you are running the [latest version of Home Assistant][releases] before reporting an issue. | ||||
|  | ||||
|         If you have a feature or enhancement request for the frontend, please [start a discussion][fr] instead of creating an issue. | ||||
|         If you have a feature or enhancement request for the frontend, please [start an discussion][fr] instead of creating an issue. | ||||
|  | ||||
|         **Please do not report issues for custom cards.** | ||||
|  | ||||
| @@ -74,7 +74,7 @@ body: | ||||
|         If known, otherwise leave blank. | ||||
|   - type: input | ||||
|     attributes: | ||||
|       label: In which browser are you experiencing the issue? | ||||
|       label: In which browser are you experiencing the issue with? | ||||
|       placeholder: Google Chrome 88.0.4324.150 | ||||
|       description: > | ||||
|         Provide the full name and don't forget to add the version! | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							| @@ -2,7 +2,7 @@ blank_issues_enabled: false | ||||
| contact_links: | ||||
|   - name: Request a feature for the UI / Dashboards | ||||
|     url: https://github.com/home-assistant/frontend/discussions/category_choices | ||||
|     about: Request a new feature for the Home Assistant frontend. | ||||
|     about: Request an new feature for the Home Assistant frontend. | ||||
|   - name: Report a bug that is NOT related to the UI / Dashboards | ||||
|     url: https://github.com/home-assistant/core/issues | ||||
|     about: This is the issue tracker for our frontend. Please report other issues in the backend ("core") repository. | ||||
|   | ||||
							
								
								
									
										8
									
								
								.github/workflows/cast_deployment.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/cast_deployment.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -21,12 +21,12 @@ jobs: | ||||
|       url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} | ||||
|     steps: | ||||
|       - name: Check out files from GitHub | ||||
|         uses: actions/checkout@v4.2.2 | ||||
|         uses: actions/checkout@v4.1.7 | ||||
|         with: | ||||
|           ref: dev | ||||
|  | ||||
|       - name: Setup Node | ||||
|         uses: actions/setup-node@v4.2.0 | ||||
|         uses: actions/setup-node@v4.0.3 | ||||
|         with: | ||||
|           node-version-file: ".nvmrc" | ||||
|           cache: yarn | ||||
| @@ -57,12 +57,12 @@ jobs: | ||||
|       url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} | ||||
|     steps: | ||||
|       - name: Check out files from GitHub | ||||
|         uses: actions/checkout@v4.2.2 | ||||
|         uses: actions/checkout@v4.1.7 | ||||
|         with: | ||||
|           ref: master | ||||
|  | ||||
|       - name: Setup Node | ||||
|         uses: actions/setup-node@v4.2.0 | ||||
|         uses: actions/setup-node@v4.0.3 | ||||
|         with: | ||||
|           node-version-file: ".nvmrc" | ||||
|           cache: yarn | ||||
|   | ||||
							
								
								
									
										22
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -24,9 +24,9 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Check out files from GitHub | ||||
|         uses: actions/checkout@v4.2.2 | ||||
|         uses: actions/checkout@v4.1.7 | ||||
|       - name: Setup Node | ||||
|         uses: actions/setup-node@v4.2.0 | ||||
|         uses: actions/setup-node@v4.0.3 | ||||
|         with: | ||||
|           node-version-file: ".nvmrc" | ||||
|           cache: yarn | ||||
| @@ -37,7 +37,7 @@ jobs: | ||||
|       - name: Build resources | ||||
|         run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages | ||||
|       - name: Setup lint cache | ||||
|         uses: actions/cache@v4.2.0 | ||||
|         uses: actions/cache@v4.0.2 | ||||
|         with: | ||||
|           path: | | ||||
|             node_modules/.cache/prettier | ||||
| @@ -58,9 +58,9 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Check out files from GitHub | ||||
|         uses: actions/checkout@v4.2.2 | ||||
|         uses: actions/checkout@v4.1.7 | ||||
|       - name: Setup Node | ||||
|         uses: actions/setup-node@v4.2.0 | ||||
|         uses: actions/setup-node@v4.0.3 | ||||
|         with: | ||||
|           node-version-file: ".nvmrc" | ||||
|           cache: yarn | ||||
| @@ -76,9 +76,9 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Check out files from GitHub | ||||
|         uses: actions/checkout@v4.2.2 | ||||
|         uses: actions/checkout@v4.1.7 | ||||
|       - name: Setup Node | ||||
|         uses: actions/setup-node@v4.2.0 | ||||
|         uses: actions/setup-node@v4.0.3 | ||||
|         with: | ||||
|           node-version-file: ".nvmrc" | ||||
|           cache: yarn | ||||
| @@ -89,7 +89,7 @@ jobs: | ||||
|         env: | ||||
|           IS_TEST: "true" | ||||
|       - name: Upload bundle stats | ||||
|         uses: actions/upload-artifact@v4.6.0 | ||||
|         uses: actions/upload-artifact@v4.4.0 | ||||
|         with: | ||||
|           name: frontend-bundle-stats | ||||
|           path: build/stats/*.json | ||||
| @@ -100,9 +100,9 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Check out files from GitHub | ||||
|         uses: actions/checkout@v4.2.2 | ||||
|         uses: actions/checkout@v4.1.7 | ||||
|       - name: Setup Node | ||||
|         uses: actions/setup-node@v4.2.0 | ||||
|         uses: actions/setup-node@v4.0.3 | ||||
|         with: | ||||
|           node-version-file: ".nvmrc" | ||||
|           cache: yarn | ||||
| @@ -113,7 +113,7 @@ jobs: | ||||
|         env: | ||||
|           IS_TEST: "true" | ||||
|       - name: Upload bundle stats | ||||
|         uses: actions/upload-artifact@v4.6.0 | ||||
|         uses: actions/upload-artifact@v4.4.0 | ||||
|         with: | ||||
|           name: supervisor-bundle-stats | ||||
|           path: build/stats/*.json | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							| @@ -23,7 +23,7 @@ jobs: | ||||
|  | ||||
|     steps: | ||||
|       - name: Checkout repository | ||||
|         uses: actions/checkout@v4.2.2 | ||||
|         uses: actions/checkout@v4.1.7 | ||||
|         with: | ||||
|           # We must fetch at least the immediate parents so that if this is | ||||
|           # a pull request then we can checkout the head. | ||||
|   | ||||
							
								
								
									
										8
									
								
								.github/workflows/demo_deployment.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/demo_deployment.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -22,12 +22,12 @@ jobs: | ||||
|       url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} | ||||
|     steps: | ||||
|       - name: Check out files from GitHub | ||||
|         uses: actions/checkout@v4.2.2 | ||||
|         uses: actions/checkout@v4.1.7 | ||||
|         with: | ||||
|           ref: dev | ||||
|  | ||||
|       - name: Setup Node | ||||
|         uses: actions/setup-node@v4.2.0 | ||||
|         uses: actions/setup-node@v4.0.3 | ||||
|         with: | ||||
|           node-version-file: ".nvmrc" | ||||
|           cache: yarn | ||||
| @@ -58,12 +58,12 @@ jobs: | ||||
|       url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} | ||||
|     steps: | ||||
|       - name: Check out files from GitHub | ||||
|         uses: actions/checkout@v4.2.2 | ||||
|         uses: actions/checkout@v4.1.7 | ||||
|         with: | ||||
|           ref: master | ||||
|  | ||||
|       - name: Setup Node | ||||
|         uses: actions/setup-node@v4.2.0 | ||||
|         uses: actions/setup-node@v4.0.3 | ||||
|         with: | ||||
|           node-version-file: ".nvmrc" | ||||
|           cache: yarn | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/design_deployment.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/design_deployment.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -16,10 +16,10 @@ jobs: | ||||
|       url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }} | ||||
|     steps: | ||||
|       - name: Check out files from GitHub | ||||
|         uses: actions/checkout@v4.2.2 | ||||
|         uses: actions/checkout@v4.1.7 | ||||
|  | ||||
|       - name: Setup Node | ||||
|         uses: actions/setup-node@v4.2.0 | ||||
|         uses: actions/setup-node@v4.0.3 | ||||
|         with: | ||||
|           node-version-file: ".nvmrc" | ||||
|           cache: yarn | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/design_preview.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/design_preview.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -21,10 +21,10 @@ jobs: | ||||
|     if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview') | ||||
|     steps: | ||||
|       - name: Check out files from GitHub | ||||
|         uses: actions/checkout@v4.2.2 | ||||
|         uses: actions/checkout@v4.1.7 | ||||
|  | ||||
|       - name: Setup Node | ||||
|         uses: actions/setup-node@v4.2.0 | ||||
|         uses: actions/setup-node@v4.0.3 | ||||
|         with: | ||||
|           node-version-file: ".nvmrc" | ||||
|           cache: yarn | ||||
|   | ||||
							
								
								
									
										10
									
								
								.github/workflows/nightly.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/nightly.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -6,7 +6,7 @@ on: | ||||
|     - cron: "0 1 * * *" | ||||
|  | ||||
| env: | ||||
|   PYTHON_VERSION: "3.13" | ||||
|   PYTHON_VERSION: "3.12" | ||||
|   NODE_OPTIONS: --max_old_space_size=6144 | ||||
|  | ||||
| permissions: | ||||
| @@ -20,7 +20,7 @@ jobs: | ||||
|       contents: write | ||||
|     steps: | ||||
|       - name: Checkout the repository | ||||
|         uses: actions/checkout@v4.2.2 | ||||
|         uses: actions/checkout@v4.1.7 | ||||
|  | ||||
|       - name: Set up Python ${{ env.PYTHON_VERSION }} | ||||
|         uses: actions/setup-python@v5 | ||||
| @@ -28,7 +28,7 @@ jobs: | ||||
|           python-version: ${{ env.PYTHON_VERSION }} | ||||
|  | ||||
|       - name: Setup Node | ||||
|         uses: actions/setup-node@v4.2.0 | ||||
|         uses: actions/setup-node@v4.0.3 | ||||
|         with: | ||||
|           node-version-file: ".nvmrc" | ||||
|           cache: yarn | ||||
| @@ -57,14 +57,14 @@ jobs: | ||||
|         run: tar -czvf translations.tar.gz translations | ||||
|  | ||||
|       - name: Upload build artifacts | ||||
|         uses: actions/upload-artifact@v4.6.0 | ||||
|         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.6.0 | ||||
|         uses: actions/upload-artifact@v4.4.0 | ||||
|         with: | ||||
|           name: translations | ||||
|           path: translations.tar.gz | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/relative-ci.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/relative-ci.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -17,7 +17,7 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Send bundle stats and build information to RelativeCI | ||||
|         uses: relative-ci/agent-action@v2.1.14 | ||||
|         uses: relative-ci/agent-action@v2.1.12 | ||||
|         with: | ||||
|           key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }} | ||||
|           token: ${{ github.token }} | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/release-drafter.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/release-drafter.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -18,6 +18,6 @@ jobs: | ||||
|       pull-requests: read | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: release-drafter/release-drafter@v6.1.0 | ||||
|       - uses: release-drafter/release-drafter@v6.0.0 | ||||
|         env: | ||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
|   | ||||
							
								
								
									
										76
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										76
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -6,7 +6,7 @@ on: | ||||
|       - published | ||||
|  | ||||
| env: | ||||
|   PYTHON_VERSION: "3.13" | ||||
|   PYTHON_VERSION: "3.12" | ||||
|   NODE_OPTIONS: --max_old_space_size=6144 | ||||
|  | ||||
| # Set default workflow permissions | ||||
| @@ -23,18 +23,18 @@ jobs: | ||||
|       contents: write # Required to upload release assets | ||||
|     steps: | ||||
|       - name: Checkout the repository | ||||
|         uses: actions/checkout@v4.2.2 | ||||
|         uses: actions/checkout@v4.1.7 | ||||
|  | ||||
|       - name: Verify version | ||||
|         uses: home-assistant/actions/helpers/verify-version@master | ||||
|  | ||||
|       - name: Set up Python ${{ env.PYTHON_VERSION }} | ||||
|         uses: actions/setup-python@v5 | ||||
|         with: | ||||
|           python-version: ${{ env.PYTHON_VERSION }} | ||||
|  | ||||
|       - name: Verify version | ||||
|         uses: home-assistant/actions/helpers/verify-version@master | ||||
|  | ||||
|       - name: Setup Node | ||||
|         uses: actions/setup-node@v4.2.0 | ||||
|         uses: actions/setup-node@v4.0.3 | ||||
|         with: | ||||
|           node-version-file: ".nvmrc" | ||||
|           cache: yarn | ||||
| @@ -55,7 +55,7 @@ jobs: | ||||
|           script/release | ||||
|  | ||||
|       - name: Upload release assets | ||||
|         uses: softprops/action-gh-release@v2.2.1 | ||||
|         uses: softprops/action-gh-release@v2.0.8 | ||||
|         with: | ||||
|           files: | | ||||
|             dist/*.whl | ||||
| @@ -74,68 +74,10 @@ jobs: | ||||
|           echo "home-assistant-frontend==$version" > ./requirements.txt | ||||
|  | ||||
|       - name: Build wheels | ||||
|         uses: home-assistant/wheels@2024.11.0 | ||||
|         uses: home-assistant/wheels@2024.07.1 | ||||
|         with: | ||||
|           abi: cp313 | ||||
|           abi: cp312 | ||||
|           tag: musllinux_1_2 | ||||
|           arch: amd64 | ||||
|           wheels-key: ${{ secrets.WHEELS_KEY }} | ||||
|           requirements: "requirements.txt" | ||||
|  | ||||
|   release-landing-page: | ||||
|     name: Release landing-page frontend | ||||
|     if: github.event.release.prerelease == false | ||||
|     runs-on: ubuntu-latest | ||||
|     permissions: | ||||
|       contents: write # Required to upload release assets | ||||
|     steps: | ||||
|       - name: Checkout the repository | ||||
|         uses: actions/checkout@v4.2.2 | ||||
|       - name: Setup Node | ||||
|         uses: actions/setup-node@v4.2.0 | ||||
|         with: | ||||
|           node-version-file: ".nvmrc" | ||||
|           cache: yarn | ||||
|       - name: Install dependencies | ||||
|         run: yarn install | ||||
|       - name: Download Translations | ||||
|         run: ./script/translations_download | ||||
|         env: | ||||
|           LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }} | ||||
|       - name: Build landing-page | ||||
|         run: landing-page/script/build_landing_page | ||||
|       - name: Tar folder | ||||
|         run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist . | ||||
|       - name: Upload release asset | ||||
|         uses: softprops/action-gh-release@v2.2.1 | ||||
|         with: | ||||
|           files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz | ||||
|  | ||||
|   release-supervisor: | ||||
|     name: Release supervisor frontend | ||||
|     if: github.event.release.prerelease == false | ||||
|     runs-on: ubuntu-latest | ||||
|     permissions: | ||||
|       contents: write # Required to upload release assets | ||||
|     steps: | ||||
|       - name: Checkout the repository | ||||
|         uses: actions/checkout@v4.2.2 | ||||
|       - name: Setup Node | ||||
|         uses: actions/setup-node@v4.2.0 | ||||
|         with: | ||||
|           node-version-file: ".nvmrc" | ||||
|           cache: yarn | ||||
|       - name: Install dependencies | ||||
|         run: yarn install | ||||
|       - name: Download Translations | ||||
|         run: ./script/translations_download | ||||
|         env: | ||||
|           LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }} | ||||
|       - name: Build supervisor | ||||
|         run: hassio/script/build_hassio | ||||
|       - name: Tar folder | ||||
|         run: tar -czf hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz -C hassio/build . | ||||
|       - name: Upload release asset | ||||
|         uses: softprops/action-gh-release@v2.2.1 | ||||
|         with: | ||||
|           files: hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							| @@ -10,7 +10,7 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: 90 days stale policy | ||||
|         uses: actions/stale@v9.1.0 | ||||
|         uses: actions/stale@v9.0.0 | ||||
|         with: | ||||
|           repo-token: ${{ secrets.GITHUB_TOKEN }} | ||||
|           days-before-stale: 90 | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/translations.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/translations.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -13,7 +13,7 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout the repository | ||||
|         uses: actions/checkout@v4.2.2 | ||||
|         uses: actions/checkout@v4.1.7 | ||||
|  | ||||
|       - name: Upload Translations | ||||
|         run: | | ||||
|   | ||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -50,6 +50,3 @@ src/cast/dev_const.ts | ||||
|  | ||||
| # Jetbrains | ||||
| /.idea/ | ||||
|  | ||||
| # test coverage | ||||
| test/coverage/ | ||||
|   | ||||
							
								
								
									
										3
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							| @@ -4,7 +4,6 @@ | ||||
|     "esbenp.prettier-vscode", | ||||
|     "runem.lit-plugin", | ||||
|     "github.vscode-pull-request-github", | ||||
|     "eamodio.gitlens", | ||||
|     "vitest.explorer" | ||||
|     "eamodio.gitlens" | ||||
|   ] | ||||
| } | ||||
|   | ||||
							
								
								
									
										32
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							| @@ -100,38 +100,6 @@ | ||||
|         "instanceLimit": 1 | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "label": "Develop Landing Page", | ||||
|       "type": "gulp", | ||||
|       "task": "develop-landing-page", | ||||
|       "problemMatcher": { | ||||
|         "owner": "ha-build", | ||||
|         "source": "ha-build", | ||||
|         "fileLocation": "absolute", | ||||
|         "severity": "error", | ||||
|         "pattern": [ | ||||
|           { | ||||
|             "regexp": "(SyntaxError): (.+): (.+) \\((\\d+):(\\d+)\\)", | ||||
|             "severity": 1, | ||||
|             "file": 2, | ||||
|             "message": 3, | ||||
|             "line": 4, | ||||
|             "column": 5 | ||||
|           } | ||||
|         ], | ||||
|         "background": { | ||||
|           "activeOnStart": true, | ||||
|           "beginsPattern": "Changes detected. Starting compilation", | ||||
|           "endsPattern": "Build done @" | ||||
|         } | ||||
|       }, | ||||
|  | ||||
|       "isBackground": true, | ||||
|       "group": "build", | ||||
|       "runOptions": { | ||||
|         "instanceLimit": 1 | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "label": "Develop Demo", | ||||
|       "type": "gulp", | ||||
|   | ||||
							
								
								
									
										925
									
								
								.yarn/releases/yarn-4.4.1.cjs
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										925
									
								
								.yarn/releases/yarn-4.4.1.cjs
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										934
									
								
								.yarn/releases/yarn-4.6.0.cjs
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										934
									
								
								.yarn/releases/yarn-4.6.0.cjs
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -6,4 +6,4 @@ enableGlobalCache: false | ||||
|  | ||||
| nodeLinker: node-modules | ||||
|  | ||||
| yarnPath: .yarn/releases/yarn-4.6.0.cjs | ||||
| yarnPath: .yarn/releases/yarn-4.4.1.cjs | ||||
|   | ||||
| @@ -27,5 +27,3 @@ A complete guide can be found at the following [link](https://www.home-assistant | ||||
| Home Assistant is open-source and Apache 2 licensed. Feel free to browse the repository, learn and reuse parts in your own projects. | ||||
|  | ||||
| We use [BrowserStack](https://www.browserstack.com) to test Home Assistant on a large variety of devices. | ||||
|  | ||||
| [](https://www.openhomefoundation.org/) | ||||
|   | ||||
							
								
								
									
										12
									
								
								build-scripts/.eslintrc.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								build-scripts/.eslintrc.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| { | ||||
|   "extends": "../.eslintrc.json", | ||||
|   "rules": { | ||||
|     "no-console": "off", | ||||
|     "import/no-extraneous-dependencies": "off", | ||||
|     "import/extensions": "off", | ||||
|     "import/no-dynamic-require": "off", | ||||
|     "global-require": "off", | ||||
|     "@typescript-eslint/no-var-requires": "off", | ||||
|     "prefer-arrow-callback": "off" | ||||
|   } | ||||
| } | ||||
| @@ -15,7 +15,7 @@ The Home Assistant build pipeline contains various steps to prepare a build. | ||||
|  | ||||
| Currently in Home Assistant we use a bundler to convert TypeScript, CSS and JSON files to JavaScript files that the browser understands. | ||||
|  | ||||
| We currently rely on Webpack. Both of these programs bundle the converted files in both production and development. | ||||
| We currently rely on Webpack but also have experimental Rollup support. Both of these programs bundle the converted files in both production and development. | ||||
|  | ||||
| For development, bundling is optional. We just want to get the right files in the browser. | ||||
|  | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import paths from "../paths.cjs"; | ||||
| const POLYFILL_DIR = join(paths.polymer_dir, "src/resources/polyfills"); | ||||
|  | ||||
| // List of polyfill keys with supported browser targets for the functionality | ||||
| const polyfillSupport = { | ||||
| const PolyfillSupport = { | ||||
|   // Note states and shadowRoot properties should be supported. | ||||
|   "element-internals": { | ||||
|     android: 90, | ||||
| @@ -18,6 +18,17 @@ const polyfillSupport = { | ||||
|     safari: 17.4, | ||||
|     samsung: 15.0, | ||||
|   }, | ||||
|   "element-append": { | ||||
|     android: 54, | ||||
|     chrome: 54, | ||||
|     edge: 17, | ||||
|     firefox: 49, | ||||
|     ios: 10.0, | ||||
|     opera: 41, | ||||
|     opera_mobile: 41, | ||||
|     safari: 10.0, | ||||
|     samsung: 6.0, | ||||
|   }, | ||||
|   "element-getattributenames": { | ||||
|     android: 61, | ||||
|     chrome: 61, | ||||
| @@ -40,18 +51,27 @@ const polyfillSupport = { | ||||
|     safari: 12.0, | ||||
|     samsung: 10.0, | ||||
|   }, | ||||
|   // FormatJS polyfill detects fix for https://bugs.chromium.org/p/v8/issues/detail?id=10682, | ||||
|   // so adjusted to several months after that was marked fixed | ||||
|   fetch: { | ||||
|     android: 42, | ||||
|     chrome: 42, | ||||
|     edge: 14, | ||||
|     firefox: 39, | ||||
|     ios: 10.3, | ||||
|     opera: 29, | ||||
|     opera_mobile: 29, | ||||
|     safari: 10.1, | ||||
|     samsung: 4.0, | ||||
|   }, | ||||
|   "intl-getcanonicallocales": { | ||||
|     android: 90, | ||||
|     chrome: 90, | ||||
|     edge: 90, | ||||
|     android: 54, | ||||
|     chrome: 54, | ||||
|     edge: 16, | ||||
|     firefox: 48, | ||||
|     ios: 10.3, | ||||
|     opera: 76, | ||||
|     opera_mobile: 64, | ||||
|     opera: 41, | ||||
|     opera_mobile: 41, | ||||
|     safari: 10.1, | ||||
|     samsung: 15.0, | ||||
|     samsung: 6.0, | ||||
|   }, | ||||
|   "intl-locale": { | ||||
|     android: 74, | ||||
| @@ -67,6 +87,17 @@ const polyfillSupport = { | ||||
|   "intl-other": { | ||||
|     // Not specified (i.e. always try polyfill) since compatibility depends on supported locales | ||||
|   }, | ||||
|   proxy: { | ||||
|     android: 49, | ||||
|     chrome: 49, | ||||
|     edge: 12, | ||||
|     firefox: 18, | ||||
|     ios: 10.0, | ||||
|     opera: 36, | ||||
|     opera_mobile: 36, | ||||
|     safari: 10.0, | ||||
|     samsung: 5.0, | ||||
|   }, | ||||
|   "resize-observer": { | ||||
|     android: 64, | ||||
|     chrome: 64, | ||||
| @@ -84,6 +115,8 @@ const polyfillSupport = { | ||||
| // corresponding polyfill key and actual module to import | ||||
| const polyfillMap = { | ||||
|   global: { | ||||
|     fetch: { key: "fetch", module: "unfetch/polyfill" }, | ||||
|     Proxy: { key: "proxy", module: "proxy-polyfill" }, | ||||
|     ResizeObserver: { | ||||
|       key: "resize-observer", | ||||
|       module: join(POLYFILL_DIR, "resize-observer.ts"), | ||||
| @@ -95,7 +128,7 @@ const polyfillMap = { | ||||
|       module: "element-internals-polyfill", | ||||
|     }, | ||||
|     ...Object.fromEntries( | ||||
|       ["getAttributeNames", "toggleAttribute"].map((prop) => { | ||||
|       ["append", "getAttributeNames", "toggleAttribute"].map((prop) => { | ||||
|         const key = `element-${prop.toLowerCase()}`; | ||||
|         return [prop, { key, module: join(POLYFILL_DIR, `${key}.ts`) }]; | ||||
|       }) | ||||
| @@ -114,7 +147,6 @@ const polyfillMap = { | ||||
|       ...Object.fromEntries( | ||||
|         [ | ||||
|           "DateTimeFormat", | ||||
|           "DurationFormat", | ||||
|           "DisplayNames", | ||||
|           "ListFormat", | ||||
|           "NumberFormat", | ||||
| @@ -135,7 +167,7 @@ export default defineProvider( | ||||
|     const resolvePolyfill = createMetaResolver(polyfillMap); | ||||
|     return { | ||||
|       name: "custom-polyfill", | ||||
|       polyfills: polyfillSupport, | ||||
|       polyfills: PolyfillSupport, | ||||
|       usageGlobal(meta, utils) { | ||||
|         const polyfill = resolvePolyfill(meta); | ||||
|         if (polyfill && shouldInjectPolyfill(polyfill.desc.key)) { | ||||
|   | ||||
| @@ -53,11 +53,6 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({ | ||||
|   __SUPERVISOR__: false, | ||||
|   __BACKWARDS_COMPAT__: false, | ||||
|   __STATIC_PATH__: "/static/", | ||||
|   __HASS_URL__: `\`${ | ||||
|     "HASS_URL" in process.env | ||||
|       ? process.env["HASS_URL"] | ||||
|       : "${location.protocol}//${location.host}" | ||||
|   }\``, | ||||
|   "process.env.NODE_ENV": JSON.stringify( | ||||
|     isProdBuild ? "production" : "development" | ||||
|   ), | ||||
| @@ -157,6 +152,7 @@ module.exports.babelOptions = ({ | ||||
|   exclude: [ | ||||
|     // \\ for Windows, / for Mac OS and Linux | ||||
|     /node_modules[\\/]core-js/, | ||||
|     /node_modules[\\/]webpack[\\/]buildin/, | ||||
|   ], | ||||
|   sourceMaps: !isTestBuild, | ||||
|   overrides: [ | ||||
| @@ -230,12 +226,13 @@ module.exports.config = { | ||||
|     return { | ||||
|       name: "frontend" + nameSuffix(latestBuild), | ||||
|       entry: { | ||||
|         "service-worker": !latestBuild | ||||
|           ? { | ||||
|               import: "./src/entrypoints/service-worker.ts", | ||||
|               layer: "sw", | ||||
|             } | ||||
|           : "./src/entrypoints/service-worker.ts", | ||||
|         "service-worker": | ||||
|           !env.useRollup() && !latestBuild | ||||
|             ? { | ||||
|                 import: "./src/entrypoints/service-worker.ts", | ||||
|                 layer: "sw", | ||||
|               } | ||||
|             : "./src/entrypoints/service-worker.ts", | ||||
|         app: "./src/entrypoints/app.ts", | ||||
|         authorize: "./src/entrypoints/authorize.ts", | ||||
|         onboarding: "./src/entrypoints/onboarding.ts", | ||||
| @@ -331,17 +328,4 @@ module.exports.config = { | ||||
|       }, | ||||
|     }; | ||||
|   }, | ||||
|  | ||||
|   landingPage({ isProdBuild, latestBuild }) { | ||||
|     return { | ||||
|       name: "landing-page" + nameSuffix(latestBuild), | ||||
|       entry: { | ||||
|         entrypoint: path.resolve(paths.landingPage_dir, "src/entrypoint.js"), | ||||
|       }, | ||||
|       outputPath: outputPath(paths.landingPage_output_root, latestBuild), | ||||
|       publicPath: publicPath(latestBuild), | ||||
|       isProdBuild, | ||||
|       latestBuild, | ||||
|     }; | ||||
|   }, | ||||
| }; | ||||
|   | ||||
| @@ -2,22 +2,26 @@ const fs = require("fs"); | ||||
| const path = require("path"); | ||||
| const paths = require("./paths.cjs"); | ||||
|  | ||||
| const isTrue = (value) => value === "1" || value?.toLowerCase() === "true"; | ||||
|  | ||||
| module.exports = { | ||||
|   useRollup() { | ||||
|     return process.env.ROLLUP === "1"; | ||||
|   }, | ||||
|   useWDS() { | ||||
|     return process.env.WDS === "1"; | ||||
|   }, | ||||
|   isProdBuild() { | ||||
|     return ( | ||||
|       process.env.NODE_ENV === "production" || module.exports.isStatsBuild() | ||||
|     ); | ||||
|   }, | ||||
|   isStatsBuild() { | ||||
|     return isTrue(process.env.STATS); | ||||
|     return process.env.STATS === "1"; | ||||
|   }, | ||||
|   isTestBuild() { | ||||
|     return isTrue(process.env.IS_TEST); | ||||
|     return process.env.IS_TEST === "true"; | ||||
|   }, | ||||
|   isNetlify() { | ||||
|     return isTrue(process.env.NETLIFY); | ||||
|     return process.env.NETLIFY === "true"; | ||||
|   }, | ||||
|   version() { | ||||
|     const version = fs | ||||
| @@ -29,6 +33,6 @@ module.exports = { | ||||
|     return version[1]; | ||||
|   }, | ||||
|   isDevContainer() { | ||||
|     return isTrue(process.env.DEV_CONTAINER); | ||||
|     return process.env.DEV_CONTAINER === "1"; | ||||
|   }, | ||||
| }; | ||||
|   | ||||
| @@ -1,16 +0,0 @@ | ||||
| import rootConfig from "../eslint.config.mjs"; | ||||
|  | ||||
| export default [ | ||||
|   ...rootConfig, | ||||
|   { | ||||
|     rules: { | ||||
|       "no-console": "off", | ||||
|       "import/no-extraneous-dependencies": "off", | ||||
|       "import/extensions": "off", | ||||
|       "import/no-dynamic-require": "off", | ||||
|       "global-require": "off", | ||||
|       "@typescript-eslint/no-require-imports": "off", | ||||
|       "prefer-arrow-callback": "off", | ||||
|     }, | ||||
|   }, | ||||
| ]; | ||||
| @@ -6,9 +6,11 @@ import "./entry-html.js"; | ||||
| import "./gather-static.js"; | ||||
| import "./gen-icons-json.js"; | ||||
| import "./locale-data.js"; | ||||
| import "./rollup.js"; | ||||
| import "./service-worker.js"; | ||||
| import "./translations.js"; | ||||
| import "./rspack.js"; | ||||
| import "./wds.js"; | ||||
| import "./webpack.js"; | ||||
|  | ||||
| gulp.task( | ||||
|   "develop-app", | ||||
| @@ -25,7 +27,11 @@ gulp.task( | ||||
|       "build-locale-data" | ||||
|     ), | ||||
|     "copy-static-app", | ||||
|     "rspack-watch-app" | ||||
|     env.useWDS() | ||||
|       ? "wds-watch-app" | ||||
|       : env.useRollup() | ||||
|         ? "rollup-watch-app" | ||||
|         : "webpack-watch-app" | ||||
|   ) | ||||
| ); | ||||
|  | ||||
| @@ -38,20 +44,9 @@ gulp.task( | ||||
|     "clean", | ||||
|     gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), | ||||
|     "copy-static-app", | ||||
|     "rspack-prod-app", | ||||
|     env.useRollup() ? "rollup-prod-app" : "webpack-prod-app", | ||||
|     gulp.parallel("gen-pages-app-prod", "gen-service-worker-app-prod"), | ||||
|     // Don't compress running tests | ||||
|     ...(env.isTestBuild() || env.isStatsBuild() ? [] : ["compress-app"]) | ||||
|   ) | ||||
| ); | ||||
|  | ||||
| gulp.task( | ||||
|   "analyze-app", | ||||
|   gulp.series( | ||||
|     async function setEnv() { | ||||
|       process.env.STATS = "1"; | ||||
|     }, | ||||
|     "clean", | ||||
|     "rspack-prod-app" | ||||
|     ...(env.isTestBuild() ? [] : ["compress-app"]) | ||||
|   ) | ||||
| ); | ||||
|   | ||||
| @@ -1,10 +1,12 @@ | ||||
| import gulp from "gulp"; | ||||
| import env from "../env.cjs"; | ||||
| import "./clean.js"; | ||||
| import "./entry-html.js"; | ||||
| import "./gather-static.js"; | ||||
| import "./rollup.js"; | ||||
| import "./service-worker.js"; | ||||
| import "./translations.js"; | ||||
| import "./rspack.js"; | ||||
| import "./webpack.js"; | ||||
|  | ||||
| gulp.task( | ||||
|   "develop-cast", | ||||
| @@ -17,7 +19,7 @@ gulp.task( | ||||
|     gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), | ||||
|     "copy-static-cast", | ||||
|     "gen-pages-cast-dev", | ||||
|     "rspack-dev-server-cast" | ||||
|     env.useRollup() ? "rollup-dev-server-cast" : "webpack-dev-server-cast" | ||||
|   ) | ||||
| ); | ||||
|  | ||||
| @@ -31,7 +33,7 @@ gulp.task( | ||||
|     "translations-enable-merge-backend", | ||||
|     gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), | ||||
|     "copy-static-cast", | ||||
|     "rspack-prod-cast", | ||||
|     env.useRollup() ? "rollup-prod-cast" : "webpack-prod-cast", | ||||
|     "gen-pages-cast-prod" | ||||
|   ) | ||||
| ); | ||||
|   | ||||
| @@ -38,14 +38,3 @@ gulp.task( | ||||
|     ]) | ||||
|   ) | ||||
| ); | ||||
|  | ||||
| gulp.task( | ||||
|   "clean-landing-page", | ||||
|   gulp.parallel("clean-translations", async () => | ||||
|     deleteSync([ | ||||
|       paths.landingPage_output_root, | ||||
|       paths.landingPage_build, | ||||
|       paths.build_dir, | ||||
|     ]) | ||||
|   ) | ||||
| ); | ||||
|   | ||||
| @@ -15,72 +15,54 @@ const brotliOptions = { | ||||
| }; | ||||
| const zopfliOptions = { threshold: 150 }; | ||||
|  | ||||
| const compressModern = (rootDir, modernDir, compress) => | ||||
| const compressDistBrotli = (rootDir, modernDir, compressServiceWorker = true) => | ||||
|   gulp | ||||
|     .src([`${modernDir}/**/${filesGlob}`, `${rootDir}/sw-modern.js`], { | ||||
|       base: rootDir, | ||||
|       allowEmpty: true, | ||||
|     }) | ||||
|     .pipe(compress === "zopfli" ? zopfli(zopfliOptions) : brotli(brotliOptions)) | ||||
|     .src( | ||||
|       [ | ||||
|         `${modernDir}/**/${filesGlob}`, | ||||
|         compressServiceWorker ? `${rootDir}/sw-modern.js` : undefined, | ||||
|       ].filter(Boolean), | ||||
|       { | ||||
|         base: rootDir, | ||||
|       } | ||||
|     ) | ||||
|     .pipe(brotli(brotliOptions)) | ||||
|     .pipe(gulp.dest(rootDir)); | ||||
|  | ||||
| const compressOther = (rootDir, modernDir, compress) => | ||||
| 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`, | ||||
|       ], | ||||
|       { base: rootDir, allowEmpty: true } | ||||
|       ].filter(Boolean), | ||||
|       { base: rootDir } | ||||
|     ) | ||||
|     .pipe(compress === "zopfli" ? zopfli(zopfliOptions) : brotli(brotliOptions)) | ||||
|     .pipe(zopfli(zopfliOptions)) | ||||
|     .pipe(gulp.dest(rootDir)); | ||||
|  | ||||
| const compressAppModernBrotli = () => | ||||
|   compressModern(paths.app_output_root, paths.app_output_latest, "brotli"); | ||||
| const compressAppModernZopfli = () => | ||||
|   compressModern(paths.app_output_root, paths.app_output_latest, "zopfli"); | ||||
|  | ||||
| const compressHassioModernBrotli = () => | ||||
|   compressModern( | ||||
| const compressAppBrotli = () => | ||||
|   compressDistBrotli(paths.app_output_root, paths.app_output_latest); | ||||
| const compressHassioBrotli = () => | ||||
|   compressDistBrotli( | ||||
|     paths.hassio_output_root, | ||||
|     paths.hassio_output_latest, | ||||
|     "brotli" | ||||
|     false | ||||
|   ); | ||||
| const compressHassioModernZopfli = () => | ||||
|   compressModern( | ||||
|  | ||||
| const compressAppZopfli = () => | ||||
|   compressDistZopfli(paths.app_output_root, paths.app_output_latest); | ||||
| const compressHassioZopfli = () => | ||||
|   compressDistZopfli( | ||||
|     paths.hassio_output_root, | ||||
|     paths.hassio_output_latest, | ||||
|     "zopfli" | ||||
|     true | ||||
|   ); | ||||
|  | ||||
| const compressAppOtherBrotli = () => | ||||
|   compressOther(paths.app_output_root, paths.app_output_latest, "brotli"); | ||||
| const compressAppOtherZopfli = () => | ||||
|   compressOther(paths.app_output_root, paths.app_output_latest, "zopfli"); | ||||
|  | ||||
| const compressHassioOtherBrotli = () => | ||||
|   compressOther(paths.hassio_output_root, paths.hassio_output_latest, "brotli"); | ||||
| const compressHassioOtherZopfli = () => | ||||
|   compressOther(paths.hassio_output_root, paths.hassio_output_latest, "zopfli"); | ||||
|  | ||||
| gulp.task( | ||||
|   "compress-app", | ||||
|   gulp.parallel( | ||||
|     compressAppModernBrotli, | ||||
|     compressAppOtherBrotli, | ||||
|     compressAppModernZopfli, | ||||
|     compressAppOtherZopfli | ||||
|   ) | ||||
| ); | ||||
| gulp.task("compress-app", gulp.parallel(compressAppBrotli, compressAppZopfli)); | ||||
| gulp.task( | ||||
|   "compress-hassio", | ||||
|   gulp.parallel( | ||||
|     compressHassioModernBrotli, | ||||
|     compressHassioOtherBrotli, | ||||
|     compressHassioModernZopfli, | ||||
|     compressHassioOtherZopfli | ||||
|   ) | ||||
|   gulp.parallel(compressHassioBrotli, compressHassioZopfli) | ||||
| ); | ||||
|   | ||||
| @@ -1,11 +1,13 @@ | ||||
| import gulp from "gulp"; | ||||
| import env from "../env.cjs"; | ||||
| import "./clean.js"; | ||||
| import "./entry-html.js"; | ||||
| import "./gather-static.js"; | ||||
| import "./gen-icons-json.js"; | ||||
| import "./rollup.js"; | ||||
| import "./service-worker.js"; | ||||
| import "./translations.js"; | ||||
| import "./rspack.js"; | ||||
| import "./webpack.js"; | ||||
|  | ||||
| gulp.task( | ||||
|   "develop-demo", | ||||
| @@ -22,7 +24,7 @@ gulp.task( | ||||
|       "build-locale-data" | ||||
|     ), | ||||
|     "copy-static-demo", | ||||
|     "rspack-dev-server-demo" | ||||
|     env.useRollup() ? "rollup-dev-server-demo" : "webpack-dev-server-demo" | ||||
|   ) | ||||
| ); | ||||
|  | ||||
| @@ -37,18 +39,7 @@ gulp.task( | ||||
|     "translations-enable-merge-backend", | ||||
|     gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), | ||||
|     "copy-static-demo", | ||||
|     "rspack-prod-demo", | ||||
|     env.useRollup() ? "rollup-prod-demo" : "webpack-prod-demo", | ||||
|     "gen-pages-demo-prod" | ||||
|   ) | ||||
| ); | ||||
|  | ||||
| gulp.task( | ||||
|   "analyze-demo", | ||||
|   gulp.series( | ||||
|     async function setEnv() { | ||||
|       process.env.STATS = "1"; | ||||
|     }, | ||||
|     "clean", | ||||
|     "rspack-prod-demo" | ||||
|   ) | ||||
| ); | ||||
|   | ||||
| @@ -13,7 +13,7 @@ const srcMeta = "src/translations/translationMetadata.json"; | ||||
| const encoding = "utf8"; | ||||
|  | ||||
| function hasHtml(data) { | ||||
|   return /<\S*>/i.test(data); | ||||
|   return /<[a-z][\s\S]*>/i.test(data); | ||||
| } | ||||
|  | ||||
| function recursiveCheckHasHtml(file, data, errors, recKey) { | ||||
| @@ -127,7 +127,6 @@ gulp.task("fetch-lokalise", async function () { | ||||
|           replace_breaks: false, | ||||
|           json_unescaped_slashes: true, | ||||
|           export_empty_as: "skip", | ||||
|           filter_data: ["verified"], | ||||
|         }) | ||||
|         .then((download) => fetch(download.bundle_url)) | ||||
|         .then((response) => { | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import { minify } from "html-minifier-terser"; | ||||
| import template from "lodash.template"; | ||||
| import { dirname, extname, resolve } from "node:path"; | ||||
| import { htmlMinifierOptions, terserOptions } from "../bundle.cjs"; | ||||
| import env from "../env.cjs"; | ||||
| import paths from "../paths.cjs"; | ||||
|  | ||||
| // macOS companion app has no way to obtain the Safari version used by WKWebView, | ||||
| @@ -55,6 +56,8 @@ const getCommonTemplateVars = () => { | ||||
|     { ignorePatch: true, allowHigherVersions: true } | ||||
|   ); | ||||
|   return { | ||||
|     useRollup: env.useRollup(), | ||||
|     useWDS: env.useWDS(), | ||||
|     modernRegex: compileRegex(browserRegexes.concat(haMacOSRegex)).toString(), | ||||
|   }; | ||||
| }; | ||||
| @@ -90,11 +93,13 @@ const minifyHtml = (content, ext) => { | ||||
| }; | ||||
|  | ||||
| // Function to generate a dev task for each project's configuration | ||||
| // Note Currently WDS paths are hard-coded to only work for app | ||||
| const genPagesDevTask = | ||||
|   ( | ||||
|     pageEntries, | ||||
|     inputRoot, | ||||
|     outputRoot, | ||||
|     useWDS = false, | ||||
|     inputSub = "src/html", | ||||
|     publicRoot = "" | ||||
|   ) => | ||||
| @@ -105,13 +110,17 @@ const genPagesDevTask = | ||||
|         resolve(inputRoot, inputSub, `${page}.template`), | ||||
|         { | ||||
|           ...commonVars, | ||||
|           latestEntryJS: entries.map( | ||||
|             (entry) => `${publicRoot}/frontend_latest/${entry}.js` | ||||
|           latestEntryJS: entries.map((entry) => | ||||
|             useWDS | ||||
|               ? `http://localhost:8000/src/entrypoints/${entry}.ts` | ||||
|               : `${publicRoot}/frontend_latest/${entry}.js` | ||||
|           ), | ||||
|           es5EntryJS: entries.map( | ||||
|             (entry) => `${publicRoot}/frontend_es5/${entry}.js` | ||||
|           ), | ||||
|           latestCustomPanelJS: `${publicRoot}/frontend_latest/custom-panel.js`, | ||||
|           latestCustomPanelJS: useWDS | ||||
|             ? "http://localhost:8000/src/entrypoints/custom-panel.ts" | ||||
|             : `${publicRoot}/frontend_latest/custom-panel.js`, | ||||
|           es5CustomPanelJS: `${publicRoot}/frontend_es5/custom-panel.js`, | ||||
|         } | ||||
|       ); | ||||
| @@ -168,7 +177,12 @@ const APP_PAGE_ENTRIES = { | ||||
|  | ||||
| gulp.task( | ||||
|   "gen-pages-app-dev", | ||||
|   genPagesDevTask(APP_PAGE_ENTRIES, paths.polymer_dir, paths.app_output_root) | ||||
|   genPagesDevTask( | ||||
|     APP_PAGE_ENTRIES, | ||||
|     paths.polymer_dir, | ||||
|     paths.app_output_root, | ||||
|     env.useWDS() | ||||
|   ) | ||||
| ); | ||||
|  | ||||
| gulp.task( | ||||
| @@ -244,28 +258,6 @@ gulp.task( | ||||
|   ) | ||||
| ); | ||||
|  | ||||
| const LANDING_PAGE_PAGE_ENTRIES = { "index.html": ["entrypoint"] }; | ||||
|  | ||||
| gulp.task( | ||||
|   "gen-pages-landing-page-dev", | ||||
|   genPagesDevTask( | ||||
|     LANDING_PAGE_PAGE_ENTRIES, | ||||
|     paths.landingPage_dir, | ||||
|     paths.landingPage_output_root | ||||
|   ) | ||||
| ); | ||||
|  | ||||
| gulp.task( | ||||
|   "gen-pages-landing-page-prod", | ||||
|   genPagesProdTask( | ||||
|     LANDING_PAGE_PAGE_ENTRIES, | ||||
|     paths.landingPage_dir, | ||||
|     paths.landingPage_output_root, | ||||
|     paths.landingPage_output_latest, | ||||
|     paths.landingPage_output_es5 | ||||
|   ) | ||||
| ); | ||||
|  | ||||
| const HASSIO_PAGE_ENTRIES = { "entrypoint.js": ["entrypoint"] }; | ||||
|  | ||||
| gulp.task( | ||||
| @@ -274,6 +266,7 @@ gulp.task( | ||||
|     HASSIO_PAGE_ENTRIES, | ||||
|     paths.hassio_dir, | ||||
|     paths.hassio_output_root, | ||||
|     undefined, | ||||
|     "src", | ||||
|     paths.hassio_publicPath | ||||
|   ) | ||||
|   | ||||
| @@ -66,7 +66,7 @@ gulp.task("fetch-nightly-translations", async function () { | ||||
|       tokenAuth = JSON.parse(await readFile(TOKEN_FILE, "utf-8")); | ||||
|     } catch { | ||||
|       if (!allowTokenSetup) { | ||||
|         console.log("No token found so build will continue with English only"); | ||||
|         console.log("No token found so  build wil continue with English only"); | ||||
|         return; | ||||
|       } | ||||
|       const auth = createOAuthDeviceAuth({ | ||||
|   | ||||
| @@ -4,14 +4,16 @@ import gulp from "gulp"; | ||||
| import yaml from "js-yaml"; | ||||
| import { marked } from "marked"; | ||||
| import path from "path"; | ||||
| import env from "../env.cjs"; | ||||
| import paths from "../paths.cjs"; | ||||
| import "./clean.js"; | ||||
| import "./entry-html.js"; | ||||
| import "./gather-static.js"; | ||||
| import "./gen-icons-json.js"; | ||||
| import "./rollup.js"; | ||||
| import "./service-worker.js"; | ||||
| import "./translations.js"; | ||||
| import "./rspack.js"; | ||||
| import "./webpack.js"; | ||||
|  | ||||
| gulp.task("gather-gallery-pages", async function gatherPages() { | ||||
|   const pageDir = path.resolve(paths.gallery_dir, "src/pages"); | ||||
| @@ -156,7 +158,9 @@ gulp.task( | ||||
|     "copy-static-gallery", | ||||
|     "gen-pages-gallery-dev", | ||||
|     gulp.parallel( | ||||
|       "rspack-dev-server-gallery", | ||||
|       env.useRollup() | ||||
|         ? "rollup-dev-server-gallery" | ||||
|         : "webpack-dev-server-gallery", | ||||
|       async function watchMarkdownFiles() { | ||||
|         gulp.watch( | ||||
|           [ | ||||
| @@ -185,7 +189,7 @@ gulp.task( | ||||
|       "gather-gallery-pages" | ||||
|     ), | ||||
|     "copy-static-gallery", | ||||
|     "rspack-prod-gallery", | ||||
|     env.useRollup() ? "rollup-prod-gallery" : "webpack-prod-gallery", | ||||
|     "gen-pages-gallery-prod" | ||||
|   ) | ||||
| ); | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import fs from "fs-extra"; | ||||
| import gulp from "gulp"; | ||||
| import path from "path"; | ||||
| import paths from "../paths.cjs"; | ||||
| import env from "../env.cjs"; | ||||
|  | ||||
| const npmPath = (...parts) => | ||||
|   path.resolve(paths.polymer_dir, "node_modules", ...parts); | ||||
| @@ -59,12 +60,15 @@ function copyPolyfills(staticDir) { | ||||
|     npmPath("@webcomponents/webcomponentsjs/webcomponents-bundle.js.map"), | ||||
|     staticPath("polyfills/") | ||||
|   ); | ||||
| } | ||||
|  | ||||
|   // dialog-polyfill css | ||||
|   copyFileDir( | ||||
|     npmPath("dialog-polyfill/dialog-polyfill.css"), | ||||
|     staticPath("polyfills/") | ||||
|   ); | ||||
| function copyLoaderJS(staticDir) { | ||||
|   if (!env.useRollup()) { | ||||
|     return; | ||||
|   } | ||||
|   const staticPath = genStaticPath(staticDir); | ||||
|   copyFileDir(npmPath("systemjs/dist/s.min.js"), staticPath("js")); | ||||
|   copyFileDir(npmPath("systemjs/dist/s.min.js.map"), staticPath("js")); | ||||
| } | ||||
|  | ||||
| function copyFonts(staticDir) { | ||||
| @@ -96,14 +100,6 @@ function copyMapPanel(staticDir) { | ||||
|   ); | ||||
| } | ||||
|  | ||||
| function copyZXingWasm(staticDir) { | ||||
|   const staticPath = genStaticPath(staticDir); | ||||
|   copyFileDir( | ||||
|     npmPath("zxing-wasm/dist/reader/zxing_reader.wasm"), | ||||
|     staticPath("js") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| gulp.task("copy-locale-data", async () => { | ||||
|   const staticDir = paths.app_output_static; | ||||
|   copyLocaleData(staticDir); | ||||
| @@ -119,11 +115,6 @@ gulp.task("copy-translations-supervisor", async () => { | ||||
|   copyTranslations(staticDir); | ||||
| }); | ||||
|  | ||||
| gulp.task("copy-translations-landing-page", async () => { | ||||
|   const staticDir = paths.landingPage_output_static; | ||||
|   copyTranslations(staticDir); | ||||
| }); | ||||
|  | ||||
| gulp.task("copy-static-supervisor", async () => { | ||||
|   const staticDir = paths.hassio_output_static; | ||||
|   copyLocaleData(staticDir); | ||||
| @@ -134,6 +125,8 @@ gulp.task("copy-static-app", async () => { | ||||
|   const staticDir = paths.app_output_static; | ||||
|   // Basic static files | ||||
|   fs.copySync(polyPath("public"), paths.app_output_root); | ||||
|  | ||||
|   copyLoaderJS(staticDir); | ||||
|   copyPolyfills(staticDir); | ||||
|   copyFonts(staticDir); | ||||
|   copyTranslations(staticDir); | ||||
| @@ -144,7 +137,6 @@ gulp.task("copy-static-app", async () => { | ||||
|   copyMapPanel(staticDir); | ||||
|  | ||||
|   // Qr Scanner assets | ||||
|   copyZXingWasm(staticDir); | ||||
|   copyQrScannerWorker(staticDir); | ||||
| }); | ||||
|  | ||||
| @@ -156,6 +148,8 @@ gulp.task("copy-static-demo", async () => { | ||||
|   ); | ||||
|   // Copy demo static files | ||||
|   fs.copySync(path.resolve(paths.demo_dir, "public"), paths.demo_output_root); | ||||
|  | ||||
|   copyLoaderJS(paths.demo_output_static); | ||||
|   copyPolyfills(paths.demo_output_static); | ||||
|   copyMapPanel(paths.demo_output_static); | ||||
|   copyFonts(paths.demo_output_static); | ||||
| @@ -169,6 +163,8 @@ gulp.task("copy-static-cast", async () => { | ||||
|   fs.copySync(polyPath("public/static"), paths.cast_output_static); | ||||
|   // Copy cast static files | ||||
|   fs.copySync(path.resolve(paths.cast_dir, "public"), paths.cast_output_root); | ||||
|  | ||||
|   copyLoaderJS(paths.cast_output_static); | ||||
|   copyPolyfills(paths.cast_output_static); | ||||
|   copyMapPanel(paths.cast_output_static); | ||||
|   copyFonts(paths.cast_output_static); | ||||
| @@ -192,14 +188,3 @@ gulp.task("copy-static-gallery", async () => { | ||||
|   copyLocaleData(paths.gallery_output_static); | ||||
|   copyMdiIcons(paths.gallery_output_static); | ||||
| }); | ||||
|  | ||||
| gulp.task("copy-static-landing-page", async () => { | ||||
|   // Copy landing-page static files | ||||
|   fs.copySync( | ||||
|     path.resolve(paths.landingPage_dir, "public"), | ||||
|     paths.landingPage_output_root | ||||
|   ); | ||||
|  | ||||
|   copyFonts(paths.landingPage_output_static); | ||||
|   copyTranslations(paths.landingPage_output_static); | ||||
| }); | ||||
|   | ||||
| @@ -5,8 +5,9 @@ import "./compress.js"; | ||||
| import "./entry-html.js"; | ||||
| import "./gather-static.js"; | ||||
| import "./gen-icons-json.js"; | ||||
| import "./rollup.js"; | ||||
| import "./translations.js"; | ||||
| import "./rspack.js"; | ||||
| import "./webpack.js"; | ||||
|  | ||||
| gulp.task( | ||||
|   "develop-hassio", | ||||
| @@ -21,7 +22,7 @@ gulp.task( | ||||
|     "copy-translations-supervisor", | ||||
|     "build-locale-data", | ||||
|     "copy-static-supervisor", | ||||
|     "rspack-watch-hassio" | ||||
|     env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio" | ||||
|   ) | ||||
| ); | ||||
|  | ||||
| @@ -37,7 +38,7 @@ gulp.task( | ||||
|     "copy-translations-supervisor", | ||||
|     "build-locale-data", | ||||
|     "copy-static-supervisor", | ||||
|     "rspack-prod-hassio", | ||||
|     env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio", | ||||
|     "gen-pages-hassio-prod", | ||||
|     ...// Don't compress running tests | ||||
|     (env.isTestBuild() ? [] : ["compress-hassio"]) | ||||
|   | ||||
| @@ -1,17 +0,0 @@ | ||||
| import "./app.js"; | ||||
| import "./cast.js"; | ||||
| import "./clean.js"; | ||||
| import "./compress.js"; | ||||
| import "./demo.js"; | ||||
| import "./download-translations.js"; | ||||
| import "./entry-html.js"; | ||||
| import "./fetch-nightly-translations.js"; | ||||
| import "./gallery.js"; | ||||
| import "./gather-static.js"; | ||||
| import "./gen-icons-json.js"; | ||||
| import "./hassio.js"; | ||||
| import "./landing-page.js"; | ||||
| import "./locale-data.js"; | ||||
| import "./rspack.js"; | ||||
| import "./service-worker.js"; | ||||
| import "./translations.js"; | ||||
| @@ -1,41 +0,0 @@ | ||||
| import gulp from "gulp"; | ||||
| import "./clean.js"; | ||||
| import "./compress.js"; | ||||
| import "./entry-html.js"; | ||||
| import "./gather-static.js"; | ||||
| import "./gen-icons-json.js"; | ||||
| import "./translations.js"; | ||||
| import "./rspack.js"; | ||||
|  | ||||
| gulp.task( | ||||
|   "develop-landing-page", | ||||
|   gulp.series( | ||||
|     async function setEnv() { | ||||
|       process.env.NODE_ENV = "development"; | ||||
|     }, | ||||
|     "clean-landing-page", | ||||
|     "translations-enable-merge-backend", | ||||
|     "build-landing-page-translations", | ||||
|     "copy-translations-landing-page", | ||||
|     "build-locale-data", | ||||
|     "copy-static-landing-page", | ||||
|     "gen-pages-landing-page-dev", | ||||
|     "rspack-watch-landing-page" | ||||
|   ) | ||||
| ); | ||||
|  | ||||
| gulp.task( | ||||
|   "build-landing-page", | ||||
|   gulp.series( | ||||
|     async function setEnv() { | ||||
|       process.env.NODE_ENV = "production"; | ||||
|     }, | ||||
|     "clean-landing-page", | ||||
|     "build-landing-page-translations", | ||||
|     "copy-translations-landing-page", | ||||
|     "build-locale-data", | ||||
|     "copy-static-landing-page", | ||||
|     "rspack-prod-landing-page", | ||||
|     "gen-pages-landing-page-prod" | ||||
|   ) | ||||
| ); | ||||
| @@ -24,11 +24,8 @@ const convertToJSON = async ( | ||||
| ) => { | ||||
|   let localeData; | ||||
|   try { | ||||
|     // use "pt" for "pt-BR", because "pt-BR" is unsupported by @formatjs | ||||
|     const language = lang === "pt-BR" ? "pt" : lang; | ||||
|  | ||||
|     localeData = await readFile( | ||||
|       join(formatjsDir, pkg, subDir, `${language}.js`), | ||||
|       join(formatjsDir, pkg, subDir, `${lang}.js`), | ||||
|       "utf-8" | ||||
|     ); | ||||
|   } catch (e) { | ||||
|   | ||||
							
								
								
									
										147
									
								
								build-scripts/gulp/rollup.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								build-scripts/gulp/rollup.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | ||||
| // Tasks to run Rollup | ||||
|  | ||||
| import log from "fancy-log"; | ||||
| import gulp from "gulp"; | ||||
| import http from "http"; | ||||
| import open from "open"; | ||||
| import path from "path"; | ||||
| import { rollup } from "rollup"; | ||||
| import handler from "serve-handler"; | ||||
| import paths from "../paths.cjs"; | ||||
| import rollupConfig from "../rollup.cjs"; | ||||
|  | ||||
| const bothBuilds = (createConfigFunc, params) => | ||||
|   gulp.series( | ||||
|     async function buildLatest() { | ||||
|       await buildRollup( | ||||
|         createConfigFunc({ | ||||
|           ...params, | ||||
|           latestBuild: true, | ||||
|         }) | ||||
|       ); | ||||
|     }, | ||||
|     async function buildES5() { | ||||
|       await buildRollup( | ||||
|         createConfigFunc({ | ||||
|           ...params, | ||||
|           latestBuild: false, | ||||
|         }) | ||||
|       ); | ||||
|     } | ||||
|   ); | ||||
|  | ||||
| function createServer(serveOptions) { | ||||
|   const server = http.createServer((request, response) => | ||||
|     handler(request, response, { | ||||
|       public: serveOptions.root, | ||||
|     }) | ||||
|   ); | ||||
|  | ||||
|   server.listen( | ||||
|     serveOptions.port, | ||||
|     serveOptions.networkAccess ? "0.0.0.0" : undefined, | ||||
|     () => { | ||||
|       log.info(`Available at http://localhost:${serveOptions.port}`); | ||||
|       open(`http://localhost:${serveOptions.port}`); | ||||
|     } | ||||
|   ); | ||||
| } | ||||
|  | ||||
| function watchRollup(createConfig, extraWatchSrc = [], serveOptions = null) { | ||||
|   const { inputOptions, outputOptions } = createConfig({ | ||||
|     isProdBuild: false, | ||||
|     latestBuild: true, | ||||
|   }); | ||||
|  | ||||
|   const watcher = rollup.watch({ | ||||
|     ...inputOptions, | ||||
|     output: [outputOptions], | ||||
|     watch: { | ||||
|       include: ["src/**"] + extraWatchSrc, | ||||
|     }, | ||||
|   }); | ||||
|  | ||||
|   let startedHttp = false; | ||||
|  | ||||
|   watcher.on("event", (event) => { | ||||
|     if (event.code === "BUNDLE_END") { | ||||
|       log(`Build done @ ${new Date().toLocaleTimeString()}`); | ||||
|     } else if (event.code === "ERROR") { | ||||
|       log.error(event.error); | ||||
|     } else if (event.code === "END") { | ||||
|       if (startedHttp || !serveOptions) { | ||||
|         return; | ||||
|       } | ||||
|       startedHttp = true; | ||||
|       createServer(serveOptions); | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   gulp.watch( | ||||
|     path.join(paths.translations_src, "en.json"), | ||||
|     gulp.series("build-translations", "copy-translations-app") | ||||
|   ); | ||||
| } | ||||
|  | ||||
| async function buildRollup(config) { | ||||
|   const bundle = await rollup.rollup(config.inputOptions); | ||||
|   await bundle.write(config.outputOptions); | ||||
| } | ||||
|  | ||||
| gulp.task("rollup-watch-app", () => { | ||||
|   watchRollup(rollupConfig.createAppConfig); | ||||
| }); | ||||
|  | ||||
| gulp.task("rollup-watch-hassio", () => { | ||||
|   watchRollup(rollupConfig.createHassioConfig, ["hassio/src/**"]); | ||||
| }); | ||||
|  | ||||
| gulp.task("rollup-dev-server-demo", () => { | ||||
|   watchRollup(rollupConfig.createDemoConfig, ["demo/src/**"], { | ||||
|     root: paths.demo_output_root, | ||||
|     port: 8090, | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| gulp.task("rollup-dev-server-cast", () => { | ||||
|   watchRollup(rollupConfig.createCastConfig, ["cast/src/**"], { | ||||
|     root: paths.cast_output_root, | ||||
|     port: 8080, | ||||
|     networkAccess: true, | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| gulp.task("rollup-dev-server-gallery", () => { | ||||
|   watchRollup(rollupConfig.createGalleryConfig, ["gallery/src/**"], { | ||||
|     root: paths.gallery_output_root, | ||||
|     port: 8100, | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| gulp.task( | ||||
|   "rollup-prod-app", | ||||
|   bothBuilds(rollupConfig.createAppConfig, { isProdBuild: true }) | ||||
| ); | ||||
|  | ||||
| gulp.task( | ||||
|   "rollup-prod-demo", | ||||
|   bothBuilds(rollupConfig.createDemoConfig, { isProdBuild: true }) | ||||
| ); | ||||
|  | ||||
| gulp.task( | ||||
|   "rollup-prod-cast", | ||||
|   bothBuilds(rollupConfig.createCastConfig, { isProdBuild: true }) | ||||
| ); | ||||
|  | ||||
| gulp.task("rollup-prod-hassio", () => | ||||
|   bothBuilds(rollupConfig.createHassioConfig, { isProdBuild: true }) | ||||
| ); | ||||
|  | ||||
| gulp.task("rollup-prod-gallery", () => | ||||
|   buildRollup( | ||||
|     rollupConfig.createGalleryConfig({ | ||||
|       isProdBuild: true, | ||||
|       latestBuild: true, | ||||
|     }) | ||||
|   ) | ||||
| ); | ||||
| @@ -172,14 +172,12 @@ const createMasterTranslation = () => | ||||
|  | ||||
| const FRAGMENTS = ["base"]; | ||||
|  | ||||
| const setFragment = (fragment) => async () => { | ||||
|   FRAGMENTS[0] = fragment; | ||||
| const toggleSupervisorFragment = async () => { | ||||
|   FRAGMENTS[0] = "supervisor"; | ||||
| }; | ||||
|  | ||||
| const panelFragment = (fragment) => | ||||
|   fragment !== "base" && | ||||
|   fragment !== "supervisor" && | ||||
|   fragment !== "landing-page"; | ||||
|   fragment !== "base" && fragment !== "supervisor"; | ||||
|  | ||||
| const HASHES = new Map(); | ||||
|  | ||||
| @@ -226,9 +224,6 @@ const createTranslations = async () => { | ||||
|             case "supervisor": | ||||
|               // Supervisor key is at the top level | ||||
|               return [flatten(data.supervisor), ""]; | ||||
|             case "landing-page": | ||||
|               // landing-page key is at the top level | ||||
|               return [flatten(data["landing-page"]), ""]; | ||||
|             default: | ||||
|               // Create a fragment with only the given panel | ||||
|               return [ | ||||
| @@ -327,10 +322,5 @@ gulp.task( | ||||
|  | ||||
| gulp.task( | ||||
|   "build-supervisor-translations", | ||||
|   gulp.series(setFragment("supervisor"), "build-translations") | ||||
| ); | ||||
|  | ||||
| gulp.task( | ||||
|   "build-landing-page-translations", | ||||
|   gulp.series(setFragment("landing-page"), "build-translations") | ||||
|   gulp.series(toggleSupervisorFragment, "build-translations") | ||||
| ); | ||||
|   | ||||
							
								
								
									
										10
									
								
								build-scripts/gulp/wds.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								build-scripts/gulp/wds.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| import gulp from "gulp"; | ||||
| import { startDevServer } from "@web/dev-server"; | ||||
|  | ||||
| gulp.task("wds-watch-app", async () => { | ||||
|   startDevServer({ | ||||
|     config: { | ||||
|       watch: true, | ||||
|     }, | ||||
|   }); | ||||
| }); | ||||
| @@ -1,11 +1,11 @@ | ||||
| // Tasks to run rspack.
 | ||||
| // Tasks to run webpack.
 | ||||
| 
 | ||||
| import fs from "fs"; | ||||
| import path from "path"; | ||||
| import log from "fancy-log"; | ||||
| import gulp from "gulp"; | ||||
| import rspack from "@rspack/core"; | ||||
| import { RspackDevServer } from "@rspack/dev-server"; | ||||
| import webpack from "webpack"; | ||||
| import WebpackDevServer from "webpack-dev-server"; | ||||
| import env from "../env.cjs"; | ||||
| import paths from "../paths.cjs"; | ||||
| import { | ||||
| @@ -14,8 +14,7 @@ import { | ||||
|   createDemoConfig, | ||||
|   createGalleryConfig, | ||||
|   createHassioConfig, | ||||
|   createLandingPageConfig, | ||||
| } from "../rspack.cjs"; | ||||
| } from "../webpack.cjs"; | ||||
| 
 | ||||
| const bothBuilds = (createConfigFunc, params) => [ | ||||
|   createConfigFunc({ ...params, latestBuild: true }), | ||||
| @@ -31,7 +30,7 @@ const isWsl = | ||||
| 
 | ||||
| /** | ||||
|  * @param {{ | ||||
|  *   compiler: import("@rspack/core").Compiler, | ||||
|  *   compiler: import("webpack").Compiler, | ||||
|  *   contentBase: string, | ||||
|  *   port: number, | ||||
|  *   listenHost?: string | ||||
| @@ -42,13 +41,12 @@ const runDevServer = async ({ | ||||
|   contentBase, | ||||
|   port, | ||||
|   listenHost = undefined, | ||||
|   proxy = undefined, | ||||
| }) => { | ||||
|   if (listenHost === undefined) { | ||||
|     // For dev container, we need to listen on all hosts
 | ||||
|     listenHost = env.isDevContainer() ? "0.0.0.0" : "localhost"; | ||||
|   } | ||||
|   const server = new RspackDevServer( | ||||
|   const server = new WebpackDevServer( | ||||
|     { | ||||
|       hot: false, | ||||
|       open: true, | ||||
| @@ -58,14 +56,13 @@ const runDevServer = async ({ | ||||
|         directory: contentBase, | ||||
|         watch: true, | ||||
|       }, | ||||
|       proxy, | ||||
|     }, | ||||
|     compiler | ||||
|   ); | ||||
| 
 | ||||
|   await server.start(); | ||||
|   // Server listening
 | ||||
|   log("[rspack-dev-server]", `Project is running at http://localhost:${port}`); | ||||
|   log("[webpack-dev-server]", `Project is running at http://localhost:${port}`); | ||||
| }; | ||||
| 
 | ||||
| const doneHandler = (done) => (err, stats) => { | ||||
| @@ -90,16 +87,16 @@ const doneHandler = (done) => (err, stats) => { | ||||
| 
 | ||||
| const prodBuild = (conf) => | ||||
|   new Promise((resolve) => { | ||||
|     rspack( | ||||
|     webpack( | ||||
|       conf, | ||||
|       // Resolve promise when done. Because we pass a callback, rspack closes itself
 | ||||
|       // Resolve promise when done. Because we pass a callback, webpack closes itself
 | ||||
|       doneHandler(resolve) | ||||
|     ); | ||||
|   }); | ||||
| 
 | ||||
| gulp.task("rspack-watch-app", () => { | ||||
| gulp.task("webpack-watch-app", () => { | ||||
|   // This command will run forever because we don't close compiler
 | ||||
|   rspack( | ||||
|   webpack( | ||||
|     process.env.ES5 | ||||
|       ? bothBuilds(createAppConfig, { isProdBuild: false }) | ||||
|       : createAppConfig({ isProdBuild: false, latestBuild: true }) | ||||
| @@ -110,7 +107,7 @@ gulp.task("rspack-watch-app", () => { | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| gulp.task("rspack-prod-app", () => | ||||
| gulp.task("webpack-prod-app", () => | ||||
|   prodBuild( | ||||
|     bothBuilds(createAppConfig, { | ||||
|       isProdBuild: true, | ||||
| @@ -120,9 +117,9 @@ gulp.task("rspack-prod-app", () => | ||||
|   ) | ||||
| ); | ||||
| 
 | ||||
| gulp.task("rspack-dev-server-demo", () => | ||||
| gulp.task("webpack-dev-server-demo", () => | ||||
|   runDevServer({ | ||||
|     compiler: rspack( | ||||
|     compiler: webpack( | ||||
|       createDemoConfig({ isProdBuild: false, latestBuild: true }) | ||||
|     ), | ||||
|     contentBase: paths.demo_output_root, | ||||
| @@ -130,18 +127,17 @@ gulp.task("rspack-dev-server-demo", () => | ||||
|   }) | ||||
| ); | ||||
| 
 | ||||
| gulp.task("rspack-prod-demo", () => | ||||
| gulp.task("webpack-prod-demo", () => | ||||
|   prodBuild( | ||||
|     bothBuilds(createDemoConfig, { | ||||
|       isProdBuild: true, | ||||
|       isStatsBuild: env.isStatsBuild(), | ||||
|     }) | ||||
|   ) | ||||
| ); | ||||
| 
 | ||||
| gulp.task("rspack-dev-server-cast", () => | ||||
| gulp.task("webpack-dev-server-cast", () => | ||||
|   runDevServer({ | ||||
|     compiler: rspack( | ||||
|     compiler: webpack( | ||||
|       createCastConfig({ isProdBuild: false, latestBuild: true }) | ||||
|     ), | ||||
|     contentBase: paths.cast_output_root, | ||||
| @@ -151,7 +147,7 @@ gulp.task("rspack-dev-server-cast", () => | ||||
|   }) | ||||
| ); | ||||
| 
 | ||||
| gulp.task("rspack-prod-cast", () => | ||||
| gulp.task("webpack-prod-cast", () => | ||||
|   prodBuild( | ||||
|     bothBuilds(createCastConfig, { | ||||
|       isProdBuild: true, | ||||
| @@ -159,9 +155,9 @@ gulp.task("rspack-prod-cast", () => | ||||
|   ) | ||||
| ); | ||||
| 
 | ||||
| gulp.task("rspack-watch-hassio", () => { | ||||
| gulp.task("webpack-watch-hassio", () => { | ||||
|   // This command will run forever because we don't close compiler
 | ||||
|   rspack( | ||||
|   webpack( | ||||
|     createHassioConfig({ | ||||
|       isProdBuild: false, | ||||
|       latestBuild: true, | ||||
| @@ -174,7 +170,7 @@ gulp.task("rspack-watch-hassio", () => { | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| gulp.task("rspack-prod-hassio", () => | ||||
| gulp.task("webpack-prod-hassio", () => | ||||
|   prodBuild( | ||||
|     bothBuilds(createHassioConfig, { | ||||
|       isProdBuild: true, | ||||
| @@ -184,9 +180,9 @@ gulp.task("rspack-prod-hassio", () => | ||||
|   ) | ||||
| ); | ||||
| 
 | ||||
| gulp.task("rspack-dev-server-gallery", () => | ||||
| gulp.task("webpack-dev-server-gallery", () => | ||||
|   runDevServer({ | ||||
|     compiler: rspack( | ||||
|     compiler: webpack( | ||||
|       createGalleryConfig({ isProdBuild: false, latestBuild: true }) | ||||
|     ), | ||||
|     contentBase: paths.gallery_output_root, | ||||
| @@ -195,7 +191,7 @@ gulp.task("rspack-dev-server-gallery", () => | ||||
|   }) | ||||
| ); | ||||
| 
 | ||||
| gulp.task("rspack-prod-gallery", () => | ||||
| gulp.task("webpack-prod-gallery", () => | ||||
|   prodBuild( | ||||
|     createGalleryConfig({ | ||||
|       isProdBuild: true, | ||||
| @@ -203,30 +199,3 @@ gulp.task("rspack-prod-gallery", () => | ||||
|     }) | ||||
|   ) | ||||
| ); | ||||
| 
 | ||||
| gulp.task("rspack-watch-landing-page", () => { | ||||
|   // This command will run forever because we don't close compiler
 | ||||
|   rspack( | ||||
|     process.env.ES5 | ||||
|       ? bothBuilds(createLandingPageConfig, { isProdBuild: false }) | ||||
|       : createLandingPageConfig({ isProdBuild: false, latestBuild: true }) | ||||
|   ).watch({ poll: isWsl }, doneHandler()); | ||||
| 
 | ||||
|   gulp.watch( | ||||
|     path.join(paths.translations_src, "en.json"), | ||||
|     gulp.series( | ||||
|       "build-landing-page-translations", | ||||
|       "copy-translations-landing-page" | ||||
|     ) | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| gulp.task("rspack-prod-landing-page", () => | ||||
|   prodBuild( | ||||
|     bothBuilds(createLandingPageConfig, { | ||||
|       isProdBuild: true, | ||||
|       isStatsBuild: env.isStatsBuild(), | ||||
|       isTestBuild: env.isTestBuild(), | ||||
|     }) | ||||
|   ) | ||||
| ); | ||||
| @@ -33,22 +33,6 @@ module.exports = { | ||||
|   ), | ||||
|   gallery_output_static: path.resolve(__dirname, "../gallery/dist/static"), | ||||
|  | ||||
|   landingPage_dir: path.resolve(__dirname, "../landing-page"), | ||||
|   landingPage_build: path.resolve(__dirname, "../landing-page/build"), | ||||
|   landingPage_output_root: path.resolve(__dirname, "../landing-page/dist"), | ||||
|   landingPage_output_latest: path.resolve( | ||||
|     __dirname, | ||||
|     "../landing-page/dist/frontend_latest" | ||||
|   ), | ||||
|   landingPage_output_es5: path.resolve( | ||||
|     __dirname, | ||||
|     "../landing-page/dist/frontend_es5" | ||||
|   ), | ||||
|   landingPage_output_static: path.resolve( | ||||
|     __dirname, | ||||
|     "../landing-page/dist/static" | ||||
|   ), | ||||
|  | ||||
|   hassio_dir: path.resolve(__dirname, "../hassio"), | ||||
|   hassio_output_root: path.resolve(__dirname, "../hassio/build"), | ||||
|   hassio_output_static: path.resolve(__dirname, "../hassio/build/static"), | ||||
|   | ||||
							
								
								
									
										14
									
								
								build-scripts/rollup-plugins/dont-hash-plugin.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								build-scripts/rollup-plugins/dont-hash-plugin.cjs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| module.exports = function (opts = {}) { | ||||
|   const dontHash = opts.dontHash || new Set(); | ||||
|  | ||||
|   return { | ||||
|     name: "dont-hash", | ||||
|     renderChunk(_code, chunk, _options) { | ||||
|       if (!chunk.isEntry || !dontHash.has(chunk.name)) { | ||||
|         return null; | ||||
|       } | ||||
|       chunk.fileName = `${chunk.name}.js`; | ||||
|       return null; | ||||
|     }, | ||||
|   }; | ||||
| }; | ||||
							
								
								
									
										24
									
								
								build-scripts/rollup-plugins/ignore-plugin.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								build-scripts/rollup-plugins/ignore-plugin.cjs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| module.exports = function (userOptions = {}) { | ||||
|   // Files need to be absolute paths. | ||||
|   // This only works if the file has no exports | ||||
|   // and only is imported for its side effects | ||||
|   const files = userOptions.files || []; | ||||
|  | ||||
|   if (files.length === 0) { | ||||
|     return { | ||||
|       name: "ignore", | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   return { | ||||
|     name: "ignore", | ||||
|  | ||||
|     load(id) { | ||||
|       return files.some((toIgnorePath) => id.startsWith(toIgnorePath)) | ||||
|         ? { | ||||
|             code: "", | ||||
|           } | ||||
|         : null; | ||||
|     }, | ||||
|   }; | ||||
| }; | ||||
							
								
								
									
										34
									
								
								build-scripts/rollup-plugins/manifest-plugin.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								build-scripts/rollup-plugins/manifest-plugin.cjs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| const url = require("url"); | ||||
|  | ||||
| const defaultOptions = { | ||||
|   publicPath: "", | ||||
| }; | ||||
|  | ||||
| module.exports = function (userOptions = {}) { | ||||
|   const options = { ...defaultOptions, ...userOptions }; | ||||
|  | ||||
|   return { | ||||
|     name: "manifest", | ||||
|     generateBundle(outputOptions, bundle) { | ||||
|       const manifest = {}; | ||||
|  | ||||
|       for (const chunk of Object.values(bundle)) { | ||||
|         if (!chunk.isEntry) { | ||||
|           continue; | ||||
|         } | ||||
|         // Add js extension to mimic Webpack manifest. | ||||
|         manifest[`${chunk.name}.js`] = url.resolve( | ||||
|           options.publicPath, | ||||
|           chunk.fileName | ||||
|         ); | ||||
|       } | ||||
|  | ||||
|       this.emitFile({ | ||||
|         type: "asset", | ||||
|         source: JSON.stringify(manifest, undefined, 2), | ||||
|         name: "manifest.json", | ||||
|         fileName: "manifest.json", | ||||
|       }); | ||||
|     }, | ||||
|   }; | ||||
| }; | ||||
							
								
								
									
										152
									
								
								build-scripts/rollup-plugins/worker-plugin.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								build-scripts/rollup-plugins/worker-plugin.cjs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| // Worker plugin | ||||
| // Each worker will include all of its dependencies | ||||
| // instead of relying on an importer. | ||||
|  | ||||
| // Forked from v.1.4.1 | ||||
| // https://github.com/surma/rollup-plugin-off-main-thread | ||||
| /** | ||||
|  * Copyright 2018 Google Inc. All Rights Reserved. | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| const rollup = require("rollup"); | ||||
| const path = require("path"); | ||||
| const MagicString = require("magic-string"); | ||||
|  | ||||
| const defaultOpts = { | ||||
|   // A RegExp to find `new Workers()` calls. The second capture group _must_ | ||||
|   // capture the provided file name without the quotes. | ||||
|   workerRegexp: /new Worker\((["'])(.+?)\1(,[^)]+)?\)/g, | ||||
|   plugins: ["node-resolve", "commonjs", "babel", "terser", "ignore"], | ||||
| }; | ||||
|  | ||||
| async function getBundledWorker(workerPath, rollupOptions) { | ||||
|   const bundle = await rollup.rollup({ | ||||
|     ...rollupOptions, | ||||
|     input: { | ||||
|       worker: workerPath, | ||||
|     }, | ||||
|   }); | ||||
|   const { output } = await bundle.generate({ | ||||
|     // Generates cleanest output, we shouldn't have any imports/exports | ||||
|     // that would be incompatible with ES5. | ||||
|     format: "es", | ||||
|     // We should not export anything. This will fail build if we are. | ||||
|     exports: "none", | ||||
|   }); | ||||
|  | ||||
|   let code; | ||||
|  | ||||
|   for (const chunkOrAsset of output) { | ||||
|     if (chunkOrAsset.name === "worker") { | ||||
|       code = chunkOrAsset.code; | ||||
|     } else if (chunkOrAsset.type !== "asset") { | ||||
|       throw new Error("Unexpected extra output"); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return code; | ||||
| } | ||||
|  | ||||
| module.exports = function (opts = {}) { | ||||
|   opts = { ...defaultOpts, ...opts }; | ||||
|  | ||||
|   let rollupOptions; | ||||
|   let refIds; | ||||
|  | ||||
|   return { | ||||
|     name: "hass-worker", | ||||
|  | ||||
|     async buildStart(options) { | ||||
|       refIds = {}; | ||||
|       rollupOptions = { | ||||
|         plugins: options.plugins.filter((plugin) => | ||||
|           opts.plugins.includes(plugin.name) | ||||
|         ), | ||||
|       }; | ||||
|     }, | ||||
|  | ||||
|     async transform(code, id) { | ||||
|       // Copy the regexp as they are stateful and this hook is async. | ||||
|       const workerRegexp = new RegExp( | ||||
|         opts.workerRegexp.source, | ||||
|         opts.workerRegexp.flags | ||||
|       ); | ||||
|       if (!workerRegexp.test(code)) { | ||||
|         return undefined; | ||||
|       } | ||||
|  | ||||
|       const ms = new MagicString(code); | ||||
|       // Reset the regexp | ||||
|       workerRegexp.lastIndex = 0; | ||||
|       for (;;) { | ||||
|         const match = workerRegexp.exec(code); | ||||
|         if (!match) { | ||||
|           break; | ||||
|         } | ||||
|  | ||||
|         const workerFile = match[2]; | ||||
|         let optionsObject = {}; | ||||
|         // Parse the optional options object | ||||
|         if (match[3] && match[3].length > 0) { | ||||
|           // FIXME: ooooof! | ||||
|           // eslint-disable-next-line @typescript-eslint/no-implied-eval | ||||
|           optionsObject = new Function(`return ${match[3].slice(1)};`)(); | ||||
|         } | ||||
|         delete optionsObject.type; | ||||
|  | ||||
|         if (!/^.*\//.test(workerFile)) { | ||||
|           this.warn( | ||||
|             `Paths passed to the Worker constructor must be relative or absolute, i.e. start with /, ./ or ../ (just like dynamic import!). Ignoring "${workerFile}".` | ||||
|           ); | ||||
|           continue; | ||||
|         } | ||||
|  | ||||
|         // Find worker file and store it as a chunk with ID prefixed for our loader | ||||
|         // eslint-disable-next-line no-await-in-loop | ||||
|         const resolvedWorkerFile = (await this.resolve(workerFile, id)).id; | ||||
|         let chunkRefId; | ||||
|         if (resolvedWorkerFile in refIds) { | ||||
|           chunkRefId = refIds[resolvedWorkerFile]; | ||||
|         } else { | ||||
|           this.addWatchFile(resolvedWorkerFile); | ||||
|           // eslint-disable-next-line no-await-in-loop | ||||
|           const source = await getBundledWorker( | ||||
|             resolvedWorkerFile, | ||||
|             rollupOptions | ||||
|           ); | ||||
|           chunkRefId = refIds[resolvedWorkerFile] = this.emitFile({ | ||||
|             name: path.basename(resolvedWorkerFile), | ||||
|             source, | ||||
|             type: "asset", | ||||
|           }); | ||||
|         } | ||||
|  | ||||
|         const workerParametersStartIndex = match.index + "new Worker(".length; | ||||
|         const workerParametersEndIndex = | ||||
|           match.index + match[0].length - ")".length; | ||||
|  | ||||
|         ms.overwrite( | ||||
|           workerParametersStartIndex, | ||||
|           workerParametersEndIndex, | ||||
|           `import.meta.ROLLUP_FILE_URL_${chunkRefId}, ${JSON.stringify( | ||||
|             optionsObject | ||||
|           )}` | ||||
|         ); | ||||
|       } | ||||
|  | ||||
|       return { | ||||
|         code: ms.toString(), | ||||
|         map: ms.generateMap({ hires: true }), | ||||
|       }; | ||||
|     }, | ||||
|   }; | ||||
| }; | ||||
							
								
								
									
										146
									
								
								build-scripts/rollup.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								build-scripts/rollup.cjs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,146 @@ | ||||
| const path = require("path"); | ||||
|  | ||||
| const commonjs = require("@rollup/plugin-commonjs"); | ||||
| const resolve = require("@rollup/plugin-node-resolve"); | ||||
| const json = require("@rollup/plugin-json"); | ||||
| const { babel } = require("@rollup/plugin-babel"); | ||||
| const replace = require("@rollup/plugin-replace"); | ||||
| const visualizer = require("rollup-plugin-visualizer"); | ||||
| const { string } = require("rollup-plugin-string"); | ||||
| const { terser } = require("rollup-plugin-terser"); | ||||
| const manifest = require("./rollup-plugins/manifest-plugin.cjs"); | ||||
| const worker = require("./rollup-plugins/worker-plugin.cjs"); | ||||
| const dontHashPlugin = require("./rollup-plugins/dont-hash-plugin.cjs"); | ||||
| const ignore = require("./rollup-plugins/ignore-plugin.cjs"); | ||||
|  | ||||
| const bundle = require("./bundle.cjs"); | ||||
| const paths = require("./paths.cjs"); | ||||
|  | ||||
| const extensions = [".js", ".ts"]; | ||||
|  | ||||
| /** | ||||
|  * @param {Object} arg | ||||
|  * @param { import("rollup").InputOption } arg.input | ||||
|  */ | ||||
| const createRollupConfig = ({ | ||||
|   entry, | ||||
|   outputPath, | ||||
|   defineOverlay, | ||||
|   isProdBuild, | ||||
|   latestBuild, | ||||
|   isStatsBuild, | ||||
|   publicPath, | ||||
|   dontHash, | ||||
|   isWDS, | ||||
| }) => ({ | ||||
|   /** | ||||
|    * @type { import("rollup").InputOptions } | ||||
|    */ | ||||
|   inputOptions: { | ||||
|     input: entry, | ||||
|     // Some entry points contain no JavaScript. This setting silences a warning about that. | ||||
|     // https://rollupjs.org/configuration-options/#preserveentrysignatures | ||||
|     preserveEntrySignatures: false, | ||||
|     plugins: [ | ||||
|       ignore({ | ||||
|         files: bundle | ||||
|           .emptyPackages({ latestBuild }) | ||||
|           // TEMP HACK: Makes Rollup build work again | ||||
|           .concat( | ||||
|             require.resolve( | ||||
|               "@webcomponents/scoped-custom-element-registry/scoped-custom-element-registry.min" | ||||
|             ) | ||||
|           ), | ||||
|       }), | ||||
|       resolve({ | ||||
|         extensions, | ||||
|         preferBuiltins: false, | ||||
|         browser: true, | ||||
|         rootDir: paths.polymer_dir, | ||||
|       }), | ||||
|       commonjs(), | ||||
|       json(), | ||||
|       babel({ | ||||
|         ...bundle.babelOptions({ latestBuild, isProdBuild }), | ||||
|         extensions, | ||||
|         babelHelpers: isWDS ? "inline" : "bundled", | ||||
|       }), | ||||
|       string({ | ||||
|         // Import certain extensions as strings | ||||
|         include: [path.join(paths.polymer_dir, "node_modules/**/*.css")], | ||||
|       }), | ||||
|       replace(bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })), | ||||
|       !isWDS && | ||||
|         manifest({ | ||||
|           publicPath, | ||||
|         }), | ||||
|       !isWDS && worker(), | ||||
|       !isWDS && dontHashPlugin({ dontHash }), | ||||
|       !isWDS && isProdBuild && terser(bundle.terserOptions({ latestBuild })), | ||||
|       !isWDS && | ||||
|         isStatsBuild && | ||||
|         visualizer({ | ||||
|           // https://github.com/btd/rollup-plugin-visualizer#options | ||||
|           open: true, | ||||
|           sourcemap: true, | ||||
|         }), | ||||
|     ].filter(Boolean), | ||||
|   }, | ||||
|   /** | ||||
|    * @type { import("rollup").OutputOptions } | ||||
|    */ | ||||
|   outputOptions: { | ||||
|     // https://rollupjs.org/configuration-options/#output-dir | ||||
|     dir: outputPath, | ||||
|     // https://rollupjs.org/configuration-options/#output-format | ||||
|     format: latestBuild ? "es" : "systemjs", | ||||
|     // https://rollupjs.org/configuration-options/#output-externallivebindings | ||||
|     externalLiveBindings: false, | ||||
|     // https://rollupjs.org/configuration-options/#output-entryfilenames | ||||
|     // https://rollupjs.org/configuration-options/#output-chunkfilenames | ||||
|     // https://rollupjs.org/configuration-options/#output-assetfilenames | ||||
|     entryFileNames: | ||||
|       isProdBuild && !isStatsBuild ? "[name]-[hash].js" : "[name].js", | ||||
|     chunkFileNames: isProdBuild && !isStatsBuild ? "c.[hash].js" : "[name].js", | ||||
|     assetFileNames: isProdBuild && !isStatsBuild ? "a.[hash].js" : "[name].js", | ||||
|     // https://rollupjs.org/configuration-options/#output-sourcemap | ||||
|     sourcemap: isProdBuild ? true : "inline", | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => | ||||
|   createRollupConfig( | ||||
|     bundle.config.app({ | ||||
|       isProdBuild, | ||||
|       latestBuild, | ||||
|       isStatsBuild, | ||||
|       isWDS, | ||||
|     }) | ||||
|   ); | ||||
|  | ||||
| const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => | ||||
|   createRollupConfig( | ||||
|     bundle.config.demo({ | ||||
|       isProdBuild, | ||||
|       latestBuild, | ||||
|       isStatsBuild, | ||||
|     }) | ||||
|   ); | ||||
|  | ||||
| const createCastConfig = ({ isProdBuild, latestBuild }) => | ||||
|   createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild })); | ||||
|  | ||||
| const createHassioConfig = ({ isProdBuild, latestBuild }) => | ||||
|   createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild })); | ||||
|  | ||||
| const createGalleryConfig = ({ isProdBuild, latestBuild }) => | ||||
|   createRollupConfig(bundle.config.gallery({ isProdBuild, latestBuild })); | ||||
|  | ||||
| module.exports = { | ||||
|   createAppConfig, | ||||
|   createDemoConfig, | ||||
|   createCastConfig, | ||||
|   createHassioConfig, | ||||
|   createGalleryConfig, | ||||
|   createRollupConfig, | ||||
| }; | ||||
| @@ -1,13 +1,16 @@ | ||||
| const { existsSync } = require("fs"); | ||||
| const path = require("path"); | ||||
| const rspack = require("@rspack/core"); | ||||
| const { RsdoctorRspackPlugin } = require("@rsdoctor/rspack-plugin"); | ||||
| const webpack = require("webpack"); | ||||
| const { StatsWriterPlugin } = require("webpack-stats-plugin"); | ||||
| const filterStats = require("@bundle-stats/plugin-webpack-filter").default; | ||||
| const TerserPlugin = require("terser-webpack-plugin"); | ||||
| const { WebpackManifestPlugin } = require("rspack-manifest-plugin"); | ||||
| const { WebpackManifestPlugin } = require("webpack-manifest-plugin"); | ||||
| const log = require("fancy-log"); | ||||
| const WebpackBar = require("webpackbar/rspack"); | ||||
| const WebpackBar = require("webpackbar"); | ||||
| const { | ||||
|   TransformAsyncModulesPlugin, | ||||
| } = require("transform-async-modules-webpack-plugin"); | ||||
| const { dependencies } = require("../package.json"); | ||||
| const paths = require("./paths.cjs"); | ||||
| const bundle = require("./bundle.cjs"); | ||||
| 
 | ||||
| @@ -25,7 +28,7 @@ class LogStartCompilePlugin { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| const createRspackConfig = ({ | ||||
| const createWebpackConfig = ({ | ||||
|   name, | ||||
|   entry, | ||||
|   outputPath, | ||||
| @@ -99,18 +102,13 @@ const createRspackConfig = ({ | ||||
|       splitChunks: { | ||||
|         // Disable splitting for web workers and worklets because imports of
 | ||||
|         // external chunks are broken for:
 | ||||
|         chunks: !isProdBuild | ||||
|           ? // improve incremental build speed, but blows up bundle size
 | ||||
|             new RegExp( | ||||
|               `^(?!(${Object.keys(entry).join("|")}|.*work(?:er|let))$)` | ||||
|             ) | ||||
|           : // - ESM output: https://github.com/webpack/webpack/issues/17014
 | ||||
|             // - Worklets use `importScripts`: https://github.com/webpack/webpack/issues/11543
 | ||||
|             (chunk) => | ||||
|               !chunk.canBeInitial() && | ||||
|               !new RegExp( | ||||
|                 `^.+-work${latestBuild ? "(?:let|er)" : "let"}$` | ||||
|               ).test(chunk.name), | ||||
|         // - ESM output: https://github.com/webpack/webpack/issues/17014
 | ||||
|         // - Worklets use `importScripts`: https://github.com/webpack/webpack/issues/11543
 | ||||
|         chunks: (chunk) => | ||||
|           !chunk.canBeInitial() && | ||||
|           !new RegExp(`^.+-work${latestBuild ? "(?:let|er)" : "let"}$`).test( | ||||
|             chunk.name | ||||
|           ), | ||||
|       }, | ||||
|     }, | ||||
|     plugins: [ | ||||
| @@ -119,10 +117,10 @@ const createRspackConfig = ({ | ||||
|         // Only include the JS of entrypoints
 | ||||
|         filter: (file) => file.isInitial && !file.name.endsWith(".map"), | ||||
|       }), | ||||
|       new rspack.DefinePlugin( | ||||
|       new webpack.DefinePlugin( | ||||
|         bundle.definedVars({ isProdBuild, latestBuild, defineOverlay }) | ||||
|       ), | ||||
|       new rspack.IgnorePlugin({ | ||||
|       new webpack.IgnorePlugin({ | ||||
|         checkResource(resource, context) { | ||||
|           // Only use ignore to intercept imports that we don't control
 | ||||
|           // inside node_module dependencies.
 | ||||
| @@ -154,7 +152,7 @@ const createRspackConfig = ({ | ||||
|           ); | ||||
|         }, | ||||
|       }), | ||||
|       new rspack.NormalModuleReplacementPlugin( | ||||
|       new webpack.NormalModuleReplacementPlugin( | ||||
|         new RegExp( | ||||
|           bundle.emptyPackages({ latestBuild, isHassioBuild }).join("|") | ||||
|         ), | ||||
| @@ -170,14 +168,10 @@ const createRspackConfig = ({ | ||||
|           stats: { assets: true, chunks: true, modules: true }, | ||||
|           transform: (stats) => JSON.stringify(filterStats(stats)), | ||||
|         }), | ||||
|       isProdBuild && | ||||
|         isStatsBuild && | ||||
|         new RsdoctorRspackPlugin({ | ||||
|           reportDir: path.join(paths.build_dir, "rsdoctor"), | ||||
|           features: ["plugins", "bundle"], | ||||
|           supports: { | ||||
|             generateTileGraph: true, | ||||
|           }, | ||||
|       !latestBuild && | ||||
|         new TransformAsyncModulesPlugin({ | ||||
|           browserslistEnv: "legacy", | ||||
|           runtime: { version: dependencies["@babel/runtime"] }, | ||||
|         }), | ||||
|     ].filter(Boolean), | ||||
|     resolve: { | ||||
| @@ -194,7 +188,6 @@ const createRspackConfig = ({ | ||||
|         "lit/directives/cache$": "lit/directives/cache.js", | ||||
|         "lit/directives/repeat$": "lit/directives/repeat.js", | ||||
|         "lit/directives/live$": "lit/directives/live.js", | ||||
|         "lit/directives/keyed$": "lit/directives/keyed.js", | ||||
|         "lit/polyfill-support$": "lit/polyfill-support.js", | ||||
|         "@lit-labs/virtualizer/layouts/grid": | ||||
|           "@lit-labs/virtualizer/layouts/grid.js", | ||||
| @@ -216,6 +209,8 @@ const createRspackConfig = ({ | ||||
|         isProdBuild && !isStatsBuild ? "[id].[contenthash][ext]" : "[id][ext]", | ||||
|       crossOriginLoading: "use-credentials", | ||||
|       hashFunction: "xxhash64", | ||||
|       hashDigest: "base64url", | ||||
|       hashDigestLength: 11, // full length of 64 bit base64url
 | ||||
|       path: outputPath, | ||||
|       publicPath, | ||||
|       // To silence warning in worker plugin
 | ||||
| @@ -257,17 +252,17 @@ const createAppConfig = ({ | ||||
|   isStatsBuild, | ||||
|   isTestBuild, | ||||
| }) => | ||||
|   createRspackConfig( | ||||
|   createWebpackConfig( | ||||
|     bundle.config.app({ isProdBuild, latestBuild, isStatsBuild, isTestBuild }) | ||||
|   ); | ||||
| 
 | ||||
| const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => | ||||
|   createRspackConfig( | ||||
|   createWebpackConfig( | ||||
|     bundle.config.demo({ isProdBuild, latestBuild, isStatsBuild }) | ||||
|   ); | ||||
| 
 | ||||
| const createCastConfig = ({ isProdBuild, latestBuild }) => | ||||
|   createRspackConfig(bundle.config.cast({ isProdBuild, latestBuild })); | ||||
|   createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild })); | ||||
| 
 | ||||
| const createHassioConfig = ({ | ||||
|   isProdBuild, | ||||
| @@ -275,7 +270,7 @@ const createHassioConfig = ({ | ||||
|   isStatsBuild, | ||||
|   isTestBuild, | ||||
| }) => | ||||
|   createRspackConfig( | ||||
|   createWebpackConfig( | ||||
|     bundle.config.hassio({ | ||||
|       isProdBuild, | ||||
|       latestBuild, | ||||
| @@ -285,10 +280,7 @@ const createHassioConfig = ({ | ||||
|   ); | ||||
| 
 | ||||
| const createGalleryConfig = ({ isProdBuild, latestBuild }) => | ||||
|   createRspackConfig(bundle.config.gallery({ isProdBuild, latestBuild })); | ||||
| 
 | ||||
| const createLandingPageConfig = ({ isProdBuild, latestBuild }) => | ||||
|   createRspackConfig(bundle.config.landingPage({ isProdBuild, latestBuild })); | ||||
|   createWebpackConfig(bundle.config.gallery({ isProdBuild, latestBuild })); | ||||
| 
 | ||||
| module.exports = { | ||||
|   createAppConfig, | ||||
| @@ -296,6 +288,5 @@ module.exports = { | ||||
|   createCastConfig, | ||||
|   createHassioConfig, | ||||
|   createGalleryConfig, | ||||
|   createRspackConfig, | ||||
|   createLandingPageConfig, | ||||
|   createWebpackConfig, | ||||
| }; | ||||
| @@ -25,7 +25,7 @@ Home Assistant Cast is made up of two separate applications: | ||||
|  | ||||
| ### Setting dev variables | ||||
|  | ||||
| Open `src/cast/dev_const.ts` and change `CAST_DEV_APP_ID` to the ID of the app you just created. And set the `CAST_DEV_HASS_URL` to the url of your development machine. | ||||
| Open `src/cast/dev_const.ts` and change `CAST_DEV_APP_ID` to the ID of the app you just created. And set the `CAST_DEV_HASS_URL` to the url of you development machine. | ||||
|  | ||||
| ### Changing configuration | ||||
|  | ||||
|   | ||||
							
								
								
									
										10
									
								
								cast/rollup.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								cast/rollup.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| import rollup from "../build-scripts/rollup.cjs"; | ||||
| import env from "../build-scripts/env.cjs"; | ||||
|  | ||||
| const config = rollup.createCastConfig({ | ||||
|   isProdBuild: env.isProdBuild(), | ||||
|   latestBuild: true, | ||||
|   isStatsBuild: env.isStatsBuild(), | ||||
| }); | ||||
|  | ||||
| export default { ...config.inputOptions, output: config.outputOptions }; | ||||
| @@ -139,7 +139,7 @@ | ||||
|         </p> | ||||
|       </div> | ||||
|  | ||||
|       <div class="section-header">What does Home Assistant Cast do?</div> | ||||
|       <div class="section-header">Wat does Home Assistant Cast do?</div> | ||||
|       <div class="card-content"> | ||||
|         <p> | ||||
|           Home Assistant Cast is a receiver application for the Chromecast. When | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
|   <%= renderTemplate("../../../src/html/_style_base.html.template") %> | ||||
|   <style> | ||||
|     body { | ||||
|       background-color: white; | ||||
|       font-size: initial; | ||||
|     } | ||||
|   </style> | ||||
|   | ||||
| @@ -1,12 +1,10 @@ | ||||
| import "@material/mwc-button/mwc-button"; | ||||
| import "@material/mwc-list/mwc-list"; | ||||
| import type { ActionDetail } from "@material/mwc-list/mwc-list"; | ||||
| import { ActionDetail } from "@material/mwc-list/mwc-list"; | ||||
| import { mdiCast, mdiCastConnected, mdiViewDashboard } from "@mdi/js"; | ||||
| import type { Auth, Connection } from "home-assistant-js-websocket"; | ||||
| import type { TemplateResult } from "lit"; | ||||
| import { LitElement, css, html } from "lit"; | ||||
| import { Auth, Connection } from "home-assistant-js-websocket"; | ||||
| import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit"; | ||||
| import { customElement, property, state } from "lit/decorators"; | ||||
| import type { CastManager } from "../../../../src/cast/cast_manager"; | ||||
| import { CastManager } from "../../../../src/cast/cast_manager"; | ||||
| import { | ||||
|   castSendShowLovelaceView, | ||||
|   ensureConnectedCastSession, | ||||
| @@ -25,7 +23,7 @@ import { | ||||
|   getLovelaceCollection, | ||||
| } from "../../../../src/data/lovelace"; | ||||
| import { isStrategyDashboard } from "../../../../src/data/lovelace/config/types"; | ||||
| import type { LovelaceViewConfig } from "../../../../src/data/lovelace/config/view"; | ||||
| import { LovelaceViewConfig } from "../../../../src/data/lovelace/config/view"; | ||||
| import "../../../../src/layouts/hass-loading-screen"; | ||||
| import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config"; | ||||
| import "./hc-layout"; | ||||
| @@ -91,8 +89,8 @@ class HcCast extends LitElement { | ||||
|                       generateDefaultViewConfig({}, {}, {}, {}, () => ""), | ||||
|                     ] | ||||
|                   ).map( | ||||
|                     (view, idx) => html` | ||||
|                       <ha-list-item | ||||
|                     (view, idx) => | ||||
|                       html`<ha-list-item | ||||
|                         graphic="avatar" | ||||
|                         .activated=${this.castManager.status?.lovelacePath === | ||||
|                         (view.path ?? idx)} | ||||
| @@ -110,9 +108,8 @@ class HcCast extends LitElement { | ||||
|                           : html`<ha-svg-icon | ||||
|                               slot="item-icon" | ||||
|                               .path=${mdiViewDashboard} | ||||
|                             ></ha-svg-icon>`} | ||||
|                       </ha-list-item> | ||||
|                     ` | ||||
|                             ></ha-svg-icon>`}</ha-list-item | ||||
|                       > ` | ||||
|                   )}</mwc-list | ||||
|                 > | ||||
|               `} | ||||
| @@ -203,72 +200,74 @@ class HcCast extends LitElement { | ||||
|       } | ||||
|       this.connection.close(); | ||||
|       location.reload(); | ||||
|     } catch (_err: any) { | ||||
|     } catch (err: any) { | ||||
|       alert("Unable to log out!"); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static styles = css` | ||||
|     .center-item { | ||||
|       display: flex; | ||||
|       justify-content: space-around; | ||||
|     } | ||||
|   static get styles(): CSSResultGroup { | ||||
|     return css` | ||||
|       .center-item { | ||||
|         display: flex; | ||||
|         justify-content: space-around; | ||||
|       } | ||||
|  | ||||
|     .action-item { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       justify-content: space-between; | ||||
|     } | ||||
|       .action-item { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: space-between; | ||||
|       } | ||||
|  | ||||
|     .question { | ||||
|       position: relative; | ||||
|       padding: 8px 16px; | ||||
|     } | ||||
|       .question { | ||||
|         position: relative; | ||||
|         padding: 8px 16px; | ||||
|       } | ||||
|  | ||||
|     .question:before { | ||||
|       border-radius: 4px; | ||||
|       position: absolute; | ||||
|       top: 0; | ||||
|       right: 0; | ||||
|       bottom: 0; | ||||
|       left: 0; | ||||
|       pointer-events: none; | ||||
|       content: ""; | ||||
|       background-color: var(--primary-color); | ||||
|       opacity: 0.12; | ||||
|       will-change: opacity; | ||||
|     } | ||||
|       .question:before { | ||||
|         border-radius: 4px; | ||||
|         position: absolute; | ||||
|         top: 0; | ||||
|         right: 0; | ||||
|         bottom: 0; | ||||
|         left: 0; | ||||
|         pointer-events: none; | ||||
|         content: ""; | ||||
|         background-color: var(--primary-color); | ||||
|         opacity: 0.12; | ||||
|         will-change: opacity; | ||||
|       } | ||||
|  | ||||
|     .connection, | ||||
|     .connection a { | ||||
|       color: var(--secondary-text-color); | ||||
|     } | ||||
|       .connection, | ||||
|       .connection a { | ||||
|         color: var(--secondary-text-color); | ||||
|       } | ||||
|  | ||||
|     mwc-button ha-svg-icon { | ||||
|       margin-right: 8px; | ||||
|       margin-inline-end: 8px; | ||||
|       margin-inline-start: initial; | ||||
|       height: 18px; | ||||
|     } | ||||
|       mwc-button ha-svg-icon { | ||||
|         margin-right: 8px; | ||||
|         margin-inline-end: 8px; | ||||
|         margin-inline-start: initial; | ||||
|         height: 18px; | ||||
|       } | ||||
|  | ||||
|     ha-list-item ha-icon, | ||||
|     ha-list-item ha-svg-icon { | ||||
|       padding: 12px; | ||||
|       color: var(--secondary-text-color); | ||||
|     } | ||||
|       ha-list-item ha-icon, | ||||
|       ha-list-item ha-svg-icon { | ||||
|         padding: 12px; | ||||
|         color: var(--secondary-text-color); | ||||
|       } | ||||
|  | ||||
|     :host([hide-icons]) ha-icon { | ||||
|       display: none; | ||||
|     } | ||||
|       :host([hide-icons]) ha-icon { | ||||
|         display: none; | ||||
|       } | ||||
|  | ||||
|     .spacer { | ||||
|       flex: 1; | ||||
|     } | ||||
|       .spacer { | ||||
|         flex: 1; | ||||
|       } | ||||
|  | ||||
|     .card-content a { | ||||
|       color: var(--primary-color); | ||||
|     } | ||||
|   `; | ||||
|       .card-content a { | ||||
|         color: var(--primary-color); | ||||
|       } | ||||
|     `; | ||||
|   } | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|   | ||||
| @@ -1,23 +1,19 @@ | ||||
| import "@material/mwc-button"; | ||||
| import { mdiCastConnected, mdiCast } from "@mdi/js"; | ||||
| import type { | ||||
| import { | ||||
|   Auth, | ||||
|   Connection, | ||||
|   getAuthOptions, | ||||
| } from "home-assistant-js-websocket"; | ||||
| import { | ||||
|   createConnection, | ||||
|   ERR_CANNOT_CONNECT, | ||||
|   ERR_HASS_HOST_REQUIRED, | ||||
|   ERR_INVALID_AUTH, | ||||
|   ERR_INVALID_HTTPS_TO_HTTP, | ||||
|   getAuth, | ||||
|   getAuthOptions, | ||||
| } from "home-assistant-js-websocket"; | ||||
| import type { TemplateResult } from "lit"; | ||||
| import { css, html, LitElement } from "lit"; | ||||
| import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; | ||||
| import { customElement, state } from "lit/decorators"; | ||||
| import type { CastManager } from "../../../../src/cast/cast_manager"; | ||||
| import { getCastManager } from "../../../../src/cast/cast_manager"; | ||||
| import { CastManager, getCastManager } from "../../../../src/cast/cast_manager"; | ||||
| import { castSendShowDemo } from "../../../../src/cast/receiver_messages"; | ||||
| import { | ||||
|   loadTokens, | ||||
| @@ -215,7 +211,7 @@ export class HcConnect extends LitElement { | ||||
|     let url: URL; | ||||
|     try { | ||||
|       url = new URL(value); | ||||
|     } catch (_err: any) { | ||||
|     } catch (err: any) { | ||||
|       this.error = "Invalid URL"; | ||||
|       return; | ||||
|     } | ||||
| @@ -252,7 +248,7 @@ export class HcConnect extends LitElement { | ||||
|       this.loading = false; | ||||
|       return; | ||||
|     } finally { | ||||
|       // Clear url if we have an auth callback in url. | ||||
|       // Clear url if we have a auth callback in url. | ||||
|       if (location.search.includes("auth_callback=1")) { | ||||
|         history.replaceState(null, "", location.pathname); | ||||
|       } | ||||
| @@ -288,39 +284,41 @@ export class HcConnect extends LitElement { | ||||
|     try { | ||||
|       saveTokens(null); | ||||
|       location.reload(); | ||||
|     } catch (_err: any) { | ||||
|     } catch (err: any) { | ||||
|       alert("Unable to log out!"); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static styles = css` | ||||
|     .card-content a { | ||||
|       color: var(--primary-color); | ||||
|     } | ||||
|     .card-actions a { | ||||
|       text-decoration: none; | ||||
|     } | ||||
|     .error { | ||||
|       color: red; | ||||
|       font-weight: bold; | ||||
|     } | ||||
|   static get styles(): CSSResultGroup { | ||||
|     return css` | ||||
|       .card-content a { | ||||
|         color: var(--primary-color); | ||||
|       } | ||||
|       .card-actions a { | ||||
|         text-decoration: none; | ||||
|       } | ||||
|       .error { | ||||
|         color: red; | ||||
|         font-weight: bold; | ||||
|       } | ||||
|  | ||||
|     .error a { | ||||
|       color: darkred; | ||||
|     } | ||||
|       .error a { | ||||
|         color: darkred; | ||||
|       } | ||||
|  | ||||
|     mwc-button ha-svg-icon { | ||||
|       margin-left: 8px; | ||||
|     } | ||||
|       mwc-button ha-svg-icon { | ||||
|         margin-left: 8px; | ||||
|       } | ||||
|  | ||||
|     .spacer { | ||||
|       flex: 1; | ||||
|     } | ||||
|       .spacer { | ||||
|         flex: 1; | ||||
|       } | ||||
|  | ||||
|     ha-textfield { | ||||
|       width: 100%; | ||||
|     } | ||||
|   `; | ||||
|       ha-textfield { | ||||
|         width: 100%; | ||||
|       } | ||||
|     `; | ||||
|   } | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|   | ||||
| @@ -1,7 +1,10 @@ | ||||
| import type { Auth, Connection, HassUser } from "home-assistant-js-websocket"; | ||||
| import { getUser } from "home-assistant-js-websocket"; | ||||
| import type { TemplateResult } from "lit"; | ||||
| import { css, html, LitElement } from "lit"; | ||||
| import { | ||||
|   Auth, | ||||
|   Connection, | ||||
|   getUser, | ||||
|   HassUser, | ||||
| } from "home-assistant-js-websocket"; | ||||
| import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; | ||||
| import { customElement, property } from "lit/decorators"; | ||||
| import "../../../../src/components/ha-card"; | ||||
|  | ||||
| @@ -63,94 +66,96 @@ class HcLayout extends LitElement { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static styles = css` | ||||
|     :host { | ||||
|       display: flex; | ||||
|       min-height: 100%; | ||||
|       align-items: center; | ||||
|       justify-content: center; | ||||
|       flex-direction: column; | ||||
|     } | ||||
|  | ||||
|     ha-card { | ||||
|       display: flex; | ||||
|       width: 100%; | ||||
|       max-width: 500px; | ||||
|     } | ||||
|  | ||||
|     .layout { | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|     } | ||||
|  | ||||
|     .card-header { | ||||
|       color: var(--ha-card-header-color, var(--primary-text-color)); | ||||
|       font-family: var(--ha-card-header-font-family, inherit); | ||||
|       font-size: var(--ha-card-header-font-size, 24px); | ||||
|       letter-spacing: -0.012em; | ||||
|       line-height: 32px; | ||||
|       padding: 24px 16px 16px; | ||||
|       display: block; | ||||
|       margin: 0; | ||||
|     } | ||||
|  | ||||
|     .hero { | ||||
|       border-radius: 4px 4px 0 0; | ||||
|     } | ||||
|     .subtitle { | ||||
|       font-size: 14px; | ||||
|       color: var(--secondary-text-color); | ||||
|       line-height: initial; | ||||
|     } | ||||
|     .subtitle a { | ||||
|       color: var(--secondary-text-color); | ||||
|     } | ||||
|  | ||||
|     :host ::slotted(.card-content:not(:first-child)), | ||||
|     slot:not(:first-child)::slotted(.card-content) { | ||||
|       padding-top: 0px; | ||||
|       margin-top: -8px; | ||||
|     } | ||||
|  | ||||
|     :host ::slotted(.section-header) { | ||||
|       font-weight: 500; | ||||
|       padding: 4px 16px; | ||||
|       text-transform: uppercase; | ||||
|     } | ||||
|  | ||||
|     :host ::slotted(.card-content) { | ||||
|       padding: 16px; | ||||
|       flex: 1; | ||||
|     } | ||||
|  | ||||
|     :host ::slotted(.card-actions) { | ||||
|       border-top: 1px solid #e8e8e8; | ||||
|       padding: 5px 16px; | ||||
|       display: flex; | ||||
|     } | ||||
|  | ||||
|     img { | ||||
|       width: 100%; | ||||
|     } | ||||
|  | ||||
|     .footer { | ||||
|       text-align: center; | ||||
|       font-size: 12px; | ||||
|       padding: 8px 0 24px; | ||||
|       color: var(--secondary-text-color); | ||||
|     } | ||||
|     .footer a { | ||||
|       color: var(--secondary-text-color); | ||||
|     } | ||||
|  | ||||
|     @media all and (max-width: 500px) { | ||||
|   static get styles(): CSSResultGroup { | ||||
|     return css` | ||||
|       :host { | ||||
|         justify-content: flex-start; | ||||
|         min-height: 90%; | ||||
|         margin-bottom: 30px; | ||||
|         display: flex; | ||||
|         min-height: 100%; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|         flex-direction: column; | ||||
|       } | ||||
|     } | ||||
|   `; | ||||
|  | ||||
|       ha-card { | ||||
|         display: flex; | ||||
|         width: 100%; | ||||
|         max-width: 500px; | ||||
|       } | ||||
|  | ||||
|       .layout { | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|       } | ||||
|  | ||||
|       .card-header { | ||||
|         color: var(--ha-card-header-color, --primary-text-color); | ||||
|         font-family: var(--ha-card-header-font-family, inherit); | ||||
|         font-size: var(--ha-card-header-font-size, 24px); | ||||
|         letter-spacing: -0.012em; | ||||
|         line-height: 32px; | ||||
|         padding: 24px 16px 16px; | ||||
|         display: block; | ||||
|         margin: 0; | ||||
|       } | ||||
|  | ||||
|       .hero { | ||||
|         border-radius: 4px 4px 0 0; | ||||
|       } | ||||
|       .subtitle { | ||||
|         font-size: 14px; | ||||
|         color: var(--secondary-text-color); | ||||
|         line-height: initial; | ||||
|       } | ||||
|       .subtitle a { | ||||
|         color: var(--secondary-text-color); | ||||
|       } | ||||
|  | ||||
|       :host ::slotted(.card-content:not(:first-child)), | ||||
|       slot:not(:first-child)::slotted(.card-content) { | ||||
|         padding-top: 0px; | ||||
|         margin-top: -8px; | ||||
|       } | ||||
|  | ||||
|       :host ::slotted(.section-header) { | ||||
|         font-weight: 500; | ||||
|         padding: 4px 16px; | ||||
|         text-transform: uppercase; | ||||
|       } | ||||
|  | ||||
|       :host ::slotted(.card-content) { | ||||
|         padding: 16px; | ||||
|         flex: 1; | ||||
|       } | ||||
|  | ||||
|       :host ::slotted(.card-actions) { | ||||
|         border-top: 1px solid #e8e8e8; | ||||
|         padding: 5px 16px; | ||||
|         display: flex; | ||||
|       } | ||||
|  | ||||
|       img { | ||||
|         width: 100%; | ||||
|       } | ||||
|  | ||||
|       .footer { | ||||
|         text-align: center; | ||||
|         font-size: 12px; | ||||
|         padding: 8px 0 24px; | ||||
|         color: var(--secondary-text-color); | ||||
|       } | ||||
|       .footer a { | ||||
|         color: var(--secondary-text-color); | ||||
|       } | ||||
|  | ||||
|       @media all and (max-width: 500px) { | ||||
|         :host { | ||||
|           justify-content: flex-start; | ||||
|           min-height: 90%; | ||||
|           margin-bottom: 30px; | ||||
|         } | ||||
|       } | ||||
|     `; | ||||
|   } | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| import type { Entity } from "../../../../src/fake_data/entity"; | ||||
| import { convertEntities } from "../../../../src/fake_data/entity"; | ||||
| import { convertEntities, Entity } from "../../../../src/fake_data/entity"; | ||||
|  | ||||
| export const castDemoEntities: () => Entity[] = () => | ||||
|   convertEntities({ | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import type { LovelaceCardConfig } from "../../../../src/data/lovelace/config/card"; | ||||
| import type { LovelaceConfig } from "../../../../src/data/lovelace/config/types"; | ||||
| import { LovelaceCardConfig } from "../../../../src/data/lovelace/config/card"; | ||||
| import { LovelaceConfig } from "../../../../src/data/lovelace/config/types"; | ||||
| import { castContext } from "../cast_context"; | ||||
|  | ||||
| export const castDemoLovelace: () => LovelaceConfig = () => { | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| import { framework } from "./cast_framework"; | ||||
| import { CAST_NS } from "../../../src/cast/const"; | ||||
| import type { HassMessage } from "../../../src/cast/receiver_messages"; | ||||
| import { HassMessage } from "../../../src/cast/receiver_messages"; | ||||
| import "../../../src/resources/custom-card-support"; | ||||
| import { castContext } from "./cast_context"; | ||||
| import { HcMain } from "./layout/hc-main"; | ||||
| import type { ReceivedMessage } from "./types"; | ||||
| import { ReceivedMessage } from "./types"; | ||||
|  | ||||
| const lovelaceController = new HcMain(); | ||||
| document.body.append(lovelaceController); | ||||
|   | ||||
| @@ -1,11 +1,13 @@ | ||||
| import { html, nothing } from "lit"; | ||||
| import { customElement, property, state } from "lit/decorators"; | ||||
| import { mockHistory } from "../../../../demo/src/stubs/history"; | ||||
| import type { LovelaceConfig } from "../../../../src/data/lovelace/config/types"; | ||||
| import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass"; | ||||
| import { provideHass } from "../../../../src/fake_data/provide_hass"; | ||||
| import { LovelaceConfig } from "../../../../src/data/lovelace/config/types"; | ||||
| import { | ||||
|   MockHomeAssistant, | ||||
|   provideHass, | ||||
| } from "../../../../src/fake_data/provide_hass"; | ||||
| import { HassElement } from "../../../../src/state/hass-element"; | ||||
| import type { HomeAssistant } from "../../../../src/types"; | ||||
| import { HomeAssistant } from "../../../../src/types"; | ||||
| import { castDemoEntities } from "../demo/cast-demo-entities"; | ||||
| import { castDemoLovelace } from "../demo/cast-demo-lovelace"; | ||||
| import "./hc-lovelace"; | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| import type { TemplateResult } from "lit"; | ||||
| import { css, html, LitElement } from "lit"; | ||||
| import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; | ||||
| import { customElement, property } from "lit/decorators"; | ||||
| import type { HomeAssistant } from "../../../../src/types"; | ||||
| import { HomeAssistant } from "../../../../src/types"; | ||||
|  | ||||
| @customElement("hc-launch-screen") | ||||
| class HcLaunchScreen extends LitElement { | ||||
| @@ -24,29 +23,31 @@ class HcLaunchScreen extends LitElement { | ||||
|     `; | ||||
|   } | ||||
|  | ||||
|   static styles = css` | ||||
|     :host { | ||||
|       display: block; | ||||
|       height: 100vh; | ||||
|       background-color: #f2f4f9; | ||||
|       font-size: 24px; | ||||
|     } | ||||
|     .container { | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       text-align: center; | ||||
|       align-items: center; | ||||
|       height: 100%; | ||||
|       justify-content: space-evenly; | ||||
|     } | ||||
|     img { | ||||
|       max-width: 80%; | ||||
|       object-fit: cover; | ||||
|     } | ||||
|     .status { | ||||
|       color: #1d2126; | ||||
|     } | ||||
|   `; | ||||
|   static get styles(): CSSResultGroup { | ||||
|     return css` | ||||
|       :host { | ||||
|         display: block; | ||||
|         height: 100vh; | ||||
|         background-color: #f2f4f9; | ||||
|         font-size: 24px; | ||||
|       } | ||||
|       .container { | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         text-align: center; | ||||
|         align-items: center; | ||||
|         height: 100%; | ||||
|         justify-content: space-evenly; | ||||
|       } | ||||
|       img { | ||||
|         max-width: 80%; | ||||
|         object-fit: cover; | ||||
|       } | ||||
|       .status { | ||||
|         color: #1d2126; | ||||
|       } | ||||
|     `; | ||||
|   } | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|   | ||||
| @@ -1,14 +1,12 @@ | ||||
| import { css, html, LitElement, type TemplateResult } from "lit"; | ||||
| import { customElement, property } from "lit/decorators"; | ||||
| import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; | ||||
| import { customElement, property, query } from "lit/decorators"; | ||||
| import { fireEvent } from "../../../../src/common/dom/fire_event"; | ||||
| import type { LovelaceConfig } from "../../../../src/data/lovelace/config/types"; | ||||
| import { LovelaceConfig } from "../../../../src/data/lovelace/config/types"; | ||||
| import { getPanelTitleFromUrlPath } from "../../../../src/data/panel"; | ||||
| import type { Lovelace } from "../../../../src/panels/lovelace/types"; | ||||
| import { Lovelace } from "../../../../src/panels/lovelace/types"; | ||||
| import "../../../../src/panels/lovelace/views/hui-view"; | ||||
| import "../../../../src/panels/lovelace/views/hui-view-container"; | ||||
| import type { HomeAssistant } from "../../../../src/types"; | ||||
| import { HomeAssistant } from "../../../../src/types"; | ||||
| import "./hc-launch-screen"; | ||||
| import "../../../../src/panels/lovelace/views/hui-view-background"; | ||||
|  | ||||
| (window as any).loadCardHelpers = () => | ||||
|   import("../../../../src/panels/lovelace/custom-card-helpers"); | ||||
| @@ -20,9 +18,11 @@ class HcLovelace extends LitElement { | ||||
|   @property({ attribute: false }) | ||||
|   public lovelaceConfig!: LovelaceConfig; | ||||
|  | ||||
|   @property({ attribute: false }) public viewPath?: string | number | null; | ||||
|   @property() public viewPath?: string | number | null; | ||||
|  | ||||
|   @property({ attribute: false }) public urlPath: string | null = null; | ||||
|   @property() public urlPath: string | null = null; | ||||
|  | ||||
|   @query("hui-view") private _huiView?: HTMLElement; | ||||
|  | ||||
|   protected render(): TemplateResult { | ||||
|     const index = this._viewIndex; | ||||
| @@ -45,22 +45,13 @@ class HcLovelace extends LitElement { | ||||
|       saveConfig: async () => undefined, | ||||
|       deleteConfig: async () => undefined, | ||||
|       setEditMode: () => undefined, | ||||
|       showToast: () => undefined, | ||||
|     }; | ||||
|  | ||||
|     const viewConfig = this.lovelaceConfig.views[index]; | ||||
|     const background = viewConfig.background || this.lovelaceConfig.background; | ||||
|  | ||||
|     return html` | ||||
|       <hui-view-container .hass=${this.hass} .theme=${viewConfig.theme}> | ||||
|         <hui-view-background .hass=${this.hass} .background=${background}> | ||||
|         </hui-view-background> | ||||
|         <hui-view | ||||
|           .hass=${this.hass} | ||||
|           .lovelace=${lovelace} | ||||
|           .index=${index} | ||||
|         ></hui-view> | ||||
|       </hui-view-container> | ||||
|       <hui-view | ||||
|         .hass=${this.hass} | ||||
|         .lovelace=${lovelace} | ||||
|         .index=${index} | ||||
|       ></hui-view> | ||||
|     `; | ||||
|   } | ||||
|  | ||||
| @@ -90,6 +81,26 @@ class HcLovelace extends LitElement { | ||||
|                 }${viewTitle || ""}` | ||||
|               : undefined, | ||||
|         }); | ||||
|  | ||||
|         const configBackground = | ||||
|           this.lovelaceConfig.views[index].background || | ||||
|           this.lovelaceConfig.background; | ||||
|  | ||||
|         const backgroundStyle = | ||||
|           typeof configBackground === "string" | ||||
|             ? configBackground | ||||
|             : configBackground?.image | ||||
|               ? `center / cover no-repeat url('${configBackground.image}')` | ||||
|               : undefined; | ||||
|  | ||||
|         if (backgroundStyle) { | ||||
|           this._huiView!.style.setProperty( | ||||
|             "--lovelace-background", | ||||
|             backgroundStyle | ||||
|           ); | ||||
|         } else { | ||||
|           this._huiView!.style.removeProperty("--lovelace-background"); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -111,18 +122,24 @@ class HcLovelace extends LitElement { | ||||
|     return undefined; | ||||
|   } | ||||
|  | ||||
|   static styles = css` | ||||
|     hui-view-container { | ||||
|       display: flex; | ||||
|       position: relative; | ||||
|       min-height: 100vh; | ||||
|       box-sizing: border-box; | ||||
|     } | ||||
|     hui-view-container > * { | ||||
|       flex: 1 1 100%; | ||||
|       max-width: 100%; | ||||
|     } | ||||
|   `; | ||||
|   static get styles(): CSSResultGroup { | ||||
|     return css` | ||||
|       :host { | ||||
|         min-height: 100vh; | ||||
|         height: 0; | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         box-sizing: border-box; | ||||
|         background: var(--primary-background-color); | ||||
|       } | ||||
|       :host > * { | ||||
|         flex: 1; | ||||
|       } | ||||
|       hui-view { | ||||
|         background: var(--lovelace-background, var(--primary-background-color)); | ||||
|       } | ||||
|     `; | ||||
|   } | ||||
| } | ||||
|  | ||||
| export interface CastViewChanged { | ||||
|   | ||||
| @@ -1,40 +1,41 @@ | ||||
| import type { UnsubscribeFunc } from "home-assistant-js-websocket"; | ||||
| import { createConnection, getAuth } from "home-assistant-js-websocket"; | ||||
| import type { TemplateResult } from "lit"; | ||||
| import { html } from "lit"; | ||||
| import { | ||||
|   createConnection, | ||||
|   getAuth, | ||||
|   UnsubscribeFunc, | ||||
| } from "home-assistant-js-websocket"; | ||||
| import { html, TemplateResult } from "lit"; | ||||
| import { customElement, state } from "lit/decorators"; | ||||
| import { CAST_NS } from "../../../../src/cast/const"; | ||||
| import type { | ||||
| import { | ||||
|   ConnectMessage, | ||||
|   GetStatusMessage, | ||||
|   HassMessage, | ||||
|   ShowDemoMessage, | ||||
|   ShowLovelaceViewMessage, | ||||
| } from "../../../../src/cast/receiver_messages"; | ||||
| import type { | ||||
| import { | ||||
|   ReceiverErrorCode, | ||||
|   ReceiverErrorMessage, | ||||
|   ReceiverStatusMessage, | ||||
| } from "../../../../src/cast/sender_messages"; | ||||
| import { ReceiverErrorCode } from "../../../../src/cast/sender_messages"; | ||||
| import { atLeastVersion } from "../../../../src/common/config/version"; | ||||
| import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click"; | ||||
| import { | ||||
|   getLegacyLovelaceCollection, | ||||
|   getLovelaceCollection, | ||||
| } from "../../../../src/data/lovelace"; | ||||
| import type { | ||||
| import { | ||||
|   isStrategyDashboard, | ||||
|   LegacyLovelaceConfig, | ||||
|   LovelaceConfig, | ||||
|   LovelaceDashboardStrategyConfig, | ||||
| } from "../../../../src/data/lovelace/config/types"; | ||||
| import { isStrategyDashboard } from "../../../../src/data/lovelace/config/types"; | ||||
| import { fetchResources } from "../../../../src/data/lovelace/resource"; | ||||
| import { loadLovelaceResources } from "../../../../src/panels/lovelace/common/load-resources"; | ||||
| import { HassElement } from "../../../../src/state/hass-element"; | ||||
| import { castContext } from "../cast_context"; | ||||
| import "./hc-launch-screen"; | ||||
| import { getPanelTitleFromUrlPath } from "../../../../src/data/panel"; | ||||
| import { checkLovelaceConfig } from "../../../../src/panels/lovelace/common/check-lovelace-config"; | ||||
|  | ||||
| const DEFAULT_CONFIG: LovelaceDashboardStrategyConfig = { | ||||
|   strategy: { | ||||
| @@ -144,10 +145,10 @@ export class HcMain extends HassElement { | ||||
|     } | ||||
|  | ||||
|     if (senderId) { | ||||
|       this._sendMessage(senderId, status); | ||||
|       this.sendMessage(senderId, status); | ||||
|     } else { | ||||
|       for (const sender of castContext.getSenders()) { | ||||
|         this._sendMessage(sender.id, status); | ||||
|         this.sendMessage(sender.id, status); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -164,10 +165,10 @@ export class HcMain extends HassElement { | ||||
|     }; | ||||
|  | ||||
|     if (senderId) { | ||||
|       this._sendMessage(senderId, error); | ||||
|       this.sendMessage(senderId, error); | ||||
|     } else { | ||||
|       for (const sender of castContext.getSenders()) { | ||||
|         this._sendMessage(sender.id, error); | ||||
|         this.sendMessage(sender.id, error); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -364,9 +365,7 @@ export class HcMain extends HassElement { | ||||
|       this._urlPath || "lovelace" | ||||
|     ); | ||||
|     castContext.setApplicationState(title || ""); | ||||
|     this._lovelaceConfig = checkLovelaceConfig( | ||||
|       lovelaceConfig | ||||
|     ) as LovelaceConfig; | ||||
|     this._lovelaceConfig = lovelaceConfig; | ||||
|   } | ||||
|  | ||||
|   private _handleShowDemo(_msg: ShowDemoMessage) { | ||||
| @@ -394,7 +393,7 @@ export class HcMain extends HassElement { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private _sendMessage(senderId: string, response: any) { | ||||
|   private sendMessage(senderId: string, response: any) { | ||||
|     castContext.sendCustomMessage(CAST_NS, senderId, response); | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										8
									
								
								cast/webpack.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								cast/webpack.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| import webpack from "../build-scripts/webpack.cjs"; | ||||
| import env from "../build-scripts/env.cjs"; | ||||
|  | ||||
| export default webpack.createCastConfig({ | ||||
|   isProdBuild: env.isProdBuild(), | ||||
|   isStatsBuild: env.isStatsBuild(), | ||||
|   latestBuild: true, | ||||
| }); | ||||
							
								
								
									
										10
									
								
								demo/rollup.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								demo/rollup.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| import rollup from "../build-scripts/rollup.cjs"; | ||||
| import env from "../build-scripts/env.cjs"; | ||||
|  | ||||
| const config = rollup.createDemoConfig({ | ||||
|   isProdBuild: env.isProdBuild(), | ||||
|   latestBuild: true, | ||||
|   isStatsBuild: env.isStatsBuild(), | ||||
| }); | ||||
|  | ||||
| export default { ...config.inputOptions, output: config.outputOptions }; | ||||
| @@ -4,6 +4,11 @@ | ||||
| # Stop on errors | ||||
| set -e | ||||
|  | ||||
| cd "$(dirname "$0")/../.." | ||||
| cd "$(dirname "$0")/.." | ||||
|  | ||||
| ./node_modules/.bin/gulp analyze-demo | ||||
| export STATS=1 | ||||
| statsfile="compilation-stats-demo.json" | ||||
|  | ||||
| ./node_modules/.bin/webpack-cli --profile --node-env=production --json=$statsfile | ||||
| npx webpack-bundle-analyzer $statsfile dist/frontend_latest | ||||
| rm -f $statsfile | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { convertEntities } from "../../../../src/fake_data/entity"; | ||||
| import type { DemoConfig } from "../types"; | ||||
| import { DemoConfig } from "../types"; | ||||
|  | ||||
| export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) => | ||||
|   convertEntities({ | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import type { DemoConfig } from "../types"; | ||||
| import { DemoConfig } from "../types"; | ||||
| import { demoEntitiesArsaboo } from "./entities"; | ||||
| import { demoLovelaceArsaboo } from "./lovelace"; | ||||
| import { demoThemeArsaboo } from "./theme"; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import type { DemoConfig } from "../types"; | ||||
| import { DemoConfig } from "../types"; | ||||
|  | ||||
| export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({ | ||||
|   title: "Home Assistant", | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||
| import type { Lovelace } from "../../../src/panels/lovelace/types"; | ||||
| import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||
| import { Lovelace } from "../../../src/panels/lovelace/types"; | ||||
| import { energyEntities } from "../stubs/entities"; | ||||
| import type { DemoConfig } from "./types"; | ||||
| import { DemoConfig } from "./types"; | ||||
|  | ||||
| export const demoConfigs: (() => Promise<DemoConfig>)[] = [ | ||||
| export const demoConfigs: Array<() => Promise<DemoConfig>> = [ | ||||
|   () => import("./sections").then((mod) => mod.demoSections), | ||||
|   () => import("./arsaboo").then((mod) => mod.demoArsaboo), | ||||
|   () => import("./teachingbirds").then((mod) => mod.demoTeachingbirds), | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { convertEntities } from "../../../../src/fake_data/entity"; | ||||
| import type { DemoConfig } from "../types"; | ||||
| import { DemoConfig } from "../types"; | ||||
|  | ||||
| export const demoEntitiesJimpower: DemoConfig["entities"] = () => | ||||
|   convertEntities({ | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import type { DemoConfig } from "../types"; | ||||
| import { DemoConfig } from "../types"; | ||||
| import { demoEntitiesJimpower } from "./entities"; | ||||
| import { demoLovelaceJimpower } from "./lovelace"; | ||||
| import { demoThemeJimpower } from "./theme"; | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import "../../custom-cards/card-modder"; | ||||
| import type { DemoConfig } from "../types"; | ||||
| import { DemoConfig } from "../types"; | ||||
|  | ||||
| export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({ | ||||
|   name: "Kingia Castle", | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { convertEntities } from "../../../../src/fake_data/entity"; | ||||
| import type { DemoConfig } from "../types"; | ||||
| import { DemoConfig } from "../types"; | ||||
|  | ||||
| export const demoEntitiesKernehed: DemoConfig["entities"] = () => | ||||
|   convertEntities({ | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import type { DemoConfig } from "../types"; | ||||
| import { DemoConfig } from "../types"; | ||||
| import { demoEntitiesKernehed } from "./entities"; | ||||
| import { demoLovelaceKernehed } from "./lovelace"; | ||||
| import { demoThemeKernehed } from "./theme"; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import type { DemoConfig } from "../types"; | ||||
| import { DemoConfig } from "../types"; | ||||
|  | ||||
| export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({ | ||||
|   name: "Hem", | ||||
|   | ||||
							
								
								
									
										16
									
								
								demo/src/configs/sections/description.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								demo/src/configs/sections/description.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| import { html } from "lit"; | ||||
| import { DemoConfig } from "../types"; | ||||
|  | ||||
| export const demoLovelaceDescription: DemoConfig["description"] = ( | ||||
|   localize | ||||
| ) => html` | ||||
|   <p> | ||||
|     ${localize("ui.panel.page-demo.config.sections.description", { | ||||
|       blog_post: html`<a | ||||
|         href="https://www.home-assistant.io/blog/2024/03/04/dashboard-chapter-1/" | ||||
|         target="_blank" | ||||
|         >${localize("ui.panel.page-demo.config.sections.description_blog_post")} | ||||
|       </a>`, | ||||
|     })} | ||||
|   </p> | ||||
| `; | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { convertEntities } from "../../../../src/fake_data/entity"; | ||||
| import type { DemoConfig } from "../types"; | ||||
| import { DemoConfig } from "../types"; | ||||
|  | ||||
| export const demoEntitiesSections: DemoConfig["entities"] = (localize) => | ||||
|   convertEntities({ | ||||
| @@ -111,47 +111,9 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) => | ||||
|         friendly_name: "Living room Temperature", | ||||
|       }, | ||||
|     }, | ||||
|     "sensor.living_room_humidity": { | ||||
|       entity_id: "sensor.living_room_humidity", | ||||
|       state: "57", | ||||
|       attributes: { | ||||
|         state_class: "measurement", | ||||
|         unit_of_measurement: "%", | ||||
|         device_class: "humidity", | ||||
|         friendly_name: "Living room Humidity", | ||||
|       }, | ||||
|     }, | ||||
|     "sensor.outdoor_temperature": { | ||||
|       entity_id: "sensor.outdoor_temperature", | ||||
|       state: "10.5", | ||||
|       attributes: { | ||||
|         state_class: "measurement", | ||||
|         unit_of_measurement: "°C", | ||||
|         device_class: "temperature", | ||||
|         friendly_name: "Outdoor temperature", | ||||
|       }, | ||||
|     }, | ||||
|     "sensor.outdoor_humidity": { | ||||
|       entity_id: "sensor.outdoor_humidity", | ||||
|       state: "70.4", | ||||
|       attributes: { | ||||
|         state_class: "measurement", | ||||
|         unit_of_measurement: "%", | ||||
|         device_class: "humidity", | ||||
|         friendly_name: "Outdoor humidity", | ||||
|       }, | ||||
|     }, | ||||
|     "device_tracker.car": { | ||||
|       entity_id: "sensor.outdoor_humidity", | ||||
|       state: "not_home", | ||||
|       attributes: { | ||||
|         friendly_name: "Car", | ||||
|         icon: "mdi:car", | ||||
|       }, | ||||
|     }, | ||||
|     "media_player.living_room_nest_mini": { | ||||
|       entity_id: "media_player.living_room_nest_mini", | ||||
|       state: "playing", | ||||
|       state: "on", | ||||
|       attributes: { | ||||
|         device_class: "speaker", | ||||
|         volume_level: 0.18, | ||||
| @@ -199,14 +161,6 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) => | ||||
|         supported_features: 32, | ||||
|       }, | ||||
|     }, | ||||
|     "binary_sensor.kitchen_motion": { | ||||
|       entity_id: "light.kitchen_motion", | ||||
|       state: "on", | ||||
|       attributes: { | ||||
|         device_class: "motion", | ||||
|         friendly_name: "Kitchen motion", | ||||
|       }, | ||||
|     }, | ||||
|     "light.worktop_spotlights": { | ||||
|       entity_id: "light.worktop_spotlights", | ||||
|       state: "off", | ||||
| @@ -441,14 +395,6 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) => | ||||
|         supported_features: 64063, | ||||
|       }, | ||||
|     }, | ||||
|     "switch.in_meeting": { | ||||
|       entity_id: "switch.in_meeting", | ||||
|       state: "on", | ||||
|       attributes: { | ||||
|         icon: "mdi:laptop-account", | ||||
|         friendly_name: "In a meeting", | ||||
|       }, | ||||
|     }, | ||||
|     "sensor.standing_desk_height": { | ||||
|       entity_id: "sensor.standing_desk_height", | ||||
|       state: "72", | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import type { DemoConfig } from "../types"; | ||||
| import { DemoConfig } from "../types"; | ||||
| import { demoLovelaceDescription } from "./description"; | ||||
| import { demoEntitiesSections } from "./entities"; | ||||
| import { demoLovelaceSections } from "./lovelace"; | ||||
|  | ||||
| @@ -6,6 +7,7 @@ export const demoSections: DemoConfig = { | ||||
|   authorName: "Home Assistant", | ||||
|   authorUrl: "https://github.com/home-assistant/frontend/", | ||||
|   name: "Home Demo", | ||||
|   description: demoLovelaceDescription, | ||||
|   lovelace: demoLovelaceSections, | ||||
|   entities: demoEntitiesSections, | ||||
|   theme: () => ({}), | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { isFrontpageEmbed } from "../../util/is_frontpage"; | ||||
| import type { DemoConfig } from "../types"; | ||||
| import { DemoConfig } from "../types"; | ||||
|  | ||||
| export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({ | ||||
|   title: "Home Assistant Demo", | ||||
| @@ -9,57 +9,17 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({ | ||||
|       title: isFrontpageEmbed ? "Home Assistant" : "Demo", | ||||
|       path: "home", | ||||
|       icon: "mdi:home-assistant", | ||||
|       badges: [ | ||||
|         { | ||||
|           type: "entity", | ||||
|           entity: "sensor.outdoor_temperature", | ||||
|           color: "red", | ||||
|         }, | ||||
|         { | ||||
|           type: "entity", | ||||
|           entity: "sensor.outdoor_humidity", | ||||
|           color: "indigo", | ||||
|         }, | ||||
|         { | ||||
|           type: "entity", | ||||
|           entity: "device_tracker.car", | ||||
|         }, | ||||
|       ], | ||||
|       sections: [ | ||||
|         ...(isFrontpageEmbed | ||||
|           ? [] | ||||
|           : [ | ||||
|               { | ||||
|                 cards: [ | ||||
|                   { | ||||
|                     type: "heading", | ||||
|                     heading: `${localize("ui.panel.page-demo.config.sections.titles.welcome")} 👋`, | ||||
|                   }, | ||||
|                   { type: "custom:ha-demo-card" }, | ||||
|                 ], | ||||
|                 title: `${localize("ui.panel.page-demo.config.sections.titles.welcome")} 👋`, | ||||
|                 cards: [{ type: "custom:ha-demo-card" }], | ||||
|               }, | ||||
|             ]), | ||||
|         { | ||||
|           cards: [ | ||||
|             { | ||||
|               type: "heading", | ||||
|               heading: localize( | ||||
|                 "ui.panel.page-demo.config.sections.titles.living_room" | ||||
|               ), | ||||
|               icon: "mdi:sofa", | ||||
|               badges: [ | ||||
|                 { | ||||
|                   type: "entity", | ||||
|                   entity: "sensor.living_room_temperature", | ||||
|                   color: "red", | ||||
|                 }, | ||||
|                 { | ||||
|                   type: "entity", | ||||
|                   entity: "sensor.living_room_humidity", | ||||
|                   color: "indigo", | ||||
|                 }, | ||||
|               ], | ||||
|             }, | ||||
|             { | ||||
|               type: "tile", | ||||
|               entity: "light.floor_lamp", | ||||
| @@ -78,6 +38,13 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({ | ||||
|               type: "tile", | ||||
|               entity: "light.bar_lamp", | ||||
|             }, | ||||
|             { | ||||
|               graph: "line", | ||||
|               type: "sensor", | ||||
|               entity: "sensor.living_room_temperature", | ||||
|               detail: 1, | ||||
|               name: "Temperature", | ||||
|             }, | ||||
|             { | ||||
|               type: "tile", | ||||
|               entity: "cover.living_room_garden_shutter", | ||||
| @@ -88,25 +55,11 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({ | ||||
|               entity: "media_player.living_room_nest_mini", | ||||
|             }, | ||||
|           ], | ||||
|           title: `🛋️ ${localize("ui.panel.page-demo.config.sections.titles.living_room")} `, | ||||
|         }, | ||||
|         { | ||||
|           type: "grid", | ||||
|           cards: [ | ||||
|             { | ||||
|               type: "heading", | ||||
|               heading: localize( | ||||
|                 "ui.panel.page-demo.config.sections.titles.kitchen" | ||||
|               ), | ||||
|               icon: "mdi:fridge", | ||||
|               badges: [ | ||||
|                 { | ||||
|                   type: "entity", | ||||
|                   entity: "binary_sensor.kitchen_motion", | ||||
|                   show_state: false, | ||||
|                   color: "blue", | ||||
|                 }, | ||||
|               ], | ||||
|             }, | ||||
|             { | ||||
|               type: "tile", | ||||
|               entity: "cover.kitchen_shutter", | ||||
| @@ -137,17 +90,11 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({ | ||||
|               entity: "media_player.kitchen_nest_audio", | ||||
|             }, | ||||
|           ], | ||||
|           title: `👩🍳 ${localize("ui.panel.page-demo.config.sections.titles.kitchen")}`, | ||||
|         }, | ||||
|         { | ||||
|           type: "grid", | ||||
|           cards: [ | ||||
|             { | ||||
|               type: "heading", | ||||
|               heading: localize( | ||||
|                 "ui.panel.page-demo.config.sections.titles.energy" | ||||
|               ), | ||||
|               icon: "mdi:transmission-tower", | ||||
|             }, | ||||
|             { | ||||
|               type: "tile", | ||||
|               entity: "binary_sensor.tesla_wall_connector_vehicle_connected", | ||||
| @@ -185,17 +132,11 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({ | ||||
|               color: "dark-grey", | ||||
|             }, | ||||
|           ], | ||||
|           title: `⚡️ ${localize("ui.panel.page-demo.config.sections.titles.energy")}`, | ||||
|         }, | ||||
|         { | ||||
|           type: "grid", | ||||
|           cards: [ | ||||
|             { | ||||
|               type: "heading", | ||||
|               heading: localize( | ||||
|                 "ui.panel.page-demo.config.sections.titles.climate" | ||||
|               ), | ||||
|               icon: "mdi:thermometer", | ||||
|             }, | ||||
|             { | ||||
|               type: "tile", | ||||
|               entity: "sun.sun", | ||||
| @@ -228,38 +169,16 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({ | ||||
|               state_content: ["preset_mode", "current_temperature"], | ||||
|             }, | ||||
|           ], | ||||
|           title: `🌤️ ${localize("ui.panel.page-demo.config.sections.titles.climate")}`, | ||||
|         }, | ||||
|         { | ||||
|           type: "grid", | ||||
|           cards: [ | ||||
|             { | ||||
|               type: "heading", | ||||
|               heading: localize( | ||||
|                 "ui.panel.page-demo.config.sections.titles.study" | ||||
|               ), | ||||
|               icon: "mdi:desk-lamp", | ||||
|               badges: [ | ||||
|                 { | ||||
|                   type: "entity", | ||||
|                   entity: "switch.in_meeting", | ||||
|                   state: "on", | ||||
|                   state_content: "name", | ||||
|                   visibility: [ | ||||
|                     { | ||||
|                       condition: "state", | ||||
|                       state: "on", | ||||
|                       entity: "switch.in_meeting", | ||||
|                     }, | ||||
|                   ], | ||||
|                 }, | ||||
|               ], | ||||
|             }, | ||||
|             { | ||||
|               type: "tile", | ||||
|               entity: "cover.study_shutter", | ||||
|               name: "Shutter", | ||||
|             }, | ||||
|  | ||||
|             { | ||||
|               type: "tile", | ||||
|               entity: "light.study_spotlights", | ||||
| @@ -276,23 +195,12 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({ | ||||
|               color: "brown", | ||||
|               icon: "mdi:desk", | ||||
|             }, | ||||
|             { | ||||
|               type: "tile", | ||||
|               entity: "switch.in_meeting", | ||||
|               name: "Meeting mode", | ||||
|             }, | ||||
|           ], | ||||
|           title: `🧑💻 ${localize("ui.panel.page-demo.config.sections.titles.study")}`, | ||||
|         }, | ||||
|         { | ||||
|           type: "grid", | ||||
|           cards: [ | ||||
|             { | ||||
|               type: "heading", | ||||
|               heading: localize( | ||||
|                 "ui.panel.page-demo.config.sections.titles.outdoor" | ||||
|               ), | ||||
|               icon: "mdi:tree", | ||||
|             }, | ||||
|             { | ||||
|               type: "tile", | ||||
|               entity: "light.outdoor_light", | ||||
| @@ -322,17 +230,11 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({ | ||||
|               name: "Illuminance", | ||||
|             }, | ||||
|           ], | ||||
|           title: `🌳 ${localize("ui.panel.page-demo.config.sections.titles.outdoor")}`, | ||||
|         }, | ||||
|         { | ||||
|           type: "grid", | ||||
|           cards: [ | ||||
|             { | ||||
|               type: "heading", | ||||
|               heading: localize( | ||||
|                 "ui.panel.page-demo.config.sections.titles.updates" | ||||
|               ), | ||||
|               icon: "mdi:update", | ||||
|             }, | ||||
|             { | ||||
|               type: "tile", | ||||
|               entity: "automation.home_assistant_auto_update", | ||||
| @@ -358,6 +260,7 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({ | ||||
|               icon: "mdi:home-assistant", | ||||
|             }, | ||||
|           ], | ||||
|           title: `🎉 ${localize("ui.panel.page-demo.config.sections.titles.updates")}`, | ||||
|         }, | ||||
|       ], | ||||
|     }, | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { convertEntities } from "../../../../src/fake_data/entity"; | ||||
| import type { DemoConfig } from "../types"; | ||||
| import { DemoConfig } from "../types"; | ||||
|  | ||||
| export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | ||||
|   convertEntities({ | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import type { DemoConfig } from "../types"; | ||||
| import { DemoConfig } from "../types"; | ||||
| import { demoEntitiesTeachingbirds } from "./entities"; | ||||
| import { demoLovelaceTeachingbirds } from "./lovelace"; | ||||
| import { demoThemeTeachingbirds } from "./theme"; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import type { DemoConfig } from "../types"; | ||||
| import { DemoConfig } from "../types"; | ||||
|  | ||||
| export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({ | ||||
|   title: "Home", | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import type { TemplateResult } from "lit"; | ||||
| import type { LocalizeFunc } from "../../../src/common/translations/localize"; | ||||
| import type { LovelaceConfig } from "../../../src/data/lovelace/config/types"; | ||||
| import type { Entity } from "../../../src/fake_data/entity"; | ||||
| import { TemplateResult } from "lit"; | ||||
| import { LocalizeFunc } from "../../../src/common/translations/localize"; | ||||
| import { LovelaceConfig } from "../../../src/data/lovelace/config/types"; | ||||
| import { Entity } from "../../../src/fake_data/entity"; | ||||
|  | ||||
| export interface DemoConfig { | ||||
|   index?: number; | ||||
|   | ||||
| @@ -1,14 +1,14 @@ | ||||
| import { mdiTelevision } from "@mdi/js"; | ||||
| import { css, html, LitElement, nothing } from "lit"; | ||||
| import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; | ||||
| import { customElement, state } from "lit/decorators"; | ||||
| import type { CastManager } from "../../../src/cast/cast_manager"; | ||||
| import { CastManager } from "../../../src/cast/cast_manager"; | ||||
| import { castSendShowDemo } from "../../../src/cast/receiver_messages"; | ||||
| import "../../../src/components/ha-icon"; | ||||
| import type { | ||||
| import { | ||||
|   CastConfig, | ||||
|   LovelaceRow, | ||||
| } from "../../../src/panels/lovelace/entity-rows/types"; | ||||
| import type { HomeAssistant } from "../../../src/types"; | ||||
| import { HomeAssistant } from "../../../src/types"; | ||||
|  | ||||
| @customElement("cast-demo-row") | ||||
| class CastDemoRow extends LitElement implements LovelaceRow { | ||||
| @@ -45,6 +45,7 @@ class CastDemoRow extends LitElement implements LovelaceRow { | ||||
|           this.requestUpdate(); | ||||
|         }); | ||||
|         mgr.castContext.addEventListener( | ||||
|           // eslint-disable-next-line no-undef | ||||
|           cast.framework.CastContextEventType.SESSION_STATE_CHANGED, | ||||
|           (ev) => { | ||||
|             // On Android, opening a new session always results in SESSION_RESUMED. | ||||
| @@ -66,35 +67,37 @@ class CastDemoRow extends LitElement implements LovelaceRow { | ||||
|     this.style.display = this._castManager ? "" : "none"; | ||||
|   } | ||||
|  | ||||
|   static styles = css` | ||||
|     :host { | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|     } | ||||
|     ha-svg-icon { | ||||
|       padding: 8px; | ||||
|       color: var(--paper-item-icon-color); | ||||
|     } | ||||
|     .flex { | ||||
|       flex: 1; | ||||
|       overflow: hidden; | ||||
|       margin-left: 16px; | ||||
|       display: flex; | ||||
|       justify-content: space-between; | ||||
|       align-items: center; | ||||
|     } | ||||
|     .name { | ||||
|       white-space: nowrap; | ||||
|       overflow: hidden; | ||||
|       text-overflow: ellipsis; | ||||
|     } | ||||
|     google-cast-launcher { | ||||
|       cursor: pointer; | ||||
|       display: inline-block; | ||||
|       height: 24px; | ||||
|       width: 24px; | ||||
|     } | ||||
|   `; | ||||
|   static get styles(): CSSResultGroup { | ||||
|     return css` | ||||
|       :host { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|       } | ||||
|       ha-svg-icon { | ||||
|         padding: 8px; | ||||
|         color: var(--paper-item-icon-color); | ||||
|       } | ||||
|       .flex { | ||||
|         flex: 1; | ||||
|         overflow: hidden; | ||||
|         margin-left: 16px; | ||||
|         display: flex; | ||||
|         justify-content: space-between; | ||||
|         align-items: center; | ||||
|       } | ||||
|       .name { | ||||
|         white-space: nowrap; | ||||
|         overflow: hidden; | ||||
|         text-overflow: ellipsis; | ||||
|       } | ||||
|       google-cast-launcher { | ||||
|         cursor: pointer; | ||||
|         display: inline-block; | ||||
|         height: 24px; | ||||
|         width: 24px; | ||||
|       } | ||||
|     `; | ||||
|   } | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|   | ||||
| @@ -1,17 +1,13 @@ | ||||
| import type { CSSResultGroup } from "lit"; | ||||
| import { css, html, LitElement, nothing } from "lit"; | ||||
| import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; | ||||
| import { customElement, property, state } from "lit/decorators"; | ||||
| import { until } from "lit/directives/until"; | ||||
| import { fireEvent } from "../../../src/common/dom/fire_event"; | ||||
| import "../../../src/components/ha-card"; | ||||
| import "../../../src/components/ha-button"; | ||||
| import "../../../src/components/ha-circular-progress"; | ||||
| import type { LovelaceCardConfig } from "../../../src/data/lovelace/config/card"; | ||||
| import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||
| import type { | ||||
|   Lovelace, | ||||
|   LovelaceCard, | ||||
| } from "../../../src/panels/lovelace/types"; | ||||
| import { LovelaceCardConfig } from "../../../src/data/lovelace/config/card"; | ||||
| import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||
| import { Lovelace, LovelaceCard } from "../../../src/panels/lovelace/types"; | ||||
| import { | ||||
|   demoConfigs, | ||||
|   selectedDemoConfig, | ||||
| @@ -26,13 +22,12 @@ export class HADemoCard extends LitElement implements LovelaceCard { | ||||
|  | ||||
|   @state() private _switching = false; | ||||
|  | ||||
|   private _hidden = window.localStorage.getItem("hide_demo_card"); | ||||
|   private _hidden = localStorage.hide_demo_card; | ||||
|  | ||||
|   public getCardSize() { | ||||
|     return this._hidden ? 0 : 2; | ||||
|   } | ||||
|  | ||||
|   // eslint-disable-next-line @typescript-eslint/no-empty-function | ||||
|   public setConfig(_config: LovelaceCardConfig) {} | ||||
|  | ||||
|   protected render() { | ||||
|   | ||||
| @@ -3,10 +3,12 @@ import "../../src/resources/compatibility"; | ||||
| import { customElement } from "lit/decorators"; | ||||
| import { isNavigationClick } from "../../src/common/dom/is-navigation-click"; | ||||
| import { navigate } from "../../src/common/navigate"; | ||||
| import type { MockHomeAssistant } from "../../src/fake_data/provide_hass"; | ||||
| import { provideHass } from "../../src/fake_data/provide_hass"; | ||||
| import { | ||||
|   MockHomeAssistant, | ||||
|   provideHass, | ||||
| } from "../../src/fake_data/provide_hass"; | ||||
| import { HomeAssistantAppEl } from "../../src/layouts/home-assistant"; | ||||
| import type { HomeAssistant } from "../../src/types"; | ||||
| import { HomeAssistant } from "../../src/types"; | ||||
| import { selectedDemoConfig } from "./configs/demo-configs"; | ||||
| import { mockAreaRegistry } from "./stubs/area_registry"; | ||||
| import { mockAuth } from "./stubs/auth"; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import type { AreaRegistryEntry } from "../../../src/data/area_registry"; | ||||
| import { AreaRegistryEntry } from "../../../src/data/area_registry"; | ||||
| import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||
|  | ||||
| export const mockAreaRegistry = ( | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||
| import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||
|  | ||||
| export const mockAuth = (hass: MockHomeAssistant) => { | ||||
|   hass.mockWS("config/auth/list", () => []); | ||||
|   | ||||
| @@ -1,9 +0,0 @@ | ||||
| import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||
|  | ||||
| export const mockConfig = (hass: MockHomeAssistant) => { | ||||
|   hass.mockWS("validate_config", () => ({ | ||||
|     actions: { valid: true }, | ||||
|     conditions: { valid: true }, | ||||
|     triggers: { valid: true }, | ||||
|   })); | ||||
| }; | ||||
| @@ -1,4 +1,4 @@ | ||||
| import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||
| import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||
|  | ||||
| export const mockConfigEntries = (hass: MockHomeAssistant) => { | ||||
|   hass.mockWS("config_entries/get", () => ({ | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import type { DeviceRegistryEntry } from "../../../src/data/device_registry"; | ||||
| import { DeviceRegistryEntry } from "../../../src/data/device_registry"; | ||||
| import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||
|  | ||||
| export const mockDeviceRegistry = ( | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| import { format, startOfToday, startOfTomorrow } from "date-fns"; | ||||
| import type { | ||||
| import { | ||||
|   EnergyInfo, | ||||
|   EnergyPreferences, | ||||
|   EnergySolarForecasts, | ||||
|   FossilEnergyConsumption, | ||||
| } from "../../../src/data/energy"; | ||||
| import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||
| import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||
|  | ||||
| export const mockEnergy = (hass: MockHomeAssistant) => { | ||||
|   hass.mockWS( | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import type { EntityRegistryEntry } from "../../../src/data/entity_registry"; | ||||
| import { EntityRegistryEntry } from "../../../src/data/entity_registry"; | ||||
| import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||
|  | ||||
| export const mockEntityRegistry = ( | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user