Compare commits
	
		
			1 Commits
		
	
	
		
			analytics-
			...
			app-alias
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 368973b668 | 
| @@ -1,13 +0,0 @@ | |||||||
| # 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/vscode/devcontainers/python:0-3.9 |  | ||||||
|  |  | ||||||
| ENV \ |  | ||||||
|   DEBIAN_FRONTEND=noninteractive \ |  | ||||||
|   DEVCONTAINER=true \ |  | ||||||
|   PATH=$PATH:./node_modules/.bin |  | ||||||
|  |  | ||||||
| # Install nvm |  | ||||||
| COPY .nvmrc /tmp/.nvmrc |  | ||||||
| RUN \ |  | ||||||
|   su vscode -c \ |  | ||||||
|     "source /usr/local/share/nvm/nvm.sh && nvm install $(cat /tmp/.nvmrc) 2>&1" |  | ||||||
| @@ -1,34 +0,0 @@ | |||||||
| { |  | ||||||
|   "name": "Home Assistant Frontend", |  | ||||||
|   "build": { |  | ||||||
|     "dockerfile": "Dockerfile", |  | ||||||
|     "context": ".." |  | ||||||
|   }, |  | ||||||
|   "appPort": "8124:8123", |  | ||||||
|   "context": "..", |  | ||||||
|   "postCreateCommand": "script/bootstrap", |  | ||||||
|   "extensions": [ |  | ||||||
|     "github.vscode-pull-request-github", |  | ||||||
|     "dbaeumer.vscode-eslint", |  | ||||||
|     "ms-vscode.vscode-typescript-tslint-plugin", |  | ||||||
|     "esbenp.prettier-vscode", |  | ||||||
|     "bierner.lit-html", |  | ||||||
|     "runem.lit-plugin", |  | ||||||
|     "ms-python.vscode-pylance" |  | ||||||
|   ], |  | ||||||
|   "settings": { |  | ||||||
|     "terminal.integrated.shell.linux": "/bin/bash", |  | ||||||
|     "files.eol": "\n", |  | ||||||
|     "editor.tabSize": 2, |  | ||||||
|     "editor.formatOnPaste": false, |  | ||||||
|     "editor.formatOnSave": true, |  | ||||||
|     "editor.formatOnType": true, |  | ||||||
|     "[typescript]": { |  | ||||||
|       "editor.defaultFormatter": "esbenp.prettier-vscode" |  | ||||||
|     }, |  | ||||||
|     "[javascript]": { |  | ||||||
|       "editor.defaultFormatter": "esbenp.prettier-vscode" |  | ||||||
|     }, |  | ||||||
|     "files.trimTrailingWhitespace": true |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| { | { | ||||||
|   "extends": [ |   "extends": [ | ||||||
|     "airbnb-typescript/base", |  | ||||||
|     "plugin:@typescript-eslint/recommended", |     "plugin:@typescript-eslint/recommended", | ||||||
|  |     "airbnb-typescript/base", | ||||||
|     "plugin:wc/recommended", |     "plugin:wc/recommended", | ||||||
|     "plugin:lit/recommended", |     "plugin:lit/recommended", | ||||||
|     "prettier", |     "prettier", | ||||||
| @@ -45,16 +45,16 @@ | |||||||
|     "func-names": 0, |     "func-names": 0, | ||||||
|     "prefer-arrow-callback": 0, |     "prefer-arrow-callback": 0, | ||||||
|     "no-underscore-dangle": 0, |     "no-underscore-dangle": 0, | ||||||
|  |     "no-var": 0, | ||||||
|     "strict": 0, |     "strict": 0, | ||||||
|     "prefer-spread": 0, |     "prefer-spread": 0, | ||||||
|     "no-plusplus": 0, |     "no-plusplus": 0, | ||||||
|     "no-bitwise": 2, |     "no-bitwise": 0, | ||||||
|     "comma-dangle": 0, |     "comma-dangle": 0, | ||||||
|     "vars-on-top": 0, |     "vars-on-top": 0, | ||||||
|     "no-continue": 0, |     "no-continue": 0, | ||||||
|     "no-param-reassign": 0, |     "no-param-reassign": 0, | ||||||
|     "no-multi-assign": 0, |     "no-multi-assign": 0, | ||||||
|     "no-console": 2, |  | ||||||
|     "radix": 0, |     "radix": 0, | ||||||
|     "no-alert": 0, |     "no-alert": 0, | ||||||
|     "no-return-await": 0, |     "no-return-await": 0, | ||||||
| @@ -66,26 +66,18 @@ | |||||||
|     "import/prefer-default-export": 0, |     "import/prefer-default-export": 0, | ||||||
|     "import/no-unresolved": 0, |     "import/no-unresolved": 0, | ||||||
|     "import/no-cycle": 0, |     "import/no-cycle": 0, | ||||||
|     "import/extensions": [ |     "import/extensions": 0, | ||||||
|       2, |  | ||||||
|       "ignorePackages", |  | ||||||
|       { "ts": "never", "js": "never" } |  | ||||||
|     ], |  | ||||||
|     "no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"], |     "no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"], | ||||||
|     "object-curly-newline": 0, |     "object-curly-newline": 0, | ||||||
|     "default-case": 0, |     "default-case": 0, | ||||||
|     "wc/no-self-class": 0, |     "wc/no-self-class": 0, | ||||||
|     "no-shadow": 0, |  | ||||||
|     "@typescript-eslint/camelcase": 0, |     "@typescript-eslint/camelcase": 0, | ||||||
|     "@typescript-eslint/ban-ts-comment": 0, |     "@typescript-eslint/ban-ts-ignore": 0, | ||||||
|     "@typescript-eslint/no-use-before-define": 0, |     "@typescript-eslint/no-use-before-define": 0, | ||||||
|     "@typescript-eslint/no-non-null-assertion": 0, |     "@typescript-eslint/no-non-null-assertion": 0, | ||||||
|     "@typescript-eslint/no-explicit-any": 0, |     "@typescript-eslint/no-explicit-any": 0, | ||||||
|     "@typescript-eslint/no-unused-vars": 0, |     "@typescript-eslint/no-unused-vars": 0, | ||||||
|     "@typescript-eslint/explicit-function-return-type": 0, |     "@typescript-eslint/explicit-function-return-type": 0 | ||||||
|     "@typescript-eslint/explicit-module-boundary-types": 0, |  | ||||||
|     "@typescript-eslint/no-shadow": ["error"], |  | ||||||
|     "lit/attribute-value-entities": 0 |  | ||||||
|   }, |   }, | ||||||
|   "plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"], |   "plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"], | ||||||
|   "processor": "disable/disable" |   "processor": "disable/disable" | ||||||
|   | |||||||
| @@ -51,7 +51,7 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w | |||||||
| <!-- | <!-- | ||||||
|   Provide details about the versions you are using, which helps us reproducing |   Provide details about the versions you are using, which helps us reproducing | ||||||
|   and finding the issue quicker. Version information is found in the |   and finding the issue quicker. Version information is found in the | ||||||
|   Home Assistant frontend: Configuration -> Info. |   Home Assistant frontend: Developer tools -> Info. | ||||||
| 
 | 
 | ||||||
|   Browser version and operating system is important! Please try to replicate |   Browser version and operating system is important! Please try to replicate | ||||||
|   your issue in a different browser and be sure to include your findings. |   your issue in a different browser and be sure to include your findings. | ||||||
| @@ -74,12 +74,12 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w | |||||||
| 
 | 
 | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## Problem-relevant frontend configuration | ## Problem-relevant configuration | ||||||
| 
 | 
 | ||||||
| <!-- | <!-- | ||||||
|   An example configuration that caused the problem for you, e.g. the YAML configuration |   An example configuration that caused the problem for you. Fill this out even | ||||||
|   of the used cards. Fill this out even if it seems unimportant to you. Please be sure |   if it seems unimportant to you. Please be sure to remove personal information | ||||||
|   to remove personal information like passwords, private URLs and other credentials. |   like passwords, private URLs and other credentials. | ||||||
| --> | --> | ||||||
| 
 | 
 | ||||||
| ```yaml | ```yaml | ||||||
| @@ -89,7 +89,7 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w | |||||||
| ## Javascript errors shown in your browser console/inspector | ## Javascript errors shown in your browser console/inspector | ||||||
| 
 | 
 | ||||||
| <!-- | <!-- | ||||||
|   If you come across any Javascript or other error logs, e.g. in your browser |   If you come across any javascript or other error logs, e.g., in your browser | ||||||
|   console/inspector please provide them. |   console/inspector please provide them. | ||||||
| --> | --> | ||||||
| 
 | 
 | ||||||
							
								
								
									
										26
									
								
								.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,26 @@ | |||||||
|  | --- | ||||||
|  | name: Request a feature for the UI, Frontend or Lovelace | ||||||
|  | about: Request an new feature for the Home Assistant frontend. | ||||||
|  | labels: feature request | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | <!-- | ||||||
|  |   DO NOT DELETE ANY TEXT from this template! | ||||||
|  |   Otherwise, your request may be closed without comment. | ||||||
|  | --> | ||||||
|  |  | ||||||
|  | ## The request | ||||||
|  |  | ||||||
|  | <!-- | ||||||
|  |   Describe to our maintainers, the feature you would like to be added. | ||||||
|  |   Please be clear and concise and, if possible, provide a screenshot or mockup. | ||||||
|  | --> | ||||||
|  |  | ||||||
|  | ## The alternatives | ||||||
|  |  | ||||||
|  | <!-- | ||||||
|  |   Are you currently using, or have you considered alternatives? | ||||||
|  |   If so, could you please describe those? | ||||||
|  | --> | ||||||
|  |  | ||||||
|  | ## Additional information | ||||||
							
								
								
									
										121
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,121 +0,0 @@ | |||||||
| name: Report a bug with the UI, Frontend or Lovelace |  | ||||||
| description: Report an issue related to the Home Assistant frontend. |  | ||||||
| labels: bug |  | ||||||
| body: |  | ||||||
|   - type: markdown |  | ||||||
|     attributes: |  | ||||||
|       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 an discussion][fr] instead of creating an issue. |  | ||||||
|  |  | ||||||
|         **Please not not report issues for custom Lovelace cards.** |  | ||||||
|  |  | ||||||
|         [fr]: https://github.com/home-assistant/frontend/discussions |  | ||||||
|         [releases]: https://github.com/home-assistant/home-assistant/releases |  | ||||||
|   - type: checkboxes |  | ||||||
|     attributes: |  | ||||||
|       label: Checklist |  | ||||||
|       description: Please verify that you've followed these steps |  | ||||||
|       options: |  | ||||||
|         - label: I have updated to the latest available Home Assistant version. |  | ||||||
|           required: true |  | ||||||
|         - label: I have cleared the cache of my browser. |  | ||||||
|           required: true |  | ||||||
|         - label: I have tried a different browser to see if it is related to my browser. |  | ||||||
|           required: true |  | ||||||
|   - type: markdown |  | ||||||
|     attributes: |  | ||||||
|       value: | |  | ||||||
|         ## The problem |  | ||||||
|   - type: textarea |  | ||||||
|     validations: |  | ||||||
|       required: true |  | ||||||
|     attributes: |  | ||||||
|       label: Describe the issue you are experiencing |  | ||||||
|       description: Provide a clear and concise description of what the bug is. |  | ||||||
|   - type: textarea |  | ||||||
|     validations: |  | ||||||
|       required: true |  | ||||||
|     attributes: |  | ||||||
|       label: Describe the behavior you expected |  | ||||||
|       description: Describe what you expected to happen or it should look/behave. |  | ||||||
|   - type: textarea |  | ||||||
|     validations: |  | ||||||
|       required: true |  | ||||||
|     attributes: |  | ||||||
|       label: Steps to reproduce the issue |  | ||||||
|       description: | |  | ||||||
|         Please tell us exactly how to reproduce your issue. |  | ||||||
|         Provide clear and concise step by step instructions and add code snippets if needed. |  | ||||||
|       value: | |  | ||||||
|         1. |  | ||||||
|         2. |  | ||||||
|         3. |  | ||||||
|         ... |  | ||||||
|   - type: markdown |  | ||||||
|     attributes: |  | ||||||
|       value: | |  | ||||||
|         ## Environment |  | ||||||
|   - type: input |  | ||||||
|     validations: |  | ||||||
|       required: true |  | ||||||
|     attributes: |  | ||||||
|       label: What version of Home Assistant Core has the issue? |  | ||||||
|       placeholder: core- |  | ||||||
|       description: > |  | ||||||
|         Can be found in the Configuration panel -> Info. |  | ||||||
|   - type: input |  | ||||||
|     attributes: |  | ||||||
|       label: What was the last working version of Home Assistant Core? |  | ||||||
|       placeholder: core- |  | ||||||
|       description: > |  | ||||||
|         If known, otherwise leave blank. |  | ||||||
|   - type: input |  | ||||||
|     attributes: |  | ||||||
|       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! |  | ||||||
|   - type: input |  | ||||||
|     attributes: |  | ||||||
|       label: Which operating system are you using to run this browser? |  | ||||||
|       placeholder: macOS Big Sur (1.11) |  | ||||||
|       description: > |  | ||||||
|         Don't forget to add the version! |  | ||||||
|   - type: markdown |  | ||||||
|     attributes: |  | ||||||
|       value: | |  | ||||||
|         # Details |  | ||||||
|  |  | ||||||
|   - type: textarea |  | ||||||
|     attributes: |  | ||||||
|       label: State of relevant entities |  | ||||||
|       description: > |  | ||||||
|         If your issue is about how an entity is shown in the UI, please add the |  | ||||||
|         state and attributes for all situations. You can find this information |  | ||||||
|         at Developer Tools -> States. |  | ||||||
|       render: txt |  | ||||||
|   - type: textarea |  | ||||||
|     attributes: |  | ||||||
|       label: Problem-relevant frontend configuration |  | ||||||
|       description: > |  | ||||||
|         An example configuration that caused the problem for you, e.g., the YAML |  | ||||||
|         configuration of the used cards. Fill this out even if it seems |  | ||||||
|         unimportant to you. Please be sure to remove personal information like |  | ||||||
|         passwords, private URLs and other credentials. |  | ||||||
|       render: yaml |  | ||||||
|   - type: textarea |  | ||||||
|     attributes: |  | ||||||
|       label: Javascript errors shown in your browser console/inspector |  | ||||||
|       description: > |  | ||||||
|         If you come across any Javascript or other error logs, e.g., in your |  | ||||||
|         browser console/inspector please provide them. |  | ||||||
|       render: txt |  | ||||||
|   - type: textarea |  | ||||||
|     attributes: |  | ||||||
|       label: Additional information |  | ||||||
|       description: > |  | ||||||
|         If you have any additional information for us, use the field below. |  | ||||||
|         Please note, you can attach screenshots or screen recordings here, by |  | ||||||
|         dragging and dropping files in the field below. |  | ||||||
							
								
								
									
										3
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,8 +1,5 @@ | |||||||
| blank_issues_enabled: false | blank_issues_enabled: false | ||||||
| contact_links: | contact_links: | ||||||
|   - name: Request a feature for the UI, Frontend or Lovelace |  | ||||||
|     url: https://github.com/home-assistant/frontend/discussions/category_choices |  | ||||||
|     about: Request an new feature for the Home Assistant frontend. |  | ||||||
|   - name: Report a bug that is NOT related to the UI, Frontend or Lovelace |   - name: Report a bug that is NOT related to the UI, Frontend or Lovelace | ||||||
|     url: https://github.com/home-assistant/core/issues |     url: https://github.com/home-assistant/core/issues | ||||||
|     about: This is the issue tracker for our frontend. Please report other issues with the backend repository. |     about: This is the issue tracker for our frontend. Please report other issues with the backend repository. | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -18,8 +18,8 @@ | |||||||
| <!-- | <!-- | ||||||
|   Describe the big picture of your changes here to communicate to the |   Describe the big picture of your changes here to communicate to the | ||||||
|   maintainers why we should accept this pull request. If it fixes a bug |   maintainers why we should accept this pull request. If it fixes a bug | ||||||
|   or resolves a feature request, be sure to link to that issue or discussion |   or resolves a feature request, be sure to link to that issue in the | ||||||
|   in the additional information section. |   additional information section. | ||||||
| --> | --> | ||||||
|  |  | ||||||
| ## Type of change | ## Type of change | ||||||
| @@ -56,7 +56,7 @@ | |||||||
| --> | --> | ||||||
|  |  | ||||||
| - This PR fixes or closes issue: fixes # | - This PR fixes or closes issue: fixes # | ||||||
| - This PR is related to issue or discussion: | - This PR is related to issue: | ||||||
| - Link to documentation pull request: | - Link to documentation pull request: | ||||||
|  |  | ||||||
| ## Checklist | ## Checklist | ||||||
|   | |||||||
							
								
								
									
										27
									
								
								.github/lock.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,27 @@ | |||||||
