diff --git a/CHANGELOG.md b/CHANGELOG.md index 3370096c..1b2b7ed0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,15 +2,26 @@ ## 0.7.1 +- Added "listened" status tracking for library files, incl. auto-mark on sync (opt-out) [https://github.com/toniebox-reverse-engineering/teddycloud/pull/467](https://github.com/toniebox-reverse-engineering/teddycloud/pull/467) +- Added total audio length (`lengthSeconds`) to the TAF info of the file index and the tag index [https://github.com/toniebox-reverse-engineering/teddycloud/pull/537](https://github.com/toniebox-reverse-engineering/teddycloud/pull/537) +- Fixed WARN log spam (incl. leaked session token) for every web UI request when login is enabled [https://github.com/toniebox-reverse-engineering/teddycloud/issues/485](https://github.com/toniebox-reverse-engineering/teddycloud/issues/485) +- Improved web radio / streaming reliability and performance, including continued playback of growing stream files and avoiding unnecessary encoder starts [https://github.com/toniebox-reverse-engineering/teddycloud/pull/478](https://github.com/toniebox-reverse-engineering/teddycloud/pull/478) [https://github.com/toniebox-reverse-engineering/teddycloud/pull/479](https://github.com/toniebox-reverse-engineering/teddycloud/pull/479) +- Fixed CC3235 boxes being incorrectly detected as Toniebox 2 instead of Toniebox 1 +- Improved box authentication and TLS/certificate handling, including fixes for authentication state reuse, TLS session handling and per-box certificate uploads [https://github.com/toniebox-reverse-engineering/teddycloud/pull/477](https://github.com/toniebox-reverse-engineering/teddycloud/pull/477) +- Added extensive security and robustness hardening across HTTP/API handling, file operations, uploads, TAF processing, RTNL, ESP32 firmware/NVS parsing and network handling, including fixes for path traversal, command injection, secret exposure, malformed-input crashes/hangs and memory-safety issues +- Server-side downloads now reject private/non-public network addresses by default to prevent SSRF; core.allowPrivateFetch can be enabled to explicitly allow internal sources such as a NAS +- Improved compatibility with real CC3200 and ESP32 Toniebox behavior based on captured traffic, including TLS, claim handling, cloud responses, content downloads and RTNL communication +- Improved runtime stability during settings/configuration reloads and reduced memory usage in settings lookup and audio encoding +- Improved HTTP and SSE reliability, including request parsing, range handling, locking and response initialization +- Fixed Docker startup when the configured PUID/PGID already belongs to an existing account in the base image +- Greatly expanded automated backend testing and CI, including isolated test environments, C and pytest test suites, simulated Tonieboxes with hardware-specific profiles, a fake Boxine cloud and additional security regression tests [https://github.com/toniebox-reverse-engineering/teddycloud/pull/461](https://github.com/toniebox-reverse-engineering/teddycloud/pull/461) +- Added and expanded architecture, settings, streaming and development documentation - gui: Download TAF tracks as separate .ogg files (ZIP when more than one track is selected) [https://github.com/toniebox-reverse-engineering/teddycloud/issues/169](https://github.com/toniebox-reverse-engineering/teddycloud/issues/169) - gui: Box setup can create a port patch for CC3200 boxes (altPort.custom.json) and patch the port of ESP32 boxes, e.g. for a proxy that routes boxes by port - gui: Certificate check per box (issued by TeddyCloud / pinned / other certificate) on the box card and in the certificates dialog [https://github.com/toniebox-reverse-engineering/teddycloud/issues/85](https://github.com/toniebox-reverse-engineering/teddycloud/issues/85) -- Added "listened" status tracking for library files, incl. auto-mark on sync (opt-out) [https://github.com/toniebox-reverse-engineering/teddycloud/pull/467](https://github.com/toniebox-reverse-engineering/teddycloud/pull/467) -- Added total audio length (`lengthSeconds`) to the TAF info of the file index and the tag index [https://github.com/toniebox-reverse-engineering/teddycloud/pull/537](https://github.com/toniebox-reverse-engineering/teddycloud/pull/537) - gui: Optional web UI login with multiple users (all full UI rights) [https://github.com/toniebox-reverse-engineering/teddycloud/issues/85](https://github.com/toniebox-reverse-engineering/teddycloud/issues/85) - gui: Rate-limit web login after repeated failures -- Fixed WARN log spam (incl. leaked session token) for every web UI request when login is enabled [https://github.com/toniebox-reverse-engineering/teddycloud/issues/485](https://github.com/toniebox-reverse-engineering/teddycloud/issues/485) -- Fixed auto-redirect from base URL to web UI returning "Login required" when web login is enabled [https://github.com/toniebox-reverse-engineering/teddycloud_web/issues/333](https://github.com/toniebox-reverse-engineering/teddycloud_web/issues/333) +- gui: Fixed auto-redirect from base URL to web UI returning "Login required" when web login is enabled [https://github.com/toniebox-reverse-engineering/teddycloud_web/issues/333](https://github.com/toniebox-reverse-engineering/teddycloud_web/issues/333) - gui: fixed missing images in library [https://github.com/toniebox-reverse-engineering/teddycloud_web/issues/320](https://github.com/toniebox-reverse-engineering/teddycloud_web/issues/320) - gui: Added Polish (pl) language support [https://github.com/toniebox-reverse-engineering/teddycloud_web/pull/325](https://github.com/toniebox-reverse-engineering/teddycloud_web/pull/325) - gui: Added "assign to Tonie" action directly from the library file browser [https://github.com/toniebox-reverse-engineering/teddycloud_web/issues/240](https://github.com/toniebox-reverse-engineering/teddycloud_web/issues/240) @@ -33,12 +44,16 @@ - gui: Fixed custom Tonie track filtering when `sourceInfo.tracks` is empty but `tonieInfo.tracks` contains track data - gui: Fixed sibling episode assignment for library-root files and improved handling of failed directory requests - gui: Expanded automated test coverage for authentication, API requests and core Tonie/library logic +- gui: Added support for the optional `hideInNav` property in plugin `plugin.json` files to prevent plugins from being shown in the navigation +- gui: Added pagination in plugin list - gui: Fixed web login edge cases: the login page now appears when login gets enabled while the UI is open, and logging out while TeddyCloud is unreachable no longer leaves the logged-in view on screen - gui: Failed file downloads in the library and the Tonie information dialog now show an error notification instead of failing silently - gui: A failed upload in the browser-side (WASM) audio encoder no longer additionally raises an uncaught promise rejection after showing the error notification - gui: Connected the "edit metadata" action of TAF files in the file browser with the Custom Model Editor, incl. track rows from the TAF header with their duration and a hint when a custom model overrides an original tonie [https://github.com/toniebox-reverse-engineering/teddycloud_web/issues/341](https://github.com/toniebox-reverse-engineering/teddycloud_web/issues/341) - gui: Track names in the Custom Model Editor keep their position, unnamed tracks in between are no longer dropped - gui: Box setup can create a port patch for CC3200 boxes (altPort.custom.json) and patch the port of ESP32 boxes, e.g. for a proxy that routes boxes by port (not possible for CC3235 boxes and the Toniebox 2) +- gui: Show a loading spinner and prevent repeated clicks while a TonieCard background download is in progress +- gui: Fixed TAF track downloads in the TonieInformationModal to use the correct linked/source TAF for Content and Tonies views, including uncached Tonies, and keep track selections across re-renders, screen size changes and audio player updates ### Commits diff --git a/package-lock.json b/package-lock.json index 37364641..f35eeca6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,27 +13,27 @@ "@dnd-kit/sortable": "^10.0.0", "@remix-run/router": "^1.23.4", "@types/jest": "^30.0.0", - "@types/node": "^26.6.4", + "@types/node": "^26.6.5", "@types/react": "^19.3.0", "@types/react-dom": "^19.3.0", "@types/w3c-web-serial": "^1.0.8", "@vitejs/plugin-react": "^6.1.2", - "antd": "^6.6.5", + "antd": "^6.6.6", "formik": "^2.4.9", - "i18next": "^26.4.2", + "i18next": "^26.4.7", "i18next-browser-languagedetector": "^8.2.1", - "i18next-http-backend": "^4.0.2", + "i18next-http-backend": "^4.0.3", "jszip": "^3.10.2", - "react": "^19.3.0", - "react-dom": "^19.3.0", - "react-i18next": "^17.0.16", + "react": "^19.3.1", + "react-dom": "^19.3.1", + "react-i18next": "^17.0.20", "react-markdown": "^10.1.0", "react-minimal-pie-chart": "^9.1.2", "react-router-dom": "^7.18.4", - "react-syntax-highlighter": "^16.1.1", - "styled-components": "^6.5.3", + "react-syntax-highlighter": "^16.1.2", + "styled-components": "^6.6.0", "typescript": "^7.0.2", - "vite": "^8.3.3", + "vite": "^8.3.4", "vite-plugin-svgr": "^5.2.0", "yup": "^1.7.1" }, @@ -41,11 +41,11 @@ "@babel/plugin-transform-private-property-in-object": "^8.0.5", "@types/js-yaml": "^4.0.9", "@types/react-syntax-highlighter": "^15.5.13", - "concurrently": "^10.0.5", + "concurrently": "^10.0.6", "cross-env": "^10.1.0", - "jsdom": "^30.1.2", - "prettier": "^3.9.9", - "vitest": "^5.0.3" + "jsdom": "^30.1.3", + "prettier": "^3.9.10", + "vitest": "^5.0.5" } }, "node_modules/@ant-design/colors": { @@ -159,9 +159,9 @@ } }, "node_modules/@asamuzakjp/dom-selector": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-9.2.4.tgz", - "integrity": "sha512-YnVzxDxLaqL0t7Q4wfcgHZjg55Wm6y5EaO7Bhu3q9J7wq/8iw3PhsnWiOQhYekx4VexnbkgWpTT6n+QIV7kczw==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-10.0.3.tgz", + "integrity": "sha512-3+777rEPsOOjoy5/sRmIzHnoRDaLpm3XWY2dmWn/N7f/XwDG0bcpnFaKL3X9FYJUbNwLmpcJC1bUYexJRxsFlw==", "dev": true, "license": "MIT", "dependencies": { @@ -1389,13 +1389,13 @@ } }, "node_modules/@rc-component/notification": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@rc-component/notification/-/notification-2.0.8.tgz", - "integrity": "sha512-MTRA3b8eHE14gh5R9nlXzbWZEDEBFVkEHj/FI1VOXw2zrSnSxqbuuU3drsq+k53e6MHwtegoFay1tnNYhtl61Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@rc-component/notification/-/notification-2.1.1.tgz", + "integrity": "sha512-HD+Gx/HUjivBI6GZtfPVLjvOJZybSEIoE2upnmoVg4w9vZQHMaIPWP6B52cHWOGg+u48HBbO+HnyVNN8AKQxgQ==", "license": "MIT", "dependencies": { "@rc-component/motion": "^1.1.4", - "@rc-component/util": "^1.11.0", + "@rc-component/util": "^1.13.0", "clsx": "^2.1.1" }, "engines": { @@ -3110,9 +3110,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "26.6.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.4.tgz", - "integrity": "sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==", + "version": "26.6.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.5.tgz", + "integrity": "sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg==", "license": "MIT", "dependencies": { "undici-types": "~8.9.0" @@ -3158,6 +3158,12 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "license": "MIT" }, + "node_modules/@types/stylis": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.7.tgz", + "integrity": "sha512-VgDNokpBoKF+wrdvhAAfS55OMQpL6QRglwTwNC3kIgBrzZxA4WsFj+2eLfEA/uMUDzBcEhYmjSbwQakn/i3ajA==", + "license": "MIT" + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -3541,22 +3547,22 @@ } }, "node_modules/@vitest/mocker": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.3.tgz", - "integrity": "sha512-T8sWAIbkSyAjkwTcaEc3Iu0o9A27X1/kdXrizhZkGuSKScRQtRzclfAMpOTcGdXCsqxeWlpGy3XjqaW8CpLORg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.5.tgz", + "integrity": "sha512-xLL3lkDegJZw+MGoGaCCsFayDZthGgpAtr3Vziyfc4p7xH89K7KRPcRBBh/kEYm0Aqkt5DtTmkYpBtRV/FrHZg==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.31", - "@vitest/spy": "5.0.3", + "@vitest/spy": "5.0.5", "estree-walker": "^3.0.3", - "magic-string": "^1.2.3" + "magic-string": "^1.4.3" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "msw": "^2.4.9", + "msw": "^2.4.9 || ^3.0.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { @@ -3579,9 +3585,9 @@ } }, "node_modules/@vitest/spy": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.3.tgz", - "integrity": "sha512-XhFysQTB8AZ+P4gMi+Lpo99vg2AZi0qKpaB9yXQl37+CaMEAPO3iH/wGVnSyL5MPERiLezpqTVtrR6UZH5GCXg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.5.tgz", + "integrity": "sha512-lwJRExuH417257Q5wv9QYugN9IMJkoxcrKrYxGPQkBzl7wlNpTUxD/5yNX4ESKVUIShKPgkGchYPFMWOOk0wzw==", "dev": true, "license": "MIT", "funding": { @@ -3614,9 +3620,9 @@ } }, "node_modules/antd": { - "version": "6.6.5", - "resolved": "https://registry.npmjs.org/antd/-/antd-6.6.5.tgz", - "integrity": "sha512-biJB/aORNU2lvZZCX783S/g8atfE8akrEKRjRrsXVttoMtJbSHLf/Z29niv0blzt6H3BgKvhkQgHoUn5MgCx7w==", + "version": "6.6.6", + "resolved": "https://registry.npmjs.org/antd/-/antd-6.6.6.tgz", + "integrity": "sha512-+x4M755a6kyZyxkN2vpfm3HbYkyytPBPSncXHKPHU2ZPs3Z1ctrWwWApe/4ek4O0eUGKNFCpb461yficetktYQ==", "license": "MIT", "dependencies": { "@ant-design/colors": "^8.0.1", @@ -3642,7 +3648,7 @@ "@rc-component/menu": "~1.5.0", "@rc-component/motion": "^1.3.3", "@rc-component/mutate-observer": "^2.0.1", - "@rc-component/notification": "~2.0.8", + "@rc-component/notification": "~2.1.1", "@rc-component/pagination": "~1.4.0", "@rc-component/picker": "~1.12.2", "@rc-component/progress": "~1.0.3", @@ -3955,15 +3961,15 @@ "license": "MIT" }, "node_modules/concurrently": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-10.0.5.tgz", - "integrity": "sha512-JaP/CoftUrCcAFW/g//RbgEGwlelnEae6cfBLgH6ZdO6s8jPkn6p9SB9u6pdVxYXoiSnFqseOlHfrEfF82TVOg==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-10.0.6.tgz", + "integrity": "sha512-qgICXXFp2/3nAebRLmxcINKEXM1mLK3ij/y31P02VTs/KIkddIeC05n73DxOXsXMB9LFSqNU1/na3lDlOhcbIw==", "dev": true, "license": "MIT", "dependencies": { "chalk": "5.6.2", "rxjs": "7.8.2", - "shell-quote": "1.9.0", + "shell-quote": "1.12.0", "supports-color": "10.2.2", "tree-kill": "1.2.2", "yargs": "18.0.0" @@ -4586,9 +4592,9 @@ } }, "node_modules/i18next": { - "version": "26.4.2", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.4.2.tgz", - "integrity": "sha512-RX+R0VLg13IbvRuJSxnqykUFS9vQZTl8wYpWPCIUDWVrSGjsQywB5Y+pjzrkboxGAuYfJZVH1InFTdgBdxq6ug==", + "version": "26.4.7", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.4.7.tgz", + "integrity": "sha512-p/3hqy6EcuqRs6wNwBYwGGC1fzBJSMxV9zMuN814ab/7QjdZbXfjXwuPZdQbngX6KDOTxM01uUiZyqmMB8ghZg==", "funding": [ { "type": "individual", @@ -4623,9 +4629,9 @@ } }, "node_modules/i18next-http-backend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-4.0.2.tgz", - "integrity": "sha512-oay62dIB2kL7+WHzoUXBjWfL3+mwijD3pQkQkIEaRLhy6kjXxUhrRenV0gInwlCASAaJaDy94+XvYizK+cAGdA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-4.0.3.tgz", + "integrity": "sha512-B5GJysCsJwqiDQRpUcikWsut82Vmekix58WIyiCpYtxaZ/wY8kwJCicMRvVcMoIa1x84siiRl5vBt6iCBVO6zg==", "license": "MIT", "engines": { "node": ">=18" @@ -5087,14 +5093,14 @@ } }, "node_modules/jsdom": { - "version": "30.1.2", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-30.1.2.tgz", - "integrity": "sha512-0FFE/jE1rppmVfUrJUgxqXjcwolZYIGtAgj1pTussMhNZxIxi7W/PvfWaMNroGi2B36X1IKHbexpZ9DhkoOPiQ==", + "version": "30.1.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-30.1.3.tgz", + "integrity": "sha512-hQMUPvRgN7KxsfIytDQji7bimuJeRQa6Qo5+iuiZQk5CafeRxLIA10/cG5YCBFNpD9RO3oqIiN4CO1Y44kxqXg==", "dev": true, "license": "MIT", "dependencies": { "@asamuzakjp/css-color": "^7.1.3", - "@asamuzakjp/dom-selector": "^9.2.4", + "@asamuzakjp/dom-selector": "^10.0.3", "@bramus/specificity": "^2.4.2", "@csstools/css-syntax-patches-for-csstree": "^1.1.15", "@exodus/bytes": "^1.16.0", @@ -5107,7 +5113,7 @@ "parse5": "^8.0.1", "saxes": "^6.0.0", "tough-cookie": "^6.0.2", - "undici": "^8.11.2", + "undici": "^8.12.1", "w3c-xmlserializer": "^6.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", @@ -5497,9 +5503,9 @@ } }, "node_modules/magic-string": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.2.tgz", - "integrity": "sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz", + "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6115,9 +6121,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.19", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", - "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "funding": [ { "type": "github", @@ -6319,9 +6325,9 @@ } }, "node_modules/postcss": { - "version": "8.5.28", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", - "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "version": "8.5.30", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.30.tgz", + "integrity": "sha512-zkd+e9S1AQHhmaBKye9zlFHWjOwVgqXl1q3ECYy8561K7qxR2cREf/STLskulBFd78k6AOFeA+ff1TtEzk9bAA==", "funding": [ { "type": "opencollective", @@ -6338,9 +6344,9 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.18", + "nanoid": "^3.3.20", "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.2" }, "engines": { "node": "^10 || ^12 || >=14" @@ -6353,9 +6359,9 @@ "license": "MIT" }, "node_modules/prettier": { - "version": "3.9.9", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.9.tgz", - "integrity": "sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg==", + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.10.tgz", + "integrity": "sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==", "dev": true, "license": "MIT", "bin": { @@ -6425,24 +6431,24 @@ } }, "node_modules/react": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", - "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.1.tgz", + "integrity": "sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz", - "integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==", + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.1.tgz", + "integrity": "sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==", "license": "MIT", "dependencies": { "scheduler": "^0.28.0" }, "peerDependencies": { - "react": "^19.3.0" + "react": "^19.3.1" } }, "node_modules/react-fast-compare": { @@ -6452,9 +6458,9 @@ "license": "MIT" }, "node_modules/react-i18next": { - "version": "17.0.16", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.16.tgz", - "integrity": "sha512-7uImVD9hG5yUOaP5KHVied1x0A004Di/KU031ULR35Ma+9e9H5GdnNZHgQEIByyAz+IAXBkRDTedNVzjPUH8ZA==", + "version": "17.0.20", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.20.tgz", + "integrity": "sha512-rzSSirf0NP9vfcUXHZAksIuyMWl7L/T/7nm8S+sSIFuIzku54qcyyVAOZXbawPNpUxGkDx/eosSv4DrpzcxuOQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.29.7", @@ -6577,9 +6583,9 @@ } }, "node_modules/react-syntax-highlighter": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.1.tgz", - "integrity": "sha512-PjVawBGy80C6YbC5DDZJeUjBmC7skaoEUdvfFQediQHgCL7aKyVHe57SaJGfQsloGDac+gCpTfRdtxzWWKmCXA==", + "version": "16.1.2", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.2.tgz", + "integrity": "sha512-4dM0+mYtfnhgwYWAvFx56fgyjIYk2yFopfiICFvMDhRfeSGRNd+ngFALdPD9q7llyGAmiE6pzOSOwI8Lf9ca1w==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.28.4", @@ -6806,9 +6812,9 @@ } }, "node_modules/shell-quote": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.9.0.tgz", - "integrity": "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.12.0.tgz", + "integrity": "sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q==", "dev": true, "license": "MIT", "engines": { @@ -6957,12 +6963,13 @@ } }, "node_modules/styled-components": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.5.3.tgz", - "integrity": "sha512-vAX79sfpmUerP9fsTTxoTrBDE0RuO4ahjInyWYoohNgqrdg63Ms4q6FJ/o2Fyity82NU3cujOT8Ewl9TThBdwg==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.6.0.tgz", + "integrity": "sha512-drKKsqvDBxVRn4ySU67iQ5nQBuo/1xyYeBfdhDde112PkqrS3+95KPnpDs5hFRcmrW+q6+gbyywYYYR14oWFtw==", "license": "MIT", "dependencies": { "@emotion/is-prop-valid": "1.4.0", + "@types/stylis": "4.2.7", "css-to-react-native": "3.2.0", "csstype": "3.2.3", "stylis": "4.3.6" @@ -7054,9 +7061,9 @@ "license": "MIT" }, "node_modules/tinybench": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.0.tgz", - "integrity": "sha512-78U2TlB2CnVenajOFzf3BKSm0J6oz5L0NV7g32LCPccvYc0lbWvys4d3uUUCS2B1N8PAf2+aekR8i1KbC3HO7Q==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.2.tgz", + "integrity": "sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==", "dev": true, "license": "MIT", "engines": { @@ -7224,9 +7231,9 @@ } }, "node_modules/undici": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/undici/-/undici-8.11.2.tgz", - "integrity": "sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.12.2.tgz", + "integrity": "sha512-XtuDWVdqTLr3NAxm2ly9/hU6kUsJOgGh1RASzMBbsw6aWCOaQzHdW+QQDfdERjfJ2V5vL5pRPa4RetO9UfivDg==", "dev": true, "license": "MIT", "engines": { @@ -7413,15 +7420,15 @@ } }, "node_modules/vite": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.3.tgz", - "integrity": "sha512-cTAldKPImjg6c+gk48U19POPn3GCBzZwpdsN8ZMEEcbpes+6/wvfqUd0C2y3qYY4wsj8PwgFwrZ/1jBPVttMSg==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.4.tgz", + "integrity": "sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ==", "license": "MIT", "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", - "postcss": "^8.5.28", - "rolldown": "~1.2.11", + "postcss": "^8.5.29", + "rolldown": "~1.2.12", "tinyglobby": "^0.2.17" }, "bin": { @@ -7504,23 +7511,23 @@ } }, "node_modules/vitest": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.3.tgz", - "integrity": "sha512-xMw97S3rjdtj5dkVat7jCsqWBpvchs3RlpQctUqwJD0KkERk40vz2fJ77lDwW/Vzh/pk18eItYAzkodhSes3jQ==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.5.tgz", + "integrity": "sha512-cPODOMF8YnkiiYtDdHo6n4wWmBrOBBbOIYH2d33M4l3CdNn72h+qMmO6uXffUN6A5AZFXNND/d+krS9mgkXRfg==", "dev": true, "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", - "@vitest/mocker": "5.0.3", - "chai": "^6.2.2", + "@vitest/mocker": "5.0.5", + "chai": "^6.3.0", "es-module-lexer": "^2.3.2", "expect-type": "^1.4.0", - "magic-string": "^1.2.3", - "obug": "^2.1.4", + "magic-string": "^1.4.3", + "obug": "^3.0.0", "picomatch": "^4.0.7", - "std-env": "^4.2.0", - "tinybench": "^6.1.4", - "tinyexec": "^1.3.0", + "std-env": "^4.3.0", + "tinybench": "^6.2.2", + "tinyexec": "^1.3.1", "tinyglobby": "^0.2.17", "why-is-node-running": "3.2.1" }, @@ -7537,12 +7544,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "5.0.3", - "@vitest/browser-preview": "5.0.3", + "@vitest/browser-playwright": "5.0.5", + "@vitest/browser-preview": "5.0.5", "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", - "@vitest/coverage-istanbul": "5.0.3", - "@vitest/coverage-v8": "5.0.3", - "@vitest/ui": "5.0.3", + "@vitest/coverage-istanbul": "5.0.5", + "@vitest/coverage-v8": "5.0.5", + "@vitest/ui": "5.0.5", "happy-dom": "*", "jsdom": "*", "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" @@ -7586,6 +7593,20 @@ } } }, + "node_modules/vitest/node_modules/obug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/obug/-/obug-3.0.0.tgz", + "integrity": "sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/w3c-xmlserializer": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-6.0.0.tgz", diff --git a/package.json b/package.json index d857c3cf..f707197d 100644 --- a/package.json +++ b/package.json @@ -9,27 +9,27 @@ "@dnd-kit/sortable": "^10.0.0", "@remix-run/router": "^1.23.4", "@types/jest": "^30.0.0", - "@types/node": "^26.6.4", + "@types/node": "^26.6.5", "@types/react": "^19.3.0", "@types/react-dom": "^19.3.0", "@types/w3c-web-serial": "^1.0.8", "@vitejs/plugin-react": "^6.1.2", - "antd": "^6.6.5", + "antd": "^6.6.6", "formik": "^2.4.9", - "i18next": "^26.4.2", + "i18next": "^26.4.7", "i18next-browser-languagedetector": "^8.2.1", - "i18next-http-backend": "^4.0.2", + "i18next-http-backend": "^4.0.3", "jszip": "^3.10.2", - "react": "^19.3.0", - "react-dom": "^19.3.0", - "react-i18next": "^17.0.16", + "react": "^19.3.1", + "react-dom": "^19.3.1", + "react-i18next": "^17.0.20", "react-markdown": "^10.1.0", "react-minimal-pie-chart": "^9.1.2", "react-router-dom": "^7.18.4", - "react-syntax-highlighter": "^16.1.1", - "styled-components": "^6.5.3", + "react-syntax-highlighter": "^16.1.2", + "styled-components": "^6.6.0", "typescript": "^7.0.2", - "vite": "^8.3.3", + "vite": "^8.3.4", "vite-plugin-svgr": "^5.2.0", "yup": "^1.7.1" }, @@ -37,11 +37,11 @@ "@babel/plugin-transform-private-property-in-object": "^8.0.5", "@types/js-yaml": "^4.0.9", "@types/react-syntax-highlighter": "^15.5.13", - "concurrently": "^10.0.5", + "concurrently": "^10.0.6", "cross-env": "^10.1.0", - "jsdom": "^30.1.2", - "prettier": "^3.9.9", - "vitest": "^5.0.3" + "jsdom": "^30.1.3", + "prettier": "^3.9.10", + "vitest": "^5.0.5" }, "scripts": { "start-http": "cross-env PORT=3000 vite", diff --git a/public/translations/de.json b/public/translations/de.json index c2dcd5bc..839c7e56 100644 --- a/public/translations/de.json +++ b/public/translations/de.json @@ -21,11 +21,11 @@ "password": "Passwort", "passwordMin": "Mindestens 4 Zeichen", "passwordRequired": "Passwort ist erforderlich", - "username": "Benutzername", - "usernameRequired": "Benutzername ist erforderlich", + "retry": "Erneut versuchen", "statusLoadFailed": "Authentifizierungsstatus konnte nicht geladen werden", "statusLoadFailedDetails": "Der Authentifizierungsstatus konnte nicht geladen werden. Prüfe die Verbindung zu TeddyCloud und versuche es erneut.", - "retry": "Erneut versuchen" + "username": "Benutzername", + "usernameRequired": "Benutzername ist erforderlich" }, "common": { "cancel": "Abbrechen" @@ -142,6 +142,7 @@ "fields": { "author": "Autor des Plugins", "description": "Kurze Zusammenfassung des Plugins", + "hideInNav": "Flag, das angibt, ob das Plugin in der Navigation ausgeblendet werden soll (true/false)", "icon": "Das für das Plugin verwendete Icon. Muss eine gültige Ant Design Icon-Komponente von https://ant.design/components/icon sein, z. B. TrophyOutlined, TagsOutlined", "pluginHomepage": "Homepage des Plugins", "pluginName": "Der Titel, der in Menüs angezeigt wird (erforderlich)", @@ -157,11 +158,16 @@ }, "helpButton": "Hilfe", "helpTextCreatePlugin": "Ein Text darüber, wie man ein Plugin erstellt", + "hidden": "Versteckt", "installedPlugins": "Installierte Plugins", "intro": "Hier findest du deine hinzugefügten TeddyCloud Plugins und kannst neue Plugins von dir oder der TeddyCloud Community hinzufügen!", "navigationTitle": "TC Plugins", "ok": "OK", "open": "Plugin öffnen", + "pagination": { + "pageSelector": "Plugins pro Seite", + "showAll": "Alle anzeigen" + }, "plugin": "Plugin", "title": "TeddyCloud Plugins", "upload": { @@ -391,10 +397,18 @@ "filter": "Filter im aktuellen Ordner", "help": { "actionItems": { + "assignToTonie": { + "text": "Weist die ausgewählte Datei einem Tonie/Tag als Inhalt zu.", + "title": "Tonie zuweisen" + }, "deleteFileFolder": { "text": "Löscht die Datei oder den Ordner (nur wenn der Ordner leer ist). Hinweis: Wenn diese Datei einem Tonie/Tag zugewiesen ist, wird der Verweis nicht aktualisiert.", "title": "Datei/Ordner löschen" }, + "downloadFile": { + "text": "Lädt die ausgewählte Datei herunter. Bei TAF-Dateien mit mehreren Tracks öffnet sich eine Track-Auswahl, um die Tracks als einzelne *.ogg-Dateien (ZIP) oder als eine *.ogg-Datei herunterzuladen; TAF-Dateien mit nur einem Track werden als eine *.ogg-Datei heruntergeladen.", + "title": "Datei herunterladen" + }, "duplicateTAPFile": { "text": "Erstellt eine Kopie der ausgewählten TAP-Datei.", "title": "TAP-Datei duplizieren" @@ -407,6 +421,16 @@ "text": "Öffnet den Tonie-Audio-Playlist-Editor, um TAP-Dateien zu verwalten.", "title": "TAP-Playlist bearbeiten" }, + "markAsListened": { + "listened": { + "text": "Die Datei ist derzeit als gehört markiert. Klicke, um sie als nicht gehört zu markieren.", + "title": "Als nicht gehört markieren" + }, + "unlistened": { + "text": "Die Datei ist derzeit als nicht gehört markiert. Klicke, um sie als gehört zu markieren.", + "title": "Als gehört markieren" + } + }, "migrateToAudioIdFolder": { "text": "Verschiebt die TAF-Datei in den Ordner 'by/audioId' der Library und aktualisiert die Quelle in der JSON-Datei.", "title": "TAF-Datei nach AudioId in die Library verschieben" @@ -426,24 +450,6 @@ "renameFile": { "text": "Benennt die Datei um. Hinweis: Wenn diese Datei einem Tonie/Tag zugewiesen ist, wird der Verweis nicht automatisch aktualisiert.", "title": "Datei umbenennen" - }, - "downloadFile": { - "title": "Datei herunterladen", - "text": "Lädt die ausgewählte Datei herunter. Bei TAF-Dateien mit mehreren Tracks öffnet sich eine Track-Auswahl, um die Tracks als einzelne *.ogg-Dateien (ZIP) oder als eine *.ogg-Datei herunterzuladen; TAF-Dateien mit nur einem Track werden als eine *.ogg-Datei heruntergeladen." - }, - "markAsListened": { - "unlistened": { - "title": "Als gehört markieren", - "text": "Die Datei ist derzeit als nicht gehört markiert. Klicke, um sie als gehört zu markieren." - }, - "listened": { - "title": "Als nicht gehört markieren", - "text": "Die Datei ist derzeit als gehört markiert. Klicke, um sie als nicht gehört zu markieren." - } - }, - "assignToTonie": { - "title": "Tonie zuweisen", - "text": "Weist die ausgewählte Datei einem Tonie/Tag als Inhalt zu." } }, "actionItemsMulti": { @@ -1175,6 +1181,19 @@ "status": "Status", "troubleShooting": "Fehlerbehebung" }, + "boxCertificate": { + "nextConnection": "Der Status wird bei der nächsten Verbindung der Box aktualisiert.", + "pin": "Gepinntes Zertifikat (SHA-256)", + "presented": "Vorgezeigtes Zertifikat (SHA-256)", + "status": "Status", + "statusText": { + "mismatch": "Anderes Zertifikat als das gepinnte!", + "none": "Noch nicht geprüft", + "pinned": "Gepinntes Zertifikat", + "trusted": "Zertifikat von TeddyCloud ausgestellt" + }, + "title": "Zertifikatsprüfung" + }, "boxFlashingCommon": { "adguard": { "adGuardRequirement": "AdGuard: Stelle sicher, dass AdGuard Home auf einem Gerät in deinem Netzwerk installiert und konfiguriert ist.", @@ -1385,6 +1404,7 @@ }, "applyingPatches": "Patches anwenden", "applyingPatchesSection": { + "altPortHint": "Wenn du einen Port-Patch erstellt hast, lege ihn ebenfalls nach /revvox/boot/patch/ und ergänze \"altPort.custom\" in der Liste der Patches.", "examplengCFGJsonCollapse": { "collapseTitle": "Beispiel \"ngCfg.json\" mit dem Patch \"altUrl.custom.305\" unter Verwendung von \"ofw2\"" }, @@ -1426,8 +1446,7 @@ "waitForPressDesc": "Wartet beim Start auf einen Druck auf das Ohr mit einer blinkenden Sequenz (blau, grün, cyan, schwarz)", "waitTimeoutInSDesc": "Wartezeit in Sekunden für waitForPress, falls kein Druck auf das Ohr (Schlafmodus)", "watchdogDesc": "Halte den Watchdog beim Start der Firmware aktiviert (wenn das Booten fehlschlägt, startet die Box neu)" - }, - "altPortHint": "Wenn du einen Port-Patch erstellt hast, lege ihn ebenfalls nach /revvox/boot/patch/ und ergänze \"altPort.custom\" in der Liste der Patches." + } }, "applyingPatchesWithNgCfgJson": "Patches mit ngCfg.json anwenden", "applyingPatchesWithNgCfgJsonLink": "Hier findest du die Anleitung", @@ -1450,13 +1469,10 @@ "connectToTonieboxProceed": "Jetzt bist du bereit, weiterzumachen!", "connectToTonieboxText": "Die grüne LED auf der Hauptplatine der Toniebox sollte dauerhaft leuchten (ohne zu blinken). Falls dies nicht der Fall ist, sind deine Verbindungen möglicherweise locker oder falsch. Trenne den Programmer, überprüfe alle Verbindungen sorgfältig und versuche es erneut. Wenn du mit dünnen Drähten arbeitest, könntest du Heißkleber verwenden, um sie zu fixieren und Bewegungen zu verhindern. Stelle außerdem sicher, dass die Toniebox über das Ladegerät mit Strom versorgt wird.", "connectToTonieboxTextUart": "Stelle sicher, dass der UART auf 3,3V konfiguriert ist, bevor du den UART-Programmer über USB mit deinem Computer verbindest.", - "createPatch": "Erstelle altUrl.custom.305.patch", + "createHostNamePatch": "Erstelle altUrl.custom.305.patch", + "createPortPatch": "Erstelle altPort.custom.patch", "customPortPatch": "Optional: Port-Patch erstellen (altPort.custom.json)", "customPortPatchHint": "Nur nötig, wenn die Box TeddyCloud über einen anderen Port als 443 erreichen soll, z. B. über einen Proxy, der nach Port verteilt. Der Patch funktioniert mit den Firmware-Versionen 3.1.0 BF2, 3.3.0 und 3.4.0. hackiebox-ng bringt bereits altPort.json für Port 10443 mit; ein eigener Patch ist nur für einen anderen Port nötig. CC3235-Boxen und die Toniebox 2 lassen sich nicht patchen: Sie verbinden sich immer mit Port 443.", - "hintPatchPort": "Gib den Port ein, mit dem sich die Box verbinden soll, und klicke dann auf 'Erstelle altPort.custom.patch'. Der Patch wird danach automatisch heruntergeladen.", - "port": "TeddyCloud-Port:", - "portInvalid": "Der Port muss zwischen 1 und 65535 liegen.", - "createPortPatch": "Erstelle altPort.custom.patch", "customUrlPatch": "Erstelle einen benutzerdefinierten URL-Patch (altUrl.custom.305.json)", "customUrlPatchHint": "Bitte erstelle diesen Patch nur, wenn die vordefinierten Patches altUrl.tc.fritz.box.json und altUrl.305.json mit deiner aktuellen TeddyCloud-Installation nicht funktionieren. Wenn sie deinen Anforderungen entsprechen, kannst du mit 'Weiter' fortfahren.", "dedicatedUart": "Dedizierter UART", @@ -1479,6 +1495,7 @@ "hint": "Es gibt derzeit keine direkte Unterstützung, um deine CC3200 Toniebox mit TeddyCloud zu flashen, aber hier findest du eine Schritt-für-Schritt-Anleitung:", "hint2": "Bitte folge dann dem CC3200-Pfad:", "hintPatchHost": "Gib den Hostnamen deiner TeddyCloud-Instanz ein und klicke dann auf 'Erstelle altUrl.custom.305.patch'. Der Patch wird danach automatisch heruntergeladen.", + "hintPatchPort": "Gib den Port ein, mit dem sich die Box verbinden soll, und klicke dann auf 'Erstelle altPort.custom.patch'. Der Patch wird danach automatisch heruntergeladen.", "hostname": "TeddyCloud-Hostname:", "hostnameTooLong": "Der Hostname darf nicht länger als 12 Zeichen sein.", "installBootloader": "Installiere den HackieboxNG Custom Bootloader", @@ -1545,6 +1562,8 @@ "next": "Weiter", "patches": "Patches", "patchesMoreInformationLink": "Weitere Informationen zu benutzerdefinierten Patches", + "port": "TeddyCloud-Port:", + "portInvalid": "Der Port muss zwischen 1 und 65535 liegen.", "predefinedUrlPatches": "Vordefinierte URL-Patches", "predefinedUrlPatchesIntro": "Für CC3200-Boxen wird empfohlen, einen altUrl-Patch zu verwenden. HackieboxNG bringt zwei vordefinierte altUrl-Patches bereits mit:", "previous": "Zurück", @@ -1620,7 +1639,6 @@ "locateFlashMemoryText": "Identifiziere den IS25LP032D-Flash-Speicher-Chip auf der Platine; er befindet sich rechts von der Ladestation.", "navigationTitle": "CC3235", "next": "Weiter", - "portHint": "CC3235-Boxen verbinden sich immer mit Port 443: Für sie gibt es keinen Port-Patch, TeddyCloud muss also auf Port 443 erreichbar sein (ein Proxy kann sie nur nach IP verteilen).", "pico": { "preparation": "Nur erforderlich, wenn du das Flashen mit einem Raspberry Pi Pico durchführen möchtest", "readingFlash": "Flash-Lesen mit Raspberry Pi Pico", @@ -1628,6 +1646,7 @@ "readingFlashText2": "Es wird empfohlen, mehrere Lesevorgänge durchzuführen und die Ergebnisse zu vergleichen.", "writingFlash": "Flashen mit Raspberry Pi Pico" }, + "portHint": "CC3235-Boxen verbinden sich immer mit Port 443: Für sie gibt es keinen Port-Patch, TeddyCloud muss also auf Port 443 erreichbar sein (ein Proxy kann sie nur nach IP verteilen).", "previous": "Zurück", "serprogFirmwareLink": "Installationsanweisungen hier finden", "title": "CC3235 Box Flashing", @@ -1743,8 +1762,9 @@ "hintESP32FirmwareFlashed": "Das Patchen ist abgeschlossen. Trenne die Toniebox vom UART, entferne den Kurzschluss von J100 und starte die Box neu. Sie sollte sich jetzt mit TeddyCloud verbinden können!", "hintESP32FirmwareReseted": "Das Zurücksetzen sollte abgeschlossen sein. Bitte trenne die Toniebox vom UART, entferne den Kurzschluss von J100 und starte die Box neu. Sie sollte nicht mehr mit deinem Teddycloud verbunden sein. Führe einen Frische-Check durch, um zu testen, ob alles einwandfrei funktioniert.", "hintFlashESP32RevvoxFlasher": "Klicke auf „ESP32 flashen“, wähle falls nötig den COM-Port aus und warte, bis der Schritt abgeschlossen ist!", - "hintPatchFlash": "Um das ausgewählte / gelesene Flash mit dem dem CA-Zertifikat deiner TeddyCloud zu versehen und ggf. weitere Anpassungen wie Hostname der TeddyCloud und/oder der WiFi-Zugangsdaten vorzunehmen, klicke nach Eingabe auf \"Patch\" oder gehe ohne Patching direkt zum nächsten Schritt. ", + "hintPatchFlash": "Um das ausgewählte / gelesene Flash mit dem dem CA-Zertifikat deiner TeddyCloud zu versehen und ggf. weitere Anpassungen wie Hostname und Port der TeddyCloud und/oder der WiFi-Zugangsdaten vorzunehmen, klicke nach Eingabe auf \"Patch\" oder gehe ohne Patching direkt zum nächsten Schritt. ", "hintPatchHost": "Gib den gewünschten Hostnamen deiner TeddyCloud-Instanz ein.", + "hintPatchPort": "Port, mit dem sich die Box verbindet. Lass 443 stehen, außer die Box soll TeddyCloud über einen anderen Port erreichen, z. B. über einen Proxy, der nach Port verteilt. CC3235-Boxen und die Toniebox 2 lassen sich nicht patchen: Sie verbinden sich immer mit Port 443.", "hintPatchWifi": "Gib optional die WiFi Zugangsdaten für deine Toniebox ein. Bitte nur setzen, wenn es wirklich nötig ist - normalerweise, wenn deine Toniebox kurz vor dem Patchen mit deinem WLAN verbunden war, ist dies nicht erforderlich.", "hintReadESP32ImportFlash": "Trenne jetzt die Stromversorgung deiner Toniebox. Stelle sicher, dass dein UART im 3,3-V-Modus arbeitet. Verbinde dein UART mit dem PC und der Toniebox. Überbrücke die J100-Lötpunkte. Schließe danach die Toniebox wieder an die Stromversorgung an. Klicke auf „Flash auslesen“ oder „Datei laden“ und wähle dann den COM-Port aus. Warte anschließend, bis der Schritt abgeschlossen ist!", "hintSaveLog": "Bitte speichere die Protokollausgabe in eine Datei für weitere Analyse bei Problemen.", @@ -1752,9 +1772,6 @@ "hostname": "TeddyCloud-Hostname:", "hostnameSettings": "Hostname TeddyCloud", "hostnameTooLong": "Wenn du später eine CC3200 Toniebox verbinden willst, darf der Hostname maximal 12 Zeichen haben (Firmware-Limit).", - "hintPatchPort": "Port, mit dem sich die Box verbindet. Lass 443 stehen, außer die Box soll TeddyCloud über einen anderen Port erreichen, z. B. über einen Proxy, der nach Port verteilt. CC3235-Boxen und die Toniebox 2 lassen sich nicht patchen: Sie verbinden sich immer mit Port 443.", - "port": "TeddyCloud-Port:", - "portInvalid": "Der Port muss zwischen 1 und 32767 liegen.", "importanceBackup": "Bevor du Änderungen an deiner Toniebox vornimmst, lade eine Kopie der Original-Firmware herunter und speichere sie sicher. So kannst du deine Toniebox im Notfall jederzeit auf den Werkszustand zurücksetzen. Speichere sie an mehreren Orten, zum Beispiel auf deinem Hauptrechner oder Laptop, einem externen Laufwerk oder USB-Stick und/oder einem vertrauenswürdigen Cloud-Dienst (z. B. Google Drive, Dropbox, OneDrive). Speichere deine einzige Kopie niemals nur auf dem TeddyCloud-Server.", "infoTable": "ESP32 info", "invalidFlashData": "Der ausgelesene / geladene Flash besteht nur aus 0x00 oder 0xFF! Bitte überprüfe die Verbindung oder die Datei und versuche es erneut!", @@ -1770,6 +1787,8 @@ "patchingFlashImage": "Flash-Image wird gepatcht...", "patchingSuccessful": "Patchen erfolgreich, bereit zum Flashen von {{size}} MiB", "payloadReceived": "Payload empfangen", + "port": "TeddyCloud-Port:", + "portInvalid": "Der Port muss zwischen 1 und 32767 liegen.", "portOpenFailedInUse": "Port konnte nicht geöffnet werden. Bereits in Gebrauch?", "preparingFlash": "Vorbereitung von {{size}} MiB Flash", "previous": "Zurück", @@ -1884,20 +1903,7 @@ "uploadTonieboxCertificates": "Zertifikate für Toniebox{{name}} hochladen" }, "warningUseAtYourOwnRisk": "Warnung: Nutzung auf eigene Gefahr!", - "warningUseAtYourOwnRiskText": "Das Flashen deiner Toniebox mit einer modifizierten Firmware führt zum Erlöschen der Garantie und kann deine Box unbrauchbar machen. Bitte folge den Anweisungen sorgfältig und stelle sicher, dass du die damit verbundenen Risiken verstehst.", - "boxCertificate": { - "title": "Zertifikatsprüfung", - "status": "Status", - "presented": "Vorgezeigtes Zertifikat (SHA-256)", - "pin": "Gepinntes Zertifikat (SHA-256)", - "nextConnection": "Der Status wird bei der nächsten Verbindung der Box aktualisiert.", - "statusText": { - "none": "Noch nicht geprüft", - "trusted": "Zertifikat von TeddyCloud ausgestellt", - "pinned": "Gepinntes Zertifikat", - "mismatch": "Anderes Zertifikat als das gepinnte!" - } - } + "warningUseAtYourOwnRiskText": "Das Flashen deiner Toniebox mit einer modifizierten Firmware führt zum Erlöschen der Garantie und kann deine Box unbrauchbar machen. Bitte folge den Anweisungen sorgfältig und stelle sicher, dass du die damit verbundenen Risiken verstehst." }, "tonies": { "addNewCustomTonie": "Neues Modell anlegen", @@ -2296,17 +2302,6 @@ "valid": "Gültig:", "yes": "Ja" }, - "tafDownload": { - "asOneFile": "Als eine *.ogg-Datei herunterladen", - "downloadCount": "{{count}} Tracks als *.ogg herunterladen", - "downloadOne": "1 Track als *.ogg herunterladen", - "duration": "Zeit", - "modalTitle": "Tracks als *.ogg herunterladen", - "selectAll": "Alle Tracks auswählen", - "selectedCount": "{{selected}} von {{total}} ausgewählt", - "track": "Track", - "unnamedTrack": "Track {{number}}" - }, "informationModal": { "hideTag": "Tonie/Tag ausblenden", "ok": "OK", @@ -2402,6 +2397,17 @@ "navigationTitle": "System Sounds", "title": "System Sounds" }, + "tafDownload": { + "asOneFile": "Als eine *.ogg-Datei herunterladen", + "downloadCount": "{{count}} Tracks als *.ogg herunterladen", + "downloadOne": "1 Track als *.ogg herunterladen", + "duration": "Zeit", + "modalTitle": "Tracks als *.ogg herunterladen", + "selectAll": "Alle Tracks auswählen", + "selectedCount": "{{selected}} von {{total}} ausgewählt", + "track": "Track", + "unnamedTrack": "Track {{number}}" + }, "tafHeaderInvalid": "Ungültige TAF", "tafHeaderOf": "TAF-Header von ", "tafHeaderToniesJsonHint": "Du darfst die Daten hier nicht bearbeiten, wenn du sie an das tonies-json übermittelst. Dazu gehört auch die Anzahl der Tracks, selbst wenn sie sich von der Anzahl im Feld track-desc unterscheidet!", diff --git a/public/translations/en.json b/public/translations/en.json index e5056d27..d37271b4 100644 --- a/public/translations/en.json +++ b/public/translations/en.json @@ -142,6 +142,7 @@ "fields": { "author": "Author of the plugin", "description": "Short summary of the plugin", + "hideInNav": "Flag indicating whether the plugin should be hidden in navigation (true/false)", "icon": "The icon used for the plugin. Must be a valid Ant Design icon component from https://ant.design/components/icon, e.g. TrophyOutlined, TagsOutlined", "pluginHomepage": "Homepage of the plugin", "pluginName": "The title shown in menus (mandatory)", @@ -157,11 +158,16 @@ }, "helpButton": "Help", "helpTextCreatePlugin": "Some Text about how to create a plugin", + "hidden": "Hidden", "installedPlugins": "Installed plugins", "intro": "Here you find your added TeddyCloud plugins and you can add new plugins provided by you or the TeddyCloud community!", "navigationTitle": "TC Plugins", "ok": "OK", "open": "Open Plugin", + "pagination": { + "pageSelector": "Plugins per Page", + "showAll": "Show All" + }, "plugin": "Plugin", "title": "TeddyCloud Plugins", "upload": { @@ -1175,6 +1181,19 @@ "status": "Status", "troubleShooting": "Troubleshooting" }, + "boxCertificate": { + "nextConnection": "The status is updated with the next connection of the box.", + "pin": "Pinned certificate (SHA-256)", + "presented": "Presented certificate (SHA-256)", + "status": "Status", + "statusText": { + "mismatch": "Other certificate than the pinned one!", + "none": "Not checked yet", + "pinned": "Pinned certificate", + "trusted": "Certificate issued by TeddyCloud" + }, + "title": "Certificate check" + }, "boxFlashingCommon": { "adguard": { "adGuardRequirement": "AdGuard: Make sure AdGuard Home is installed and configured on a device in your network.", @@ -1385,6 +1404,7 @@ }, "applyingPatches": "Applying patches", "applyingPatchesSection": { + "altPortHint": "If you created a port patch, put it into /revvox/boot/patch/ as well and add \"altPort.custom\" to the patches list.", "examplengCFGJsonCollapse": { "collapseTitle": "Example \"ngCfg.json\" with \"altUrl.custom.305\"-patch using \"ofw2\"" }, @@ -1426,8 +1446,7 @@ "waitForPressDesc": "Waits for an earpress on startup with a blink sequence (blue, green, cyan, black)", "waitTimeoutInSDesc": "Timeout in seconds for waitForPress if no earpress (hibernation)", "watchdogDesc": "Keep watchdog enabled when booting firmware (if booting fails, box will restart)" - }, - "altPortHint": "If you created a port patch, put it into /revvox/boot/patch/ as well and add \"altPort.custom\" to the patches list." + } }, "applyingPatchesWithNgCfgJson": "Apply patches using ngCfg.json", "applyingPatchesWithNgCfgJsonLink": "Find instructions here", @@ -1450,13 +1469,10 @@ "connectToTonieboxProceed": "You are now ready to proceed!", "connectToTonieboxText": "The green LED on the Toniebox mainboard should stay steadily lit (no flashing or blinking). If it does not, your connections might be loose or incorrect. Disconnect the programmer, double-check all connections, and try again. If you are working with thin wires, consider applying hot glue to secure them in place and prevent any movement. Additionally, ensure that the Toniebox is powered using its charger.", "connectToTonieboxTextUart": "Ensure that the UART is configured to 3.3V before connecting the UART programmer to your computer via USB.", - "createPatch": "Create altUrl.custom.305.patch", - "customPortPatch": "Optional: create a port patch (altPort.custom.json)", - "customPortPatchHint": "Only needed if the box should reach TeddyCloud on another port than 443, e.g. through a proxy that routes by port. The patch works with the firmware versions 3.1.0 BF2, 3.3.0 and 3.4.0. hackiebox-ng already ships altPort.json for port 10443; create your own patch only for another port. CC3235 boxes and the Toniebox 2 cannot be patched: they always connect to port 443.", - "hintPatchPort": "Enter the port the box should connect to and click 'Create altPort.custom.patch'. The patch will automatically download afterwards.", - "port": "TeddyCloud port:", - "portInvalid": "The port must be between 1 and 65535.", + "createHostNamePatch": "Create altUrl.custom.305.patch", "createPortPatch": "Create altPort.custom.patch", + "customPortPatch": "Optional: Create a port patch (altPort.custom.json)", + "customPortPatchHint": "Only needed if the box should reach TeddyCloud on another port than 443, e.g. through a proxy that routes by port. The patch works with the firmware versions 3.1.0 BF2, 3.3.0 and 3.4.0. hackiebox-ng already ships altPort.json for port 10443; create your own patch only for another port. CC3235 boxes and the Toniebox 2 cannot be patched: they always connect to port 443.", "customUrlPatch": "Create a custom URL Patch (altUrl.custom.305.json)", "customUrlPatchHint": "Please create this patch only if the predefined altUrl.tc.fritz.box.json and altUrl.305.json patches are not functioning with your current TeddyCloud setup. If they do meet your needs you can proceed by clicking 'Next'.", "dedicatedUart": "Dedicated UART", @@ -1479,6 +1495,7 @@ "hint": "There is currently no direct support of flashing your CC3200 Toniebox with TeddyCloud, but you will find a step by step guidance here:", "hint2": "Please follow then the CC3200 path:", "hintPatchHost": "Enter the hostname of your TeddyCloud instance and then click 'Create altUrl.custom.305.patch'. The patch will automatically download afterwards.", + "hintPatchPort": "Enter the port the box should connect to and click 'Create altPort.custom.patch'. The patch will automatically download afterwards.", "hostname": "TeddyCloud Hostname:", "hostnameTooLong": "The hostname must not exceed 12 characters.", "installBootloader": "Install the HackieboxNG custom bootloader", @@ -1545,6 +1562,8 @@ "next": "Next", "patches": "Patches", "patchesMoreInformationLink": "More information on custom patches", + "port": "TeddyCloud port:", + "portInvalid": "The port must be between 1 and 65535.", "predefinedUrlPatches": "Predefined URL Patches", "predefinedUrlPatchesIntro": "For CC3200 boxes, it's recommended to use an altUrl patch. HackieboxNG includes two predefined altUrl patches ready to use:", "previous": "Back", @@ -1620,7 +1639,6 @@ "locateFlashMemoryText": "Identify the IS25LP032D flash memory chip on the board; it is located to the right of the charging dock.", "navigationTitle": "CC3235", "next": "Next", - "portHint": "CC3235 boxes always connect to port 443: there is no port patch for them, so TeddyCloud has to be reachable on port 443 (a proxy can only route them by IP).", "pico": { "preparation": "Only required if you plan to perform the flashing with a Raspberry Pi Pico", "readingFlash": "Reading Flash with Raspberry Pi Pico", @@ -1628,6 +1646,7 @@ "readingFlashText2": "It's recommended to perform multiple reads and compare results.", "writingFlash": "Writing Flash with Raspberry Pi Pico" }, + "portHint": "CC3235 boxes always connect to port 443: there is no port patch for them, so TeddyCloud has to be reachable on port 443 (a proxy can only route them by IP).", "previous": "Back", "serprogFirmwareLink": "Find installation instructions here", "title": "CC3235 Box Flashing", @@ -1743,8 +1762,9 @@ "hintESP32FirmwareFlashed": "The patching should be done. Please disconnect the Toniebox from the UART, remove the short circuit from J100 and restart the box. It should now be able to connect to TeddyCloud!", "hintESP32FirmwareReseted": "The reset should be done. Please disconnect the Toniebox from the UART, remove the short circuit from J100 and restart the box. It should be no longer connected to your Teddycloud. Do a freshnesscheck to test if it works fine.", "hintFlashESP32RevvoxFlasher": "Click on \"Flash ESP32\", if necessary choose the COM-Port and wait till the step is done!", - "hintPatchFlash": "To apply the selected/read flash with your TeddyCloud CA certificate and possibly make other adjustments like the TeddyCloud hostname and/or WiFi credentials, click 'Patch' after entering the information or proceed to the next step without patching.", + "hintPatchFlash": "To apply the selected/read flash with your TeddyCloud CA certificate and possibly make other adjustments like the TeddyCloud hostname, port and/or WiFi credentials, click 'Patch' after entering the information or proceed to the next step without patching.", "hintPatchHost": "Enter the desired hostname for your TeddyCloud instance.", + "hintPatchPort": "Port the box connects to. Keep 443 unless the box should reach TeddyCloud on another port, e.g. through a proxy that routes by port. CC3235 boxes and the Toniebox 2 cannot be patched: they always connect to port 443.", "hintPatchWifi": "Optionally enter the WiFi credentials for your Toniebox. Please only set this if you really need to - usually, if your Toniebox was connected to your WiFi right before patching, you don't need to.", "hintReadESP32ImportFlash": "Now disconnect the powersource of your Toniebox. Make sure that the UART operates in 3.3V mode. Connect your UART with your PC and Toniebox. Short cut the J100 pinholes. Then connect your Toniebox to power. Click on \"Read Flash\" or \"Load File\", then choose the COM-Port. Finally wait till the step is done!", "hintSaveLog": "Please save the log output to a file for further analysis in case of problems.", @@ -1752,9 +1772,6 @@ "hostname": "TeddyCloud Hostname:", "hostnameSettings": "Hostname TeddyCloud", "hostnameTooLong": "If you want to connect a CC3200 Toniebox later, the hostname must not exceed 12 characters (firmware limitation).", - "hintPatchPort": "Port the box connects to. Keep 443 unless the box should reach TeddyCloud on another port, e.g. through a proxy that routes by port. CC3235 boxes and the Toniebox 2 cannot be patched: they always connect to port 443.", - "port": "TeddyCloud port:", - "portInvalid": "The port must be between 1 and 32767.", "importanceBackup": "Before making any changes of your Toniebox, download and securely store a copy of the original firmware. This ensures you can always restore your Toniebox to its factory state if something goes wrong. Save it in multiple locations, such as your main computer or laptop, an external hard drive or USB stick and/or a trusted cloud storage service (e.g. Google Drive, Dropbox, OneDrive). Never store your only copy on the TeddyCloud-Server.", "infoTable": "ESP32 info", "invalidFlashData": "Read / loaded flash consists only of 0x00 or 0xFF! Please check connection or the loaded file and try again!", @@ -1770,6 +1787,8 @@ "patchingFlashImage": "Patching flash image...", "patchingSuccessful": "Patching successful, ready to flash {{size}} MiB.", "payloadReceived": "Payload received", + "port": "TeddyCloud port:", + "portInvalid": "The port must be between 1 and 32767.", "portOpenFailedInUse": "Failed to open this port. Already in use?", "preparingFlash": "Preparing {{size}} MiB flash", "previous": "Previous", @@ -1884,20 +1903,7 @@ "uploadTonieboxCertificates": "Upload certificates for Toniebox{{name}}" }, "warningUseAtYourOwnRisk": "Warning: Use at your own risk!", - "warningUseAtYourOwnRiskText": "Flashing your Toniebox with a patched firmware will void your warranty and may lead to a broken box. Please follow the instructions carefully and ensure you understand the risks involved.", - "boxCertificate": { - "title": "Certificate check", - "status": "Status", - "presented": "Presented certificate (SHA-256)", - "pin": "Pinned certificate (SHA-256)", - "nextConnection": "The status is updated with the next connection of the box.", - "statusText": { - "none": "Not checked yet", - "trusted": "Certificate issued by TeddyCloud", - "pinned": "Pinned certificate", - "mismatch": "Other certificate than the pinned one!" - } - } + "warningUseAtYourOwnRiskText": "Flashing your Toniebox with a patched firmware will void your warranty and may lead to a broken box. Please follow the instructions carefully and ensure you understand the risks involved." }, "tonies": { "addNewCustomTonie": "Create new model", @@ -2296,17 +2302,6 @@ "valid": "Valid:", "yes": "Yes" }, - "tafDownload": { - "asOneFile": "Download as one *.ogg file", - "downloadCount": "Download {{count}} tracks as *.ogg", - "downloadOne": "Download 1 track as *.ogg", - "duration": "Time", - "modalTitle": "Download tracks as *.ogg", - "selectAll": "Select all tracks", - "selectedCount": "{{selected}} of {{total}} selected", - "track": "Track", - "unnamedTrack": "Track {{number}}" - }, "informationModal": { "hideTag": "Hide Tonie/Tag", "ok": "OK", @@ -2402,6 +2397,17 @@ "navigationTitle": "System Sounds", "title": "System Sounds" }, + "tafDownload": { + "asOneFile": "Download as one *.ogg file", + "downloadCount": "Download {{count}} tracks as *.ogg", + "downloadOne": "Download 1 track as *.ogg", + "duration": "Time", + "modalTitle": "Download tracks as *.ogg", + "selectAll": "Select all tracks", + "selectedCount": "{{selected}} of {{total}} selected", + "track": "Track", + "unnamedTrack": "Track {{number}}" + }, "tafHeaderInvalid": "Invalid TAF", "tafHeaderOf": "TAF-Header of ", "tafHeaderToniesJsonHint": "You must not edit the data here when submitting it to the tonies-json. This includes the track count, even if it is different from the track count in the track-desc field!", diff --git a/public/translations/es.json b/public/translations/es.json index 78e399c1..da3a7be1 100644 --- a/public/translations/es.json +++ b/public/translations/es.json @@ -21,11 +21,11 @@ "password": "Contraseña", "passwordMin": "Al menos 4 caracteres", "passwordRequired": "La contraseña es obligatoria", - "username": "Usuario", - "usernameRequired": "El usuario es obligatorio", + "retry": "Reintentar", "statusLoadFailed": "No se pudo cargar el estado de autenticación", "statusLoadFailedDetails": "No se pudo cargar el estado de autenticación. Comprueba la conexión con TeddyCloud e inténtalo de nuevo.", - "retry": "Reintentar" + "username": "Usuario", + "usernameRequired": "El usuario es obligatorio" }, "common": { "cancel": "Cancelar" @@ -142,6 +142,7 @@ "fields": { "author": "Autor del plugin", "description": "Resumen breve del plugin", + "hideInNav": "Indicador que define si el plugin debe ocultarse en la navegación (true/false)", "icon": "El icono utilizado para el plugin. Debe ser un componente de icono válido de Ant Design de https://ant.design/components/icon, p. ej. TrophyOutlined, TagsOutlined", "pluginHomepage": "Página principal del plugin", "pluginName": "El título que aparece en los menús (obligatorio)", @@ -157,11 +158,16 @@ }, "helpButton": "Ayuda", "helpTextCreatePlugin": "Texto sobre cómo crear un plugin", + "hidden": "Oculto", "installedPlugins": "Plugins instalados", "intro": "Aquí puedes ver tus plugins añadidos a TeddyCloud y también agregar nuevos plugins creados por ti o por la comunidad TeddyCloud.", "navigationTitle": "Plugins TC", "ok": "OK", "open": "Abrir plugin", + "pagination": { + "pageSelector": "Plugins por página", + "showAll": "Mostrar todos" + }, "plugin": "Plugin", "title": "Plugins de TeddyCloud", "upload": { @@ -391,10 +397,18 @@ "filter": "Filtrar dentro de la carpeta actual", "help": { "actionItems": { + "assignToTonie": { + "text": "Asigna el archivo seleccionado como contenido a un Tonie/Tag.", + "title": "Asignar a Tonie" + }, "deleteFileFolder": { "text": "Elimina el archivo o carpeta (solo si la carpeta está vacía). Nota: Si este archivo está asignado a un Tonie/Tag, la referencia no se actualizará.", "title": "Eliminar archivo/carpeta" }, + "downloadFile": { + "text": "Descarga el archivo seleccionado. En archivos TAF con varias pistas se abre una selección de pistas para descargarlas como archivos *.ogg separados (ZIP) o como un solo archivo *.ogg; los archivos TAF de una sola pista se descargan como un único archivo *.ogg.", + "title": "Descargar archivo" + }, "duplicateTAPFile": { "text": "Crea una copia del archivo TAP seleccionado.", "title": "Duplicar archivo TAP" @@ -407,6 +421,16 @@ "text": "Abre el editor de TeddyAudio Playlist de Tonie para administrar archivos TAP.", "title": "Editar lista de reproducción TAP" }, + "markAsListened": { + "listened": { + "text": "El archivo está marcado actualmente como escuchado. Haz clic para marcarlo como no escuchado.", + "title": "Marcar como no escuchado" + }, + "unlistened": { + "text": "El archivo está marcado actualmente como no escuchado. Haz clic para marcarlo como escuchado.", + "title": "Marcar como escuchado" + } + }, "migrateToAudioIdFolder": { "text": "Mueve el archivo TAF a la carpeta 'by/audioId' de la biblioteca y actualiza la fuente en el archivo JSON.", "title": "Migrar archivo TAF a la biblioteca por AudioId" @@ -426,24 +450,6 @@ "renameFile": { "text": "Renombra el archivo. Nota: Si este archivo está asignado a un Tonie/Tag, la referencia no se actualizará automáticamente.", "title": "Renombrar archivo" - }, - "downloadFile": { - "title": "Descargar archivo", - "text": "Descarga el archivo seleccionado. En archivos TAF con varias pistas se abre una selección de pistas para descargarlas como archivos *.ogg separados (ZIP) o como un solo archivo *.ogg; los archivos TAF de una sola pista se descargan como un único archivo *.ogg." - }, - "markAsListened": { - "unlistened": { - "title": "Marcar como escuchado", - "text": "El archivo está marcado actualmente como no escuchado. Haz clic para marcarlo como escuchado." - }, - "listened": { - "title": "Marcar como no escuchado", - "text": "El archivo está marcado actualmente como escuchado. Haz clic para marcarlo como no escuchado." - } - }, - "assignToTonie": { - "title": "Asignar a Tonie", - "text": "Asigna el archivo seleccionado como contenido a un Tonie/Tag." } }, "actionItemsMulti": { @@ -1175,6 +1181,19 @@ "status": "Estado", "troubleShooting": "Resolución de problemas" }, + "boxCertificate": { + "nextConnection": "El estado se actualiza con la próxima conexión de la Toniebox.", + "pin": "Certificado fijado (SHA-256)", + "presented": "Certificado presentado (SHA-256)", + "status": "Estado", + "statusText": { + "mismatch": "¡Otro certificado distinto del fijado!", + "none": "Aún no comprobado", + "pinned": "Certificado fijado", + "trusted": "Certificado emitido por TeddyCloud" + }, + "title": "Comprobación del certificado" + }, "boxFlashingCommon": { "adguard": { "adGuardRequirement": "AdGuard: Asegúrate de que AdGuard Home esté instalado y configurado en un dispositivo de tu red.", @@ -1385,6 +1404,7 @@ }, "applyingPatches": "Aplicando parches", "applyingPatchesSection": { + "altPortHint": "Si has creado un parche de puerto, colócalo también en /revvox/boot/patch/ y añade \"altPort.custom\" a la lista de parches.", "examplengCFGJsonCollapse": { "collapseTitle": "Ejemplo de \"ngCfg.json\" con el parche \"altUrl.custom.305\" usando \"ofw2\"" }, @@ -1426,8 +1446,7 @@ "waitForPressDesc": "Espera una presión en la oreja al inicio con una secuencia de parpadeo (azul, verde, cian, negro)", "waitTimeoutInSDesc": "Tiempo de espera en segundos para waitForPress si no hay presión en la oreja (hibernación)", "watchdogDesc": "Mantener el watchdog habilitado al iniciar firmware (si el inicio falla, la caja se reiniciará)" - }, - "altPortHint": "Si has creado un parche de puerto, colócalo también en /revvox/boot/patch/ y añade \"altPort.custom\" a la lista de parches." + } }, "applyingPatchesWithNgCfgJson": "Aplicar parches utilizando ngCfg.json", "applyingPatchesWithNgCfgJsonLink": "Encuentra las instrucciones aquí", @@ -1450,13 +1469,10 @@ "connectToTonieboxProceed": "¡Ahora estás listo para continuar!", "connectToTonieboxText": "El LED verde en la placa principal de la Toniebox debe permanecer encendido de forma continua (sin parpadear). Si no lo hace, es posible que las conexiones estén flojas o incorrectas. Desconecta el programador, verifica todas las conexiones y vuelve a intentarlo. Si estás trabajando con cables finos, considera usar pegamento caliente para asegurarlos y evitar que se muevan. Además, asegúrate de que la Toniebox esté alimentada mediante su cargador.", "connectToTonieboxTextUart": "Asegúrate de que el UART esté configurado a 3.3V antes de conectar el programador UART a tu computadora mediante USB.", - "createPatch": "Crear altUrl.custom.305.patch", - "customPortPatch": "Opcional: crear un parche de puerto (altPort.custom.json)", - "customPortPatchHint": "Solo es necesario si la Toniebox debe conectarse a TeddyCloud a través de un puerto distinto de 443, por ejemplo mediante un proxy que enruta por puerto. El parche funciona con las versiones de firmware 3.1.0 BF2, 3.3.0 y 3.4.0. hackiebox-ng ya incluye altPort.json para el puerto 10443; crea tu propio parche solo para otro puerto. Las Toniebox CC3235 y la Toniebox 2 no se pueden parchear: siempre se conectan al puerto 443.", - "hintPatchPort": "Introduce el puerto al que debe conectarse la Toniebox y haz clic en 'Crear altPort.custom.patch'. El parche se descargará automáticamente.", - "port": "Puerto de TeddyCloud:", - "portInvalid": "El puerto debe estar entre 1 y 65535.", + "createHostNamePatch": "Crear altUrl.custom.305.patch", "createPortPatch": "Crear altPort.custom.patch", + "customPortPatch": "Opcional: Crear un parche de puerto (altPort.custom.json)", + "customPortPatchHint": "Solo es necesario si la Toniebox debe conectarse a TeddyCloud a través de un puerto distinto de 443, por ejemplo mediante un proxy que enruta por puerto. El parche funciona con las versiones de firmware 3.1.0 BF2, 3.3.0 y 3.4.0. hackiebox-ng ya incluye altPort.json para el puerto 10443; crea tu propio parche solo para otro puerto. Las Toniebox CC3235 y la Toniebox 2 no se pueden parchear: siempre se conectan al puerto 443.", "customUrlPatch": "Crear un parche de URL personalizado (altUrl.custom.305.json)", "customUrlPatchHint": "Por favor, crea este parche solo si los parches predefinidos altUrl.tc.fritz.box.json y altUrl.305.json no funcionan con tu configuración actual de TeddyCloud. Si satisfacen tus necesidades, puedes continuar haciendo clic en 'Siguiente'.", "dedicatedUart": "UART dedicado", @@ -1479,6 +1495,7 @@ "hint": "Actualmente no hay soporte directo para flashear tu Toniebox CC3200 con TeddyCloud, pero encontrarás una guía paso a paso aquí:", "hint2": "Sigue luego el camino para CC3200:", "hintPatchHost": "Ingresa el nombre de host de tu instancia de TeddyCloud y luego haz clic en 'Crear altUrl.custom.305.patch'. El parche se descargará automáticamente después.", + "hintPatchPort": "Introduce el puerto al que debe conectarse la Toniebox y haz clic en 'Crear altPort.custom.patch'. El parche se descargará automáticamente.", "hostname": "Nombre de host de TeddyCloud:", "hostnameTooLong": "El nombre de host no debe exceder los 12 caracteres.", "installBootloader": "Instalar el Custom Bootloader HackieboxNG", @@ -1545,6 +1562,8 @@ "next": "Siguiente", "patches": "Parches", "patchesMoreInformationLink": "Más información sobre parches personalizados", + "port": "Puerto de TeddyCloud:", + "portInvalid": "El puerto debe estar entre 1 y 65535.", "predefinedUrlPatches": "Parches de URL predefinidos", "predefinedUrlPatchesIntro": "Para cajas CC3200, se recomienda usar un parche altUrl. HackieboxNG ya incluye dos parches altUrl predefinidos:", "previous": "Atrás", @@ -1620,7 +1639,6 @@ "locateFlashMemoryText": "Identifica el chip de memoria flash IS25LP032D en la placa; se encuentra a la derecha del dock de carga.", "navigationTitle": "CC3235", "next": "Siguiente", - "portHint": "Las cajas CC3235 siempre se conectan al puerto 443: no hay parche de puerto para ellas, así que TeddyCloud debe estar accesible en el puerto 443 (un proxy solo puede enrutarlas por IP).", "pico": { "preparation": "Solo necesario si planeas realizar el flasheo con un Raspberry Pi Pico", "readingFlash": "Leer memoria flash con Raspberry Pi Pico", @@ -1628,6 +1646,7 @@ "readingFlashText2": "Se recomienda realizar varias lecturas y comparar los resultados.", "writingFlash": "Escribir en la memoria flash con Raspberry Pi Pico" }, + "portHint": "Las cajas CC3235 siempre se conectan al puerto 443: no hay parche de puerto para ellas, así que TeddyCloud debe estar accesible en el puerto 443 (un proxy solo puede enrutarlas por IP).", "previous": "Atrás", "serprogFirmwareLink": "Encuentra las instrucciones de instalación aquí", "title": "Flasheo de la caja CC3235", @@ -1743,8 +1762,9 @@ "hintESP32FirmwareFlashed": "El parcheo debería estar hecho. Desconecta la Toniebox del UART, quita el cortocircuito de J100 y reinicia la caja. ¡Ahora debería poder conectarse a TeddyCloud!", "hintESP32FirmwareReseted": "El restablecimiento debería estar completado. Por favor, desconecta la Toniebox del UART, retira el cortocircuito de J100 y reinicia la caja. Ya no debería estar conectada a tu Teddycloud. Realiza una comprobación de frescura para verificar que todo funciona bien.", "hintFlashESP32RevvoxFlasher": "Haz clic en \"Flashear ESP32\", elige el puerto COM si es necesario y espera a que el paso termine.", - "hintPatchFlash": "Para aplicar el flash seleccionado/leído con tu certificado CA de TeddyCloud y posiblemente hacer otros ajustes como el nombre de host de TeddyCloud y/o credenciales WiFi, haz clic en 'Parchear' después de ingresar la información o continúa con el siguiente paso sin parchear.", + "hintPatchFlash": "Para aplicar el flash seleccionado/leído con tu certificado CA de TeddyCloud y posiblemente hacer otros ajustes como el nombre de host y el puerto de TeddyCloud y/o credenciales WiFi, haz clic en 'Parchear' después de ingresar la información o continúa con el siguiente paso sin parchear.", "hintPatchHost": "Introduce el nombre de host deseado para tu instancia de TeddyCloud.", + "hintPatchPort": "Puerto al que se conecta la Toniebox. Deja 443 salvo que la Toniebox deba conectarse a TeddyCloud a través de otro puerto, por ejemplo mediante un proxy que enruta por puerto. Las Toniebox CC3235 y la Toniebox 2 no se pueden parchear: siempre se conectan al puerto 443.", "hintPatchWifi": "Opcionalmente ingresa las credenciales WiFi para tu Toniebox. Por favor, solo configúralo si realmente es necesario; normalmente, si tu Toniebox estaba conectada a tu WiFi justo antes de aplicar el parche, no necesitas hacerlo.", "hintReadESP32ImportFlash": "Ahora desconecta la fuente de alimentación de tu Toniebox. Asegúrate de que tu UART funcione en modo de 3,3 V. Conecta tu UART al PC y a la Toniebox. Puentea los puntos de soldadura J100. Después conecta de nuevo la Toniebox a la alimentación. Haz clic en \"Leer flash\" o \"Cargar archivo\" y elige el puerto COM. Finalmente espera a que el paso termine.", "hintSaveLog": "Por favor, guarda la salida del registro en un archivo para un análisis adicional en caso de problemas.", @@ -1752,9 +1772,6 @@ "hostname": "Nombre de host de TeddyCloud:", "hostnameSettings": "Configuración del nombre de host", "hostnameTooLong": "Si más adelante quieres conectar una Toniebox CC3200, el nombre de host no puede superar los 12 caracteres (limitación del firmware).", - "hintPatchPort": "Puerto al que se conecta la Toniebox. Deja 443 salvo que la Toniebox deba conectarse a TeddyCloud a través de otro puerto, por ejemplo mediante un proxy que enruta por puerto. Las Toniebox CC3235 y la Toniebox 2 no se pueden parchear: siempre se conectan al puerto 443.", - "port": "Puerto de TeddyCloud:", - "portInvalid": "El puerto debe estar entre 1 y 32767.", "importanceBackup": "Antes de hacer cualquier cambio en tu Toniebox, descarga y guarda de forma segura una copia del firmware original. Esto te permitirá restaurar tu Toniebox a su estado de fábrica si algo sale mal. Guárdala en varios lugares, como tu ordenador principal o portátil, un disco duro externo o memoria USB, y/o un servicio de almacenamiento en la nube de confianza (por ejemplo, Google Drive, Dropbox, OneDrive). Nunca guardes tu única copia solo en el servidor TeddyCloud.", "infoTable": "Información del ESP32", "invalidFlashData": "La lectura / carga del flash contiene únicamente 0x00 o 0xFF. ¡Revisa la conexión o el archivo cargado e inténtalo de nuevo!", @@ -1770,6 +1787,8 @@ "patchingFlashImage": "Parcheando imagen de flash...", "patchingSuccessful": "Parcheo exitoso, listo para flashear {{size}} MiB.", "payloadReceived": "Carga útil recibida", + "port": "Puerto de TeddyCloud:", + "portInvalid": "El puerto debe estar entre 1 y 32767.", "portOpenFailedInUse": "Error al abrir este puerto. ¿Ya está en uso?", "preparingFlash": "Preparando flash de {{size}} MiB", "previous": "Anterior", @@ -1884,20 +1903,7 @@ "uploadTonieboxCertificates": "Subir certificados para Toniebox{{name}}" }, "warningUseAtYourOwnRisk": "Advertencia: úsalo bajo tu propia responsabilidad", - "warningUseAtYourOwnRiskText": "Flashear tu Toniebox con un firmware modificado anulará la garantía y puede dejar la caja inutilizable. Sigue las instrucciones cuidadosamente y asegúrate de comprender los riesgos asociados.", - "boxCertificate": { - "title": "Comprobación del certificado", - "status": "Estado", - "presented": "Certificado presentado (SHA-256)", - "pin": "Certificado fijado (SHA-256)", - "nextConnection": "El estado se actualiza con la próxima conexión de la Toniebox.", - "statusText": { - "none": "Aún no comprobado", - "trusted": "Certificado emitido por TeddyCloud", - "pinned": "Certificado fijado", - "mismatch": "¡Otro certificado distinto del fijado!" - } - } + "warningUseAtYourOwnRiskText": "Flashear tu Toniebox con un firmware modificado anulará la garantía y puede dejar la caja inutilizable. Sigue las instrucciones cuidadosamente y asegúrate de comprender los riesgos asociados." }, "tonies": { "addNewCustomTonie": "Crear nuevo modelo", @@ -2296,17 +2302,6 @@ "valid": "Válido:", "yes": "Sí" }, - "tafDownload": { - "asOneFile": "Descargar como un solo archivo *.ogg", - "downloadCount": "Descargar {{count}} pistas como *.ogg", - "downloadOne": "Descargar 1 pista como *.ogg", - "duration": "Tiempo", - "modalTitle": "Descargar pistas como *.ogg", - "selectAll": "Seleccionar todas las pistas", - "selectedCount": "{{selected}} de {{total}} seleccionadas", - "track": "Pista", - "unnamedTrack": "Pista {{number}}" - }, "informationModal": { "hideTag": "Ocultar Tonie/Tag", "ok": "OK", @@ -2402,6 +2397,17 @@ "navigationTitle": "Sonidos del sistema", "title": "Sonidos del sistema" }, + "tafDownload": { + "asOneFile": "Descargar como un solo archivo *.ogg", + "downloadCount": "Descargar {{count}} pistas como *.ogg", + "downloadOne": "Descargar 1 pista como *.ogg", + "duration": "Tiempo", + "modalTitle": "Descargar pistas como *.ogg", + "selectAll": "Seleccionar todas las pistas", + "selectedCount": "{{selected}} de {{total}} seleccionadas", + "track": "Pista", + "unnamedTrack": "Pista {{number}}" + }, "tafHeaderInvalid": "TAF no válido", "tafHeaderOf": "TAF-Header de ", "tafHeaderToniesJsonHint": "No debes editar los datos aquí cuando los envíes al tonies-json. ¡Esto incluye el número de pistas, incluso si es diferente del número de pistas en el campo track-desc!", diff --git a/public/translations/fr.json b/public/translations/fr.json index 66052c9d..51874120 100644 --- a/public/translations/fr.json +++ b/public/translations/fr.json @@ -21,11 +21,11 @@ "password": "Mot de passe", "passwordMin": "Au moins 4 caractères", "passwordRequired": "Le mot de passe est requis", - "username": "Nom d'utilisateur", - "usernameRequired": "Le nom d'utilisateur est requis", + "retry": "Réessayer", "statusLoadFailed": "Impossible de charger l’état d’authentification", "statusLoadFailedDetails": "L’état d’authentification n’a pas pu être chargé. Vérifiez la connexion à TeddyCloud et réessayez.", - "retry": "Réessayer" + "username": "Nom d'utilisateur", + "usernameRequired": "Le nom d'utilisateur est requis" }, "common": { "cancel": "Annuler" @@ -142,6 +142,7 @@ "fields": { "author": "Auteur du plugin", "description": "Résumé court du plugin", + "hideInNav": "Indicateur précisant si le plugin doit être masqué dans la navigation (true/false)", "icon": "L'icône utilisée pour le plugin. Doit être un composant d'icône Ant Design valide depuis https://ant.design/components/icon, par ex. TrophyOutlined, TagsOutlined", "pluginHomepage": "Page d'accueil du plugin", "pluginName": "Le titre affiché dans les menus (obligatoire)", @@ -157,11 +158,16 @@ }, "helpButton": "Aide", "helpTextCreatePlugin": "Un texte sur la création d'un plugin", + "hidden": "Masqué", "installedPlugins": "Plugins installés", "intro": "Vous trouverez ici vos plugins TeddyCloud ajoutés et pouvez ajouter de nouveaux plugins fournis par vous-même ou la communauté TeddyCloud !", "navigationTitle": "Plugins TC", "ok": "OK", "open": "Ouvrir le plugin", + "pagination": { + "pageSelector": "Plugins par page", + "showAll": "Afficher tout" + }, "plugin": "Plugin", "title": "Plugins TeddyCloud", "upload": { @@ -391,10 +397,18 @@ "filter": "Filtrer dans le dossier actuel", "help": { "actionItems": { + "assignToTonie": { + "text": "Assigne le fichier sélectionné comme contenu à un Tonie/Tag.", + "title": "Assigner à un Tonie" + }, "deleteFileFolder": { "text": "Supprime le fichier ou le dossier (seulement si le dossier est vide). Remarque : si ce fichier est attribué à un Tonie/Tag, la référence ne sera pas mise à jour.", "title": "Supprimer le fichier/dossier" }, + "downloadFile": { + "text": "Télécharge le fichier sélectionné. Pour les fichiers TAF contenant plusieurs pistes, une sélection des pistes s'ouvre pour les télécharger en fichiers *.ogg séparés (ZIP) ou en un seul fichier *.ogg ; les fichiers TAF d'une seule piste sont téléchargés en un seul fichier *.ogg.", + "title": "Télécharger le fichier" + }, "duplicateTAPFile": { "text": "Crée une copie du fichier TAP sélectionné.", "title": "Dupliquer le fichier TAP" @@ -407,6 +421,16 @@ "text": "Ouvre l'éditeur de TeddyAudio Playlist pour gérer les fichiers TAP.", "title": "Modifier la liste de lecture TAP" }, + "markAsListened": { + "listened": { + "text": "Le fichier est actuellement marqué comme écouté. Cliquez pour le marquer comme non écouté.", + "title": "Marquer comme non écouté" + }, + "unlistened": { + "text": "Le fichier est actuellement marqué comme non écouté. Cliquez pour le marquer comme écouté.", + "title": "Marquer comme écouté" + } + }, "migrateToAudioIdFolder": { "text": "Déplace le fichier TAF dans le dossier 'by/audioId' de la bibliothèque et met à jour la source dans le fichier JSON.", "title": "Migrer le fichier TAF dans la bibliothèque par AudioId" @@ -426,24 +450,6 @@ "renameFile": { "text": "Renomme le fichier. Remarque : si ce fichier est attribué à un Tonie/Tag, la référence ne sera pas mise à jour automatiquement.", "title": "Renommer le fichier" - }, - "downloadFile": { - "title": "Télécharger le fichier", - "text": "Télécharge le fichier sélectionné. Pour les fichiers TAF contenant plusieurs pistes, une sélection des pistes s'ouvre pour les télécharger en fichiers *.ogg séparés (ZIP) ou en un seul fichier *.ogg ; les fichiers TAF d'une seule piste sont téléchargés en un seul fichier *.ogg." - }, - "markAsListened": { - "unlistened": { - "title": "Marquer comme écouté", - "text": "Le fichier est actuellement marqué comme non écouté. Cliquez pour le marquer comme écouté." - }, - "listened": { - "title": "Marquer comme non écouté", - "text": "Le fichier est actuellement marqué comme écouté. Cliquez pour le marquer comme non écouté." - } - }, - "assignToTonie": { - "title": "Assigner à un Tonie", - "text": "Assigne le fichier sélectionné comme contenu à un Tonie/Tag." } }, "actionItemsMulti": { @@ -1175,6 +1181,19 @@ "status": "Statut", "troubleShooting": "Dépannage" }, + "boxCertificate": { + "nextConnection": "Le statut est mis à jour à la prochaine connexion de la Toniebox.", + "pin": "Certificat épinglé (SHA-256)", + "presented": "Certificat présenté (SHA-256)", + "status": "Statut", + "statusText": { + "mismatch": "Autre certificat que celui épinglé !", + "none": "Pas encore vérifié", + "pinned": "Certificat épinglé", + "trusted": "Certificat émis par TeddyCloud" + }, + "title": "Vérification du certificat" + }, "boxFlashingCommon": { "adguard": { "adGuardRequirement": "AdGuard : Assure-toi qu'AdGuard Home est installé et configuré sur un appareil de ton réseau.", @@ -1385,6 +1404,7 @@ }, "applyingPatches": "Application des patches", "applyingPatchesSection": { + "altPortHint": "Si tu as créé un patch de port, place-le aussi dans /revvox/boot/patch/ et ajoute \"altPort.custom\" à la liste des patches.", "examplengCFGJsonCollapse": { "collapseTitle": "Exemple de \"ngCfg.json\" avec le patch \"altUrl.custom.305\" en utilisant \"ofw2\"" }, @@ -1426,8 +1446,7 @@ "waitForPressDesc": "Attends une pression sur l'oreille au démarrage avec une séquence de clignotement (bleu, vert, cyan, noir)", "waitTimeoutInSDesc": "Délai en secondes pour waitForPress si aucune pression sur l'oreille (hibernation)", "watchdogDesc": "Garder le watchdog activé lors du démarrage du firmware (si le démarrage échoue, la box redémarrera)" - }, - "altPortHint": "Si tu as créé un patch de port, place-le aussi dans /revvox/boot/patch/ et ajoute \"altPort.custom\" à la liste des patches." + } }, "applyingPatchesWithNgCfgJson": "Appliquer les patches en utilisant ngCfg.json", "applyingPatchesWithNgCfgJsonLink": "Trouve les instructions ici", @@ -1450,13 +1469,10 @@ "connectToTonieboxProceed": "Tu es maintenant prêt à continuer !", "connectToTonieboxText": "La LED verte de la carte principale de la Toniebox doit rester allumée en continu (sans clignoter). Si ce n'est pas le cas, tes connexions pourraient être mal fixées ou incorrectes. Déconnecte le programmeur, vérifie toutes les connexions et essaie à nouveau. Si tu travailles avec des fils fins, pense à utiliser de la colle chaude pour les fixer et éviter qu'ils ne bougent. De plus, assure-toi que la Toniebox est alimentée via son chargeur.", "connectToTonieboxTextUart": "Assure-toi que l'UART est configuré à 3,3V avant de connecter le programmeur UART à ton ordinateur via USB.", - "createPatch": "Créer altUrl.custom.305.patch", - "customPortPatch": "Optionnel : créer un patch de port (altPort.custom.json)", - "customPortPatchHint": "Nécessaire uniquement si la Toniebox doit joindre TeddyCloud sur un autre port que 443, par exemple via un proxy qui route selon le port. Le patch fonctionne avec les versions de firmware 3.1.0 BF2, 3.3.0 et 3.4.0. hackiebox-ng fournit déjà altPort.json pour le port 10443 ; crée ton propre patch uniquement pour un autre port. Les Toniebox CC3235 et la Toniebox 2 ne peuvent pas être patchées : elles se connectent toujours au port 443.", - "hintPatchPort": "Saisis le port auquel la Toniebox doit se connecter, puis clique sur 'Créer altPort.custom.patch'. Le patch sera ensuite téléchargé automatiquement.", - "port": "Port TeddyCloud :", - "portInvalid": "Le port doit être compris entre 1 et 65535.", + "createHostNamePatch": "Créer altUrl.custom.305.patch", "createPortPatch": "Créer altPort.custom.patch", + "customPortPatch": "Optionnel : Créer un patch de port (altPort.custom.json)", + "customPortPatchHint": "Nécessaire uniquement si la Toniebox doit joindre TeddyCloud sur un autre port que 443, par exemple via un proxy qui route selon le port. Le patch fonctionne avec les versions de firmware 3.1.0 BF2, 3.3.0 et 3.4.0. hackiebox-ng fournit déjà altPort.json pour le port 10443 ; crée ton propre patch uniquement pour un autre port. Les Toniebox CC3235 et la Toniebox 2 ne peuvent pas être patchées : elles se connectent toujours au port 443.", "customUrlPatch": "Créer un patch URL personnalisé (altUrl.custom.305.json)", "customUrlPatchHint": "Veuillez créer ce patch uniquement si les patches prédéfinis altUrl.tc.fritz.box.json et altUrl.305.json ne fonctionnent pas avec votre configuration actuelle de TeddyCloud. S'ils répondent à vos besoins, vous pouvez continuer en cliquant sur 'Suivant'.", "dedicatedUart": "UART dédié", @@ -1479,6 +1495,7 @@ "hint": "Il n'y a actuellement pas de support direct pour flasher votre CC3200 Toniebox avec TeddyCloud, mais vous trouverez ici un guide étape par étape :", "hint2": "Veuillez suivre ensuite le chemin CC3200 :", "hintPatchHost": "Entrez le nom d'hôte de votre instance TeddyCloud, puis cliquez sur 'Créer altUrl.custom.305.patch'. Le patch sera téléchargé automatiquement par la suite.", + "hintPatchPort": "Saisis le port auquel la Toniebox doit se connecter, puis clique sur 'Créer altPort.custom.patch'. Le patch sera ensuite téléchargé automatiquement.", "hostname": "Nom d'hôte TeddyCloud :", "hostnameTooLong": "Le nom d'hôte ne doit pas dépasser 12 caractères.", "installBootloader": "Installer le Custom Bootloader HackieboxNG", @@ -1545,6 +1562,8 @@ "next": "Suivant", "patches": "Patches", "patchesMoreInformationLink": "Plus d'informations sur les patches personnalisés", + "port": "Port TeddyCloud :", + "portInvalid": "Le port doit être compris entre 1 et 65535.", "predefinedUrlPatches": "Correctifs d'URL prédéfinis", "predefinedUrlPatchesIntro": "Pour les boîtiers CC3200, il est recommandé d'utiliser un correctif altUrl. HackieboxNG inclut déjà deux correctifs altUrl prêts à l'emploi :", "previous": "Retour", @@ -1620,7 +1639,6 @@ "locateFlashMemoryText": "Identifie la puce de mémoire flash IS25LP032D sur la carte ; elle est située à droite du dock de chargement.", "navigationTitle": "CC3235", "next": "Suivant", - "portHint": "Les box CC3235 se connectent toujours au port 443 : il n'existe pas de patch de port pour elles, TeddyCloud doit donc être accessible sur le port 443 (un proxy ne peut les router que par IP).", "pico": { "preparation": "Uniquement nécessaire si tu prévois de flasher avec un Raspberry Pi Pico", "readingFlash": "Lire la mémoire flash avec Raspberry Pi Pico", @@ -1628,6 +1646,7 @@ "readingFlashText2": "Il est recommandé d'effectuer plusieurs lectures et de comparer les résultats.", "writingFlash": "Écrire la mémoire flash avec le Raspberry Pi Pico" }, + "portHint": "Les box CC3235 se connectent toujours au port 443 : il n'existe pas de patch de port pour elles, TeddyCloud doit donc être accessible sur le port 443 (un proxy ne peut les router que par IP).", "previous": "Retour", "serprogFirmwareLink": "Trouver les instructions d'installation ici", "title": "CC3235 Flashage de la boîte", @@ -1743,8 +1762,9 @@ "hintESP32FirmwareFlashed": "Le patchage est terminé. Déconnecte la Toniebox de l'UART, retire le court-circuit du J100 et redémarre la box. Elle devrait maintenant pouvoir se connecter à TeddyCloud !", "hintESP32FirmwareReseted": "La réinitialisation devrait être terminée. Veuillez déconnecter la Toniebox du UART, retirer le court-circuit de J100 et redémarrer la box. Elle ne devrait plus être connectée à ton Teddycloud. Fais une vérification de fraîcheur pour tester si tout fonctionne correctement.", "hintFlashESP32RevvoxFlasher": "Clique sur « Flasher l'ESP32 », choisis le port COM si nécessaire et attends que l'étape soit terminée.", - "hintPatchFlash": "Pour appliquer le flash sélectionné/lu avec le certificat CA de votre TeddyCloud et éventuellement effectuer d'autres ajustements tels que le nom d'hôte de TeddyCloud et/ou les identifiants WiFi, cliquez sur 'Patch' après avoir entré les informations ou passez à l'étape suivante sans patch.", + "hintPatchFlash": "Pour appliquer le flash sélectionné/lu avec le certificat CA de votre TeddyCloud et éventuellement effectuer d'autres ajustements tels que le nom d'hôte et le port de TeddyCloud et/ou les identifiants WiFi, cliquez sur 'Patch' après avoir entré les informations ou passez à l'étape suivante sans patch.", "hintPatchHost": "Entrez le nom d'hôte souhaité pour votre instance TeddyCloud.", + "hintPatchPort": "Port auquel la Toniebox se connecte. Garde 443, sauf si la Toniebox doit joindre TeddyCloud sur un autre port, par exemple via un proxy qui route selon le port. Les Toniebox CC3235 et la Toniebox 2 ne peuvent pas être patchées : elles se connectent toujours au port 443.", "hintPatchWifi": "Entrez optionnellement les identifiants WiFi pour votre Toniebox. Merci de ne le faire que si c'est vraiment nécessaire - normalement, si votre Toniebox était connectée à votre WiFi juste avant la mise à jour, ce n'est pas nécessaire.", "hintReadESP32ImportFlash": "Débranche maintenant l'alimentation de ta Toniebox. Assure-toi que ton UART fonctionne en mode 3,3 V. Connecte ton UART au PC et à la Toniebox. Court-circuite les pastilles J100. Rebranche ensuite ta Toniebox à l'alimentation. Clique sur « Lire le flash » ou « Charger le fichier », puis choisis le port COM. Enfin, attends que l'étape soit terminée.", "hintSaveLog": "Veuillez enregistrer la sortie du journal dans un fichier pour une analyse ultérieure en cas de problème.", @@ -1752,9 +1772,6 @@ "hostname": "Nom d'hôte TeddyCloud:", "hostnameSettings": "Nom d'hôte TeddyCloud", "hostnameTooLong": "Si vous souhaitez connecter ultérieurement une Toniebox CC3200, le nom d'hôte ne doit pas dépasser 12 caractères (limitation du firmware).", - "hintPatchPort": "Port auquel la Toniebox se connecte. Garde 443, sauf si la Toniebox doit joindre TeddyCloud sur un autre port, par exemple via un proxy qui route selon le port. Les Toniebox CC3235 et la Toniebox 2 ne peuvent pas être patchées : elles se connectent toujours au port 443.", - "port": "Port TeddyCloud :", - "portInvalid": "Le port doit être compris entre 1 et 32767.", "importanceBackup": "Avant de faire des modifications sur ta Toniebox, télécharge une copie du firmware original et stocke-la de manière sécurisée. Cela te permettra de restaurer ta Toniebox à son état d'usine en cas de problème. Sauvegarde-la à plusieurs endroits, comme sur ton ordinateur principal ou portable, un disque dur externe ou une clé USB, et/ou un service de stockage en ligne fiable (comme Google Drive, Dropbox, OneDrive). Ne conserve jamais ta seule copie uniquement sur le serveur TeddyCloud.", "infoTable": "Infos ESP32", "invalidFlashData": "La lecture / le chargement du flash ne contient que des 0x00 ou 0xFF. Vérifie la connexion ou le fichier chargé et réessaie !", @@ -1770,6 +1787,8 @@ "patchingFlashImage": "Patch de l'image du flash...", "patchingSuccessful": "Patch réussi, prêt à flasher {{size}} MiB", "payloadReceived": "Charge utile reçue", + "port": "Port TeddyCloud :", + "portInvalid": "Le port doit être compris entre 1 et 32767.", "portOpenFailedInUse": "Échec de l'ouverture de ce port. Déjà en cours d'utilisation ?", "preparingFlash": "Préparation du flash de {{size}} MiB", "previous": "Précédent", @@ -1884,20 +1903,7 @@ "uploadTonieboxCertificates": "Télécharger les certificats pour la Toniebox{{name}}" }, "warningUseAtYourOwnRisk": "Avertissement : utilisation à tes propres risques", - "warningUseAtYourOwnRiskText": "Flasher ta Toniebox avec un firmware modifié annulera la garantie et peut rendre la box inutilisable. Suis attentivement les instructions et assure-toi de bien comprendre les risques encourus.", - "boxCertificate": { - "title": "Vérification du certificat", - "status": "Statut", - "presented": "Certificat présenté (SHA-256)", - "pin": "Certificat épinglé (SHA-256)", - "nextConnection": "Le statut est mis à jour à la prochaine connexion de la Toniebox.", - "statusText": { - "none": "Pas encore vérifié", - "trusted": "Certificat émis par TeddyCloud", - "pinned": "Certificat épinglé", - "mismatch": "Autre certificat que celui épinglé !" - } - } + "warningUseAtYourOwnRiskText": "Flasher ta Toniebox avec un firmware modifié annulera la garantie et peut rendre la box inutilisable. Suis attentivement les instructions et assure-toi de bien comprendre les risques encourus." }, "tonies": { "addNewCustomTonie": "Créer un nouveau modèle", @@ -2296,17 +2302,6 @@ "valid": "Valide:", "yes": "Oui" }, - "tafDownload": { - "asOneFile": "Télécharger en un seul fichier *.ogg", - "downloadCount": "Télécharger {{count}} pistes en *.ogg", - "downloadOne": "Télécharger 1 piste en *.ogg", - "duration": "Durée", - "modalTitle": "Télécharger les pistes en *.ogg", - "selectAll": "Sélectionner toutes les pistes", - "selectedCount": "{{selected}} sur {{total}} sélectionnées", - "track": "Piste", - "unnamedTrack": "Piste {{number}}" - }, "informationModal": { "hideTag": "Masquer le Tonie/Tag", "ok": "OK", @@ -2402,6 +2397,17 @@ "navigationTitle": "Sons du système", "title": "Sons du système" }, + "tafDownload": { + "asOneFile": "Télécharger en un seul fichier *.ogg", + "downloadCount": "Télécharger {{count}} pistes en *.ogg", + "downloadOne": "Télécharger 1 piste en *.ogg", + "duration": "Durée", + "modalTitle": "Télécharger les pistes en *.ogg", + "selectAll": "Sélectionner toutes les pistes", + "selectedCount": "{{selected}} sur {{total}} sélectionnées", + "track": "Piste", + "unnamedTrack": "Piste {{number}}" + }, "tafHeaderInvalid": "TAF invalide", "tafHeaderOf": "En-tête TAF de ", "tafHeaderToniesJsonHint": "Tu ne dois pas modifier les données ici lorsque tu les envoies au tonies-json. Cela inclut le nombre de pistes, même s'il est différent du nombre de pistes dans le champ track-desc !", diff --git a/public/translations/it.json b/public/translations/it.json index 37e6bc58..637598c7 100644 --- a/public/translations/it.json +++ b/public/translations/it.json @@ -21,11 +21,11 @@ "password": "Password", "passwordMin": "Almeno 4 caratteri", "passwordRequired": "La password è obbligatoria", - "username": "Nome utente", - "usernameRequired": "Il nome utente è obbligatorio", + "retry": "Riprova", "statusLoadFailed": "Impossibile caricare lo stato di autenticazione", "statusLoadFailedDetails": "Impossibile caricare lo stato di autenticazione. Controlla la connessione a TeddyCloud e riprova.", - "retry": "Riprova" + "username": "Nome utente", + "usernameRequired": "Il nome utente è obbligatorio" }, "common": { "cancel": "Annulla" @@ -142,6 +142,7 @@ "fields": { "author": "Autore del plugin", "description": "Breve descrizione del plugin", + "hideInNav": "Flag che indica se il plugin deve essere nascosto nella navigazione (true/false)", "icon": "L'icona utilizzata per il plugin. Deve essere un componente icona Ant Design valido da https://ant.design/components/icon, ad es. TrophyOutlined, TagsOutlined", "pluginHomepage": "Homepage del plugin", "pluginName": "Il titolo mostrato nei menu (obbligatorio)", @@ -157,11 +158,16 @@ }, "helpButton": "Aiuto", "helpTextCreatePlugin": "Testo su come creare un plugin", + "hidden": "Nascosto", "installedPlugins": "Plugin installati", "intro": "Qui trovi i plugin TeddyCloud che hai aggiunto e puoi aggiungere nuovi plugin forniti da te o dalla comunità TeddyCloud!", "navigationTitle": "Plugin TC", "ok": "OK", "open": "Apri plugin", + "pagination": { + "pageSelector": "Plugins per pagina", + "showAll": "Mostra tutti" + }, "plugin": "Plugin", "title": "Plugin TeddyCloud", "upload": { @@ -391,10 +397,18 @@ "filter": "Filtra nella cartella corrente", "help": { "actionItems": { + "assignToTonie": { + "text": "Assegna il file selezionato come contenuto a un Tonie/Tag.", + "title": "Assegna a Tonie" + }, "deleteFileFolder": { "text": "Elimina il file o la cartella (solo se la cartella è vuota). Nota: se questo file è assegnato a un Tonie/Tag, il riferimento non verrà aggiornato.", "title": "Elimina file/cartella" }, + "downloadFile": { + "text": "Scarica il file selezionato. Per i file TAF con più tracce si apre una selezione delle tracce per scaricarle come file *.ogg separati (ZIP) o come un unico file *.ogg; i file TAF con una sola traccia vengono scaricati come un unico file *.ogg.", + "title": "Scarica file" + }, "duplicateTAPFile": { "text": "Crea un duplicato del file TAP selezionato.", "title": "Duplica file TAP" @@ -407,6 +421,16 @@ "text": "Apre l'editor di playlist TeddyAudio per gestire i file TAP.", "title": "Modifica playlist TAP" }, + "markAsListened": { + "listened": { + "text": "Il file è attualmente segnato come ascoltato. Fai clic per segnarlo come non ascoltato.", + "title": "Segna come non ascoltato" + }, + "unlistened": { + "text": "Il file è attualmente segnato come non ascoltato. Fai clic per segnarlo come ascoltato.", + "title": "Segna come ascoltato" + } + }, "migrateToAudioIdFolder": { "text": "Sposta il file TAF nella cartella 'by/audioId' della libreria e aggiorna la sorgente nel file JSON.", "title": "Migra file TAF nella libreria per AudioId" @@ -426,24 +450,6 @@ "renameFile": { "text": "Rinomina il file. Nota: se questo file è assegnato a un Tonie/Tag, il riferimento non verrà aggiornato automaticamente.", "title": "Rinomina file" - }, - "downloadFile": { - "title": "Scarica file", - "text": "Scarica il file selezionato. Per i file TAF con più tracce si apre una selezione delle tracce per scaricarle come file *.ogg separati (ZIP) o come un unico file *.ogg; i file TAF con una sola traccia vengono scaricati come un unico file *.ogg." - }, - "markAsListened": { - "unlistened": { - "title": "Segna come ascoltato", - "text": "Il file è attualmente segnato come non ascoltato. Fai clic per segnarlo come ascoltato." - }, - "listened": { - "title": "Segna come non ascoltato", - "text": "Il file è attualmente segnato come ascoltato. Fai clic per segnarlo come non ascoltato." - } - }, - "assignToTonie": { - "title": "Assegna a Tonie", - "text": "Assegna il file selezionato come contenuto a un Tonie/Tag." } }, "actionItemsMulti": { @@ -1175,6 +1181,19 @@ "status": "Stato", "troubleShooting": "Risoluzione dei problemi" }, + "boxCertificate": { + "nextConnection": "Lo stato viene aggiornato alla prossima connessione della Toniebox.", + "pin": "Certificato fissato (SHA-256)", + "presented": "Certificato presentato (SHA-256)", + "status": "Stato", + "statusText": { + "mismatch": "Certificato diverso da quello fissato!", + "none": "Non ancora verificato", + "pinned": "Certificato fissato", + "trusted": "Certificato emesso da TeddyCloud" + }, + "title": "Verifica del certificato" + }, "boxFlashingCommon": { "adguard": { "adGuardRequirement": "AdGuard: assicurati che AdGuard Home sia installato e configurato su un dispositivo della tua rete.", @@ -1385,6 +1404,7 @@ }, "applyingPatches": "Applicazione delle patch", "applyingPatchesSection": { + "altPortHint": "Se hai creato una patch della porta, mettila anche in /revvox/boot/patch/ e aggiungi \"altPort.custom\" all'elenco delle patch.", "examplengCFGJsonCollapse": { "collapseTitle": "Esempio di \"ngCfg.json\" con patch \"altUrl.custom.305\" usando \"ofw2\"" }, @@ -1426,8 +1446,7 @@ "waitForPressDesc": "Attende la pressione di un orecchio all'avvio con una sequenza lampeggiante (blu, verde, ciano, nero)", "waitTimeoutInSDesc": "Timeout in secondi per waitForPress se non viene premuto alcun orecchio (ibernazione)", "watchdogDesc": "Mantiene il watchdog abilitato durante l'avvio del firmware (se l'avvio fallisce, il box si riavvia)" - }, - "altPortHint": "Se hai creato una patch della porta, mettila anche in /revvox/boot/patch/ e aggiungi \"altPort.custom\" all'elenco delle patch." + } }, "applyingPatchesWithNgCfgJson": "Applica patch usando ngCfg.json", "applyingPatchesWithNgCfgJsonLink": "Trova qui le istruzioni", @@ -1450,13 +1469,10 @@ "connectToTonieboxProceed": "Ora sei pronto per procedere!", "connectToTonieboxText": "Il LED verde sulla scheda madre del Toniebox deve rimanere acceso in modo fisso (senza lampeggiare). In caso contrario, i collegamenti potrebbero essere allentati o errati. Scollega il programmatore, ricontrolla tutti i collegamenti e riprova. Se utilizzi fili sottili, valuta l'uso di colla a caldo per fissarli e impedirne il movimento. Assicurati inoltre che il Toniebox sia alimentato tramite il caricatore.", "connectToTonieboxTextUart": "Assicurati che la UART sia configurata a 3,3 V prima di collegare il programmatore UART al computer tramite USB.", - "createPatch": "Crea altUrl.custom.305.patch", - "customPortPatch": "Opzionale: crea una patch della porta (altPort.custom.json)", - "customPortPatchHint": "Necessaria solo se la Toniebox deve raggiungere TeddyCloud su una porta diversa da 443, ad esempio tramite un proxy che instrada in base alla porta. La patch funziona con le versioni firmware 3.1.0 BF2, 3.3.0 e 3.4.0. hackiebox-ng include già altPort.json per la porta 10443; crea una patch propria solo per un'altra porta. Le Toniebox CC3235 e la Toniebox 2 non si possono patchare: si connettono sempre alla porta 443.", - "hintPatchPort": "Inserisci la porta a cui la Toniebox deve connettersi e fai clic su 'Crea altPort.custom.patch'. La patch verrà scaricata automaticamente.", - "port": "Porta TeddyCloud:", - "portInvalid": "La porta deve essere compresa tra 1 e 65535.", + "createHostNamePatch": "Crea altUrl.custom.305.patch", "createPortPatch": "Crea altPort.custom.patch", + "customPortPatch": "Opzionale: Crea una patch della porta (altPort.custom.json)", + "customPortPatchHint": "Necessaria solo se la Toniebox deve raggiungere TeddyCloud su una porta diversa da 443, ad esempio tramite un proxy che instrada in base alla porta. La patch funziona con le versioni firmware 3.1.0 BF2, 3.3.0 e 3.4.0. hackiebox-ng include già altPort.json per la porta 10443; crea una patch propria solo per un'altra porta. Le Toniebox CC3235 e la Toniebox 2 non si possono patchare: si connettono sempre alla porta 443.", "customUrlPatch": "Crea una patch URL personalizzata (altUrl.custom.305.json)", "customUrlPatchHint": "Crea questa patch solo se le patch predefinite altUrl.tc.fritz.box.json e altUrl.305.json non funzionano con la configurazione TeddyCloud corrente. Se soddisfano le tue esigenze, puoi procedere facendo clic su 'Avanti'.", "dedicatedUart": "UART dedicata", @@ -1479,6 +1495,7 @@ "hint": "Attualmente TeddyCloud non supporta direttamente il flashing del Toniebox CC3200, ma qui trovi una guida passo passo:", "hint2": "Segui quindi il percorso CC3200:", "hintPatchHost": "Inserisci l'hostname della tua istanza TeddyCloud e fai clic su 'Create altUrl.custom.305.patch'. La patch verrà scaricata automaticamente.", + "hintPatchPort": "Inserisci la porta a cui la Toniebox deve connettersi e fai clic su 'Crea altPort.custom.patch'. La patch verrà scaricata automaticamente.", "hostname": "Hostname TeddyCloud:", "hostnameTooLong": "L'hostname non deve superare 12 caratteri.", "installBootloader": "Installa il bootloader personalizzato HackieboxNG", @@ -1545,6 +1562,8 @@ "next": "Avanti", "patches": "Patch", "patchesMoreInformationLink": "Ulteriori informazioni sulle patch personalizzate", + "port": "Porta TeddyCloud:", + "portInvalid": "La porta deve essere compresa tra 1 e 65535.", "predefinedUrlPatches": "Patch URL predefinite", "predefinedUrlPatchesIntro": "Per i box CC3200 si consiglia di usare una patch altUrl. HackieboxNG include due patch altUrl predefinite pronte all'uso:", "previous": "Indietro", @@ -1620,7 +1639,6 @@ "locateFlashMemoryText": "Individua sulla scheda il chip di memoria flash IS25LP032D; si trova a destra della base di ricarica.", "navigationTitle": "CC3235", "next": "Avanti", - "portHint": "I box CC3235 si collegano sempre alla porta 443: per loro non esiste una patch della porta, quindi TeddyCloud deve essere raggiungibile sulla porta 443 (un proxy può instradarli solo per IP).", "pico": { "preparation": "Necessario solo se intendi eseguire il flashing con un Raspberry Pi Pico", "readingFlash": "Lettura della flash con Raspberry Pi Pico", @@ -1628,6 +1646,7 @@ "readingFlashText2": "Si consiglia di eseguire più letture e confrontare i risultati.", "writingFlash": "Scrittura della flash con Raspberry Pi Pico" }, + "portHint": "I box CC3235 si collegano sempre alla porta 443: per loro non esiste una patch della porta, quindi TeddyCloud deve essere raggiungibile sulla porta 443 (un proxy può instradarli solo per IP).", "previous": "Indietro", "serprogFirmwareLink": "Trova qui le istruzioni di installazione", "title": "Flashing del box CC3235", @@ -1743,8 +1762,9 @@ "hintESP32FirmwareFlashed": "L'applicazione delle patch dovrebbe essere completata. Scollega il Toniebox dalla UART, rimuovi il cortocircuito da J100 e riavvia il box. Ora dovrebbe potersi connettere a TeddyCloud!", "hintESP32FirmwareReseted": "Il ripristino dovrebbe essere completato. Scollega il Toniebox dalla UART, rimuovi il cortocircuito da J100 e riavvia il box. Non dovrebbe più essere collegato a TeddyCloud. Esegui un freshness check per verificare che funzioni correttamente.", "hintFlashESP32RevvoxFlasher": "Fai clic su \"Flash ESP32\", se necessario scegli la porta COM e attendi il completamento del passaggio!", - "hintPatchFlash": "Per applicare alla flash selezionata/letta il certificato CA TeddyCloud ed eventualmente altre modifiche, come l'hostname di TeddyCloud e/o le credenziali Wi-Fi, fai clic su 'Patch' dopo aver inserito le informazioni oppure passa al passaggio successivo senza applicare patch.", + "hintPatchFlash": "Per applicare alla flash selezionata/letta il certificato CA TeddyCloud ed eventualmente altre modifiche, come l'hostname e il port di TeddyCloud e/o le credenziali Wi-Fi, fai clic su 'Patch' dopo aver inserito le informazioni oppure passa al passaggio successivo senza applicare patch.", "hintPatchHost": "Inserisci l'hostname desiderato per la tua istanza TeddyCloud.", + "hintPatchPort": "Porta a cui si connette la Toniebox. Lascia 443, a meno che la Toniebox non debba raggiungere TeddyCloud su un'altra porta, ad esempio tramite un proxy che instrada in base alla porta. Le Toniebox CC3235 e la Toniebox 2 non si possono patchare: si connettono sempre alla porta 443.", "hintPatchWifi": "Facoltativamente inserisci le credenziali Wi-Fi del Toniebox. Impostale solo se realmente necessario: di solito, se il Toniebox era collegato al Wi-Fi subito prima dell'applicazione delle patch, non serve.", "hintReadESP32ImportFlash": "Ora scollega l'alimentazione del Toniebox. Assicurati che la UART funzioni in modalità 3,3 V. Collega la UART al PC e al Toniebox. Cortocircuita i fori del pin J100. Quindi alimenta nuovamente il Toniebox. Fai clic su \"Read Flash\" o \"Load File\", scegli la porta COM e attendi il completamento del passaggio!", "hintSaveLog": "In caso di problemi, salva l'output del log in un file per ulteriori analisi.", @@ -1752,9 +1772,6 @@ "hostname": "Hostname TeddyCloud:", "hostnameSettings": "Hostname TeddyCloud", "hostnameTooLong": "Se in seguito vuoi collegare un Toniebox CC3200, l'hostname non deve superare 12 caratteri (limitazione del firmware).", - "hintPatchPort": "Porta a cui si connette la Toniebox. Lascia 443, a meno che la Toniebox non debba raggiungere TeddyCloud su un'altra porta, ad esempio tramite un proxy che instrada in base alla porta. Le Toniebox CC3235 e la Toniebox 2 non si possono patchare: si connettono sempre alla porta 443.", - "port": "Porta TeddyCloud:", - "portInvalid": "La porta deve essere compresa tra 1 e 32767.", "importanceBackup": "Prima di apportare modifiche al Toniebox, scarica e conserva in modo sicuro una copia del firmware originale. In questo modo potrai sempre ripristinare il Toniebox alle condizioni di fabbrica in caso di problemi. Salvala in più posizioni, ad esempio sul computer principale o laptop, su un disco esterno o chiavetta USB e/o su un servizio cloud affidabile (ad es. Google Drive, Dropbox, OneDrive). Non conservare mai l'unica copia sul server TeddyCloud.", "infoTable": "Informazioni ESP32", "invalidFlashData": "La flash letta/caricata contiene solo 0x00 o 0xFF! Controlla il collegamento o il file caricato e riprova!", @@ -1770,6 +1787,8 @@ "patchingFlashImage": "Applicazione patch all'immagine flash...", "patchingSuccessful": "Patch applicata correttamente, pronto per flashare {{size}} MiB.", "payloadReceived": "Payload ricevuto", + "port": "Porta TeddyCloud:", + "portInvalid": "La porta deve essere compresa tra 1 e 32767.", "portOpenFailedInUse": "Impossibile aprire questa porta. È già in uso?", "preparingFlash": "Preparazione della flash da {{size}} MiB", "previous": "Indietro", @@ -1884,20 +1903,7 @@ "uploadTonieboxCertificates": "Carica certificati per Toniebox{{name}}" }, "warningUseAtYourOwnRisk": "Avviso: usa a tuo rischio!", - "warningUseAtYourOwnRiskText": "Flashare il Toniebox con un firmware modificato invaliderà la garanzia e potrebbe danneggiare il box. Segui attentamente le istruzioni e assicurati di comprendere i rischi.", - "boxCertificate": { - "title": "Verifica del certificato", - "status": "Stato", - "presented": "Certificato presentato (SHA-256)", - "pin": "Certificato fissato (SHA-256)", - "nextConnection": "Lo stato viene aggiornato alla prossima connessione della Toniebox.", - "statusText": { - "none": "Non ancora verificato", - "trusted": "Certificato emesso da TeddyCloud", - "pinned": "Certificato fissato", - "mismatch": "Certificato diverso da quello fissato!" - } - } + "warningUseAtYourOwnRiskText": "Flashare il Toniebox con un firmware modificato invaliderà la garanzia e potrebbe danneggiare il box. Segui attentamente le istruzioni e assicurati di comprendere i rischi." }, "tonies": { "addNewCustomTonie": "Crea nuovo modello", @@ -2296,17 +2302,6 @@ "valid": "Valido:", "yes": "Sì" }, - "tafDownload": { - "asOneFile": "Scarica come un unico file *.ogg", - "downloadCount": "Scarica {{count}} tracce come *.ogg", - "downloadOne": "Scarica 1 traccia come *.ogg", - "duration": "Durata", - "modalTitle": "Scarica tracce come *.ogg", - "selectAll": "Seleziona tutte le tracce", - "selectedCount": "{{selected}} di {{total}} selezionate", - "track": "Traccia", - "unnamedTrack": "Traccia {{number}}" - }, "informationModal": { "hideTag": "Nascondi Tonie/Tag", "ok": "OK", @@ -2402,6 +2397,17 @@ "navigationTitle": "Suoni di sistema", "title": "Suoni di sistema" }, + "tafDownload": { + "asOneFile": "Scarica come un unico file *.ogg", + "downloadCount": "Scarica {{count}} tracce come *.ogg", + "downloadOne": "Scarica 1 traccia come *.ogg", + "duration": "Durata", + "modalTitle": "Scarica tracce come *.ogg", + "selectAll": "Seleziona tutte le tracce", + "selectedCount": "{{selected}} di {{total}} selezionate", + "track": "Traccia", + "unnamedTrack": "Traccia {{number}}" + }, "tafHeaderInvalid": "TAF non valido", "tafHeaderOf": "Intestazione TAF di ", "tafHeaderToniesJsonHint": "Non devi modificare questi dati quando li invii a tonies-json. Ciò include il numero di tracce, anche se è diverso dal numero di tracce nel campo track-desc!", diff --git a/public/translations/pl.json b/public/translations/pl.json index 1b069108..d2a0165b 100644 --- a/public/translations/pl.json +++ b/public/translations/pl.json @@ -21,11 +21,11 @@ "password": "Hasło", "passwordMin": "Co najmniej 4 znaki", "passwordRequired": "Hasło jest wymagane", - "username": "Nazwa użytkownika", - "usernameRequired": "Nazwa użytkownika jest wymagana", + "retry": "Spróbuj ponownie", "statusLoadFailed": "Nie udało się wczytać stanu uwierzytelniania", "statusLoadFailedDetails": "Nie udało się wczytać stanu uwierzytelniania. Sprawdź połączenie z TeddyCloud i spróbuj ponownie.", - "retry": "Spróbuj ponownie" + "username": "Nazwa użytkownika", + "usernameRequired": "Nazwa użytkownika jest wymagana" }, "common": { "cancel": "Anuluj" @@ -142,6 +142,7 @@ "fields": { "author": "Autor wtyczki", "description": "Krótki opis wtyczki", + "hideInNav": "Flaga określająca, czy wtyczka ma być ukryta w nawigacji (true/false)", "icon": "Ikona używana dla wtyczki. Musi być prawidłowym komponentem ikony Ant Design z https://ant.design/components/icon, np. TrophyOutlined, TagsOutlined", "pluginHomepage": "Strona domowa wtyczki", "pluginName": "Tytuł wyświetlany w menu (wymagane)", @@ -157,11 +158,16 @@ }, "helpButton": "Pomoc", "helpTextCreatePlugin": "Przykładowy tekst o tym, jak stworzyć wtyczkę", + "hidden": "Ukryta", "installedPlugins": "Zainstalowane wtyczki", "intro": "Tutaj znajdziesz dodane przez Ciebie wtyczki TeddyCloud oraz możesz dodać nowe wtyczki udostępnione przez Ciebie lub społeczność TeddyCloud!", "navigationTitle": "Wtyczki TC", "ok": "OK", "open": "Otwórz wtyczkę", + "pagination": { + "pageSelector": "Wtyczki na stronę", + "showAll": "Pokaż wszystkie" + }, "plugin": "Wtyczka", "title": "Wtyczki TeddyCloud", "upload": { @@ -391,10 +397,18 @@ "filter": "Filtruj w bieżącym folderze", "help": { "actionItems": { + "assignToTonie": { + "text": "Przypisuje wybrany plik jako zawartość do Tonie/Taga.", + "title": "Przypisz do Tonie" + }, "deleteFileFolder": { "text": "Usuwa plik lub folder (tylko jeśli folder jest pusty). Uwaga: jeśli ten plik jest przypisany do Tonie/Taga, odwołanie nie zostanie zaktualizowane.", "title": "Usuń plik/folder" }, + "downloadFile": { + "text": "Pobiera wybrany plik. W przypadku plików TAF z wieloma utworami otwiera się wybór utworów, aby pobrać je jako osobne pliki *.ogg (ZIP) lub jako jeden plik *.ogg; pliki TAF z jednym utworem są pobierane jako jeden plik *.ogg.", + "title": "Pobierz plik" + }, "duplicateTAPFile": { "text": "Tworzy duplikat wybranego pliku TAP.", "title": "Duplikuj plik TAP" @@ -407,6 +421,16 @@ "text": "Otwiera edytor playlist TeddyAudio do zarządzania plikami TAP.", "title": "Edytuj playlistę TAP" }, + "markAsListened": { + "listened": { + "text": "Plik jest obecnie oznaczony jako odsłuchany. Kliknij, aby oznaczyć go jako nieodsłuchany.", + "title": "Oznacz jako nieodsłuchane" + }, + "unlistened": { + "text": "Plik jest obecnie oznaczony jako nieodsłuchany. Kliknij, aby oznaczyć go jako odsłuchany.", + "title": "Oznacz jako odsłuchane" + } + }, "migrateToAudioIdFolder": { "text": "Przenosi plik TAF do folderu biblioteki 'by/audioId' i aktualizuje źródło w pliku JSON.", "title": "Migruj plik TAF do biblioteki wg AudioId" @@ -426,24 +450,6 @@ "renameFile": { "text": "Zmienia nazwę pliku. Uwaga: jeśli ten plik jest przypisany do Tonie/Taga, odwołanie nie zostanie zaktualizowane automatycznie.", "title": "Zmień nazwę pliku" - }, - "downloadFile": { - "title": "Pobierz plik", - "text": "Pobiera wybrany plik. W przypadku plików TAF z wieloma utworami otwiera się wybór utworów, aby pobrać je jako osobne pliki *.ogg (ZIP) lub jako jeden plik *.ogg; pliki TAF z jednym utworem są pobierane jako jeden plik *.ogg." - }, - "markAsListened": { - "unlistened": { - "title": "Oznacz jako odsłuchane", - "text": "Plik jest obecnie oznaczony jako nieodsłuchany. Kliknij, aby oznaczyć go jako odsłuchany." - }, - "listened": { - "title": "Oznacz jako nieodsłuchane", - "text": "Plik jest obecnie oznaczony jako odsłuchany. Kliknij, aby oznaczyć go jako nieodsłuchany." - } - }, - "assignToTonie": { - "title": "Przypisz do Tonie", - "text": "Przypisuje wybrany plik jako zawartość do Tonie/Taga." } }, "actionItemsMulti": { @@ -1175,6 +1181,19 @@ "status": "Status", "troubleShooting": "Rozwiązywanie problemów" }, + "boxCertificate": { + "nextConnection": "Status zostanie zaktualizowany przy następnym połączeniu Toniebox.", + "pin": "Przypięty certyfikat (SHA-256)", + "presented": "Przedstawiony certyfikat (SHA-256)", + "status": "Status", + "statusText": { + "mismatch": "Inny certyfikat niż przypięty!", + "none": "Jeszcze nie sprawdzono", + "pinned": "Przypięty certyfikat", + "trusted": "Certyfikat wystawiony przez TeddyCloud" + }, + "title": "Sprawdzanie certyfikatu" + }, "boxFlashingCommon": { "adguard": { "adGuardRequirement": "AdGuard: upewnij się, że AdGuard Home jest zainstalowany i skonfigurowany na urządzeniu w Twojej sieci.", @@ -1385,6 +1404,7 @@ }, "applyingPatches": "Stosowanie łatek", "applyingPatchesSection": { + "altPortHint": "Jeśli utworzyłeś poprawkę portu, umieść ją również w /revvox/boot/patch/ i dodaj \"altPort.custom\" do listy poprawek.", "examplengCFGJsonCollapse": { "collapseTitle": "Przykładowy plik \"ngCfg.json\" z łatką \"altUrl.custom.305\" używającą \"ofw2\"" }, @@ -1426,8 +1446,7 @@ "waitForPressDesc": "Czeka na naciśnięcie ucha przy starcie z sekwencją mrugania (niebieski, zielony, cyjan, czarny)", "waitTimeoutInSDesc": "Limit czasu w sekundach dla waitForPress, jeśli brak naciśnięcia ucha (hibernacja)", "watchdogDesc": "Utrzymuje watchdog włączony podczas uruchamiania firmware (jeśli rozruch się nie powiedzie, boxa się zrestartuje)" - }, - "altPortHint": "Jeśli utworzyłeś poprawkę portu, umieść ją również w /revvox/boot/patch/ i dodaj \"altPort.custom\" do listy poprawek." + } }, "applyingPatchesWithNgCfgJson": "Zastosuj łatki za pomocą ngCfg.json", "applyingPatchesWithNgCfgJsonLink": "Znajdź instrukcje tutaj", @@ -1450,13 +1469,10 @@ "connectToTonieboxProceed": "Możesz teraz kontynuować!", "connectToTonieboxText": "Zielona dioda LED na płycie głównej Tonieboxa powinna świecić się stabilnie (bez migania lub mrugania). Jeśli tak nie jest, Twoje połączenia mogą być poluzowane lub nieprawidłowe. Odłącz programator, sprawdź dwukrotnie wszystkie połączenia i spróbuj ponownie. Jeśli pracujesz z cienkimi przewodami, rozważ zastosowanie kleju na gorąco, aby je zabezpieczyć i zapobiec ruchowi. Dodatkowo upewnij się, że Toniebox jest zasilany za pomocą swojej ładowarki.", "connectToTonieboxTextUart": "Upewnij się, że UART jest skonfigurowany na 3,3V przed podłączeniem programatora UART do komputera przez USB.", - "createPatch": "Utwórz altUrl.custom.305.patch", - "customPortPatch": "Opcjonalnie: utwórz poprawkę portu (altPort.custom.json)", - "customPortPatchHint": "Potrzebne tylko wtedy, gdy Toniebox ma łączyć się z TeddyCloud przez inny port niż 443, np. przez proxy kierujące ruch według portu. Poprawka działa z wersjami firmware 3.1.0 BF2, 3.3.0 i 3.4.0. hackiebox-ng zawiera już altPort.json dla portu 10443; własną poprawkę twórz tylko dla innego portu. Toniebox z CC3235 i Toniebox 2 nie można załatać: zawsze łączą się z portem 443.", - "hintPatchPort": "Wpisz port, z którym Toniebox ma się łączyć, i kliknij 'Utwórz altPort.custom.patch'. Poprawka zostanie automatycznie pobrana.", - "port": "Port TeddyCloud:", - "portInvalid": "Port musi mieścić się w zakresie od 1 do 65535.", + "createHostNamePatch": "Utwórz altUrl.custom.305.patch", "createPortPatch": "Utwórz altPort.custom.patch", + "customPortPatch": "Opcjonalnie: Utwórz poprawkę portu (altPort.custom.json)", + "customPortPatchHint": "Potrzebne tylko wtedy, gdy Toniebox ma łączyć się z TeddyCloud przez inny port niż 443, np. przez proxy kierujące ruch według portu. Poprawka działa z wersjami firmware 3.1.0 BF2, 3.3.0 i 3.4.0. hackiebox-ng zawiera już altPort.json dla portu 10443; własną poprawkę twórz tylko dla innego portu. Toniebox z CC3235 i Toniebox 2 nie można załatać: zawsze łączą się z portem 443.", "customUrlPatch": "Utwórz niestandardową łatkę URL (altUrl.custom.305.json)", "customUrlPatchHint": "Utwórz tę łatkę tylko wtedy, gdy predefiniowane łatki altUrl.tc.fritz.box.json i altUrl.305.json nie działają z Twoją obecną konfiguracją TeddyCloud. Jeśli spełniają Twoje potrzeby, możesz kontynuować, klikając 'Next'.", "dedicatedUart": "Dedykowany UART", @@ -1479,6 +1495,7 @@ "hint": "Obecnie nie ma bezpośredniego wsparcia flashowania Tonieboxa CC3200 z poziomu TeddyCloud, ale znajdziesz tutaj przewodnik krok po kroku:", "hint2": "Następnie postępuj zgodnie ze ścieżką CC3200:", "hintPatchHost": "Wprowadź nazwę hosta swojej instancji TeddyCloud, a następnie kliknij 'Create altUrl.custom.305.patch'. Łatka zostanie automatycznie pobrana.", + "hintPatchPort": "Wpisz port, z którym Toniebox ma się łączyć, i kliknij 'Utwórz altPort.custom.patch'. Poprawka zostanie automatycznie pobrana.", "hostname": "Nazwa hosta TeddyCloud:", "hostnameTooLong": "Nazwa hosta nie może przekraczać 12 znaków.", "installBootloader": "Zainstaluj niestandardowy bootloader HackieboxNG", @@ -1545,6 +1562,8 @@ "next": "Dalej", "patches": "Łatki", "patchesMoreInformationLink": "Więcej informacji o niestandardowych łatkach", + "port": "Port TeddyCloud:", + "portInvalid": "Port musi mieścić się w zakresie od 1 do 65535.", "predefinedUrlPatches": "Predefiniowane łatki URL", "predefinedUrlPatchesIntro": "Dla boxów CC3200 zaleca się użycie łatki altUrl. HackieboxNG zawiera dwie predefiniowane, gotowe do użycia łatki altUrl:", "previous": "Wstecz", @@ -1620,7 +1639,6 @@ "locateFlashMemoryText": "Zidentyfikuj układ pamięci flash IS25LP032D na płytce; znajduje się on na prawo od stacji dokującej ładowania.", "navigationTitle": "CC3235", "next": "Dalej", - "portHint": "Pudełka CC3235 zawsze łączą się z portem 443: nie ma dla nich łatki portu, więc TeddyCloud musi być dostępny na porcie 443 (proxy może je kierować tylko według IP).", "pico": { "preparation": "Wymagane tylko wtedy, gdy planujesz flashowanie za pomocą Raspberry Pi Pico", "readingFlash": "Odczyt Flasha za pomocą Raspberry Pi Pico", @@ -1628,6 +1646,7 @@ "readingFlashText2": "Zalecane jest wykonanie wielu odczytów i porównanie wyników.", "writingFlash": "Zapis Flasha za pomocą Raspberry Pi Pico" }, + "portHint": "Pudełka CC3235 zawsze łączą się z portem 443: nie ma dla nich łatki portu, więc TeddyCloud musi być dostępny na porcie 443 (proxy może je kierować tylko według IP).", "previous": "Wstecz", "serprogFirmwareLink": "Znajdź instrukcje instalacji tutaj", "title": "Flashowanie boxa CC3235", @@ -1743,8 +1762,9 @@ "hintESP32FirmwareFlashed": "Patchowanie powinno być zakończone. Odłącz Tonieboxa od UART, usuń zwarcie z J100 i zrestartuj boxa. Powinien teraz móc połączyć się z TeddyCloud!", "hintESP32FirmwareReseted": "Reset powinien być zakończony. Odłącz Tonieboxa od UART, usuń zwarcie z J100 i zrestartuj boxa. Nie powinien już być połączony z Twoim Teddycloud. Wykonaj freshnesscheck, aby sprawdzić, czy działa poprawnie.", "hintFlashESP32RevvoxFlasher": "Kliknij \"Flash ESP32\", w razie potrzeby wybierz port COM i poczekaj, aż krok się zakończy!", - "hintPatchFlash": "Aby zastosować wybrany/odczytany flash z certyfikatem CA TeddyCloud i ewentualnie wprowadzić inne zmiany, takie jak nazwa hosta TeddyCloud i/lub dane Wi-Fi, kliknij 'Patch' po wprowadzeniu informacji lub przejdź do następnego kroku bez patchowania.", + "hintPatchFlash": "Aby zastosować wybrany/odczytany flash z certyfikatem CA TeddyCloud i ewentualnie wprowadzić inne zmiany, takie jak nazwa hosta i port TeddyCloud i/lub dane Wi-Fi, kliknij 'Patch' po wprowadzeniu informacji lub przejdź do następnego kroku bez patchowania.", "hintPatchHost": "Wprowadź żądaną nazwę hosta dla swojej instancji TeddyCloud.", + "hintPatchPort": "Port, z którym łączy się Toniebox. Pozostaw 443, chyba że Toniebox ma łączyć się z TeddyCloud przez inny port, np. przez proxy kierujące ruch według portu. Toniebox z CC3235 i Toniebox 2 nie można załatać: zawsze łączą się z portem 443.", "hintPatchWifi": "Opcjonalnie wprowadź dane Wi-Fi dla swojego Tonieboxa. Ustaw to tylko, jeśli rzeczywiście musisz - zazwyczaj, jeśli Twój Toniebox był podłączony do Twojej sieci Wi-Fi bezpośrednio przed patchowaniem, nie jest to konieczne.", "hintReadESP32ImportFlash": "Teraz odłącz źródło zasilania swojego Tonieboxa. Upewnij się, że UART działa w trybie 3,3V. Podłącz swój UART do komputera i Tonieboxa. Zewrzyj otwory pinów J100. Następnie podłącz Tonieboxa do zasilania. Kliknij \"Read Flash\" lub \"Load File\", a następnie wybierz port COM. Na koniec poczekaj, aż krok się zakończy!", "hintSaveLog": "Zapisz dane wyjściowe logu do pliku w celu dalszej analizy w razie problemów.", @@ -1752,9 +1772,6 @@ "hostname": "Nazwa hosta TeddyCloud:", "hostnameSettings": "Nazwa hosta TeddyCloud", "hostnameTooLong": "Jeśli chcesz później podłączyć Tonieboxa CC3200, nazwa hosta nie może przekraczać 12 znaków (ograniczenie firmware).", - "hintPatchPort": "Port, z którym łączy się Toniebox. Pozostaw 443, chyba że Toniebox ma łączyć się z TeddyCloud przez inny port, np. przez proxy kierujące ruch według portu. Toniebox z CC3235 i Toniebox 2 nie można załatać: zawsze łączą się z portem 443.", - "port": "Port TeddyCloud:", - "portInvalid": "Port musi mieścić się w zakresie od 1 do 32767.", "importanceBackup": "Przed wprowadzeniem jakichkolwiek zmian w Tonieboxie pobierz i bezpiecznie przechowaj kopię oryginalnego firmware. Zapewni to możliwość przywrócenia Tonieboxa do stanu fabrycznego, jeśli coś pójdzie nie tak. Zapisz ją w kilku miejscach, np. na głównym komputerze lub laptopie, zewnętrznym dysku twardym lub pendrivie i/lub w zaufanej usłudze przechowywania w chmurze (np. Google Drive, Dropbox, OneDrive). Nigdy nie przechowuj jedynej kopii wyłącznie na serwerze TeddyCloud.", "infoTable": "Informacje ESP32", "invalidFlashData": "Odczytany/wczytany flash składa się wyłącznie z 0x00 lub 0xFF! Sprawdź połączenie lub wczytany plik i spróbuj ponownie!", @@ -1770,6 +1787,8 @@ "patchingFlashImage": "Patchowanie obrazu flasha...", "patchingSuccessful": "Patchowanie zakończone pomyślnie, gotowe do wgrania {{size}} MiB.", "payloadReceived": "Otrzymano dane", + "port": "Port TeddyCloud:", + "portInvalid": "Port musi mieścić się w zakresie od 1 do 32767.", "portOpenFailedInUse": "Nie udało się otworzyć tego portu. Może jest już używany?", "preparingFlash": "Przygotowywanie {{size}} MiB flasha", "previous": "Wstecz", @@ -1884,20 +1903,7 @@ "uploadTonieboxCertificates": "Prześlij certyfikaty dla Tonieboxa {{name}}" }, "warningUseAtYourOwnRisk": "Ostrzeżenie: używasz na własne ryzyko!", - "warningUseAtYourOwnRiskText": "Flashowanie Tonieboxa spatchowanym firmware unieważni gwarancję i może doprowadzić do uszkodzenia boxa. Postępuj dokładnie zgodnie z instrukcjami i upewnij się, że rozumiesz związane z tym ryzyko.", - "boxCertificate": { - "title": "Sprawdzanie certyfikatu", - "status": "Status", - "presented": "Przedstawiony certyfikat (SHA-256)", - "pin": "Przypięty certyfikat (SHA-256)", - "nextConnection": "Status zostanie zaktualizowany przy następnym połączeniu Toniebox.", - "statusText": { - "none": "Jeszcze nie sprawdzono", - "trusted": "Certyfikat wystawiony przez TeddyCloud", - "pinned": "Przypięty certyfikat", - "mismatch": "Inny certyfikat niż przypięty!" - } - } + "warningUseAtYourOwnRiskText": "Flashowanie Tonieboxa spatchowanym firmware unieważni gwarancję i może doprowadzić do uszkodzenia boxa. Postępuj dokładnie zgodnie z instrukcjami i upewnij się, że rozumiesz związane z tym ryzyko." }, "tonies": { "addNewCustomTonie": "Utwórz nowy model", @@ -2296,17 +2302,6 @@ "valid": "Prawidłowy:", "yes": "Tak" }, - "tafDownload": { - "asOneFile": "Pobierz jako jeden plik *.ogg", - "downloadCount": "Pobierz utwory ({{count}}) jako *.ogg", - "downloadOne": "Pobierz 1 utwór jako *.ogg", - "duration": "Czas", - "modalTitle": "Pobierz utwory jako *.ogg", - "selectAll": "Zaznacz wszystkie utwory", - "selectedCount": "Wybrano {{selected}} z {{total}}", - "track": "Utwór", - "unnamedTrack": "Utwór {{number}}" - }, "informationModal": { "hideTag": "Ukryj Tonie/Tag", "ok": "OK", @@ -2402,6 +2397,17 @@ "navigationTitle": "Dźwięki systemowe", "title": "Dźwięki systemowe" }, + "tafDownload": { + "asOneFile": "Pobierz jako jeden plik *.ogg", + "downloadCount": "Pobierz utwory ({{count}}) jako *.ogg", + "downloadOne": "Pobierz 1 utwór jako *.ogg", + "duration": "Czas", + "modalTitle": "Pobierz utwory jako *.ogg", + "selectAll": "Zaznacz wszystkie utwory", + "selectedCount": "Wybrano {{selected}} z {{total}}", + "track": "Utwór", + "unnamedTrack": "Utwór {{number}}" + }, "tafHeaderInvalid": "Nieprawidłowy TAF", "tafHeaderOf": "Nagłówek TAF dla ", "tafHeaderToniesJsonHint": "Nie wolno edytować tych danych podczas przesyłania ich do tonies-json. Dotyczy to również liczby ścieżek, nawet jeśli różni się ona od liczby ścieżek w polu track-desc!", diff --git a/src/components/common/buttons/PluginTemplateDownloadButton.tsx b/src/components/common/buttons/PluginTemplateDownloadButton.tsx index deafddc5..62de66ee 100644 --- a/src/components/common/buttons/PluginTemplateDownloadButton.tsx +++ b/src/components/common/buttons/PluginTemplateDownloadButton.tsx @@ -12,7 +12,8 @@ const createPluginJson = (): string => "standalone": false, "pluginHomepage": "Homepage of the plugin (optionally)", "teddyCloudSection": "one of: home|tonies|tonieboxes|settings|community (optionally)", - "icon": "name of an icon from Ant Design icons (optional)" + "icon": "name of an icon from Ant Design icons (optional)", + "hideInNav": false }`; const createScriptJs = (): string => @@ -430,6 +431,7 @@ You can add more files as needed, for example: - \`pluginHomepage\`: Optional project or documentation URL - \`teddyCloudSection\`: Optional section of the TeddyCloud UI (home|tonies|tonieboxes|settings|community) - \`icon\`: Optional Ant Design icon name +- \`hideInNav\`: Optional boolean flag to hide the plugin in navigation (true/false, no quotes!) --- diff --git a/src/components/common/footer/AudioPlayerFooter.tsx b/src/components/common/footer/AudioPlayerFooter.tsx index 09bf71f6..c7b855ec 100644 --- a/src/components/common/footer/AudioPlayerFooter.tsx +++ b/src/components/common/footer/AudioPlayerFooter.tsx @@ -192,7 +192,10 @@ const AudioPlayerFooter: React.FC = ({ onVisibilityChang setVolume(lastVolume); }; - const handleAudioPlay = () => setIsPlaying(true); + const handleAudioPlay = () => { + setIsPlaying(true); + setAudioPlayerDisplay("flex"); + }; const handleAudioPause = () => setIsPlaying(false); const handleAudioEnded = () => setIsPlaying(false); @@ -207,6 +210,7 @@ const AudioPlayerFooter: React.FC = ({ onVisibilityChang } globalAudio.load(); } + setIsPlaying(false); setAudioPlayerDisplay("none"); onVisibilityChange(false); }; diff --git a/src/components/community/CommunitySubNav.tsx b/src/components/community/CommunitySubNav.tsx index ef4eec32..5face314 100644 --- a/src/components/community/CommunitySubNav.tsx +++ b/src/components/community/CommunitySubNav.tsx @@ -48,25 +48,27 @@ export const CommunitySubNav = () => { const { plugins } = useTeddyCloud(); - const pluginItems = plugins.map((plugin) => ({ - key: `tcplugins-${plugin.pluginId}`, - label: ( - { - setNavOpen(false); - setSubNavOpen(false); - }} - > - {plugin.pluginName} - - ), - icon: React.createElement(plugin.icon), - title: plugin.pluginName, - })); + const pluginItems = plugins + .filter((p) => !p.hideInNav) + .map((plugin) => ({ + key: `tcplugins-${plugin.pluginId}`, + label: ( + { + setNavOpen(false); + setSubNavOpen(false); + }} + > + {plugin.pluginName} + + ), + icon: React.createElement(plugin.icon), + title: plugin.pluginName, + })); const filteredPluginItems = plugins - .filter((p) => p.teddyCloudSection === TeddyCloudSection.Community) + .filter((p) => p.teddyCloudSection === TeddyCloudSection.Community && !p.hideInNav) .map((plugin) => ({ key: `plugin-${plugin.pluginId}`, label: ( diff --git a/src/components/community/plugincard/PluginCard.tsx b/src/components/community/plugincard/PluginCard.tsx index 94a8b89a..79409546 100644 --- a/src/components/community/plugincard/PluginCard.tsx +++ b/src/components/community/plugincard/PluginCard.tsx @@ -1,5 +1,5 @@ -import { Card, Typography, Badge, Tooltip, theme } from "antd"; -import { DesktopOutlined, HomeOutlined, DeleteOutlined } from "@ant-design/icons"; +import { Card, Typography, Badge, Tooltip, theme, Tag } from "antd"; +import { DesktopOutlined, HomeOutlined, DeleteOutlined, TagFilled } from "@ant-design/icons"; import { useTranslation } from "react-i18next"; import { canHover } from "../../../utils/browser/browserUtils"; import { withBase } from "../../../utils/basePath"; @@ -40,15 +40,41 @@ export const PluginCard: React.FC = ({ const titleContent = plugin.teddyCloudSection ? ( -
-

