diff --git a/.github/label-actions.yml b/.github/label-actions.yml index 20f85fd45..47540368c 100644 --- a/.github/label-actions.yml +++ b/.github/label-actions.yml @@ -1,6 +1,6 @@ # Configuration for Label Actions - https://github.com/dessant/label-actions -not on Github: +not an Issue: issues: comment: | Ahoi! @@ -9,10 +9,12 @@ not on Github: Many helpful people will not see your message here and you are unlikely to get a useful response. - We use github to handle bugreports, feature requests and - planning new releases. + We use the Github Issue-Tracker only for development related + topics, like feature requests, bug reports etc. To get help, + please join our Discord-Server or ask in Github Discussions: - Please use our Discord-Server for help: [discord.gg/mainsail](https://discord.gg/mainsail) + - [discord.gg/mainsail](https://discord.gg/mainsail) + - [GitHub Discussions](https://github.com/orgs/mainsail-crew/discussions) This ticket will be automatically closed. diff --git a/.github/workflows/auto-analyze.yml b/.github/workflows/auto-analyze.yml index a68e27837..cce72a827 100644 --- a/.github/workflows/auto-analyze.yml +++ b/.github/workflows/auto-analyze.yml @@ -19,12 +19,12 @@ jobs: - name: Check package.json was changed id: changed-file-package-json - uses: tj-actions/changed-files@v23 + uses: tj-actions/changed-files@v41 with: sha: ${{ github.event.pull_request.head.sha }} - name: Install node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 if: contains(steps.changed-file-package-json.outputs.modified_files, 'package.json') with: node-version: 20 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6d6eac88a..a1e0a9607 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: uses: actions/checkout@v4 - name: Install node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 20 @@ -39,7 +39,7 @@ jobs: - name: Copy output run: cp ${{ github.workspace }}/dist/mainsail.zip mainsail-latest.zip - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: mainsail-latest.zip path: mainsail-latest.zip diff --git a/.github/workflows/check-pr-title.yml b/.github/workflows/check-pr-title.yml index a5dbf04f9..c7aaaad5d 100644 --- a/.github/workflows/check-pr-title.yml +++ b/.github/workflows/check-pr-title.yml @@ -13,7 +13,7 @@ jobs: name: Validate PR title runs-on: ubuntu-latest steps: - - uses: amannn/action-semantic-pull-request@v5 + - uses: amannn/action-semantic-pull-request@v5.4.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/check_locale.yml b/.github/workflows/check_locale.yml index 58f8b4b80..389ae7aa9 100644 --- a/.github/workflows/check_locale.yml +++ b/.github/workflows/check_locale.yml @@ -28,7 +28,7 @@ jobs: - name: Get changed files id: changed-files - uses: tj-actions/changed-files@v23 + uses: tj-actions/changed-files@v41 with: sha: ${{ github.event.pull_request.head.sha }} files: 'src/locales/*.json' diff --git a/.github/workflows/code_style.yml b/.github/workflows/code_style.yml index f70ae163d..ff70cc7ad 100644 --- a/.github/workflows/code_style.yml +++ b/.github/workflows/code_style.yml @@ -30,7 +30,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Install node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 20 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ceed8fa2a..3106510be 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -60,7 +60,7 @@ jobs: fetch-depth: 0 - name: Install node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 20 @@ -117,7 +117,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: 'develop' token: ${{ secrets.PAT }} @@ -141,7 +141,7 @@ jobs: - name: Show CHANGELOG run: | cat CHANGELOG.md - - uses: stefanzweifel/git-auto-commit-action@v4 + - uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: 'docs(changelog): update changelog' file_pattern: CHANGELOG.md diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 174b6f0f3..b458bee76 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v4 - name: Install node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 20 @@ -28,6 +28,6 @@ jobs: run: npm run build - name: Cypress run - uses: cypress-io/github-action@v4 + uses: cypress-io/github-action@v6 with: start: npm run preview diff --git a/CHANGELOG.md b/CHANGELOG.md index ca2208e0d..e6175037c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,47 @@ # Changelog All notable changes to Mainsail will be documented in this file. +## [2.10.0](https://github.com/mainsail-crew/mainsail/releases/tag/v2.10.0) - 2024-02-15 +### Features + +- **history**: Add interrupted state to history job ([#1738](https://github.com/mainsail-crew/mainsail/pull/1738)) +- Add ability to re-arrange job queue's items ([#1692](https://github.com/mainsail-crew/mainsail/pull/1692)) +- Add sum + eta in jobqueue panel ([#1770](https://github.com/mainsail-crew/mainsail/pull/1770)) +- Add devices dialog in editor ([#1765](https://github.com/mainsail-crew/mainsail/pull/1765)) +- Add ability to add history items to job queue ([#1778](https://github.com/mainsail-crew/mainsail/pull/1778)) + +### Bug Fixes and Improvements + +- **console**: Fix color of autocomplete and command list ([#1733](https://github.com/mainsail-crew/mainsail/pull/1733)) +- **timelapse**: Fix issue with changing timelapse settings ([#1745](https://github.com/mainsail-crew/mainsail/pull/1745)) +- Incorrect scaling of images in dialogImage ([#1746](https://github.com/mainsail-crew/mainsail/pull/1746)) +- Show extruder extra menu without load/unload macros ([#1747](https://github.com/mainsail-crew/mainsail/pull/1747)) +- Fix ETA calculation from jobqueue during print preheat ([#1773](https://github.com/mainsail-crew/mainsail/pull/1773)) +- File upload rate displays `NaN` instead of an actual value ([#1777](https://github.com/mainsail-crew/mainsail/pull/1777)) + +### Performance + +- Batch gcode file metadata requests ([#1737](https://github.com/mainsail-crew/mainsail/pull/1737)) + +### Refactor + +- Refactor spoolman integration to support v2 response ([#1749](https://github.com/mainsail-crew/mainsail/pull/1749)) +- Refactor heightmap page ([#1759](https://github.com/mainsail-crew/mainsail/pull/1759)) + +### Localization + +- **da**: Update danish translation ([#1757](https://github.com/mainsail-crew/mainsail/pull/1757)) +- **de**: Update german locale ([#1772](https://github.com/mainsail-crew/mainsail/pull/1772)) +- **en**: Fix typo in DescriptionPreviouslyThrottled ([#1776](https://github.com/mainsail-crew/mainsail/pull/1776)) +- **it**: Update italian translation ([#1763](https://github.com/mainsail-crew/mainsail/pull/1763)) +- **zh**: Update chinese locale ([#1767](https://github.com/mainsail-crew/mainsail/pull/1767)) + +### Other + +- **deps**: Update @sindarius/gcodeviewer ([#1755](https://github.com/mainsail-crew/mainsail/pull/1755)) +- Update github issue bot text ([#1743](https://github.com/mainsail-crew/mainsail/pull/1743)) +- Fix typo in bot text ([#1748](https://github.com/mainsail-crew/mainsail/pull/1748)) + ## [2.9.1](https://github.com/mainsail-crew/mainsail/releases/tag/v2.9.1) - 2023-12-31 ### Bug Fixes and Improvements diff --git a/package-lock.json b/package-lock.json index dbc5c6d17..0c1fb3601 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mainsail", - "version": "2.9.1", + "version": "2.10.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mainsail", - "version": "2.9.1", + "version": "2.10.0", "dependencies": { "@codemirror/commands": "^6.0.1", "@codemirror/lang-css": "^6.0.0", @@ -18,7 +18,7 @@ "@codemirror/view": "^6.0.3", "@jaames/iro": "^5.5.2", "@lezer/highlight": "^1.0.0", - "@sindarius/gcodeviewer": "^3.2.2", + "@sindarius/gcodeviewer": "^3.7.8", "@uiw/codemirror-theme-vscode": "^4.19.11", "axios": "^1.6.0", "codemirror": "^6.0.1", @@ -79,7 +79,7 @@ "start-server-and-test": "^2.0.0", "typescript": "^4.5.5", "unplugin-vue-components": "^0.22.12", - "vite": "^4.4.12", + "vite": "^4.5.2", "vite-plugin-checker": "^0.6.0", "vite-plugin-package-version": "^1.0.2", "vite-plugin-pwa": "^0.16.4", @@ -2136,76 +2136,76 @@ } }, "node_modules/@babylonjs/core": { - "version": "5.57.1", - "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-5.57.1.tgz", - "integrity": "sha512-k8U+SFPVGvHgeNr651nxZL26iVCmTzjmRrdbQa0BGas+lg8PIV/tfhN4uTeWWQeRFLvjbkcHA5qD8x/Xk3EpMQ==" + "version": "6.43.0", + "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-6.43.0.tgz", + "integrity": "sha512-pLFvvtC26TeICUcF0YjuuQqvCahJwPlATXnJkvBzMWXXxUE0Y5rI8L28iScu2WKy975605KdccJI5VL2NG7sUQ==" }, "node_modules/@babylonjs/gui": { - "version": "5.57.1", - "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-5.57.1.tgz", - "integrity": "sha512-NUOYKF5iAA0UmFFHUBkb12K9Zvqw8BnOVAbM27CZ7WpnHFQ+4/CJuy9nFoCGkBjv2A5W7M1ML/7cjmce0CbdRA==", + "version": "6.43.0", + "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-6.43.0.tgz", + "integrity": "sha512-iYsB7uyC40w0QkFGcPw8jAzE26jsgTFvWHTVKPSZxjQ5cG2RQEeK3aD8l+GvWauSc2qPB0wMOsvkAtusdy1g7g==", "peer": true, "peerDependencies": { - "@babylonjs/core": "^5.22.0" + "@babylonjs/core": "^6.0.0" } }, "node_modules/@babylonjs/gui-editor": { - "version": "5.57.1", - "resolved": "https://registry.npmjs.org/@babylonjs/gui-editor/-/gui-editor-5.57.1.tgz", - "integrity": "sha512-FY4py7CzLiW0NAd0atBb5oCI/bS6kLsfszliZFvgrWl+eJ6z54aD1m23dZ+zvy8IqxcbSUkFYKqSfRinZttnkA==", + "version": "6.43.0", + "resolved": "https://registry.npmjs.org/@babylonjs/gui-editor/-/gui-editor-6.43.0.tgz", + "integrity": "sha512-OtahbC04Mklj2i2WdGlYXc9oN/ntpp2DuNZhqc1l95ioPIZWtlhSzMQKzh6/VmJQZBwwRi8HZwPPjQ1arIkRHA==", "peer": true, "peerDependencies": { - "@babylonjs/core": "^5.22.0", - "@babylonjs/gui": "^5.22.0", + "@babylonjs/core": "^6.0.0", + "@babylonjs/gui": "^6.0.0", "@types/react": ">=16.7.3", "@types/react-dom": ">=16.0.9" } }, "node_modules/@babylonjs/inspector": { - "version": "5.57.1", - "resolved": "https://registry.npmjs.org/@babylonjs/inspector/-/inspector-5.57.1.tgz", - "integrity": "sha512-F40V91R4h+vhlmBs7FHA4qQkFKjJwWF78cUhUgTVwGdwHDRwZ3Pweu26xEfTtw71am26tgHgBZedBTNCokgqtQ==", + "version": "6.43.0", + "resolved": "https://registry.npmjs.org/@babylonjs/inspector/-/inspector-6.43.0.tgz", + "integrity": "sha512-Cjw/Xk8v5/84IFi6rhLS363XgDkQzPrfQG7nFKHF6k8OWwz8Ze/cb8O8IeXKEc2QFImCdRnZoDv7BkJnZ03Caw==", "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.1.0", "@fortawesome/free-regular-svg-icons": "^6.0.0", "@fortawesome/free-solid-svg-icons": "^6.0.0" }, "peerDependencies": { - "@babylonjs/core": "^5.22.0", - "@babylonjs/gui": "^5.22.0", - "@babylonjs/gui-editor": "^5.22.0", - "@babylonjs/loaders": "^5.22.0", - "@babylonjs/materials": "^5.22.0", - "@babylonjs/serializers": "^5.22.0", + "@babylonjs/core": "^6.0.0", + "@babylonjs/gui": "^6.0.0", + "@babylonjs/gui-editor": "^6.0.0", + "@babylonjs/loaders": "^6.0.0", + "@babylonjs/materials": "^6.0.0", + "@babylonjs/serializers": "^6.0.0", "@types/react": ">=16.7.3", "@types/react-dom": ">=16.0.9" } }, "node_modules/@babylonjs/loaders": { - "version": "5.57.1", - "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-5.57.1.tgz", - "integrity": "sha512-DHk0iOwJgnTcj8vI+cQVzpiUvWGJvYqWyZxsf7nzhbeND+ZwNWSG0AelaGbGHpWTaDSd/DjUcoWVZuT4Cg6/Xg==", + "version": "6.43.0", + "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-6.43.0.tgz", + "integrity": "sha512-+cgw4NDchjc/5dZYGzOEhJU+9++ygYlA+ChybE5yvF+yUDWZ7fGXnEXMFImXeFELjk5RRK1NS3H3YvPe7A+Rug==", "peerDependencies": { - "@babylonjs/core": "^5.22.0", - "babylonjs-gltf2interface": "^5.22.0" + "@babylonjs/core": "^6.0.0", + "babylonjs-gltf2interface": "^6.0.0" } }, "node_modules/@babylonjs/materials": { - "version": "5.57.1", - "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-5.57.1.tgz", - "integrity": "sha512-NLdR6eGUr+wFqie937HRWsnaWPeECpjOrYDGvjjYDg08VAcIC3YyctAuQxv8rId7BoDttkUd8Ey/IZ5d/9H7GA==", + "version": "6.43.0", + "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-6.43.0.tgz", + "integrity": "sha512-x4pKz2d0IRFZ9pmWIJ4FR/X5mak8Ycl2tAQtOf1ihsanNyDgXnB3Jfgl9z9TaoQSoU2y+HpjvenDF4oaLkXzUw==", "peerDependencies": { - "@babylonjs/core": "^5.22.0" + "@babylonjs/core": "^6.0.0" } }, "node_modules/@babylonjs/serializers": { - "version": "5.57.1", - "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-5.57.1.tgz", - "integrity": "sha512-wV7pMhvEP9lf3zjYo2YmQDl2D8V617wONkMuNRj+lpJsD2I2PT7B2wxUqtKVoCY+TyCEM5ns9vn722V7bHZiHg==", + "version": "6.43.0", + "resolved": "https://registry.npmjs.org/@babylonjs/serializers/-/serializers-6.43.0.tgz", + "integrity": "sha512-cVFGO98SapRf/QJRQx7+hZkbFnDhXuHK9obDJKKpv/76vSpLWDoLoZk1v1Nu8pm30n75Ukffcco77Za14ZZhFA==", "peer": true, "peerDependencies": { - "@babylonjs/core": "^5.22.0", - "babylonjs-gltf2interface": "^5.22.0" + "@babylonjs/core": "^6.0.0", + "babylonjs-gltf2interface": "^6.0.0" } }, "node_modules/@codemirror/autocomplete": { @@ -2825,45 +2825,45 @@ } }, "node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz", - "integrity": "sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz", + "integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==", "hasInstallScript": true, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.2.tgz", - "integrity": "sha512-gjYDSKv3TrM2sLTOKBc5rH9ckje8Wrwgx1CxAPbN5N3Fm4prfi7NsJVWd1jklp7i5uSCVwhZS5qlhMXqLrpAIg==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz", + "integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==", "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.2" + "@fortawesome/fontawesome-common-types": "6.5.1" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/free-regular-svg-icons": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.4.2.tgz", - "integrity": "sha512-0+sIUWnkgTVVXVAPQmW4vxb9ZTHv0WstOa3rBx9iPxrrrDH6bNLsDYuwXF9b6fGm+iR7DKQvQshUH/FJm3ed9Q==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.1.tgz", + "integrity": "sha512-m6ShXn+wvqEU69wSP84coxLbNl7sGVZb+Ca+XZq6k30SzuP3X4TfPqtycgUh9ASwlNh5OfQCd8pDIWxl+O+LlQ==", "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.2" + "@fortawesome/fontawesome-common-types": "6.5.1" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.2.tgz", - "integrity": "sha512-sYwXurXUEQS32fZz9hVCUUv/xu49PEJEyUOsA51l6PU/qVgfbTb2glsTEaJngVVT8VqBATRIdh7XVgV1JF1LkA==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz", + "integrity": "sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==", "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.2" + "@fortawesome/fontawesome-common-types": "6.5.1" }, "engines": { "node": ">=6" @@ -3197,14 +3197,14 @@ "dev": true }, "node_modules/@sindarius/gcodeviewer": { - "version": "3.7.6", - "resolved": "https://registry.npmjs.org/@sindarius/gcodeviewer/-/gcodeviewer-3.7.6.tgz", - "integrity": "sha512-6L8znazyJgAJ8IiDTSF1o4DB+iQxszM3WfsQBFcKaSIwUYJVWQIpuccHrXvsvFQB6xQ7WAwrac5grkGfzIrNpA==", - "dependencies": { - "@babylonjs/core": "^5.53.0", - "@babylonjs/inspector": "^5.53.0", - "@babylonjs/loaders": "^5.53.0", - "@babylonjs/materials": "^5.53.0", + "version": "3.7.8", + "resolved": "https://registry.npmjs.org/@sindarius/gcodeviewer/-/gcodeviewer-3.7.8.tgz", + "integrity": "sha512-/Z9YE/GaVBV4dydju9kJ1uv8/GlffUzjoIGYxlj+kvZnBhP7OZ0ZWviL7zs4d7npSwSw7J+LNYWaIpVW13KQrA==", + "dependencies": { + "@babylonjs/core": "^6.43.0", + "@babylonjs/inspector": "^6.43.0", + "@babylonjs/loaders": "^6.43.0", + "@babylonjs/materials": "^6.43.0", "d3": "^7.4.4" } }, @@ -3293,15 +3293,15 @@ "dev": true }, "node_modules/@types/prop-types": { - "version": "15.7.8", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.8.tgz", - "integrity": "sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ==", + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", "peer": true }, "node_modules/@types/react": { - "version": "18.2.24", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.24.tgz", - "integrity": "sha512-Ee0Jt4sbJxMu1iDcetZEIKQr99J1Zfb6D4F3qfUWoR1JpInkY1Wdg4WwCyBjL257D0+jGqSl1twBjV8iCaC0Aw==", + "version": "18.2.56", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.56.tgz", + "integrity": "sha512-NpwHDMkS/EFZF2dONFQHgkPRwhvgq/OAvIaGQzxGSBmaeR++kTg6njr15Vatz0/2VcCEwJQFi6Jf4Q0qBu0rLA==", "peer": true, "dependencies": { "@types/prop-types": "*", @@ -3310,9 +3310,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.8", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.8.tgz", - "integrity": "sha512-bAIvO5lN/U8sPGvs1Xm61rlRHHaq5rp5N3kp9C+NJ/Q41P8iqjkXSu0+/qu8POsjH9pNWb0OYabFez7taP7omw==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.19.tgz", + "integrity": "sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==", "peer": true, "dependencies": { "@types/react": "*" @@ -3328,9 +3328,9 @@ } }, "node_modules/@types/scheduler": { - "version": "0.16.4", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.4.tgz", - "integrity": "sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", "peer": true }, "node_modules/@types/semver": { @@ -4225,9 +4225,9 @@ } }, "node_modules/babylonjs-gltf2interface": { - "version": "5.57.1", - "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-5.57.1.tgz", - "integrity": "sha512-RZnaKfJ6Q/AYLdIjBYMRxCW/HPEC8jabAL1U8wJ0KVziw6NSbSV6S80S22fUCPTyaZ7nCekn1TYg1IPmJ/eA6w==", + "version": "6.43.0", + "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-6.43.0.tgz", + "integrity": "sha512-+orCb4giE41Ysl8d2ZOeYCRBsT6FnmDwkW9+sHcEACUEpdtFeTShZ+ckucDAB/Ia2e3ah9F2hADbGA/8TfFngg==", "peer": true }, "node_modules/balanced-match": { @@ -6118,9 +6118,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -9593,9 +9593,9 @@ } }, "node_modules/vite": { - "version": "4.4.12", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.12.tgz", - "integrity": "sha512-KtPlUbWfxzGVul8Nut8Gw2Qe8sBzWY+8QVc5SL8iRFnpnrcoCaNlzO40c1R6hPmcdTwIPEDkq0Y9+27a5tVbdQ==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz", + "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==", "dev": true, "dependencies": { "esbuild": "^0.18.10", diff --git a/package.json b/package.json index f88d311a1..74cbd5c73 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mainsail", - "version": "2.9.1", + "version": "2.10.0", "private": true, "decription": "a klipper web interface", "author": { @@ -33,7 +33,7 @@ "@codemirror/view": "^6.0.3", "@jaames/iro": "^5.5.2", "@lezer/highlight": "^1.0.0", - "@sindarius/gcodeviewer": "^3.2.2", + "@sindarius/gcodeviewer": "^3.7.8", "@uiw/codemirror-theme-vscode": "^4.19.11", "axios": "^1.6.0", "codemirror": "^6.0.1", @@ -94,7 +94,7 @@ "start-server-and-test": "^2.0.0", "typescript": "^4.5.5", "unplugin-vue-components": "^0.22.12", - "vite": "^4.4.12", + "vite": "^4.5.2", "vite-plugin-checker": "^0.6.0", "vite-plugin-package-version": "^1.0.2", "vite-plugin-pwa": "^0.16.4", diff --git a/src/App.vue b/src/App.vue index 065416668..30d03d53e 100644 --- a/src/App.vue +++ b/src/App.vue @@ -4,7 +4,7 @@ - + @@ -44,6 +44,7 @@ import TheBedScrewsDialog from '@/components/dialogs/TheBedScrewsDialog.vue' import TheScrewsTiltAdjustDialog from '@/components/dialogs/TheScrewsTiltAdjustDialog.vue' import { setAndLoadLocale } from './plugins/i18n' import TheMacroPrompt from '@/components/dialogs/TheMacroPrompt.vue' +import { AppRoute } from '@/routes' Component.registerHooks(['metaInfo']) @@ -172,6 +173,20 @@ export default class App extends Mixins(BaseMixin, ThemeMixin) { return Math.floor(this.$store.getters['printer/getPrintPercent'] * 100) } + get containerClasses() { + const currentRouteOptions = this.$router.options.routes?.find( + (route) => route.name === this.$route.name + ) as AppRoute + + return { + 'px-3': true, + 'px-sm-6': true, + 'py-sm-6': true, + 'mx-auto': true, + fullscreen: currentRouteOptions?.fullscreen ?? false, + } + } + @Watch('language') async languageChanged(newVal: string): Promise { await setAndLoadLocale(newVal) diff --git a/src/assets/styles/page.css b/src/assets/styles/page.css index fc3438e48..1a9084aa8 100644 --- a/src/assets/styles/page.css +++ b/src/assets/styles/page.css @@ -11,6 +11,10 @@ body { max-width: 1800px; } +#page-container.fullscreen { + max-width: none; +} + .overflowingContentWidgets { visibility: hidden; } diff --git a/src/components/CommandHelpModal.vue b/src/components/CommandHelpModal.vue index 97298feae..b7712c16f 100644 --- a/src/components/CommandHelpModal.vue +++ b/src/components/CommandHelpModal.vue @@ -54,7 +54,7 @@ two-line> @@ -135,14 +134,17 @@ import { mdiHelp, mdiHelpCircle, mdiRestart, + mdiUsb, } from '@mdi/js' import type Codemirror from '@/components/inputs/Codemirror.vue' +import DevicesDialog from '@/components/dialogs/DevicesDialog.vue' @Component({ - components: { Panel, CodemirrorAsync }, + components: { DevicesDialog, Panel, CodemirrorAsync }, }) export default class TheEditor extends Mixins(BaseMixin) { - private dialogConfirmChange = false + dialogConfirmChange = false + dialogDevices = false formatFilesize = formatFilesize @@ -157,8 +159,7 @@ export default class TheEditor extends Mixins(BaseMixin) { mdiHelpCircle = mdiHelpCircle mdiFileDocumentEditOutline = mdiFileDocumentEditOutline mdiFileDocumentOutline = mdiFileDocumentOutline - - private scrollbarOptions = { scrollbars: { autoHide: 'never' } } + mdiUsb = mdiUsb declare $refs: { editor: Codemirror @@ -334,10 +335,14 @@ export default class TheEditor extends Mixins(BaseMixin) { @Watch('changed') changedChanged(newVal: boolean) { - if (this.confirmUnsavedChanges) { - if (newVal) window.addEventListener('beforeunload', windowBeforeUnloadFunction) - else window.removeEventListener('beforeunload', windowBeforeUnloadFunction) + if (!this.confirmUnsavedChanges) return + + if (newVal) { + window.addEventListener('beforeunload', windowBeforeUnloadFunction) + return } + + window.removeEventListener('beforeunload', windowBeforeUnloadFunction) } } diff --git a/src/components/TheTopbar.vue b/src/components/TheTopbar.vue index ee9225313..c74ab0980 100644 --- a/src/components/TheTopbar.vue +++ b/src/components/TheTopbar.vue @@ -110,7 +110,7 @@ import { Mixins } from 'vue-property-decorator' import BaseMixin from '@/components/mixins/base' import { validGcodeExtensions } from '@/store/variables' import Component from 'vue-class-component' -import axios from 'axios' +import axios, { AxiosProgressEvent } from 'axios' import { formatFilesize } from '@/plugins/helpers' import TheTopCornerMenu from '@/components/TheTopCornerMenu.vue' import TheSettingsMenu from '@/components/TheSettingsMenu.vue' @@ -128,10 +128,6 @@ type uploadSnackbar = { speed: number total: number cancelTokenSource: any - lastProgress: { - time: number - loaded: number - } } @Component({ @@ -162,10 +158,6 @@ export default class TheTopbar extends Mixins(BaseMixin) { speed: 0, total: 0, cancelTokenSource: null, - lastProgress: { - time: 0, - loaded: 0, - }, } formatFilesize = formatFilesize @@ -317,8 +309,6 @@ export default class TheTopbar extends Mixins(BaseMixin) { this.uploadSnackbar.status = true this.uploadSnackbar.percent = 0 this.uploadSnackbar.speed = 0 - this.uploadSnackbar.lastProgress.loaded = 0 - this.uploadSnackbar.lastProgress.time = 0 formData.append('file', file, filename) formData.append('print', 'true') @@ -329,18 +319,10 @@ export default class TheTopbar extends Mixins(BaseMixin) { .post(this.apiUrl + '/server/files/upload', formData, { cancelToken: this.uploadSnackbar.cancelTokenSource.token, headers: { 'Content-Type': 'multipart/form-data' }, - onUploadProgress: (progressEvent: ProgressEvent) => { - this.uploadSnackbar.percent = (progressEvent.loaded * 100) / progressEvent.total - if (this.uploadSnackbar.lastProgress.time) { - const time = progressEvent.timeStamp - this.uploadSnackbar.lastProgress.time - const data = progressEvent.loaded - this.uploadSnackbar.lastProgress.loaded - - if (time) this.uploadSnackbar.speed = data / (time / 1000) - } - - this.uploadSnackbar.lastProgress.time = progressEvent.timeStamp - this.uploadSnackbar.lastProgress.loaded = progressEvent.loaded - this.uploadSnackbar.total = progressEvent.total + onUploadProgress: (progressEvent: AxiosProgressEvent) => { + this.uploadSnackbar.percent = (progressEvent.progress ?? 0) * 100 + this.uploadSnackbar.speed = progressEvent.rate ?? 0 + this.uploadSnackbar.total = progressEvent.total ?? 0 }, }) .then((result) => { diff --git a/src/components/charts/HeightmapChart.vue b/src/components/charts/HeightmapChart.vue new file mode 100644 index 000000000..0d4e81907 --- /dev/null +++ b/src/components/charts/HeightmapChart.vue @@ -0,0 +1,411 @@ + + diff --git a/src/components/console/ConsoleTableEntry.vue b/src/components/console/ConsoleTableEntry.vue index 871ac909d..9e0cd7897 100644 --- a/src/components/console/ConsoleTableEntry.vue +++ b/src/components/console/ConsoleTableEntry.vue @@ -1,7 +1,7 @@ @@ -17,7 +17,7 @@ export default class ConsoleTableEntry extends Mixins(BaseMixin) { declare readonly event: ServerStateEvent get entryStyle() { - const classes = ['ma-0'] + const classes = ['ma-0', 'flex-nowrap'] classes.push(this.$store.state.gui.console.entryStyle ?? 'default') if (this.event.type === 'action') classes.push('text--disabled') diff --git a/src/components/dialogs/AddBatchToQueueDialog.vue b/src/components/dialogs/AddBatchToQueueDialog.vue new file mode 100644 index 000000000..f98c958c9 --- /dev/null +++ b/src/components/dialogs/AddBatchToQueueDialog.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/src/components/dialogs/CoolDownDialog.vue b/src/components/dialogs/CoolDownDialog.vue new file mode 100644 index 000000000..2d9dfa4ef --- /dev/null +++ b/src/components/dialogs/CoolDownDialog.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/src/components/dialogs/DevicesDialog.vue b/src/components/dialogs/DevicesDialog.vue new file mode 100644 index 000000000..10555aaeb --- /dev/null +++ b/src/components/dialogs/DevicesDialog.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/src/components/dialogs/DevicesDialogCan.vue b/src/components/dialogs/DevicesDialogCan.vue new file mode 100644 index 000000000..39a6dc1c4 --- /dev/null +++ b/src/components/dialogs/DevicesDialogCan.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/src/components/dialogs/DevicesDialogCanDevice.vue b/src/components/dialogs/DevicesDialogCanDevice.vue new file mode 100644 index 000000000..163362ce3 --- /dev/null +++ b/src/components/dialogs/DevicesDialogCanDevice.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/components/dialogs/DevicesDialogSerial.vue b/src/components/dialogs/DevicesDialogSerial.vue new file mode 100644 index 000000000..0a0104c86 --- /dev/null +++ b/src/components/dialogs/DevicesDialogSerial.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/src/components/dialogs/DevicesDialogSerialDevice.vue b/src/components/dialogs/DevicesDialogSerialDevice.vue new file mode 100644 index 000000000..b3293ee33 --- /dev/null +++ b/src/components/dialogs/DevicesDialogSerialDevice.vue @@ -0,0 +1,45 @@ + + + diff --git a/src/components/dialogs/DevicesDialogUsb.vue b/src/components/dialogs/DevicesDialogUsb.vue new file mode 100644 index 000000000..229b0e9ff --- /dev/null +++ b/src/components/dialogs/DevicesDialogUsb.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/src/components/dialogs/DevicesDialogUsbDevice.vue b/src/components/dialogs/DevicesDialogUsbDevice.vue new file mode 100644 index 000000000..58458994c --- /dev/null +++ b/src/components/dialogs/DevicesDialogUsbDevice.vue @@ -0,0 +1,55 @@ + + + diff --git a/src/components/dialogs/DevicesDialogVideo.vue b/src/components/dialogs/DevicesDialogVideo.vue new file mode 100644 index 000000000..f6ef97c9e --- /dev/null +++ b/src/components/dialogs/DevicesDialogVideo.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/src/components/dialogs/DevicesDialogVideoDeviceLibcamera.vue b/src/components/dialogs/DevicesDialogVideoDeviceLibcamera.vue new file mode 100644 index 000000000..22091d0dc --- /dev/null +++ b/src/components/dialogs/DevicesDialogVideoDeviceLibcamera.vue @@ -0,0 +1,61 @@ + + + diff --git a/src/components/dialogs/DevicesDialogVideoDeviceV4l2.vue b/src/components/dialogs/DevicesDialogVideoDeviceV4l2.vue new file mode 100644 index 000000000..7909db8fa --- /dev/null +++ b/src/components/dialogs/DevicesDialogVideoDeviceV4l2.vue @@ -0,0 +1,84 @@ + + + diff --git a/src/components/dialogs/HeightmapCalibrateMeshDialog.vue b/src/components/dialogs/HeightmapCalibrateMeshDialog.vue new file mode 100644 index 000000000..94d2faaf7 --- /dev/null +++ b/src/components/dialogs/HeightmapCalibrateMeshDialog.vue @@ -0,0 +1,88 @@ + + diff --git a/src/components/dialogs/HeightmapRemoveProfileDialog.vue b/src/components/dialogs/HeightmapRemoveProfileDialog.vue new file mode 100644 index 000000000..7b0c393a9 --- /dev/null +++ b/src/components/dialogs/HeightmapRemoveProfileDialog.vue @@ -0,0 +1,50 @@ + + diff --git a/src/components/dialogs/HeightmapRenameProfileDialog.vue b/src/components/dialogs/HeightmapRenameProfileDialog.vue new file mode 100644 index 000000000..969a4b15a --- /dev/null +++ b/src/components/dialogs/HeightmapRenameProfileDialog.vue @@ -0,0 +1,95 @@ + + diff --git a/src/components/dialogs/JobqueueEntryChangeCountDialog.vue b/src/components/dialogs/JobqueueEntryChangeCountDialog.vue new file mode 100644 index 000000000..0cd1d88c6 --- /dev/null +++ b/src/components/dialogs/JobqueueEntryChangeCountDialog.vue @@ -0,0 +1,88 @@ + + diff --git a/src/components/dialogs/SpoolmanChangeSpoolDialog.vue b/src/components/dialogs/SpoolmanChangeSpoolDialog.vue index 14206b70a..b8e12a27d 100644 --- a/src/components/dialogs/SpoolmanChangeSpoolDialog.vue +++ b/src/components/dialogs/SpoolmanChangeSpoolDialog.vue @@ -156,6 +156,11 @@ export default class SpoolmanChangeSpoolDialog extends Mixins(BaseMixin) { } customFilter(value: any, search: string, item: ServerSpoolmanStateSpool): boolean { + if (search.trim().startsWith('web+spoolman:s-')) { + const spoolId = parseInt(search.split('-')[1] ?? -1) + return item.id === spoolId + } + const querySplits = search.toLowerCase().split(' ') const searchArray = [ item.comment, diff --git a/src/components/dialogs/SpoolmanChangeSpoolDialogRow.vue b/src/components/dialogs/SpoolmanChangeSpoolDialogRow.vue index a5d0ba8fd..2ef2741fa 100644 --- a/src/components/dialogs/SpoolmanChangeSpoolDialogRow.vue +++ b/src/components/dialogs/SpoolmanChangeSpoolDialogRow.vue @@ -16,7 +16,7 @@ @@ -131,4 +131,9 @@ export default class SpoolmanChangeSpoolDialogRow extends Mixins(BaseMixin) { .no--padding { padding: 0; } + +.comment { + white-space: pre-wrap; + overflow-wrap: anywhere; +} diff --git a/src/components/gcodeviewer/Viewer.vue b/src/components/gcodeviewer/Viewer.vue index 778415ec5..6e195f752 100644 --- a/src/components/gcodeviewer/Viewer.vue +++ b/src/components/gcodeviewer/Viewer.vue @@ -277,7 +277,7 @@ import { Component, Mixins, Prop, Ref, Watch } from 'vue-property-decorator' import BaseMixin from '../mixins/base' import GCodeViewer from '@sindarius/gcodeviewer' -import axios from 'axios' +import axios, { AxiosProgressEvent } from 'axios' import { formatFilesize } from '@/plugins/helpers' import Panel from '@/components/ui/Panel.vue' import CodeStream from '@/components/gcodeviewer/CodeStream.vue' @@ -304,10 +304,6 @@ interface downloadSnackbar { speed: number total: number cancelTokenSource: any - lastProgress: { - time: number - loaded: number - } } let viewer: any = null @@ -357,10 +353,6 @@ export default class Viewer extends Mixins(BaseMixin) { speed: 0, total: 0, cancelTokenSource: {}, - lastProgress: { - time: 0, - loaded: 0, - }, } private excludeObject = { @@ -634,7 +626,6 @@ export default class Viewer extends Mixins(BaseMixin) { async loadFile(filename: string) { this.downloadSnackbar.status = true this.downloadSnackbar.speed = 0 - this.downloadSnackbar.lastProgress.time = 0 this.downloadSnackbar.filename = filename.startsWith('gcodes/') ? filename.slice(7) : filename const CancelToken = axios.CancelToken this.downloadSnackbar.cancelTokenSource = CancelToken.source() @@ -642,20 +633,10 @@ export default class Viewer extends Mixins(BaseMixin) { .get(this.apiUrl + '/server/files/' + encodeURI(filename), { cancelToken: this.downloadSnackbar.cancelTokenSource.token, responseType: 'blob', - onDownloadProgress: (progressEvent) => { - this.downloadSnackbar.percent = (progressEvent.loaded * 100) / progressEvent.total - if (this.downloadSnackbar.lastProgress.time) { - const time = progressEvent.timeStamp - this.downloadSnackbar.lastProgress.time - const data = progressEvent.loaded - this.downloadSnackbar.lastProgress.loaded - - if (time > 1000 || this.downloadSnackbar.speed === 0) { - this.downloadSnackbar.speed = data / (time / 1000) - this.downloadSnackbar.lastProgress.time = progressEvent.timeStamp - this.downloadSnackbar.lastProgress.loaded = progressEvent.loaded - } - } else this.downloadSnackbar.lastProgress.time = progressEvent.timeStamp - - this.downloadSnackbar.total = progressEvent.total + onDownloadProgress: (progressEvent: AxiosProgressEvent) => { + this.downloadSnackbar.percent = (progressEvent.progress ?? 0) * 100 + this.downloadSnackbar.speed = progressEvent.rate ?? 0 + this.downloadSnackbar.total = progressEvent.total ?? 0 }, }) .then((res) => res.data.text()) diff --git a/src/components/inputs/MiscellaneousSlider.vue b/src/components/inputs/MiscellaneousSlider.vue index 43536deb7..6c7e3d70a 100644 --- a/src/components/inputs/MiscellaneousSlider.vue +++ b/src/components/inputs/MiscellaneousSlider.vue @@ -14,9 +14,9 @@ {{ mdiLightbulbOutline }} - {{ mdiFan }} + {{ mdiFan }} {{ convertName(name) }} - + {{ Math.round(rpm ?? 0) }} RPM {{ Math.round(parseFloat(value) * 100) }} % @@ -199,12 +199,11 @@ export default class MiscellaneousSlider extends Mixins(BaseMixin) { sendCmd(newVal: number): void { if (this.value === newVal) return - let gcode = '' + let gcode = `SET_PIN PIN=${this.name} VALUE=${newVal.toFixed(2)}` if (newVal < this.min) newVal = 0 newVal = newVal * this.multi if (this.type === 'fan') gcode = `M106 S${newVal.toFixed(0)}` if (this.type === 'fan_generic') gcode = `SET_FAN_SPEED FAN=${this.name} SPEED=${newVal}` - if (this.type === 'output_pin') gcode = `SET_PIN PIN=${this.name} VALUE=${newVal.toFixed(2)}` if (this.type === 'led') gcode = `SET_LED LED=${this.name} ${this.ledChannelName}=${newVal.toFixed(2)} SYNC=0 TRANSMIT=1` diff --git a/src/components/inputs/TextfieldWithCopy.vue b/src/components/inputs/TextfieldWithCopy.vue new file mode 100644 index 000000000..81b3fe969 --- /dev/null +++ b/src/components/inputs/TextfieldWithCopy.vue @@ -0,0 +1,52 @@ + + + diff --git a/src/components/mixins/bedmesh.ts b/src/components/mixins/bedmesh.ts new file mode 100644 index 000000000..36a4cbf12 --- /dev/null +++ b/src/components/mixins/bedmesh.ts @@ -0,0 +1,62 @@ +import Vue from 'vue' +import Component from 'vue-class-component' + +@Component +export default class BedmeshMixin extends Vue { + get bed_mesh() { + return this.$store.state.printer.bed_mesh ?? {} + } + + get profiles() { + return this.bed_mesh.profiles ?? {} + } + + get mesh_min() { + return this.bed_mesh.mesh_min ?? [0, 0] + } + + get mesh_max() { + return this.bed_mesh.mesh_max ?? [0, 0] + } + + get min() { + return Math.min(...this.points) + } + + get max() { + return Math.max(...this.points) + } + + get variance() { + return Math.abs(this.min - this.max).toFixed(3) + } + + get is_active() { + // if the current profile_mane is not empty, return true + if (this.bed_mesh.profile_name !== '') return true + + return this.mesh_min[0] !== 0 || this.mesh_min[1] !== 0 || this.mesh_max[0] !== 0 || this.mesh_max[1] !== 0 + } + + get name() { + if (this.bed_mesh.profile_name !== '') return this.bed_mesh.profile_name + + return 'Unknown' + } + + get probed_matrix() { + return this.bed_mesh.probed_matrix ?? [] + } + + get points() { + const points: number[] = [] + + for (let i = 0; i < this.probed_matrix.length; i++) { + for (let j = 0; j < this.probed_matrix[i].length; j++) { + points.push(this.probed_matrix[i][j]) + } + } + + return points + } +} diff --git a/src/components/mixins/zoffset.ts b/src/components/mixins/zoffset.ts index c4f8b63de..9785f34ca 100644 --- a/src/components/mixins/zoffset.ts +++ b/src/components/mixins/zoffset.ts @@ -32,14 +32,15 @@ export default class ZoffsetMixin extends Vue { get endstop_pin() { const stepperConfig = this.settings[this.stepper_name] ?? {} - return stepperConfig?.endstop_pin + return stepperConfig?.endstop_pin.trim() } get zOffset(): number { return this.$store.state.printer?.gcode_move?.homing_origin[2].toFixed(3) } get isEndstopProbe() { - return this.endstop_pin.search('probe:z_virtual_endstop') !== -1 + // remove spaces and search for probe:z_virtual_endstop + return this.endstop_pin.replaceAll(' ', '').search('probe:z_virtual_endstop') !== -1 } get existZOffsetApplyProbe() { diff --git a/src/components/panels/.i18nignore b/src/components/panels/.i18nignore index 83fc597c4..c0c5e50b6 100644 --- a/src/components/panels/.i18nignore +++ b/src/components/panels/.i18nignore @@ -2,6 +2,7 @@ $t('History.StatusValues.cancelled') $t('History.StatusValues.completed') $t('History.StatusValues.error') $t('History.StatusValues.in_progress') +$t('History.StatusValues.interrupted') $t('History.StatusValues.klippy_disconnect') $t('History.StatusValues.klippy_shutdown') $t('History.StatusValues.server_exit') diff --git a/src/components/panels/ExtruderControlPanel.vue b/src/components/panels/ExtruderControlPanel.vue index c2e3349aa..ddd27375f 100644 --- a/src/components/panels/ExtruderControlPanel.vue +++ b/src/components/panels/ExtruderControlPanel.vue @@ -180,7 +180,12 @@ export default class ExtruderControlPanel extends Mixins(BaseMixin, ControlMixin } get showFilamentMacros(): boolean { - return this.loadFilamentMacro !== undefined || this.unloadFilamentMacro !== undefined + return ( + this.loadFilamentMacro !== undefined || + this.unloadFilamentMacro !== undefined || + this.purgeFilamentMacro !== undefined || + this.cleanNozzleMacro !== undefined + ) } get showTools(): boolean { diff --git a/src/components/panels/GcodefilesPanel.vue b/src/components/panels/GcodefilesPanel.vue index bd903e684..712ba6136 100644 --- a/src/components/panels/GcodefilesPanel.vue +++ b/src/components/panels/GcodefilesPanel.vue @@ -568,53 +568,10 @@ - - - - - - - - - - - - {{ $t('Files.Cancel') }} - {{ $t('Files.AddToQueue') }} - - - + @@ -628,8 +585,6 @@ import Panel from '@/components/ui/Panel.vue' import SettingsRow from '@/components/settings/SettingsRow.vue' import draggable from 'vuedraggable' import { - mdiChevronDown, - mdiChevronUp, mdiDragVertical, mdiCheckboxBlankOutline, mdiCheckboxMarked, @@ -654,6 +609,7 @@ import { mdiContentCopy, } from '@mdi/js' import StartPrintDialog from '@/components/dialogs/StartPrintDialog.vue' +import AddBatchToQueueDialog from '@/components/dialogs/AddBatchToQueueDialog.vue' import ControlMixin from '@/components/mixins/control' import PathNavigation from '@/components/ui/PathNavigation.vue' @@ -675,12 +631,6 @@ interface dialogPrintFile { item: FileStateGcodefile } -interface dialogAddBatchToQueue { - show: boolean - count: number - item: FileStateGcodefile -} - interface dialogRenameObject { show: boolean newName: string @@ -698,11 +648,9 @@ interface tableColumnSetting { } @Component({ - components: { StartPrintDialog, Panel, SettingsRow, PathNavigation, draggable }, + components: { StartPrintDialog, AddBatchToQueueDialog, Panel, SettingsRow, PathNavigation, draggable }, }) export default class GcodefilesPanel extends Mixins(BaseMixin, ControlMixin) { - mdiChevronDown = mdiChevronDown - mdiChevronUp = mdiChevronUp mdiContentCopy = mdiContentCopy mdiFile = mdiFile mdiFileDocumentMultipleOutline = mdiFileDocumentMultipleOutline @@ -779,10 +727,9 @@ export default class GcodefilesPanel extends Mixins(BaseMixin, ControlMixin) { item: { ...this.contextMenu.item }, } - private dialogAddBatchToQueue: dialogAddBatchToQueue = { - show: false, - count: 1, - item: { ...this.contextMenu.item }, + dialogAddBatchToQueue: { isVisible: boolean; filename: string } = { + isVisible: false, + filename: '', } private dialogRenameFile: dialogRenameObject = { @@ -1235,11 +1182,12 @@ export default class GcodefilesPanel extends Mixins(BaseMixin, ControlMixin) { refreshMetadata(data: FileStateGcodefile[]) { const items = data.filter((file) => !file.isDirectory && !file.metadataRequested && !file.metadataPulled) - items.forEach((file: FileStateGcodefile) => { - this.$store.dispatch('files/requestMetadata', { + this.$store.dispatch( + 'files/requestMetadata', + items.map((file: FileStateGcodefile) => ({ filename: 'gcodes' + this.currentPath + '/' + file.filename, - }) - }) + })) + ) } clickRow(item: FileStateGcodefile, force = false) { @@ -1271,23 +1219,15 @@ export default class GcodefilesPanel extends Mixins(BaseMixin, ControlMixin) { } openAddBatchToQueueDialog(item: FileStateGcodefile) { - this.dialogAddBatchToQueue.show = true - this.dialogAddBatchToQueue.count = 1 - this.dialogAddBatchToQueue.item = item - } - - async addBatchToQueueAction() { - let filename = [this.currentPath, this.dialogAddBatchToQueue.item.filename].join('/') + let filename = [this.currentPath, item.filename].join('/') if (filename.startsWith('/')) filename = filename.slice(1) - const array: string[] = [] - for (let i = 0; i < this.dialogAddBatchToQueue.count; i++) { - array.push(filename) - } - - await this.$store.dispatch('server/jobQueue/addToQueue', array) + this.dialogAddBatchToQueue.isVisible = true + this.dialogAddBatchToQueue.filename = filename + } - this.dialogAddBatchToQueue.show = false + closeAddBatchToQueueDialog() { + this.dialogAddBatchToQueue.isVisible = false } changeMetadataVisible(name: string, value: boolean) { @@ -1548,15 +1488,6 @@ export default class GcodefilesPanel extends Mixins(BaseMixin, ControlMixin) { } - - diff --git a/src/components/panels/Heightmap/HeightmapProfilesPanel.vue b/src/components/panels/Heightmap/HeightmapProfilesPanel.vue new file mode 100644 index 000000000..46e51cbb3 --- /dev/null +++ b/src/components/panels/Heightmap/HeightmapProfilesPanel.vue @@ -0,0 +1,35 @@ + + diff --git a/src/components/panels/Heightmap/HeightmapProfilesPanelRow.vue b/src/components/panels/Heightmap/HeightmapProfilesPanelRow.vue new file mode 100644 index 000000000..72aa67260 --- /dev/null +++ b/src/components/panels/Heightmap/HeightmapProfilesPanelRow.vue @@ -0,0 +1,148 @@ + + + + diff --git a/src/components/panels/HistoryListPanel.vue b/src/components/panels/HistoryListPanel.vue index 3ef8af0b9..1c0e6e52c 100644 --- a/src/components/panels/HistoryListPanel.vue +++ b/src/components/panels/HistoryListPanel.vue @@ -223,6 +223,18 @@ {{ mdiPrinter }} {{ $t('History.Reprint') }} + + {{ mdiPlaylistPlus }} + {{ $t('Files.AddToQueue') }} + + + {{ mdiPlaylistPlus }} + {{ $t('Files.AddBatchToQueue') }} + {{ mdiDelete }} {{ $t('History.Delete') }} @@ -493,6 +505,11 @@ + @@ -509,6 +526,7 @@ import { mdiDatabaseArrowDownOutline, mdiCog, mdiPrinter, + mdiPlaylistPlus, mdiTextBoxSearch, mdiFile, mdiFileDocumentMultipleOutline, @@ -520,8 +538,10 @@ import { mdiNotebook, mdiFileCancel, } from '@mdi/js' +import AddBatchToQueueDialog from '@/components/dialogs/AddBatchToQueueDialog.vue' + @Component({ - components: { Panel }, + components: { Panel, AddBatchToQueueDialog }, }) export default class HistoryListPanel extends Mixins(BaseMixin) { mdiDatabaseExportOutline = mdiDatabaseExportOutline @@ -529,6 +549,7 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { mdiDatabaseArrowDownOutline = mdiDatabaseArrowDownOutline mdiCog = mdiCog mdiPrinter = mdiPrinter + mdiPlaylistPlus = mdiPlaylistPlus mdiFileDocumentMultipleOutline = mdiFileDocumentMultipleOutline mdiTextBoxSearch = mdiTextBoxSearch mdiFile = mdiFile @@ -559,6 +580,11 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { boolShow: false, } + dialogAddBatchToQueue: { isVisible: boolean; filename: string } = { + isVisible: false, + filename: '', + } + private noteDialog: { item: ServerHistoryStateJob | null note: string @@ -785,6 +811,10 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { return this.$store.state.gui.general?.language ?? 'en' } + get isJobQueueAvailable() { + return this.moonrakerComponents.includes('job_queue') + } + refreshHistory() { this.$store.dispatch('socket/addLoading', { name: 'historyLoadAll' }) @@ -941,6 +971,21 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { this.$socket.emit('printer.print.start', { filename: item.filename }, { action: 'switchToDashboard' }) } + async addToQueue(item: ServerHistoryStateJob) { + await this.$store.dispatch('server/jobQueue/addToQueue', [item.filename]) + + this.$toast.info(this.$t('History.AddToQueueSuccessful', { filename: item.filename }).toString()) + } + + openAddBatchToQueueDialog(item: ServerHistoryStateJob) { + this.dialogAddBatchToQueue.isVisible = true + this.dialogAddBatchToQueue.filename = item.filename + } + + closeAddBatchToQueueDialog() { + this.dialogAddBatchToQueue.isVisible = false + } + deleteJob() { this.$socket.emit( 'server.history.delete_job', diff --git a/src/components/panels/JobqueuePanel.vue b/src/components/panels/JobqueuePanel.vue index b7391e121..22d54e144 100644 --- a/src/components/panels/JobqueuePanel.vue +++ b/src/components/panels/JobqueuePanel.vue @@ -1,63 +1,54 @@ - diff --git a/src/components/panels/Machine/ConfigFilesPanel.vue b/src/components/panels/Machine/ConfigFilesPanel.vue index 23e2c8085..a02bd2649 100644 --- a/src/components/panels/Machine/ConfigFilesPanel.vue +++ b/src/components/panels/Machine/ConfigFilesPanel.vue @@ -260,7 +260,7 @@ image
@@ -601,10 +601,6 @@ interface uploadSnackbar { number: number max: number cancelTokenSource: any - lastProgress: { - time: number - loaded: number - } } interface draggingFile { @@ -719,10 +715,6 @@ export default class ConfigFilesPanel extends Mixins(BaseMixin, ThemeMixin) { number: 0, max: 0, cancelTokenSource: {}, - lastProgress: { - time: 0, - loaded: 0, - }, } private draggingFile: draggingFile = { item: { @@ -850,8 +842,15 @@ export default class ConfigFilesPanel extends Mixins(BaseMixin, ThemeMixin) { } if (this.hideBackupFiles) { - const backupFileMatcher = /.*\/?printer-\d{8}_\d{6}\.cfg$/ - files = files.filter((file) => !file.filename.match(backupFileMatcher)) + const klipperBackupFileMatcher = /^printer-\d{8}_\d{6}\.cfg$/ + const crowsnestBackupFileMatcher = /^crowsnest\.conf\.\d{4}-\d{2}-\d{2}-\d{4}$/ + + files = files.filter( + (file) => + !file.filename.match(klipperBackupFileMatcher) && + !file.filename.match(crowsnestBackupFileMatcher) && + !file.filename.endsWith('.bkp') + ) } return files diff --git a/src/components/panels/Machine/UpdatePanel/GitCommitsList.vue b/src/components/panels/Machine/UpdatePanel/GitCommitsList.vue index 688ff0e4d..cc11e51d5 100644 --- a/src/components/panels/Machine/UpdatePanel/GitCommitsList.vue +++ b/src/components/panels/Machine/UpdatePanel/GitCommitsList.vue @@ -1,5 +1,5 @@ - - - - - - - - - -

{{ $t('Machine.UpdatePanel.MoreCommitsInfo') }}

-
- - {{ $t('Machine.UpdatePanel.LinkToGithub') }} - -
-
-
-
-
-
-
-
+ + + + + + + +

{{ $t('Machine.UpdatePanel.MoreCommitsInfo') }}

+
+ + {{ $t('Machine.UpdatePanel.LinkToGithub') }} + +
+
+
+
+
+
@@ -110,6 +106,24 @@ export default class GitCommitsList extends Mixins(BaseMixin) { return `https://github.com/${this.repo?.owner}/${this.repo?.name}/commits/${this.repo?.branch}/?after=${this.lastCommit?.sha}+0` } + get overlayScrollbarsStyle() { + if (this.isMobile) { + return { + height: 'calc(100vh - 48px)', + } + } + + return { + height: '400px', + } + } + + get timelineClassName() { + if (this.isMobile) return ['groupedCommits', 'mobile'] + + return ['groupedCommits'] + } + closeDialog() { this.$emit('close-dialog') } @@ -161,4 +175,18 @@ export default class GitCommitsList extends Mixins(BaseMixin) { } } } + +::v-deep .groupedCommits.mobile { + &:before { + left: 20px; + } + + .v-timeline-item__body { + max-width: calc(100% - 41px); + } + + .v-timeline-item__divider { + min-width: 41px; + } +} diff --git a/src/components/panels/Machine/UpdatePanel/GitCommitsListDayCommit.vue b/src/components/panels/Machine/UpdatePanel/GitCommitsListDayCommit.vue index 68854fbc9..44c2a3160 100644 --- a/src/components/panels/Machine/UpdatePanel/GitCommitsListDayCommit.vue +++ b/src/components/panels/Machine/UpdatePanel/GitCommitsListDayCommit.vue @@ -1,6 +1,6 @@ @@ -231,8 +188,6 @@ import ControlMixin from '@/components/mixins/control' import { FileStateGcodefile } from '@/store/files/types' import StartPrintDialog from '@/components/dialogs/StartPrintDialog.vue' import { - mdiChevronDown, - mdiChevronUp, mdiFile, mdiPlay, mdiPlaylistPlus, @@ -246,6 +201,7 @@ import { } from '@mdi/js' import Panel from '@/components/ui/Panel.vue' import { defaultBigThumbnailBackground } from '@/store/variables' +import AddBatchToQueueDialog from '@/components/dialogs/AddBatchToQueueDialog.vue' interface dialogRenameObject { show: boolean @@ -253,21 +209,14 @@ interface dialogRenameObject { item: FileStateGcodefile } -interface dialogAddBatchToQueue { - show: boolean - count: number - item: FileStateGcodefile -} - @Component({ components: { Panel, StartPrintDialog, + AddBatchToQueueDialog, }, }) export default class StatusPanelGcodefiles extends Mixins(BaseMixin, ControlMixin) { - mdiChevronDown = mdiChevronDown - mdiChevronUp = mdiChevronUp mdiFile = mdiFile mdiPlay = mdiPlay mdiPlaylistPlus = mdiPlaylistPlus @@ -320,10 +269,9 @@ export default class StatusPanelGcodefiles extends Mixins(BaseMixin, ControlMixi item: { ...this.dialogFile }, } - private dialogAddBatchToQueue: dialogAddBatchToQueue = { - show: false, - count: 1, - item: { ...this.contextMenu.item }, + dialogAddBatchToQueue: { isVisible: boolean; filename: string } = { + isVisible: false, + filename: '', } private countInputRules = [ @@ -343,12 +291,12 @@ export default class StatusPanelGcodefiles extends Mixins(BaseMixin, ControlMixi const requestItems = gcodes.filter( (file: FileStateGcodefile) => !file.metadataRequested && !file.metadataPulled ) - requestItems.forEach((file: FileStateGcodefile) => { - this.$store.dispatch('files/requestMetadata', { + this.$store.dispatch( + 'files/requestMetadata', + requestItems.map((file: FileStateGcodefile) => ({ filename: 'gcodes/' + file.filename, - }) - }) - + })) + ) return gcodes } @@ -470,23 +418,12 @@ export default class StatusPanelGcodefiles extends Mixins(BaseMixin, ControlMixi } openAddBatchToQueueDialog(item: FileStateGcodefile) { - this.dialogAddBatchToQueue.show = true - this.dialogAddBatchToQueue.count = 1 - this.dialogAddBatchToQueue.item = item + this.dialogAddBatchToQueue.isVisible = true + this.dialogAddBatchToQueue.filename = item.filename } - async addBatchToQueueAction() { - let filename = [this.currentPath, this.dialogAddBatchToQueue.item.filename].join('/') - if (filename.startsWith('/')) filename = filename.slice(1) - - const array: string[] = [] - for (let i = 0; i < this.dialogAddBatchToQueue.count; i++) { - array.push(filename) - } - - await this.$store.dispatch('server/jobQueue/addToQueue', array) - - this.dialogAddBatchToQueue.show = false + closeAddBatchToQueueDialog() { + this.dialogAddBatchToQueue.isVisible = false } view3D(item: FileStateGcodefile) { @@ -571,11 +508,4 @@ export default class StatusPanelGcodefiles extends Mixins(BaseMixin, ControlMixi .filesGcodeCard { position: relative; } - -._spin_button_group { - width: 24px; - margin-top: -6px; - margin-left: -6px; - margin-bottom: -6px; -} diff --git a/src/components/panels/Status/Jobqueue.vue b/src/components/panels/Status/Jobqueue.vue index 7de8ca37b..e9ee73c94 100644 --- a/src/components/panels/Status/Jobqueue.vue +++ b/src/components/panels/Status/Jobqueue.vue @@ -1,40 +1,21 @@ @@ -42,122 +23,41 @@ import Component from 'vue-class-component' import { Mixins } from 'vue-property-decorator' import BaseMixin from '@/components/mixins/base' -import { ServerJobQueueStateJob } from '@/store/server/jobQueue/types' -import { mdiFileMultiple } from '@mdi/js' import JobqueueEntry from '@/components/panels/Status/JobqueueEntry.vue' @Component({ components: { JobqueueEntry }, }) export default class StatusPanelJobqueue extends Mixins(BaseMixin) { - mdiFileMultiple = mdiFileMultiple - - private contentTdWidth = 100 - - declare $refs: { - filesJobqueue: any - } - get jobs() { return this.$store.getters['server/jobQueue/getJobs'] ?? [] } - get jobsTable() { - return this.jobs.slice(0, 5) - } + get maxLength() { + if (this.jobs.length > 5) return 4 - get jobsRest() { - return this.jobs.slice(5) + return 5 } - get restJobsLength() { - let count = 0 - - this.jobsRest.forEach((item: ServerJobQueueStateJob) => { - count += (item.combinedIds?.length ?? 0) + 1 - }) - - return count - } - - get descriptionRestJobs() { - let filamentLength = 0 - let filamentWeight = 0 - let printTime = 0 - - this.jobsRest.forEach((item: ServerJobQueueStateJob) => { - const count = (item.combinedIds?.length ?? 0) + 1 - - if (item.metadata?.filament_total) filamentLength += item.metadata?.filament_total * count - if (item.metadata?.filament_weight_total) filamentWeight += item.metadata?.filament_weight_total * count - if (item.metadata?.estimated_time) printTime += item.metadata.estimated_time * count - }) - - let output = '' - - output += this.$t('Files.Filament') + ': ' - if (filamentLength || filamentWeight) { - if (filamentLength) output += filamentLength.toFixed() + ' mm' - if (filamentLength && filamentWeight) output += ' / ' - if (filamentWeight) output += filamentWeight.toFixed(2) + ' g' - } else output += '--' - - output += ', ' + this.$t('Files.PrintTime') + ': ' - if (printTime) output += this.formatPrintTime(printTime) - else output += '--' - - return output + get jobsTable() { + return this.jobs.slice(0, this.maxLength) } - formatPrintTime(totalSeconds: number) { - if (totalSeconds) { - let output = '' - - const days = Math.floor(totalSeconds / (3600 * 24)) - if (days) { - totalSeconds %= 3600 * 24 - output += days + 'd' - } - - const hours = Math.floor(totalSeconds / 3600) - totalSeconds %= 3600 - if (hours) output += ' ' + hours + 'h' - - const minutes = Math.floor(totalSeconds / 60) - if (minutes) output += ' ' + minutes + 'm' - - const seconds = totalSeconds % 60 - if (seconds) output += ' ' + seconds.toFixed(0) + 's' - - return output - } - - return '--' + get jobsRest() { + return this.jobs.slice(this.maxLength) } startJobqueue() { this.$store.dispatch('server/jobQueue/start') } - - mounted() { - setTimeout(() => { - this.calcContentTdWidth() - }, 200) - } - - calcContentTdWidth() { - this.contentTdWidth = this.$refs.filesJobqueue?.$el?.clientWidth - 48 - 48 - 32 - } - - handleResize() { - this.$nextTick(() => { - this.calcContentTdWidth() - }) - } } diff --git a/src/components/panels/Status/JobqueueEntry.vue b/src/components/panels/Status/JobqueueEntry.vue index 6a1a6ea72..17b468988 100644 --- a/src/components/panels/Status/JobqueueEntry.vue +++ b/src/components/panels/Status/JobqueueEntry.vue @@ -1,116 +1,75 @@ - - diff --git a/src/components/panels/Status/JobqueueEntryRest.vue b/src/components/panels/Status/JobqueueEntryRest.vue new file mode 100644 index 000000000..cd6519a23 --- /dev/null +++ b/src/components/panels/Status/JobqueueEntryRest.vue @@ -0,0 +1,120 @@ + + + diff --git a/src/components/panels/Status/JobqueueEntrySum.vue b/src/components/panels/Status/JobqueueEntrySum.vue new file mode 100644 index 000000000..28fcd8526 --- /dev/null +++ b/src/components/panels/Status/JobqueueEntrySum.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/src/components/panels/StatusPanel.vue b/src/components/panels/StatusPanel.vue index 2c1efcab1..ec8612bec 100644 --- a/src/components/panels/StatusPanel.vue +++ b/src/components/panels/StatusPanel.vue @@ -59,8 +59,10 @@ - - {{ mdiAlertOutline }} + + + {{ mdiAlertOutline }} + {{ print_stats_message }} @@ -70,10 +72,10 @@ @@ -51,13 +52,19 @@ import Component from 'vue-class-component' import { Mixins } from 'vue-property-decorator' import BaseMixin from '@/components/mixins/base' import { GuiPresetsStatePreset } from '@/store/gui/presets/types' -import { mdiFire, mdiMenuDown, mdiSnowflake } from '@mdi/js' +import { mdiFire, mdiMenuDown, mdiSnowflake, mdiCloseThick } from '@mdi/js' +import CoolDownDialog from '@/components/dialogs/CoolDownDialog.vue' -@Component +@Component({ + components: { CoolDownDialog }, +}) export default class TemperaturePanelPresets extends Mixins(BaseMixin) { mdiFire = mdiFire mdiMenuDown = mdiMenuDown mdiSnowflake = mdiSnowflake + mdiCloseThick = mdiCloseThick + + showCoolDownDialog = false get presets(): GuiPresetsStatePreset[] { return this.$store.getters['gui/presets/getPresets'] ?? [] @@ -67,6 +74,10 @@ export default class TemperaturePanelPresets extends Mixins(BaseMixin) { return this.$store.getters['gui/presets/getCooldownGcode'] } + get confirmOnCoolDown(): boolean { + return this.$store.state.gui.uiSettings.confirmOnCoolDown + } + preheat(preset: GuiPresetsStatePreset): void { for (const [name, attributes] of Object.entries(preset.values)) { if (attributes.bool) { @@ -100,7 +111,17 @@ export default class TemperaturePanelPresets extends Mixins(BaseMixin) { } } + btnCoolDown(): void { + if (this.confirmOnCoolDown) { + this.showCoolDownDialog = true + return + } + + this.cooldown() + } + cooldown(): void { + this.showCoolDownDialog = false this.$store.dispatch('server/addEvent', { message: this.cooldownGcode, type: 'command' }) this.$socket.emit('printer.gcode.script', { script: this.cooldownGcode }) } diff --git a/src/components/settings/SettingsTimelapseTab.vue b/src/components/settings/SettingsTimelapseTab.vue index b01dccf3e..13cc39b70 100644 --- a/src/components/settings/SettingsTimelapseTab.vue +++ b/src/components/settings/SettingsTimelapseTab.vue @@ -98,6 +98,10 @@ hide-details="auto" outlined dense + :rules="[ + (v) => !!v || $t('Settings.TimelapseTab.RulesRequired'), + (v) => v >= 0 || $t('Settings.TimelapseTab.RulesZeroAndPositive'), + ]" :disabled="blockedsettings.includes('stream_delay_compensation')" /> @@ -148,6 +152,15 @@ hide-details="auto" outlined dense + :rules="[ + (v) => !!v || $t('Settings.TimelapseTab.RulesRequired'), + (v) => + (v >= stepperXmin && v <= stepperXmax) || + $t('Settings.TimelapseTab.RulesBetweenMinMax', { + min: stepperXmin, + max: stepperXmax, + }), + ]" :disabled="blockedsettings.includes('park_custom_pos_x')" hide-spin-buttons /> @@ -164,6 +177,15 @@ hide-details="auto" outlined dense + :rules="[ + (v) => !!v || $t('Settings.TimelapseTab.RulesRequired'), + (v) => + (v >= stepperYmin && v <= stepperYmax) || + $t('Settings.TimelapseTab.RulesBetweenMinMax', { + min: stepperYmin, + max: stepperYmax, + }), + ]" :disabled="blockedsettings.includes('park_custom_pos_y')" hide-spin-buttons /> @@ -180,6 +202,10 @@ hide-details="auto" outlined dense + :rules="[ + (v) => !!v || $t('Settings.TimelapseTab.RulesRequired'), + (v) => v >= 0 || $t('Settings.TimelapseTab.RulesZeroAndPositive'), + ]" :disabled="blockedsettings.includes('park_custom_pos_dz')" hide-spin-buttons /> @@ -195,6 +221,10 @@ hide-details="auto" outlined dense + :rules="[ + (v) => !!v || $t('Settings.TimelapseTab.RulesRequired'), + (v) => v >= 0 || $t('Settings.TimelapseTab.RulesZeroAndPositive'), + ]" :disabled="blockedsettings.includes('park_travel_speed')" hide-spin-buttons /> @@ -221,6 +251,10 @@ hide-details="auto" outlined dense + :rules="[ + (v) => !!v || $t('Settings.TimelapseTab.RulesRequired'), + (v) => v > 0 || $t('Settings.TimelapseTab.RulesPositive'), + ]" :disabled="blockedsettings.includes('park_retract_speed')" hide-spin-buttons /> @@ -235,6 +269,10 @@ hide-details="auto" outlined dense + :rules="[ + (v) => !!v || $t('Settings.TimelapseTab.RulesRequired'), + (v) => v >= 0 || $t('Settings.TimelapseTab.RulesZeroAndPositive'), + ]" :disabled="blockedsettings.includes('park_retract_distance')" hide-spin-buttons /> @@ -249,6 +287,10 @@ hide-details="auto" outlined dense + :rules="[ + (v) => !!v || $t('Settings.TimelapseTab.RulesRequired'), + (v) => v > 0 || $t('Settings.TimelapseTab.RulesPositive'), + ]" :disabled="blockedsettings.includes('park_extrude_speed')" hide-spin-buttons /> @@ -263,6 +305,10 @@ hide-details="auto" outlined dense + :rules="[ + (v) => !!v || $t('Settings.TimelapseTab.RulesRequired'), + (v) => v >= 0 || $t('Settings.TimelapseTab.RulesZeroAndPositive'), + ]" :disabled="blockedsettings.includes('park_extrude_distance')" hide-spin-buttons /> @@ -279,6 +325,10 @@ step="0.1" outlined dense + :rules="[ + (v) => !!v || $t('Settings.TimelapseTab.RulesRequired'), + (v) => v >= 0 || $t('Settings.TimelapseTab.RulesZeroAndPositive'), + ]" :disabled="blockedsettings.includes('park_time')" /> @@ -306,6 +356,10 @@ hide-details="auto" outlined dense + :rules="[ + (v) => !!v || $t('Settings.TimelapseTab.RulesRequired'), + (v) => v > 0 || $t('Settings.TimelapseTab.RulesPositive'), + ]" :disabled="blockedsettings.includes('targetlength')" hide-spin-buttons /> @@ -320,6 +374,10 @@ hide-details="auto" outlined dense + :rules="[ + (v) => !!v || $t('Settings.TimelapseTab.RulesRequired'), + (v) => v > 0 || $t('Settings.TimelapseTab.RulesPositive'), + ]" :disabled="blockedsettings.includes('variable_fps_min')" hide-spin-buttons /> @@ -334,6 +392,10 @@ hide-details="auto" outlined dense + :rules="[ + (v) => !!v || $t('Settings.TimelapseTab.RulesRequired'), + (v) => v > variable_fps_min || $t('Settings.TimelapseTab.RulesMin'), + ]" :disabled="blockedsettings.includes('variable_fps_max')" hide-spin-buttons /> @@ -350,6 +412,10 @@ hide-details="auto" outlined dense + :rules="[ + (v) => !!v || $t('Settings.TimelapseTab.RulesRequired'), + (v) => v > 0 || $t('Settings.TimelapseTab.RulesPositive'), + ]" :disabled="blockedsettings.includes('output_framerate')" hide-spin-buttons /> @@ -364,6 +430,10 @@ hide-details="auto" outlined dense + :rules="[ + (v) => !!v || $t('Settings.TimelapseTab.RulesRequired'), + (v) => v >= 0 || $t('Settings.TimelapseTab.RulesZeroAndPositive'), + ]" :disabled="blockedsettings.includes('duplicatelastframe')" hide-spin-buttons /> @@ -377,6 +447,10 @@ hide-details="auto" outlined dense + :rules="[ + (v) => !!v || $t('Settings.TimelapseTab.RulesRequired'), + (v) => v > 0 || $t('Settings.TimelapseTab.RulesPositive'), + ]" :disabled="blockedsettings.includes('constant_rate_factor')" hide-spin-buttons /> @@ -542,7 +616,9 @@ export default class SettingsTimelapseTab extends Mixins(BaseMixin) { return this.$store.state.server.timelapse.settings.stream_delay_compensation } - set stream_delay_compensation(newVal) { + set stream_delay_compensation(newVal: number | string) { + if (newVal === '') newVal = 0 + this.$store.dispatch('server/timelapse/saveSetting', { stream_delay_compensation: newVal }) } @@ -582,23 +658,45 @@ export default class SettingsTimelapseTab extends Mixins(BaseMixin) { return this.$store.state.server.timelapse.settings.park_custom_pos_x } - set park_custom_pos_x(newVal) { + set park_custom_pos_x(newVal: number | string) { + if (newVal === '' || newVal < this.stepperXmin || newVal > this.stepperXmax) return + this.$store.dispatch('server/timelapse/saveSetting', { park_custom_pos_x: newVal }) } + get stepperXmin() { + return this.$store.state.printer.configfile?.settings?.stepper_x?.position_min ?? 0 + } + + get stepperXmax() { + return this.$store.state.printer.configfile?.settings?.stepper_x?.position_max ?? 200 + } + get park_custom_pos_y() { return this.$store.state.server.timelapse.settings.park_custom_pos_y } - set park_custom_pos_y(newVal) { + set park_custom_pos_y(newVal: number | string) { + if (newVal === '' || newVal < this.stepperYmin || newVal > this.stepperYmax) return + this.$store.dispatch('server/timelapse/saveSetting', { park_custom_pos_y: newVal }) } + get stepperYmin() { + return this.$store.state.printer.configfile?.settings?.stepper_y?.position_min ?? 0 + } + + get stepperYmax() { + return this.$store.state.printer.configfile?.settings?.stepper_y?.position_max ?? 200 + } + get park_custom_pos_dz() { return this.$store.state.server.timelapse.settings.park_custom_pos_dz } - set park_custom_pos_dz(newVal) { + set park_custom_pos_dz(newVal: number | string) { + if (newVal === '' || newVal < 0) return + this.$store.dispatch('server/timelapse/saveSetting', { park_custom_pos_dz: newVal }) } @@ -606,7 +704,9 @@ export default class SettingsTimelapseTab extends Mixins(BaseMixin) { return this.$store.state.server.timelapse.settings.park_travel_speed } - set park_travel_speed(newVal) { + set park_travel_speed(newVal: number | string) { + if (newVal === '' || newVal < 0) return + this.$store.dispatch('server/timelapse/saveSetting', { park_travel_speed: newVal }) } @@ -614,7 +714,9 @@ export default class SettingsTimelapseTab extends Mixins(BaseMixin) { return this.$store.state.server.timelapse.settings.park_retract_speed } - set park_retract_speed(newVal) { + set park_retract_speed(newVal: number | string) { + if (newVal === '' || newVal <= 0) return + this.$store.dispatch('server/timelapse/saveSetting', { park_retract_speed: newVal }) } @@ -622,7 +724,9 @@ export default class SettingsTimelapseTab extends Mixins(BaseMixin) { return this.$store.state.server.timelapse.settings.park_extrude_speed } - set park_extrude_speed(newVal) { + set park_extrude_speed(newVal: number | string) { + if (newVal === '' || newVal <= 0) return + this.$store.dispatch('server/timelapse/saveSetting', { park_extrude_speed: newVal }) } @@ -630,7 +734,9 @@ export default class SettingsTimelapseTab extends Mixins(BaseMixin) { return this.$store.state.server.timelapse.settings.park_retract_distance } - set park_retract_distance(newVal) { + set park_retract_distance(newVal: number | string) { + if (newVal === '' || newVal < 0) return + this.$store.dispatch('server/timelapse/saveSetting', { park_retract_distance: newVal }) } @@ -638,7 +744,9 @@ export default class SettingsTimelapseTab extends Mixins(BaseMixin) { return this.$store.state.server.timelapse.settings.park_extrude_distance } - set park_extrude_distance(newVal) { + set park_extrude_distance(newVal: number | string) { + if (newVal === '' || newVal < 0) return + this.$store.dispatch('server/timelapse/saveSetting', { park_extrude_distance: newVal }) } @@ -646,7 +754,9 @@ export default class SettingsTimelapseTab extends Mixins(BaseMixin) { return this.$store.state.server.timelapse.settings.park_time } - set park_time(newVal) { + set park_time(newVal: number | string) { + if (newVal === '' || newVal < 0) return + this.$store.dispatch('server/timelapse/saveSetting', { park_time: newVal }) } @@ -662,7 +772,9 @@ export default class SettingsTimelapseTab extends Mixins(BaseMixin) { return this.$store.state.server.timelapse.settings.constant_rate_factor } - set constant_rate_factor(newVal) { + set constant_rate_factor(newVal: number | string) { + if (newVal === '' || newVal <= 0) return + this.$store.dispatch('server/timelapse/saveSetting', { constant_rate_factor: newVal }) } @@ -670,7 +782,9 @@ export default class SettingsTimelapseTab extends Mixins(BaseMixin) { return this.$store.state.server.timelapse.settings.output_framerate } - set output_framerate(newVal) { + set output_framerate(newVal: number | string) { + if (newVal === '' || newVal <= 0) return + this.$store.dispatch('server/timelapse/saveSetting', { output_framerate: newVal }) } @@ -702,7 +816,9 @@ export default class SettingsTimelapseTab extends Mixins(BaseMixin) { return this.$store.state.server.timelapse.settings.targetlength } - set targetlength(newVal) { + set targetlength(newVal: number | string) { + if (newVal === '' || newVal <= 0) return + this.$store.dispatch('server/timelapse/saveSetting', { targetlength: newVal }) } @@ -710,7 +826,9 @@ export default class SettingsTimelapseTab extends Mixins(BaseMixin) { return this.$store.state.server.timelapse.settings.variable_fps_min } - set variable_fps_min(newVal) { + set variable_fps_min(newVal: number | string) { + if (newVal === '' || newVal <= 0) return + this.$store.dispatch('server/timelapse/saveSetting', { variable_fps_min: newVal }) } @@ -718,7 +836,9 @@ export default class SettingsTimelapseTab extends Mixins(BaseMixin) { return this.$store.state.server.timelapse.settings.variable_fps_max } - set variable_fps_max(newVal) { + set variable_fps_max(newVal: number | string) { + if (newVal === '' || newVal <= this.variable_fps_min) return + this.$store.dispatch('server/timelapse/saveSetting', { variable_fps_max: newVal }) } @@ -726,7 +846,9 @@ export default class SettingsTimelapseTab extends Mixins(BaseMixin) { return this.$store.state.server.timelapse.settings.duplicatelastframe } - set duplicatelastframe(newVal) { + set duplicatelastframe(newVal: number | string) { + if (newVal === '' || newVal < 0) return + this.$store.dispatch('server/timelapse/saveSetting', { duplicatelastframe: newVal }) } diff --git a/src/components/settings/SettingsUiSettingsTab.vue b/src/components/settings/SettingsUiSettingsTab.vue index bd4ba51e2..96d4a7556 100644 --- a/src/components/settings/SettingsUiSettingsTab.vue +++ b/src/components/settings/SettingsUiSettingsTab.vue @@ -142,6 +142,13 @@ + + + + Interface > Control", "SpeedFactor": "Hastighed", - "ZTilt": "Z Tilt" + "ZOffset": "Z-Offset", + "ZTilt": "Z-Tilt" }, "WebcamPanel": { "All": "Alle", @@ -853,6 +864,8 @@ "Style": "Stil", "ValueGreaterThan": "Værdi større end {value}", "ZOffsetIncrements": "Z-offset trin (i mm)", + "ZOffsetSaveOption": "Z-Offset Gem-mulighed", + "ZOffsetSaveOptionDescription": "Ændr metoden for 'gem Z-Offset'", "ZTiltAdjust": "Z-Tilt justering{isDefault}" }, "DashboardTab": { @@ -928,6 +941,18 @@ "RestoreDialog": "Vælg de sektioner du ønsker at gendanne fra backup:", "TimeFormat": "Tidsformat" }, + "HeightmapTab": { + "ColorSchemes": "Farvetemaer", + "Heightmap": "Højdekort", + "IsDefault": "(Standard)", + "Schemes": { + "GrayScale": "Gråskala", + "Hot": "Varm", + "Hsv": "TMV", + "Portland": "Portland", + "Spring": "Forår" + } + }, "InterfaceSettings": "Interfaceindstillinger", "MacrosTab": { "Add": "Tilføj", @@ -1132,6 +1157,10 @@ "ScrewsTiltAdjustDialogDescription": "Viser vindue til hjælp med Bed-skrue justeringer ved brug af proben.", "TempchartHeight": "Temperaturgrafens højde", "TempchartHeightDescription": "Ændrer højden på grafen i Temperaturvisningen.", + "Theme": "Tema", + "ThemeDark": "Mørkt", + "ThemeDescription": "Ændr grundfarverne og tonen af applikationen", + "ThemeLight": "Lyst", "UiSettings": "UI-indstillinger" }, "Update": "Opdater", @@ -1140,6 +1169,7 @@ "CreateWebcam": "Nyt Webcam", "EditCrowsnestConf": "Redigér crowsnest.conf", "EditWebcam": "Rediger Webcam", + "EnableAudio": "Aktivér lyd", "FlipWebcam": "Vend webcam-billedet:", "HideFps": "Skjul FPS", "Hlsstream": "HLS Stream", @@ -1170,6 +1200,7 @@ "Vertically": "vertikalt", "Webcams": "Webcams", "WebrtcCameraStreamer": "WebRTC (camera-streamer)", + "WebrtcGo2rtc": "WebRTC (go2rtc)", "WebrtcJanus": "WebRTC (janus-gateway)", "WebrtcMediaMTX": "WebRTC (MediaMTX)" } diff --git a/src/locales/de.json b/src/locales/de.json index 5794525fe..f7e4b544d 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -198,7 +198,6 @@ "EditFile": "Datei bearbeiten", "Empty": "Leer", "ExtruderTemp": "Extruder Temp.", - "Filament": "Filament", "FilamentName": "Filament Name", "FilamentType": "Filament Typ", "FilamentUsage": "Filamentverbrauch", @@ -300,19 +299,15 @@ "InvalidNameAscii": "Name ist ungültig. Nur ASCII Zeichen sind erlaubt.", "InvalidNameEmpty": "Feld darf nicht leer sein!", "InvalidNameReserved": "Das Profil 'default' ist reserviert, bitte wähle einen anderen Profilnamen.", - "Later": "später", "Mesh": "Mesh", "Name": "Name", "NoBedMeshHasBeenLoadedYet": "Es wurde noch kein Bed Mesh geladen.", "NoProfile": "Kein Profil verfügbar", - "Ok": "Ok", "Probed": "Abgetastet", "Profiles": "Profile", "Remove": "löschen", - "RemoveSaveDescription": "Das Löschen des Höhenprofils wurde registriert. Mit einem Klick auf SAVE_CONFIG wird es aus der printer.cfg gelöscht und Klipper neu gestartet.", "Rename": "umbenennen", "RenameBedMeshProfile": "Bed Mesh umbenennen", - "SAVE_CONFIG": "SAVE_CONFIG", "ScaleGradient": "Farbverlauf skalieren", "ScaleZMax": "Skaliere z-max.", "TitleCalibrate": "Neues Bed Mesh kalibrieren", @@ -375,6 +370,7 @@ "completed": "Abgeschlossen", "error": "Fehler", "in_progress": "In Arbeit", + "interrupted": "Unterbrochen", "klippy_disconnect": "Klippy getrennt", "klippy_shutdown": "Klippy heruntergefahren", "Others": "Sonstige", @@ -389,7 +385,6 @@ "TotalTime": "Gesamtzeit" }, "JobQueue": { - "AllJobs": "Alle Aufträge", "Cancel": "abbrechen", "ChangeCount": "Anzahl ändern", "Count": "Anzahl", @@ -397,10 +392,10 @@ "InvalidCountEmpty": "Die Eingabe darf nicht leer sein!", "InvalidCountGreaterZero": "Die Eingabe muss größer als 0 sein!", "JobQueue": "Auftragswarteschlange", - "Jobs": "Aufträge", "Pause": "Pause", "RemoveFromQueue": "Von Auftragswarteschlange entfernen", - "Start": "Start" + "Start": "Start", + "StartPrint": "Auftrag starten" }, "Machine": { "ConfigFilesPanel": { @@ -1089,6 +1084,11 @@ "RetractDistanceDescription": "Die Länge an Filament die der Extruder zurückzieht.", "RetractSpeed": "Rückzuggeschwindigkeit", "RetractSpeedDescription": "Geschwindigkeit mit der der Extruder das Filament zurückzieht.", + "RulesBetweenMinMax": "Der Wert muss zwischen {min} und {max} sein!", + "RulesMin": "Der Wert muss mindestens {min} sein!", + "RulesPositive": "Der Wert muss positiv sein!", + "RulesRequired": "Der Wert ist erforderlich!", + "RulesZeroAndPositive": "Der Wert muss 0 oder größer sein!", "SaveFrames": "Bilder speichern", "SaveFramesDescription": "Speichern der Bilder in einer Zip-Datei für externes Rendern", "StreamDelayCompensation": "Stream-Verzögerungs-Kompensation", diff --git a/src/locales/en.json b/src/locales/en.json index 8cb6bda38..4b25e5eb0 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -36,6 +36,9 @@ "NoEmptyAllowedError": "Input must not be empty!" }, "Printers": "Printers", + "TextfieldWithCopy": { + "Copied": "Copied" + }, "TheServiceWorker": { "DescriptionNeedUpdate": "The local cache is outdated and needs to be updated. Please click on the button below to update the cache.", "TitleNeedUpdate": "PWA needs update", @@ -46,7 +49,7 @@ "DescriptionFrequencyCapped": "rPi ARM max frequency is currently limited to 1.2 GHz.", "DescriptionPreviouslyFrequencyCapped": "rPi ARM max frequency was at least once limited to 1.2 GHz since last power-on.", "DescriptionPreviouslyTemperatureLimited": "rPi uC (3A+/3B+ only) temperature was at least once above the soft limit (default 60C) since last power-on.", - "DescriptionPreviouslyThrottled": "rPI ARM core(s) where throttled down at least once since last power-on.", + "DescriptionPreviouslyThrottled": "rPI ARM core(s) were throttled down at least once since last power-on.", "DescriptionPreviouslyUnderVolted": "rPI supply voltage dropped below 4.65V at least once since the last power-on.", "DescriptionTemperatureLimitActive": "rPi uC (3A+/3B+ only) temperature is currently above the soft limit (default 60C).", "DescriptionUnderVoltageDetected": "rPI supply voltage currently below 4.65V", @@ -146,6 +149,26 @@ "SendCode": "Send code...", "SetupConsole": "Setup Console" }, + "CoolDownDialog": { + "AreYouSure": "Are you sure?", + "CoolDown": "CoolDown", + "No": "No", + "Yes": "Yes" + }, + "DevicesDialog": { + "CanBusInfo": "Only unassigned nodes can be detected. It’s recommended to have only one unassigned device connected to the can bus to avoid communication issues. For more details, please click on the link:", + "ClickRefresh": "Click on the refresh button to search for devices.", + "DevicePath": "Device path", + "Formats": "Formats", + "Headline": "Devices", + "HideSystemEntries": "Hide system entries", + "LibcameraId": "Libcamera ID", + "NoDeviceFound": "No device found. Please check the connection and click on the refresh button.", + "PathByHardware": "Path by hardware", + "PathById": "Path by ID", + "Refresh": "refresh", + "Resolutions": "Resolutions" + }, "Dialogs": { "StartPrint": { "Cancel": "Cancel", @@ -158,6 +181,7 @@ }, "Editor": { "ConfigReference": "Config Reference", + "DeviceDialog": "Devices", "DontSave": "Don't save", "Downloading": "Downloading", "FailedSave": "{filename} could not be uploaded!", @@ -198,7 +222,6 @@ "EditFile": "Edit File", "Empty": "Empty", "ExtruderTemp": "Extruder Temp.", - "Filament": "Filament", "FilamentName": "Filament Name", "FilamentType": "Filament Type", "FilamentUsage": "Filament Usage", @@ -322,6 +345,7 @@ }, "History": { "AddNote": "Add note", + "AddToQueueSuccessful": "File {filename} added to Queue.", "AllJobs": "All", "AvgPrinttime": "Print Time - Ø", "Cancel": "Cancel", @@ -375,6 +399,7 @@ "completed": "Completed", "error": "Error", "in_progress": "In progress", + "interrupted": "Interrupted", "klippy_disconnect": "Klippy disconnect", "klippy_shutdown": "Klippy shutdown", "Others": "Others", @@ -389,7 +414,6 @@ "TotalTime": "Total Time" }, "JobQueue": { - "AllJobs": "All Jobs", "Cancel": "Cancel", "ChangeCount": "Change count", "Count": "Count", @@ -397,10 +421,10 @@ "InvalidCountEmpty": "Input must not be empty!", "InvalidCountGreaterZero": "Input must be greater than 0!", "JobQueue": "Job Queue", - "Jobs": "Jobs", "Pause": "Pause", "RemoveFromQueue": "Remove from Queue", - "Start": "Start" + "Start": "Start", + "StartPrint": "Start Job" }, "Machine": { "ConfigFilesPanel": { @@ -1089,6 +1113,11 @@ "RetractDistanceDescription": "The length of filament that the extruder retracts.", "RetractSpeed": "Retract Speed", "RetractSpeedDescription": "Speed at which the extruder retracts the filament.", + "RulesBetweenMinMax": "Value must be between {min} and {max}!", + "RulesMin": "Value must be minimum {min}!", + "RulesPositive": "Value must be positive!", + "RulesRequired": "Value is required!", + "RulesZeroAndPositive": "Value must be 0 or greater!", "SaveFrames": "Save Frames", "SaveFramesDescription": "Save the frames to a zip-file for external rendering", "StreamDelayCompensation": "Stream Delay Compensation", @@ -1119,6 +1148,8 @@ "BoolBigThumbnailDescription": "Display a large thumbnail in the status panel during a print.", "BoolHideUploadAndPrintButton": "Hide Upload and Print Button", "BoolHideUploadAndPrintButtonDescription": "Show or hide the \"Upload and Print\" button in the top bar.", + "ConfirmOnCoolDown": "Require confirm on CoolDown", + "ConfirmOnCoolDownDescription": "Show a confirmation dialog on CoolDown", "ConfirmOnEmergencyStop": "Require confirm on Emergency Stop", "ConfirmOnEmergencyStopDescription": "Show a confirmation dialog on Emergency Stop", "ConfirmOnPowerDeviceChange": "Require confirm on Device Power changes", diff --git a/src/locales/it.json b/src/locales/it.json index c3149a23b..d4a2267fd 100644 --- a/src/locales/it.json +++ b/src/locales/it.json @@ -17,7 +17,9 @@ }, "MoonrakerWarnings": { "MoonrakerComponent": "Moonraker: {component}", - "MoonrakerFailedComponentDescription": "È stato rilevato un errore durante il caricamento del componente Moonraker '{component}'. Controllare il file di registro e risolvere il problema.", + "MoonrakerFailedComponentDescription": "È stato rilevato un errore durante il caricamento del componente '{component}' di Moonraker. Controllare il file di registro e risolvere il problema.", + "MoonrakerFailedInitComponentDescription": "È stato rilevato un errore durante l'inizializzazione del componente '{component}' di Moonraker. Controllare il file di registro e risolvere il problema.", + "MoonrakerInitComponent": "Init. Moonraker: {component}", "MoonrakerWarning": "Avviso di Moonraker", "UnparsedConfigOption": "Opzione di configurazione non inviata '{opzione}: {valore}' rilevata nella sezione [{sezione}]. Questa potrebbe essere un'opzione non più disponibile o potrebbe essere il risultato di un modulo che non riesce a caricarsi. In futuro questo comporterà un errore di avvio.", "UnparsedConfigSection": "Rilevata la sezione di configurazione non inviata [{sezione}]. Questo potrebbe essere il risultato di un componente che non è riuscito a caricare. In futuro questo comporterà un errore di avvio." @@ -25,7 +27,7 @@ "Never": "mai", "NextReboot": "prossimo riavvio", "NoNotification": "Nessuna notifica disponibile", - "Notifications": "Notifica", + "Notifications": "Notifiche", "Remind": "Ricorda:" }, "NumberInput": { @@ -287,7 +289,7 @@ "Min": "Min", "Name": "Nome", "Range": "Range", - "Size": "Grandezza" + "Size": "Dimensione" }, "DeleteBedMeshProfile": "Elimina il Profilo", "DoYouReallyWantToDelete": "Vuoi davvero eliminare il profilo \"{name}\"?", @@ -301,7 +303,7 @@ "Later": "in seguito", "Mesh": "Mesh", "Name": "Nome", - "NoBedMeshHasBeenLoadedYet": "Non è stato ancora caricato nessuna bed_mesh.", + "NoBedMeshHasBeenLoadedYet": "Nessuna bed-mesh è stata caricata.", "NoProfile": "Nessun profilo disponibile", "Ok": "OK", "Probed": "Analizzato", @@ -349,6 +351,7 @@ "Jobs": "Processi di stampa", "LastModified": "Ultima Modifica", "LayerHeight": "Altezza del Layer", + "LoadCompleteHistory": "Carica la cronologia completa", "LongestPrinttime": "Tempo di stampa più lungo", "Note": "Nota", "ObjectHeight": "Altezza Oggetto", @@ -372,6 +375,7 @@ "completed": "Completato", "error": "Errore", "in_progress": "In corso", + "interrupted": "Interrotto", "klippy_disconnect": "Klippy disconnesso", "klippy_shutdown": "Klippy spento", "Others": "Altro", @@ -379,8 +383,6 @@ }, "Table": "Tabella", "TitleExportHistory": "Esporta Cronologia", - "TitleRefreshHistory": "Aggiorna Cronologia", - "TitleSettings": "Impostazioni", "TotalDuration": "Tempo Totale", "TotalFilamentUsed": "Filamento Totale Utilizzato", "TotalJobs": "Processi Totali", @@ -478,7 +480,7 @@ "Temp": "Temp: {temp}°C", "TempMax": "max: {temp}°C", "TempMin": "min: {temp}°C", - "Transmitted": "Transmessi: {transmitted}", + "Transmitted": "Trasmessi: {transmitted}", "Version": "Versione: {version}" } }, @@ -536,12 +538,15 @@ "Panels": { "ExtruderControlPanel": { "Allowed": "Consentito", + "CleanNozzle": "Pulisci Ugello", "EstimatedExtrusion": "Estrusione:", "Extrude": "Estrudi", + "ExtruderControl": "Controllo Estrusore", "ExtruderTempTooLow": "Temp. Estrusore <", - "ExtrusionFactor": "Fattore di estrusione", + "ExtrusionFactor": "Fattore di Estrusione", "ExtrusionFeedrate": "Feedrate Estrusione", "FilamentLength": "Lunghezza Filamento", + "FirmwareRetraction": "Retrazione Firmware", "FirmwareRetractionSettings": { "RetractLength": "Lunghezza Retrazione", "RetractSpeed": "Velocità Retrazione", @@ -550,14 +555,17 @@ }, "Headline": "Estrusore", "LoadFilament": "Carica Filamento", + "PressureAdvance": "Pressure-Advance", "PressureAdvanceSettings": { - "Advance": "Pressure Advance", + "Advance": "Pressure-Advance", "Extruder": "Estrusore", - "SmoothTime": "Smooth Time" + "SmoothTime": "Smooth-Time" }, + "PurgeFilament": "Spurga Filamento", "Requested": "Richiesto", "Retract": "Retrazione", "TooLargeExtrusion": "Estrusione troppo grande!", + "Tools": "Utensili", "UnloadFilament": "Scarica Filamento" }, "FarmPrinterPanel": { @@ -580,6 +588,7 @@ "MotionSettings": { "Acceleration": "Accelerazione", "MaxAccelToDecel": "Max Accel. a Decel.", + "MinimumCruiseRatio": "Min. Ratio Spostamento", "SquareCornerVelocity": "Velocità Angolo Retto", "Velocity": "Velocità" } @@ -605,15 +614,14 @@ "MiscellaneousPanel": { "Headline": "Miscellanea", "Light": { - "Blue": "blue", - "Green": "green", - "Red": "red", - "White": "white" + "Blue": "blu", + "Green": "verde", + "Red": "rosso", + "White": "bianco" }, "RunoutSensor": { "Detected": "rilevato", - "Disabled": "disabilitato", - "Empty": "Vuoto" + "Empty": "vuoto" } }, "PowerControlPanel": { @@ -643,7 +651,7 @@ "Refresh": "aggiorna", "Search": "Trova", "SelectSpool": "Seleziona Bobina", - "Today": "Today", + "Today": "Oggi", "TooLessFilament": "La bobina corrente potrebbe non avere abbastanza filamento per questa stampa. ({spoolWeight}g su {fileWeight}g)", "Weight": "Peso", "Yesterday": "Ieri" @@ -709,7 +717,7 @@ "Temperature": "temperatura attuale" }, "Headline": "Temperature", - "HideMcuHostSensors": "Nascondi i sensoriHide di Host/MCU", + "HideMcuHostSensors": "Nascondi i sensori di Host/MCU", "HideMonitors": "Nascondi il monitoraggio", "Max": "max", "Min": "min", @@ -728,14 +736,18 @@ "ToolheadControlPanel": { "Absolute": "assoluto", "ALL": "TUTTI", + "ControlButtons": "Pulsanti di controllo", + "CoordinateFields": "Campo Coordinate", "Headline": "Toolhead", "PleaseConfigureSteps": "Configura gli steps", "Position": "Posizione", + "PositionOutput": "Posizione output", "QGL": "QGL", "Relative": "relativo", "SettingsInterfaceControl": "Impostazioni> Interfaccia> Controllo", "SpeedFactor": "Fattore di velocità", - "ZTilt": "Inclinazione Z" + "ZOffset": "Z-Offset", + "ZTilt": "Z-Tilt" }, "WebcamPanel": { "All": "Tutto", @@ -832,7 +844,7 @@ "Cross": "Croce", "EnableXYHoming": "Abilita l'homing combinato degli assi X e Y", "EstimatedExtrusionInfo": "Mostra informazioni stimate sull'estrusione", - "EstimatedExtrusionInfoDescription": "Mostra / Nascondi informazioni stimate in base alla quantità di estrusione e alla velocità di avanzamento", + "EstimatedExtrusionInfoDescription": "Mostra/Nascondi informazioni stimate in base alla quantità di estrusione e alla velocità di avanzamento", "HideDuringPrint": "Hide axis controls during print", "InvertXMovement": "Inverti il movimento per l'asse X", "InvertYMovement": "Inverti il movimento per l'asse Y", @@ -853,6 +865,8 @@ "Style": "Stile", "ValueGreaterThan": "Il valore deve essere più grande di {value}", "ZOffsetIncrements": "Z-Offset incremento (in mm)", + "ZOffsetSaveOption": "Opzione salvataggio Z-Offset", + "ZOffsetSaveOptionDescription": "Modificare l'opzione per salvare lo Z-Offset", "ZTiltAdjust": "Z-Tilt Adjust{isDefault}" }, "DashboardTab": { @@ -883,7 +897,7 @@ "GridColor": "Colore Griglia", "MaxFeed": "Max Feed Rate", "MinFeed": "Min Feed Rate", - "ProgressColor": "colore di avanzamento", + "ProgressColor": "Colore Avanzamento", "ShowAxes": "Mostra Assi" }, "GeneralTab": { @@ -928,7 +942,19 @@ "RestoreDialog": "Seleziona tutte le sezioni che vuoi ripristinare:", "TimeFormat": "Formato Data" }, - "InterfaceSettings": "Impostazioni dell'Interfaccia", + "HeightmapTab": { + "ColorSchemes": "Schemi Colore", + "Heightmap": "Mappa dell'Altezza", + "IsDefault": "(Default)", + "Schemes": { + "GrayScale": "Scala di Grigi", + "Hot": "Hot", + "Hsv": "Hsv", + "Portland": "Portland", + "Spring": "Spring" + } + }, + "InterfaceSettings": "Impostazioni Interfaccia", "MacrosTab": { "Add": "aggiungi", "AddGroup": "aggiungi Gruppo", @@ -1064,7 +1090,12 @@ "RetractDistanceDescription": "Lunghezza del filamento retratto dall'estrusore", "RetractSpeed": "Velocità Retrazione", "RetractSpeedDescription": "Velocità di retrazione del filamento dall'estrusore.", - "SaveFrames": "Salva Frame", + "RulesBetweenMinMax": "Il valore deve essere compreso tra {min} e {max}!", + "RulesMin": "Il valore deve essere almeno di {min}!", + "RulesPositive": "Il valore deve essere positivo!", + "RulesRequired": "È necessario inserire un valore!", + "RulesZeroAndPositive": "Il valore deve essere uguale o maggiore di 0!", + "SaveFrames": "Salva Frames", "SaveFramesDescription": "Salva i Frame in un file zip per rendering esterno", "StreamDelayCompensation": "Compensazione Ritardo Stream", "StreamDelayCompensationDescription": "Ritarda cattura frame", @@ -1075,10 +1106,10 @@ "Timelapse": "Timelapse", "TravelSpeed": "Velocità Spostamento", "TravelSpeedDescription": "Velocità spostamento verso la posizione di parcheggio e ripresa", - "UnretractDistance": "Distanza di ri-estrusione", + "UnretractDistance": "Distanza di deretrazione", "UnretractDistanceDescription": "La lunghezza di filamento che l'estrusore ri-estrude dopo una retrazione.", - "UnretractSpeed": "Velocità di ri-estrusione", - "UnretractSpeedDescription": "Velocità dell'estrusore a ri-estrudere dopo una retrazione.", + "UnretractSpeed": "Velocità di deretrazione", + "UnretractSpeedDescription": "Velocità dell'estrusore a riestrudere dopo una retrazione.", "VariableFps": "FPS Variabili", "VariableFpsDescription": "Se abilitato, il framerate del video di output sarà calcolato in base alla lunghezza target", "VariableFpsMax": "FPS Variabili max", @@ -1125,22 +1156,27 @@ "NavigationStyleDescription": "Cambia l'aspetto di navigazione", "NavigationStyleIconsAndText": "Icone + Testo", "NavigationStyleIconsOnly": "Solo icone", - "PowerDeviceName": "Dispositivo alimentazione stampantePrinter", + "PowerDeviceName": "Dispositivo Alimentazione Stampante", "PowerDeviceNameDescription": "Selezionare quale dispositivo di alimentazione Moonraker deve essere utilizzato per accendere la stampante.", "Primary": "Primario", "ScrewsTiltAdjustDialog": "Finestra di dialogo per lo Screws Tilt Adjust", "ScrewsTiltAdjustDialogDescription": "Mostra dialoghi di aiuto per lo SCREWS_TILT_CALCULATE.", - "TempchartHeight": "Grafico della temperatura in altezza", - "TempchartHeightDescription": "Modificare l'altezza del grafico della temperatura sul cruscotto.", - "UiSettings": "Impostazioni UI" + "TempchartHeight": "Altezza grafico della temperatura", + "TempchartHeightDescription": "Modifica l'altezza del grafico della temperatura sulla dashboard.", + "Theme": "Tema", + "ThemeDark": "Scuro", + "ThemeDescription": "Modifica l'aspetto generale dell'applicazionen", + "ThemeLight": "Chiaro", + "UiSettings": "Impostazioni Interfaccia Utente" }, "Update": "update", "WebcamsTab": { - "AddWebcam": "aggiungi webcam", + "AddWebcam": "Aggiungi Webcam", "CreateWebcam": "Crea Webcam", "EditCrowsnestConf": "Modifica crowsnest.conf", "EditWebcam": "Modifica Webcam", - "FlipWebcam": "Specchia immagine della webcam:", + "EnableAudio": "Abilita Audio", + "FlipWebcam": "Ruota immagine della webcam:", "HideFps": "Nascondi contatore FPS", "Hlsstream": "HLS Stream", "Horizontally": "orizzontalmente", @@ -1170,6 +1206,7 @@ "Vertically": "verticalmente", "Webcams": "Webcam", "WebrtcCameraStreamer": "WebRTC (camera-streamer)", + "WebrtcGo2rtc": "WebRTC (go2rtc)", "WebrtcJanus": "WebRTC (janus-gateway)", "WebrtcMediaMTX": "WebRTC (MediaMTX)" } @@ -1192,7 +1229,7 @@ "Enabled": "Abilitato", "EstimatedLength": "Lunghezza stimata", "Files": "File", - "Filesize": "Grandezza", + "Filesize": "Dimensione", "Fixed": "fisso", "Framerate": "Framerate", "Frames": "Frames", diff --git a/src/locales/pl.json b/src/locales/pl.json index da91fd503..7887bada8 100644 --- a/src/locales/pl.json +++ b/src/locales/pl.json @@ -388,7 +388,6 @@ "TotalTime": "Całkowity czas druku" }, "JobQueue": { - "AllJobs": "Wszystkie wydruki", "Cancel": "Anuluj", "ChangeCount": "Zmień ilość", "Count": "Ilość", @@ -396,7 +395,6 @@ "InvalidCountEmpty": "Pole wejściowe nie może być puste!", "InvalidCountGreaterZero": "Wprowadzona wartość musi być większa niż 0!", "JobQueue": "Kolejka wydruków", - "Jobs": "Wydruki", "Pause": "Pauza", "RemoveFromQueue": "Usuń z kolejki", "Start": "Start" diff --git a/src/locales/uk.json b/src/locales/uk.json index 02e6b68ca..684818cc9 100644 --- a/src/locales/uk.json +++ b/src/locales/uk.json @@ -1,12 +1,16 @@ { "App": { "Notifications": { + "BrowserWarnings": { + "Description": "{name} більше не підтримується. Поточна версія {version}, але Mainsail потребує версію {minVersion} або вище.", + "Headline": "Застарілий браузер" + }, "DependencyDescription": "Поточна версія {name} не підтримує всі функції Mainsail. Оновіть {Ім'я} принаймні до {needversion}.", "DependencyName": "Залежність: {Ім'я}", "DismissAll": "Відкинути всі", "KlipperWarnings": { "DeprecatedOption": "Опція '{option}' у розділі '{section}' застаріла і буде видалена у майбутньому випуску.", - "DeprecatedOptionHeadline": "Застаріла опція кліппера", + "DeprecatedOptionHeadline": "Застаріла опція Klipper", "DeprecatedValue": "Значення '{value}' опції '{option}' у секції '{section}' застаріла і буде видалена у майбутньому випуску.", "DeprecatedValueHeadline": "Застаріле значення Klipper", "KlipperWarning": "Попередження Klipper" @@ -14,6 +18,8 @@ "MoonrakerWarnings": { "MoonrakerComponent": "Moonraker: {component}", "MoonrakerFailedComponentDescription": "Помилка була виявлена під час завантаження компонента Moonraker '{component}'. Будь ласка, перевірте файл журналу та виправте проблему.", + "MoonrakerFailedInitComponentDescription": "Було виявлено помилку під час ініціалізації компонента moonraker '{component}'. Перевірте файл журналу та виправте проблему.", + "MoonrakerInitComponent": "Поч. компонент Moonraker: {component}", "MoonrakerWarning": "Попередження Moonraker", "UnparsedConfigOption": "Непарний параметр конфігурації '{option}: {value}' виявлено в розділі [{section}]. Це може бути варіантом, який більше не доступний, або може бути результатом модуля, який не зміг завантажитися.", "UnparsedConfigSection": "Непарний розділ конфігурації [{section}] виявлений. Це може бути результатом компонента, який не вдалося завантажити.." @@ -30,8 +36,16 @@ "NoEmptyAllowedError": "Поле не повинно бути порожнім!" }, "Printers": "Принтери", + "TextfieldWithCopy": { + "Copied": "Скопійовано" + }, + "TheServiceWorker": { + "DescriptionNeedUpdate": "Локальний кеш застарів і потребує оновлення. Щоб оновити кеш, натисніть кнопку нижче.", + "TitleNeedUpdate": "Веб додаток потребує оновлення", + "Update": "оновити" + }, "ThrottledStates": { - "DescriptionCurrentlyThrottled": "Ядро (и) RPI ARM в даний час зменьшуються.", + "DescriptionCurrentlyThrottled": "Ядро (и) RPI ARM в даний час зменшується.", "DescriptionFrequencyCapped": "Частота Max ARM RPI наразі обмежена 1,2 ГГц.", "DescriptionPreviouslyFrequencyCapped": "Частота Max ARM RPI принаймні раз була обмежена 1,2 ГГц з останнього ввімкнення.", "DescriptionPreviouslyTemperatureLimited": "Температура RPI UC (3a+/3b+) була принаймні раз вище за легке обмеження (за замовчуванням 60°) з останнього ввімкнення.", @@ -52,16 +66,17 @@ "Complete": "Завершено - {filename}", "Error": "Помилка", "Pause": "Призупинення друку", + "PrinterOff": "Принтер вимкнений", "Printing": "{percent}% Друк - {filename}", "PrintingETA": "{percent}% Друк - Очікуваний час: {eta} - {filename}" }, "TopBar": { "CannotUploadTheFile": "Не вдається завантажити файл!", "EmergencyStop": "АВАРІЙНА ЗУПИНКА", - "SAVE_CONFIG": "ЗБЕРЕЖЕННЯ КОНФІГУРАЦІЇ", + "SAVE_CONFIG": "ЗБЕРЕГТИ КОНФІГУРАЦІЮ", "Uploading": "Завантаження", "UploadOfFileSuccessful": "Завантаження {file} успішно!", - "UploadPrint": "Завантажити та Друканутити" + "UploadPrint": "Завантажити та надрукувати" }, "TopCornerMenu": { "Cancel": "Скасувати", @@ -71,10 +86,10 @@ "HostShutdown": "Вимкнення хоста призведе до зупинки поточного друку!", "KlipperFirmwareRestart": "Перезапуск прошивки Klipper призведе до зупинки поточного друку!", "KlipperRestart": "Перезапуск Klipper призведе до зупинки поточного друку!", - "KlipperStop": "Зупинення Кліппера призведе до зупинки поточного друку! ", + "KlipperStop": "Зупинення Klipper призведе до зупинки поточного друку! ", "ServiceRestart": "Перезапуск цього сервісу може призвести до виходу з ладу поточного друку!", - "ServiceStart": "Запуск цього сервіса може призвести до виходу з ладу поточного друку!", - "ServiceStop": "Зупинення цього сервіса може призвести до виходу з ладу поточного друку!" + "ServiceStart": "Запуск цього сервісу може призвести до виходу з ладу поточного друку!", + "ServiceStop": "Зупинення цього сервісу може призвести до виходу з ладу поточного друку!" }, "Title": { "HostReboot": "Перезавантаження хоста", @@ -107,6 +122,17 @@ "UpdatingDone": "Оновлення {software} виконано!" } }, + "BedScrews": { + "Abort": "перервати", + "Accept": "прийняти", + "Adjusted": "відрегульований", + "Description": "Натисніть ADJUSTED, якщо поточний гвинт було відрегульовано. Натисніть ACCEPT, щоб продовжити без коригування.", + "Headline": "Гвинти столу", + "ScrewAccepted": "Готові гвинти", + "ScrewIndex": "Індекс гвинта", + "ScrewName": "Ім'я гвинта", + "ScrewOutput": "{current} з {max}" + }, "ConnectionDialog": { "CannotConnectTo": "Не вдається підключитися до Moonraker ({host}).", "CheckMoonrakerLog": "Якщо це повідомлення з’являється неодноразово, будь ласка, подивіться у файл журналу, розташований за адресою:", @@ -123,10 +149,25 @@ "SendCode": "Надіслати код...", "SetupConsole": "Консоль налаштування" }, + "DevicesDialog": { + "CanBusInfo": "Можна виявити лише непризначені вузли. Рекомендується мати лише один непризначений пристрій, підключений до шини can, щоб уникнути проблем зі зв’язком. Щоб отримати детальнішу інформацію, натисніть на посилання:", + "ClickRefresh": "Натисніть кнопку оновити, щоб знайти пристрої.", + "DevicePath": "Шлях пристрою", + "Formats": "Формати", + "Headline": "Пристрої", + "HideSystemEntries": "Приховати системні записи", + "LibcameraId": "Libcamera ID", + "NoDeviceFound": "Пристрій не знайдено. Перевірте підключення та натисніть кнопку оновити.", + "PathByHardware": "Фізичний шлях", + "PathById": "Шлях за ID", + "Refresh": "оновити", + "Resolutions": "Резолюції" + }, "Dialogs": { "StartPrint": { "Cancel": "Скасувати", "DoYouWantToStartFilename": "Ви хочете роздрукувати {filename}?", + "DoYouWantToStartFilenameFilament": "Хочете почати друк {filename} з наявного філаменту?", "Headline": "Почати друк", "Print": "друк", "Timelapse": "Таймлапс" @@ -134,6 +175,7 @@ }, "Editor": { "ConfigReference": "Довідка Конфігурації", + "DeviceDialog": "Пристрої", "DontSave": "Не зберігати", "Downloading": "Завантаження", "FailedSave": "{filename} не вдалося завантажити!", @@ -149,15 +191,17 @@ "EmergencyStopDialog": { "AreYouSure": "Ви впевнені?", "EmergencyStop": "АВАРІЙНА ЗУПИНКА", - "No": "НІТ", + "No": "НІ", "Yes": "ТАК" }, "Files": { + "AddBatchToQueue": "Додати партію в чергу", "AddToQueue": "Додати в чергу", "AllFiles": "ВСІ", "BedTemp": "t° Столу.", "Cancel": "Скасувати", "ChamberTemp": "t° Камери.", + "Count": "Кількість", "Create": "Створити", "CreateNewDirectory": "Створити нову теку", "CurrentPath": "Директорія", @@ -165,11 +209,13 @@ "DeleteDirectory": "Видалити теку", "DeleteDirectoryQuestion": "Ви дійсно хочете видалити \"{name}\" теку із усім її вмістом?", "DeleteSelectedQuestion": "Ви дійсно хочете видалити {count} обрані файли?", + "DeleteSingleFileQuestion": "Ви дійсно хочете видалити файл \"{name}\"?", "Download": "Завантажити", + "Duplicate": "Дублікат", + "DuplicateFile": "Дублікат файлу", "EditFile": "Редагувати Файл", "Empty": "Порожньо", "ExtruderTemp": "t° Екструдеру.", - "Filament": "Пруток", "FilamentName": "Назва Прутка", "FilamentType": "Тип Прутка", "FilamentUsage": "Використано Прутка", @@ -181,6 +227,8 @@ "GCodeFiles": "Файли G-Code", "GcodesRootDirectoryDoesntExists": "Жодного каталогу G-коду не знайдено. Будь ласка, перевірте опцію \"path\" in the [virtual_sdcard] розділі конфігурації Klipper.", "HiddenFiles": "Приховані файли", + "InvalidNameAlreadyExists": "Ім'я вже існує, виберіть інше ім'я.", + "InvalidNameEmpty": "Поле не повинне бути порожнім!", "LastEndTime": "Закінчено", "LastFilamentUsed": "Використано прутку", "LastModified": "Завантажено", @@ -196,10 +244,12 @@ "PrintedFiles": "Надруковані Файли", "PrintStart": "Почати Друк", "PrintTime": "Час Друку", - "RefreshCurrentDirectory": "Оновити поточну дирикторію", + "RefreshCurrentDirectory": "Оновити поточну директорію", "Rename": "Перейменувати", "RenameDirectory": "Перейменувати директорію", "RenameFile": "Перейменувати Файл", + "ScanMeta": "Сканувати метадані", + "ScanMetaSuccess": "Успішно проскановано метадані з: {filename}", "Search": "Пошук", "SetupCurrentList": "Поточний список налаштування", "Slicer": "Слайсер", @@ -219,6 +269,7 @@ }, "GCodeViewer": { "ClearLoadedFile": "Очистити", + "CNCMode": "Режим ЧПД", "ColorMode": "Кольоровий Режим", "Downloading": "Завантаження", "ForceLineRendering": "Примусова лінійна Візуалізація", @@ -229,9 +280,10 @@ "Low": "Низько", "Max": "Максимум", "Medium": "Середньо", - "ReloadRequired": "Потрібно перезавантаження", + "ReloadRequired": "Потрібне перезавантаження", "Rendering": "Візуалізація", "RenderQuality": "Якість Візуалізації", + "ShowGCode": "Показати G-Code", "ShowObjectSelection": "Показати Вибір Об'єктів", "ShowToolhead": "Показати Інструмент", "ShowTravelMoves": "Показати Вільні Переміщення", @@ -253,8 +305,8 @@ "Max": "Max", "Min": "Min", "Name": "Ім'я", - "Size": "Розмір", - "Variance": "Розбіжність" + "Range": "Діапазон", + "Size": "Розмір" }, "DeleteBedMeshProfile": "Видалити профіль сітки для столу", "DoYouReallyWantToDelete": "Ви дійсно хочете видалити профіль \"{name}\"?", @@ -262,36 +314,36 @@ "Flat": "Плоский", "Heightmap": "Карта", "InvalidNameAlreadyExists": "Ім'я профілю вже існує, будь ласка, виберіть інше ім'я профілю.", + "InvalidNameAscii": "Ім'я недійсне. Дозволяється лише символи ascii.", "InvalidNameEmpty": "Введення не повинно бути порожнім!", "InvalidNameReserved": "Profile 'default' зарезервовано, будь ласка, виберіть інше ім'я профілю.", - "Later": "Згодом", "Mesh": "Сітка", "Name": "Ім'я", "NoBedMeshHasBeenLoadedYet": "Жодна сітка для ліжка ще не була завантажена.", "NoProfile": "Немає профілю", - "Ok": "OK", "Probed": "Зондований", "Profiles": "Профілі", "Remove": "видалити", - "RemoveSaveDescription": "Профіль BED_MESH зареєстрований як видалений. Клацніть на SAVE_CONFIG, щоб видалити його з printer.cfg та перезапустіть Klipper.", "Rename": "перейменувати", "RenameBedMeshProfile": "Перейменуйте профіль сітки для столу", - "SAVE_CONFIG": "SAVE_CONFIG", "ScaleGradient": "Градієнт Масштабу", "ScaleZMax": "Масштаб Z-Max.", "TitleCalibrate": "Калібрування нової сітки столу", - "TitleClear": "Очистити сітку калібровки", + "TitleClear": "Очистити сітку калібрування", "TitleHomeAll": "Всі до Дому", "Wireframe": "Каркас" }, "History": { "AddNote": "Додати коментар", + "AddToQueueSuccessful": "Файл {filename} додано до черги.", "AllJobs": "ВСІ", "AvgPrinttime": "Час Друку - Ø", "Cancel": "Скасувати", + "Chart": "Графік", "CreateNote": "Створити Примітку", "Delete": "Видалити", - "DeleteSelectedQuestion": "Ви дійсно хочете видалити {count} вибрану роботу?", + "DeleteSelectedQuestion": "Ви дійсно хочете видалити {count} вибране завдання?", + "DeleteSingleJobQuestion": "Ви справді хочете видалити завдання?", "Details": "Деталі", "EditNote": "Редагувати Примітку", "Empty": "порожньо", @@ -300,7 +352,7 @@ "EstimatedFilamentWeight": "Орієнтовна вага прутка", "EstimatedTime": "Орієнтовний Час", "FilamentCalc": "Калькулятор Прутка", - "FilamentUsage": "Використаня Прутка", + "FilamentUsage": "Використання Прутка", "FilamentUsed": "Використано Прутка", "Filename": "Ім'я", "Filesize": "Розмір", @@ -309,22 +361,23 @@ "FirstLayerHeight": "Висота першого шару", "HistoryFilamentUsage": "Пруток", "HistoryPrinttimeAVG": "Друк", - "JobDetails": "Деталі Роботи", - "Jobs": "Робота", + "JobDetails": "Деталі Завдань", + "Jobs": "Завдання", "LastModified": "Дата Створення", "LayerHeight": "Висота Шару", + "LoadCompleteHistory": "Завантажити повну історію", "LongestPrinttime": "Найдовший Час Друку", "Note": "Примітка", "ObjectHeight": "Висота Об'єкта", "PrintDuration": "Час Друку", - "PrintHistory": "Історія Роздруківок", + "PrintHistory": "Історія друку", "PrintTime": "Час Друку", "PrinttimeAvg": "Час Друку - Ø", "Reprint": "Передрукувати", - "Save": "зберігти", + "Save": "зберегти", "Search": "пошук", - "SelectedFilamentUsed": "Вибран використовуваний пруток", - "SelectedJobs": "Вибрані Роботи", + "SelectedFilamentUsed": "Використано філаменту", + "SelectedJobs": "Вибрані завдання", "SelectedPrinttime": "Вибраний Час Друку", "Slicer": "Слайсер", "SlicerVersion": "Версія Слайсера", @@ -336,28 +389,32 @@ "completed": "Закінчені", "error": "Помилкові", "in_progress": "Триває", + "interrupted": "Перерваний", "klippy_disconnect": "Klippy відключився", - "klippy_shutdown": "Помилка Кліпперу", + "klippy_shutdown": "Помилка Klipper", "Others": "Інші", "server_exit": "Вихід сервера" }, + "Table": "Таблиця", "TitleExportHistory": "Історія Експорту", - "TitleRefreshHistory": "Оновити Історію", - "TitleSettings": "Налаштування", "TotalDuration": "Загальний Час", - "TotalFilamentUsed": "Використано Прутка", - "TotalJobs": "Всі Роботи", + "TotalFilamentUsed": "Використано філаменту", + "TotalJobs": "Всі завдання", "TotalPrinttime": "Загальний Час Друку", "TotalTime": "Загальний Час" }, "JobQueue": { - "AllJobs": "Всі Роботи", + "Cancel": "Скасувати", + "ChangeCount": "Змінити кількість", + "Count": "Кількість", "Empty": "Порожньо", - "JobQueue": "Черга Робіт", - "Jobs": "Роботи", + "InvalidCountEmpty": "Поле не повинне бути порожнім!", + "InvalidCountGreaterZero": "Значення має бути більше 0!", + "JobQueue": "Черга завдань", "Pause": "Пауза", "RemoveFromQueue": "Видаліть з Черги", - "Start": "Почати" + "Start": "Почати", + "StartPrint": "Почати завдання" }, "Machine": { "ConfigFilesPanel": { @@ -373,7 +430,10 @@ "DeleteDirectory": "Видалити директорію", "DeleteDirectoryQuestion": "Видалити директорію \"{name}\" і весь її вміст?", "DeleteSelectedQuestion": "Видалити {count} вибрані елементи?", + "DeleteSingleFileQuestion": "Ви дійсно хочете видалити файл \"{name}\"?", "Download": "Завантажити", + "Duplicate": "Дублікат", + "DuplicateFile": "Дублікат файлу", "EditFile": "Редагувати файл", "Empty": "Порожньо", "Files": "Файл", @@ -404,15 +464,21 @@ "TRIGGERED": "ЗАМКНЕНО" }, "LogfilesPanel": { - "Logfiles": "Журнали" + "Accept": "прийняти", + "Cancel": "скасувати", + "Logfiles": "Журнали", + "Rollover": "Журнали переходів", + "RolloverDescription": "Виберіть, які журнали потрібно скинути:", + "RolloverToastFailed": "Перехідний журнал для \"{name}\": {message}", + "RolloverToastSuccessful": "Журнал для \"{name}\" успішно скинуто." }, "SystemPanel": { "Constants": "Константи", - "Cpu": "ЦПК", + "Cpu": "CPU", "HostDetails": "Деталі хоста", "LastStats": "Остання статистика", "Load": "Навантаження", - "Memory": "ОЗУ", + "Memory": "RAM", "NoMoreInfos": "Більше немає Infos", "SystemLoad": "Навантаження системи", "Values": { @@ -421,8 +487,8 @@ "Distro": "Дистрибутив: {name} {version_id}", "Frequency": "Частота: {frequency}", "Load": "Навантаження: {load}", - "Memory": "ОЗУ: {memory}", - "Os": "Операційка: {os}", + "Memory": "RAM: {memory}", + "Os": "Операційна Система: {os}", "Received": "Отримано: {received}", "Temp": "t° проц.: {temp}°C", "TempMax": "макс: {temp}°C", @@ -432,38 +498,68 @@ } }, "UpdatePanel": { + "Abort": "Перервати", + "AreYouSure": "Ти впевнений?", "CheckForUpdates": "Перевірити оновлення", - "Commits": "Коментар", - "CommitsAvailable": "Немає коментарів | {count} доступний коментар | {count} Доступні", - "CommitsOnDate": "Коментар від {date}", - "CommittedDaysAgo": "Прокоментовано {days} днів тому", - "CommittedHoursAgo": "Прокоментовано {hours} годин тому", - "CommittedOnDate": "Прокоментовано {date}", - "CommittedYesterday": "Сьогоднішній комент", + "Close": "Закрити", + "CommitHistory": "Історія комітів", + "Commits": "Коміт", + "CommitsAvailable": "Немає комітів | {count} доступний коміт | {count} доступних комітів", + "CommitsOnDate": "Коміт від {date}", + "CommittedDaysAgo": "Коміт {days} днів тому", + "CommittedHoursAgo": "Коміт {hours} годин тому", + "CommittedOnDate": "Коміт {date}", + "CommittedYesterday": "Коміт вчорашній", + "ConfigChanges": "Зміни конфігурації", + "Corrupt": "пошкоджений", + "CountPackagesCanBeUpgraded": "{count} пакетів можна оновити", "Detached": "відокремлений", "Dirty": "брудний", - "ERROR": "ПОМИЛКА", + "GenericUpdateQuestion": "Перевірте історію комітів (якщо доступна) і сторінку GitHub для цього пакету, щоб дізнатися, чи потрібні якісь ручні налаштування для цього оновлення.", + "GitHubPage": "Сторінка GitHub", + "HardRecovery": "Повне скидання", + "InitUpdateManager": "Менеджер оновлень ще не ініціалізовано. Це нормально, коли ви запускаєте систему вперше. Натисніть кнопку оновити, щоб ініціалізувати всі компоненти.", "Invalid": "недійсний", + "IUnderstandTheRisks": "Я розумію ризики", + "KlipperUpdateQuestionConfig": "Це оновлення також може містити зміни в параметрах конфігурації, які потрібно буде змінити у файлі printer.cfg, подробиці дивіться в журналі змін.", + "KlipperUpdateQuestionFirmware": "Це призведе до оновлення програмного забезпечення Klipper хоста. Плати керування (MCU), на яких працює мікропрограмне забезпечення Klipper, можливо, потребуватиме перепрошивки за допомогою перекомпільованого мікропрограмного забезпечення, перш ніж принтер можна буде знову ввести в експлуатацію.", + "LinkToGithub": "Посилання на GitHub", + "MoonrakerUpdateQuestion": "Це оновить Moonraker API. Для продовження використання принтера можуть знадобитися зміни у файлі moonraker.conf.", + "MoreCommitsInfo": "Тут відображено максимум 30 комітів. Щоб переглянути всі коміти, перейдіть за посиланням:", "OSPackages": "ОС-Пакети", - "PackagesCanBeUpgraded": "пакети можна оновити", + "SoftRecovery": "Часткове відновлення", + "StartUpdate": "Почати оновлення", "System": "Система", + "ThesePackagesCanBeUpgrade": "Ці системні пакети можна оновити:", "Unknown": "невідомо", "Update": "оновити", - "UpdateAll": "Оновіть усі компоненти", + "UpdateAll": "Оновити всі", "UpdateManager": "Менеджер Оновлень", + "UpdateWarning": "Попередження про оновлення: {name}", "Upgrade": "оновити", - "UpToDate": "актуальний" + "UpgradeableSystemPackages": "Системні пакети що можна оновити", + "UpToDate": "актуальний", + "WebClientUpdateQuestion": "У деяких випадках оновлення веб-клієнт має зміни, які можуть спричинити несумісність. Для отримання додаткової інформації див. примітки до релізу." } }, + "ManualProbe": { + "Abort": "перервати", + "Accept": "прийняти", + "Advanced": "Просунутий", + "Headline": "Проба вручну" + }, "Panels": { "ExtruderControlPanel": { "Allowed": "Допущений", + "CleanNozzle": "Очистити сопло", "EstimatedExtrusion": "Розрахункова екструзія:", "Extrude": "Видавити", + "ExtruderControl": "Керування екструдером", "ExtruderTempTooLow": " t° екструдера. <", "ExtrusionFactor": "Коефіцієнт ЕКСТРУЗІЇ", "ExtrusionFeedrate": "Екструзійна подача", - "FilamentLength": "Продавити на", + "FilamentLength": "Довжина філаменту", + "FirmwareRetraction": "Програмний ретракт", "FirmwareRetractionSettings": { "RetractLength": "Довжина Ретракту", "RetractSpeed": "Швидкість Ретракту", @@ -472,41 +568,50 @@ }, "Headline": "Екструдер", "LoadFilament": "Завантажити Пруток", + "PressureAdvance": "Pressure Advance", "PressureAdvanceSettings": { "Advance": "Pressure Advance", "Extruder": "Екструдер", "SmoothTime": "Плавний Час" }, - "Requested": "Запитуваний", + "PurgeFilament": "Purge філаменту", + "Requested": "Заданий", "Retract": "Ретракт", "TooLargeExtrusion": "Екструзія занадто велика!", - "UnloadFilament": "Вигрузка Прутка" + "Tools": "Інструменти", + "UnloadFilament": "Вивантаження філаменту" }, "FarmPrinterPanel": { - "ReconnectToPrinter": "Переподключення", + "ReconnectToPrinter": "Перепідключення", "SwitchToPrinter": "Перемкнутися на Принтер", "WebcamOff": "ВИМК" }, "KlippyStatePanel": { + "CheckKlippyAndUdsAddress": "Будь ласка, перевірте, чи працює служба Klipper і чи правильно налаштовано klippy_uds_address у файлі moonraker.conf.", "FirmwareRestart": "Перезапуск Прошивки", - "KlipperCheck": "Будь ласка, перевірте, чи працює служба Klipper та налаштована UDS (UNIX домен).", "MoonrakerCannotConnect": "Moonraker не може підключитися до Klipper!", - "Restart": "Перезапустити" + "PowerOn": "Увімкнено", + "PrinterSwitchedOff": "Принтер вимкнений", + "PrinterSwitchedOffDescription": "Принтер наразі вимкнено, і Klipper не може підключитися. Щоб увімкнути принтер, натисніть кнопку нижче:", + "Restart": "Перезапустити", + "ServiceReports": "{service} повідомляє" }, "MachineSettingsPanel": { "Headline": "Машина", "MotionSettings": { "Acceleration": "Прискорення", - "MaxAccelToDecel": "Max Accel. to Decel.", + "MaxAccelToDecel": "Макс. прискорення до сповільнення.", + "MinimumCruiseRatio": "Мін. коефіцієнт руху", "SquareCornerVelocity": "Квадратна кутова швидкість", "Velocity": "Швидкість" } }, "MacrosPanel": { "Headline": "Макрос", - "Send": "надсилати" + "Send": "відправити" }, "MiniconsolePanel": { + "Autoscroll": "Автопрокручування", "Headline": "Консоль", "HideTemperatures": "Сховати температуру", "HideTimelapse": "Сховати таймлапс", @@ -521,26 +626,57 @@ }, "MiscellaneousPanel": { "Headline": "Різне", + "Light": { + "Blue": "блакитний", + "Green": "зелений", + "Red": "червоний", + "White": "білий" + }, "RunoutSensor": { "Detected": "виявлено", - "Disabled": "Не активно", "Empty": "Порожньо" } }, "PowerControlPanel": { "Error": "Помилка", "Off": "ВИМК", - "On": "ВВІМК", + "On": "УВІМК", "PowerControl": "Управління Живленням" }, + "SpoolmanPanel": { + "Cancel": "Скасувати", + "ChangeSpool": "Зміна шпулі", + "DaysAgo": "{days} днів тому", + "EjectSpool": "Прибрати шпулю", + "EjectSpoolQuestion": "Ви впевнені, що хочете прибрати шпулю з філаментом?", + "Filament": "Філамент", + "FilamentTypeMismatch": "Матеріал активної шпулі ({spoolType}) не збігається з матеріалом G-коду ({fileType}).", + "Headline": "Spoolman", + "LastUsed": "Останнє використання", + "Location": "Місцезнаходження", + "Material": "Матеріал", + "Never": "Ніколи", + "NoActiveSpool": "Відстеження філаменту неактивне. Щоб почати, будь ласка, виберіть котушку.", + "NoResults": "За поточними критеріями пошуку шпулю не знайдено.", + "NoSpools": "Немає шпуль", + "NoSpoolSelected": "Шпулю не вибрано. Виберіть шпулю, інакше цей друк не відстежуватиметься.", + "OpenSpoolManager": "відкрити менеджер шпуль", + "Refresh": "оновити", + "Search": "Пошук", + "SelectSpool": "Виберіть шпулю", + "Today": "Сьогодні", + "TooLessFilament": "У поточній котушці може бути недостатньо філаменту для цього друку. ({spoolWeight}г з {fileWeight}г)", + "Weight": "Вага", + "Yesterday": "Вчора" + }, "StatusPanel": { "CancelPrint": "Скасувати друк", "ClearPrintStats": "Чітка статистика друку", "Difference": "Різниця", "EmptyGcodes": "Немає G-коду, що доступний.", - "EmptyJobqueue": "Наразі в черзі роботи немає файлу.", + "EmptyJobqueue": "Наразі в черзі завдань немає файлу.", "Estimate": "Оцінка", - "ETA": "Час Закінченя", + "ETA": "Час закінчення", "ExcludeObject": { "Cancel": "скасувати", "Excluded": "Виключений", @@ -548,21 +684,33 @@ "ExcludeObjectHeadline": "Виключіть Об'єкт", "ExcludeObjectText": "Ви дійсно хочете виключити \"{name}\"?" }, - "Filament": "Пруток", + "Filament": "Філамент", "File": "Файл", "Files": "Файли", "Flow": "Потік", "Headline": "Статус", - "Jobqueue": "Черга робіт", - "JobqueueMoreFiles": "більше нема робіт | ще одна робота | {count} більше робіт", + "Jobqueue": "Черга завдань", + "JobqueueMoreFiles": "більше нема робіт | ще одне завдання | {count} більше завдань", "Layer": "Шар", "Max": "макс", "ObjectHeight": "Висота Об'кта", + "PauseAtLayer": { + "Abort": "перервати", + "Accept": "прийняти", + "AtLayer": "на Шарі", + "Call": "Виклик", + "DescriptionPauseAtLayerActive": "Ця функція вже активна та викличе \"{call}\" на шарі {layer}. Якщо ви знову викличете команду, ці параметри буде перезаписано.", + "DescriptionPauseNextLayerActive": "Ця функція вже активна та викличе \"{call}\" на наступному шарі.", + "Layer": "Шар", + "NextLayer": "наступний шар", + "PauseAtLayer": "Пауза на шарі", + "Type": "Тип" + }, "PausePrint": "Призупинення друку", "Print": "Друк", "PrintTime": "Час Друку", "ReprintJob": "Роздруковано", - "Requested": "Запитуваний", + "Requested": "Встановлено", "ResumePrint": "Відновити друк", "Slicer": "Слайсер", "Speed": "Швидкість", @@ -581,7 +729,9 @@ "Target": "цільова температура", "Temperature": "поточна температура" }, - "Headline": "Температури", + "Headline": "Температура", + "HideMcuHostSensors": "Приховати датчики хоста/MCU", + "HideMonitors": "Приховати монітори", "Max": "макс", "Min": "мін", "Name": "Ім'я", @@ -599,18 +749,22 @@ "ToolheadControlPanel": { "Absolute": "абсолютні", "ALL": "ВСІ", + "ControlButtons": "Кнопки управління", + "CoordinateFields": "Поля координат", "Headline": "Інструмент", "PleaseConfigureSteps": "Будь ласка, налаштуйте кроки", "Position": "Положення", + "PositionOutput": "Розташування", "QGL": "QGL", "Relative": "відносні", "SettingsInterfaceControl": "Налаштування > Інтерфейс > Контроль", - "SpeedFactor": "Коефіцієнт ШВИДКОСТІі", + "SpeedFactor": "Коефіцієнт ШВИДКОСТІ", + "ZOffset": "Z-Offset", "ZTilt": "Z-Нахил" }, "WebcamPanel": { "All": "ВСІ", - "FPS": "ФПС", + "FPS": "FPS", "Headline": "Веб-камера", "NoWebcam": "Немає веб-камери. Додайте веб -камеру \"Налаштування Інтерфейсу\" -> \"Веб-камери\".", "UnknownWebcamService": "Невідома служба веб-камери" @@ -618,22 +772,20 @@ "ZoffsetPanel": { "Clear": "Очистити", "Headline": "Z-Offset", - "Later": "Згодом", + "Later": "Пізніше", "Ok": "OK", "Save": "Зберегти", - "SaveConfig": "SAVE CONFIG", - "SaveInfoDescription": "Новий Z-Offset був обчислений та зареєстрований. Натисніть на \"SAVE CONFIG\" Щоб зберегти його на printer.cfg та перезапустити Klipper.", - "SaveInfoDescriptionPrint": "Новий Z-Offset був обчислений та зареєстрований. Після друку натисніть на \"SAVE CONFIG\" у верхній панелі, щоб зберегти його у printer.cfg та перезапустити Klipper.", - "SaveInfoHeadline": "Інформація", - "ToEndstop": "до Кінцевика", - "ToProbe": "Зондувати" + "SaveConfig": "ЗБЕРЕГТИ КОНФІГУРАЦІЮ", + "SaveInfoDescription": "Новий Z-Offset був обчислений та зареєстрований. Натисніть на \"ЗБЕРЕГТИ КОНФІГУРАЦІЮ\" Щоб зберегти його на printer.cfg та перезапустити Klipper.", + "SaveInfoDescriptionPrint": "Новий Z-Offset був обчислений та зареєстрований. Після друку натисніть на \"ЗБЕРЕГТИ КОНФІГУРАЦІЮ\" у верхній панелі, щоб зберегти його у printer.cfg та перезапустити Klipper.", + "SaveInfoHeadline": "Інформація" } }, "PowerDeviceChangeDialog": { "AreYouSure": "Ви впевнені?", - "No": "НІТ", + "No": "НІ", "TurnDeviceOff": "Поворот {device} ВИМК", - "TurnDeviceOn": "Поворот {device} ВВІМК", + "TurnDeviceOn": "Поворот {device} УВІМК", "Yes": "ТАК" }, "Router": { @@ -648,6 +800,13 @@ "Timelapse": "Таймлапс", "Webcam": "Веб-камера" }, + "ScrewsTiltAdjust": { + "Accept": "прийняти", + "Base": "Основа", + "ErrorText": "Щось пішло не так під час процесу взяття проби.", + "Headline": "Гвинти регулювання нахилу", + "Retry": "повторити спробу" + }, "SelectPrinterDialog": { "AddPrinter": "Додати Принтер", "AddPrintersToJson": "Будь ласка, додайте принтери до config.json.", @@ -659,7 +818,7 @@ "Hello": "Привіт і ласкаво просимо до віддаленого режиму Mainsail!", "HostnameInvalid": "Недійсне ім'я хоста/IP", "HostnameIp": "Ім'я хоста/IP", - "HostnameRequired": "Ім'я хоста потрібно", + "HostnameRequired": "Необхідне ім'я хоста", "Port": "Порт", "PortRequired": "Потрібен порт", "RememberToAdd": "Будь ласка, не забудьте додати '{cors}' у moonraker.conf всередині 'cors_domains'.", @@ -676,10 +835,10 @@ "Console": "Консоль", "CreateHeadline": "Створити фільтр", "Direction": "Напрямок", - "DirectionShell": "Останній запис унизу", - "DirectionTable": "Останній запис у верхній частині", + "DirectionShell": "Останній запис внизу", + "DirectionTable": "Останній запис звурху", "EditHeadline": "Редагувати фільтр", - "EntryStyle": "Entry-Дизайн", + "EntryStyle": "Початковий дизайн", "EntryStyleCompact": "компактний", "EntryStyleDefault": "за замовчуванням", "Filters": "Фільтри", @@ -692,13 +851,14 @@ "UpdateButton": "Оновити фільтр" }, "ControlTab": { - "Bars": "Брус", - "Circle": "Кола", + "Bars": "Вкладка", + "Circle": "Коло", "Control": "Контроль", "Cross": "Перехрестя", "EnableXYHoming": "Увімкнути комбіноване наведення вісь X і Y до дому", "EstimatedExtrusionInfo": "Показати орієнтовну інформацію про екструзію", "EstimatedExtrusionInfoDescription": "Показати / приховати інформацію про орієнтовні екструзії на основі кількості екструзії та подачі", + "HideDuringPrint": "Приховати елементи керування віссю під час друку", "InvertXMovement": "Інвертуйте напрямок руху осі X", "InvertYMovement": "Інвертувати напрямок руху осі Y", "InvertZMovement": "Інвертувати напрямок руху осі Z", @@ -713,11 +873,13 @@ "MoveDistancesZInMm": "Дистанція переміщення вісі Z у мм", "QuadGantryLevel": "Quad Gantry Вирівнювання{isDefault}", "SpeedEInMms": "Швидкість екструзії (у мм/с)", - "SpeedXY": "Швидкість руху вісєй X та Y", + "SpeedXY": "Швидкість руху вісі X та Y", "SpeedZ": "Швидкість руху вісі Z", "Style": "Стиль", "ValueGreaterThan": "Значення повинно бути більшим, ніж {value}", "ZOffsetIncrements": "Збільшення Z-Offset (в мм)", + "ZOffsetSaveOption": "Опція збереження Z-Offset", + "ZOffsetSaveOptionDescription": "Змініть параметр, щоб зберегти Z-Offset", "ZTiltAdjust": "Налаштування Z-нахилу{isDefault}" }, "DashboardTab": { @@ -730,13 +892,16 @@ }, "Edit": "Редагувати", "EditorTab": { - "ConfirmUnsavedChanges": "Підказувати зберігати чи ніт не збережені зміни", + "ConfirmUnsavedChanges": "Підказувати зберігати чи ні не збережені зміни", "ConfirmUnsavedChangesDescription": "Якщо ввімкнено, редактор вимагає підтвердження, щоб зберегти, або відкинути внесені зміни. Якщо відключено, зміни не зберігаються.", "Editor": "Редактор", "KlipperRestartMethod": "Метод перезапуску Klipper", "KlipperRestartMethodDescription": "Виберіть, який метод перезавантаження буде використовуватися на 'Зберегти та перезапустити' під час редагування файлів конфігурації Klipper..", - "UseEscToClose": "Використовуйте ESC для закриття редактора", - "UseEscToCloseDescription": "Дозволяє клавішу ESC закрити редактор" + "Spaces": "Пробіли: {count}", + "TabSize": "Розмір відступів", + "TabSizeDescription": "Регулює кількість пробілів для табуляції", + "UseEscToClose": "ESC закриває редактор", + "UseEscToCloseDescription": "Дозволити клавішу ESC для закриття редактора" }, "GCodeViewerTab": { "BackgroundColor": "Колір фону", @@ -746,9 +911,11 @@ "MaxFeed": "Максимальна швидкість подачі", "MinFeed": "Мінімальна швидкість подачі", "ProgressColor": "Колір прогресу", - "ShowAxes": "Показати осі" + "ShowAxes": "Показати вісі" }, "GeneralTab": { + "12hours": "12 годин ({time})", + "24hours": "24 години ({time})", "Backup": "Резервна копія", "BackupDialog": "Виберіть усі розділи, які ви хочете створити резервну копію:", "CalcEstimateTime": "Оцінка розрахунку часу", @@ -763,27 +930,42 @@ "FileRelative": "Позиція файлу (відносна)", "Slicer": "Слайсер (M73)" }, - "CannotReadJson": "Не вдається читати/розбирати резервний файл.", + "CannotReadJson": "Не вдається читати/розібрати резервний файл.", + "DateFormat": "Формат дати", "DbConsoleHistory": "Історія Консолі", - "DbHistoryJobs": "Історія роботи", + "DbHistoryJobs": "Історія завдань", "DbHistoryTotals": "Загальна Історія", + "DBNavigation": "Навігація", "DbTimelapseSettings": "Налаштування Таймлапсу", "DbView": "Налаштування перегляду", - "DbWebcams": "Веб -камери", "EstimateValues": { "Filament": "Пруток", "File": "Файл", "Slicer": "Слайсер" }, + "Everything": "Все", "FactoryDialog": "Будь ласка, виберіть усі розділи, які ви хочете скинути:", "FactoryReset": "Заводські налаштування", "General": "Загальний", "Language": "Мова", - "MoonrakerDb": "Moonraker DB", + "MainsailSettingsMoonrakerDb": "Параметри Mainsail в БД Moonraker", "PrinterName": "Ім'я принтера", "Reset": "скинути", "Restore": "Відновити", - "RestoreDialog": "Будь ласка, виберіть усі розділи, які ви хочете відновити:" + "RestoreDialog": "Будь ласка, виберіть усі розділи, які ви хочете відновити:", + "TimeFormat": "Формат часу" + }, + "HeightmapTab": { + "ColorSchemes": "Палітри кольорів", + "Heightmap": "Карта висот", + "IsDefault": "(за замовчуванням)", + "Schemes": { + "GrayScale": "Відтінки сірого", + "Hot": "Теплота", + "Hsv": "Hsv", + "Portland": "Портленд", + "Spring": "Весна" + } }, "InterfaceSettings": "Налаштування інтерфейсу", "MacrosTab": { @@ -792,7 +974,7 @@ "AvailableMacros": "Доступні Макроси", "ChangeMacroColor": "Змініть колір кнопки.", "Color": "Колір", - "CountMacros": "Жодних макросів не додано | {count} Макрос | {count} Макроси", + "CountMacros": "Жодних макросів не додано | {count} Макрос | {count} Макросів", "Custom": "свій", "CustomColor": "Свій Колір", "DeletedMacro": "Видалений макрос", @@ -813,20 +995,46 @@ "NoMacrosInGroup": "У цій групі немає макросів.", "Primary": "основний", "Secondary": "вторинний", - "ShowInStatePaused": "Показати/приховати, якщо принтер призупинений.", - "ShowInStatePrinting": "Показати/приховати, якщо принтер друкує.", - "ShowInStateStandby": "Показати/приховати, якщо принтер буде в режимі очікування.", + "ShowInStatePaused": "Показати/приховати, коли принтер призупинений.", + "ShowInStatePrinting": "Показати/приховати, коли принтер друкує.", + "ShowInStateStandby": "Показати/приховати, коли принтер в режимі очікування.", "Simple": "Простий", "Status": "Статус", "Success": "успіх", "UnknownGroup": "Невідома Група", "Warning": "УВАГА" }, + "MiscellaneousTab": { + "AddGroup": "додати групу", + "AddPreset": "додати предустановку", + "Color": "Колір", + "CreateGroup": "Створити групу", + "CreatePreset": "Створити предустановку", + "End": "Кінець", + "EndDescription": "Останній світлодіод цієї групи.", + "Groups": "Групи", + "GroupSubTitle": "Початок: {start}, кінець: {end}", + "LightGroups": "{name} - Групи", + "LightPresets": "{name} - Предустановки", + "Miscellaneous": "Різне", + "Name": "Ім'я", + "NoDevicesFound": "Пристроїв не знайдено", + "NoGroupFound": "Групи не знайдено", + "NoPresetFound": "Предустановки не знайдено", + "Presets": "Предустановки", + "Start": "Cтарт", + "StartDescription": "Перший світлодіод цієї групи.", + "UnableToLoadLight": "Не вдалося завантажити світло", + "UnableToLoadPreset": "Неможливо завантажити предналаштування" + }, + "NavigationTab": { + "Navigation": "Навігація" + }, "PresetsTab": { "AddPreset": "додати пресет", "Cooldown": "ОХОЛОДЖЕННЯ Нагрівачів", "CreateHeadline": "Створити пресет", - "CustomGCode": "Свій Г-код", + "CustomGCode": "Свій G-код", "EditCooldown": "Редагувати ОХОЛОДЖЕННЯ", "EditHeadline": "Редагувати пресет", "ErrorInvalidValue": "Недійсне значення", @@ -849,6 +1057,7 @@ "UpdatePrinter": "Оновити принтер", "UseConfigJson": "InstanceDB = JSON виявлений. Будь ласка, використовуйте config.json щоб змінити список принтерів." }, + "Store": "зберегти", "TimelapseTab": { "Autorender": "Автовізуалізація", "AutorenderDescription": "Якщо ввімкнено, відео Таймлапс автоматично відображатиметься в кінці друку", @@ -894,6 +1103,11 @@ "RetractDistanceDescription": "Довжина прутка, яку екструдер втягує.", "RetractSpeed": "Швидкість втягування", "RetractSpeedDescription": "Швидкість, з якою екструдер відтягує пруток.", + "RulesBetweenMinMax": "Значення має бути від {min} до {max}!", + "RulesMin": "Значення має бути не менше {min}!", + "RulesPositive": "Значення не має бути від'ємним!", + "RulesRequired": "Необхідно вказати значення!", + "RulesZeroAndPositive": "Значення має бути 0 або більше!", "SaveFrames": "Зберегти зображення", "SaveFramesDescription": "Збережіть кадри у zip-файлі для зовнішнього відображення", "StreamDelayCompensation": "Компенсація затримки потоку", @@ -911,12 +1125,15 @@ "UnretractSpeedDescription": "Швидкість, з якою екструдер подає пруток після втягування.", "VariableFps": "Змінна FPS", "VariableFpsDescription": "Якщо ввімкнено, кадри вихідного відео буде обчислено на основі довжини відео", - "VariableFpsMax": "Змінна FPS макс", - "VariableFpsMaxDescription": "", - "VariableFpsMin": "Змінна FPS мін", - "VariableFpsMinDescription": "" + "VariableFpsMax": "Динамічний FPS макс", + "VariableFpsMaxDescription": "Максимальне значення при динамічному FPS", + "VariableFpsMin": "Динамічний FPS мін", + "VariableFpsMinDescription": "Мінімальне значення при динамічному FPS" }, "UiSettingsTab": { + "BedScrewsDialog": "Вікно гвинтів стола", + "BedScrewsDialogDescription": "Відобразити допоміжне діалогове вікно для BED_SCREWS_ADJUST.", + "BigThumbnailBackground": "Колір тла великої мініатюри", "BoolBigThumbnail": "Велика мініатюра", "BoolBigThumbnailDescription": "Відобразити велику мініатюру на панелі статусу під час друку.", "BoolHideUploadAndPrintButton": "Приховати кнопку завантаження та друк", @@ -925,40 +1142,67 @@ "ConfirmOnEmergencyStopDescription": "Показати діалогове вікно підтвердження АВАРІЙНОЇ ЗУПИНКИ", "ConfirmOnPowerDeviceChange": "Вимагати підтвердження змін живлення пристрою", "ConfirmOnPowerDeviceChangeDescription": "Показати діалогове вікно підтвердження щодо змін живлення пристрою", + "DefaultNavigationState": "Стандартний стан навігації", + "DefaultNavigationStateAlwaysClosed": "завжди закрита", + "DefaultNavigationStateAlwaysOpen": "завжди відкрита", + "DefaultNavigationStateDescription": "У якому стані має бути навігація за умовчанням.", + "DefaultNavigationStateLastState": "останній стан", + "DisableFanAnimation": "Вимкнути анімацію кулера", + "DisableFanAnimationDescription": "Це може зменшити навантаження на ваш браузер.", "DisplayCANCEL_PRINT": "Відображати кнопку Скасування Друку", "DisplayCANCEL_PRINTDescription": "Показує кнопку CANCEL_PRINT постійно - не потрібно підтвердження другого рівня.", "GcodeThumbnails": "Ескіз G-Code", "GcodeThumbnailsDescription": "Клацніть на кнопку, щоб дістатися до інструкцій.", "Guide": "Інструкція", + "HideSaveConfigButtonForBedMesh": "Приховати кнопку SAVE_CONFIG для змін bed_mesh", + "HideSaveConfigButtonForBedMeshDescription": "Приховати SAVE_CONFIG, якщо тільки зміни bed_mesh очікують на збереження в Klipper.", + "HideUpdateWarnings": "Приховати попередження про оновлення", + "HideUpdateWarningsDescription": "Ця опція приховає всі попередження про оновлення в менеджері оновлень.", "LockSliders": "Заблокуйте повзунки на пристроях сенсорного екрану", "LockSlidersDelay": "Затримка блокування повзунка", "LockSlidersDelayDescription": "Слайдери заблокуються після заданої затримки. Якщо встановлено на 0 або залишився порожнім, повзунки лише заблокують зміну/перезавантаження сторінки.", "LockSlidersDescription": "Слайдери на сенсорному екрані потрібно розблокувати, перш ніж зміни будуть дозволені.", "Logo": "Колір Логотипу", + "ManualProbeDialog": "Вікно помічника ручної проби", + "ManualProbeDialogDescription": "Відобразити допоміжне діалогове вікно для PROBE_CALIBRATE або Z_ENDSTOP_CALIBRATE.", "NavigationStyle": "Стиль навігації", "NavigationStyleDescription": "Змінити зовнішній вигляд навігації", "NavigationStyleIconsAndText": "Піктограми + текст", "NavigationStyleIconsOnly": "Тільки іконки", + "PowerDeviceName": "Пристрій живлення принтера", + "PowerDeviceNameDescription": "Виберіть, який пристрій живлення Moonraker слід використовувати для живлення принтера.", "Primary": "Основний Колір", - "ShowWebcamInNavigation": "Показати веб -камеру в навігації", + "ScrewsTiltAdjustDialog": "Вікно налаштування гвинтів нахилу стола", + "ScrewsTiltAdjustDialogDescription": "Відобразити допоміжне вікно для SCREWS_TILT_CALCULATE.", + "TempchartHeight": "Висота графіку температур", + "TempchartHeightDescription": "Змініть висоту графіка температури на інформаційній панелі.", + "Theme": "Тема", + "ThemeDark": "Темна", + "ThemeDescription": "Змініть загальний вигляд програми", + "ThemeLight": "Світла", "UiSettings": "Налаштування в інтерфейсі" }, + "Update": "оновлення", "WebcamsTab": { "AddWebcam": "Додати веб-камеру", "CreateWebcam": "Створити веб-камеру", "EditCrowsnestConf": "Редагувати crowsnest.conf", "EditWebcam": "Редагувати веб-камеру", + "EnableAudio": "Увімкнути аудіо", "FlipWebcam": "Повернути веб-камеру:", + "HideFps": "Приховати лічильник FPS", + "Hlsstream": "Потік HLS", "Horizontally": "горизонтально", "IconBed": "Ліжко", "IconCam": "Cam", "IconDoor": "Двері", "IconFilament": "Пруток", "IconHot": "Гарячий", - "IconMcu": "ЦПК", + "IconMcu": "MCU", "IconNozzle": "Сопло", "IconPrinter": "Прінтер", - "Ipstream": "IP -камера", + "Ipstream": "IP-камера", + "JMuxerStream": "Необроблений потік h264 (jmuxer)", "Mjpegstreamer": "MJPEG-Стример", "MjpegstreamerAdaptive": "Adaptive MJPEG-Streamer (експериментальний)", "Name": "Ім'я", @@ -973,7 +1217,11 @@ "UrlStream": "URL-адреса Потоку", "Uv4lMjpeg": "UV4L-MJPEG", "Vertically": "вертикально", - "Webcams": "Веб-камери" + "Webcams": "Веб-камери", + "WebrtcCameraStreamer": "WebRTC (camera-streamer)", + "WebrtcGo2rtc": "WebRTC (go2rtc)", + "WebrtcJanus": "WebRTC (janus-gateway)", + "WebrtcMediaMTX": "WebRTC (MediaMTX)" } }, "Timelapse": { @@ -987,6 +1235,7 @@ "DeleteDirectory": "Видалити директорію", "DeleteDirectoryQuestion": "Ви дійсно хочете видалити \"{name}\" директоріб із усім її вмістом?", "DeleteSelectedQuestion": "Ви дійсно хочете видалити {count} вибрані файли?", + "DeleteSingleFileQuestion": "Ви дійсно хочете видалити файл \"{name}\"?", "Download": "Завантажити", "DuplicateLastframe": "Дублікат останнього кадру", "Empty": "Не знайдено закінченого Таймлапсу.", @@ -998,8 +1247,8 @@ "Framerate": "Частота кадрів", "Frames": "Кадри", "Free": "Вільно", - "FreeDisk": "Вільний диск", - "LastModified": "Датат Створення", + "FreeDisk": "Вільнр на диску", + "LastModified": "Дата Створення", "MaxFramerate": "Макс. частота кадрів", "MinFramerate": "Мін. частота кадрів", "Name": "Ім'я", diff --git a/src/locales/zh.json b/src/locales/zh.json index 9f5a29050..f33d907d1 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -3,28 +3,30 @@ "Notifications": { "BrowserWarnings": { "Description": "{name} 已过时且没有完整支持。当前版本是{version},但是Mainsail需要{minVersion}或更高版本。", - "Headline": "过时的浏览器" + "Headline": "浏览器已过时" }, "DependencyDescription": "当前版本的{name}并不支持Mainsail的全部功能。{name}需要至少更新到{neededVersion}版本。", "DependencyName": "依赖:{name}", - "DismissAll": "全部忽略", + "DismissAll": "忽略全部", "KlipperWarnings": { "DeprecatedOption": "在'{section}'标签中的'{option}'选项已被弃用,将在未来的版本中删除。", - "DeprecatedOptionHeadline": "已被弃用的Klipper选项", + "DeprecatedOptionHeadline": "Klipper选项已被弃用", "DeprecatedValue": "在'{section}'标签中的'{option}'选项的参数'{value}'已被弃用,将在未来的版本中删除。", - "DeprecatedValueHeadline": "已被弃用的Klipper参数", + "DeprecatedValueHeadline": "Klipper参数已被弃用", "KlipperWarning": "Klipper警告" }, "MoonrakerWarnings": { - "MoonrakerComponent": "Moonraker: {component}", - "MoonrakerFailedComponentDescription": "加载moonraker组件'{component}'时检测到错误。请检查日志文件并修复此问题。", + "MoonrakerComponent": "Moonraker:{component}", + "MoonrakerFailedComponentDescription": "加载moonraker组件'{component}'时发生错误。请检查日志文件并修复此问题。", + "MoonrakerFailedInitComponentDescription": "初始化moonraker组件'{component}'时发生错误。请检查日志文件并修复此问题。", + "MoonrakerInitComponent": "初始化 Moonraker:{component}", "MoonrakerWarning": "Moonraker警告", "UnparsedConfigOption": "在[{section}]标签中检测到无法解析的配置'{option}: {value}'。这可能是一个不再可用的选项,也可能是组件加载失败的结果。在将来,这将导致启动错误。", - "UnparsedConfigSection": "检测到无法解析的标签[{section}]。这可能是一个不再可用的标签,也可能是组件加载失败的结果。在将来,这将导致启动错误。" + "UnparsedConfigSection": "检测到无法解析的标签[{section}]。这可能是一个不再可用的标签,也可能是组件加载失败的结果。在未来,这将导致启动错误。" }, "Never": "永不", "NextReboot": "下次重启", - "NoNotification": "没有通知", + "NoNotification": "没有可用的通知", "Notifications": "通知", "Remind": "提醒:" }, @@ -33,7 +35,10 @@ "MustBeBetweenError": "必须在{min}和{max}之间!", "NoEmptyAllowedError": "输入值不能为空!" }, - "Printers": "打印机群组", + "Printers": "打印机列表", + "TextfieldWithCopy": { + "Copied": "复制" + }, "TheServiceWorker": { "DescriptionNeedUpdate": "本地缓存已过时,需要进行更新。请点击下面的按钮来更新缓存。", "TitleNeedUpdate": "PWA需要更新", @@ -42,15 +47,15 @@ "ThrottledStates": { "DescriptionCurrentlyThrottled": "树莓派ARM核心当前被限制。", "DescriptionFrequencyCapped": "树莓派ARM最高频率目前限制在1.2 GHz。", - "DescriptionPreviouslyFrequencyCapped": "自从开机以来树莓派ARM最大频率至少有一次被限制在1.2 GHz。", - "DescriptionPreviouslyTemperatureLimited": "自从开机以来树莓派uC(仅3A+/3B+)温度至少有一次超过软件限值(默认60C)。", - "DescriptionPreviouslyThrottled": "自从开机以来树莓派ARM核心至少有一次被限制。", - "DescriptionPreviouslyUnderVolted": "自从开机以来树莓派供电电压至少有一次低于4.65V。", - "DescriptionTemperatureLimitActive": "树莓派uC(仅3A+/3B+)温度目前超过软件限值(默认60C)。", + "DescriptionPreviouslyFrequencyCapped": "自开机以来树莓派ARM最大频率至少一次被限制在1.2 GHz。", + "DescriptionPreviouslyTemperatureLimited": "自开机以来树莓派uC(仅限3A+/3B+)温度至少一次超过软件限值(默认60C)。", + "DescriptionPreviouslyThrottled": "自开机以来树莓派ARM核心至少有一次被限制。", + "DescriptionPreviouslyUnderVolted": "自开机以来树莓派供电电压至少有一次低于4.65V。", + "DescriptionTemperatureLimitActive": "树莓派uC(仅限3A+/3B+)温度目前超过软件限值(默认60C)。", "DescriptionUnderVoltageDetected": "树莓派供电电压目前低于4.65V", "TitleCurrentlyThrottled": "受限提醒", "TitleFrequencyCapped": "频率上限提醒", - "TitlePreviouslyFrequencyCapped": "频率上限提醒", + "TitlePreviouslyFrequencyCapped": "频率已上限提醒", "TitlePreviouslyTemperatureLimited": "温度限制提醒", "TitlePreviouslyThrottled": "受限提醒", "TitlePreviouslyUnderVolted": "电压过低提醒", @@ -61,7 +66,7 @@ "Complete": "已完成 - {filename}", "Error": "错误", "Pause": "暂停打印", - "PrinterOff": "打印机已关闭", + "PrinterOff": "打印机关闭", "Printing": "已打印 {percent}% - {filename}", "PrintingETA": "已打印 {percent}% - 预估: {eta} - {filename}" }, @@ -118,19 +123,19 @@ } }, "BedScrews": { - "Abort": "取消", + "Abort": "中止", "Accept": "接受", "Adjusted": "已调整", - "Description": "如果当前螺丝已调整,请点击已调整。未调整下继续请点击认可。", + "Description": "如果当前螺丝已调整,请点击已调整。如果不需要调整,请点击接受继续。", "Headline": "热床调平螺丝", - "ScrewAccepted": "已认可的调平螺丝", - "ScrewIndex": "螺丝索引", + "ScrewAccepted": "已接受的调平螺丝", + "ScrewIndex": "螺丝编号", "ScrewName": "螺丝名称", - "ScrewOutput": "当前{current}/总共{max}" + "ScrewOutput": "{current}/{max}" }, "ConnectionDialog": { - "CannotConnectTo": "无法连接Moonraker ({host})。", - "CheckMoonrakerLog": "如果此消息重复显示,请从此处查看日志文件:", + "CannotConnectTo": "无法连接到Moonraker ({host})。", + "CheckMoonrakerLog": "如果此消息重复显示,请查看位于以下位置的日志文件:", "Connecting": "连接{host}", "Failed": "连接失败", "Initializing": "正在初始化", @@ -141,14 +146,28 @@ "Empty": "没有内容", "HideTemperatures": "隐藏温度变化消息", "HideTimelapse": "隐藏延时摄影消息", - "SendCode": "输入需要执行的命令...", + "SendCode": "输入要执行的代码...", "SetupConsole": "设置控制台" }, + "DevicesDialog": { + "CanBusInfo": "只有未分配的节点才能被检测到。建议仅连接一个未分配的设备到CAN总线,以避免通信问题。更多详情,请点击链接:", + "ClickRefresh": "点击刷新按钮以搜索设备。", + "DevicePath": "设备路径", + "Formats": "格式", + "Headline": "设备", + "HideSystemEntries": "隐藏系统条目", + "LibcameraId": "Libcamera标识符", + "NoDeviceFound": "未发现设备。请检查连接并点击刷新按钮。", + "PathByHardware": "硬件路径", + "PathById": "标识符路径", + "Refresh": "刷新", + "Resolutions": "分辨率" + }, "Dialogs": { "StartPrint": { "Cancel": "取消", - "DoYouWantToStartFilename": "是否开始打印{filename}?", - "DoYouWantToStartFilenameFilament": "是否用以下耗材开始打印{filename}?", + "DoYouWantToStartFilename": "是否开始打印文件{filename}?", + "DoYouWantToStartFilenameFilament": "是否使用以下耗材开始打印文件{filename}?", "Headline": "开始任务", "Print": "打印", "Timelapse": "延时摄影" @@ -156,64 +175,64 @@ }, "Editor": { "ConfigReference": "配置参考", + "DeviceDialog": "设备", "DontSave": "不保存", "Downloading": "正在下载", - "FailedSave": "无法上传{filename}!", - "FileReadOnly": "只读", + "FailedSave": "上传{filename}失败!", + "FileReadOnly": "只读文件", "SaveClose": "保存并关闭", "SaveRestart": "保存并重启", - "SuccessfullySaved": "{filename}保存成功。", - "UnsavedChanges": "修改未被保存", - "UnsavedChangesMessage": "是否将修改保存至{filename}?", - "UnsavedChangesSubMessage": "不保存你的修改将会丢失。你可以在编辑设置里关闭此提示。", + "SuccessfullySaved": "{filename}保存成功!", + "UnsavedChanges": "有未保存的更改", + "UnsavedChangesMessage": "是否保存对{filename}的更改?", + "UnsavedChangesSubMessage": "如果不保存,您的更改将丢失。您可以在编辑器设置中关闭此提示。", "Uploading": "正在上传" }, "EmergencyStopDialog": { - "AreYouSure": "是否确定?", + "AreYouSure": "确定要执行此操作吗?", "EmergencyStop": "紧急停止", "No": "取消", "Yes": "确定" }, "Files": { - "AddBatchToQueue": "批量添加到队列", - "AddToQueue": "添加到队列", - "AllFiles": "所有文件", + "AddBatchToQueue": "批量加入队列", + "AddToQueue": "加入队列", + "AllFiles": "全部文件", "BedTemp": "热床温度", - "Cancel": "取消", + "Cancel": "取消操作", "ChamberTemp": "仓室温度", - "Count": "计数", - "Create": "新建", - "CreateNewDirectory": "新建文件夹", - "CurrentPath": "当前路径", - "Delete": "删除", - "DeleteDirectory": "删除文件夹", - "DeleteDirectoryQuestion": "你确定要删除\"{name}\"和里面的所有内容吗?", - "DeleteSelectedQuestion": "你确定要删除选中的{count}个文件?", - "DeleteSingleFileQuestion": "你确定要删除文件\"{name}\"吗?", - "Download": "下载", - "Duplicate": "复制", - "DuplicateFile": "复制文件", - "EditFile": "编辑文件", - "Empty": "没有文件", - "ExtruderTemp": "挤出机温度", - "Filament": "耗材", - "FilamentName": "耗材名称", + "Count": "数量", + "Create": "创建", + "CreateNewDirectory": "创建新文件夹", + "CurrentPath": "当前目录", + "Delete": "删除文件", + "DeleteDirectory": "删除目录", + "DeleteDirectoryQuestion": "确定要删除\"{name}\"及其所有内容吗?", + "DeleteSelectedQuestion": "确定要删除所选的{count}个文件吗?", + "DeleteSingleFileQuestion": "确定要删除文件\"{name}\"吗?", + "Download": "下载文件", + "Duplicate": "复制文件", + "DuplicateFile": "复制文件副本", + "EditFile": "编辑文件内容", + "Empty": "目录为空", + "ExtruderTemp": "喷嘴温度", + "FilamentName": "耗材名字", "FilamentType": "耗材类型", "FilamentUsage": "耗材用量", "FilamentWeight": "耗材重量", - "Files": "每页显示文件数", + "Files": "每页文件数量", "Filesize": "文件大小", "Free": "未使用", - "FreeDisk": "剩余磁盘空间", + "FreeDisk": "可用磁盘空间", "GCodeFiles": "G-Code文件", "GcodesRootDirectoryDoesntExists": "未找到G-Code文件夹。请检查Klipper配置[virtual_sdcard]标签中的\"path\"项是否正确填写。", "HiddenFiles": "显示隐藏文件", - "InvalidNameAlreadyExists": "名字已经存在,请换一个名字。", + "InvalidNameAlreadyExists": "该名字已存在,请选择其他名字。", "InvalidNameEmpty": "输入值不能为空!", - "LastEndTime": "上次打印结束时间", + "LastEndTime": "上次打印完成时间", "LastFilamentUsed": "上次耗材使用量", "LastModified": "最后修改时间", - "LastPrintDuration": "上次打印耗时", + "LastPrintDuration": "上次打印时长", "LastStartTime": "上次打印开始时间", "LastTotalDuration": "上次打印总耗时", "LayerHeight": "层高", @@ -223,7 +242,7 @@ "ObjectHeight": "物体高度", "Preheat": "预热", "PrintedFiles": "已打印文件", - "PrintStart": "开始打印", + "PrintStart": "开始打印任务", "PrintTime": "打印时长", "RefreshCurrentDirectory": "刷新当前路径", "Rename": "重命名", @@ -273,13 +292,13 @@ "Tracking": "正在追踪", "Transparency": "透明度", "Ultra": "极致", - "VoxelMode": "体素模式(ASMBL)" + "VoxelMode": "三维像素模式(ASMBL)" }, "Heightmap": { - "Abort": "取消", + "Abort": "中止", "BedMeshCalibrate": "校准床网", "BedMeshRemove": "移除床网", - "Calibrate": "校准", + "Calibrate": "测量", "Clear": "清除", "CurrentMesh": { "Headline": "当前床网", @@ -297,29 +316,26 @@ "InvalidNameAlreadyExists": "预设名称已经存在,请换一个名称。", "InvalidNameAscii": "错误的名称,请删除所有非ascii字符后重试。", "InvalidNameEmpty": "输入值不能为空!", - "InvalidNameReserved": "预设 'default' 已保留,请选择其他预设名称。", - "Later": "以后", - "Mesh": "床网", + "InvalidNameReserved": "预设'default'已保留,请选择其他预设名称。", + "Mesh": "网格", "Name": "名称", "NoBedMeshHasBeenLoadedYet": "没有加载床网。", "NoProfile": "没有预设可用", - "Ok": "好的", - "Probed": "探测结果", + "Probed": "测量结果", "Profiles": "预设", "Remove": "删除", - "RemoveSaveDescription": "预设床网已标记需删除。点击\"保存配置\"将会从printer.cfg中移除并重启Klipper。", "Rename": "重命名", "RenameBedMeshProfile": "重命名预设床网", - "SAVE_CONFIG": "保存配置", "ScaleGradient": "尺度梯度", "ScaleZMax": "Z最大标度", "TitleCalibrate": "校准新床网", "TitleClear": "清除床网", - "TitleHomeAll": "归位所有轴", + "TitleHomeAll": "全部归位", "Wireframe": "线框" }, "History": { "AddNote": "添加便条", + "AddToQueueSuccessful": "文件{filename}已添加到队列。", "AllJobs": "全部", "AvgPrinttime": "平均打印时长", "Cancel": "取消", @@ -337,7 +353,7 @@ "EstimatedTime": "预估打印时长", "FilamentCalc": "耗材预估长度", "FilamentUsage": "耗材用量", - "FilamentUsed": "实际耗材用量", + "FilamentUsed": "实际耗材消耗量", "Filename": "文件名", "Filesize": "文件大小", "FirstLayerBedTemp": "首层热床温度", @@ -349,12 +365,13 @@ "Jobs": "每页显示任务数", "LastModified": "修改日期", "LayerHeight": "层高", + "LoadCompleteHistory": "加载完整历史记录", "LongestPrinttime": "最长打印时长", "Note": "便条", "ObjectHeight": "物体高度", "PrintDuration": "打印耗时", "PrintHistory": "打印历史", - "PrintTime": "实际打印时长", + "PrintTime": "打印持续时间", "PrinttimeAvg": "平均打印时长", "Reprint": "重新打印", "Save": "保存", @@ -372,6 +389,7 @@ "completed": "完成打印", "error": "故障", "in_progress": "打印中", + "interrupted": "中断", "klippy_disconnect": "Klippy失去连接", "klippy_shutdown": "Klippy关闭", "Others": "其他", @@ -379,8 +397,6 @@ }, "Table": "表格", "TitleExportHistory": "导出历史记录", - "TitleRefreshHistory": "刷新历史记录", - "TitleSettings": "设置", "TotalDuration": "总消耗时长", "TotalFilamentUsed": "耗材使用总量", "TotalJobs": "任务次数", @@ -388,7 +404,6 @@ "TotalTime": "总消耗时长" }, "JobQueue": { - "AllJobs": "全部任务", "Cancel": "取消", "ChangeCount": "变更计数", "Count": "计数", @@ -396,10 +411,10 @@ "InvalidCountEmpty": "输入值不能为空!", "InvalidCountGreaterZero": "输入值必须大于0!", "JobQueue": "任务队列", - "Jobs": "每页显示任务数", "Pause": "暂停", "RemoveFromQueue": "从队列中删除", - "Start": "开始" + "Start": "开始", + "StartPrint": "开始任务" }, "Machine": { "ConfigFilesPanel": { @@ -420,7 +435,7 @@ "Duplicate": "复制", "DuplicateFile": "复制文件", "EditFile": "编辑文件", - "Empty": "没有文件", + "Empty": "当前目录为空", "Files": "每页显示文件数", "Filesize": "文件大小", "Free": "未使用", @@ -446,7 +461,7 @@ "EndstopInfo": "点击右侧同步按钮刷新限位状态", "Endstops": "限位", "open": "未触发", - "TRIGGERED": "已触发" + "TRIGGERED": "被触发" }, "LogfilesPanel": { "Accept": "接受", @@ -483,7 +498,7 @@ } }, "UpdatePanel": { - "Abort": "放弃", + "Abort": "中止", "AreYouSure": "你确定吗?", "CheckForUpdates": "检查更新", "Close": "关闭", @@ -500,7 +515,7 @@ "CountPackagesCanBeUpgraded": "有{count}个软件包可以更新", "Detached": "独立分支", "Dirty": "被修改", - "GenericUpdateQuestion": "检查提交历史(如果有的话)和该项目的 GitHub 页面,以查看是否需要对此更新进行手动调整。", + "GenericUpdateQuestion": "检查提交历史(如果有的话)和该项目的GitHub页面,以查看是否需要对此更新进行手动调整。", "GitHubPage": "GitHub页面", "HardRecovery": "版本回退(清空修改内容)", "InitUpdateManager": "更新管理器尚未初始化。这是在您首次启动系统时的正常现象。请点击刷新按钮来初始化所有组件。", @@ -520,7 +535,7 @@ "Update": "更新", "UpdateAll": "更新全部组件", "UpdateManager": "更新管理", - "UpdateWarning": "更新警告: {name}", + "UpdateWarning": "更新警告:{name}", "Upgrade": "升级", "UpgradeableSystemPackages": "可升级的系统软件包", "UpToDate": "已是最新版本", @@ -528,7 +543,7 @@ } }, "ManualProbe": { - "Abort": "取消", + "Abort": "中止", "Accept": "接受", "Advanced": "高级", "Headline": "手动Probe" @@ -536,12 +551,15 @@ "Panels": { "ExtruderControlPanel": { "Allowed": "允许", + "CleanNozzle": "清洁喷嘴", "EstimatedExtrusion": "预计挤出量:", "Extrude": "挤出", + "ExtruderControl": "挤出机控制", "ExtruderTempTooLow": "挤出温度 <", "ExtrusionFactor": "挤出倍数", "ExtrusionFeedrate": "进给速度", "FilamentLength": "耗材长度", + "FirmwareRetraction": "固件回抽", "FirmwareRetractionSettings": { "RetractLength": "回抽长度", "RetractSpeed": "回抽速度", @@ -550,14 +568,17 @@ }, "Headline": "挤出机", "LoadFilament": "装载耗材", + "PressureAdvance": "挤出压力提前", "PressureAdvanceSettings": { - "Advance": "压力提前", + "Advance": "挤出压力提前", "Extruder": "挤出机", "SmoothTime": "平滑时间" }, + "PurgeFilament": "清除喷嘴内残留材料", "Requested": "需要", "Retract": "回抽", "TooLargeExtrusion": "挤出过大!", + "Tools": "工具", "UnloadFilament": "释放耗材" }, "FarmPrinterPanel": { @@ -580,6 +601,7 @@ "MotionSettings": { "Acceleration": "加速度", "MaxAccelToDecel": "加速到减速最大值", + "MinimumCruiseRatio": "最小巡航比", "SquareCornerVelocity": "直角拐弯速度", "Velocity": "速度" } @@ -612,7 +634,6 @@ }, "RunoutSensor": { "Detected": "检测到", - "Disabled": "已关闭", "Empty": "无" } }, @@ -627,7 +648,7 @@ "ChangeSpool": "更换料盘", "DaysAgo": "{days}天前", "EjectSpool": "弹出料盘", - "EjectSpoolQuestion": "你确定要弹出耗材料盘吗?", + "EjectSpoolQuestion": "你确定要弹出耗材盘吗?", "Filament": "耗材", "FilamentTypeMismatch": "已激活料盘的材料({spoolType})与G-Code的材料({fileType})不匹配。", "Headline": "Spoolman", @@ -635,7 +656,7 @@ "Location": "位置", "Material": "材料", "Never": "从未", - "NoActiveSpool": "耗材跟踪未激活。若要使用,请先选择一个料盘。", + "NoActiveSpool": "耗材追踪未激活。请先选择一个料盘以开始使用。", "NoResults": "未找到符合当前搜索条件的料盘。", "NoSpools": "没有可用料盘", "NoSpoolSelected": "没有选择料盘。请选择一个料盘,否则将无法跟踪此次打印。", @@ -644,7 +665,7 @@ "Search": "搜索", "SelectSpool": "选择料盘", "Today": "今天", - "TooLessFilament": "当前料盘可能没有足够的耗材用于此次打印。(剩余{spoolWeight}克/需要{fileWeight}克)", + "TooLessFilament": "当前料盘可能没有足够的耗材用于此次打印。(剩余{spoolWeight}克/需要{fileWeight}克)", "Weight": "重量", "Yesterday": "昨天" }, @@ -653,7 +674,7 @@ "ClearPrintStats": "清除打印统计数据", "Difference": "差异", "EmptyGcodes": "没有G-Code文件", - "EmptyJobqueue": "当前任务队列中没有文件。", + "EmptyJobqueue": "当前任务队列中没有文件存在。", "Estimate": "估算剩余", "ETA": "预估完成", "ExcludeObject": { @@ -674,7 +695,7 @@ "Max": "最高", "ObjectHeight": "物体高度", "PauseAtLayer": { - "Abort": "取消", + "Abort": "中止", "Accept": "接受", "AtLayer": "所在层", "Call": "调用", @@ -685,12 +706,12 @@ "PauseAtLayer": "在某层暂停", "Type": "类型" }, - "PausePrint": "暂停打印", + "PausePrint": "暂停打印作业", "Print": "打印", "PrintTime": "打印耗时", "ReprintJob": "再次打印", "Requested": "请求", - "ResumePrint": "恢复打印", + "ResumePrint": "继续打印作业", "Slicer": "切片剩余", "Speed": "打印速度", "Status": "状态", @@ -698,7 +719,7 @@ "Unknown": "未知" }, "TemperaturePanel": { - "AutoscaleChart": "自动缩放图表", + "AutoscaleChart": "自动调整图表比例", "Avg": "平均", "Cooldown": "冷却", "Current": "当前", @@ -715,7 +736,7 @@ "Min": "最低", "Name": "名称", "Presets": "预设", - "SetupTemperatures": "设置温度", + "SetupTemperatures": "配置温度", "ShowChart": "显示图表", "ShowNameInChart": "在图表中显示{name} ", "ShowNameInList": "在列表中显示{name} ", @@ -728,13 +749,17 @@ "ToolheadControlPanel": { "Absolute": "绝对坐标", "ALL": "全部", + "ControlButtons": "控制按钮", + "CoordinateFields": "坐标字段", "Headline": "打印头", "PleaseConfigureSteps": "请配置步骤", "Position": "位置", + "PositionOutput": "位置输出", "QGL": "QGL", "Relative": "相对坐标", "SettingsInterfaceControl": "设置 > 界面 > 控制", "SpeedFactor": "速度倍数", + "ZOffset": "Z轴偏移", "ZTilt": "Z轴倾斜" }, "WebcamPanel": { @@ -766,8 +791,8 @@ "Router": { "Console": "控制台", "Dashboard": "控制面板", - "G-Code Files": "G-Code 文件", - "G-Code Viewer": "G-Code 预览", + "G-Code Files": "G-Code文件", + "G-Code Viewer": "G-Code预览", "Heightmap": "高度图", "History": "历史记录", "Machine": "机器", @@ -784,8 +809,8 @@ }, "SelectPrinterDialog": { "AddPrinter": "添加打印机", - "AddPrintersToJson": "请将打印机添加到 config.json 。", - "CannotConnectTo": "无法连接到{host} 。", + "AddPrintersToJson": "请将打印机添加到config.json。", + "CannotConnectTo": "无法连接到{host}。", "ChangePrinter": "切换打印机", "Connecting": "正在连接到{host}", "ConnectionFailed": "连接失败", @@ -794,8 +819,8 @@ "HostnameInvalid": "不可用的主机名称/IP地址", "HostnameIp": "主机名称/IP地址", "HostnameRequired": "需要配置主机名称/IP地址", - "Port": "Moonraker服务端口", - "PortRequired": "需要配置Moonraker服务端口", + "Port": "Moonraker服务端口号", + "PortRequired": "需要配置Moonraker服务端口号", "RememberToAdd": "请在moonraker.conf的'cors_domains'标签中添加'{cors}'", "SelectPrinter": "选择打印机", "TryAgain": "重试", @@ -813,7 +838,7 @@ "DirectionShell": "最后的消息显示在底部", "DirectionTable": "最后的消息显示在顶部", "EditHeadline": "编辑过滤器", - "EntryStyle": "控制台显示样式", + "EntryStyle": "控制台显示风格", "EntryStyleCompact": "紧凑", "EntryStyleDefault": "默认", "Filters": "过滤器", @@ -853,6 +878,8 @@ "Style": "打印头控制界面样式", "ValueGreaterThan": "设置的值应该大于{value}", "ZOffsetIncrements": "Z轴偏移调整幅度(mm)", + "ZOffsetSaveOption": "Z轴偏移保存选项", + "ZOffsetSaveOptionDescription": "更改选项以保存Z轴偏移", "ZTiltAdjust": "Z轴倾斜调整" }, "DashboardTab": { @@ -865,7 +892,7 @@ }, "Edit": "编辑", "EditorTab": { - "ConfirmUnsavedChanges": "提示保存或放弃未保存的更改", + "ConfirmUnsavedChanges": "确认保存或放弃未保存的更改", "ConfirmUnsavedChangesDescription": "如果启用,编辑器需要确认保存或放弃所做的更改。如果禁用,更改将被默认丢弃。", "Editor": "编辑器", "KlipperRestartMethod": "Klipper重启方法", @@ -879,7 +906,7 @@ "GCodeViewerTab": { "BackgroundColor": "背景颜色", "ExtruderColor": "喷嘴颜色", - "GCodeViewer": "G-Code 预览", + "GCodeViewer": "G-Code预览", "GridColor": "网格颜色", "MaxFeed": "最大挤出速度", "MinFeed": "最小挤出速度", @@ -889,7 +916,7 @@ "GeneralTab": { "12hours": "12-小时 ({time})", "24hours": "24-小时 ({time})", - "Backup": "备份", + "Backup": "备份数据", "BackupDialog": "请选择你想要备份的内容:", "CalcEstimateTime": "估算剩余时间", "CalcEstimateTimeDescription": "如果选择了多项,将会计算平均值", @@ -920,7 +947,7 @@ "FactoryDialog": "请选择想要恢复到默认值的项:", "FactoryReset": "恢复默认设置", "General": "通用", - "Language": "界面语言", + "Language": "界面语言选择", "MainsailSettingsMoonrakerDb": "在Moonraker数据库中的Mainsail设置", "PrinterName": "打印机名称", "Reset": "重启", @@ -928,6 +955,18 @@ "RestoreDialog": "请选择你想要恢复的项:", "TimeFormat": "时间格式" }, + "HeightmapTab": { + "ColorSchemes": "配色方案", + "Heightmap": "高度图", + "IsDefault": "(默认)", + "Schemes": { + "GrayScale": "灰度", + "Hot": "热色调", + "Hsv": "HSV色彩空间", + "Portland": "波特兰色彩方案", + "Spring": "春季色调" + } + }, "InterfaceSettings": "Mainsail设置", "MacrosTab": { "Add": "添加", @@ -1016,7 +1055,7 @@ "Port": "Moonraker服务端口", "RemotePrinters": "打印机", "UpdatePrinter": "更新打印机", - "UseConfigJson": "检测到InstanceDB参数为JSON。请使用 config.json 来修改打印机列表。" + "UseConfigJson": "检测到InstanceDB参数为JSON。请使用config.json来修改打印机列表。" }, "Store": "存储", "TimelapseTab": { @@ -1040,7 +1079,7 @@ "HyperlapseCycle": "Hyperlapse周期时间", "HyperlapseCycleDescription": "每隔X秒获取一张快照", "Mode": "模式", - "ModeDescription": "选择Layermacro(基于层变化)或者Hyperlapse(基于时间)模式", + "ModeDescription": "选择Layermacro模式(基于层变化)或者Hyperlapse模式(基于时间)", "OutputFramerate": "输出帧率", "OutputFramerateDescription": "设置视频帧率。注意:如果可变FPS设置开启,此设置会被忽略", "Parkhead": "放置打印头到停靠位置", @@ -1064,6 +1103,11 @@ "RetractDistanceDescription": "挤出机回抽耗材的长度", "RetractSpeed": "回抽速度", "RetractSpeedDescription": "挤出机回抽耗材的速度", + "RulesBetweenMinMax": "值必须介于{min}和{max}之间!", + "RulesMin": "值必须至少为{min}!", + "RulesPositive": "值必须为正数!", + "RulesRequired": "值为必填项!", + "RulesZeroAndPositive": "值必须大于等于0!", "SaveFrames": "保存帧", "SaveFramesDescription": "保存帧到zip文件用于外部渲染", "StreamDelayCompensation": "视频流延迟补偿", @@ -1132,6 +1176,10 @@ "ScrewsTiltAdjustDialogDescription": "显示SCREWS_TILT_CALCULATE辅助对话框。", "TempchartHeight": "温度图表高度", "TempchartHeightDescription": "修改控制面板上温度图表的高度。", + "Theme": "主题", + "ThemeDark": "暗色主题", + "ThemeDescription": "更改应用程序的整体外观和感觉", + "ThemeLight": "亮色主题", "UiSettings": "UI设置" }, "Update": "更新", @@ -1140,6 +1188,7 @@ "CreateWebcam": "添加摄像头", "EditCrowsnestConf": "编辑 crowsnest.conf", "EditWebcam": "编辑摄像头", + "EnableAudio": "启用音频", "FlipWebcam": "翻转摄像头图像:", "HideFps": "隐藏FPS数值显示", "Hlsstream": "HLS视频流", @@ -1170,6 +1219,7 @@ "Vertically": "垂直", "Webcams": "摄像头", "WebrtcCameraStreamer": "WebRTC (camera-streamer)", + "WebrtcGo2rtc": "WebRTC (go2rtc)", "WebrtcJanus": "WebRTC (janus-gateway)", "WebrtcMediaMTX": "WebRTC (MediaMTX)" } diff --git a/src/pages/Console.vue b/src/pages/Console.vue index e29799cee..a37067bcb 100644 --- a/src/pages/Console.vue +++ b/src/pages/Console.vue @@ -281,7 +281,7 @@ export default class PageConsole extends Mixins(BaseMixin) { commands.forEach( (command) => (output += - "" + + "" + command.command + ': ' + command.description + diff --git a/src/pages/Heightmap.vue b/src/pages/Heightmap.vue index c51019992..7885e740c 100644 --- a/src/pages/Heightmap.vue +++ b/src/pages/Heightmap.vue @@ -1,1234 +1,43 @@ - - diff --git a/src/plugins/helpers.ts b/src/plugins/helpers.ts index 0641ec209..8cbd54a94 100644 --- a/src/plugins/helpers.ts +++ b/src/plugins/helpers.ts @@ -248,3 +248,37 @@ export function windowBeforeUnloadFunction(e: BeforeUnloadEvent) { e.preventDefault() e.returnValue = '' } + +export function copyToClipboard(text: string) { + if (navigator.clipboard) { + navigator.clipboard.writeText(text) + return + } + + const textArea = document.createElement('textarea') + let element = document.getElementById('devices-dialog') + if (!element) element = document.body + + textArea.value = text + textArea.style.position = 'absolute' + textArea.style.top = '0' + textArea.style.left = '0' + textArea.style.zIndex = '100000' + textArea.style.opacity = '0' + element.appendChild(textArea) + textArea.focus() + textArea.select() + try { + document.execCommand('copy') + } catch (err) { + console.error('Unable to copy to clipboard', err) + } + textArea.remove() +} + +export function sortResolutions(a: string, b: string) { + const aSplit = parseInt(a.split('x')[0]) + const bSplit = parseInt(b.split('x')[0]) + + return aSplit - bSplit +} diff --git a/src/plugins/webSocketClient.ts b/src/plugins/webSocketClient.ts index 2fdb3e0ad..1208bb7cb 100644 --- a/src/plugins/webSocketClient.ts +++ b/src/plugins/webSocketClient.ts @@ -10,6 +10,7 @@ export class WebSocketClient { reconnectInterval = 1000 reconnects = 0 keepAliveTimeout = 1000 + messageId: number = 0 timerId: number | null = null store: Store | null = null waits: Wait[] = [] @@ -25,6 +26,59 @@ export class WebSocketClient { this.url = url } + handleMessage(data: any) { + const wait = this.getWaitById(data.id) + + // report error messages + if (data.error?.message) { + // only report errors, if not disconnected and no init component + if (data.error?.message !== 'Klippy Disconnected') { + window.console.error(`Response Error: ${data.error.message} (${wait?.action ?? 'no action'})`) + } + + if (wait?.id) { + const modulename = wait.action?.split('/')[1] ?? null + + if ( + modulename && + wait.action?.startsWith('server/') && + initableServerComponents.includes(modulename) && + this.store?.state.socket?.initializationList.length + ) { + const component = wait.action.replace('server/', '').split('/')[0] + window.console.error(`init server component ${component} failed`) + this.store?.dispatch('server/addFailedInitComponent', component) + this.store?.dispatch('socket/removeInitComponent', `server/${component}/`) + } + + this.removeWaitById(wait.id) + } + + return + } + + // pass it to socket/onMessage, if no wait exists + if (!wait) { + this.store?.dispatch('socket/onMessage', data) + return + } + + // pass result to action + if (wait?.action) { + let result = data.result + if (result === 'ok') result = { result: result } + if (typeof result === 'string') result = { result: result } + + const preload = {} + if (wait.actionPayload) Object.assign(preload, wait.actionPayload) + Object.assign(preload, { requestParams: wait.params }) + Object.assign(preload, result) + this.store?.dispatch(wait.action, preload) + } + + this.removeWaitById(wait.id) + } + async connect() { this.store?.dispatch('socket/setData', { isConnecting: true, @@ -58,55 +112,13 @@ export class WebSocketClient { if (this.store === null) return const data = JSON.parse(msg.data) - const wait = this.getWaitById(data.id) - - // report error messages - if (data.error?.message) { - // only report errors, if not disconnected and no init component - if (data.error?.message !== 'Klippy Disconnected' && !wait?.action?.startsWith('server/')) { - window.console.error(`Response Error: ${data.error.message} (${wait?.action ?? 'no action'})`) + if (Array.isArray(data)) { + for (const message of data) { + this.handleMessage(message) } - - if (wait?.id) { - const modulename = wait.action?.split('/')[1] ?? null - - if ( - modulename && - wait.action?.startsWith('server/') && - initableServerComponents.includes(modulename) - ) { - const component = wait.action.replace('server/', '').split('/')[0] - window.console.error(`init server component ${component} failed`) - this.store?.dispatch('server/addFailedInitComponent', component) - this.store?.dispatch('socket/removeInitComponent', `server/${component}/`) - } - - this.removeWaitById(wait.id) - } - - return + } else { + this.handleMessage(data) } - - // pass it to socket/onMessage, if no wait exists - if (!wait) { - this.store?.dispatch('socket/onMessage', data) - return - } - - // pass result to action - if (wait?.action) { - let result = data.result - if (result === 'ok') result = { result: result } - if (typeof result === 'string') result = { result: result } - - const preload = {} - if (wait.actionPayload) Object.assign(preload, wait.actionPayload) - Object.assign(preload, { requestParams: wait.params }) - Object.assign(preload, result) - this.store?.dispatch(wait.action, preload) - } - - this.removeWaitById(wait.id) } } @@ -130,7 +142,7 @@ export class WebSocketClient { emit(method: string, params: Params, options: emitOptions = {}): void { if (this.instance?.readyState !== WebSocket.OPEN) return - const id = Math.floor(Math.random() * 10000) + 1 + const id = this.messageId++ this.waits.push({ id: id, params: params, @@ -150,6 +162,33 @@ export class WebSocketClient { }) ) } + + emitBatch(messages: BatchMessage[]): void { + if (messages.length === 0) return + if (this.instance?.readyState !== WebSocket.OPEN) return + + const body = [] + for (const { method, params, emitOptions = {} } of messages) { + const id = this.messageId++ + this.waits.push({ + id: id, + params: params, + action: emitOptions.action ?? null, + actionPayload: emitOptions.actionPayload ?? {}, + loading: emitOptions.loading ?? null, + }) + + if (emitOptions.loading) this.store?.dispatch('socket/addLoading', { name: emitOptions.loading }) + body.push({ + jsonrpc: '2.0', + method, + params, + id, + }) + } + + this.instance.send(JSON.stringify(body)) + } } export function WebSocketPlugin(Vue: typeof _Vue, options: WebSocketPluginOptions): void { @@ -169,6 +208,13 @@ export interface WebSocketClient { connect(): void close(): void emit(method: string, params: Params, emitOptions: emitOptions): void + emitBatch(messages: BatchMessage[]): void +} + +export interface BatchMessage { + method: string + params: Params + emitOptions: emitOptions } export interface Wait { diff --git a/src/routes/index.ts b/src/routes/index.ts index 6964df89b..94117ff3c 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -22,6 +22,7 @@ import { const routes: AppRoute[] = [ { + name: 'dashboard', title: 'Dashboard', path: '/', icon: mdiMonitorDashboard, @@ -31,6 +32,7 @@ const routes: AppRoute[] = [ position: 10, }, { + name: 'farm', title: 'Printers', path: '/allPrinters', component: Farm, @@ -38,6 +40,7 @@ const routes: AppRoute[] = [ showInNavi: false, }, { + name: 'webcam', title: 'Webcam', path: '/cam', icon: mdiWebcam, @@ -45,8 +48,10 @@ const routes: AppRoute[] = [ alwaysShow: true, showInNavi: true, position: 20, + fullscreen: true, }, { + name: 'console', title: 'Console', path: '/console', icon: mdiConsoleLine, @@ -57,6 +62,7 @@ const routes: AppRoute[] = [ position: 30, }, { + name: 'heightmap', title: 'Heightmap', path: '/heightmap', icon: mdiGrid, @@ -67,6 +73,7 @@ const routes: AppRoute[] = [ position: 40, }, { + name: 'gcodefiles', title: 'G-Code Files', path: '/files', icon: mdiFileDocumentMultipleOutline, @@ -75,8 +82,10 @@ const routes: AppRoute[] = [ showInNavi: true, registeredDirectory: 'gcodes', position: 50, + fullscreen: true, }, { + name: 'gcodeviewer', title: 'G-Code Viewer', path: '/viewer', icon: mdiVideo3d, @@ -84,8 +93,10 @@ const routes: AppRoute[] = [ alwaysShow: true, showInNavi: true, position: 60, + fullscreen: true, }, { + name: 'history', title: 'History', path: '/history', icon: mdiHistory, @@ -96,6 +107,7 @@ const routes: AppRoute[] = [ position: 70, }, { + name: 'timelapse', title: 'Timelapse', path: '/timelapse', icon: mdiTimelapse, @@ -106,6 +118,7 @@ const routes: AppRoute[] = [ position: 80, }, { + name: 'machine', title: 'Machine', path: '/config', icon: mdiWrench, @@ -127,6 +140,7 @@ const routes: AppRoute[] = [ export default routes export interface AppRoute { + name?: string title: string | null path: string redirect?: string @@ -140,4 +154,5 @@ export interface AppRoute { klipperIsConnected?: boolean children?: AppRoute[] position?: number + fullscreen?: boolean } diff --git a/src/store/editor/actions.ts b/src/store/editor/actions.ts index c5112a90f..c7d50b2b8 100644 --- a/src/store/editor/actions.ts +++ b/src/store/editor/actions.ts @@ -133,7 +133,10 @@ export const actions: ActionTree = { } else if (payload.restartServiceName !== null) { Vue.$socket.emit('machine.services.restart', { service: payload.restartServiceName }) } - dispatch('close') + + commit('updateLoadedHash', payload.content) + + if (payload.restartServiceName !== null) dispatch('close') }) .catch((error) => { window.console.log(error.response?.data.error) diff --git a/src/store/editor/mutations.ts b/src/store/editor/mutations.ts index 5fb331451..771b077c5 100644 --- a/src/store/editor/mutations.ts +++ b/src/store/editor/mutations.ts @@ -68,4 +68,9 @@ export const mutations: MutationTree = { state.changed = sha256(payload) != state.loadedHash }, + + updateLoadedHash(state, payload) { + Vue.set(state, 'loadedHash', sha256(payload.replace(/(?:\r\n|\r|\n)/g, '\n'))) + Vue.set(state, 'changed', false) + }, } diff --git a/src/store/files/actions.ts b/src/store/files/actions.ts index a67e1f114..032e98e22 100644 --- a/src/store/files/actions.ts +++ b/src/store/files/actions.ts @@ -10,7 +10,8 @@ import { import { RootState } from '@/store/types' import i18n from '@/plugins/i18n' import { hiddenDirectories, validGcodeExtensions } from '@/store/variables' -import axios from 'axios' +import axios, { AxiosProgressEvent } from 'axios' +import { BatchMessage } from '@/plugins/webSocketClient' export const actions: ActionTree = { reset({ commit }) { @@ -159,13 +160,26 @@ export const actions: ActionTree = { } }, - requestMetadata({ commit }, payload: { filename: string }) { - const rootPath = payload.filename.slice(0, payload.filename.indexOf('/')) - if (rootPath === 'gcodes') { - const requestFilename = payload.filename.slice(7) - commit('setMetadataRequested', { filename: requestFilename }) - Vue.$socket.emit('server.files.metadata', { filename: requestFilename }, { action: 'files/getMetadata' }) + requestMetadata({ commit }, payload: { filename: string }[]) { + // request file metadata in batches to reduce the number of table re-renders when responses are received + let messages: BatchMessage[] = [] + for (const { filename } of payload) { + if (messages.length >= 100) { + Vue.$socket.emitBatch(messages) + messages = [] + } + const rootPath = filename.slice(0, filename.indexOf('/')) + if (rootPath === 'gcodes') { + const requestFilename = filename.slice(7) + commit('setMetadataRequested', { filename: requestFilename }) + messages.push({ + method: 'server.files.metadata', + params: { filename: requestFilename }, + emitOptions: { action: 'files/getMetadata' }, + }) + } } + Vue.$socket.emitBatch(messages) }, getMetadata({ commit, rootState }, payload) { @@ -203,9 +217,11 @@ export const actions: ActionTree = { payload.item.root === 'gcodes' && validGcodeExtensions.includes(payload.item.path.slice(payload.item.path.lastIndexOf('.'))) ) { - await dispatch('requestMetadata', { - filename: 'gcodes/' + payload.item.path, - }) + await dispatch('requestMetadata', [ + { + filename: 'gcodes/' + payload.item.path, + }, + ]) } break @@ -300,34 +316,17 @@ export const actions: ActionTree = { await commit('uploadSetFilename', payload.file.name) await commit('uploadSetShow', true) - let lastTime = 0 - let lastLoaded = 0 - return new Promise((resolve) => { axios .post(apiUrl + '/server/files/upload', formData, { cancelToken: cancelTokenSource.token, headers: { 'Content-Type': 'multipart/form-data' }, - onUploadProgress: (progressEvent: any) => { - const percent = (progressEvent.loaded * 100) / progressEvent.total + onUploadProgress: (progressEvent: AxiosProgressEvent) => { + const percent = (progressEvent.progress ?? 0) * 100 commit('uploadSetPercent', percent) - if (lastTime === 0) { - lastTime = progressEvent.timeStamp - lastLoaded = progressEvent.loaded - - return - } - - const time = progressEvent.timeStamp - lastTime - if (time < 1000) return - - const data = progressEvent.loaded - lastLoaded - const speed = data / (time / 1000) - commit('uploadSetSpeed', speed) - - lastTime = progressEvent.timeStamp - lastLoaded = progressEvent.loaded + const rate = progressEvent.rate ?? 0 + commit('uploadSetSpeed', rate) }, }) .then((result: any) => { diff --git a/src/store/gui/index.ts b/src/store/gui/index.ts index 1e192c60f..e565753ef 100644 --- a/src/store/gui/index.ts +++ b/src/store/gui/index.ts @@ -156,6 +156,7 @@ export const getDefaultState = (): GuiState => { lockSlidersOnTouchDevices: true, lockSlidersDelay: 1.5, confirmOnEmergencyStop: false, + confirmOnCoolDown: false, confirmOnPowerDeviceChange: false, boolBigThumbnail: true, bigThumbnailBackground: defaultBigThumbnailBackground, diff --git a/src/store/gui/types.ts b/src/store/gui/types.ts index 930961603..7b4dfee09 100644 --- a/src/store/gui/types.ts +++ b/src/store/gui/types.ts @@ -105,6 +105,7 @@ export interface GuiState { lockSlidersOnTouchDevices: boolean lockSlidersDelay: number confirmOnEmergencyStop: boolean + confirmOnCoolDown: boolean confirmOnPowerDeviceChange: boolean boolBigThumbnail: boolean bigThumbnailBackground: string diff --git a/src/store/printer/getters.ts b/src/store/printer/getters.ts index ae86074d9..225b06bee 100644 --- a/src/store/printer/getters.ts +++ b/src/store/printer/getters.ts @@ -2,7 +2,6 @@ import { checkKlipperConfigModules } from '@/store/variables' import { GetterTree } from 'vuex' import { PrinterState, - PrinterStateBedMesh, PrinterStateExtruder, PrinterStateExtruderStepper, PrinterStateFan, @@ -290,7 +289,15 @@ export const getters: GetterTree = { getMiscellaneous: (state) => { const output: PrinterStateMiscellaneous[] = [] - const supportedObjects = ['controller_fan', 'heater_fan', 'fan_generic', 'fan', 'output_pin'] + const supportedObjects = [ + 'controller_fan', + 'heater_fan', + 'fan_generic', + 'fan', + 'output_pin', + 'pwm_tool', + 'pwm_cycle_time', + ] const controllableFans = ['fan_generic', 'fan'] @@ -309,10 +316,11 @@ export const getters: GetterTree = { if (nameSplit[0].toLowerCase() === 'fan') scale = 255 - if (nameSplit[0].toLowerCase() === 'output_pin') { + if (['output_pin', 'pwm_tool', 'pwm_cycle_time'].includes(nameSplit[0])) { controllable = true pwm = false if ('pwm' in settings) pwm = settings?.pwm ?? false + if (['pwm_tool', 'pwm_cycle_time'].includes(nameSplit[0])) pwm = true if ('scale' in settings) scale = settings?.scale ?? 1 } @@ -526,38 +534,6 @@ export const getters: GetterTree = { return output }, - getBedMeshProfiles: (state) => { - const profiles: PrinterStateBedMesh[] = [] - let currentProfile = '' - if (state.bed_mesh) currentProfile = state.bed_mesh.profile_name - - if (state.bed_mesh && 'profiles' in state.bed_mesh) { - Object.keys(state.bed_mesh?.profiles).forEach((key) => { - const value: any = state.bed_mesh.profiles[key] - - let points: number[] = [] - value.points.forEach((row: number[]) => { - points = points.concat(row) - }) - - const min = Math.min(...points) - const max = Math.max(...points) - - profiles.push({ - name: key, - data: { ...value.mesh_params, points: value.points }, - points: points, - min: min, - max: max, - variance: Math.abs(min - max), - is_active: currentProfile === key, - }) - }) - } - - return caseInsensitiveSort(profiles, 'name') - }, - getExtruders: (state) => { const extruders: PrinterStateExtruder[] = [] if (state.configfile?.settings) { diff --git a/src/store/printer/types.ts b/src/store/printer/types.ts index 5c2665e8a..e0ebe8275 100644 --- a/src/store/printer/types.ts +++ b/src/store/printer/types.ts @@ -157,26 +157,30 @@ export interface PrinterStateFilamentSensors { } export interface PrinterStateBedMesh { - name: string - data: { - algo: string + profile_name: string + mesh_min: [number, number] + mesh_max: [number, number] + probed_matrix: number[][] + mesh_matrix: number[][] + profiles: { + [key: string]: PrinterStateBedMeshProfile + } +} + +export interface PrinterStateBedMeshProfile { + points: number[][] + mesh_params: { + min_x: number max_x: number + min_y: number max_y: number + x_count: number + y_count: number mesh_x_pps: number mesh_y_pps: number - min_x: number - min_y: number - points: { [key: number]: number[] } + algo: 'bicubic' | 'lagrange' tension: number - version: number - x_count: number - y_count: number } - points: number[] - min: number - max: number - variance: number - is_active: boolean } export interface PrinterStateMacroParam { diff --git a/src/store/server/jobQueue/actions.ts b/src/store/server/jobQueue/actions.ts index 705eb09c8..f8afb3afd 100644 --- a/src/store/server/jobQueue/actions.ts +++ b/src/store/server/jobQueue/actions.ts @@ -18,8 +18,8 @@ export const actions: ActionTree = { }, async getStatus({ commit, dispatch }, payload) { - if ('queued_jobs' in payload) await commit('setQueuedJobs', payload.queued_jobs) - if ('queue_state' in payload) await commit('setQueueState', payload.queue_state) + if ('queued_jobs' in payload) commit('setQueuedJobs', payload.queued_jobs) + if ('queue_state' in payload) commit('setQueueState', payload.queue_state) await dispatch('socket/removeInitModule', 'server/jobQueue/init', { root: true }) }, @@ -28,29 +28,61 @@ export const actions: ActionTree = { Vue.$socket.emit('server.job_queue.post_job', { filenames: filenames }) }, - changeCount({ getters }, payload: { job_id: string; count: number }) { - const filenames: string[] = [] - const jobs = getters['getJobs'] - - jobs.forEach((job: ServerJobQueueStateJob) => { - if (job.job_id === payload.job_id) { - for (let i = 0; i < payload.count; i++) { - filenames.push(job.filename) - } - - return - } - - const count = (job.combinedIds?.length ?? 0) + 1 - for (let i = 0; i < count; i++) { - filenames.push(job.filename) - } - }) - - Vue.$socket.emit('server.job_queue.post_job', { - filenames, - reset: true, - }) + changeCount({ dispatch, getters }, payload: { job_id: string; count: number }) { + const jobs: ServerJobQueueStateJob[] = getters['getJobs'] + + const index = jobs.findIndex((job) => job.job_id === payload.job_id) + if (index === -1) return + + jobs[index].combinedIds = Array(payload.count - 1).fill(payload.job_id) + + dispatch('sendNewQueueList', { jobs }) + }, + + changePosition({ dispatch, getters }, payload: { oldIndex: number; newIndex: number }) { + const jobs: ServerJobQueueStateJob[] = getters['getJobs'] + + const job = jobs.splice(payload.oldIndex, 1)[0] + jobs.splice(payload.newIndex, 0, job) + + dispatch('sendNewQueueList', { jobs }) + }, + + startByJobId({ dispatch, getters }, job_id: string) { + const jobs: ServerJobQueueStateJob[] = getters['getJobs'] + + const index = jobs.findIndex((job) => job.job_id === job_id) + if (index === -1) return + + const job = jobs.splice(index, 1)[0] + jobs.splice(0, 0, job) + + dispatch('sendNewQueueList', { jobs, printStart: true }) + }, + + sendNewQueueList(_, payload: { jobs: ServerJobQueueStateJob[]; printStart?: boolean }) { + const filenames = payload.jobs + .map((job) => { + const numJobs = (job.combinedIds?.length ?? 0) + 1 + // return job.filename if the job will be only printed one time + if (numJobs === 1) return job.filename + + // return an array of job.filename if the job will be printed multiple times + return Array(numJobs).fill(job.filename) + }) + .flat() + + const emitOptions: { action?: string } = {} + if (payload.printStart) emitOptions.action = 'server/jobQueue/start' + + Vue.$socket.emit( + 'server.job_queue.post_job', + { + filenames, + reset: true, + }, + emitOptions + ) }, deleteFromQueue(_, job_ids: string[]) { diff --git a/src/store/server/spoolman/actions.ts b/src/store/server/spoolman/actions.ts index 510c23c75..1f3eaf2d5 100644 --- a/src/store/server/spoolman/actions.ts +++ b/src/store/server/spoolman/actions.ts @@ -3,6 +3,18 @@ import { ActionTree } from 'vuex' import { RootState } from '@/store/types' import { ServerSpoolmanState } from '@/store/server/spoolman/types' +function convertV2response(payload: { error?: { message: string } | null; response: any }) { + if ((payload.error?.message ?? null) !== null) { + Vue.$toast.error(payload.error?.message ?? 'unknown spoolman error') + return null + } + + // if the response is v2, we need to get the response into the payload + if ('response' in payload) return payload.response + + return payload +} + export const actions: ActionTree = { reset({ commit }) { commit('reset') @@ -15,6 +27,7 @@ export const actions: ActionTree = { { request_method: 'GET', path: '/v1/info', + use_v2_response: true, }, { action: 'server/spoolman/getInfo' } ) @@ -23,6 +36,7 @@ export const actions: ActionTree = { { request_method: 'GET', path: '/v1/health', + use_v2_response: true, }, { action: 'server/spoolman/getHealth' } ) @@ -31,6 +45,7 @@ export const actions: ActionTree = { { request_method: 'GET', path: '/v1/vendor', + use_v2_response: true, }, { action: 'server/spoolman/getVendors' } ) @@ -57,6 +72,7 @@ export const actions: ActionTree = { 'server.spoolman.proxy', { request_method: 'GET', + use_v2_response: true, path: `/v1/spool/${payload.spool_id}`, }, { action: 'server/spoolman/getActiveSpool' } @@ -65,29 +81,41 @@ export const actions: ActionTree = { getActiveSpool({ commit }, payload) { if ('requestParams' in payload) delete payload.requestParams + payload = convertV2response(payload) + if (payload === null) return commit('setActiveSpool', payload) }, getHealth({ commit, dispatch }, payload) { delete payload.requestParams - commit('setHealth', payload.status) dispatch('socket/removeInitModule', 'server/spoolman/getHealth', { root: true }) + + payload = convertV2response(payload) + if (payload === null) return + + commit('setHealth', payload.status) }, getInfo({ commit, dispatch }, payload) { delete payload.requestParams - commit('setInfo', payload) dispatch('socket/removeInitModule', 'server/spoolman/getInfo', { root: true }) + payload = convertV2response(payload) + if (payload === null) return + + commit('setInfo', payload) }, getVendors({ commit, dispatch }, payload) { delete payload.requestParams + dispatch('socket/removeInitModule', 'server/spoolman/getVendors', { root: true }) + payload = convertV2response(payload) + if (payload === null) return + commit( 'setVendors', Object.entries(payload).map((value) => value) ) - dispatch('socket/removeInitModule', 'server/spoolman/getVendors', { root: true }) }, refreshSpools({ dispatch }) { @@ -105,10 +133,12 @@ export const actions: ActionTree = { getSpools({ commit, dispatch }, payload) { if ('requestParams' in payload) delete payload.requestParams + dispatch('socket/removeLoading', 'refreshSpools', { root: true }) + payload = convertV2response(payload) + if (payload === null) return + const spools = Object.entries(payload).map((value) => value[1]) commit('setSpools', spools) - - dispatch('socket/removeLoading', 'refreshSpools', { root: true }) }, setActiveSpool(_, id: number | null) { diff --git a/src/store/variables.ts b/src/store/variables.ts index 8afa67f6f..f3676d781 100644 --- a/src/store/variables.ts +++ b/src/store/variables.ts @@ -4,7 +4,7 @@ export const defaultPrimaryColor = '#2196f3' export const defaultBigThumbnailBackground = '#1e1e1e' export const minKlipperVersion = 'v0.11.0-257' -export const minMoonrakerVersion = 'v0.8.0-137' +export const minMoonrakerVersion = 'v0.8.0-306' export const minBrowserVersions = [{ name: 'safari', version: '16.5.2' }] export const colorArray = ['#F44336', '#8e379d', '#03DAC5', '#3F51B5', '#ffde03', '#009688', '#E91E63']