|  | # Configuration for Lock Threads - https://github.com/dessant/lock-threads | ||||||
|  |  | ||||||
|  | # Number of days of inactivity before a closed issue or pull request is locked | ||||||
|  | daysUntilLock: 1 | ||||||
|  |  | ||||||
|  | # Skip issues and pull requests created before a given timestamp. Timestamp must | ||||||
|  | # follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable | ||||||
|  | skipCreatedBefore: 2020-01-01 | ||||||
|  |  | ||||||
|  | # Issues and pull requests with these labels will be ignored. Set to `[]` to disable | ||||||
|  | exemptLabels: [] | ||||||
|  |  | ||||||
|  | # Label to add before locking, such as `outdated`. Set to `false` to disable | ||||||
|  | lockLabel: false | ||||||
|  |  | ||||||
|  | # Comment to post before locking. Set to `false` to disable | ||||||
|  | lockComment: false | ||||||
|  |  | ||||||
|  | # Assign `resolved` as the reason for locking. Set to `false` to disable | ||||||
|  | setLockReason: false | ||||||
|  |  | ||||||
|  | # Limit to only `issues` or `pulls` | ||||||
|  | only: pulls | ||||||
|  |  | ||||||
|  | # Optionally, specify configuration settings just for `issues` or `pulls` | ||||||
|  | issues: | ||||||
|  |   daysUntilLock: 30 | ||||||
							
								
								
									
										56
									
								
								.github/stale.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,56 @@ | |||||||
|  | # Configuration for probot-stale - https://github.com/probot/stale | ||||||
|  |  | ||||||
|  | # Number of days of inactivity before an Issue or Pull Request becomes stale | ||||||
|  | daysUntilStale: 90 | ||||||
|  |  | ||||||
|  | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. | ||||||
|  | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. | ||||||
|  | daysUntilClose: 7 | ||||||
|  |  | ||||||
|  | # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) | ||||||
|  | onlyLabels: [] | ||||||
|  |  | ||||||
|  | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable | ||||||
|  | exemptLabels: | ||||||
|  |   - feature request | ||||||
|  |   - Help wanted | ||||||
|  |   - to do | ||||||
|  |  | ||||||
|  | # Set to true to ignore issues in a project (defaults to false) | ||||||
|  | exemptProjects: true | ||||||
|  |  | ||||||
|  | # Set to true to ignore issues in a milestone (defaults to false) | ||||||
|  | exemptMilestones: true | ||||||
|  |  | ||||||
|  | # Set to true to ignore issues with an assignee (defaults to false) | ||||||
|  | exemptAssignees: false | ||||||
|  |  | ||||||
|  | # Label to use when marking as stale | ||||||
|  | staleLabel: stale | ||||||
|  |  | ||||||
|  | # Comment to post when marking as stale. Set to `false` to disable | ||||||
|  | markComment: > | ||||||
|  |   There hasn't been any activity on this issue recently. Due to the high number | ||||||
|  |   of incoming GitHub notifications, we have to clean some of the old issues, | ||||||
|  |   as many of them have already been resolved with the latest updates. | ||||||
|  |  | ||||||
|  |   Please make sure to update to the latest Home Assistant version and check | ||||||
|  |   if that solves the issue. Let us know if that works for you by adding a | ||||||
|  |   comment 👍 | ||||||
|  |  | ||||||
|  |   This issue now has been marked as stale and will be closed if no further | ||||||
|  |   activity occurs. Thank you for your contributions. | ||||||
|  |  | ||||||
|  | # Comment to post when removing the stale label. | ||||||
|  | # unmarkComment: > | ||||||
|  | #   Your comment here. | ||||||
|  |  | ||||||
|  | # Comment to post when closing a stale Issue or Pull Request. | ||||||
|  | # closeComment: > | ||||||
|  | #   Your comment here. | ||||||
|  |  | ||||||
|  | # Limit the number of actions per hour, from 1-30. Default is 30 | ||||||
|  | limitPerRun: 30 | ||||||
|  |  | ||||||
|  | # Limit to only `issues` or `pulls` | ||||||
|  | only: issues | ||||||
							
								
								
									
										6
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -34,8 +34,10 @@ jobs: | |||||||
|         run: yarn install |         run: yarn install | ||||||
|         env: |         env: | ||||||
|           CI: true |           CI: true | ||||||
|       - name: Build resources |       - name: Build icons | ||||||
|         run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos |         run: ./node_modules/.bin/gulp gen-icons-json | ||||||
|  |       - name: Build translations | ||||||
|  |         run: ./node_modules/.bin/gulp build-translations | ||||||
|       - name: Run eslint |       - name: Run eslint | ||||||
|         run: ./node_modules/.bin/eslint '{**/src,src}/**/*.{js,ts,html}' --ignore-path .gitignore |         run: ./node_modules/.bin/eslint '{**/src,src}/**/*.{js,ts,html}' --ignore-path .gitignore | ||||||
|       - name: Run tsc |       - name: Run tsc | ||||||
|   | |||||||
							
								
								
									
										60
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,60 +0,0 @@ | |||||||
| name: "CodeQL" |  | ||||||
|  |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     branches: [dev, master] |  | ||||||
|   pull_request: |  | ||||||
|     # The branches below must be a subset of the branches above |  | ||||||
|     branches: [dev] |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   analyze: |  | ||||||
|     name: Analyze |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|  |  | ||||||
|     strategy: |  | ||||||
|       fail-fast: false |  | ||||||
|       matrix: |  | ||||||
|         # Override automatic language detection by changing the below list |  | ||||||
|         # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] |  | ||||||
|         language: ['javascript'] |  | ||||||
|         # Learn more... |  | ||||||
|         # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection |  | ||||||
|  |  | ||||||
|     steps: |  | ||||||
|     - name: Checkout repository |  | ||||||
|       uses: actions/checkout@v2 |  | ||||||
|       with: |  | ||||||
|         # We must fetch at least the immediate parents so that if this is |  | ||||||
|         # a pull request then we can checkout the head. |  | ||||||
|         fetch-depth: 2 |  | ||||||
|  |  | ||||||
|     # If this run was triggered by a pull request event, then checkout |  | ||||||
|     # the head of the pull request instead of the merge commit. |  | ||||||
|     - run: git checkout HEAD^2 |  | ||||||
|       if: ${{ github.event_name == 'pull_request' }} |  | ||||||
|  |  | ||||||
|     # Initializes the CodeQL tools for scanning. |  | ||||||
|     - name: Initialize CodeQL |  | ||||||
|       uses: github/codeql-action/init@v1 |  | ||||||
|       with: |  | ||||||
|         languages: ${{ matrix.language }} |  | ||||||
|  |  | ||||||
|     # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java). |  | ||||||
|     # If this step fails, then you should remove it and run the build manually (see below) |  | ||||||
|     - name: Autobuild |  | ||||||
|       uses: github/codeql-action/autobuild@v1 |  | ||||||
|  |  | ||||||
|     # ℹ️ Command-line programs to run using the OS shell. |  | ||||||
|     # 📚 https://git.io/JvXDl |  | ||||||
|  |  | ||||||
|     # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines |  | ||||||
|     #    and modify them (or add more) to build your code if your project |  | ||||||
|     #    uses a compiled language |  | ||||||
|  |  | ||||||
|     #- run: | |  | ||||||
|     #   make bootstrap |  | ||||||
|     #   make release |  | ||||||
|  |  | ||||||
|     - name: Perform CodeQL Analysis |  | ||||||
|       uses: github/codeql-action/analyze@v1 |  | ||||||
							
								
								
									
										20
									
								
								.github/workflows/lock.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,20 +0,0 @@ | |||||||
| name: Lock |  | ||||||
|  |  | ||||||
| # yamllint disable-line rule:truthy |  | ||||||
| on: |  | ||||||
|   schedule: |  | ||||||
|     - cron: "0 * * * *" |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   lock: |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - uses: dessant/lock-threads@v2.0.1 |  | ||||||
|         with: |  | ||||||
|           github-token: ${{ github.token }} |  | ||||||
|           issue-lock-inactive-days: "30" |  | ||||||
|           issue-exclude-created-before: "2020-10-01T00:00:00Z" |  | ||||||
|           issue-lock-reason: "" |  | ||||||
|           pr-lock-inactive-days: "1" |  | ||||||
|           pr-exclude-created-before: "2020-11-01T00:00:00Z" |  | ||||||
|           pr-lock-reason: "" |  | ||||||
							
								
								
									
										19
									
								
								.github/workflows/netflify.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,19 +0,0 @@ | |||||||
| name: Netlify |  | ||||||
|  |  | ||||||
| on: |  | ||||||
|   schedule: |  | ||||||
|     - cron: "0 0 * * *" |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   trigger_builds: |  | ||||||
|     name: Trigger netlify build preview |  | ||||||
|     runs-on: "ubuntu-latest" |  | ||||||
|     steps: |  | ||||||
|       - name: Trigger Cast build |  | ||||||
|         run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_CAST_DEV_BUILD_HOOK }} |  | ||||||
|  |  | ||||||
|       - name: Trigger Demo build |  | ||||||
|         run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_DEMO_DEV_BUILD_HOOK }} |  | ||||||
|  |  | ||||||
|       - name: Trigger Gallery build |  | ||||||
|         run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_GALLERY_DEV_BUILD_HOOK }} |  | ||||||
							
								
								
									
										81
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,81 +0,0 @@ | |||||||
| name: Release |  | ||||||
|  |  | ||||||
| on: |  | ||||||
|   release: |  | ||||||
|     types: |  | ||||||
|       - published |  | ||||||
|  |  | ||||||
| env: |  | ||||||
|   WHEELS_TAG: 3.7-alpine3.11 |  | ||||||
|   PYTHON_VERSION: 3.7 |  | ||||||
|   NODE_VERSION: 12.1 |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   release: |  | ||||||
|     name: Release |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - name: Checkout the repository |  | ||||||
|         uses: actions/checkout@v2 |  | ||||||
|  |  | ||||||
|       - name: Verify version |  | ||||||
|         uses: home-assistant/actions/helpers/verify-version@master |  | ||||||
|  |  | ||||||
|       - name: Set up Python ${{ env.PYTHON_VERSION }} |  | ||||||
|         uses: actions/setup-python@v2 |  | ||||||
|         with: |  | ||||||
|           python-version: ${{ env.PYTHON_VERSION }} |  | ||||||
|  |  | ||||||
|       - name: Set up Node ${{ env.NODE_VERSION }} |  | ||||||
|         uses: actions/setup-node@v2 |  | ||||||
|         with: |  | ||||||
|           node-version: ${{ env.NODE_VERSION }} |  | ||||||
|  |  | ||||||
|       - name: Build and release package |  | ||||||
|         run: | |  | ||||||
|           python3 -m pip install twine |  | ||||||
|           export TWINE_USERNAME="__token__" |  | ||||||
|           export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}" |  | ||||||
|  |  | ||||||
|           script/release |  | ||||||
|  |  | ||||||
|   wheels-init: |  | ||||||
|     name: Init wheels build |  | ||||||
|     needs: release |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - name: Generate requirements.txt |  | ||||||
|         run: | |  | ||||||
|           # Sleep to give pypi time to populate the new version across mirrors |  | ||||||
|           sleep 240 |  | ||||||
|           version=$(echo "${{ github.ref }}" | awk -F"/" '{print $NF}' ) |  | ||||||
|           echo "home-assistant-frontend==$version" > ./requirements.txt |  | ||||||
|  |  | ||||||
|       - name: Upload requirements.txt |  | ||||||
|         uses: actions/upload-artifact@v2 |  | ||||||
|         with: |  | ||||||
|           name: requirements |  | ||||||
|           path: ./requirements.txt |  | ||||||
|  |  | ||||||
|   build-wheels: |  | ||||||
|     name: Build wheels for ${{ matrix.arch }} |  | ||||||
|     needs: wheels-init |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     strategy: |  | ||||||
|       matrix: |  | ||||||
|         arch: ["aarch64", "armhf", "armv7", "amd64", "i386"] |  | ||||||
|     steps: |  | ||||||
|       - name: Download requirements.txt |  | ||||||
|         uses: actions/download-artifact@v2 |  | ||||||
|         with: |  | ||||||
|           name: requirements |  | ||||||
|  |  | ||||||
|       - name: Build wheels |  | ||||||
|         uses: home-assistant/wheels@master |  | ||||||
|         with: |  | ||||||
|           tag: ${{ env.WHEELS_TAG }} |  | ||||||
|           arch: ${{ matrix.arch }} |  | ||||||
|           wheels-host: ${{ secrets.WHEELS_HOST }} |  | ||||||
|           wheels-key: ${{ secrets.WHEELS_KEY }} |  | ||||||
|           wheels-user: wheels |  | ||||||
|           requirements: "requirements.txt" |  | ||||||
							
								
								
									
										42
									
								
								.github/workflows/stale.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,42 +0,0 @@ | |||||||
| name: Stale |  | ||||||
|  |  | ||||||
| # yamllint disable-line rule:truthy |  | ||||||
| on: |  | ||||||
|   schedule: |  | ||||||
|     - cron: "0 * * * *" |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   stale: |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - name: 90 days stale policy |  | ||||||
|         uses: actions/stale@v3.0.13 |  | ||||||
|         with: |  | ||||||
|           repo-token: ${{ secrets.GITHUB_TOKEN }} |  | ||||||
|           days-before-stale: 90 |  | ||||||
|           days-before-close: 7 |  | ||||||
|           operations-per-run: 25 |  | ||||||
|           remove-stale-when-updated: true |  | ||||||
|           stale-issue-label: "stale" |  | ||||||
|           exempt-issue-labels: "no-stale,Help%20wanted,help-wanted,feature-request,feature%20request" |  | ||||||
|           stale-issue-message: > |  | ||||||
|             There hasn't been any activity on this issue recently. Due to the |  | ||||||
|             high number of incoming GitHub notifications, we have to clean some |  | ||||||
|             of the old issues, as many of them have already been resolved with |  | ||||||
|             the latest updates. |  | ||||||
|  |  | ||||||
|             Please make sure to update to the latest Home Assistant version and |  | ||||||
|             check if that solves the issue. Let us know if that works for you by |  | ||||||
|             adding a comment 👍 |  | ||||||
|  |  | ||||||
|             This issue has now been marked as stale and will be closed if no |  | ||||||
|             further activity occurs. Thank you for your contributions. |  | ||||||
|  |  | ||||||
|           stale-pr-label: "stale" |  | ||||||
|           exempt-pr-labels: "no-stale" |  | ||||||
|           stale-pr-message: > |  | ||||||
|             There hasn't been any activity on this pull request recently. This |  | ||||||
|             pull request has been automatically marked as stale because of that |  | ||||||
|             and will be closed if no further activity occurs within 7 days. |  | ||||||
|  |  | ||||||
|             Thank you for your contributions. |  | ||||||
							
								
								
									
										65
									
								
								.github/workflows/translations.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,65 +0,0 @@ | |||||||