- {plugin.pluginName} +
+

+ {plugin.pluginName}

) : ( -

- {plugin.pluginName} +

+ {plugin.pluginName}

); @@ -59,20 +85,24 @@ export const PluginCard: React.FC = ({ key={plugin.pluginId} style={{ width: "100%", - margin: 8, + height: "100%", + minWidth: 0, borderRadius: 8, background: token.colorBgContainerDisabled, display: "flex", flexDirection: "column", - justifyContent: "space-between", + }} + styles={{ + body: { + flex: 1, + }, }} title={
{titleContent} @@ -127,6 +157,13 @@ export const PluginCard: React.FC = ({ e.currentTarget.style.display = "none"; }} /> + {plugin.hideInNav && ( + + + {t("community.plugins.hidden")} + + + )} {plugin.author && `${t("community.plugins.by")} ${plugin.author}`} {plugin.version && ` - v${plugin.version}`} diff --git a/src/components/community/pluginlist/PluginList.tsx b/src/components/community/pluginlist/PluginList.tsx index fa96a0ff..9337a6e9 100644 --- a/src/components/community/pluginlist/PluginList.tsx +++ b/src/components/community/pluginlist/PluginList.tsx @@ -1,6 +1,7 @@ +import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; -import { Alert, Badge, Button, Col, Empty, Row, Tag, Typography } from "antd"; +import { Alert, Badge, Button, Col, Empty, Flex, Row, Tag, Typography } from "antd"; import { AppstoreAddOutlined, QuestionCircleOutlined } from "@ant-design/icons"; import { usePluginList } from "./hooks/usePluginList"; import PluginTemplateDownloadButton from "../../common/buttons/PluginTemplateDownloadButton"; @@ -8,8 +9,11 @@ import { PluginCard } from "../plugincard/PluginCard"; import { PluginDeleteDialog } from "./modals/PluginDeleteModal"; import { PluginHelpModal } from "./modals/PluginHelpModal"; import { PluginUploadModal } from "./modals/PluginUploadModal"; +import PluginPagination from "./pagination/PluginPagination"; +import { userStorage } from "../../../utils/storage/userStorage"; const { Paragraph } = Typography; +const STORAGE_KEY = "pluginListState"; export const PluginList = () => { const { t } = useTranslation(); @@ -21,6 +25,9 @@ export const PluginList = () => { activeSectionFilters, pluginCountBySection, toggleSectionFilter, + hiddenOnly, + setHiddenOnly, + hiddenPluginCount, isVisibleHelpModal, openHelp, @@ -41,15 +48,124 @@ export const PluginList = () => { handleCancelDelete, } = usePluginList(); + const [pageSize, setPageSize] = useState(() => { + const storedState = userStorage.getItem(STORAGE_KEY); + if (storedState) { + try { + const { pageSize: storedPageSize } = JSON.parse(storedState); + if (typeof storedPageSize === "number") return storedPageSize; + } catch (error) { + console.error("Error parsing stored plugin list state:", error); + } + } + return 12; + }); + const [currentPage, setCurrentPage] = useState(1); + const [paginationEnabled, setPaginationEnabled] = useState(true); + const [showAll, setShowAll] = useState(false); + const pluginListRef = useRef(null); + + useEffect(() => { + const storedState = userStorage.getItem(STORAGE_KEY); + if (storedState) { + try { + const { pageSize: storedPageSize, showAll: storedShowAll } = + JSON.parse(storedState); + if (storedShowAll) { + setPageSize(storedPageSize); + setShowAll(true); + setPaginationEnabled(false); + } else { + setPageSize(storedPageSize); + setCurrentPage(1); + } + } catch (error) { + console.error("Error parsing stored plugin list state:", error); + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + const stateToStore = JSON.stringify({ + pageSize, + paginationEnabled, + showAll, + }); + userStorage.setItem(STORAGE_KEY, stateToStore); + }, [pageSize, paginationEnabled, showAll]); + + const handleShowAll = (size?: number) => { + const effectiveSize = size ?? pageSize; + setPageSize(effectiveSize); + setShowAll(true); + setPaginationEnabled(false); + }; + + const handleShowPagination = () => { + setPaginationEnabled(true); + setShowAll(false); + handlePageSizeChange(1, pageSize); + }; + + const handlePageSizeChange = (current: number, size: number) => { + setPageSize(size); + setCurrentPage(current); + setTimeout(scrollToPluginList, 0); + }; + + const handleSectionFilterChange = (section: string, checked: boolean) => { + toggleSectionFilter(section, checked); + setCurrentPage(1); + }; + + const handleHiddenFilterChange = (checked: boolean) => { + setHiddenOnly(checked); + setCurrentPage(1); + }; + + const scrollToPluginList = () => { + const element = pluginListRef.current; + + if (!element) { + return; + } + + const rect = element.getBoundingClientRect(); + + if (rect.top < 0) { + window.scrollTo({ + top: window.scrollY + rect.top - 16, + behavior: "smooth", + }); + } + }; + + const currentPageData = showAll + ? filteredPlugins + : filteredPlugins.slice((currentPage - 1) * pageSize, currentPage * pageSize); + + const listPagination = ( +
+ {!paginationEnabled ? ( + + ) : ( + handleShowAll()} + /> + )} +
+ ); + return ( <>

{t("community.plugins.title")}

-
{
- + + <>

{t("community.plugins.installedPlugins")}

-
+
{allSections.map((section) => { const isChecked = activeSectionFilters.includes(section); + return ( toggleSectionFilter(section, checked)} + onChange={(checked) => + handleSectionFilterChange(section, checked) + } > - - {section.charAt(0).toUpperCase() + section.slice(1)} - + {section.charAt(0).toUpperCase() + section.slice(1)} ); })} + + + + {t("community.plugins.hidden")} + +
- {filteredPlugins.length === 0 ? ( - - ) : ( - - {filteredPlugins.map((plugin) => ( - -
- - navigate(`/community/tcplugins/${pluginId}`) - } - onOpenHomepage={(url) => window.open(url, "_blank")} - onDelete={requestDelete} - /> -
- - ))} -
- )} + + + {filteredPlugins.length === 0 ? ( + + ) : ( + <> + {listPagination} + + + {currentPageData.map((plugin) => ( + + + navigate(`/community/tcplugins/${pluginId}`) + } + onOpenHomepage={(url) => window.open(url, "_blank")} + onDelete={requestDelete} + /> + + ))} + + + {listPagination} + + )} + { onConfirm={handleConfirmDelete} onCancel={handleCancelDelete} /> - + ); }; diff --git a/src/components/community/pluginlist/hooks/usePluginList.ts b/src/components/community/pluginlist/hooks/usePluginList.ts index 3670fa5c..51202583 100644 --- a/src/components/community/pluginlist/hooks/usePluginList.ts +++ b/src/components/community/pluginlist/hooks/usePluginList.ts @@ -20,6 +20,12 @@ export const usePluginList = () => { const [file, setFile] = useState(null); const [uploading, setUploading] = useState(false); const [isUploadModalOpen, setIsUploadModalOpen] = useState(false); + const [hiddenOnly, setHiddenOnly] = useState(false); + + const hiddenPluginCount = useMemo( + () => plugins.filter((plugin) => plugin.hideInNav).length, + [plugins], + ); const allSections = useMemo( () => @@ -49,14 +55,18 @@ export const usePluginList = () => { [plugins, t], ); - const filteredPlugins = useMemo( - () => - (plugins as TeddyCloudPlugin[]).filter((plugin) => { - const section = plugin.teddyCloudSection || t("community.plugins.filter.unknown"); - return activeSectionFilters.includes(section); - }), - [plugins, activeSectionFilters, t], - ); + const filteredPlugins = useMemo(() => { + return plugins.filter((plugin) => { + const matchesSection = + activeSectionFilters.length === 0 || + (plugin.teddyCloudSection && + activeSectionFilters.includes(plugin.teddyCloudSection)); + + const matchesHidden = !hiddenOnly || plugin.hideInNav; + + return matchesSection && matchesHidden; + }); + }, [plugins, activeSectionFilters, hiddenOnly]); const toggleSectionFilter = (section: string, checked: boolean) => { setActiveSectionFilters((prev) => @@ -166,6 +176,9 @@ export const usePluginList = () => { filteredPlugins, allSections, activeSectionFilters, + hiddenOnly, + setHiddenOnly, + hiddenPluginCount, pluginCountBySection, // filters diff --git a/src/components/community/pluginlist/modals/PluginHelpModal.tsx b/src/components/community/pluginlist/modals/PluginHelpModal.tsx index ef7230ef..5472aade 100644 --- a/src/components/community/pluginlist/modals/PluginHelpModal.tsx +++ b/src/components/community/pluginlist/modals/PluginHelpModal.tsx @@ -22,6 +22,7 @@ export const PluginHelpModal: React.FC = ({ open, onClose ["pluginHomepage", t("community.plugins.help.fields.pluginHomepage")], ["teddyCloudSection", t("community.plugins.help.fields.teddyCloudSection")], ["icon", t("community.plugins.help.fields.icon")], + ["hideInNav", t("community.plugins.help.fields.hideInNav")], ]; return ( @@ -65,7 +66,8 @@ export const PluginHelpModal: React.FC = ({ open, onClose "version": "Version of the plugin", "pluginHomepage": "Homepage of the plugin", "teddyCloudSection": "tonies", - "icon": "TrophyOutlined" + "icon": "TrophyOutlined", + "hideInNav": false }`} /> diff --git a/src/components/community/pluginlist/pagination/PluginPagination.tsx b/src/components/community/pluginlist/pagination/PluginPagination.tsx new file mode 100644 index 00000000..86302398 --- /dev/null +++ b/src/components/community/pluginlist/pagination/PluginPagination.tsx @@ -0,0 +1,42 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { Button, Pagination } from "antd"; + +interface PluginPaginationProps { + currentPage: number; + onChange: (current: number, size: number) => void; + total: number; + pageSize: number; + additionalButtonOnClick: () => void; +} + +const PluginPagination: React.FC = ({ + currentPage, + onChange, + total, + pageSize, + additionalButtonOnClick, +}) => { + const { t } = useTranslation(); + + return ( + <> + + + + ); +}; + +export default PluginPagination; diff --git a/src/components/home/HomeSubNav.tsx b/src/components/home/HomeSubNav.tsx index 230806cc..4b77c4d7 100644 --- a/src/components/home/HomeSubNav.tsx +++ b/src/components/home/HomeSubNav.tsx @@ -25,7 +25,7 @@ export const HomeSubNav = () => { }, [currentLanguage]); const pluginItems = plugins - .filter((p) => p.teddyCloudSection === TeddyCloudSection.Home) + .filter((p) => p.teddyCloudSection === TeddyCloudSection.Home && !p.hideInNav) .map((plugin) => ({ key: `plugin-${plugin.pluginId}`, label: ( diff --git a/src/components/settings/SettingsSubNav.tsx b/src/components/settings/SettingsSubNav.tsx index 76d82cdb..87cc81ea 100644 --- a/src/components/settings/SettingsSubNav.tsx +++ b/src/components/settings/SettingsSubNav.tsx @@ -48,7 +48,7 @@ export const SettingsSubNav = () => { }, [currentLanguage]); const pluginItems = plugins - .filter((p) => p.teddyCloudSection === TeddyCloudSection.Settings) + .filter((p) => p.teddyCloudSection === TeddyCloudSection.Settings && !p.hideInNav) .map((plugin) => ({ key: `plugin-${plugin.pluginId}`, label: ( diff --git a/src/components/tonieboxes/TonieboxesSubNav.tsx b/src/components/tonieboxes/TonieboxesSubNav.tsx index 7147b8c4..801395f6 100644 --- a/src/components/tonieboxes/TonieboxesSubNav.tsx +++ b/src/components/tonieboxes/TonieboxesSubNav.tsx @@ -28,7 +28,7 @@ export const TonieboxesSubNav = () => { }, [openKeys, currentLanguage]); const pluginItems = plugins - .filter((p) => p.teddyCloudSection === TeddyCloudSection.Tonieboxes) + .filter((p) => p.teddyCloudSection === TeddyCloudSection.Tonieboxes && !p.hideInNav) .map((plugin) => ({ key: `plugin-${plugin.pluginId}`, label: ( diff --git a/src/components/tonieboxes/boxsetup/cc3200/CC3200FlashingGuide.tsx b/src/components/tonieboxes/boxsetup/cc3200/CC3200FlashingGuide.tsx index bc80f3cf..354fbb1a 100644 --- a/src/components/tonieboxes/boxsetup/cc3200/CC3200FlashingGuide.tsx +++ b/src/components/tonieboxes/boxsetup/cc3200/CC3200FlashingGuide.tsx @@ -1,13 +1,7 @@ import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { Button, Divider, Steps, Typography } from "antd"; -import { - CheckSquareOutlined, - CodeOutlined, - EyeOutlined, - LeftOutlined, - RightOutlined, -} from "@ant-design/icons"; +import { CheckSquareOutlined, EyeOutlined, LeftOutlined, RightOutlined } from "@ant-design/icons"; import { BoxVersionsEnum } from "../../../../types/tonieboxTypes"; @@ -52,30 +46,6 @@ export const CC3200BoxFlashingGuide: React.FC = () => { { title: t("tonieboxes.cc3200BoxFlashing.applyingPatches") }, ]; - interface AltUrlCustomPatchButtonProps { - hostname: string; - disabled: boolean; - } - - const AltUrlCustomPatchButton: React.FC = ({ - hostname, - disabled, - }) => { - const { t } = useTranslation(); - const { createPatch } = useAltUrlCustomPatch(hostname); - - return ( - - ); - }; - const sanitizeHostname = (input: string) => input.replace(/[^a-zA-Z0-9-.]/g, "").trim(); const handleHostnameChange = (value: string) => { @@ -101,6 +71,7 @@ export const CC3200BoxFlashingGuide: React.FC = () => { const portNumber = Number(port); const isPortInvalid = !isValidAltPort(portNumber) || portNumber === 443; const { createPatch: createPortPatch } = useAltPortCustomPatch(portNumber); + const { createPatch: createHostNamePatch } = useAltUrlCustomPatch(hostname); const prev = () => setCurrentStep((s) => Math.max(0, s - 1)); const next = () => setCurrentStep((s) => Math.min(steps.length - 1, s + 1)); @@ -132,6 +103,8 @@ export const CC3200BoxFlashingGuide: React.FC = () => { hostname={hostname} warningTextHostname={warningTextHostname} onHostnameChange={handleHostnameChange} + onCreateHostNamePatch={createHostNamePatch} + isHostnameInvalid={isHostnameInvalid} port={port} warningTextPort={warningTextPort} onPortChange={handlePortChange} @@ -257,12 +230,7 @@ export const CC3200BoxFlashingGuide: React.FC = () => { }} > {previousButton} -
- -
+
+ {warningTextHostname && (

{warningTextHostname}

diff --git a/src/components/tonies/ToniesSubNav.tsx b/src/components/tonies/ToniesSubNav.tsx index ca9adbb0..1a5c0c68 100644 --- a/src/components/tonies/ToniesSubNav.tsx +++ b/src/components/tonies/ToniesSubNav.tsx @@ -33,7 +33,7 @@ export const ToniesSubNav = () => { }, [currentLanguage]); const pluginItems = plugins - .filter((p) => p.teddyCloudSection === TeddyCloudSection.Tonies) + .filter((p) => p.teddyCloudSection === TeddyCloudSection.Tonies && !p.hideInNav) .map((plugin) => ({ key: `plugin-${plugin.pluginId}`, label: ( diff --git a/src/components/tonies/common/modals/SelectImageModal.tsx b/src/components/tonies/common/modals/SelectImageModal.tsx index 1f9d988a..7e65f2ec 100644 --- a/src/components/tonies/common/modals/SelectImageModal.tsx +++ b/src/components/tonies/common/modals/SelectImageModal.tsx @@ -250,7 +250,7 @@ export const SelectImageModal: React.FC = ({ const uploadDraggerProps: UploadProps = { name: "file", multiple: true, - accept: IMAGE_EXTENSIONS.map((ext) => `.${ext}`).join(","), + accept: IMAGE_EXTENSIONS.map((ext) => `${ext}`).join(","), fileList: uploadFileList, customRequest: handleUploadRequest, onChange: (info) => { diff --git a/src/components/tonies/common/modals/TafTrackDownloadModal.tsx b/src/components/tonies/common/modals/TafTrackDownloadModal.tsx index 7c0534b6..b5d7a649 100644 --- a/src/components/tonies/common/modals/TafTrackDownloadModal.tsx +++ b/src/components/tonies/common/modals/TafTrackDownloadModal.tsx @@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Button, Checkbox, Flex, Modal, Table, theme } from "antd"; import type { ColumnsType } from "antd/es/table"; -import { DownloadOutlined, PlayCircleOutlined } from "@ant-design/icons"; +import { DownloadOutlined, LoadingOutlined, PlayCircleOutlined } from "@ant-design/icons"; import styled from "styled-components"; import { @@ -46,12 +46,13 @@ const formatClock = (seconds?: number): string => { export const buildTafDownloadTracks = ( titles: string[] | undefined, trackSeconds: number[] | undefined, + lengthSeconds: number | undefined, ): TafDownloadTrack[] => { const count = Math.max(titles?.length || 0, trackSeconds?.length || 0); const tracks: TafDownloadTrack[] = []; for (let i = 0; i < count; i++) { const start = trackSeconds?.[i]; - const next = trackSeconds?.[i + 1]; + const next = trackSeconds?.[i + 1] || lengthSeconds; tracks.push({ number: i + 1, title: titles?.[i]?.trim() || "", @@ -72,19 +73,35 @@ export const TafDownloadPanel: React.FC = ({ const { token } = useToken(); const canSplit = tracks.length > 1; const allKeys = useMemo(() => tracks.map((track) => track.number), [tracks]); + const selectionScope = `${contentUrl}|${allKeys.join(",")}`; const [selectedKeys, setSelectedKeys] = useState([]); + const [downloadingWholeFile, setDownloadingWholeFile] = useState(false); + const [downloadingSelected, setDownloadingSelected] = useState(false); useEffect(() => { setSelectedKeys([]); - }, [allKeys]); + }, [selectionScope]); const downloadBase = sanitizeDownloadName(baseFilename || "download"); - const downloadWholeFile = () => { - triggerBrowserDownload(buildTafDownloadUrl(contentUrl), `${downloadBase}.ogg`); + const downloadWholeFile = async () => { + if (downloadingWholeFile) { + return; + } + + setDownloadingWholeFile(true); + try { + await triggerBrowserDownload(buildTafDownloadUrl(contentUrl), `${downloadBase}.ogg`); + } finally { + setDownloadingWholeFile(false); + } }; - const downloadSelected = () => { + const downloadSelected = async () => { + if (downloadingSelected) { + return; + } + const selected = tracks.filter((track) => selectedKeys.includes(track.number)); if (selected.length === 0) { return; @@ -95,12 +112,18 @@ export const TafDownloadPanel: React.FC = ({ return `${padTrackNumber(track.number)} ${sanitizeDownloadName(title)}.ogg`; }); const filename = selected.length === 1 ? entries[0] : `${downloadBase}.zip`; - triggerBrowserDownload( - buildTafDownloadUrl(contentUrl, { - tracks: selected.map((track) => track.number), - }), - filename, - ); + + setDownloadingSelected(true); + try { + await triggerBrowserDownload( + buildTafDownloadUrl(contentUrl, { + tracks: selected.map((track) => track.number), + }), + filename, + ); + } finally { + setDownloadingSelected(false); + } }; if (!canSplit) { @@ -120,7 +143,8 @@ export const TafDownloadPanel: React.FC = ({ ))} +
+ ) : isBackgroundDownloading ? ( + ) : ( ) diff --git a/src/components/tonies/toniecard/hooks/useAssignSiblingEpisode.ts b/src/components/tonies/toniecard/hooks/useAssignSiblingEpisode.ts index 44b049a6..f7dffdb6 100644 --- a/src/components/tonies/toniecard/hooks/useAssignSiblingEpisode.ts +++ b/src/components/tonies/toniecard/hooks/useAssignSiblingEpisode.ts @@ -3,14 +3,14 @@ import { useEffect, useState } from "react"; import { TeddyCloudApi } from "../../../../api"; import { defaultAPIConfig } from "../../../../config/defaultApiConfig"; import { Record } from "../../../../types/fileBrowserTypes"; -import { ValidateStatus } from "../TonieCardTypes"; +import { ValidationState } from "../../../../types/tonieCardTypes"; const api = new TeddyCloudApi(defaultAPIConfig()); type UseAssignSiblingEpisodeParams = { source: string; onSelectedSourceChange: (value: string) => void; - setInputValidationSource: (state: { validateStatus: ValidateStatus; help: string }) => void; + setInputValidationSource: (value: ValidationState) => void; }; /** diff --git a/src/components/tonies/toniecard/hooks/useTonieCardActions.ts b/src/components/tonies/toniecard/hooks/useTonieCardActions.ts index 1db960ca..dc507216 100644 --- a/src/components/tonies/toniecard/hooks/useTonieCardActions.ts +++ b/src/components/tonies/toniecard/hooks/useTonieCardActions.ts @@ -1,3 +1,4 @@ +import { useState } from "react"; import { defaultAPIConfig } from "../../../../config/defaultApiConfig"; import { TeddyCloudApi } from "../../../../api"; import { NotificationTypeEnum } from "../../../../types/teddyCloudNotificationTypes"; @@ -31,6 +32,8 @@ export const useTonieCardActions = ({ closeLoadingNotification, fetchUpdatedTonieCard, }: UseTonieCardActionsParams) => { + const [isBackgroundDownloading, setIsBackgroundDownloading] = useState(false); + const notifySuccess = (message: string, description: string) => { addNotification(NotificationTypeEnum.Success, message, description, t("tonies.title")); }; @@ -122,9 +125,10 @@ export const useTonieCardActions = ({ const handleBackgroundDownload = async () => { const path = tonieCard.downloadTriggerUrl; - if (!path) return; + if (!path || isBackgroundDownloading) return; const key = "loading" + tonieCard.ruid; + setIsBackgroundDownloading(true); try { addLoadingNotification( @@ -159,6 +163,8 @@ export const useTonieCardActions = ({ }).replace(' "" ', ""), error, ); + } finally { + setIsBackgroundDownloading(false); } }; @@ -166,5 +172,6 @@ export const useTonieCardActions = ({ handleLiveClick, handleNoCloudClick, handleBackgroundDownload, + isBackgroundDownloading, }; }; diff --git a/src/components/tonies/toniecard/hooks/useTonieCardSaveFlow.ts b/src/components/tonies/toniecard/hooks/useTonieCardSaveFlow.ts index c40a092d..eea6ed26 100644 --- a/src/components/tonies/toniecard/hooks/useTonieCardSaveFlow.ts +++ b/src/components/tonies/toniecard/hooks/useTonieCardSaveFlow.ts @@ -4,7 +4,7 @@ import { NotificationTypeEnum } from "../../../../types/teddyCloudNotificationTy import { TonieCardProps } from "../../../../types/tonieTypes"; import { toModelKey } from "../../hooks/useCustomModelKeys"; import { showAudioModelMismatchConfirm } from "../modals/AudioModelMismatchConfirmModal"; -import { ValidationState } from "../TonieCardTypes"; +import { ValidationState } from "../../../../types/tonieCardTypes"; const api = new TeddyCloudApi(defaultAPIConfig()); diff --git a/src/components/tonies/toniecard/hooks/useTooltipInfoByModel.ts b/src/components/tonies/toniecard/hooks/useTooltipInfoByModel.ts index 0d2b68ac..a417d472 100644 --- a/src/components/tonies/toniecard/hooks/useTooltipInfoByModel.ts +++ b/src/components/tonies/toniecard/hooks/useTooltipInfoByModel.ts @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; import { defaultAPIConfig } from "../../../../config/defaultApiConfig"; import { TeddyCloudApi } from "../../../../api"; import { toModelKey } from "../../hooks/useCustomModelKeys"; -import { TooltipInfo } from "../TonieCardTypes"; +import { TooltipInfo } from "../../../../types/tonieCardTypes"; const api = new TeddyCloudApi(defaultAPIConfig()); diff --git a/src/components/tonies/toniecard/utils/tooltipInfo.ts b/src/components/tonies/toniecard/utils/tooltipInfo.ts index af0595e3..681504cb 100644 --- a/src/components/tonies/toniecard/utils/tooltipInfo.ts +++ b/src/components/tonies/toniecard/utils/tooltipInfo.ts @@ -1,5 +1,5 @@ import { toModelKey } from "../../hooks/useCustomModelKeys"; -import { TooltipInfo } from "../TonieCardTypes"; +import { TooltipInfo } from "../../../../types/tonieCardTypes"; export const normalized = (v?: string) => (v || "").trim().toLowerCase(); diff --git a/src/components/tonies/tonieslist/ToniesList.tsx b/src/components/tonies/tonieslist/ToniesList.tsx index d84d3947..7ec69acd 100644 --- a/src/components/tonies/tonieslist/ToniesList.tsx +++ b/src/components/tonies/tonieslist/ToniesList.tsx @@ -459,7 +459,9 @@ export const ToniesList: React.FC<{ const listPagination = (
{!paginationEnabled ? ( - + ) : ( void; };