| name: Translations |  | ||||||
|  |  | ||||||
| on: |  | ||||||
|   schedule: |  | ||||||
|     - cron: "30 0 * * *" |  | ||||||
|   push: |  | ||||||
|     branches: |  | ||||||
|       - dev |  | ||||||
|     paths: |  | ||||||
|       - src/translations/en.json |  | ||||||
|  |  | ||||||
| env: |  | ||||||
|   NODE_VERSION: 12 |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   upload: |  | ||||||
|     name: Upload |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - name: Checkout the repository |  | ||||||
|         uses: actions/checkout@v2 |  | ||||||
|  |  | ||||||
|       - name: Set up Node ${{ env.NODE_VERSION }} |  | ||||||
|         uses: actions/setup-node@v2 |  | ||||||
|         with: |  | ||||||
|           node-version: ${{ env.NODE_VERSION }} |  | ||||||
|  |  | ||||||
|       - name: Upload Translations |  | ||||||
|         run: | |  | ||||||
|           export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}" |  | ||||||
|  |  | ||||||
|           ./script/translations_upload_base |  | ||||||
|  |  | ||||||
|   download: |  | ||||||
|     name: Download |  | ||||||
|     needs: upload |  | ||||||
|     if: github.event_name == 'schedule' |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - name: Checkout the repository |  | ||||||
|         uses: actions/checkout@v2 |  | ||||||
|  |  | ||||||
|       - name: Set up Node ${{ env.NODE_VERSION }} |  | ||||||
|         uses: actions/setup-node@v2 |  | ||||||
|         with: |  | ||||||
|           node-version: ${{ env.NODE_VERSION }} |  | ||||||
|  |  | ||||||
|       - name: Download Translations |  | ||||||
|         run: | |  | ||||||
|           export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}" |  | ||||||
|  |  | ||||||
|           npm install |  | ||||||
|           ./script/translations_download |  | ||||||
|  |  | ||||||
|       - name: Initialize git |  | ||||||
|         uses: home-assistant/actions/helpers/git-init@master |  | ||||||
|         with: |  | ||||||
|           name: GitHub Action |  | ||||||
|           email: github-action@users.noreply.github.com |  | ||||||
|  |  | ||||||
|       - name: Update translation |  | ||||||
|         run: | |  | ||||||
|           git add translations |  | ||||||
|           git commit -am "Translation update" |  | ||||||
|           git push |  | ||||||
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -23,8 +23,6 @@ dist | |||||||
| # vscode | # vscode | ||||||
| .vscode/* | .vscode/* | ||||||
| !.vscode/extensions.json | !.vscode/extensions.json | ||||||
| !.vscode/launch.json |  | ||||||
| !.vscode/tasks.json |  | ||||||
|  |  | ||||||
| # Cast dev settings  | # Cast dev settings  | ||||||
| src/cast/dev_const.ts | src/cast/dev_const.ts | ||||||
| @@ -35,6 +33,3 @@ yarn-error.log | |||||||
|  |  | ||||||
| #asdf | #asdf | ||||||
| .tool-versions | .tool-versions | ||||||
|  |  | ||||||
| # Home Assistant config |  | ||||||
| /config |  | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								.hound.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,6 @@ | |||||||
|  | jshint: | ||||||
|  |   enabled: false | ||||||
|  |  | ||||||
|  | eslint: | ||||||
|  |   enabled: true | ||||||
|  |   config_file: .eslintrc-hound.json | ||||||
							
								
								
									
										44
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,44 +0,0 @@ | |||||||
| { |  | ||||||
|   // https://github.com/microsoft/vscode-js-debug/blob/master/OPTIONS.md |  | ||||||
|   "configurations": [ |  | ||||||
|     { |  | ||||||
|       "name": "Debug Frontend", |  | ||||||
|       "request": "launch", |  | ||||||
|       "type": "pwa-chrome", |  | ||||||
|       "url": "http://localhost:8123/", |  | ||||||
|       "webRoot": "${workspaceFolder}/hass_frontend", |  | ||||||
|       "disableNetworkCache": true, |  | ||||||
|       "preLaunchTask": "Develop Frontend", |  | ||||||
|       "outFiles": [ |  | ||||||
|         "${workspaceFolder}/hass_frontend/frontend_latest/*.js" |  | ||||||
|       ] |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "name": "Debug Gallery", |  | ||||||
|       "request": "launch", |  | ||||||
|       "type": "pwa-chrome", |  | ||||||
|       "url": "http://localhost:8100/", |  | ||||||
|       "webRoot": "${workspaceFolder}/gallery/dist", |  | ||||||
|       "disableNetworkCache": true, |  | ||||||
|       "preLaunchTask": "Develop Gallery" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "name": "Debug Demo", |  | ||||||
|       "request": "launch", |  | ||||||
|       "type": "pwa-chrome", |  | ||||||
|       "url": "http://localhost:8090/", |  | ||||||
|       "webRoot": "${workspaceFolder}/demo/dist", |  | ||||||
|       "disableNetworkCache": true, |  | ||||||
|       "preLaunchTask": "Develop Demo" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "name": "Debug Cast", |  | ||||||
|       "request": "launch", |  | ||||||
|       "type": "pwa-chrome", |  | ||||||
|       "url": "http://localhost:8080/", |  | ||||||
|       "webRoot": "${workspaceFolder}/cast/dist", |  | ||||||
|       "disableNetworkCache": true, |  | ||||||
|       "preLaunchTask": "Develop Cast" |  | ||||||
|     }, |  | ||||||
|   ] |  | ||||||
| } |  | ||||||
							
								
								
									
										208
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,208 +0,0 @@ | |||||||
| { |  | ||||||
|   "version": "2.0.0", |  | ||||||
|   "tasks": [ |  | ||||||
|     { |  | ||||||
|       "label": "Develop Frontend", |  | ||||||
|       "type": "gulp", |  | ||||||
|       "task": "develop-app", |  | ||||||
|       // Sync changes here to other tasks until issue resolved |  | ||||||
|       // https://github.com/Microsoft/vscode/issues/61497 |  | ||||||
|       "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": { |  | ||||||
|         "kind": "build", |  | ||||||
|         "isDefault": true |  | ||||||
|       }, |  | ||||||
|       "runOptions": { |  | ||||||
|         "instanceLimit": 1 |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "label": "Develop Supervisor panel", |  | ||||||
|       "type": "gulp", |  | ||||||
|       "task": "develop-hassio", |  | ||||||
|       "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 Gallery", |  | ||||||
|       "type": "gulp", |  | ||||||
|       "task": "develop-gallery", |  | ||||||
|       "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", |  | ||||||
|       "task": "develop-demo", |  | ||||||
|       "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 Cast", |  | ||||||
|       "type": "gulp", |  | ||||||
|       "task": "develop-cast", |  | ||||||
|       "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": "Run HA Core in devcontainer", |  | ||||||
|       "type": "shell", |  | ||||||
|       "command": "script/core", |  | ||||||
|       "isBackground": true, |  | ||||||
|       "group": { |  | ||||||
|         "kind": "build", |  | ||||||
|         "isDefault": true |  | ||||||
|       }, |  | ||||||
|       "problemMatcher": [], |  | ||||||
|       "runOptions": { |  | ||||||
|         "instanceLimit": 1 |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "label": "Run HA Core for Supervisor in devcontainer", |  | ||||||
|       "type": "shell", |  | ||||||
|       "command": "HASSIO=${input:supervisorHost} HASSIO_TOKEN=${input:supervisorToken} script/core", |  | ||||||
|       "isBackground": true, |  | ||||||
|       "group": { |  | ||||||
|         "kind": "build", |  | ||||||
|         "isDefault": true |  | ||||||
|       }, |  | ||||||
|       "problemMatcher": [], |  | ||||||
|       "runOptions": { |  | ||||||
|         "instanceLimit": 1 |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   ], |  | ||||||
|   "inputs": [ |  | ||||||
|     { |  | ||||||
|       "id": "supervisorHost", |  | ||||||
|       "type": "promptString", |  | ||||||
|       "description": "The IP of the Supervisor host running the Remote API proxy add-on" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "id": "supervisorToken", |  | ||||||
|       "type": "promptString", |  | ||||||
|       "description": "The token for the Remote API proxy add-on" |  | ||||||
|     } |  | ||||||
|   ] |  | ||||||
| } |  | ||||||
| @@ -14,7 +14,7 @@ This is the repository for the official [Home Assistant](https://home-assistant. | |||||||
| - Development: [Instructions](https://developers.home-assistant.io/docs/frontend/development/) | - Development: [Instructions](https://developers.home-assistant.io/docs/frontend/development/) | ||||||
| - Production build: `script/build_frontend` | - Production build: `script/build_frontend` | ||||||
| - Gallery: `cd gallery && script/develop_gallery` | - Gallery: `cd gallery && script/develop_gallery` | ||||||
| - Supervisor: [Instructions](https://developers.home-assistant.io/docs/supervisor/developing) | - Hass.io: [Instructions](https://developers.home-assistant.io/docs/en/hassio_hass.html) | ||||||
|  |  | ||||||
| ## Frontend development | ## Frontend development | ||||||
|  |  | ||||||
| @@ -26,4 +26,4 @@ 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. | 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. | We use [BrowserStack](https://www.browserstack.com) to test Home Assistant on a large variation of devices. | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								azure-pipelines-netlify.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,30 @@ | |||||||
|  | # https://dev.azure.com/home-assistant | ||||||
|  |  | ||||||
|  | trigger: none | ||||||
|  | pr: none | ||||||
|  | schedules: | ||||||
|  |   - cron: "0 0 * * *" | ||||||
|  |     displayName: "build preview" | ||||||
|  |     branches: | ||||||
|  |       include: | ||||||
|  |       - dev | ||||||
|  |     always: true | ||||||
|  | variables: | ||||||
|  |   - group: netlify | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |  | ||||||
|  | - job: 'Netlify_preview' | ||||||
|  |   pool: | ||||||
|  |     vmImage: 'ubuntu-latest' | ||||||
|  |   steps: | ||||||
|  |   - script: | | ||||||
|  |       # Cast | ||||||
|  |       curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_CAST} | ||||||
|  |  | ||||||
|  |       # Demo | ||||||
|  |       curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_DEMO} | ||||||
|  |  | ||||||
|  |       # Gallery | ||||||
|  |       curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_GALLERY} | ||||||
|  |     displayName: 'Trigger netlify build preview' | ||||||
							
								
								
									
										59
									
								
								azure-pipelines-release.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,59 @@ | |||||||
|  | # https://dev.azure.com/home-assistant | ||||||
|  |  | ||||||
|  | trigger: | ||||||
|  |   batch: true | ||||||
|  |   tags: | ||||||
|  |     include: | ||||||
|  |       - "*" | ||||||
|  | pr: none | ||||||
|  | variables: | ||||||
|  |   - name: versionWheels | ||||||
|  |     value: '1.10.1-3.7-alpine3.11' | ||||||
|  |   - name: versionNode | ||||||
|  |     value: '12.1' | ||||||
|  |   - group: twine | ||||||
|  | resources: | ||||||
|  |   repositories: | ||||||
|  |     - repository: azure | ||||||
|  |       type: github | ||||||
|  |       name: 'home-assistant/ci-azure' | ||||||
|  |       endpoint: 'home-assistant' | ||||||
|  |  | ||||||
|  |  | ||||||
|  | stages: | ||||||
|  |   - stage: "Validate" | ||||||
|  |     jobs: | ||||||
|  |     - template: templates/azp-job-version.yaml@azure | ||||||
|  |  | ||||||
|  |   - stage: "Build" | ||||||
|  |     jobs: | ||||||
|  |       - job: "ReleasePython" | ||||||
|  |         pool: | ||||||
|  |           vmImage: "ubuntu-latest" | ||||||
|  |         steps: | ||||||
|  |           - task: UsePythonVersion@0 | ||||||
|  |             displayName: "Use Python 3.7" | ||||||
|  |             inputs: | ||||||
|  |               versionSpec: "3.7" | ||||||
|  |           - task: NodeTool@0 | ||||||
|  |             displayName: "Use Node $(versionNode)" | ||||||
|  |             inputs: | ||||||
|  |               versionSpec: "$(versionNode)" | ||||||
|  |           - script: pip install twine wheel | ||||||
|  |             displayName: "Install tools" | ||||||
|  |           - script: | | ||||||
|  |               export TWINE_USERNAME="$(twineUser)" | ||||||
|  |               export TWINE_PASSWORD="$(twinePassword)" | ||||||
|  |  | ||||||
|  |               script/release | ||||||
|  |             displayName: "Build and release package" | ||||||
|  |   - stage: "Wheels" | ||||||
|  |     jobs: | ||||||
|  |       - template: templates/azp-job-wheels.yaml@azure | ||||||
|  |         parameters: | ||||||
|  |           builderVersion: '$(versionWheels)' | ||||||
|  |           wheelsRequirement: 'requirement.txt' | ||||||
|  |           preBuild: | ||||||
|  |           - script: | | ||||||
|  |               sleep 240 | ||||||
|  |               echo "home-assistant-frontend==$(Build.SourceBranchName)" > requirement.txt | ||||||
							
								
								
									
										70
									
								
								azure-pipelines-translation.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,70 @@ | |||||||
|  | # https://dev.azure.com/home-assistant | ||||||
|  |  | ||||||
|  | trigger: | ||||||
|  |   batch: true | ||||||
|  |   branches: | ||||||
|  |     include: | ||||||
|  |     - dev | ||||||
|  |   paths: | ||||||
|  |     include: | ||||||
|  |     - translations/en.json | ||||||
|  | pr: none | ||||||
|  | schedules: | ||||||
|  |   - cron: "30 0 * * *" | ||||||
|  |     displayName: "frontend translation update" | ||||||
|  |     branches: | ||||||
|  |       include: | ||||||
|  |       - dev | ||||||
|  |     always: true | ||||||
|  | variables: | ||||||
|  | - group: translation | ||||||
|  | resources: | ||||||
|  |   repositories: | ||||||
|  |   - repository: azure | ||||||
|  |     type: github | ||||||
|  |     name: 'home-assistant/ci-azure' | ||||||
|  |     endpoint: 'home-assistant' | ||||||
|  |  | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |  | ||||||
|  | - job: 'Upload' | ||||||
|  |   pool: | ||||||
|  |     vmImage: 'ubuntu-latest' | ||||||
|  |   steps: | ||||||
|  |   - task: NodeTool@0 | ||||||
|  |     displayName: 'Use Node 12.x' | ||||||
|  |     inputs: | ||||||
|  |       versionSpec: '12.x' | ||||||
|  |   - script: | | ||||||
|  |       export LOKALISE_TOKEN="$(lokaliseToken)" | ||||||
|  |       export AZURE_BRANCH="$(Build.SourceBranchName)" | ||||||
|  |  | ||||||
|  |       ./script/translations_upload_base | ||||||
|  |     displayName: 'Upload Translation' | ||||||
|  |  | ||||||
|  | - job: 'Download' | ||||||
|  |   dependsOn: | ||||||
|  |   - 'Upload' | ||||||
|  |   condition: or(eq(variables['Build.Reason'], 'Schedule'), eq(variables['Build.Reason'], 'Manual')) | ||||||
|  |   pool: | ||||||
|  |     vmImage: 'ubuntu-latest' | ||||||
|  |   steps: | ||||||
|  |   - task: NodeTool@0 | ||||||
|  |     displayName: 'Use Node 12.x' | ||||||
|  |     inputs: | ||||||
|  |       versionSpec: '12.x' | ||||||
|  |   - template: templates/azp-step-git-init.yaml@azure | ||||||
|  |   - script: | | ||||||
|  |       export LOKALISE_TOKEN="$(lokaliseToken)" | ||||||
|  |       export AZURE_BRANCH="$(Build.SourceBranchName)" | ||||||
|  |  | ||||||
|  |       npm install | ||||||
|  |       ./script/translations_download | ||||||
|  |     displayName: 'Download Translation' | ||||||
|  |   - script: | | ||||||
|  |       git checkout dev | ||||||
|  |       git add translation | ||||||
|  |       git commit -am "[ci skip] Translation update" | ||||||
|  |       git push | ||||||
|  |     displayName: 'Update translation' | ||||||
| @@ -1,39 +0,0 @@ | |||||||
| # Bundling Home Assistant Frontend |  | ||||||
|  |  | ||||||
| The Home Assistant build pipeline contains various steps to prepare a build. |  | ||||||
|  |  | ||||||
| - Generating icon files to be included |  | ||||||
| - Generating translation files to be included |  | ||||||
| - Converting TypeScript, CSS and JSON files to JavaScript |  | ||||||
| - Bundling |  | ||||||
| - Minifying the files |  | ||||||
| - Generating the HTML entrypoint files |  | ||||||
| - Generating the service worker |  | ||||||
| - Compressing the files |  | ||||||
|  |  | ||||||
| ## Converting files |  | ||||||
|  |  | ||||||
| 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 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. |  | ||||||
|  |  | ||||||
| Responsibilities of the converter during development: |  | ||||||
|  |  | ||||||
| - Convert TypeScript to JavaScript |  | ||||||
| - Convert CSS to JavaScript that sets the content as the default export |  | ||||||
| - Convert JSON to JavaScript that sets the content as the default export |  | ||||||
| - Make sure import, dynamic import and web worker references work |  | ||||||
|   - Add extensions where missing |  | ||||||
|   - Resolve absolute package imports |  | ||||||
| - Filter out specific imports/packages |  | ||||||
| - Replace constants with values |  | ||||||
|  |  | ||||||
| In production, the following responsibilities are added: |  | ||||||
|  |  | ||||||
| - Minify HTML |  | ||||||
| - Bundle multiple imports so that the browser can fetch less files |  | ||||||
| - Generate a second version that is ES5 compatible |  | ||||||
|  |  | ||||||
| Configuration for all these steps are specified in [bundle.js](bundle.js). |  | ||||||
| @@ -44,7 +44,7 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({ | |||||||
| }); | }); | ||||||
|  |  | ||||||
| module.exports.terserOptions = (latestBuild) => ({ | module.exports.terserOptions = (latestBuild) => ({ | ||||||
|   safari10: !latestBuild, |   safari10: true, | ||||||
|   ecma: latestBuild ? undefined : 5, |   ecma: latestBuild ? undefined : 5, | ||||||
|   output: { comments: false }, |   output: { comments: false }, | ||||||
| }); | }); | ||||||
| @@ -52,24 +52,17 @@ module.exports.terserOptions = (latestBuild) => ({ | |||||||
| module.exports.babelOptions = ({ latestBuild }) => ({ | module.exports.babelOptions = ({ latestBuild }) => ({ | ||||||
|   babelrc: false, |   babelrc: false, | ||||||
|   presets: [ |   presets: [ | ||||||
|     !latestBuild && [ |     !latestBuild && [require("@babel/preset-env").default, { modules: false }], | ||||||
|       require("@babel/preset-env").default, |  | ||||||
|       { |  | ||||||
|         useBuiltIns: "entry", |  | ||||||
|         corejs: "3.6", |  | ||||||
|       }, |  | ||||||
|     ], |  | ||||||
|     require("@babel/preset-typescript").default, |     require("@babel/preset-typescript").default, | ||||||
|   ].filter(Boolean), |   ].filter(Boolean), | ||||||
|   plugins: [ |   plugins: [ | ||||||
|     // Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2}) |     // Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2}) | ||||||
|     !latestBuild && [ |     [ | ||||||
|       "@babel/plugin-proposal-object-rest-spread", |       "@babel/plugin-proposal-object-rest-spread", | ||||||
|       { loose: true, useBuiltIns: true }, |       { loose: true, useBuiltIns: true }, | ||||||
|     ], |     ], | ||||||
|     // Only support the syntax, Webpack will handle it. |     // Only support the syntax, Webpack will handle it. | ||||||
|     "@babel/plugin-syntax-import-meta", |     "@babel/syntax-dynamic-import", | ||||||
|     "@babel/plugin-syntax-dynamic-import", |  | ||||||
|     "@babel/plugin-proposal-optional-chaining", |     "@babel/plugin-proposal-optional-chaining", | ||||||
|     "@babel/plugin-proposal-nullish-coalescing-operator", |     "@babel/plugin-proposal-nullish-coalescing-operator", | ||||||
|     [ |     [ | ||||||
| @@ -80,7 +73,7 @@ module.exports.babelOptions = ({ latestBuild }) => ({ | |||||||
|       require("@babel/plugin-proposal-class-properties").default, |       require("@babel/plugin-proposal-class-properties").default, | ||||||
|       { loose: true }, |       { loose: true }, | ||||||
|     ], |     ], | ||||||
|   ].filter(Boolean), |   ], | ||||||
| }); | }); | ||||||
|  |  | ||||||
| // Are already ES5, cause warnings when babelified. | // Are already ES5, cause warnings when babelified. | ||||||
| @@ -117,7 +110,7 @@ BundleConfig { | |||||||
| */ | */ | ||||||
|  |  | ||||||
| module.exports.config = { | module.exports.config = { | ||||||
|   app({ isProdBuild, latestBuild, isStatsBuild, isWDS }) { |   app({ isProdBuild, latestBuild, isStatsBuild }) { | ||||||
|     return { |     return { | ||||||
|       entry: { |       entry: { | ||||||
|         service_worker: "./src/entrypoints/service_worker.ts", |         service_worker: "./src/entrypoints/service_worker.ts", | ||||||
| @@ -132,7 +125,6 @@ module.exports.config = { | |||||||
|       isProdBuild, |       isProdBuild, | ||||||
|       latestBuild, |       latestBuild, | ||||||
|       isStatsBuild, |       isStatsBuild, | ||||||
|       isWDS, |  | ||||||
|     }; |     }; | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
| @@ -186,6 +178,7 @@ module.exports.config = { | |||||||
|       publicPath: publicPath(latestBuild, paths.hassio_publicPath), |       publicPath: publicPath(latestBuild, paths.hassio_publicPath), | ||||||
|       isProdBuild, |       isProdBuild, | ||||||
|       latestBuild, |       latestBuild, | ||||||
|  |       dontHash: new Set(["entrypoint"]), | ||||||
|     }; |     }; | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,9 +6,6 @@ module.exports = { | |||||||
|   useRollup() { |   useRollup() { | ||||||
|     return process.env.ROLLUP === "1"; |     return process.env.ROLLUP === "1"; | ||||||
|   }, |   }, | ||||||
|   useWDS() { |  | ||||||
|     return process.env.WDS === "1"; |  | ||||||
|   }, |  | ||||||
|   isProdBuild() { |   isProdBuild() { | ||||||
|     return ( |     return ( | ||||||
|       process.env.NODE_ENV === "production" || module.exports.isStatsBuild() |       process.env.NODE_ENV === "production" || module.exports.isStatsBuild() | ||||||
|   | |||||||
| @@ -12,7 +12,6 @@ require("./webpack.js"); | |||||||
| require("./service-worker.js"); | require("./service-worker.js"); | ||||||
| require("./entry-html.js"); | require("./entry-html.js"); | ||||||
| require("./rollup.js"); | require("./rollup.js"); | ||||||
| require("./wds.js"); |  | ||||||
|  |  | ||||||
| gulp.task( | gulp.task( | ||||||
|   "develop-app", |   "develop-app", | ||||||
| @@ -29,11 +28,7 @@ gulp.task( | |||||||
|       "build-translations" |       "build-translations" | ||||||
|     ), |     ), | ||||||
|     "copy-static-app", |     "copy-static-app", | ||||||
|     env.useWDS() |     env.useRollup() ? "rollup-watch-app" : "webpack-watch-app" | ||||||
|       ? "wds-watch-app" |  | ||||||
|       : env.useRollup() |  | ||||||
|       ? "rollup-watch-app" |  | ||||||
|       : "webpack-watch-app" |  | ||||||
|   ) |   ) | ||||||
| ); | ); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,7 +19,6 @@ const renderTemplate = (pth, data = {}, pathFunc = templatePath) => { | |||||||
|   return compiled({ |   return compiled({ | ||||||
|     ...data, |     ...data, | ||||||
|     useRollup: env.useRollup(), |     useRollup: env.useRollup(), | ||||||
|     useWDS: env.useWDS(), |  | ||||||
|     renderTemplate, |     renderTemplate, | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
| @@ -91,23 +90,12 @@ gulp.task("gen-pages-prod", (done) => { | |||||||
| }); | }); | ||||||
|  |  | ||||||
| gulp.task("gen-index-app-dev", (done) => { | gulp.task("gen-index-app-dev", (done) => { | ||||||
|   let latestAppJS, latestCoreJS, latestCustomPanelJS; |   // In dev mode we don't mangle names, so we hardcode urls. That way we can | ||||||
|  |   // run webpack as last in watch mode, which blocks output. | ||||||
|   if (env.useWDS()) { |  | ||||||
|     latestAppJS = "http://localhost:8000/src/entrypoints/app.ts"; |  | ||||||
|     latestCoreJS = "http://localhost:8000/src/entrypoints/core.ts"; |  | ||||||
|     latestCustomPanelJS = |  | ||||||
|       "http://localhost:8000/src/entrypoints/custom-panel.ts"; |  | ||||||
|   } else { |  | ||||||
|     latestAppJS = "/frontend_latest/app.js"; |  | ||||||
|     latestCoreJS = "/frontend_latest/core.js"; |  | ||||||
|     latestCustomPanelJS = "/frontend_latest/custom-panel.js"; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   const content = renderTemplate("index", { |   const content = renderTemplate("index", { | ||||||
|     latestAppJS, |     latestAppJS: "/frontend_latest/app.js", | ||||||
|     latestCoreJS, |     latestCoreJS: "/frontend_latest/core.js", | ||||||
|     latestCustomPanelJS, |     latestCustomPanelJS: "/frontend_latest/custom-panel.js", | ||||||
|  |  | ||||||
|     es5AppJS: "/frontend_es5/app.js", |     es5AppJS: "/frontend_es5/app.js", | ||||||
|     es5CoreJS: "/frontend_es5/core.js", |     es5CoreJS: "/frontend_es5/core.js", | ||||||
| @@ -213,6 +201,8 @@ gulp.task("gen-index-cast-prod", (done) => { | |||||||
| }); | }); | ||||||
|  |  | ||||||
| gulp.task("gen-index-demo-dev", (done) => { | gulp.task("gen-index-demo-dev", (done) => { | ||||||
|  |   // In dev mode we don't mangle names, so we hardcode urls. That way we can | ||||||
|  |   // run webpack as last in watch mode, which blocks output. | ||||||
|   const content = renderDemoTemplate("index", { |   const content = renderDemoTemplate("index", { | ||||||
|     latestDemoJS: "/frontend_latest/main.js", |     latestDemoJS: "/frontend_latest/main.js", | ||||||
|  |  | ||||||
| @@ -250,6 +240,8 @@ gulp.task("gen-index-demo-prod", (done) => { | |||||||
| }); | }); | ||||||
|  |  | ||||||
| gulp.task("gen-index-gallery-dev", (done) => { | gulp.task("gen-index-gallery-dev", (done) => { | ||||||
|  |   // In dev mode we don't mangle names, so we hardcode urls. That way we can | ||||||
|  |   // run webpack as last in watch mode, which blocks output. | ||||||
|   const content = renderGalleryTemplate("index", { |   const content = renderGalleryTemplate("index", { | ||||||
|     latestGalleryJS: "./frontend_latest/entrypoint.js", |     latestGalleryJS: "./frontend_latest/entrypoint.js", | ||||||
|   }); |   }); | ||||||
| @@ -277,42 +269,3 @@ gulp.task("gen-index-gallery-prod", (done) => { | |||||||
|   ); |   ); | ||||||
|   done(); |   done(); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| gulp.task("gen-index-hassio-dev", async () => { |  | ||||||
|   writeHassioEntrypoint( |  | ||||||
|     `${paths.hassio_publicPath}/frontend_latest/entrypoint.js`, |  | ||||||
|     `${paths.hassio_publicPath}/frontend_es5/entrypoint.js` |  | ||||||
|   ); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| gulp.task("gen-index-hassio-prod", async () => { |  | ||||||
|   const latestManifest = require(path.resolve( |  | ||||||
|     paths.hassio_output_latest, |  | ||||||
|     "manifest.json" |  | ||||||
|   )); |  | ||||||
|   const es5Manifest = require(path.resolve( |  | ||||||
|     paths.hassio_output_es5, |  | ||||||
|     "manifest.json" |  | ||||||
|   )); |  | ||||||
|   writeHassioEntrypoint( |  | ||||||
|     latestManifest["entrypoint.js"], |  | ||||||
|     es5Manifest["entrypoint.js"] |  | ||||||
|   ); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| function writeHassioEntrypoint(latestEntrypoint, es5Entrypoint) { |  | ||||||
|   fs.mkdirSync(paths.hassio_output_root, { recursive: true }); |  | ||||||
|   fs.writeFileSync( |  | ||||||
|     path.resolve(paths.hassio_output_root, "entrypoint.js"), |  | ||||||
|     ` |  | ||||||
| try { |  | ||||||
|   new Function("import('${latestEntrypoint}')")(); |  | ||||||
| } catch (err) { |  | ||||||
|   var el = document.createElement('script'); |  | ||||||
|   el.src = '${es5Entrypoint}'; |  | ||||||
|   document.body.appendChild(el); |  | ||||||
| } |  | ||||||
|   `, |  | ||||||
|     { encoding: "utf-8" } |  | ||||||
|   ); |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -1,10 +1,7 @@ | |||||||
| // Run demo develop mode | // Run demo develop mode | ||||||
| const gulp = require("gulp"); | const gulp = require("gulp"); | ||||||
| const fs = require("fs"); |  | ||||||
| const path = require("path"); |  | ||||||
|  |  | ||||||
| const env = require("../env"); | const env = require("../env"); | ||||||
| const paths = require("../paths"); |  | ||||||
|  |  | ||||||
| require("./clean.js"); | require("./clean.js"); | ||||||
| require("./translations.js"); | require("./translations.js"); | ||||||
| @@ -15,31 +12,6 @@ require("./service-worker.js"); | |||||||
| require("./entry-html.js"); | require("./entry-html.js"); | ||||||
| require("./rollup.js"); | require("./rollup.js"); | ||||||
|  |  | ||||||
| gulp.task("gather-gallery-demos", async function gatherDemos() { |  | ||||||
|   const files = await fs.promises.readdir( |  | ||||||
|     path.resolve(paths.gallery_dir, "src/demos") |  | ||||||
|   ); |  | ||||||
|  |  | ||||||
|   let content = "export const DEMOS = {\n"; |  | ||||||
|  |  | ||||||
|   for (const file of files) { |  | ||||||
|     const demoId = path.basename(file, ".ts"); |  | ||||||
|     const demoPath = "../src/demos/" + demoId; |  | ||||||
|     content += `  "${demoId}": () => import("${demoPath}"),\n`; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   content += "};"; |  | ||||||
|  |  | ||||||
|   const galleryBuild = path.resolve(paths.gallery_dir, "build"); |  | ||||||
|  |  | ||||||
|   fs.mkdirSync(galleryBuild, { recursive: true }); |  | ||||||
|   fs.writeFileSync( |  | ||||||
|     path.resolve(galleryBuild, "import-demos.ts"), |  | ||||||
|     content, |  | ||||||
|     "utf-8" |  | ||||||
|   ); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| gulp.task( | gulp.task( | ||||||
|   "develop-gallery", |   "develop-gallery", | ||||||
|   gulp.series( |   gulp.series( | ||||||
| @@ -48,11 +20,7 @@ gulp.task( | |||||||
|     }, |     }, | ||||||
|     "clean-gallery", |     "clean-gallery", | ||||||
|     "translations-enable-merge-backend", |     "translations-enable-merge-backend", | ||||||
|     gulp.parallel( |     gulp.parallel("gen-icons-json", "build-translations"), | ||||||
|       "gen-icons-json", |  | ||||||
|       "build-translations", |  | ||||||
|       "gather-gallery-demos" |  | ||||||
|     ), |  | ||||||
|     "copy-static-gallery", |     "copy-static-gallery", | ||||||
|     "gen-index-gallery-dev", |     "gen-index-gallery-dev", | ||||||
|     env.useRollup() ? "rollup-dev-server-gallery" : "webpack-dev-server-gallery" |     env.useRollup() ? "rollup-dev-server-gallery" : "webpack-dev-server-gallery" | ||||||
| @@ -67,11 +35,7 @@ gulp.task( | |||||||
|     }, |     }, | ||||||
|     "clean-gallery", |     "clean-gallery", | ||||||
|     "translations-enable-merge-backend", |     "translations-enable-merge-backend", | ||||||
|     gulp.parallel( |     gulp.parallel("gen-icons-json", "build-translations"), | ||||||
|       "gen-icons-json", |  | ||||||
|       "build-translations", |  | ||||||
|       "gather-gallery-demos" |  | ||||||
|     ), |  | ||||||
|     "copy-static-gallery", |     "copy-static-gallery", | ||||||
|     env.useRollup() ? "rollup-prod-gallery" : "webpack-prod-gallery", |     env.useRollup() ? "rollup-prod-gallery" : "webpack-prod-gallery", | ||||||
|     "gen-index-gallery-prod" |     "gen-index-gallery-prod" | ||||||
|   | |||||||
| @@ -85,11 +85,6 @@ gulp.task("copy-translations-app", async () => { | |||||||
|   copyTranslations(staticDir); |   copyTranslations(staticDir); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| gulp.task("copy-translations-supervisor", async () => { |  | ||||||
|   const staticDir = paths.hassio_output_static; |  | ||||||
|   copyTranslations(staticDir); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| gulp.task("copy-static-app", async () => { | gulp.task("copy-static-app", async () => { | ||||||
|   const staticDir = paths.app_output_static; |   const staticDir = paths.app_output_static; | ||||||
|   // Basic static files |   // Basic static files | ||||||
|   | |||||||
| @@ -11,7 +11,6 @@ const META_PATH = path.resolve(ICON_PACKAGE_PATH, "meta.json"); | |||||||
| const PACKAGE_PATH = path.resolve(ICON_PACKAGE_PATH, "package.json"); | const PACKAGE_PATH = path.resolve(ICON_PACKAGE_PATH, "package.json"); | ||||||
| const ICON_PATH = path.resolve(ICON_PACKAGE_PATH, "svg"); | const ICON_PATH = path.resolve(ICON_PACKAGE_PATH, "svg"); | ||||||
| const OUTPUT_DIR = path.resolve(__dirname, "../../build/mdi"); | const OUTPUT_DIR = path.resolve(__dirname, "../../build/mdi"); | ||||||
| const REMOVED_ICONS_PATH = path.resolve(__dirname, "../removedIcons.json"); |  | ||||||
|  |  | ||||||
| const encoding = "utf8"; | const encoding = "utf8"; | ||||||
|  |  | ||||||
| @@ -26,13 +25,6 @@ const getMeta = () => { | |||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const addRemovedMeta = (meta) => { |  | ||||||
|   const file = fs.readFileSync(REMOVED_ICONS_PATH, { encoding }); |  | ||||||
|   const removed = JSON.parse(file); |  | ||||||
|   const combinedMeta = [...meta, ...removed]; |  | ||||||
|   return combinedMeta.sort((a, b) => a.name.localeCompare(b.name)); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const splitBySize = (meta) => { | const splitBySize = (meta) => { | ||||||
|   const chunks = []; |   const chunks = []; | ||||||
|   const CHUNK_SIZE = 50000; |   const CHUNK_SIZE = 50000; | ||||||
| @@ -77,7 +69,7 @@ const findDifferentiator = (curString, prevString) => { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| gulp.task("gen-icons-json", (done) => { | gulp.task("gen-icons-json", (done) => { | ||||||
|   const meta = addRemovedMeta(getMeta()); |   const meta = getMeta(); | ||||||
|   const split = splitBySize(meta); |   const split = splitBySize(meta); | ||||||
|  |  | ||||||
|   if (!fs.existsSync(OUTPUT_DIR)) { |   if (!fs.existsSync(OUTPUT_DIR)) { | ||||||
|   | |||||||
| @@ -10,8 +10,24 @@ require("./gen-icons-json.js"); | |||||||
| require("./webpack.js"); | require("./webpack.js"); | ||||||
| require("./compress.js"); | require("./compress.js"); | ||||||
| require("./rollup.js"); | require("./rollup.js"); | ||||||
| require("./gather-static.js"); |  | ||||||
| require("./translations.js"); | async function writeEntrypointJS() { | ||||||
|  |   // We ship two builds and we need to do feature detection on what version to load. | ||||||
|  |   fs.mkdirSync(paths.hassio_output_root, { recursive: true }); | ||||||
|  |   fs.writeFileSync( | ||||||
|  |     path.resolve(paths.hassio_output_root, "entrypoint.js"), | ||||||
|  |     ` | ||||||
|  | try { | ||||||
|  |   new Function("import('${paths.hassio_publicPath}/frontend_latest/entrypoint.js')")(); | ||||||
|  | } catch (err) { | ||||||
|  |   var el = document.createElement('script'); | ||||||
|  |   el.src = '${paths.hassio_publicPath}/frontend_es5/entrypoint.js'; | ||||||
|  |   document.body.appendChild(el); | ||||||
|  | } | ||||||
|  |   `, | ||||||
|  |     { encoding: "utf-8" } | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  |  | ||||||
| gulp.task( | gulp.task( | ||||||
|   "develop-hassio", |   "develop-hassio", | ||||||
| @@ -21,9 +37,7 @@ gulp.task( | |||||||
|     }, |     }, | ||||||
|     "clean-hassio", |     "clean-hassio", | ||||||
|     "gen-icons-json", |     "gen-icons-json", | ||||||
|     "gen-index-hassio-dev", |     writeEntrypointJS, | ||||||
|     "build-supervisor-translations", |  | ||||||
|     "copy-translations-supervisor", |  | ||||||
|     env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio" |     env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio" | ||||||
|   ) |   ) | ||||||
| ); | ); | ||||||
| @@ -36,10 +50,8 @@ gulp.task( | |||||||
|     }, |     }, | ||||||
|     "clean-hassio", |     "clean-hassio", | ||||||
|     "gen-icons-json", |     "gen-icons-json", | ||||||
|     "build-supervisor-translations", |  | ||||||
|     "copy-translations-supervisor", |  | ||||||
|     env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio", |     env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio", | ||||||
|     "gen-index-hassio-prod", |     writeEntrypointJS, | ||||||
|     ...// Don't compress running tests |     ...// Don't compress running tests | ||||||
|     (env.isTest() ? [] : ["compress-hassio"]) |     (env.isTest() ? [] : ["compress-hassio"]) | ||||||
|   ) |   ) | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ const gulp = require("gulp"); | |||||||
| const fs = require("fs"); | const fs = require("fs"); | ||||||
| const foreach = require("gulp-foreach"); | const foreach = require("gulp-foreach"); | ||||||
| const merge = require("gulp-merge-json"); | const merge = require("gulp-merge-json"); | ||||||
|  | const minify = require("gulp-jsonminify"); | ||||||
| const rename = require("gulp-rename"); | const rename = require("gulp-rename"); | ||||||
| const transform = require("gulp-json-transform"); | const transform = require("gulp-json-transform"); | ||||||
| const { mapFiles } = require("../util"); | const { mapFiles } = require("../util"); | ||||||
| @@ -33,10 +34,21 @@ String.prototype.rsplit = function (sep, maxsplit) { | |||||||
|     : split; |     : split; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // Panel translations which should be split from the core translations. | // Panel translations which should be split from the core translations. These | ||||||
| const TRANSLATION_FRAGMENTS = Object.keys( | // should mirror the fragment definitions in polymer.json, so that we load | ||||||
|   require("../../src/translations/en.json").ui.panel | // additional resources at equivalent points. | ||||||
| ); | const TRANSLATION_FRAGMENTS = [ | ||||||
|  |   "config", | ||||||
|  |   "history", | ||||||
|  |   "logbook", | ||||||
|  |   "mailbox", | ||||||
|  |   "profile", | ||||||
|  |   "shopping-list", | ||||||
|  |   "page-authorize", | ||||||
|  |   "page-demo", | ||||||
|  |   "page-onboarding", | ||||||
|  |   "developer-tools", | ||||||
|  | ]; | ||||||
|  |  | ||||||
| function recursiveFlatten(prefix, data) { | function recursiveFlatten(prefix, data) { | ||||||
|   let output = {}; |   let output = {}; | ||||||
| @@ -266,7 +278,6 @@ gulp.task(taskName, function () { | |||||||
|         TRANSLATION_FRAGMENTS.forEach((fragment) => { |         TRANSLATION_FRAGMENTS.forEach((fragment) => { | ||||||
|           delete data.ui.panel[fragment]; |           delete data.ui.panel[fragment]; | ||||||
|         }); |         }); | ||||||
|         delete data.supervisor; |  | ||||||
|         return data; |         return data; | ||||||
|       }) |       }) | ||||||
|     ) |     ) | ||||||
| @@ -290,6 +301,7 @@ gulp.task("build-flattened-translations", function () { | |||||||
|         return flatten(data); |         return flatten(data); | ||||||
|       }) |       }) | ||||||
|     ) |     ) | ||||||
|  |     .pipe(minify()) | ||||||
|     .pipe( |     .pipe( | ||||||
|       rename((filePath) => { |       rename((filePath) => { | ||||||
|         if (filePath.dirname === "core") { |         if (filePath.dirname === "core") { | ||||||
| @@ -343,26 +355,18 @@ gulp.task( | |||||||
|   } |   } | ||||||
| ); | ); | ||||||
|  |  | ||||||
| gulp.task("build-translation-fragment-supervisor", function () { | gulp.task( | ||||||
|   return gulp |   "build-translations", | ||||||
|     .src(fullDir + "/*.json") |   gulp.series( | ||||||
|     .pipe(transform((data) => data.supervisor)) |     "clean-translations", | ||||||
|     .pipe(gulp.dest(workDir + "/supervisor")); |     "ensure-translations-build-dir", | ||||||
| }); |     env.isProdBuild() ? (done) => done() : "create-test-translation", | ||||||
|  |     "build-master-translation", | ||||||
| gulp.task("build-translation-flatten-supervisor", function () { |     "build-merged-translations", | ||||||
|   return gulp |     gulp.parallel(...splitTasks), | ||||||
|     .src(workDir + "/supervisor/*.json") |     "build-flattened-translations", | ||||||
|     .pipe( |     "build-translation-fingerprints", | ||||||
|       transform(function (data) { |     function writeMetadata() { | ||||||
|         // Polymer.AppLocalizeBehavior requires flattened json |  | ||||||
|         return flatten(data); |  | ||||||
|       }) |  | ||||||
|     ) |  | ||||||
|     .pipe(gulp.dest(outDir)); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| gulp.task("build-translation-write-metadata", function writeMetadata() { |  | ||||||
|       return gulp |       return gulp | ||||||
|         .src( |         .src( | ||||||
|           [ |           [ | ||||||
| @@ -378,13 +382,14 @@ gulp.task("build-translation-write-metadata", function writeMetadata() { | |||||||
|             const newData = {}; |             const newData = {}; | ||||||
|             Object.entries(data).forEach(([key, value]) => { |             Object.entries(data).forEach(([key, value]) => { | ||||||
|               // Filter out translations without native name. |               // Filter out translations without native name. | ||||||
|           if (value.nativeName) { |               if (data[key].nativeName) { | ||||||
|             newData[key] = value; |                 newData[key] = data[key]; | ||||||
|               } else { |               } else { | ||||||
|                 console.warn( |                 console.warn( | ||||||
|                   `Skipping language ${key}. Native name was not translated.` |                   `Skipping language ${key}. Native name was not translated.` | ||||||
|                 ); |                 ); | ||||||
|               } |               } | ||||||
|  |               if (data[key]) newData[key] = value; | ||||||
|             }); |             }); | ||||||
|             return newData; |             return newData; | ||||||
|           }) |           }) | ||||||
| @@ -397,33 +402,6 @@ gulp.task("build-translation-write-metadata", function writeMetadata() { | |||||||
|         ) |         ) | ||||||
|         .pipe(rename("translationMetadata.json")) |         .pipe(rename("translationMetadata.json")) | ||||||
|         .pipe(gulp.dest(workDir)); |         .pipe(gulp.dest(workDir)); | ||||||
| }); |     } | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "build-translations", |  | ||||||
|   gulp.series( |  | ||||||
|     "clean-translations", |  | ||||||
|     "ensure-translations-build-dir", |  | ||||||
|     env.isProdBuild() ? (done) => done() : "create-test-translation", |  | ||||||
|     "build-master-translation", |  | ||||||
|     "build-merged-translations", |  | ||||||
|     gulp.parallel(...splitTasks), |  | ||||||
|     "build-flattened-translations", |  | ||||||
|     "build-translation-fingerprints", |  | ||||||
|     "build-translation-write-metadata" |  | ||||||
|   ) |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| gulp.task( |  | ||||||
|   "build-supervisor-translations", |  | ||||||
|   gulp.series( |  | ||||||
|     "clean-translations", |  | ||||||
|     "ensure-translations-build-dir", |  | ||||||
|     "build-master-translation", |  | ||||||
|     "build-merged-translations", |  | ||||||
|     "build-translation-fragment-supervisor", |  | ||||||
|     "build-translation-flatten-supervisor", |  | ||||||
|     "build-translation-fingerprints", |  | ||||||
|     "build-translation-write-metadata" |  | ||||||
|   ) |   ) | ||||||
| ); | ); | ||||||
|   | |||||||
| @@ -1,11 +0,0 @@ | |||||||
| // Tasks to run Rollup |  | ||||||
| const gulp = require("gulp"); |  | ||||||
| const { startDevServer } = require("@web/dev-server"); |  | ||||||
|  |  | ||||||
| gulp.task("wds-watch-app", () => { |  | ||||||
|   startDevServer({ |  | ||||||
|     config: { |  | ||||||
|       watch: true, |  | ||||||
|     }, |  | ||||||
|   }); |  | ||||||
| }); |  | ||||||
| @@ -18,14 +18,6 @@ const bothBuilds = (createConfigFunc, params) => [ | |||||||
|   createConfigFunc({ ...params, latestBuild: false }), |   createConfigFunc({ ...params, latestBuild: false }), | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * @param {{ |  | ||||||
|  *   compiler: import("webpack").Compiler, |  | ||||||
|  *   contentBase: string, |  | ||||||
|  *   port: number, |  | ||||||
|  *   listenHost?: string |  | ||||||
|  * }} |  | ||||||
|  */ |  | ||||||
| const runDevServer = ({ | const runDevServer = ({ | ||||||
|   compiler, |   compiler, | ||||||
|   contentBase, |   contentBase, | ||||||
| @@ -41,13 +33,10 @@ const runDevServer = ({ | |||||||
|       throw err; |       throw err; | ||||||
|     } |     } | ||||||
|     // Server listening |     // Server listening | ||||||
|     log( |     log("[webpack-dev-server]", `http://localhost:${port}`); | ||||||
|       "[webpack-dev-server]", |  | ||||||
|       `Project is running at http://localhost:${port}` |  | ||||||
|     ); |  | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
| const doneHandler = (done) => (err, stats) => { | const handler = (done) => (err, stats) => { | ||||||
|   if (err) { |   if (err) { | ||||||
|     log.error(err.stack || err); |     log.error(err.stack || err); | ||||||
|     if (err.details) { |     if (err.details) { | ||||||
| @@ -56,31 +45,22 @@ const doneHandler = (done) => (err, stats) => { | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (stats.hasErrors() || stats.hasWarnings()) { |  | ||||||
|     console.log(stats.toString("minimal")); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   log(`Build done @ ${new Date().toLocaleTimeString()}`); |   log(`Build done @ ${new Date().toLocaleTimeString()}`); | ||||||
|  |  | ||||||
|  |   if (stats.hasErrors() || stats.hasWarnings()) { | ||||||
|  |     log.warn(stats.toString("minimal")); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   if (done) { |   if (done) { | ||||||
|     done(); |     done(); | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const prodBuild = (conf) => |  | ||||||
|   new Promise((resolve) => { |  | ||||||
|     webpack( |  | ||||||
|       conf, |  | ||||||
|       // Resolve promise when done. Because we pass a callback, webpack closes itself |  | ||||||
|       doneHandler(resolve) |  | ||||||
|     ); |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
| gulp.task("webpack-watch-app", () => { | gulp.task("webpack-watch-app", () => { | ||||||
|   // This command will run forever because we don't close compiler |   // we are not calling done, so this command will run forever | ||||||
|   webpack(createAppConfig({ isProdBuild: false, latestBuild: true })).watch( |   webpack(createAppConfig({ isProdBuild: false, latestBuild: true })).watch( | ||||||
|     { ignored: /build-translations/ }, |     { ignored: /build-translations/ }, | ||||||
|     doneHandler() |     handler() | ||||||
|   ); |   ); | ||||||
|   gulp.watch( |   gulp.watch( | ||||||
|     path.join(paths.translations_src, "en.json"), |     path.join(paths.translations_src, "en.json"), | ||||||
| @@ -88,11 +68,14 @@ gulp.task("webpack-watch-app", () => { | |||||||
|   ); |   ); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| gulp.task("webpack-prod-app", () => | gulp.task( | ||||||
|   prodBuild( |   "webpack-prod-app", | ||||||
|     bothBuilds(createAppConfig, { |   () => | ||||||
|       isProdBuild: true, |     new Promise((resolve) => | ||||||
|     }) |       webpack( | ||||||
|  |         bothBuilds(createAppConfig, { isProdBuild: true }), | ||||||
|  |         handler(resolve) | ||||||
|  |       ) | ||||||
|     ) |     ) | ||||||
| ); | ); | ||||||
|  |  | ||||||
| @@ -104,11 +87,16 @@ gulp.task("webpack-dev-server-demo", () => { | |||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| gulp.task("webpack-prod-demo", () => | gulp.task( | ||||||
|   prodBuild( |   "webpack-prod-demo", | ||||||
|  |   () => | ||||||
|  |     new Promise((resolve) => | ||||||
|  |       webpack( | ||||||
|         bothBuilds(createDemoConfig, { |         bothBuilds(createDemoConfig, { | ||||||
|           isProdBuild: true, |           isProdBuild: true, | ||||||
|     }) |         }), | ||||||
|  |         handler(resolve) | ||||||
|  |       ) | ||||||
|     ) |     ) | ||||||
| ); | ); | ||||||
|  |  | ||||||
| @@ -122,34 +110,40 @@ gulp.task("webpack-dev-server-cast", () => { | |||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| gulp.task("webpack-prod-cast", () => | gulp.task( | ||||||
|   prodBuild( |   "webpack-prod-cast", | ||||||
|  |   () => | ||||||
|  |     new Promise((resolve) => | ||||||
|  |       webpack( | ||||||
|         bothBuilds(createCastConfig, { |         bothBuilds(createCastConfig, { | ||||||
|           isProdBuild: true, |           isProdBuild: true, | ||||||
|     }) |         }), | ||||||
|  |  | ||||||
|  |         handler(resolve) | ||||||
|  |       ) | ||||||
|     ) |     ) | ||||||
| ); | ); | ||||||
|  |  | ||||||
| gulp.task("webpack-watch-hassio", () => { | gulp.task("webpack-watch-hassio", () => { | ||||||
|   // This command will run forever because we don't close compiler |   // we are not calling done, so this command will run forever | ||||||
|   webpack( |   webpack( | ||||||
|     createHassioConfig({ |     createHassioConfig({ | ||||||
|       isProdBuild: false, |       isProdBuild: false, | ||||||
|       latestBuild: true, |       latestBuild: true, | ||||||
|     }) |     }) | ||||||
|   ).watch({ ignored: /build-translations/ }, doneHandler()); |   ).watch({}, handler()); | ||||||
|  |  | ||||||
|   gulp.watch( |  | ||||||
|     path.join(paths.translations_src, "en.json"), |  | ||||||
|     gulp.series("build-supervisor-translations", "copy-translations-supervisor") |  | ||||||
|   ); |  | ||||||
| }); | }); | ||||||
|  |  | ||||||
| gulp.task("webpack-prod-hassio", () => | gulp.task( | ||||||
|   prodBuild( |   "webpack-prod-hassio", | ||||||
|  |   () => | ||||||
|  |     new Promise((resolve) => | ||||||
|  |       webpack( | ||||||
|         bothBuilds(createHassioConfig, { |         bothBuilds(createHassioConfig, { | ||||||
|           isProdBuild: true, |           isProdBuild: true, | ||||||
|     }) |         }), | ||||||
|  |         handler(resolve) | ||||||
|  |       ) | ||||||
|     ) |     ) | ||||||
| ); | ); | ||||||
|  |  | ||||||
| @@ -162,11 +156,17 @@ gulp.task("webpack-dev-server-gallery", () => { | |||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| gulp.task("webpack-prod-gallery", () => | gulp.task( | ||||||
|   prodBuild( |   "webpack-prod-gallery", | ||||||
|  |   () => | ||||||
|  |     new Promise((resolve) => | ||||||
|  |       webpack( | ||||||
|         createGalleryConfig({ |         createGalleryConfig({ | ||||||
|           isProdBuild: true, |           isProdBuild: true, | ||||||
|           latestBuild: true, |           latestBuild: true, | ||||||
|     }) |         }), | ||||||
|  |  | ||||||
|  |         handler(resolve) | ||||||
|  |       ) | ||||||
|     ) |     ) | ||||||
| ); | ); | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| const path = require("path"); | var path = require("path"); | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|   polymer_dir: path.resolve(__dirname, ".."), |   polymer_dir: path.resolve(__dirname, ".."), | ||||||
| @@ -34,12 +34,6 @@ module.exports = { | |||||||
|  |  | ||||||
|   hassio_dir: path.resolve(__dirname, "../hassio"), |   hassio_dir: path.resolve(__dirname, "../hassio"), | ||||||
|   hassio_output_root: path.resolve(__dirname, "../hassio/build"), |   hassio_output_root: path.resolve(__dirname, "../hassio/build"), | ||||||
|   hassio_output_static: path.resolve(__dirname, "../hassio/build/static"), |  | ||||||
|   hassio_output_latest: path.resolve( |  | ||||||
|     __dirname, |  | ||||||
|     "../hassio/build/frontend_latest" |  | ||||||
|   ), |  | ||||||
|   hassio_output_es5: path.resolve(__dirname, "../hassio/build/frontend_es5"), |  | ||||||
|   hassio_publicPath: "/api/hassio/app", |   hassio_publicPath: "/api/hassio/app", | ||||||
|  |  | ||||||
|   translations_src: path.resolve(__dirname, "../src/translations"), |   translations_src: path.resolve(__dirname, "../src/translations"), | ||||||
|   | |||||||
| @@ -1 +0,0 @@ | |||||||
| [] |  | ||||||
| @@ -1,3 +1,5 @@ | |||||||
|  | const path = require("path"); | ||||||
|  |  | ||||||
| module.exports = function (userOptions = {}) { | module.exports = function (userOptions = {}) { | ||||||
|   // Files need to be absolute paths. |   // Files need to be absolute paths. | ||||||
|   // This only works if the file has no exports |   // This only works if the file has no exports | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ const path = require("path"); | |||||||
| const commonjs = require("@rollup/plugin-commonjs"); | const commonjs = require("@rollup/plugin-commonjs"); | ||||||
| const resolve = require("@rollup/plugin-node-resolve"); | const resolve = require("@rollup/plugin-node-resolve"); | ||||||
| const json = require("@rollup/plugin-json"); | const json = require("@rollup/plugin-json"); | ||||||
| const babel = require("@rollup/plugin-babel").babel; | const babel = require("rollup-plugin-babel"); | ||||||
| const replace = require("@rollup/plugin-replace"); | const replace = require("@rollup/plugin-replace"); | ||||||
| const visualizer = require("rollup-plugin-visualizer"); | const visualizer = require("rollup-plugin-visualizer"); | ||||||
| const { string } = require("rollup-plugin-string"); | const { string } = require("rollup-plugin-string"); | ||||||
| @@ -31,7 +31,6 @@ const createRollupConfig = ({ | |||||||
|   isStatsBuild, |   isStatsBuild, | ||||||
|   publicPath, |   publicPath, | ||||||
|   dontHash, |   dontHash, | ||||||
|   isWDS, |  | ||||||
| }) => { | }) => { | ||||||
|   return { |   return { | ||||||
|     /** |     /** | ||||||
| @@ -62,7 +61,6 @@ const createRollupConfig = ({ | |||||||
|           ...bundle.babelOptions({ latestBuild }), |           ...bundle.babelOptions({ latestBuild }), | ||||||
|           extensions, |           extensions, | ||||||
|           exclude: bundle.babelExclude(), |           exclude: bundle.babelExclude(), | ||||||
|           babelHelpers: isWDS ? "inline" : "bundled", |  | ||||||
|         }), |         }), | ||||||
|         string({ |         string({ | ||||||
|           // Import certain extensions as strings |           // Import certain extensions as strings | ||||||
| @@ -71,21 +69,19 @@ const createRollupConfig = ({ | |||||||
|         replace( |         replace( | ||||||
|           bundle.definedVars({ isProdBuild, latestBuild, defineOverlay }) |           bundle.definedVars({ isProdBuild, latestBuild, defineOverlay }) | ||||||
|         ), |         ), | ||||||
|         !isWDS && |  | ||||||
|         manifest({ |         manifest({ | ||||||
|           publicPath, |           publicPath, | ||||||
|         }), |         }), | ||||||
|         !isWDS && worker(), |         worker(), | ||||||
|         !isWDS && dontHashPlugin({ dontHash }), |         dontHashPlugin({ dontHash }), | ||||||
|         !isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)), |         isProdBuild && terser(bundle.terserOptions(latestBuild)), | ||||||
|         !isWDS && |  | ||||||
|         isStatsBuild && |         isStatsBuild && | ||||||
|           visualizer({ |           visualizer({ | ||||||
|             // https://github.com/btd/rollup-plugin-visualizer#options |             // https://github.com/btd/rollup-plugin-visualizer#options | ||||||
|             open: true, |             open: true, | ||||||
|             sourcemap: true, |             sourcemap: true, | ||||||
|           }), |           }), | ||||||
|       ].filter(Boolean), |       ], | ||||||
|     }, |     }, | ||||||
|     /** |     /** | ||||||
|      * @type { import("rollup").OutputOptions } |      * @type { import("rollup").OutputOptions } | ||||||
| @@ -112,13 +108,12 @@ const createRollupConfig = ({ | |||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => { | const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => { | ||||||
|   return createRollupConfig( |   return createRollupConfig( | ||||||
|     bundle.config.app({ |     bundle.config.app({ | ||||||
|       isProdBuild, |       isProdBuild, | ||||||
|       latestBuild, |       latestBuild, | ||||||
|       isStatsBuild, |       isStatsBuild, | ||||||
|       isWDS, |  | ||||||
|     }) |     }) | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,24 +1,10 @@ | |||||||
| const webpack = require("webpack"); | const webpack = require("webpack"); | ||||||
| const path = require("path"); | const path = require("path"); | ||||||
| const TerserPlugin = require("terser-webpack-plugin"); | const TerserPlugin = require("terser-webpack-plugin"); | ||||||
| const { WebpackManifestPlugin } = require("webpack-manifest-plugin"); | const ManifestPlugin = require("webpack-manifest-plugin"); | ||||||
|  | const WorkerPlugin = require("worker-plugin"); | ||||||
| const paths = require("./paths.js"); | const paths = require("./paths.js"); | ||||||
| const bundle = require("./bundle"); | const bundle = require("./bundle"); | ||||||
| const log = require("fancy-log"); |  | ||||||
|  |  | ||||||
| class LogStartCompilePlugin { |  | ||||||
|   ignoredFirst = false; |  | ||||||
|  |  | ||||||
|   apply(compiler) { |  | ||||||
|     compiler.hooks.beforeCompile.tap("LogStartCompilePlugin", () => { |  | ||||||
|       if (!this.ignoredFirst) { |  | ||||||
|         this.ignoredFirst = true; |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|       log("Changes detected. Starting compilation"); |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const createWebpackConfig = ({ | const createWebpackConfig = ({ | ||||||
|   entry, |   entry, | ||||||
| @@ -36,7 +22,6 @@ const createWebpackConfig = ({ | |||||||
|   const ignorePackages = bundle.ignorePackages({ latestBuild }); |   const ignorePackages = bundle.ignorePackages({ latestBuild }); | ||||||
|   return { |   return { | ||||||
|     mode: isProdBuild ? "production" : "development", |     mode: isProdBuild ? "production" : "development", | ||||||
|     target: ["web", latestBuild ? "es2017" : "es5"], |  | ||||||
|     devtool: isProdBuild |     devtool: isProdBuild | ||||||
|       ? "cheap-module-source-map" |       ? "cheap-module-source-map" | ||||||
|       : "eval-cheap-module-source-map", |       : "eval-cheap-module-source-map", | ||||||
| @@ -45,7 +30,7 @@ const createWebpackConfig = ({ | |||||||
|     module: { |     module: { | ||||||
|       rules: [ |       rules: [ | ||||||
|         { |         { | ||||||
|           test: /\.m?js$|\.ts$/, |           test: /\.js$|\.ts$/, | ||||||
|           exclude: bundle.babelExclude(), |           exclude: bundle.babelExclude(), | ||||||
|           use: { |           use: { | ||||||
|             loader: "babel-loader", |             loader: "babel-loader", | ||||||
| @@ -61,14 +46,17 @@ const createWebpackConfig = ({ | |||||||
|     optimization: { |     optimization: { | ||||||
|       minimizer: [ |       minimizer: [ | ||||||
|         new TerserPlugin({ |         new TerserPlugin({ | ||||||
|  |           cache: true, | ||||||
|           parallel: true, |           parallel: true, | ||||||
|           extractComments: true, |           extractComments: true, | ||||||
|  |           sourceMap: true, | ||||||
|           terserOptions: bundle.terserOptions(latestBuild), |           terserOptions: bundle.terserOptions(latestBuild), | ||||||
|         }), |         }), | ||||||
|       ], |       ], | ||||||
|     }, |     }, | ||||||
|     plugins: [ |     plugins: [ | ||||||
|       new WebpackManifestPlugin({ |       new WorkerPlugin(), | ||||||
|  |       new ManifestPlugin({ | ||||||
|         // Only include the JS of entrypoints |         // Only include the JS of entrypoints | ||||||
|         filter: (file) => file.isInitial && !file.name.endsWith(".map"), |         filter: (file) => file.isInitial && !file.name.endsWith(".map"), | ||||||
|       }), |       }), | ||||||
| @@ -111,17 +99,7 @@ const createWebpackConfig = ({ | |||||||
|         new RegExp(bundle.emptyPackages({ latestBuild }).join("|")), |         new RegExp(bundle.emptyPackages({ latestBuild }).join("|")), | ||||||
|         path.resolve(paths.polymer_dir, "src/util/empty.js") |         path.resolve(paths.polymer_dir, "src/util/empty.js") | ||||||
|       ), |       ), | ||||||
|       // We need to change the import of the polyfill for EventTarget, so we replace the polyfill file with our customized one |     ], | ||||||
|       new webpack.NormalModuleReplacementPlugin( |  | ||||||
|         new RegExp( |  | ||||||
|           require.resolve( |  | ||||||
|             "lit-virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js" |  | ||||||
|           ) |  | ||||||
|         ), |  | ||||||
|         path.resolve(paths.polymer_dir, "src/resources/EventTarget-ponyfill.js") |  | ||||||
|       ), |  | ||||||
|       !isProdBuild && new LogStartCompilePlugin(), |  | ||||||
|     ].filter(Boolean), |  | ||||||
|     resolve: { |     resolve: { | ||||||
|       extensions: [".ts", ".js", ".json"], |       extensions: [".ts", ".js", ".json"], | ||||||
|     }, |     }, | ||||||
|   | |||||||
| Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 18 KiB | 
| Before Width: | Height: | Size: 186 KiB After Width: | Height: | Size: 186 KiB | 
| Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB | 
| @@ -37,13 +37,14 @@ | |||||||
|   <body> |   <body> | ||||||
|     <%= renderTemplate('_js_base') %> |     <%= renderTemplate('_js_base') %> | ||||||
|  |  | ||||||
|     <script> |     <script type="module" crossorigin="use-credentials"> | ||||||
|       import("<%= latestLauncherJS %>"); |       import "<%= latestLauncherJS %>"; | ||||||
|       window.latestJS = true; |  | ||||||
|     </script> |     </script> | ||||||
|  |  | ||||||
|     <script> |     <script nomodule> | ||||||
|       if (!window.latestJS) { |       (function() { | ||||||
|  |         // // Safari 10.1 supports type=module but ignores nomodule, so we add this check. | ||||||
|  |         if (!isS101) { | ||||||
|           <% if (useRollup) { %> |           <% if (useRollup) { %> | ||||||
|             _ls("/static/js/s.min.js").onload = function() { |             _ls("/static/js/s.min.js").onload = function() { | ||||||
|               System.import("<%= es5LauncherJS %>"); |               System.import("<%= es5LauncherJS %>"); | ||||||
| @@ -52,6 +53,7 @@ | |||||||
|             _ls("<%= es5LauncherJS %>"); |             _ls("<%= es5LauncherJS %>"); | ||||||
|           <% } %> |           <% } %> | ||||||
|         } |         } | ||||||
|  |       })(); | ||||||
|     </script> |     </script> | ||||||
|  |  | ||||||
|     <hc-layout subtitle="FAQ"> |     <hc-layout subtitle="FAQ"> | ||||||
| @@ -212,8 +214,13 @@ | |||||||
|           Chromecast is a technology developed by Google, and is available on: |           Chromecast is a technology developed by Google, and is available on: | ||||||
|         </p> |         </p> | ||||||
|         <ul> |         <ul> | ||||||
|           <li>Google Chrome (all platforms except iOS)</li> |           <li>Google Chrome (all platforms except on iOS)</li> | ||||||
|           <li>Microsoft Edge (all platforms)</li> |           <li> | ||||||
|  |             Microsoft Edge (all platforms, | ||||||
|  |             <a href="https://www.microsoftedgeinsider.com" target="_blank" | ||||||
|  |               >dev and canary builds only</a | ||||||
|  |             >) | ||||||
|  |           </li> | ||||||
|         </ul> |         </ul> | ||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -28,13 +28,14 @@ | |||||||
|  |  | ||||||
|     <hc-connect></hc-connect> |     <hc-connect></hc-connect> | ||||||
|  |  | ||||||
|     <script> |     <script type="module" crossorigin="use-credentials"> | ||||||
|       import("<%= latestLauncherJS %>"); |       import "<%= latestLauncherJS %>"; | ||||||
|       window.latestJS = true; |  | ||||||
|     </script> |     </script> | ||||||
|  |  | ||||||
|     <script> |     <script nomodule> | ||||||
|       if (!window.latestJS) { |       (function() { | ||||||
|  |         // // Safari 10.1 supports type=module but ignores nomodule, so we add this check. | ||||||
|  |         if (!isS101) { | ||||||
|           <% if (useRollup) { %> |           <% if (useRollup) { %> | ||||||
|             _ls("/static/js/s.min.js").onload = function() { |             _ls("/static/js/s.min.js").onload = function() { | ||||||
|               System.import("<%= es5LauncherJS %>"); |               System.import("<%= es5LauncherJS %>"); | ||||||
| @@ -43,6 +44,7 @@ | |||||||
|             _ls("<%= es5LauncherJS %>"); |             _ls("<%= es5LauncherJS %>"); | ||||||
|           <% } %> |           <% } %> | ||||||
|         } |         } | ||||||
|  |       })(); | ||||||
|     </script> |     </script> | ||||||
|     <script> |     <script> | ||||||
|     (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ |     (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ | ||||||
|   | |||||||
| @@ -1,4 +1,3 @@ | |||||||
| import "../../../src/resources/safari-14-attachshadow-patch"; | import "~app/resources/ha-style"; | ||||||
| import "../../../src/resources/ha-style"; | import "~app/resources/roboto"; | ||||||
| import "../../../src/resources/roboto"; |  | ||||||
| import "./layout/hc-connect"; | import "./layout/hc-connect"; | ||||||
|   | |||||||
| @@ -8,7 +8,6 @@ import { | |||||||
|   html, |   html, | ||||||
|   LitElement, |   LitElement, | ||||||
|   property, |   property, | ||||||
|   internalProperty, |  | ||||||
|   TemplateResult, |   TemplateResult, | ||||||
| } from "lit-element"; | } from "lit-element"; | ||||||
| import { CastManager } from "../../../../src/cast/cast_manager"; | import { CastManager } from "../../../../src/cast/cast_manager"; | ||||||
| @@ -29,7 +28,7 @@ import { | |||||||
|   getLovelaceCollection, |   getLovelaceCollection, | ||||||
|   LovelaceConfig, |   LovelaceConfig, | ||||||
| } from "../../../../src/data/lovelace"; | } from "../../../../src/data/lovelace"; | ||||||
| import "../../../../src/layouts/hass-loading-screen"; | import "../../../../src/layouts/loading-screen"; | ||||||
| import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config"; | import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config"; | ||||||
| import "./hc-layout"; | import "./hc-layout"; | ||||||
| import "@material/mwc-button/mwc-button"; | import "@material/mwc-button/mwc-button"; | ||||||
| @@ -42,13 +41,13 @@ class HcCast extends LitElement { | |||||||
|  |  | ||||||
|   @property() public castManager!: CastManager; |   @property() public castManager!: CastManager; | ||||||
|  |  | ||||||
|   @internalProperty() private askWrite = false; |   @property() private askWrite = false; | ||||||
|  |  | ||||||
|   @internalProperty() private lovelaceConfig?: LovelaceConfig | null; |   @property() private lovelaceConfig?: LovelaceConfig | null; | ||||||
|  |  | ||||||
|   protected render(): TemplateResult { |   protected render(): TemplateResult { | ||||||
|     if (this.lovelaceConfig === undefined) { |     if (this.lovelaceConfig === undefined) { | ||||||
|       return html`<hass-loading-screen no-toolbar></hass-loading-screen>`; |       return html` <loading-screen></loading-screen>> `; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const error = |     const error = | ||||||
|   | |||||||
| @@ -17,8 +17,8 @@ import { | |||||||
|   customElement, |   customElement, | ||||||
|   html, |   html, | ||||||
|   LitElement, |   LitElement, | ||||||
|  |   property, | ||||||
|   TemplateResult, |   TemplateResult, | ||||||
|   internalProperty, |  | ||||||
| } from "lit-element"; | } from "lit-element"; | ||||||
| import { CastManager, getCastManager } from "../../../../src/cast/cast_manager"; | import { CastManager, getCastManager } from "../../../../src/cast/cast_manager"; | ||||||
| import { castSendShowDemo } from "../../../../src/cast/receiver_messages"; | import { castSendShowDemo } from "../../../../src/cast/receiver_messages"; | ||||||
| @@ -27,7 +27,7 @@ import { | |||||||
|   saveTokens, |   saveTokens, | ||||||
| } from "../../../../src/common/auth/token_storage"; | } from "../../../../src/common/auth/token_storage"; | ||||||
| import "../../../../src/components/ha-icon"; | import "../../../../src/components/ha-icon"; | ||||||
| import "../../../../src/layouts/hass-loading-screen"; | import "../../../../src/layouts/loading-screen"; | ||||||
| import { registerServiceWorker } from "../../../../src/util/register-service-worker"; | import { registerServiceWorker } from "../../../../src/util/register-service-worker"; | ||||||
| import "./hc-layout"; | import "./hc-layout"; | ||||||
|  |  | ||||||
| @@ -60,19 +60,19 @@ const INTRO = html` | |||||||
|  |  | ||||||
| @customElement("hc-connect") | @customElement("hc-connect") | ||||||
| export class HcConnect extends LitElement { | export class HcConnect extends LitElement { | ||||||
|   @internalProperty() private loading = false; |   @property() private loading = false; | ||||||
|  |  | ||||||
|   // If we had stored credentials but we cannot connect, |   // If we had stored credentials but we cannot connect, | ||||||
|   // show a screen asking retry or logout. |   // show a screen asking retry or logout. | ||||||
|   @internalProperty() private cannotConnect = false; |   @property() private cannotConnect = false; | ||||||
|  |  | ||||||
|   @internalProperty() private error?: string | TemplateResult; |   @property() private error?: string | TemplateResult; | ||||||
|  |  | ||||||
|   @internalProperty() private auth?: Auth; |   @property() private auth?: Auth; | ||||||
|  |  | ||||||
|   @internalProperty() private connection?: Connection; |   @property() private connection?: Connection; | ||||||
|  |  | ||||||
|   @internalProperty() private castManager?: CastManager | null; |   @property() private castManager?: CastManager | null; | ||||||
|  |  | ||||||
|   private openDemo = false; |   private openDemo = false; | ||||||
|  |  | ||||||
| @@ -98,7 +98,7 @@ export class HcConnect extends LitElement { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (this.castManager === undefined || this.loading) { |     if (this.castManager === undefined || this.loading) { | ||||||
|       return html` <hass-loading-screen no-toolbar></hass-loading-screen> `; |       return html` <loading-screen></loading-screen> `; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (this.castManager === null) { |     if (this.castManager === null) { | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ class HcLayout extends LitElement { | |||||||
|       <ha-card> |       <ha-card> | ||||||
|         <div class="layout"> |         <div class="layout"> | ||||||
|           <img class="hero" src="/images/google-nest-hub.png" /> |           <img class="hero" src="/images/google-nest-hub.png" /> | ||||||
|           <h1 class="card-header"> |           <div class="card-header"> | ||||||
|             Home Assistant Cast${this.subtitle ? ` – ${this.subtitle}` : ""} |             Home Assistant Cast${this.subtitle ? ` – ${this.subtitle}` : ""} | ||||||
|             ${this.auth |             ${this.auth | ||||||
|               ? html` |               ? html` | ||||||
| @@ -44,7 +44,7 @@ class HcLayout extends LitElement { | |||||||
|                   </div> |                   </div> | ||||||
|                 ` |                 ` | ||||||
|               : ""} |               : ""} | ||||||
|           </h1> |           </div> | ||||||
|           <slot></slot> |           <slot></slot> | ||||||
|         </div> |         </div> | ||||||
|       </ha-card> |       </ha-card> | ||||||
| @@ -98,12 +98,8 @@ class HcLayout extends LitElement { | |||||||
|         line-height: 32px; |         line-height: 32px; | ||||||
|         padding: 24px 16px 16px; |         padding: 24px 16px 16px; | ||||||
|         display: block; |         display: block; | ||||||
|         margin: 0; |  | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       .hero { |  | ||||||
|         border-radius: 4px 4px 0 0; |  | ||||||
|       } |  | ||||||
|       .subtitle { |       .subtitle { | ||||||
|         font-size: 14px; |         font-size: 14px; | ||||||
|         color: var(--secondary-text-color); |         color: var(--secondary-text-color); | ||||||
|   | |||||||
| @@ -1,65 +1,18 @@ | |||||||
| /* eslint-disable no-undef */ | /* eslint-disable no-undef */ | ||||||
| import { CAST_NS } from "../../../src/cast/const"; | import { CAST_NS } from "~app/cast/const"; | ||||||
| import { HassMessage } from "../../../src/cast/receiver_messages"; | import { HassMessage } from "~app/cast/receiver_messages"; | ||||||
| import "../../../src/resources/custom-card-support"; | import "~app/resources/custom-card-support"; | ||||||
| import { castContext } from "./cast_context"; | import { castContext } from "./cast_context"; | ||||||
| import { HcMain } from "./layout/hc-main"; | import { HcMain } from "./layout/hc-main"; | ||||||
| import { ReceivedMessage } from "./types"; | import { ReceivedMessage } from "./types"; | ||||||
|  |  | ||||||
| const lovelaceController = new HcMain(); | const controller = new HcMain(); | ||||||
| document.body.append(lovelaceController); | document.body.append(controller); | ||||||
|  |  | ||||||
| const mediaPlayer = document.createElement("cast-media-player"); |  | ||||||
| mediaPlayer.style.display = "none"; |  | ||||||
| document.body.append(mediaPlayer); |  | ||||||
| const playerStylesAdded = false; |  | ||||||
|  |  | ||||||
| let controls: HTMLElement | null; |  | ||||||
|  |  | ||||||
| const setTouchControlsVisibility = (visible: boolean) => { |  | ||||||
|   if (!castContext.getDeviceCapabilities().touch_input_supported) { |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|   controls = |  | ||||||
|     controls || |  | ||||||
|     (document.body.querySelector("touch-controls") as HTMLElement | null); |  | ||||||
|   if (controls) { |  | ||||||
|     controls.style.display = visible ? "initial" : "none"; |  | ||||||
|   } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const showLovelaceController = () => { |  | ||||||
|   mediaPlayer.style.display = "none"; |  | ||||||
|   lovelaceController.style.display = "initial"; |  | ||||||
|   document.body.setAttribute("style", "overflow-y: auto !important"); |  | ||||||
|   setTouchControlsVisibility(false); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const showMediaPlayer = () => { |  | ||||||
|   lovelaceController.style.display = "none"; |  | ||||||
|   mediaPlayer.style.display = "initial"; |  | ||||||
|   document.body.removeAttribute("style"); |  | ||||||
|   setTouchControlsVisibility(true); |  | ||||||
|   if (!playerStylesAdded) { |  | ||||||
|     const style = document.createElement("style"); |  | ||||||
|     style.innerHTML = ` |  | ||||||
|     body { |  | ||||||
|       --logo-image: url('https://www.home-assistant.io/images/home-assistant-logo.svg'); |  | ||||||
|       --logo-repeat: no-repeat; |  | ||||||
|       --playback-logo-image: url('https://www.home-assistant.io/images/home-assistant-logo.svg'); |  | ||||||
|       --theme-hue: 200; |  | ||||||
|       --progress-color: #03a9f4; |  | ||||||
|       --splash-image: url('https://home-assistant.io/images/cast/splash.png'); |  | ||||||
|       --splash-size: cover; |  | ||||||
|     } |  | ||||||
|     `; |  | ||||||
|     document.head.appendChild(style); |  | ||||||
|   } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const options = new cast.framework.CastReceiverOptions(); | const options = new cast.framework.CastReceiverOptions(); | ||||||
| options.disableIdleTimeout = true; | options.disableIdleTimeout = true; | ||||||
| options.customNamespaces = { | options.customNamespaces = { | ||||||
|  |   // @ts-ignore | ||||||
|   [CAST_NS]: cast.framework.system.MessageType.JSON, |   [CAST_NS]: cast.framework.system.MessageType.JSON, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -77,61 +30,13 @@ options.uiConfig = new cast.framework.ui.UiConfig(); | |||||||
| // @ts-ignore | // @ts-ignore | ||||||
| options.uiConfig.touchScreenOptimizedApp = true; | options.uiConfig.touchScreenOptimizedApp = true; | ||||||
|  |  | ||||||
| castContext.setInactivityTimeout(86400); // 1 day |  | ||||||
|  |  | ||||||
| castContext.addCustomMessageListener( | castContext.addCustomMessageListener( | ||||||
|   CAST_NS, |   CAST_NS, | ||||||
|   // @ts-ignore |   // @ts-ignore | ||||||
|   (ev: ReceivedMessage<HassMessage>) => { |   (ev: ReceivedMessage<HassMessage>) => { | ||||||
|     // We received a show Lovelace command, stop media from playing, hide media player and show Lovelace controller |  | ||||||
|     if ( |  | ||||||
|       playerManager.getPlayerState() !== |  | ||||||
|       cast.framework.messages.PlayerState.IDLE |  | ||||||
|     ) { |  | ||||||
|       playerManager.stop(); |  | ||||||
|     } else { |  | ||||||
|       showLovelaceController(); |  | ||||||
|     } |  | ||||||
|     const msg = ev.data; |     const msg = ev.data; | ||||||
|     msg.senderId = ev.senderId; |     msg.senderId = ev.senderId; | ||||||
|     lovelaceController.processIncomingMessage(msg); |     controller.processIncomingMessage(msg); | ||||||
|   } |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| const playerManager = castContext.getPlayerManager(); |  | ||||||
|  |  | ||||||
| playerManager.setMessageInterceptor( |  | ||||||
|   cast.framework.messages.MessageType.LOAD, |  | ||||||
|   (loadRequestData) => { |  | ||||||
|     // We received a play media command, hide Lovelace and show media player |  | ||||||
|     showMediaPlayer(); |  | ||||||
|     const media = loadRequestData.media; |  | ||||||
|     // Special handling if it came from Google Assistant |  | ||||||
|     if (media.entity) { |  | ||||||
|       media.contentId = media.entity; |  | ||||||
|       media.streamType = cast.framework.messages.StreamType.LIVE; |  | ||||||
|       media.contentType = "application/vnd.apple.mpegurl"; |  | ||||||
|       // @ts-ignore |  | ||||||
|       media.hlsVideoSegmentFormat = |  | ||||||
|         cast.framework.messages.HlsVideoSegmentFormat.FMP4; |  | ||||||
|     } |  | ||||||
|     return loadRequestData; |  | ||||||
|   } |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| playerManager.addEventListener( |  | ||||||
|   cast.framework.events.EventType.MEDIA_STATUS, |  | ||||||
|   (event) => { |  | ||||||
|     if ( |  | ||||||
|       event.mediaStatus?.playerState === |  | ||||||
|         cast.framework.messages.PlayerState.IDLE && |  | ||||||
|       event.mediaStatus?.idleReason && |  | ||||||
|       event.mediaStatus?.idleReason !== |  | ||||||
|         cast.framework.messages.IdleReason.INTERRUPTED |  | ||||||
|     ) { |  | ||||||
|       // media finished or stopped, return to default Lovelace |  | ||||||
|       showLovelaceController(); |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| ); | ); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,10 +1,4 @@ | |||||||
| import { | import { customElement, html, property, TemplateResult } from "lit-element"; | ||||||
|   customElement, |  | ||||||
|   html, |  | ||||||
|   internalProperty, |  | ||||||
|   property, |  | ||||||
|   TemplateResult, |  | ||||||
| } from "lit-element"; |  | ||||||
| import { mockHistory } from "../../../../demo/src/stubs/history"; | import { mockHistory } from "../../../../demo/src/stubs/history"; | ||||||
| import { LovelaceConfig } from "../../../../src/data/lovelace"; | import { LovelaceConfig } from "../../../../src/data/lovelace"; | ||||||
| import { | import { | ||||||
| @@ -19,9 +13,9 @@ import "./hc-lovelace"; | |||||||
|  |  | ||||||
| @customElement("hc-demo") | @customElement("hc-demo") | ||||||
| class HcDemo extends HassElement { | class HcDemo extends HassElement { | ||||||
|   @property({ attribute: false }) public lovelacePath!: string; |   @property() public lovelacePath!: string; | ||||||
|  |  | ||||||
|   @internalProperty() private _lovelaceConfig?: LovelaceConfig; |   @property() private _lovelaceConfig?: LovelaceConfig; | ||||||
|  |  | ||||||
|   protected render(): TemplateResult { |   protected render(): TemplateResult { | ||||||
|     if (!this._lovelaceConfig) { |     if (!this._lovelaceConfig) { | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ import { HomeAssistant } from "../../../../src/types"; | |||||||
|  |  | ||||||
| @customElement("hc-launch-screen") | @customElement("hc-launch-screen") | ||||||
| class HcLaunchScreen extends LitElement { | class HcLaunchScreen extends LitElement { | ||||||
|   @property({ attribute: false }) public hass?: HomeAssistant; |   @property() public hass?: HomeAssistant; | ||||||
|  |  | ||||||
|   @property() public error?: string; |   @property() public error?: string; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,20 +9,19 @@ import { | |||||||
| } from "lit-element"; | } from "lit-element"; | ||||||
| import { LovelaceConfig } from "../../../../src/data/lovelace"; | import { LovelaceConfig } from "../../../../src/data/lovelace"; | ||||||
| import { Lovelace } from "../../../../src/panels/lovelace/types"; | import { Lovelace } from "../../../../src/panels/lovelace/types"; | ||||||
|  | import "../../../../src/panels/lovelace/views/hui-panel-view"; | ||||||
| import "../../../../src/panels/lovelace/views/hui-view"; | import "../../../../src/panels/lovelace/views/hui-view"; | ||||||
| import { HomeAssistant } from "../../../../src/types"; | import { HomeAssistant } from "../../../../src/types"; | ||||||
| import "./hc-launch-screen"; | import "./hc-launch-screen"; | ||||||
|  |  | ||||||
| @customElement("hc-lovelace") | @customElement("hc-lovelace") | ||||||
| class HcLovelace extends LitElement { | class HcLovelace extends LitElement { | ||||||
|   @property({ attribute: false }) public hass!: HomeAssistant; |   @property() public hass!: HomeAssistant; | ||||||
|  |  | ||||||
|   @property({ attribute: false }) public lovelaceConfig!: LovelaceConfig; |   @property() public lovelaceConfig!: LovelaceConfig; | ||||||
|  |  | ||||||
|   @property() public viewPath?: string | number; |   @property() public viewPath?: string | number; | ||||||
|  |  | ||||||
|   public urlPath?: string | null; |  | ||||||
|  |  | ||||||
|   protected render(): TemplateResult { |   protected render(): TemplateResult { | ||||||
|     const index = this._viewIndex; |     const index = this._viewIndex; | ||||||
|     if (index === undefined) { |     if (index === undefined) { | ||||||
| @@ -35,21 +34,28 @@ class HcLovelace extends LitElement { | |||||||
|     } |     } | ||||||
|     const lovelace: Lovelace = { |     const lovelace: Lovelace = { | ||||||
|       config: this.lovelaceConfig, |       config: this.lovelaceConfig, | ||||||
|       rawConfig: this.lovelaceConfig, |  | ||||||
|       editMode: false, |       editMode: false, | ||||||
|       urlPath: this.urlPath!, |  | ||||||
|       enableFullEditMode: () => undefined, |       enableFullEditMode: () => undefined, | ||||||
|       mode: "storage", |       mode: "storage", | ||||||
|       locale: this.hass.locale, |       language: "en", | ||||||
|       saveConfig: async () => undefined, |       saveConfig: async () => undefined, | ||||||
|       deleteConfig: async () => undefined, |       deleteConfig: async () => undefined, | ||||||
|       setEditMode: () => undefined, |       setEditMode: () => undefined, | ||||||
|     }; |     }; | ||||||
|     return html` |     return this.lovelaceConfig.views[index].panel | ||||||
|  |       ? html` | ||||||
|  |           <hui-panel-view | ||||||
|  |             .hass=${this.hass} | ||||||
|  |             .lovelace=${lovelace} | ||||||
|  |             .config=${this.lovelaceConfig.views[index]} | ||||||
|  |           ></hui-panel-view> | ||||||
|  |         ` | ||||||
|  |       : html` | ||||||
|           <hui-view |           <hui-view | ||||||
|             .hass=${this.hass} |             .hass=${this.hass} | ||||||
|             .lovelace=${lovelace} |             .lovelace=${lovelace} | ||||||
|             .index=${index} |             .index=${index} | ||||||
|  |             columns="2" | ||||||
|           ></hui-view> |           ></hui-view> | ||||||
|         `; |         `; | ||||||
|   } |   } | ||||||
| @@ -67,7 +73,7 @@ class HcLovelace extends LitElement { | |||||||
|  |  | ||||||
|         if (configBackground) { |         if (configBackground) { | ||||||
|           (this.shadowRoot!.querySelector( |           (this.shadowRoot!.querySelector( | ||||||
|             "hui-view" |             "hui-view, hui-panel-view" | ||||||
|           ) as HTMLElement)!.style.setProperty( |           ) as HTMLElement)!.style.setProperty( | ||||||
|             "--lovelace-background", |             "--lovelace-background", | ||||||
|             configBackground |             configBackground | ||||||
| @@ -95,7 +101,6 @@ class HcLovelace extends LitElement { | |||||||
|     return css` |     return css` | ||||||
|       :host { |       :host { | ||||||
|         min-height: 100vh; |         min-height: 100vh; | ||||||
|         height: 0; |  | ||||||
|         display: flex; |         display: flex; | ||||||
|         flex-direction: column; |         flex-direction: column; | ||||||
|         box-sizing: border-box; |         box-sizing: border-box; | ||||||
|   | |||||||
| @@ -3,12 +3,7 @@ import { | |||||||
|   getAuth, |   getAuth, | ||||||
|   UnsubscribeFunc, |   UnsubscribeFunc, | ||||||
| } from "home-assistant-js-websocket"; | } from "home-assistant-js-websocket"; | ||||||
| import { | import { customElement, html, property, TemplateResult } from "lit-element"; | ||||||
|   customElement, |  | ||||||
|   html, |  | ||||||
|   internalProperty, |  | ||||||
|   TemplateResult, |  | ||||||
| } from "lit-element"; |  | ||||||
| import { CAST_NS } from "../../../../src/cast/const"; | import { CAST_NS } from "../../../../src/cast/const"; | ||||||
| import { | import { | ||||||
|   ConnectMessage, |   ConnectMessage, | ||||||
| @@ -36,13 +31,13 @@ let resourcesLoaded = false; | |||||||
|  |  | ||||||
| @customElement("hc-main") | @customElement("hc-main") | ||||||
| export class HcMain extends HassElement { | export class HcMain extends HassElement { | ||||||
|   @internalProperty() private _showDemo = false; |   @property() private _showDemo = false; | ||||||
|  |  | ||||||
|   @internalProperty() private _lovelaceConfig?: LovelaceConfig; |   @property() private _lovelaceConfig?: LovelaceConfig; | ||||||
|  |  | ||||||
|   @internalProperty() private _lovelacePath: string | number | null = null; |   @property() private _lovelacePath: string | number | null = null; | ||||||
|  |  | ||||||
|   @internalProperty() private _error?: string; |   @property() private _error?: string; | ||||||
|  |  | ||||||
|   private _unsubLovelace?: UnsubscribeFunc; |   private _unsubLovelace?: UnsubscribeFunc; | ||||||
|  |  | ||||||
| @@ -87,7 +82,6 @@ export class HcMain extends HassElement { | |||||||
|         .hass=${this.hass} |         .hass=${this.hass} | ||||||
|         .lovelaceConfig=${this._lovelaceConfig} |         .lovelaceConfig=${this._lovelaceConfig} | ||||||
|         .viewPath=${this._lovelacePath} |         .viewPath=${this._lovelacePath} | ||||||
|         .urlPath=${this._urlPath} |  | ||||||
|         @config-refresh=${this._generateLovelaceConfig} |         @config-refresh=${this._generateLovelaceConfig} | ||||||
|       ></hc-lovelace> |       ></hc-lovelace> | ||||||
|     `; |     `; | ||||||
| @@ -216,22 +210,18 @@ export class HcMain extends HassElement { | |||||||
|     } |     } | ||||||
|     this._showDemo = false; |     this._showDemo = false; | ||||||
|     this._lovelacePath = msg.viewPath; |     this._lovelacePath = msg.viewPath; | ||||||
|  |     if (castContext.getDeviceCapabilities().touch_input_supported) { | ||||||
|  |       this._breakFree(); | ||||||
|  |     } | ||||||
|     this._sendStatus(); |     this._sendStatus(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private async _generateLovelaceConfig() { |   private async _generateLovelaceConfig() { | ||||||
|     const { generateLovelaceDashboardStrategy } = await import( |     const { generateLovelaceConfigFromHass } = await import( | ||||||
|       "../../../../src/panels/lovelace/strategies/get-strategy" |       "../../../../src/panels/lovelace/common/generate-lovelace-config" | ||||||
|     ); |     ); | ||||||
|     this._handleNewLovelaceConfig( |     this._handleNewLovelaceConfig( | ||||||
|       await generateLovelaceDashboardStrategy( |       await generateLovelaceConfigFromHass(this.hass!) | ||||||
|         { |  | ||||||
|           hass: this.hass!, |  | ||||||
|           narrow: false, |  | ||||||
|         }, |  | ||||||
|         "original-states" |  | ||||||
|       ) |  | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -245,6 +235,9 @@ export class HcMain extends HassElement { | |||||||
|       this._showDemo = true; |       this._showDemo = true; | ||||||
|       this._lovelacePath = "overview"; |       this._lovelacePath = "overview"; | ||||||
|       this._sendStatus(); |       this._sendStatus(); | ||||||
|  |       if (castContext.getDeviceCapabilities().touch_input_supported) { | ||||||
|  |         this._breakFree(); | ||||||
|  |       } | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -265,6 +258,14 @@ export class HcMain extends HassElement { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   private _breakFree() { | ||||||
|  |     const controls = document.body.querySelector("touch-controls"); | ||||||
|  |     if (controls) { | ||||||
|  |       controls.remove(); | ||||||
|  |     } | ||||||
|  |     document.body.setAttribute("style", "overflow-y: auto !important"); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   private sendMessage(senderId: string, response: any) { |   private sendMessage(senderId: string, response: any) { | ||||||
|     castContext.sendCustomMessage(CAST_NS, senderId, response); |     castContext.sendCustomMessage(CAST_NS, senderId, response); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import "web-animations-js/web-animations-next-lite.min"; | import "web-animations-js/web-animations-next-lite.min"; | ||||||
| import "../../../src/resources/ha-style"; |  | ||||||
| import "../../../src/resources/roboto"; | import "../../../src/resources/roboto"; | ||||||
|  | import "../../../src/resources/ha-style"; | ||||||
| import "./layout/hc-lovelace"; | import "./layout/hc-lovelace"; | ||||||
|   | |||||||
| Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.8 KiB | 
| Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB | 
| Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB | 
| Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.3 KiB | 
| Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 8.4 KiB | 
| Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.2 KiB | 
| Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.7 KiB | 
| Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 3.1 KiB | 
| Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.3 KiB | 
| Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 6.1 KiB | 
| Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.7 KiB | 
| Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.4 KiB | 
| Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.5 KiB | 
| Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 9.9 KiB | 
| Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.9 KiB | 
| Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.5 KiB | 
| Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.8 KiB | 
| Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 4.6 KiB | 
| Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 4.5 KiB | 
| Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.5 KiB | 
| Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 8.2 KiB | 
| Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 8.5 KiB | 
| Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 11 KiB | 
| Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.7 KiB | 
| Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.3 KiB | 
| Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 9.9 KiB | 
| Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.9 KiB | 
| Before Width: | Height: | Size: 532 B After Width: | Height: | Size: 767 B | 
| Before Width: | Height: | Size: 535 B After Width: | Height: | Size: 803 B | 
| Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB | 
| Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB | 
| Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB | 
| Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB | 
| Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 8.2 KiB | 
| Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 33 KiB | 
| Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 106 KiB | 
| Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB | 
| Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 62 KiB | 
| Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 56 KiB | 
| Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 57 KiB | 
| Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 24 KiB | 
| Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB | 
| Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |