Compare commits
6 commits
29d96f8451
...
d19d369825
Author | SHA1 | Date | |
---|---|---|---|
d19d369825 | |||
90fe1be9ae | |||
86885d02d6 | |||
26cc8a8587 | |||
26be01e058 | |||
d538a7a2b0 |
23 changed files with 3257 additions and 3633 deletions
170
bun.lock
170
bun.lock
|
@ -9,19 +9,20 @@
|
|||
"@sveltejs/adapter-auto": "^3.3.1",
|
||||
"@sveltejs/adapter-static": "^3.0.8",
|
||||
"@sveltejs/kit": "^2.17.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
||||
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
||||
"@tabler/icons-webfont": "^2.47.0",
|
||||
"debug": "^4.4.0",
|
||||
"i18next": "^23.16.8",
|
||||
"lodash": "^4.17.21",
|
||||
"normalize.css": "^8.0.1",
|
||||
"svelte": "^4.2.19",
|
||||
"svelte-i18next": "^2.2.2",
|
||||
"svelte": "^5.0.0",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.7.3",
|
||||
"vite": "^5.4.14",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@inlang/paraglide-js": "^2.0.0",
|
||||
"@inlang/plugin-m-function-matcher": "^2.1.0",
|
||||
"@inlang/plugin-message-format": "^4.0.0",
|
||||
"@tsconfig/svelte": "^5.0.4",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/eslint": "8.56.0",
|
||||
|
@ -37,19 +38,16 @@
|
|||
"prettier": "^3.5.0",
|
||||
"prettier-plugin-svelte": "^3.3.3",
|
||||
"puppeteer": "^22.15.0",
|
||||
"svelte-check": "^3.8.6",
|
||||
"svelte-check": "^4.0.0",
|
||||
"wait-on": "^7.2.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"esbuild",
|
||||
"svelte-preprocess",
|
||||
"puppeteer",
|
||||
],
|
||||
"packages": {
|
||||
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
||||
|
||||
"@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
|
||||
|
||||
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="],
|
||||
|
@ -124,8 +122,20 @@
|
|||
|
||||
"@humanwhocodes/object-schema": ["@humanwhocodes/object-schema@2.0.3", "", {}, "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA=="],
|
||||
|
||||
"@inlang/paraglide-js": ["@inlang/paraglide-js@2.4.0", "", { "dependencies": { "@inlang/recommend-sherlock": "0.2.1", "@inlang/sdk": "2.4.9", "commander": "11.1.0", "consola": "3.4.0", "json5": "2.2.3", "unplugin": "^2.1.2", "urlpattern-polyfill": "^10.0.0" }, "bin": { "paraglide-js": "bin/run.js" } }, "sha512-T/m9uoev574/1JrhCnPcgK1xnAwkVMgaDev4LFthnmID8ubX2xjboSGO3IztwXWwO0aJoT1UJr89JCwjbwgnJQ=="],
|
||||
|
||||
"@inlang/plugin-m-function-matcher": ["@inlang/plugin-m-function-matcher@2.1.0", "", { "dependencies": { "@inlang/sdk": "2.4.9" } }, "sha512-IAbG7rOl+rlTiZY7qj92we6lmII693lVthPtY9bFDkZ/Ig7FPSpae/TfLzqjf2KcR1nDdx1zRpSo6roDPeM85g=="],
|
||||
|
||||
"@inlang/plugin-message-format": ["@inlang/plugin-message-format@4.0.0", "", { "dependencies": { "flat": "^6.0.1" } }, "sha512-zNpLxLTt+bDd3JLXj1ONzo+Q6AOzz2MfcgGo8XB6/bweGhFIndK3GU/q0iU4o7VI4KS1+OHNLpKwFcrAifwERQ=="],
|
||||
|
||||
"@inlang/recommend-sherlock": ["@inlang/recommend-sherlock@0.2.1", "", { "dependencies": { "comment-json": "^4.2.3" } }, "sha512-ckv8HvHy/iTqaVAEKrr+gnl+p3XFNwe5D2+6w6wJk2ORV2XkcRkKOJ/XsTUJbPSiyi4PI+p+T3bqbmNx/rDUlg=="],
|
||||
|
||||
"@inlang/sdk": ["@inlang/sdk@2.4.9", "", { "dependencies": { "@lix-js/sdk": "0.4.7", "@sinclair/typebox": "^0.31.17", "kysely": "^0.27.4", "sqlite-wasm-kysely": "0.3.0", "uuid": "^10.0.0" } }, "sha512-cvz/C1rF5WBxzHbEoiBoI6Sz6q6M+TdxfWkEGBYTD77opY8i8WN01prUWXEM87GPF4SZcyIySez9U0Ccm12oFQ=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
|
||||
|
||||
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
||||
|
||||
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||
|
||||
"@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="],
|
||||
|
@ -134,6 +144,10 @@
|
|||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
||||
|
||||
"@lix-js/sdk": ["@lix-js/sdk@0.4.7", "", { "dependencies": { "@lix-js/server-protocol-schema": "0.1.1", "dedent": "1.5.1", "human-id": "^4.1.1", "js-sha256": "^0.11.0", "kysely": "^0.27.4", "sqlite-wasm-kysely": "0.3.0", "uuid": "^10.0.0" } }, "sha512-pRbW+joG12L0ULfMiWYosIW0plmW4AsUdiPCp+Z8rAsElJ+wJ6in58zhD3UwUcd4BNcpldEGjg6PdA7e0RgsDQ=="],
|
||||
|
||||
"@lix-js/server-protocol-schema": ["@lix-js/server-protocol-schema@0.1.1", "", {}, "sha512-jBeALB6prAbtr5q4vTuxnRZZv1M2rKe8iNqRQhFJ4Tv7150unEa0vKyz0hs8Gl3fUGsWaNJBh3J8++fpbrpRBQ=="],
|
||||
|
||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||
|
||||
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
||||
|
@ -188,15 +202,21 @@
|
|||
|
||||
"@sideway/pinpoint": ["@sideway/pinpoint@2.0.0", "", {}, "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="],
|
||||
|
||||
"@sinclair/typebox": ["@sinclair/typebox@0.31.28", "", {}, "sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ=="],
|
||||
|
||||
"@sqlite.org/sqlite-wasm": ["@sqlite.org/sqlite-wasm@3.48.0-build4", "", { "bin": { "sqlite-wasm": "bin/index.js" } }, "sha512-hI6twvUkzOmyGZhQMza1gpfqErZxXRw6JEsiVjUbo7tFanVD+8Oil0Ih3l2nGzHdxPI41zFmfUQG7GHqhciKZQ=="],
|
||||
|
||||
"@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.6", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ=="],
|
||||
|
||||
"@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@3.3.1", "", { "dependencies": { "import-meta-resolve": "^4.1.0" }, "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-5Sc7WAxYdL6q9j/+D0jJKjGREGlfIevDyHSQ2eNETHcB1TKlQWHcAo8AS8H1QdjNvSXpvOwNjykDUHPEAyGgdQ=="],
|
||||
|
||||
"@sveltejs/adapter-static": ["@sveltejs/adapter-static@3.0.8", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg=="],
|
||||
|
||||
"@sveltejs/kit": ["@sveltejs/kit@2.17.1", "", { "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.6.0", "devalue": "^5.1.0", "esm-env": "^1.2.2", "import-meta-resolve": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0" }, "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-CpoGSLqE2MCmcQwA2CWJvOsZ9vW+p/1H3itrFykdgajUNAEyQPbsaSn7fZb6PLHQwe+07njxje9ss0fjZoCAyw=="],
|
||||
|
||||
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@3.1.2", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", "debug": "^4.3.4", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.10", "svelte-hmr": "^0.16.0", "vitefu": "^0.2.5" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.0" } }, "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA=="],
|
||||
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@4.0.4", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0", "debug": "^4.3.7", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.12", "vitefu": "^1.0.3" }, "peerDependencies": { "svelte": "^5.0.0-next.96 || ^5.0.0", "vite": "^5.0.0" } }, "sha512-0ba1RQ/PHen5FGpdSrW7Y3fAMQjrXantECALeOiOdBdzR5+5vPP6HVZRLmZaQL+W8m++o+haIAKq5qT+MiZ7VA=="],
|
||||
|
||||
"@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@2.1.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^3.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.0" } }, "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg=="],
|
||||
"@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@3.0.1", "", { "dependencies": { "debug": "^4.3.7" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^4.0.0-next.0||^4.0.0", "svelte": "^5.0.0-next.96 || ^5.0.0", "vite": "^5.0.0" } }, "sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ=="],
|
||||
|
||||
"@tabler/icons": ["@tabler/icons@2.47.0", "", {}, "sha512-4w5evLh+7FUUiA1GucvGj2ReX2TvOjEr4ejXdwL/bsjoSkof6r1gQmzqI+VHrE2CpJpB3al7bCTulOkFa/RcyA=="],
|
||||
|
||||
|
@ -222,8 +242,6 @@
|
|||
|
||||
"@types/node": ["@types/node@22.13.0", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA=="],
|
||||
|
||||
"@types/pug": ["@types/pug@2.0.10", "", {}, "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA=="],
|
||||
|
||||
"@types/semver": ["@types/semver@7.5.8", "", {}, "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ=="],
|
||||
|
||||
"@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="],
|
||||
|
@ -258,19 +276,19 @@
|
|||
|
||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
|
||||
|
||||
"array-timsort": ["array-timsort@1.0.3", "", {}, "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ=="],
|
||||
|
||||
"array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="],
|
||||
|
||||
"ast-types": ["ast-types@0.13.4", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w=="],
|
||||
|
||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||
|
||||
"axios": ["axios@1.7.9", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw=="],
|
||||
"axios": ["axios@1.12.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw=="],
|
||||
|
||||
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
||||
|
||||
|
@ -292,27 +310,27 @@
|
|||
|
||||
"basic-ftp": ["basic-ftp@5.0.5", "", {}, "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg=="],
|
||||
|
||||
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
||||
|
||||
"brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
||||
|
||||
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
||||
|
||||
"buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
||||
|
||||
"buffer-crc32": ["buffer-crc32@1.0.0", "", {}, "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w=="],
|
||||
"buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="],
|
||||
|
||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||
|
||||
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
||||
|
||||
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||
|
||||
"chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
||||
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||
|
||||
"chromium-bidi": ["chromium-bidi@0.6.3", "", { "dependencies": { "mitt": "3.0.1", "urlpattern-polyfill": "10.0.0", "zod": "3.23.8" }, "peerDependencies": { "devtools-protocol": "*" } }, "sha512-qXlsCmpCZJAnoTYI83Iu6EdYQpMYdVkCfq08KDh2pmlVqK5t5IA9mGs4/LwCwp4fqisSOMXZxP3HIh8w8aRn0A=="],
|
||||
|
||||
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
||||
|
||||
"code-red": ["code-red@1.0.4", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15", "@types/estree": "^1.0.1", "acorn": "^8.10.0", "estree-walker": "^3.0.3", "periscopic": "^3.1.0" } }, "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw=="],
|
||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
||||
|
@ -322,18 +340,22 @@
|
|||
|
||||
"commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
|
||||
|
||||
"comment-json": ["comment-json@4.2.5", "", { "dependencies": { "array-timsort": "^1.0.3", "core-util-is": "^1.0.3", "esprima": "^4.0.1", "has-own-prop": "^2.0.0", "repeat-string": "^1.6.1" } }, "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw=="],
|
||||
|
||||
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
||||
|
||||
"concurrently": ["concurrently@8.2.2", "", { "dependencies": { "chalk": "^4.1.2", "date-fns": "^2.30.0", "lodash": "^4.17.21", "rxjs": "^7.8.1", "shell-quote": "^1.8.1", "spawn-command": "0.0.2", "supports-color": "^8.1.1", "tree-kill": "^1.2.2", "yargs": "^17.7.2" }, "bin": { "conc": "dist/bin/concurrently.js", "concurrently": "dist/bin/concurrently.js" } }, "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg=="],
|
||||
|
||||
"consola": ["consola@3.4.0", "", {}, "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA=="],
|
||||
|
||||
"cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="],
|
||||
|
||||
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
|
||||
|
||||
"cosmiconfig": ["cosmiconfig@9.0.0", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"css-tree": ["css-tree@2.3.1", "", { "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" } }, "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw=="],
|
||||
|
||||
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
||||
|
||||
"data-uri-to-buffer": ["data-uri-to-buffer@6.0.2", "", {}, "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw=="],
|
||||
|
@ -342,6 +364,8 @@
|
|||
|
||||
"debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
||||
|
||||
"dedent": ["dedent@1.5.1", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg=="],
|
||||
|
||||
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
||||
|
||||
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
|
||||
|
@ -350,8 +374,6 @@
|
|||
|
||||
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
||||
|
||||
"detect-indent": ["detect-indent@6.1.0", "", {}, "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA=="],
|
||||
|
||||
"devalue": ["devalue@5.1.1", "", {}, "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw=="],
|
||||
|
||||
"devtools-protocol": ["devtools-protocol@0.0.1312386", "", {}, "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA=="],
|
||||
|
@ -360,6 +382,8 @@
|
|||
|
||||
"doctrine": ["doctrine@3.0.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"end-of-stream": ["end-of-stream@1.4.4", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q=="],
|
||||
|
@ -368,7 +392,13 @@
|
|||
|
||||
"error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="],
|
||||
|
||||
"es6-promise": ["es6-promise@3.3.1", "", {}, "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg=="],
|
||||
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
||||
|
||||
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
||||
|
||||
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
||||
|
||||
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
|
||||
|
||||
"esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
|
||||
|
||||
|
@ -398,12 +428,12 @@
|
|||
|
||||
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
|
||||
|
||||
"esrap": ["esrap@2.1.0", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA=="],
|
||||
|
||||
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
|
||||
|
||||
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
|
||||
|
||||
"estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
|
||||
|
||||
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
|
||||
|
||||
"extract-zip": ["extract-zip@2.0.1", "", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="],
|
||||
|
@ -422,26 +452,36 @@
|
|||
|
||||
"fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="],
|
||||
|
||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
|
||||
"file-entry-cache": ["file-entry-cache@6.0.1", "", { "dependencies": { "flat-cache": "^3.0.4" } }, "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg=="],
|
||||
|
||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||
|
||||
"find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
|
||||
|
||||
"flat": ["flat@6.0.1", "", { "bin": { "flat": "cli.js" } }, "sha512-/3FfIa8mbrg3xE7+wAhWeV+bd7L2Mof+xtZb5dRDKZ+wDvYJK4WDYeIOuOhre5Yv5aQObZrlbRmk3RTSiuQBtw=="],
|
||||
|
||||
"flat-cache": ["flat-cache@3.2.0", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" } }, "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw=="],
|
||||
|
||||
"flatted": ["flatted@3.3.2", "", {}, "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA=="],
|
||||
|
||||
"follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="],
|
||||
|
||||
"form-data": ["form-data@4.0.1", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw=="],
|
||||
"form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="],
|
||||
|
||||
"fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
|
||||
|
||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
|
||||
|
||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||
|
||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||
|
||||
"get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="],
|
||||
|
||||
"get-uri": ["get-uri@6.0.4", "", { "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", "debug": "^4.3.4" } }, "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ=="],
|
||||
|
@ -454,17 +494,25 @@
|
|||
|
||||
"globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="],
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||
|
||||
"graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
|
||||
|
||||
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||
|
||||
"has-own-prop": ["has-own-prop@2.0.0", "", {}, "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ=="],
|
||||
|
||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||
|
||||
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
|
||||
|
||||
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"i18next": ["i18next@23.16.8", "", { "dependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg=="],
|
||||
"human-id": ["human-id@4.1.1", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg=="],
|
||||
|
||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||
|
||||
|
@ -484,8 +532,6 @@
|
|||
|
||||
"is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
|
||||
|
||||
"is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
|
||||
|
||||
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
||||
|
||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||
|
@ -502,6 +548,8 @@
|
|||
|
||||
"joi": ["joi@17.13.3", "", { "dependencies": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", "@sideway/address": "^4.1.5", "@sideway/formula": "^3.0.1", "@sideway/pinpoint": "^2.0.0" } }, "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA=="],
|
||||
|
||||
"js-sha256": ["js-sha256@0.11.1", "", {}, "sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg=="],
|
||||
|
||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
||||
|
@ -516,12 +564,16 @@
|
|||
|
||||
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
|
||||
|
||||
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
||||
|
||||
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
||||
|
||||
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
||||
|
||||
"known-css-properties": ["known-css-properties@0.35.0", "", {}, "sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A=="],
|
||||
|
||||
"kysely": ["kysely@0.27.6", "", {}, "sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ=="],
|
||||
|
||||
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
||||
|
||||
"lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="],
|
||||
|
@ -540,7 +592,7 @@
|
|||
|
||||
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
|
||||
|
||||
"mdn-data": ["mdn-data@2.0.30", "", {}, "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="],
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
|
||||
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||
|
||||
|
@ -550,16 +602,12 @@
|
|||
|
||||
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
|
||||
"min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="],
|
||||
|
||||
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||
|
||||
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
||||
|
||||
"mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
|
||||
|
||||
"mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
|
||||
|
||||
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
|
||||
|
||||
"mrmime": ["mrmime@2.0.0", "", {}, "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw=="],
|
||||
|
@ -574,8 +622,6 @@
|
|||
|
||||
"node-wav": ["node-wav@0.0.2", "", {}, "sha512-M6Rm/bbG6De/gKGxOpeOobx/dnGuP0dz40adqx38boqHhlWssBJZgLCPBNtb9NkrmnKYiV04xELq+R6PFOnoLA=="],
|
||||
|
||||
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
||||
|
||||
"normalize.css": ["normalize.css@8.0.1", "", {}, "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="],
|
||||
|
||||
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||
|
@ -604,11 +650,9 @@
|
|||
|
||||
"pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="],
|
||||
|
||||
"periscopic": ["periscopic@3.1.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^3.0.0", "is-reference": "^3.0.0" } }, "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"postcss": ["postcss@8.5.1", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ=="],
|
||||
|
||||
|
@ -642,10 +686,12 @@
|
|||
|
||||
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||
|
||||
"readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
||||
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||
|
||||
"regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="],
|
||||
|
||||
"repeat-string": ["repeat-string@1.6.1", "", {}, "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w=="],
|
||||
|
||||
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
|
||||
|
||||
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||
|
@ -662,8 +708,6 @@
|
|||
|
||||
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
|
||||
|
||||
"sander": ["sander@0.5.1", "", { "dependencies": { "es6-promise": "^3.1.2", "graceful-fs": "^4.1.3", "mkdirp": "^0.5.1", "rimraf": "^2.5.2" } }, "sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA=="],
|
||||
|
||||
"semver": ["semver@7.7.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ=="],
|
||||
|
||||
"set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="],
|
||||
|
@ -684,8 +728,6 @@
|
|||
|
||||
"socks-proxy-agent": ["socks-proxy-agent@8.0.5", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="],
|
||||
|
||||
"sorcery": ["sorcery@0.11.1", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.14", "buffer-crc32": "^1.0.0", "minimist": "^1.2.0", "sander": "^0.5.0" }, "bin": { "sorcery": "bin/sorcery" } }, "sha512-o7npfeJE6wi6J9l0/5LKshFzZ2rMatRiCDwYeDQaOzqdzRJwALhX7mk/A/ecg6wjMu7wdZbmXfD2S/vpOg0bdQ=="],
|
||||
|
||||
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
@ -694,30 +736,24 @@
|
|||
|
||||
"sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="],
|
||||
|
||||
"sqlite-wasm-kysely": ["sqlite-wasm-kysely@0.3.0", "", { "dependencies": { "@sqlite.org/sqlite-wasm": "^3.48.0-build2" }, "peerDependencies": { "kysely": "*" } }, "sha512-TzjBNv7KwRw6E3pdKdlRyZiTmUIE0UttT/Sl56MVwVARl/u5gp978KepazCJZewFUnlWHz9i3NQd4kOtP/Afdg=="],
|
||||
|
||||
"streamx": ["streamx@2.22.0", "", { "dependencies": { "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" }, "optionalDependencies": { "bare-events": "^2.2.0" } }, "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw=="],
|
||||
|
||||
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="],
|
||||
|
||||
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
||||
|
||||
"supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
|
||||
|
||||
"svelte": ["svelte@4.2.19", "", { "dependencies": { "@ampproject/remapping": "^2.2.1", "@jridgewell/sourcemap-codec": "^1.4.15", "@jridgewell/trace-mapping": "^0.3.18", "@types/estree": "^1.0.1", "acorn": "^8.9.0", "aria-query": "^5.3.0", "axobject-query": "^4.0.0", "code-red": "^1.0.3", "css-tree": "^2.3.1", "estree-walker": "^3.0.3", "is-reference": "^3.0.1", "locate-character": "^3.0.0", "magic-string": "^0.30.4", "periscopic": "^3.1.0" } }, "sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw=="],
|
||||
"svelte": ["svelte@5.39.6", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-bOJXmuwLNaoqPCTWO8mPu/fwxI5peGE5Efe7oo6Cakpz/G60vsnVF6mxbGODaxMUFUKEnjm6XOwHEqOht6cbvw=="],
|
||||
|
||||
"svelte-check": ["svelte-check@3.8.6", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.17", "chokidar": "^3.4.1", "picocolors": "^1.0.0", "sade": "^1.7.4", "svelte-preprocess": "^5.1.3", "typescript": "^5.0.3" }, "peerDependencies": { "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-ij0u4Lw/sOTREP13BdWZjiXD/BlHE6/e2e34XzmVmsp5IN4kVa3PWP65NM32JAgwjZlwBg/+JtiNV1MM8khu0Q=="],
|
||||
"svelte-check": ["svelte-check@4.3.2", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-71udP5w2kaSTcX8iV0hn3o2FWlabQHhJTJLIQrCqMsrcOeDUO2VhCQKKCA8AMVHSPwdxLEWkUWh9OKxns5PD9w=="],
|
||||
|
||||
"svelte-eslint-parser": ["svelte-eslint-parser@0.43.0", "", { "dependencies": { "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "postcss": "^8.4.39", "postcss-scss": "^4.0.9" }, "peerDependencies": { "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA=="],
|
||||
|
||||
"svelte-hmr": ["svelte-hmr@0.16.0", "", { "peerDependencies": { "svelte": "^3.19.0 || ^4.0.0" } }, "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA=="],
|
||||
|
||||
"svelte-i18next": ["svelte-i18next@2.2.2", "", { "peerDependencies": { "i18next": "*", "svelte": "*" } }, "sha512-IpJDZCH5cCgKfHQHgiLmGT4j9HCdg4fqsP3oP2deLu8PxmNj0Ui6khMiDoxAxedAiYEhr0xendv2xqh3Rq+uQQ=="],
|
||||
|
||||
"svelte-preprocess": ["svelte-preprocess@5.1.4", "", { "dependencies": { "@types/pug": "^2.0.6", "detect-indent": "^6.1.0", "magic-string": "^0.30.5", "sorcery": "^0.11.0", "strip-indent": "^3.0.0" }, "peerDependencies": { "@babel/core": "^7.10.2", "coffeescript": "^2.5.1", "less": "^3.11.3 || ^4.0.0", "postcss": "^7 || ^8", "postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", "pug": "^3.0.0", "sass": "^1.26.8", "stylus": "^0.55.0", "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0", "svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0", "typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["@babel/core", "coffeescript", "less", "postcss", "postcss-load-config", "pug", "sass", "stylus", "sugarss", "typescript"] }, "sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA=="],
|
||||
|
||||
"tar-fs": ["tar-fs@3.0.8", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg=="],
|
||||
|
||||
"tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="],
|
||||
|
@ -748,18 +784,24 @@
|
|||
|
||||
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"unplugin": ["unplugin@2.3.10", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw=="],
|
||||
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
|
||||
"urlpattern-polyfill": ["urlpattern-polyfill@10.0.0", "", {}, "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg=="],
|
||||
|
||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||
|
||||
"uuid": ["uuid@10.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="],
|
||||
|
||||
"vite": ["vite@5.4.14", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA=="],
|
||||
|
||||
"vitefu": ["vitefu@0.2.5", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["vite"] }, "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q=="],
|
||||
"vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="],
|
||||
|
||||
"wait-on": ["wait-on@7.2.0", "", { "dependencies": { "axios": "^1.6.1", "joi": "^17.11.0", "lodash": "^4.17.21", "minimist": "^1.2.8", "rxjs": "^7.8.1" }, "bin": { "wait-on": "bin/wait-on" } }, "sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ=="],
|
||||
|
||||
"webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
||||
|
@ -782,19 +824,23 @@
|
|||
|
||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||
|
||||
"zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="],
|
||||
|
||||
"zod": ["zod@3.23.8", "", {}, "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g=="],
|
||||
|
||||
"@inlang/paraglide-js/commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.3", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="],
|
||||
|
||||
"chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||
|
||||
"chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"sander/rimraf": ["rimraf@2.7.1", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="],
|
||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"yauzl/buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="],
|
||||
"unplugin/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
|
||||
"wait-on/rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
}
|
||||
|
|
170
messages/cs.json
Normal file
170
messages/cs.json
Normal file
|
@ -0,0 +1,170 @@
|
|||
{
|
||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||
"search": "Hledat",
|
||||
"tests_audio_description": "Zkontrolujte své stereo kanály nebo prostorový zvukový výstup, ověřte, zda jsou reproduktory ve fázi.",
|
||||
"tests_audio_label": "Zvuk",
|
||||
"tests_av-sync_description": "Zkontrolujte, zda jsou zvuk a video synchronizovány, a změřte zpoždění.",
|
||||
"tests_av-sync_label": "Synchronizace zvuku a videa",
|
||||
"tests_card_description": "Testovací karta pro váš displej nebo projektor, zkontrolujte barvy, rozlišení a geometrii.",
|
||||
"tests_card_label": "Karta",
|
||||
"tests_camera_description": "Zkontrolujte, zda vaše webkamera nebo snímací zařízení funguje, jeho kvalitu obrazu, rozlišení a snímkovou frekvenci. Pořiďte snímek.",
|
||||
"tests_camera_label": "Kamera",
|
||||
"tests_gamepad_description": "Otestujte svůj gamepad, zkontrolujte, zda funguje, všechna tlačítka a joysticky, drift páček, mrtvé zóny a kalibraci.",
|
||||
"tests_gamepad_label": "Gamepad",
|
||||
"tests_keyboard_description": "Zkontrolujte, zda všechny klávesy fungují a jaké kódy kláves odesílají.",
|
||||
"tests_keyboard_label": "Klávesnice",
|
||||
"tests_microphone_description": "Zkontrolujte, zda váš mikrofon funguje, jeho kvalitu, hlasitost a šum.",
|
||||
"tests_microphone_label": "Mikrofon",
|
||||
"tests_mouse_description": "Zkontrolujte, zda vaše myš nebo dotykové zařízení funguje správně, zda existují mrtvé zóny nebo chvění.",
|
||||
"tests_mouse_label": "Myš",
|
||||
"tests_sensors_description": "Zobrazte výstup senzorů vašeho zařízení, např. GPS, akcelerometr, gyroskop, kompas atd.",
|
||||
"tests_sensors_label": "Senzory",
|
||||
"tests_internet_description": "Změřte rychlost a latenci internetu.",
|
||||
"tests_internet_label": "Rychlost internetu",
|
||||
"tests_timer_description": "Zkontrolujte, zda váš časovač s vysokým rozlišením funguje.",
|
||||
"tests_timer_label": "Časovač s vysokým rozlišením",
|
||||
"category_inputs": "Vstupy",
|
||||
"category_outputs": "Výstupy",
|
||||
"category_audio": "Zvuk",
|
||||
"category_video": "Video",
|
||||
"category_control": "Ovládání",
|
||||
"category_misc": "Různé",
|
||||
"noTestsFound": "Nebyly nalezeny žádné testy.",
|
||||
"camera_title": "Test kamery",
|
||||
"camera_device": "Zařízení",
|
||||
"camera_noCameraFound": "Nebyla nalezena žádná kamera",
|
||||
"camera_refresh": "Obnovit",
|
||||
"camera_resolution": "Rozlišení",
|
||||
"camera_frameRate": "Snímková frekvence",
|
||||
"camera_noCameraSelected": "Není vybrána žádná kamera",
|
||||
"camera_takePicture": "Vyfotit",
|
||||
"camera_unflipImage": "Převrátit obrázek zpět",
|
||||
"camera_flipImage": "Převrátit obrázek",
|
||||
"camera_closeSnapshot": "Zavřít snímek",
|
||||
"audio_channel_frontLeft": "Přední levý",
|
||||
"audio_channel_frontCenter": "Přední středový",
|
||||
"audio_channel_frontRight": "Přední pravý",
|
||||
"audio_channel_sideLeft": "Boční levý",
|
||||
"audio_channel_sideRight": "Boční pravý",
|
||||
"audio_channel_rearLeft": "Zadní levý",
|
||||
"audio_channel_rearRight": "Zadní pravý",
|
||||
"audio_channel_lfe": "LFE",
|
||||
"gamepad_title": "Testy gamepadu a joysticku",
|
||||
"gamepad_device": "Zařízení",
|
||||
"gamepad_noGamepadsDetected": "Nebyly detekovány žádné gamepady. (Zkuste stisknout tlačítko)",
|
||||
"gamepad_refresh": "Obnovit",
|
||||
"gamepad_buttons": "Tlačítka",
|
||||
"gamepad_axes": "Osy",
|
||||
"gamepad_history": "Historie",
|
||||
"audio_channelTests": "Testy kanálů",
|
||||
"audio_stereo": "Stereo",
|
||||
"audio_surroundAudio": "Prostorový zvuk",
|
||||
"audio_surround51": "5.1 Prostorový",
|
||||
"audio_surround71": "7.1 Prostorový",
|
||||
"audio_phaseTest": "Test fáze",
|
||||
"audio_frequency": "Frekvence",
|
||||
"audio_inPhase": "Ve fázi",
|
||||
"audio_outOfPhase": "Mimo fázi",
|
||||
"audio_stop": "Zastavit",
|
||||
"screenInfo_screenResolution": "Rozlišení obrazovky",
|
||||
"screenInfo_windowResolution": "Rozlišení okna",
|
||||
"screenInfo_devicePixelRatio": "Poměr pixelů zařízení",
|
||||
"audio_channel_left": "Levý",
|
||||
"audio_channel_center": "Střed",
|
||||
"audio_channel_right": "Pravý",
|
||||
"keyboard_title": "Test klávesnice",
|
||||
"keyboard_instruction": "Stiskněte klávesu na klávesnici pro zobrazení objektu události a kódu klávesy.",
|
||||
"keyboard_pressedKeys": "Stisknuté klávesy:",
|
||||
"timer_title": "Časovač s vysokým rozlišením",
|
||||
"timer_fps": "FPS",
|
||||
"timer_restart": "Restartovat",
|
||||
"audio_stopCycling": "Zastavit cyklování",
|
||||
"audio_cycleThrough": "Procházet",
|
||||
"common_back": "Zpět",
|
||||
"audio_title": "Test zvuku",
|
||||
"avSync_title": "Synchronizace zvuku a videa",
|
||||
"internet_title": "Rychlost internetu",
|
||||
"tests_signal-generator_description": "Generujte sinusové vlny, šum (bílý, růžový, hnědý) a frekvenční přechody. Zahrnuje osciloskop a spektrum.",
|
||||
"tests_signal-generator_label": "Generátor signálu",
|
||||
"signalGen_title": "Generátor signálu",
|
||||
"signalGen_type": "Typ",
|
||||
"signalGen_sine": "Sinus",
|
||||
"signalGen_sweep": "Přechod",
|
||||
"signalGen_noiseWhite": "Bílý šum",
|
||||
"signalGen_noisePink": "Růžový šum",
|
||||
"signalGen_noiseBrown": "Hnědý šum",
|
||||
"signalGen_frequency": "Frekvence",
|
||||
"signalGen_from": "Od",
|
||||
"signalGen_to": "Do",
|
||||
"signalGen_duration": "Doba trvání",
|
||||
"signalGen_gain": "Hlasitost",
|
||||
"signalGen_start": "Start",
|
||||
"signalGen_stop": "Stop",
|
||||
"signalGen_scope": "Osciloskop",
|
||||
"signalGen_spectrum": "Spektrum",
|
||||
"signalGen_loop": "Smyčka",
|
||||
"mic_title": "Test mikrofonu",
|
||||
"mic_startMicrophone": "Spustit mikrofon",
|
||||
"mic_stop": "Zastavit",
|
||||
"mic_monitoringOn": "Monitorování: ZAP",
|
||||
"mic_monitoringOff": "Monitorování: VYP",
|
||||
"mic_gain": "Zesílení",
|
||||
"mic_monitorDelay": "Zpoždění monitoru",
|
||||
"mic_sampleRate": "Vzorkovací frekvence",
|
||||
"mic_inputDevice": "Vstupní zařízení",
|
||||
"mic_volume": "Hlasitost",
|
||||
"mic_recording": "Nahrávání",
|
||||
"mic_startRecording": "Spustit nahrávání",
|
||||
"mic_stopRecording": "Zastavit nahrávání",
|
||||
"mic_downloadRecording": "Stáhnout nahrávku",
|
||||
"mic_device": "Zařízení",
|
||||
"mic_noMicFound": "Nenalezen žádný mikrofon",
|
||||
"mic_refresh": "Obnovit",
|
||||
"mic_clipping": "Ořezávání",
|
||||
"mic_constraints": "Omezení",
|
||||
"mic_echoCancellation": "Potlačení ozvěny",
|
||||
"mic_noiseSuppression": "Potlačení šumu",
|
||||
"mic_agc": "Automatické řízení zisku",
|
||||
"mic_applyConstraints": "Použít",
|
||||
"mic_channels": "Kanály",
|
||||
"mic_stereo": "Stereo",
|
||||
"mic_requested": "Požadováno",
|
||||
"mic_obtained": "Získáno",
|
||||
"mic_peakNow": "Špička",
|
||||
"mic_peakHold": "Držení špičky",
|
||||
"mic_resetPeaks": "Resetovat špičky",
|
||||
"mic_advanced": "Pokročilé",
|
||||
"mic_default": "Výchozí",
|
||||
"mic_on": "Zapnuto",
|
||||
"mic_off": "Vypnuto",
|
||||
"mic_mono": "Mono",
|
||||
"sensors_title": "Senzory",
|
||||
"sensors_geolocation": "Geolokace",
|
||||
"sensors_start": "Start",
|
||||
"sensors_stop": "Stop",
|
||||
"sensors_accuracy": "Přesnost (m)",
|
||||
"sensors_altitude": "Nadmořská výška (m)",
|
||||
"sensors_heading": "Směr (stupně)",
|
||||
"sensors_speed": "Rychlost (m/s)",
|
||||
"sensors_timestamp": "Časové razítko",
|
||||
"sensors_copy": "Kopírovat JSON",
|
||||
"sensors_copied": "Zkopírováno do schránky",
|
||||
"sensors_notSupported": "Není podporováno na tomto zařízení/prohlížeči",
|
||||
"sensors_deviceMotion": "Pohyb zařízení",
|
||||
"sensors_deviceOrientation": "Orientace zařízení",
|
||||
"sensors_accelerometer": "Akcelerometr",
|
||||
"sensors_gyroscope": "Gyroskop",
|
||||
"sensors_magnetometer": "Magnetometr",
|
||||
"sensors_ambientLight": "Okolní světlo",
|
||||
"sensors_illuminance": "Osvětlení (lux)",
|
||||
"sensors_barometer": "Barometr",
|
||||
"sensors_pressure": "Tlak (hPa)",
|
||||
"sensors_temperature": "Teplota (°C)",
|
||||
"sensors_permissions": "Oprávnění",
|
||||
"sensors_enableMotionOrientation": "Povolit pohyb/orientaci",
|
||||
"sensors_motion": "Pohyb",
|
||||
"sensors_orientation": "Orientace",
|
||||
"sensors_status_granted": "Uděleno",
|
||||
"sensors_status_denied": "Odepřeno",
|
||||
"sensors_status_unknown": "Neznámý"
|
||||
}
|
170
messages/de.json
Normal file
170
messages/de.json
Normal file
|
@ -0,0 +1,170 @@
|
|||
{
|
||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||
"search": "Suchen",
|
||||
"tests_audio_description": "Überprüfen Sie Ihre Stereokanäle oder den Surround-Audioausgang, überprüfen Sie, ob Ihre Lautsprecher in Phase sind.",
|
||||
"tests_audio_label": "Audio",
|
||||
"tests_av-sync_description": "Überprüfen Sie, ob Audio und Video synchron sind, und messen Sie die Verzögerung.",
|
||||
"tests_av-sync_label": "Audio/Video-Synchronisation",
|
||||
"tests_card_description": "Testkarte für Ihr Display oder Ihren Projektor, überprüfen Sie Farben, Auflösung und Geometrie.",
|
||||
"tests_card_label": "Karte",
|
||||
"tests_camera_description": "Überprüfen Sie, ob Ihre Webcam oder Ihr Aufnahmegerät funktioniert, die Bildqualität, Auflösung und Bildrate. Machen Sie einen Schnappschuss.",
|
||||
"tests_camera_label": "Kamera",
|
||||
"tests_gamepad_description": "Testen Sie Ihr Gamepad, überprüfen Sie, ob es funktioniert, alle Tasten und Joysticks, Stick-Drift, Totzonen und Kalibrierung.",
|
||||
"tests_gamepad_label": "Gamepad",
|
||||
"tests_keyboard_description": "Überprüfen Sie, ob alle Tasten funktionieren und welche Tastencodes sie senden.",
|
||||
"tests_keyboard_label": "Tastatur",
|
||||
"tests_microphone_description": "Überprüfen Sie, ob Ihr Mikrofon funktioniert, seine Qualität, Lautstärke und Rauschen.",
|
||||
"tests_microphone_label": "Mikrofon",
|
||||
"tests_mouse_description": "Überprüfen Sie, ob Ihre Maus oder Ihr Touch-Gerät ordnungsgemäß funktioniert, ob es tote Zonen oder Jitter gibt.",
|
||||
"tests_mouse_label": "Maus",
|
||||
"tests_sensors_description": "Sehen Sie sich die Ausgabe der Sensoren Ihres Geräts an, z. B. GPS, Beschleunigungsmesser, Gyroskop, Kompass usw.",
|
||||
"tests_sensors_label": "Sensoren",
|
||||
"tests_internet_description": "Messen Sie Ihre Internetgeschwindigkeit und Latenz.",
|
||||
"tests_internet_label": "Internetgeschwindigkeit",
|
||||
"tests_timer_description": "Überprüfen Sie, ob Ihr hochauflösender Timer funktioniert.",
|
||||
"tests_timer_label": "Hochauflösender Timer",
|
||||
"category_inputs": "Eingänge",
|
||||
"category_outputs": "Ausgänge",
|
||||
"category_audio": "Audio",
|
||||
"category_video": "Video",
|
||||
"category_control": "Steuerung",
|
||||
"category_misc": "Sonstiges",
|
||||
"noTestsFound": "Keine Tests gefunden.",
|
||||
"camera_title": "Kameratest",
|
||||
"camera_device": "Gerät",
|
||||
"camera_noCameraFound": "Keine Kamera gefunden",
|
||||
"camera_refresh": "Aktualisieren",
|
||||
"camera_resolution": "Auflösung",
|
||||
"camera_frameRate": "Bildrate",
|
||||
"camera_noCameraSelected": "Keine Kamera ausgewählt",
|
||||
"camera_takePicture": "Bild aufnehmen",
|
||||
"camera_unflipImage": "Bild zurückklappen",
|
||||
"camera_flipImage": "Bild spiegeln",
|
||||
"camera_closeSnapshot": "Schnappschuss schließen",
|
||||
"audio_channel_frontLeft": "Vorne links",
|
||||
"audio_channel_frontCenter": "Vorne Mitte",
|
||||
"audio_channel_frontRight": "Vorne rechts",
|
||||
"audio_channel_sideLeft": "Seite links",
|
||||
"audio_channel_sideRight": "Seite rechts",
|
||||
"audio_channel_rearLeft": "Hinten links",
|
||||
"audio_channel_rearRight": "Hinten rechts",
|
||||
"audio_channel_lfe": "LFE",
|
||||
"gamepad_title": "Gamepad- und Joystick-Tests",
|
||||
"gamepad_device": "Gerät",
|
||||
"gamepad_noGamepadsDetected": "Keine Gamepads erkannt. (Versuchen Sie, eine Taste zu drücken)",
|
||||
"gamepad_refresh": "Aktualisieren",
|
||||
"gamepad_buttons": "Tasten",
|
||||
"gamepad_axes": "Achsen",
|
||||
"gamepad_history": "Verlauf",
|
||||
"audio_channelTests": "Kanaltester",
|
||||
"audio_stereo": "Stereo",
|
||||
"audio_surroundAudio": "Surround-Audio",
|
||||
"audio_surround51": "5.1 Surround",
|
||||
"audio_surround71": "7.1 Surround",
|
||||
"audio_phaseTest": "Phasentest",
|
||||
"audio_frequency": "Frequenz",
|
||||
"audio_inPhase": "In Phase",
|
||||
"audio_outOfPhase": "Außer Phase",
|
||||
"audio_stop": "Stopp",
|
||||
"screenInfo_screenResolution": "Bildschirmauflösung",
|
||||
"screenInfo_windowResolution": "Fensterauflösung",
|
||||
"screenInfo_devicePixelRatio": "Gerätepixelverhältnis",
|
||||
"audio_channel_left": "Links",
|
||||
"audio_channel_center": "Mitte",
|
||||
"audio_channel_right": "Rechts",
|
||||
"keyboard_title": "Tastaturtest",
|
||||
"keyboard_instruction": "Drücken Sie eine Taste auf der Tastatur, um das Ereignisobjekt und den Tastencode anzuzeigen.",
|
||||
"keyboard_pressedKeys": "Gedrückte Tasten:",
|
||||
"timer_title": "Hochauflösender Timer",
|
||||
"timer_fps": "FPS",
|
||||
"timer_restart": "Neustart",
|
||||
"audio_stopCycling": "Zyklus stoppen",
|
||||
"audio_cycleThrough": "Durchlaufen",
|
||||
"common_back": "Zurück",
|
||||
"audio_title": "Audiotest",
|
||||
"avSync_title": "Audio/Video-Synchronisation",
|
||||
"internet_title": "Internetgeschwindigkeit",
|
||||
"tests_signal-generator_description": "Erzeugen Sie Sinuswellen, Rauschen (weiß, pink, braun) und Frequenz-Sweeps. Mit Oszilloskop und Spektrum.",
|
||||
"tests_signal-generator_label": "Signalgenerator",
|
||||
"signalGen_title": "Signalgenerator",
|
||||
"signalGen_type": "Typ",
|
||||
"signalGen_sine": "Sinus",
|
||||
"signalGen_sweep": "Sweep",
|
||||
"signalGen_noiseWhite": "Weißes Rauschen",
|
||||
"signalGen_noisePink": "Pinkes Rauschen",
|
||||
"signalGen_noiseBrown": "Braunes Rauschen",
|
||||
"signalGen_frequency": "Frequenz",
|
||||
"signalGen_from": "Von",
|
||||
"signalGen_to": "Bis",
|
||||
"signalGen_duration": "Dauer",
|
||||
"signalGen_gain": "Verstärkung",
|
||||
"signalGen_start": "Start",
|
||||
"signalGen_stop": "Stopp",
|
||||
"signalGen_scope": "Oszilloskop",
|
||||
"signalGen_spectrum": "Spektrum",
|
||||
"signalGen_loop": "Schleife",
|
||||
"mic_title": "Mikrofontest",
|
||||
"mic_startMicrophone": "Mikrofon starten",
|
||||
"mic_stop": "Stopp",
|
||||
"mic_monitoringOn": "Monitoring: EIN",
|
||||
"mic_monitoringOff": "Monitoring: AUS",
|
||||
"mic_gain": "Verstärkung",
|
||||
"mic_monitorDelay": "Monitor-Verzögerung",
|
||||
"mic_sampleRate": "Abtastrate",
|
||||
"mic_inputDevice": "Eingabegerät",
|
||||
"mic_volume": "Lautstärke",
|
||||
"mic_recording": "Aufnahme",
|
||||
"mic_startRecording": "Aufnahme starten",
|
||||
"mic_stopRecording": "Aufnahme stoppen",
|
||||
"mic_downloadRecording": "Aufnahme herunterladen",
|
||||
"mic_device": "Gerät",
|
||||
"mic_noMicFound": "Kein Mikrofon gefunden",
|
||||
"mic_refresh": "Aktualisieren",
|
||||
"mic_clipping": "Übersteuerung",
|
||||
"mic_constraints": "Einschränkungen",
|
||||
"mic_echoCancellation": "Echounterdrückung",
|
||||
"mic_noiseSuppression": "Rauschunterdrückung",
|
||||
"mic_agc": "Automatische Verstärkungsregelung",
|
||||
"mic_applyConstraints": "Anwenden",
|
||||
"mic_channels": "Kanäle",
|
||||
"mic_stereo": "Stereo",
|
||||
"mic_requested": "Angefordert",
|
||||
"mic_obtained": "Erhalten",
|
||||
"mic_peakNow": "Spitze",
|
||||
"mic_peakHold": "Spitze halten",
|
||||
"mic_resetPeaks": "Spitzen zurücksetzen",
|
||||
"mic_advanced": "Erweitert",
|
||||
"mic_default": "Standard",
|
||||
"mic_on": "Ein",
|
||||
"mic_off": "Aus",
|
||||
"mic_mono": "Mono",
|
||||
"sensors_title": "Sensoren",
|
||||
"sensors_geolocation": "Geolokalisierung",
|
||||
"sensors_start": "Start",
|
||||
"sensors_stop": "Stopp",
|
||||
"sensors_accuracy": "Genauigkeit (m)",
|
||||
"sensors_altitude": "Höhe (m)",
|
||||
"sensors_heading": "Richtung (Grad)",
|
||||
"sensors_speed": "Geschwindigkeit (m/s)",
|
||||
"sensors_timestamp": "Zeitstempel",
|
||||
"sensors_copy": "JSON kopieren",
|
||||
"sensors_copied": "In die Zwischenablage kopiert",
|
||||
"sensors_notSupported": "Auf diesem Gerät/Browser nicht unterstützt",
|
||||
"sensors_deviceMotion": "Gerätebewegung",
|
||||
"sensors_deviceOrientation": "Geräteausrichtung",
|
||||
"sensors_accelerometer": "Beschleunigungsmesser",
|
||||
"sensors_gyroscope": "Gyroskop",
|
||||
"sensors_magnetometer": "Magnetometer",
|
||||
"sensors_ambientLight": "Umgebungslicht",
|
||||
"sensors_illuminance": "Beleuchtungsstärke (lux)",
|
||||
"sensors_barometer": "Barometer",
|
||||
"sensors_pressure": "Druck (hPa)",
|
||||
"sensors_temperature": "Temperatur (°C)",
|
||||
"sensors_permissions": "Berechtigungen",
|
||||
"sensors_enableMotionOrientation": "Bewegung/Ausrichtung aktivieren",
|
||||
"sensors_motion": "Bewegung",
|
||||
"sensors_orientation": "Ausrichtung",
|
||||
"sensors_status_granted": "Gewährt",
|
||||
"sensors_status_denied": "Verweigert",
|
||||
"sensors_status_unknown": "Unbekannt"
|
||||
}
|
|
@ -83,5 +83,88 @@
|
|||
"common_back": "Back",
|
||||
"audio_title": "Audio test",
|
||||
"avSync_title": "Audio/Video Synchronization",
|
||||
"internet_title": "Internet speed"
|
||||
"internet_title": "Internet speed",
|
||||
"tests_signal-generator_description": "Generate sine waves, noise (white, pink, brown) and frequency sweeps. Includes oscilloscope and spectrum.",
|
||||
"tests_signal-generator_label": "Signal Generator",
|
||||
"signalGen_title": "Signal Generator",
|
||||
"signalGen_type": "Type",
|
||||
"signalGen_sine": "Sine",
|
||||
"signalGen_sweep": "Sweep",
|
||||
"signalGen_noiseWhite": "White noise",
|
||||
"signalGen_noisePink": "Pink noise",
|
||||
"signalGen_noiseBrown": "Brown noise",
|
||||
"signalGen_frequency": "Frequency",
|
||||
"signalGen_from": "From",
|
||||
"signalGen_to": "To",
|
||||
"signalGen_duration": "Duration",
|
||||
"signalGen_gain": "Gain",
|
||||
"signalGen_start": "Start",
|
||||
"signalGen_stop": "Stop",
|
||||
"signalGen_scope": "Oscilloscope",
|
||||
"signalGen_spectrum": "Spectrum",
|
||||
"signalGen_loop": "Loop",
|
||||
"mic_title": "Microphone test",
|
||||
"mic_startMicrophone": "Start microphone",
|
||||
"mic_stop": "Stop",
|
||||
"mic_monitoringOn": "Monitoring: ON",
|
||||
"mic_monitoringOff": "Monitoring: OFF",
|
||||
"mic_gain": "Gain",
|
||||
"mic_monitorDelay": "Monitor delay",
|
||||
"mic_sampleRate": "Sample rate",
|
||||
"mic_inputDevice": "Input",
|
||||
"mic_volume": "Volume",
|
||||
"mic_recording": "Recording",
|
||||
"mic_startRecording": "Start recording",
|
||||
"mic_stopRecording": "Stop recording",
|
||||
"mic_downloadRecording": "Download",
|
||||
"mic_device": "Device",
|
||||
"mic_noMicFound": "No microphone found",
|
||||
"mic_refresh": "Refresh",
|
||||
"mic_clipping": "Clipping",
|
||||
"mic_constraints": "Constraints",
|
||||
"mic_echoCancellation": "Echo cancellation",
|
||||
"mic_noiseSuppression": "Noise suppression",
|
||||
"mic_agc": "Auto gain control",
|
||||
"mic_applyConstraints": "Apply",
|
||||
"mic_channels": "Channels",
|
||||
"mic_stereo": "Stereo",
|
||||
"mic_requested": "Requested",
|
||||
"mic_obtained": "Obtained",
|
||||
"mic_peakNow": "Peak",
|
||||
"mic_peakHold": "Peak hold",
|
||||
"mic_resetPeaks": "Reset peaks",
|
||||
"mic_advanced": "Advanced",
|
||||
"mic_default": "Default",
|
||||
"mic_on": "On",
|
||||
"mic_off": "Off",
|
||||
"mic_mono": "Mono",
|
||||
"sensors_title": "Sensors",
|
||||
"sensors_geolocation": "Geolocation",
|
||||
"sensors_start": "Start",
|
||||
"sensors_stop": "Stop",
|
||||
"sensors_accuracy": "Accuracy (m)",
|
||||
"sensors_altitude": "Altitude (m)",
|
||||
"sensors_heading": "Heading (deg)",
|
||||
"sensors_speed": "Speed (m/s)",
|
||||
"sensors_timestamp": "Timestamp",
|
||||
"sensors_copy": "Copy JSON",
|
||||
"sensors_copied": "Copied to clipboard",
|
||||
"sensors_notSupported": "Not supported on this device/browser",
|
||||
"sensors_deviceMotion": "Device Motion",
|
||||
"sensors_deviceOrientation": "Device Orientation",
|
||||
"sensors_accelerometer": "Accelerometer",
|
||||
"sensors_gyroscope": "Gyroscope",
|
||||
"sensors_magnetometer": "Magnetometer",
|
||||
"sensors_ambientLight": "Ambient Light",
|
||||
"sensors_illuminance": "Illuminance (lux)",
|
||||
"sensors_barometer": "Barometer",
|
||||
"sensors_pressure": "Pressure (hPa)",
|
||||
"sensors_temperature": "Temperature (°C)",
|
||||
"sensors_permissions": "Permissions",
|
||||
"sensors_enableMotionOrientation": "Enable Motion/Orientation",
|
||||
"sensors_motion": "Motion",
|
||||
"sensors_orientation": "Orientation",
|
||||
"sensors_status_granted": "Granted",
|
||||
"sensors_status_denied": "Denied",
|
||||
"sensors_status_unknown": "Unknown"
|
||||
}
|
||||
|
|
170
messages/es.json
Normal file
170
messages/es.json
Normal file
|
@ -0,0 +1,170 @@
|
|||
{
|
||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||
"search": "Buscar",
|
||||
"tests_audio_description": "Comprueba tus canales estéreo o la salida de audio envolvente, verifica si tus altavoces están en fase.",
|
||||
"tests_audio_label": "Audio",
|
||||
"tests_av-sync_description": "Comprueba si el audio y el video están sincronizados y mide el retraso.",
|
||||
"tests_av-sync_label": "Sincronización de Audio/Video",
|
||||
"tests_card_description": "Tarjeta de prueba para tu pantalla o proyector, comprueba colores, resolución y geometría.",
|
||||
"tests_card_label": "Tarjeta",
|
||||
"tests_camera_description": "Comprueba si tu cámara web o dispositivo de captura funciona, su calidad de imagen, resolución y velocidad de fotogramas. Toma una instantánea.",
|
||||
"tests_camera_label": "Cámara",
|
||||
"tests_gamepad_description": "Prueba tu gamepad, comprueba si funciona, todos los botones y joysticks, deriva del stick, zonas muertas y calibración.",
|
||||
"tests_gamepad_label": "Gamepad",
|
||||
"tests_keyboard_description": "Comprueba si todas las teclas funcionan y qué códigos de tecla envían.",
|
||||
"tests_keyboard_label": "Teclado",
|
||||
"tests_microphone_description": "Comprueba si tu micrófono funciona, su calidad, volumen y ruido.",
|
||||
"tests_microphone_label": "Micrófono",
|
||||
"tests_mouse_description": "Comprueba si tu ratón o dispositivo táctil funciona correctamente, si hay zonas muertas o fluctuaciones.",
|
||||
"tests_mouse_label": "Ratón",
|
||||
"tests_sensors_description": "Consulta la salida de los sensores de tu dispositivo, p. ej., GPS, acelerómetro, giroscopio, brújula, etc.",
|
||||
"tests_sensors_label": "Sensores",
|
||||
"tests_internet_description": "Mide tu velocidad de internet y latencia.",
|
||||
"tests_internet_label": "Velocidad de internet",
|
||||
"tests_timer_description": "Comprueba si tu temporizador de alta resolución funciona.",
|
||||
"tests_timer_label": "Temporizador de alta resolución",
|
||||
"category_inputs": "Entradas",
|
||||
"category_outputs": "Salidas",
|
||||
"category_audio": "Audio",
|
||||
"category_video": "Video",
|
||||
"category_control": "Control",
|
||||
"category_misc": "Misceláneo",
|
||||
"noTestsFound": "No se encontraron pruebas.",
|
||||
"camera_title": "Prueba de cámara",
|
||||
"camera_device": "Dispositivo",
|
||||
"camera_noCameraFound": "No se encontró ninguna cámara",
|
||||
"camera_refresh": "Actualizar",
|
||||
"camera_resolution": "Resolución",
|
||||
"camera_frameRate": "Velocidad de fotogramas",
|
||||
"camera_noCameraSelected": "No se ha seleccionado ninguna cámara",
|
||||
"camera_takePicture": "Tomar foto",
|
||||
"camera_unflipImage": "Desvoltear imagen",
|
||||
"camera_flipImage": "Voltear imagen",
|
||||
"camera_closeSnapshot": "Cerrar instantánea",
|
||||
"audio_channel_frontLeft": "Frontal izquierdo",
|
||||
"audio_channel_frontCenter": "Frontal central",
|
||||
"audio_channel_frontRight": "Frontal derecho",
|
||||
"audio_channel_sideLeft": "Lateral izquierdo",
|
||||
"audio_channel_sideRight": "Lateral derecho",
|
||||
"audio_channel_rearLeft": "Trasero izquierdo",
|
||||
"audio_channel_rearRight": "Trasero derecho",
|
||||
"audio_channel_lfe": "LFE",
|
||||
"gamepad_title": "Pruebas de Gamepad y Joystick",
|
||||
"gamepad_device": "Dispositivo",
|
||||
"gamepad_noGamepadsDetected": "No se detectaron gamepads. (Intenta presionar un botón)",
|
||||
"gamepad_refresh": "Actualizar",
|
||||
"gamepad_buttons": "Botones",
|
||||
"gamepad_axes": "Ejes",
|
||||
"gamepad_history": "Historial",
|
||||
"audio_channelTests": "Pruebas de canal",
|
||||
"audio_stereo": "Estéreo",
|
||||
"audio_surroundAudio": "Audio envolvente",
|
||||
"audio_surround51": "Envolvente 5.1",
|
||||
"audio_surround71": "Envolvente 7.1",
|
||||
"audio_phaseTest": "Prueba de fase",
|
||||
"audio_frequency": "Frecuencia",
|
||||
"audio_inPhase": "En fase",
|
||||
"audio_outOfPhase": "Fuera de fase",
|
||||
"audio_stop": "Detener",
|
||||
"screenInfo_screenResolution": "Resolución de pantalla",
|
||||
"screenInfo_windowResolution": "Resolución de ventana",
|
||||
"screenInfo_devicePixelRatio": "Relación de píxeles del dispositivo",
|
||||
"audio_channel_left": "Izquierda",
|
||||
"audio_channel_center": "Centro",
|
||||
"audio_channel_right": "Derecha",
|
||||
"keyboard_title": "Prueba de teclado",
|
||||
"keyboard_instruction": "Presiona una tecla en el teclado para ver el objeto de evento y el código de tecla.",
|
||||
"keyboard_pressedKeys": "Teclas presionadas:",
|
||||
"timer_title": "Temporizador de alta resolución",
|
||||
"timer_fps": "FPS",
|
||||
"timer_restart": "Reiniciar",
|
||||
"audio_stopCycling": "Detener ciclo",
|
||||
"audio_cycleThrough": "Recorrer",
|
||||
"common_back": "Atrás",
|
||||
"audio_title": "Prueba de audio",
|
||||
"avSync_title": "Sincronización de Audio/Video",
|
||||
"internet_title": "Velocidad de internet",
|
||||
"tests_signal-generator_description": "Genera ondas senoidales, ruido (blanco, rosa, marrón) y barridos de frecuencia. Incluye osciloscopio y espectro.",
|
||||
"tests_signal-generator_label": "Generador de señal",
|
||||
"signalGen_title": "Generador de señal",
|
||||
"signalGen_type": "Tipo",
|
||||
"signalGen_sine": "Senoidal",
|
||||
"signalGen_sweep": "Barrido",
|
||||
"signalGen_noiseWhite": "Ruido blanco",
|
||||
"signalGen_noisePink": "Ruido rosa",
|
||||
"signalGen_noiseBrown": "Ruido marrón",
|
||||
"signalGen_frequency": "Frecuencia",
|
||||
"signalGen_from": "Desde",
|
||||
"signalGen_to": "Hasta",
|
||||
"signalGen_duration": "Duración",
|
||||
"signalGen_gain": "Ganancia",
|
||||
"signalGen_start": "Iniciar",
|
||||
"signalGen_stop": "Detener",
|
||||
"signalGen_scope": "Osciloscopio",
|
||||
"signalGen_spectrum": "Espectro",
|
||||
"signalGen_loop": "Bucle",
|
||||
"mic_title": "Prueba de micrófono",
|
||||
"mic_startMicrophone": "Iniciar micrófono",
|
||||
"mic_stop": "Detener",
|
||||
"mic_monitoringOn": "Monitorización: ON",
|
||||
"mic_monitoringOff": "Monitorización: OFF",
|
||||
"mic_gain": "Ganancia",
|
||||
"mic_monitorDelay": "Retraso del monitor",
|
||||
"mic_sampleRate": "Tasa de muestreo",
|
||||
"mic_inputDevice": "Dispositivo de entrada",
|
||||
"mic_volume": "Volumen",
|
||||
"mic_recording": "Grabación",
|
||||
"mic_startRecording": "Iniciar grabación",
|
||||
"mic_stopRecording": "Detener grabación",
|
||||
"mic_downloadRecording": "Descargar grabación",
|
||||
"mic_device": "Dispositivo",
|
||||
"mic_noMicFound": "No se encontró ningún micrófono",
|
||||
"mic_refresh": "Actualizar",
|
||||
"mic_clipping": "Recorte",
|
||||
"mic_constraints": "Restricciones",
|
||||
"mic_echoCancellation": "Cancelación de eco",
|
||||
"mic_noiseSuppression": "Supresión de ruido",
|
||||
"mic_agc": "Control automático de ganancia",
|
||||
"mic_applyConstraints": "Aplicar",
|
||||
"mic_channels": "Canales",
|
||||
"mic_stereo": "Estéreo",
|
||||
"mic_requested": "Solicitado",
|
||||
"mic_obtained": "Obtenido",
|
||||
"mic_peakNow": "Pico",
|
||||
"mic_peakHold": "Mantener pico",
|
||||
"mic_resetPeaks": "Restablecer picos",
|
||||
"mic_advanced": "Avanzado",
|
||||
"mic_default": "Predeterminado",
|
||||
"mic_on": "Encendido",
|
||||
"mic_off": "Apagado",
|
||||
"mic_mono": "Mono",
|
||||
"sensors_title": "Sensores",
|
||||
"sensors_geolocation": "Geolocalización",
|
||||
"sensors_start": "Iniciar",
|
||||
"sensors_stop": "Detener",
|
||||
"sensors_accuracy": "Precisión (m)",
|
||||
"sensors_altitude": "Altitud (m)",
|
||||
"sensors_heading": "Rumbo (grados)",
|
||||
"sensors_speed": "Velocidad (m/s)",
|
||||
"sensors_timestamp": "Marca de tiempo",
|
||||
"sensors_copy": "Copiar JSON",
|
||||
"sensors_copied": "Copiado al portapapeles",
|
||||
"sensors_notSupported": "No compatible con este dispositivo/navegador",
|
||||
"sensors_deviceMotion": "Movimiento del dispositivo",
|
||||
"sensors_deviceOrientation": "Orientación del dispositivo",
|
||||
"sensors_accelerometer": "Acelerómetro",
|
||||
"sensors_gyroscope": "Giroscopio",
|
||||
"sensors_magnetometer": "Magnetómetro",
|
||||
"sensors_ambientLight": "Luz ambiental",
|
||||
"sensors_illuminance": "Iluminancia (lux)",
|
||||
"sensors_barometer": "Barómetro",
|
||||
"sensors_pressure": "Presión (hPa)",
|
||||
"sensors_temperature": "Temperatura (°C)",
|
||||
"sensors_permissions": "Permisos",
|
||||
"sensors_enableMotionOrientation": "Activar Movimiento/Orientación",
|
||||
"sensors_motion": "Movimiento",
|
||||
"sensors_orientation": "Orientación",
|
||||
"sensors_status_granted": "Concedido",
|
||||
"sensors_status_denied": "Denegado",
|
||||
"sensors_status_unknown": "Desconocido"
|
||||
}
|
170
messages/fr.json
Normal file
170
messages/fr.json
Normal file
|
@ -0,0 +1,170 @@
|
|||
{
|
||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||
"search": "Rechercher",
|
||||
"tests_audio_description": "Vérifiez vos canaux stéréo ou votre sortie audio surround, vérifiez si vos haut-parleurs sont en phase.",
|
||||
"tests_audio_label": "Audio",
|
||||
"tests_av-sync_description": "Vérifiez si votre audio et votre vidéo sont synchronisés et mesurez le décalage.",
|
||||
"tests_av-sync_label": "Synchronisation Audio/Vidéo",
|
||||
"tests_card_description": "Carte de test pour votre écran ou projecteur, vérifiez les couleurs, la résolution et la géométrie.",
|
||||
"tests_card_label": "Carte",
|
||||
"tests_camera_description": "Vérifiez si votre webcam ou votre périphérique de capture fonctionne, sa qualité d'image, sa résolution et sa fréquence d'images. Prenez une photo.",
|
||||
"tests_camera_label": "Caméra",
|
||||
"tests_gamepad_description": "Testez votre manette de jeu, vérifiez si elle fonctionne, tous les boutons et joysticks, la dérive des sticks, les zones mortes et le calibrage.",
|
||||
"tests_gamepad_label": "Manette de jeu",
|
||||
"tests_keyboard_description": "Vérifiez si toutes les touches fonctionnent et quels codes de touche elles envoient.",
|
||||
"tests_keyboard_label": "Clavier",
|
||||
"tests_microphone_description": "Vérifiez si votre microphone fonctionne, sa qualité, son volume et le bruit.",
|
||||
"tests_microphone_label": "Microphone",
|
||||
"tests_mouse_description": "Vérifiez si votre souris ou votre périphérique tactile fonctionne correctement, s'il y a des zones mortes ou des sautillements.",
|
||||
"tests_mouse_label": "Souris",
|
||||
"tests_sensors_description": "Consultez la sortie des capteurs de votre appareil, par ex. GPS, accéléromètre, gyroscope, boussole, etc.",
|
||||
"tests_sensors_label": "Capteurs",
|
||||
"tests_internet_description": "Mesurez votre vitesse Internet et votre latence.",
|
||||
"tests_internet_label": "Vitesse Internet",
|
||||
"tests_timer_description": "Vérifiez si votre minuteur haute résolution fonctionne.",
|
||||
"tests_timer_label": "Minuteur haute résolution",
|
||||
"category_inputs": "Entrées",
|
||||
"category_outputs": "Sorties",
|
||||
"category_audio": "Audio",
|
||||
"category_video": "Vidéo",
|
||||
"category_control": "Contrôle",
|
||||
"category_misc": "Divers",
|
||||
"noTestsFound": "Aucun test trouvé.",
|
||||
"camera_title": "Test de la caméra",
|
||||
"camera_device": "Appareil",
|
||||
"camera_noCameraFound": "Aucune caméra trouvée",
|
||||
"camera_refresh": "Actualiser",
|
||||
"camera_resolution": "Résolution",
|
||||
"camera_frameRate": "Fréquence d'images",
|
||||
"camera_noCameraSelected": "Aucune caméra sélectionnée",
|
||||
"camera_takePicture": "Prendre une photo",
|
||||
"camera_unflipImage": "Retourner l'image",
|
||||
"camera_flipImage": "Inverser l'image",
|
||||
"camera_closeSnapshot": "Fermer l'instantané",
|
||||
"audio_channel_frontLeft": "Avant gauche",
|
||||
"audio_channel_frontCenter": "Avant centre",
|
||||
"audio_channel_frontRight": "Avant droit",
|
||||
"audio_channel_sideLeft": "Côté gauche",
|
||||
"audio_channel_sideRight": "Côté droit",
|
||||
"audio_channel_rearLeft": "Arrière gauche",
|
||||
"audio_channel_rearRight": "Arrière droit",
|
||||
"audio_channel_lfe": "LFE",
|
||||
"gamepad_title": "Tests de manette de jeu et de joystick",
|
||||
"gamepad_device": "Appareil",
|
||||
"gamepad_noGamepadsDetected": "Aucune manette de jeu détectée. (Essayez d'appuyer sur un bouton)",
|
||||
"gamepad_refresh": "Actualiser",
|
||||
"gamepad_buttons": "Boutons",
|
||||
"gamepad_axes": "Axes",
|
||||
"gamepad_history": "Historique",
|
||||
"audio_channelTests": "Tests de canaux",
|
||||
"audio_stereo": "Stéréo",
|
||||
"audio_surroundAudio": "Audio surround",
|
||||
"audio_surround51": "Surround 5.1",
|
||||
"audio_surround71": "Surround 7.1",
|
||||
"audio_phaseTest": "Test de phase",
|
||||
"audio_frequency": "Fréquence",
|
||||
"audio_inPhase": "En phase",
|
||||
"audio_outOfPhase": "Hors phase",
|
||||
"audio_stop": "Arrêter",
|
||||
"screenInfo_screenResolution": "Résolution de l'écran",
|
||||
"screenInfo_windowResolution": "Résolution de la fenêtre",
|
||||
"screenInfo_devicePixelRatio": "Ratio de pixels de l'appareil",
|
||||
"audio_channel_left": "Gauche",
|
||||
"audio_channel_center": "Centre",
|
||||
"audio_channel_right": "Droite",
|
||||
"keyboard_title": "Test du clavier",
|
||||
"keyboard_instruction": "Appuyez sur une touche du clavier pour voir l'objet événement et le code de la touche.",
|
||||
"keyboard_pressedKeys": "Touches enfoncées :",
|
||||
"timer_title": "Minuteur haute résolution",
|
||||
"timer_fps": "FPS",
|
||||
"timer_restart": "Redémarrer",
|
||||
"audio_stopCycling": "Arrêter le cycle",
|
||||
"audio_cycleThrough": "Parcourir",
|
||||
"common_back": "Retour",
|
||||
"audio_title": "Test audio",
|
||||
"avSync_title": "Synchronisation Audio/Vidéo",
|
||||
"internet_title": "Vitesse Internet",
|
||||
"tests_signal-generator_description": "Générez des ondes sinusoïdales, du bruit (blanc, rose, marron) et des balayages de fréquence. Comprend un oscilloscope et un spectre.",
|
||||
"tests_signal-generator_label": "Générateur de signaux",
|
||||
"signalGen_title": "Générateur de signaux",
|
||||
"signalGen_type": "Type",
|
||||
"signalGen_sine": "Sinusoïdal",
|
||||
"signalGen_sweep": "Balayage",
|
||||
"signalGen_noiseWhite": "Bruit blanc",
|
||||
"signalGen_noisePink": "Bruit rose",
|
||||
"signalGen_noiseBrown": "Bruit marron",
|
||||
"signalGen_frequency": "Fréquence",
|
||||
"signalGen_from": "De",
|
||||
"signalGen_to": "À",
|
||||
"signalGen_duration": "Durée",
|
||||
"signalGen_gain": "Volume",
|
||||
"signalGen_start": "Démarrer",
|
||||
"signalGen_stop": "Arrêter",
|
||||
"signalGen_scope": "Oscilloscope",
|
||||
"signalGen_spectrum": "Spectre",
|
||||
"signalGen_loop": "Boucle",
|
||||
"mic_title": "Test du microphone",
|
||||
"mic_startMicrophone": "Démarrer le microphone",
|
||||
"mic_stop": "Arrêter",
|
||||
"mic_monitoringOn": "Monitoring : ON",
|
||||
"mic_monitoringOff": "Monitoring : OFF",
|
||||
"mic_gain": "Gain",
|
||||
"mic_monitorDelay": "Délai du moniteur",
|
||||
"mic_sampleRate": "Taux d'échantillonnage",
|
||||
"mic_inputDevice": "Périphérique d'entrée",
|
||||
"mic_volume": "Volume",
|
||||
"mic_recording": "Enregistrement",
|
||||
"mic_startRecording": "Démarrer l'enregistrement",
|
||||
"mic_stopRecording": "Arrêter l'enregistrement",
|
||||
"mic_downloadRecording": "Télécharger l'enregistrement",
|
||||
"mic_device": "Appareil",
|
||||
"mic_noMicFound": "Aucun microphone trouvé",
|
||||
"mic_refresh": "Actualiser",
|
||||
"mic_clipping": "Écrêtage",
|
||||
"mic_constraints": "Contraintes",
|
||||
"mic_echoCancellation": "Annulation de l'écho",
|
||||
"mic_noiseSuppression": "Suppression du bruit",
|
||||
"mic_agc": "Contrôle automatique du gain",
|
||||
"mic_applyConstraints": "Appliquer",
|
||||
"mic_channels": "Canaux",
|
||||
"mic_stereo": "Stéréo",
|
||||
"mic_requested": "Demandé",
|
||||
"mic_obtained": "Obtenu",
|
||||
"mic_peakNow": "Pic",
|
||||
"mic_peakHold": "Maintien du pic",
|
||||
"mic_resetPeaks": "Réinitialiser les pics",
|
||||
"mic_advanced": "Avancé",
|
||||
"mic_default": "Défaut",
|
||||
"mic_on": "Activé",
|
||||
"mic_off": "Désactivé",
|
||||
"mic_mono": "Mono",
|
||||
"sensors_title": "Capteurs",
|
||||
"sensors_geolocation": "Géolocalisation",
|
||||
"sensors_start": "Démarrer",
|
||||
"sensors_stop": "Arrêter",
|
||||
"sensors_accuracy": "Précision (m)",
|
||||
"sensors_altitude": "Altitude (m)",
|
||||
"sensors_heading": "Cap (deg)",
|
||||
"sensors_speed": "Vitesse (m/s)",
|
||||
"sensors_timestamp": "Horodatage",
|
||||
"sensors_copy": "Copier JSON",
|
||||
"sensors_copied": "Copié dans le presse-papiers",
|
||||
"sensors_notSupported": "Non pris en charge sur cet appareil/navigateur",
|
||||
"sensors_deviceMotion": "Mouvement de l'appareil",
|
||||
"sensors_deviceOrientation": "Orientation de l'appareil",
|
||||
"sensors_accelerometer": "Accéléromètre",
|
||||
"sensors_gyroscope": "Gyroscope",
|
||||
"sensors_magnetometer": "Magnétomètre",
|
||||
"sensors_ambientLight": "Lumière ambiante",
|
||||
"sensors_illuminance": "Éclairement (lux)",
|
||||
"sensors_barometer": "Baromètre",
|
||||
"sensors_pressure": "Pression (hPa)",
|
||||
"sensors_temperature": "Température (°C)",
|
||||
"sensors_permissions": "Autorisations",
|
||||
"sensors_enableMotionOrientation": "Activer Mouvement/Orientation",
|
||||
"sensors_motion": "Mouvement",
|
||||
"sensors_orientation": "Orientation",
|
||||
"sensors_status_granted": "Accordé",
|
||||
"sensors_status_denied": "Refusé",
|
||||
"sensors_status_unknown": "Inconnu"
|
||||
}
|
170
messages/ja.json
Normal file
170
messages/ja.json
Normal file
|
@ -0,0 +1,170 @@
|
|||
{
|
||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||
"search": "検索",
|
||||
"tests_audio_description": "ステレオチャンネルまたはサラウンドオーディオ出力を確認し、スピーカーが同相であるかを確認します。",
|
||||
"tests_audio_label": "オーディオ",
|
||||
"tests_av-sync_description": "オーディオとビデオが同期しているかを確認し、遅延を測定します。",
|
||||
"tests_av-sync_label": "オーディオ/ビデオ同期",
|
||||
"tests_card_description": "ディスプレイまたはプロジェクターのテストカード、色、解像度、ジオメトリを確認します。",
|
||||
"tests_card_label": "カード",
|
||||
"tests_camera_description": "ウェブカメラまたはキャプチャデバイスが動作しているか、画質、解像度、フレームレートを確認します。スナップショットを撮ります。",
|
||||
"tests_camera_label": "カメラ",
|
||||
"tests_gamepad_description": "ゲームパッドをテストし、動作、すべてのボタンとジョイスティック、スティックのドリフト、デッドゾーン、キャリブレーションを確認します。",
|
||||
"tests_gamepad_label": "ゲームパッド",
|
||||
"tests_keyboard_description": "すべてのキーが機能しているかと、それらが送信するキーコードを確認します。",
|
||||
"tests_keyboard_label": "キーボード",
|
||||
"tests_microphone_description": "マイクが機能しているか、品質、音量、ノイズを確認します。",
|
||||
"tests_microphone_label": "マイク",
|
||||
"tests_mouse_description": "マウスまたはタッチデバイスが正しく機能しているか、デッドゾーンやジッターがないかを確認します。",
|
||||
"tests_mouse_label": "マウス",
|
||||
"tests_sensors_description": "デバイスのセンサー(GPS、加速度計、ジャイロスコープ、コンパスなど)の出力を確認します。",
|
||||
"tests_sensors_label": "センサー",
|
||||
"tests_internet_description": "インターネットの速度と遅延を測定します。",
|
||||
"tests_internet_label": "インターネット速度",
|
||||
"tests_timer_description": "高解像度タイマーが機能しているかを確認します。",
|
||||
"tests_timer_label": "高解像度タイマー",
|
||||
"category_inputs": "入力",
|
||||
"category_outputs": "出力",
|
||||
"category_audio": "オーディオ",
|
||||
"category_video": "ビデオ",
|
||||
"category_control": "コントロール",
|
||||
"category_misc": "その他",
|
||||
"noTestsFound": "テストが見つかりません。",
|
||||
"camera_title": "カメラテスト",
|
||||
"camera_device": "デバイス",
|
||||
"camera_noCameraFound": "カメラが見つかりません",
|
||||
"camera_refresh": "更新",
|
||||
"camera_resolution": "解像度",
|
||||
"camera_frameRate": "フレームレート",
|
||||
"camera_noCameraSelected": "カメラが選択されていません",
|
||||
"camera_takePicture": "写真を撮る",
|
||||
"camera_unflipImage": "画像の反転を元に戻す",
|
||||
"camera_flipImage": "画像を反転",
|
||||
"camera_closeSnapshot": "スナップショットを閉じる",
|
||||
"audio_channel_frontLeft": "フロント左",
|
||||
"audio_channel_frontCenter": "フロントセンター",
|
||||
"audio_channel_frontRight": "フロント右",
|
||||
"audio_channel_sideLeft": "サイド左",
|
||||
"audio_channel_sideRight": "サイド右",
|
||||
"audio_channel_rearLeft": "リア左",
|
||||
"audio_channel_rearRight": "リア右",
|
||||
"audio_channel_lfe": "LFE",
|
||||
"gamepad_title": "ゲームパッドとジョイスティックのテスト",
|
||||
"gamepad_device": "デバイス",
|
||||
"gamepad_noGamepadsDetected": "ゲームパッドが検出されません。(ボタンを押してみてください)",
|
||||
"gamepad_refresh": "更新",
|
||||
"gamepad_buttons": "ボタン",
|
||||
"gamepad_axes": "軸",
|
||||
"gamepad_history": "履歴",
|
||||
"audio_channelTests": "チャンネルテスト",
|
||||
"audio_stereo": "ステレオ",
|
||||
"audio_surroundAudio": "サラウンドオーディオ",
|
||||
"audio_surround51": "5.1サラウンド",
|
||||
"audio_surround71": "7.1サラウンド",
|
||||
"audio_phaseTest": "フェーズテスト",
|
||||
"audio_frequency": "周波数",
|
||||
"audio_inPhase": "同相",
|
||||
"audio_outOfPhase": "逆相",
|
||||
"audio_stop": "停止",
|
||||
"screenInfo_screenResolution": "画面解像度",
|
||||
"screenInfo_windowResolution": "ウィンドウ解像度",
|
||||
"screenInfo_devicePixelRatio": "デバイスピクセル比",
|
||||
"audio_channel_left": "左",
|
||||
"audio_channel_center": "中央",
|
||||
"audio_channel_right": "右",
|
||||
"keyboard_title": "キーボードテスト",
|
||||
"keyboard_instruction": "キーボードのキーを押して、イベントオブジェクトとキーコードを確認します。",
|
||||
"keyboard_pressedKeys": "押されたキー:",
|
||||
"timer_title": "高解像度タイマー",
|
||||
"timer_fps": "FPS",
|
||||
"timer_restart": "再起動",
|
||||
"audio_stopCycling": "サイクリングを停止",
|
||||
"audio_cycleThrough": "サイクルスルー",
|
||||
"common_back": "戻る",
|
||||
"audio_title": "オーディオテスト",
|
||||
"avSync_title": "オーディオ/ビデオ同期",
|
||||
"internet_title": "インターネット速度",
|
||||
"tests_signal-generator_description": "正弦波、ノイズ(ホワイト、ピンク、ブラウン)、周波数スイープを生成します。オシロスコープとスペクトラムが含まれています。",
|
||||
"tests_signal-generator_label": "信号発生器",
|
||||
"signalGen_title": "信号発生器",
|
||||
"signalGen_type": "タイプ",
|
||||
"signalGen_sine": "正弦波",
|
||||
"signalGen_sweep": "スイープ",
|
||||
"signalGen_noiseWhite": "ホワイトノイズ",
|
||||
"signalGen_noisePink": "ピンクノイズ",
|
||||
"signalGen_noiseBrown": "ブラウンノイズ",
|
||||
"signalGen_frequency": "周波数",
|
||||
"signalGen_from": "から",
|
||||
"signalGen_to": "まで",
|
||||
"signalGen_duration": "期間",
|
||||
"signalGen_gain": "音量",
|
||||
"signalGen_start": "開始",
|
||||
"signalGen_stop": "停止",
|
||||
"signalGen_scope": "オシロスコープ",
|
||||
"signalGen_spectrum": "スペクトラム",
|
||||
"signalGen_loop": "ループ",
|
||||
"mic_title": "マイクテスト",
|
||||
"mic_startMicrophone": "マイクを開始",
|
||||
"mic_stop": "停止",
|
||||
"mic_monitoringOn": "モニタリング:オン",
|
||||
"mic_monitoringOff": "モニタリング:オフ",
|
||||
"mic_gain": "ゲイン",
|
||||
"mic_monitorDelay": "モニター遅延",
|
||||
"mic_sampleRate": "サンプルレート",
|
||||
"mic_inputDevice": "入力デバイス",
|
||||
"mic_volume": "音量",
|
||||
"mic_recording": "録音",
|
||||
"mic_startRecording": "録音を開始",
|
||||
"mic_stopRecording": "録音を停止",
|
||||
"mic_downloadRecording": "録音をダウンロード",
|
||||
"mic_device": "デバイス",
|
||||
"mic_noMicFound": "マイクが見つかりません",
|
||||
"mic_refresh": "更新",
|
||||
"mic_clipping": "クリッピング",
|
||||
"mic_constraints": "制約",
|
||||
"mic_echoCancellation": "エコーキャンセル",
|
||||
"mic_noiseSuppression": "ノイズ抑制",
|
||||
"mic_agc": "自動ゲインコントロール",
|
||||
"mic_applyConstraints": "適用",
|
||||
"mic_channels": "チャンネル",
|
||||
"mic_stereo": "ステレオ",
|
||||
"mic_requested": "要求",
|
||||
"mic_obtained": "取得",
|
||||
"mic_peakNow": "ピーク",
|
||||
"mic_peakHold": "ピークホールド",
|
||||
"mic_resetPeaks": "ピークをリセット",
|
||||
"mic_advanced": "詳細",
|
||||
"mic_default": "デフォルト",
|
||||
"mic_on": "オン",
|
||||
"mic_off": "オフ",
|
||||
"mic_mono": "モノラル",
|
||||
"sensors_title": "センサー",
|
||||
"sensors_geolocation": "地理位置情報",
|
||||
"sensors_start": "開始",
|
||||
"sensors_stop": "停止",
|
||||
"sensors_accuracy": "精度 (m)",
|
||||
"sensors_altitude": "高度 (m)",
|
||||
"sensors_heading": "方角 (度)",
|
||||
"sensors_speed": "速度 (m/s)",
|
||||
"sensors_timestamp": "タイムスタンプ",
|
||||
"sensors_copy": "JSONをコピー",
|
||||
"sensors_copied": "クリップボードにコピーしました",
|
||||
"sensors_notSupported": "このデバイス/ブラウザではサポートされていません",
|
||||
"sensors_deviceMotion": "デバイスの動き",
|
||||
"sensors_deviceOrientation": "デバイスの向き",
|
||||
"sensors_accelerometer": "加速度計",
|
||||
"sensors_gyroscope": "ジャイロスコープ",
|
||||
"sensors_magnetometer": "磁力計",
|
||||
"sensors_ambientLight": "環境光",
|
||||
"sensors_illuminance": "照度 (lux)",
|
||||
"sensors_barometer": "気圧計",
|
||||
"sensors_pressure": "圧力 (hPa)",
|
||||
"sensors_temperature": "温度 (°C)",
|
||||
"sensors_permissions": "権限",
|
||||
"sensors_enableMotionOrientation": "モーション/オリエンテーションを有効にする",
|
||||
"sensors_motion": "モーション",
|
||||
"sensors_orientation": "オリエンテーション",
|
||||
"sensors_status_granted": "許可",
|
||||
"sensors_status_denied": "拒否",
|
||||
"sensors_status_unknown": "不明"
|
||||
}
|
170
messages/ukr.json
Normal file
170
messages/ukr.json
Normal file
|
@ -0,0 +1,170 @@
|
|||
{
|
||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||
"search": "Пошук",
|
||||
"tests_audio_description": "Перевірте свої стереоканали або об'ємний аудіовихід, перевірте, чи ваші динаміки у фазі.",
|
||||
"tests_audio_label": "Аудіо",
|
||||
"tests_av-sync_description": "Перевірте, чи синхронізовані аудіо та відео, та виміряйте затримку.",
|
||||
"tests_av-sync_label": "Синхронізація аудіо/відео",
|
||||
"tests_card_description": "Тестова картка для вашого дисплея або проектора, перевірте кольори, роздільну здатність та геометрію.",
|
||||
"tests_card_label": "Картка",
|
||||
"tests_camera_description": "Перевірте, чи працює ваша веб-камера або пристрій захоплення, її якість зображення, роздільну здатність та частоту кадрів. Зробіть знімок.",
|
||||
"tests_camera_label": "Камера",
|
||||
"tests_gamepad_description": "Перевірте свій геймпад, перевірте, чи він працює, всі кнопки та джойстики, дрейф стіків, мертві зони та калібрування.",
|
||||
"tests_gamepad_label": "Геймпад",
|
||||
"tests_keyboard_description": "Перевірте, чи всі клавіші працюють і які коди клавіш вони надсилають.",
|
||||
"tests_keyboard_label": "Клавіатура",
|
||||
"tests_microphone_description": "Перевірте, чи працює ваш мікрофон, його якість, гучність та шум.",
|
||||
"tests_microphone_label": "Мікрофон",
|
||||
"tests_mouse_description": "Перевірте, чи правильно працює ваша миша або сенсорний пристрій, чи є мертві зони або тремтіння.",
|
||||
"tests_mouse_label": "Миша",
|
||||
"tests_sensors_description": "Перегляньте вивід датчиків вашого пристрою, наприклад, GPS, акселерометр, гіроскоп, компас тощо.",
|
||||
"tests_sensors_label": "Датчики",
|
||||
"tests_internet_description": "Виміряйте швидкість та затримку вашого Інтернету.",
|
||||
"tests_internet_label": "Швидкість Інтернету",
|
||||
"tests_timer_description": "Перевірте, чи працює ваш таймер високої роздільної здатності.",
|
||||
"tests_timer_label": "Таймер високої роздільної здатності",
|
||||
"category_inputs": "Входи",
|
||||
"category_outputs": "Виходи",
|
||||
"category_audio": "Аудіо",
|
||||
"category_video": "Відео",
|
||||
"category_control": "Управління",
|
||||
"category_misc": "Різне",
|
||||
"noTestsFound": "Тестів не знайдено.",
|
||||
"camera_title": "Тест камери",
|
||||
"camera_device": "Пристрій",
|
||||
"camera_noCameraFound": "Камеру не знайдено",
|
||||
"camera_refresh": "Оновити",
|
||||
"camera_resolution": "Роздільна здатність",
|
||||
"camera_frameRate": "Частота кадрів",
|
||||
"camera_noCameraSelected": "Камеру не вибрано",
|
||||
"camera_takePicture": "Зробити знімок",
|
||||
"camera_unflipImage": "Повернути зображення",
|
||||
"camera_flipImage": "Перевернути зображення",
|
||||
"camera_closeSnapshot": "Закрити знімок",
|
||||
"audio_channel_frontLeft": "Передній лівий",
|
||||
"audio_channel_frontCenter": "Передній центральний",
|
||||
"audio_channel_frontRight": "Передній правий",
|
||||
"audio_channel_sideLeft": "Бічний лівий",
|
||||
"audio_channel_sideRight": "Бічний правий",
|
||||
"audio_channel_rearLeft": "Задній лівий",
|
||||
"audio_channel_rearRight": "Задній правий",
|
||||
"audio_channel_lfe": "LFE",
|
||||
"gamepad_title": "Тести геймпада та джойстика",
|
||||
"gamepad_device": "Пристрій",
|
||||
"gamepad_noGamepadsDetected": "Геймпади не виявлено. (Спробуйте натиснути кнопку)",
|
||||
"gamepad_refresh": "Оновити",
|
||||
"gamepad_buttons": "Кнопки",
|
||||
"gamepad_axes": "Осі",
|
||||
"gamepad_history": "Історія",
|
||||
"audio_channelTests": "Тести каналів",
|
||||
"audio_stereo": "Стерео",
|
||||
"audio_surroundAudio": "Об'ємний звук",
|
||||
"audio_surround51": "5.1 Об'ємний",
|
||||
"audio_surround71": "7.1 Об'ємний",
|
||||
"audio_phaseTest": "Тест фази",
|
||||
"audio_frequency": "Частота",
|
||||
"audio_inPhase": "У фазі",
|
||||
"audio_outOfPhase": "Поза фазою",
|
||||
"audio_stop": "Зупинити",
|
||||
"screenInfo_screenResolution": "Роздільна здатність екрана",
|
||||
"screenInfo_windowResolution": "Роздільна здатність вікна",
|
||||
"screenInfo_devicePixelRatio": "Співвідношення пікселів пристрою",
|
||||
"audio_channel_left": "Лівий",
|
||||
"audio_channel_center": "Центр",
|
||||
"audio_channel_right": "Правий",
|
||||
"keyboard_title": "Тест клавіатури",
|
||||
"keyboard_instruction": "Натисніть клавішу на клавіатурі, щоб побачити об'єкт події та код клавіші.",
|
||||
"keyboard_pressedKeys": "Натиснуті клавіші:",
|
||||
"timer_title": "Таймер високої роздільної здатності",
|
||||
"timer_fps": "FPS",
|
||||
"timer_restart": "Перезапустити",
|
||||
"audio_stopCycling": "Зупинити цикл",
|
||||
"audio_cycleThrough": "Перебирати",
|
||||
"common_back": "Назад",
|
||||
"audio_title": "Тест аудіо",
|
||||
"avSync_title": "Синхронізація аудіо/відео",
|
||||
"internet_title": "Швидкість Інтернету",
|
||||
"tests_signal-generator_description": "Генеруйте синусоїди, шум (білий, рожевий, коричневий) та частотні розгортки. Включає осцилограф та спектр.",
|
||||
"tests_signal-generator_label": "Генератор сигналів",
|
||||
"signalGen_title": "Генератор сигналів",
|
||||
"signalGen_type": "Тип",
|
||||
"signalGen_sine": "Синусоїда",
|
||||
"signalGen_sweep": "Розгортка",
|
||||
"signalGen_noiseWhite": "Білий шум",
|
||||
"signalGen_noisePink": "Рожевий шум",
|
||||
"signalGen_noiseBrown": "Коричневий шум",
|
||||
"signalGen_frequency": "Частота",
|
||||
"signalGen_from": "Від",
|
||||
"signalGen_to": "До",
|
||||
"signalGen_duration": "Тривалість",
|
||||
"signalGen_gain": "Гучність",
|
||||
"signalGen_start": "Старт",
|
||||
"signalGen_stop": "Стоп",
|
||||
"signalGen_scope": "Осцилограф",
|
||||
"signalGen_spectrum": "Спектр",
|
||||
"signalGen_loop": "Цикл",
|
||||
"mic_title": "Тест мікрофона",
|
||||
"mic_startMicrophone": "Почати мікрофон",
|
||||
"mic_stop": "Зупинити",
|
||||
"mic_monitoringOn": "Моніторинг: УВІМК",
|
||||
"mic_monitoringOff": "Моніторинг: ВИМК",
|
||||
"mic_gain": "Посилення",
|
||||
"mic_monitorDelay": "Затримка монітора",
|
||||
"mic_sampleRate": "Частота дискретизації",
|
||||
"mic_inputDevice": "Пристрій введення",
|
||||
"mic_volume": "Гучність",
|
||||
"mic_recording": "Запис",
|
||||
"mic_startRecording": "Почати запис",
|
||||
"mic_stopRecording": "Зупинити запис",
|
||||
"mic_downloadRecording": "Завантажити запис",
|
||||
"mic_device": "Пристрій",
|
||||
"mic_noMicFound": "Мікрофон не знайдено",
|
||||
"mic_refresh": "Оновити",
|
||||
"mic_clipping": "Відсікання",
|
||||
"mic_constraints": "Обмеження",
|
||||
"mic_echoCancellation": "Скасування відлуння",
|
||||
"mic_noiseSuppression": "Придушення шуму",
|
||||
"mic_agc": "Автоматичне регулювання посилення",
|
||||
"mic_applyConstraints": "Застосувати",
|
||||
"mic_channels": "Канали",
|
||||
"mic_stereo": "Стерео",
|
||||
"mic_requested": "Запитано",
|
||||
"mic_obtained": "Отримано",
|
||||
"mic_peakNow": "Пік",
|
||||
"mic_peakHold": "Утримання піку",
|
||||
"mic_resetPeaks": "Скинути піки",
|
||||
"mic_advanced": "Розширений",
|
||||
"mic_default": "За замовчуванням",
|
||||
"mic_on": "Увімкнено",
|
||||
"mic_off": "Вимкнено",
|
||||
"mic_mono": "Моно",
|
||||
"sensors_title": "Датчики",
|
||||
"sensors_geolocation": "Геолокація",
|
||||
"sensors_start": "Старт",
|
||||
"sensors_stop": "Стоп",
|
||||
"sensors_accuracy": "Точність (м)",
|
||||
"sensors_altitude": "Висота (м)",
|
||||
"sensors_heading": "Напрямок (град)",
|
||||
"sensors_speed": "Швидкість (м/с)",
|
||||
"sensors_timestamp": "Часова мітка",
|
||||
"sensors_copy": "Копіювати JSON",
|
||||
"sensors_copied": "Скопійовано в буфер обміну",
|
||||
"sensors_notSupported": "Не підтримується на цьому пристрої/браузері",
|
||||
"sensors_deviceMotion": "Рух пристрою",
|
||||
"sensors_deviceOrientation": "Орієнтація пристрою",
|
||||
"sensors_accelerometer": "Акселерометр",
|
||||
"sensors_gyroscope": "Гіроскоп",
|
||||
"sensors_magnetometer": "Магнітометр",
|
||||
"sensors_ambientLight": "Освітленість",
|
||||
"sensors_illuminance": "Освітленість (люкс)",
|
||||
"sensors_barometer": "Барометр",
|
||||
"sensors_pressure": "Тиск (гПа)",
|
||||
"sensors_temperature": "Температура (°C)",
|
||||
"sensors_permissions": "Дозволи",
|
||||
"sensors_enableMotionOrientation": "Увімкнути рух/орієнтацію",
|
||||
"sensors_motion": "Рух",
|
||||
"sensors_orientation": "Орієнтація",
|
||||
"sensors_status_granted": "Надано",
|
||||
"sensors_status_denied": "Відмовлено",
|
||||
"sensors_status_unknown": "Невідомо"
|
||||
}
|
170
messages/zh-CN.json
Normal file
170
messages/zh-CN.json
Normal file
|
@ -0,0 +1,170 @@
|
|||
{
|
||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||
"search": "搜索",
|
||||
"tests_audio_description": "检查您的立体声通道或环绕声音频输出,验证您的扬声器是否同相。",
|
||||
"tests_audio_label": "音频",
|
||||
"tests_av-sync_description": "检查您的音频和视频是否同步,并测量延迟。",
|
||||
"tests_av-sync_label": "音视频同步",
|
||||
"tests_card_description": "用于您的显示器或投影仪的测试卡,检查颜色、分辨率和几何形状。",
|
||||
"tests_card_label": "测试卡",
|
||||
"tests_camera_description": "检查您的网络摄像头或采集设备是否正常工作,其图像质量、分辨率和帧率。拍摄快照。",
|
||||
"tests_camera_label": "摄像头",
|
||||
"tests_gamepad_description": "测试您的游戏手柄,检查其是否正常工作,所有按钮和摇杆,摇杆漂移,死区和校准。",
|
||||
"tests_gamepad_label": "游戏手柄",
|
||||
"tests_keyboard_description": "检查所有按键是否正常工作以及它们发送的键码。",
|
||||
"tests_keyboard_label": "键盘",
|
||||
"tests_microphone_description": "检查您的麦克风是否正常工作,其质量、音量和噪音。",
|
||||
"tests_microphone_label": "麦克风",
|
||||
"tests_mouse_description": "检查您的鼠标或触摸设备是否正常工作,是否存在死区或抖动。",
|
||||
"tests_mouse_label": "鼠标",
|
||||
"tests_sensors_description": "查看您设备的传感器输出,例如GPS、加速度计、陀螺仪、指南针等。",
|
||||
"tests_sensors_label": "传感器",
|
||||
"tests_internet_description": "测量您的互联网速度和延迟。",
|
||||
"tests_internet_label": "网速",
|
||||
"tests_timer_description": "检查您的高分辨率计时器是否正常工作。",
|
||||
"tests_timer_label": "高分辨率计时器",
|
||||
"category_inputs": "输入",
|
||||
"category_outputs": "输出",
|
||||
"category_audio": "音频",
|
||||
"category_video": "视频",
|
||||
"category_control": "控制",
|
||||
"category_misc": "杂项",
|
||||
"noTestsFound": "未找到测试。",
|
||||
"camera_title": "摄像头测试",
|
||||
"camera_device": "设备",
|
||||
"camera_noCameraFound": "未找到摄像头",
|
||||
"camera_refresh": "刷新",
|
||||
"camera_resolution": "分辨率",
|
||||
"camera_frameRate": "帧率",
|
||||
"camera_noCameraSelected": "未选择摄像头",
|
||||
"camera_takePicture": "拍照",
|
||||
"camera_unflipImage": "取消翻转图像",
|
||||
"camera_flipImage": "翻转图像",
|
||||
"camera_closeSnapshot": "关闭快照",
|
||||
"audio_channel_frontLeft": "左前置",
|
||||
"audio_channel_frontCenter": "中前置",
|
||||
"audio_channel_frontRight": "右前置",
|
||||
"audio_channel_sideLeft": "左侧",
|
||||
"audio_channel_sideRight": "右侧",
|
||||
"audio_channel_rearLeft": "左后置",
|
||||
"audio_channel_rearRight": "右后置",
|
||||
"audio_channel_lfe": "LFE",
|
||||
"gamepad_title": "游戏手柄和摇杆测试",
|
||||
"gamepad_device": "设备",
|
||||
"gamepad_noGamepadsDetected": "未检测到游戏手柄。(请尝试按下一个按钮)",
|
||||
"gamepad_refresh": "刷新",
|
||||
"gamepad_buttons": "按钮",
|
||||
"gamepad_axes": "轴",
|
||||
"gamepad_history": "历史",
|
||||
"audio_channelTests": "声道测试",
|
||||
"audio_stereo": "立体声",
|
||||
"audio_surroundAudio": "环绕声",
|
||||
"audio_surround51": "5.1环绕声",
|
||||
"audio_surround71": "7.1环绕声",
|
||||
"audio_phaseTest": "相位测试",
|
||||
"audio_frequency": "频率",
|
||||
"audio_inPhase": "同相",
|
||||
"audio_outOfPhase": "异相",
|
||||
"audio_stop": "停止",
|
||||
"screenInfo_screenResolution": "屏幕分辨率",
|
||||
"screenInfo_windowResolution": "窗口分辨率",
|
||||
"screenInfo_devicePixelRatio": "设备像素比",
|
||||
"audio_channel_left": "左",
|
||||
"audio_channel_center": "中",
|
||||
"audio_channel_right": "右",
|
||||
"keyboard_title": "键盘测试",
|
||||
"keyboard_instruction": "按键盘上的一个键以查看事件对象和键码。",
|
||||
"keyboard_pressedKeys": "按下的键:",
|
||||
"timer_title": "高分辨率计时器",
|
||||
"timer_fps": "FPS",
|
||||
"timer_restart": "重新开始",
|
||||
"audio_stopCycling": "停止循环",
|
||||
"audio_cycleThrough": "循环浏览",
|
||||
"common_back": "返回",
|
||||
"audio_title": "音频测试",
|
||||
"avSync_title": "音视频同步",
|
||||
"internet_title": "网速",
|
||||
"tests_signal-generator_description": "生成正弦波、噪声(白噪声、粉红噪声、布朗噪声)和频率扫描。包括示波器和频谱图。",
|
||||
"tests_signal-generator_label": "信号发生器",
|
||||
"signalGen_title": "信号发生器",
|
||||
"signalGen_type": "类型",
|
||||
"signalGen_sine": "正弦波",
|
||||
"signalGen_sweep": "扫描",
|
||||
"signalGen_noiseWhite": "白噪声",
|
||||
"signalGen_noisePink": "粉红噪声",
|
||||
"signalGen_noiseBrown": "布朗噪声",
|
||||
"signalGen_frequency": "频率",
|
||||
"signalGen_from": "从",
|
||||
"signalGen_to": "到",
|
||||
"signalGen_duration": "持续时间",
|
||||
"signalGen_gain": "音量",
|
||||
"signalGen_start": "开始",
|
||||
"signalGen_stop": "停止",
|
||||
"signalGen_scope": "示波器",
|
||||
"signalGen_spectrum": "频谱图",
|
||||
"signalGen_loop": "循环",
|
||||
"mic_title": "麦克风测试",
|
||||
"mic_startMicrophone": "开始麦克风",
|
||||
"mic_stop": "停止",
|
||||
"mic_monitoringOn": "监听:开",
|
||||
"mic_monitoringOff": "监听:关",
|
||||
"mic_gain": "增益",
|
||||
"mic_monitorDelay": "监听延迟",
|
||||
"mic_sampleRate": "采样率",
|
||||
"mic_inputDevice": "输入设备",
|
||||
"mic_volume": "音量",
|
||||
"mic_recording": "录音",
|
||||
"mic_startRecording": "开始录音",
|
||||
"mic_stopRecording": "停止录音",
|
||||
"mic_downloadRecording": "下载录音",
|
||||
"mic_device": "设备",
|
||||
"mic_noMicFound": "未找到麦克风",
|
||||
"mic_refresh": "刷新",
|
||||
"mic_clipping": "削波",
|
||||
"mic_constraints": "约束",
|
||||
"mic_echoCancellation": "回声消除",
|
||||
"mic_noiseSuppression": "噪声抑制",
|
||||
"mic_agc": "自动增益控制",
|
||||
"mic_applyConstraints": "应用",
|
||||
"mic_channels": "声道",
|
||||
"mic_stereo": "立体声",
|
||||
"mic_requested": "已请求",
|
||||
"mic_obtained": "已获取",
|
||||
"mic_peakNow": "峰值",
|
||||
"mic_peakHold": "峰值保持",
|
||||
"mic_resetPeaks": "重置峰值",
|
||||
"mic_advanced": "高级",
|
||||
"mic_default": "默认",
|
||||
"mic_on": "开",
|
||||
"mic_off": "关",
|
||||
"mic_mono": "单声道",
|
||||
"sensors_title": "传感器",
|
||||
"sensors_geolocation": "地理位置",
|
||||
"sensors_start": "开始",
|
||||
"sensors_stop": "停止",
|
||||
"sensors_accuracy": "精度 (m)",
|
||||
"sensors_altitude": "高度 (m)",
|
||||
"sensors_heading": "方向 (度)",
|
||||
"sensors_speed": "速度 (m/s)",
|
||||
"sensors_timestamp": "时间戳",
|
||||
"sensors_copy": "复制JSON",
|
||||
"sensors_copied": "已复制到剪贴板",
|
||||
"sensors_notSupported": "此设备/浏览器不支持",
|
||||
"sensors_deviceMotion": "设备运动",
|
||||
"sensors_deviceOrientation": "设备方向",
|
||||
"sensors_accelerometer": "加速度计",
|
||||
"sensors_gyroscope": "陀螺仪",
|
||||
"sensors_magnetometer": "磁力计",
|
||||
"sensors_ambientLight": "环境光",
|
||||
"sensors_illuminance": "照度 (lux)",
|
||||
"sensors_barometer": "气压计",
|
||||
"sensors_pressure": "压力 (hPa)",
|
||||
"sensors_temperature": "温度 (°C)",
|
||||
"sensors_permissions": "权限",
|
||||
"sensors_enableMotionOrientation": "启用运动/方向",
|
||||
"sensors_motion": "运动",
|
||||
"sensors_orientation": "方向",
|
||||
"sensors_status_granted": "已授予",
|
||||
"sensors_status_denied": "已拒绝",
|
||||
"sensors_status_unknown": "未知"
|
||||
}
|
3530
pnpm-lock.yaml
generated
3530
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
1
project.inlang/.gitignore
vendored
Normal file
1
project.inlang/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
cache
|
|
@ -8,5 +8,5 @@
|
|||
"pathPattern": "./messages/{locale}.json"
|
||||
},
|
||||
"baseLocale": "en",
|
||||
"locales": ["en"]
|
||||
"locales": ["en", "es", "fr", "de", "zh-CN", "ja", "cs", "ukr"]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
const START_COUNT = 27;
|
||||
const MAX_COUNT = 33;
|
||||
|
@ -19,9 +18,16 @@
|
|||
interface Props {
|
||||
transparent?: boolean;
|
||||
subdued?: boolean;
|
||||
onchange?: (detail: {
|
||||
horizontalCount: number;
|
||||
verticalCount: number;
|
||||
blockSize: number;
|
||||
horizontalMargin: number;
|
||||
verticalMargin: number;
|
||||
}) => void;
|
||||
}
|
||||
|
||||
let { transparent = false, subdued = false }: Props = $props();
|
||||
let { transparent = false, subdued = false, onchange }: Props = $props();
|
||||
|
||||
function updateCounts() {
|
||||
const gridWidth = window.innerWidth - MARGIN_SIZE;
|
||||
|
@ -56,7 +62,7 @@
|
|||
horizontalMargin = (window.innerWidth - blockSize * horizontalCount) / 2;
|
||||
verticalMargin = (window.innerHeight - blockSize * verticalCount) / 2;
|
||||
cornerBlocks = shorterCount > 8 ? 3 : Math.max(1, Math.floor(shorterCount / 4));
|
||||
dispatch('change', {
|
||||
onchange?.({
|
||||
horizontalCount,
|
||||
verticalCount,
|
||||
blockSize,
|
||||
|
|
|
@ -5,14 +5,13 @@
|
|||
import ColorGradient from '$lib/ColorGradient.svelte';
|
||||
import BrightnessGradient from '$lib/BrightnessGradient.svelte';
|
||||
import Clock from '$lib/Clock.svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher<{ focus: void }>();
|
||||
|
||||
interface Props {
|
||||
bg?: boolean;
|
||||
onfocus?: () => void;
|
||||
}
|
||||
|
||||
let { bg = false }: Props = $props();
|
||||
let { bg = false, onfocus }: Props = $props();
|
||||
|
||||
let sizes = $state({
|
||||
blockSize: 64,
|
||||
|
@ -44,9 +43,9 @@
|
|||
--column-width: {columnWidth};
|
||||
--column-height: {columnHeight};
|
||||
--left-column: {leftColumn};"
|
||||
ondblclick={() => dispatch('focus') && document.body.requestFullscreen()}
|
||||
ondblclick={() => onfocus?.() && document.body.requestFullscreen()}
|
||||
>
|
||||
<BackgroundGrid on:change={(ev) => (sizes = ev.detail)} subdued={bg} />
|
||||
<BackgroundGrid onchange={(detail) => (sizes = detail)} subdued={bg} />
|
||||
|
||||
<div class="axes">
|
||||
<Axes />
|
||||
|
|
86
src/lib/audio/Oscilloscope.svelte
Normal file
86
src/lib/audio/Oscilloscope.svelte
Normal file
|
@ -0,0 +1,86 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
analyser: AnalyserNode;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
let { analyser, title = 'Oscilloscope' }: Props = $props();
|
||||
|
||||
let canvas: HTMLCanvasElement | null = null;
|
||||
let animationId: number | null = null;
|
||||
|
||||
function draw() {
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
const dpr = Math.max(1, window.devicePixelRatio || 1);
|
||||
const w = canvas.clientWidth * dpr;
|
||||
const h = canvas.clientHeight * dpr;
|
||||
if (canvas.width !== w || canvas.height !== h) {
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
}
|
||||
|
||||
const bufferLength = analyser.fftSize;
|
||||
const dataArray = new Uint8Array(bufferLength);
|
||||
|
||||
analyser.getByteTimeDomainData(dataArray);
|
||||
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
ctx.lineWidth = 2 * dpr;
|
||||
ctx.strokeStyle = '#5cb85c';
|
||||
ctx.beginPath();
|
||||
|
||||
const sliceWidth = w / bufferLength;
|
||||
let x = 0;
|
||||
for (let i = 0; i < bufferLength; i++) {
|
||||
const v = dataArray[i] / 128.0; // 0..255 -> 0..2
|
||||
const y = (v * h) / 2;
|
||||
if (i === 0) {
|
||||
ctx.moveTo(x, y);
|
||||
} else {
|
||||
ctx.lineTo(x, y);
|
||||
}
|
||||
x += sliceWidth;
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
animationId = requestAnimationFrame(draw);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
analyser.fftSize = 2048;
|
||||
analyser.smoothingTimeConstant = 0.2;
|
||||
animationId = requestAnimationFrame(draw);
|
||||
return () => {
|
||||
if (animationId) cancelAnimationFrame(animationId);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="panel">
|
||||
<div class="title">{title}</div>
|
||||
<canvas bind:this={canvas} class="scope"></canvas>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.title {
|
||||
opacity: 0.85;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.scope {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
170
src/lib/audio/Spectrum.svelte
Normal file
170
src/lib/audio/Spectrum.svelte
Normal file
|
@ -0,0 +1,170 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
analyser: AnalyserNode;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
let { analyser, title = 'Spectrum' }: Props = $props();
|
||||
|
||||
let canvas: HTMLCanvasElement | null = null;
|
||||
let animationId: number | null = null;
|
||||
|
||||
// Format frequency values nicely for axis labels
|
||||
function formatHz(f: number): string {
|
||||
if (f >= 1000) {
|
||||
const k = f / 1000;
|
||||
// Show at most one decimal place when needed
|
||||
return `${k % 1 === 0 ? k.toFixed(0) : k.toFixed(1)} kHz`;
|
||||
}
|
||||
return `${Math.round(f)} Hz`;
|
||||
}
|
||||
|
||||
// Generate 1-2-5 decade ticks up to Nyquist
|
||||
function generateFreqTicks(maxF: number): number[] {
|
||||
const ticks: number[] = [];
|
||||
const bases = [1, 2, 5];
|
||||
const maxExp = Math.floor(Math.log10(Math.max(1, maxF)));
|
||||
for (let e = 0; e <= maxExp; e++) {
|
||||
const pow = Math.pow(10, e);
|
||||
for (const b of bases) {
|
||||
const f = b * pow;
|
||||
if (f >= 20 && f <= maxF) ticks.push(f);
|
||||
}
|
||||
}
|
||||
// Ensure sorted unique ticks
|
||||
return [...new Set(ticks)].sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
function draw() {
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
const dpr = Math.max(1, window.devicePixelRatio || 1);
|
||||
const w = canvas.clientWidth * dpr;
|
||||
const h = canvas.clientHeight * dpr;
|
||||
if (canvas.width !== w || canvas.height !== h) {
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
}
|
||||
|
||||
const bufferLength = analyser.frequencyBinCount;
|
||||
const dataArray = new Uint8Array(bufferLength);
|
||||
analyser.getByteFrequencyData(dataArray);
|
||||
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
|
||||
// Layout for axis and grid
|
||||
const sampleRate = analyser.context.sampleRate;
|
||||
const nyquist = sampleRate / 2;
|
||||
const axisPad = 24 * dpr; // space reserved at bottom for tick labels
|
||||
const plotTop = 0;
|
||||
const plotBottom = h - axisPad;
|
||||
const plotH = Math.max(1, plotBottom - plotTop);
|
||||
|
||||
ctx.fillStyle = 'rgba(92, 184, 92, 0.25)';
|
||||
ctx.strokeStyle = '#5cb85c';
|
||||
ctx.lineWidth = 2 * dpr;
|
||||
ctx.beginPath();
|
||||
|
||||
const barWidth = w / bufferLength;
|
||||
let x = 0;
|
||||
for (let i = 0; i < bufferLength; i++) {
|
||||
const v = dataArray[i] / 255.0;
|
||||
const y = plotBottom - v * plotH;
|
||||
if (i === 0) {
|
||||
ctx.moveTo(x, y);
|
||||
} else {
|
||||
ctx.lineTo(x, y);
|
||||
}
|
||||
x += barWidth;
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
// Draw frequency axis, gridlines, and labels
|
||||
ctx.save();
|
||||
const gridColor = 'rgba(255, 255, 255, 0.15)';
|
||||
const labelColor = 'rgba(255, 255, 255, 0.8)';
|
||||
// Baseline
|
||||
ctx.strokeStyle = gridColor;
|
||||
ctx.lineWidth = 1 * dpr;
|
||||
ctx.setLineDash([]);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, plotBottom);
|
||||
ctx.lineTo(w, plotBottom);
|
||||
ctx.stroke();
|
||||
|
||||
// Ticks
|
||||
const ticks = generateFreqTicks(nyquist);
|
||||
const tickLen = 6 * dpr;
|
||||
ctx.font = `${Math.round(11 * dpr)}px system-ui, -apple-system, Segoe UI, Roboto, sans-serif`;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'top';
|
||||
let lastLabelX = -Infinity;
|
||||
const minLabelSpacing = 60 * dpr;
|
||||
|
||||
for (const f of ticks) {
|
||||
const xTick = (f / nyquist) * w;
|
||||
// Gridline
|
||||
ctx.strokeStyle = gridColor;
|
||||
ctx.lineWidth = 1 * dpr;
|
||||
ctx.setLineDash([2 * dpr, 4 * dpr]);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(xTick, plotTop);
|
||||
ctx.lineTo(xTick, plotBottom);
|
||||
ctx.stroke();
|
||||
|
||||
// Tick mark at the axis
|
||||
ctx.setLineDash([]);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(xTick, plotBottom);
|
||||
ctx.lineTo(xTick, plotBottom + tickLen);
|
||||
ctx.stroke();
|
||||
|
||||
// Label if there's room
|
||||
if (xTick - lastLabelX >= minLabelSpacing) {
|
||||
ctx.fillStyle = labelColor;
|
||||
ctx.fillText(formatHz(f), xTick, plotBottom + tickLen + 2 * dpr);
|
||||
lastLabelX = xTick;
|
||||
}
|
||||
}
|
||||
ctx.restore();
|
||||
|
||||
animationId = requestAnimationFrame(draw);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
analyser.fftSize = 2048;
|
||||
analyser.smoothingTimeConstant = 0.8;
|
||||
animationId = requestAnimationFrame(draw);
|
||||
return () => {
|
||||
if (animationId) cancelAnimationFrame(animationId);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="panel">
|
||||
<div class="title">{title}</div>
|
||||
<canvas bind:this={canvas} class="spectrum"></canvas>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.title {
|
||||
opacity: 0.85;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.spectrum {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
|
@ -10,7 +10,7 @@
|
|||
let { children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<TestCard bg on:focus={() => goto('/card')} />
|
||||
<TestCard bg onfocus={() => goto('/card')} />
|
||||
<main class:sub={!page.data.root}>
|
||||
<a href=".." class="button button-back"><i class="ti ti-arrow-back"></i>{m.common_back()}</a>
|
||||
{@render children?.()}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import { version } from '../../../package.json';
|
||||
import type { Snapshot } from '@sveltejs/kit';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { onMount } from 'svelte';
|
||||
const buildDate = import.meta.env.VITE_BUILD_DATE || '???';
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
import { setLocale } from '$lib/paraglide/runtime';
|
||||
|
||||
let search = $state('');
|
||||
|
||||
|
@ -62,6 +62,11 @@
|
|||
icon: 'ti-volume',
|
||||
categories: ['outputs', 'audio']
|
||||
},
|
||||
{
|
||||
id: 'signal-generator',
|
||||
icon: 'ti-wave-sine',
|
||||
categories: ['outputs', 'audio']
|
||||
},
|
||||
{
|
||||
id: 'av-sync',
|
||||
icon: 'ti-time-duration-off',
|
||||
|
@ -90,14 +95,12 @@
|
|||
{
|
||||
id: 'microphone',
|
||||
icon: 'ti-microphone',
|
||||
categories: ['inputs', 'audio'],
|
||||
disabled: true
|
||||
categories: ['inputs', 'audio']
|
||||
},
|
||||
{
|
||||
id: 'sensors',
|
||||
icon: 'ti-cpu-2',
|
||||
categories: ['inputs', 'misc'],
|
||||
disabled: true
|
||||
categories: ['inputs', 'misc']
|
||||
},
|
||||
{
|
||||
id: 'internet',
|
||||
|
@ -134,7 +137,7 @@
|
|||
);
|
||||
});
|
||||
}
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
doSearch(search);
|
||||
});
|
||||
|
||||
|
@ -167,10 +170,50 @@
|
|||
search = restoredSearch;
|
||||
}
|
||||
};
|
||||
|
||||
// Locale selection (cookie-based persistence)
|
||||
type LocaleOption = { tag: string; native: string };
|
||||
const locales = [
|
||||
{ tag: 'en', native: 'English' },
|
||||
{ tag: 'es', native: 'Español' },
|
||||
{ tag: 'fr', native: 'Français' },
|
||||
{ tag: 'de', native: 'Deutsch' },
|
||||
{ tag: 'zh-CN', native: '简体中文' },
|
||||
{ tag: 'ja', native: '日本語' },
|
||||
{ tag: 'cs', native: 'Čeština' },
|
||||
{ tag: 'ukr', native: 'Українська' }
|
||||
] as const satisfies LocaleOption[];
|
||||
type Locale = (typeof locales)[number]['tag'];
|
||||
|
||||
let selectedLang = $state('en');
|
||||
|
||||
onMount(() => {
|
||||
const current = document.documentElement.getAttribute('lang') || selectedLang;
|
||||
selectedLang = locales.some((l) => l.tag === current) ? current : selectedLang;
|
||||
});
|
||||
|
||||
function changeLocale(tag: string) {
|
||||
if (locales.some((l) => l.tag === tag)) {
|
||||
setLocale(tag as Locale);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<h1>Total Tech Test</h1>
|
||||
|
||||
<div class="locale-select">
|
||||
<label for="locale-picker"><i class="ti ti-language"></i></label>
|
||||
<select
|
||||
id="locale-picker"
|
||||
bind:value={selectedLang}
|
||||
onchange={(e) => changeLocale((e.target as HTMLSelectElement).value)}
|
||||
>
|
||||
{#each locales as l}
|
||||
<option value={l.tag}>{l.native}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<!-- svelte-ignore a11y_autofocus -->
|
||||
<input type="search" placeholder={m.search()} bind:value={search} autofocus />
|
||||
|
@ -292,23 +335,36 @@
|
|||
opacity: 0.8;
|
||||
}
|
||||
|
||||
& a {
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
& .ti {
|
||||
display: block;
|
||||
font-size: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.locale-select {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
align-self: flex-end;
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
top: 1rem;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.locale-select:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.locale-select select {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
color: inherit;
|
||||
border: 1px solid currentColor;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.test {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
import debug from 'debug';
|
||||
|
@ -20,10 +18,10 @@
|
|||
let snapshot: string | undefined = $state();
|
||||
let flipped = $state(false);
|
||||
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
dbg('devices %O', devices);
|
||||
});
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
dbg('currentDevice %s', currentDevice);
|
||||
});
|
||||
|
||||
|
@ -54,7 +52,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
if (currentDevice) {
|
||||
navigator.mediaDevices
|
||||
.getUserMedia({
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
import debug from 'debug';
|
||||
|
@ -60,20 +58,20 @@
|
|||
requestAnimationFrame(update);
|
||||
}
|
||||
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
if (currentGamepad) {
|
||||
update();
|
||||
}
|
||||
});
|
||||
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
dbg('Gamepads %O', gamepads);
|
||||
});
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
dbg('Current gamepad %s', currentGamepad);
|
||||
});
|
||||
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
currentGamepad?.vibrationActuator?.playEffect('dual-rumble', {
|
||||
duration: 1000
|
||||
});
|
||||
|
|
|
@ -0,0 +1,622 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import Spectrum from '$lib/audio/Spectrum.svelte';
|
||||
import Oscilloscope from '$lib/audio/Oscilloscope.svelte';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
|
||||
// Mic / audio graph state
|
||||
let audioCtx: AudioContext | null = null;
|
||||
let mediaStream: MediaStream | null = null;
|
||||
let source: MediaStreamAudioSourceNode | null = null;
|
||||
let analyserTime: AnalyserNode | null = $state(null);
|
||||
let analyserFreq: AnalyserNode | null = $state(null);
|
||||
let monitorGain: GainNode | null = null;
|
||||
let delayNode: DelayNode | null = null;
|
||||
|
||||
// UI state (Svelte 5 runes-style)
|
||||
let micActive = $state(false);
|
||||
let monitoring = $state(false);
|
||||
let gain = $state(1.0);
|
||||
let delayMs = $state(0);
|
||||
let sampleRate = $state<number | null>(null);
|
||||
let deviceLabel = $state<string>('');
|
||||
|
||||
// Volume meter (RMS 0..1)
|
||||
let volume = $state(0);
|
||||
let rafId: number | null = null;
|
||||
// Peak metrics
|
||||
let peak = $state(0); // instantaneous peak 0..1
|
||||
let peakHold = $state(0); // hold 0..1
|
||||
let peakHoldDecayPerSec = 0.5; // how fast the hold marker decays
|
||||
|
||||
// Recording
|
||||
let recorder: MediaRecorder | null = null;
|
||||
let isRecording = $state(false);
|
||||
let recordedChunks: BlobPart[] = [];
|
||||
let recordingUrl = $state<string | null>(null);
|
||||
|
||||
// Devices
|
||||
let devices: MediaDeviceInfo[] = $state([]);
|
||||
let selectedDeviceId: string | null = $state(null);
|
||||
|
||||
// Clipping indicator
|
||||
let clipping = $state(false);
|
||||
let lastClipTs = 0;
|
||||
|
||||
// Track settings and constraints
|
||||
let channels = $state<number | null>(null);
|
||||
let obtainedEchoCancellation = $state<boolean | null>(null);
|
||||
let obtainedNoiseSuppression = $state<boolean | null>(null);
|
||||
let obtainedAGC = $state<boolean | null>(null);
|
||||
|
||||
// Requested constraints (best-effort, tri-state: null=default, true, false)
|
||||
let reqEchoCancellation = $state<boolean | null>(null);
|
||||
let reqNoiseSuppression = $state<boolean | null>(null);
|
||||
let reqAGC = $state<boolean | null>(null);
|
||||
|
||||
async function startMic() {
|
||||
if (micActive) return;
|
||||
try {
|
||||
const audioConstraints: MediaTrackConstraints = selectedDeviceId
|
||||
? { deviceId: { exact: selectedDeviceId } }
|
||||
: {};
|
||||
if (reqEchoCancellation !== null) audioConstraints.echoCancellation = reqEchoCancellation;
|
||||
if (reqNoiseSuppression !== null) audioConstraints.noiseSuppression = reqNoiseSuppression;
|
||||
if (reqAGC !== null) audioConstraints.autoGainControl = reqAGC;
|
||||
|
||||
const constraints: MediaStreamConstraints = {
|
||||
audio: Object.keys(audioConstraints).length ? audioConstraints : true,
|
||||
video: false
|
||||
};
|
||||
mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
deviceLabel = mediaStream.getAudioTracks()[0]?.label ?? '';
|
||||
|
||||
audioCtx = new AudioContext();
|
||||
sampleRate = audioCtx.sampleRate;
|
||||
|
||||
source = audioCtx.createMediaStreamSource(mediaStream);
|
||||
analyserTime = audioCtx.createAnalyser();
|
||||
analyserFreq = audioCtx.createAnalyser();
|
||||
monitorGain = audioCtx.createGain();
|
||||
delayNode = audioCtx.createDelay(2.0); // up to 2 seconds
|
||||
|
||||
// Default params
|
||||
analyserTime.fftSize = 2048;
|
||||
analyserTime.smoothingTimeConstant = 0.2;
|
||||
analyserFreq.fftSize = 2048;
|
||||
analyserFreq.smoothingTimeConstant = 0.8;
|
||||
monitorGain.gain.value = gain;
|
||||
delayNode.delayTime.value = delayMs / 1000;
|
||||
|
||||
// Fan-out: source -> (analysers)
|
||||
source.connect(analyserTime);
|
||||
source.connect(analyserFreq);
|
||||
|
||||
// Monitoring path (initially disconnected; toggleMonitoring() handles it)
|
||||
updateMonitoringGraph();
|
||||
|
||||
// Start volume meter loop
|
||||
startVolumeLoop();
|
||||
micActive = true;
|
||||
// Update device list after permission to get labels
|
||||
refreshDevices();
|
||||
|
||||
// Read obtained settings
|
||||
const track = mediaStream.getAudioTracks()[0];
|
||||
const s = track.getSettings?.() as MediaTrackSettings | undefined;
|
||||
channels = s?.channelCount ?? null;
|
||||
obtainedEchoCancellation = (s?.echoCancellation as boolean | undefined) ?? null;
|
||||
obtainedNoiseSuppression = (s?.noiseSuppression as boolean | undefined) ?? null;
|
||||
obtainedAGC = (s?.autoGainControl as boolean | undefined) ?? null;
|
||||
} catch (err) {
|
||||
console.error('Failed to start microphone', err);
|
||||
stopMic();
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshDevices() {
|
||||
try {
|
||||
const list = await navigator.mediaDevices.enumerateDevices();
|
||||
devices = list.filter((d) => d.kind === 'audioinput');
|
||||
if (devices.length > 0 && !selectedDeviceId) {
|
||||
selectedDeviceId = devices[0].deviceId;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('enumerateDevices failed', e);
|
||||
}
|
||||
}
|
||||
|
||||
function onDeviceChange(e: Event) {
|
||||
selectedDeviceId = (e.target as HTMLSelectElement).value || null;
|
||||
if (micActive) {
|
||||
// Restart mic with the new device
|
||||
const wasMonitoring = monitoring;
|
||||
stopMic();
|
||||
monitoring = wasMonitoring; // preserve desired state; will reconnect on start
|
||||
startMic();
|
||||
}
|
||||
}
|
||||
|
||||
function updateMonitoringGraph() {
|
||||
if (!audioCtx || !source || !monitorGain || !delayNode) return;
|
||||
|
||||
try {
|
||||
// Always disconnect monitoring path before reconnecting to avoid duplicate connections
|
||||
monitorGain.disconnect();
|
||||
delayNode.disconnect();
|
||||
} catch {}
|
||||
|
||||
if (monitoring) {
|
||||
// source -> monitorGain -> delayNode -> destination
|
||||
monitorGain.gain.value = gain;
|
||||
delayNode.delayTime.value = delayMs / 1000;
|
||||
|
||||
source.connect(monitorGain);
|
||||
monitorGain.connect(delayNode);
|
||||
delayNode.connect(audioCtx.destination);
|
||||
}
|
||||
}
|
||||
|
||||
function startVolumeLoop() {
|
||||
cancelVolumeLoop();
|
||||
const buf = new Uint8Array(analyserTime?.fftSize ?? 2048);
|
||||
const loop = () => {
|
||||
if (analyserTime) {
|
||||
analyserTime.getByteTimeDomainData(buf);
|
||||
// Compute RMS
|
||||
let sum = 0;
|
||||
let pk = 0;
|
||||
for (let i = 0; i < buf.length; i++) {
|
||||
const v = (buf[i] - 128) / 128; // -1..1
|
||||
sum += v * v;
|
||||
const a = Math.abs(v);
|
||||
if (a > pk) pk = a;
|
||||
}
|
||||
const rms = Math.sqrt(sum / buf.length);
|
||||
volume = rms; // 0..1
|
||||
peak = pk;
|
||||
// Update hold
|
||||
const dt = 1 / 60; // approx
|
||||
peakHold = Math.max(pk, Math.max(0, peakHold - peakHoldDecayPerSec * dt));
|
||||
// Clipping detection (very near full-scale)
|
||||
const now = performance.now();
|
||||
if (pk >= 0.985) {
|
||||
clipping = true;
|
||||
lastClipTs = now;
|
||||
} else if (clipping && now - lastClipTs > 500) {
|
||||
// Auto clear after 500ms without clipping
|
||||
clipping = false;
|
||||
}
|
||||
}
|
||||
rafId = requestAnimationFrame(loop);
|
||||
};
|
||||
rafId = requestAnimationFrame(loop);
|
||||
}
|
||||
|
||||
function cancelVolumeLoop() {
|
||||
if (rafId) cancelAnimationFrame(rafId);
|
||||
rafId = null;
|
||||
}
|
||||
|
||||
function stopMic() {
|
||||
cancelVolumeLoop();
|
||||
micActive = false;
|
||||
monitoring = false;
|
||||
|
||||
try {
|
||||
recorder?.state !== 'inactive' && recorder?.stop();
|
||||
} catch {}
|
||||
recorder = null;
|
||||
isRecording = false;
|
||||
|
||||
try {
|
||||
source?.disconnect();
|
||||
} catch {}
|
||||
try {
|
||||
monitorGain?.disconnect();
|
||||
} catch {}
|
||||
try {
|
||||
delayNode?.disconnect();
|
||||
} catch {}
|
||||
|
||||
analyserTime = null;
|
||||
analyserFreq = null;
|
||||
monitorGain = null;
|
||||
delayNode = null;
|
||||
source = null;
|
||||
|
||||
if (audioCtx) {
|
||||
audioCtx.close().catch(() => {});
|
||||
audioCtx = null;
|
||||
}
|
||||
|
||||
if (mediaStream) {
|
||||
for (const t of mediaStream.getTracks()) t.stop();
|
||||
mediaStream = null;
|
||||
}
|
||||
peak = 0;
|
||||
peakHold = 0;
|
||||
channels = null;
|
||||
obtainedEchoCancellation = obtainedNoiseSuppression = obtainedAGC = null;
|
||||
}
|
||||
|
||||
function toggleMonitoring() {
|
||||
monitoring = !monitoring;
|
||||
updateMonitoringGraph();
|
||||
}
|
||||
|
||||
function onGainChange(e: Event) {
|
||||
gain = Number((e.target as HTMLInputElement).value);
|
||||
if (monitorGain) monitorGain.gain.value = gain;
|
||||
}
|
||||
|
||||
function onDelayChange(e: Event) {
|
||||
delayMs = Number((e.target as HTMLInputElement).value);
|
||||
if (delayNode) delayNode.delayTime.value = delayMs / 1000;
|
||||
}
|
||||
|
||||
// Recording via MediaRecorder on raw MediaStream
|
||||
function startRecording() {
|
||||
if (!mediaStream || isRecording) return;
|
||||
try {
|
||||
// If there's an old blob URL, release it
|
||||
if (recordingUrl) {
|
||||
URL.revokeObjectURL(recordingUrl);
|
||||
recordingUrl = null;
|
||||
}
|
||||
recordedChunks = [];
|
||||
recorder = new MediaRecorder(mediaStream);
|
||||
recorder.ondataavailable = (ev) => {
|
||||
if (ev.data && ev.data.size > 0) recordedChunks.push(ev.data);
|
||||
};
|
||||
recorder.onstop = () => {
|
||||
const blob = new Blob(recordedChunks, { type: recorder?.mimeType || 'audio/webm' });
|
||||
recordingUrl = URL.createObjectURL(blob);
|
||||
};
|
||||
recorder.start();
|
||||
isRecording = true;
|
||||
} catch (e) {
|
||||
console.error('Unable to start recording', e);
|
||||
}
|
||||
}
|
||||
|
||||
function stopRecording() {
|
||||
if (!recorder || recorder.state === 'inactive') return;
|
||||
recorder.stop();
|
||||
isRecording = false;
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
// Try to prefetch devices (may require prior permission for labels)
|
||||
refreshDevices();
|
||||
return () => {
|
||||
stopMic();
|
||||
if (recordingUrl) URL.revokeObjectURL(recordingUrl);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<article>
|
||||
<h3>{m.mic_title()}</h3>
|
||||
|
||||
<section class="controls">
|
||||
<div class="row">
|
||||
<button class="button" onclick={startMic} disabled={micActive}
|
||||
>{m.mic_startMicrophone()}</button
|
||||
>
|
||||
<button class="button stop" onclick={stopMic} disabled={!micActive}>{m.mic_stop()}</button>
|
||||
<button
|
||||
class="button"
|
||||
onclick={toggleMonitoring}
|
||||
disabled={!micActive}
|
||||
aria-pressed={monitoring}
|
||||
>
|
||||
{monitoring ? m.mic_monitoringOn() : m.mic_monitoringOff()}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="row inputs">
|
||||
<label>
|
||||
{m.mic_device()}
|
||||
<select onchange={onDeviceChange} disabled={devices.length === 0}>
|
||||
{#if devices.length === 0}
|
||||
<option value="">{m.mic_noMicFound()}</option>
|
||||
{:else}
|
||||
{#each devices as d}
|
||||
<option value={d.deviceId} selected={d.deviceId === selectedDeviceId}
|
||||
>{d.label || m.mic_device()}</option
|
||||
>
|
||||
{/each}
|
||||
{/if}
|
||||
</select>
|
||||
</label>
|
||||
<button class="button" onclick={refreshDevices}>{m.mic_refresh()}</button>
|
||||
</div>
|
||||
|
||||
<div class="row inputs">
|
||||
<label>
|
||||
{m.mic_gain()}
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="2"
|
||||
step="0.01"
|
||||
value={gain}
|
||||
oninput={onGainChange}
|
||||
disabled={!micActive}
|
||||
/>
|
||||
<span class="value">{gain.toFixed(2)}x</span>
|
||||
</label>
|
||||
<label>
|
||||
{m.mic_monitorDelay()}
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="1000"
|
||||
step="1"
|
||||
value={delayMs}
|
||||
oninput={onDelayChange}
|
||||
disabled={!micActive}
|
||||
/>
|
||||
<span class="value">{delayMs} ms</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<details>
|
||||
<summary>{m.mic_advanced()}</summary>
|
||||
<div class="row inputs">
|
||||
<div class="label">{m.mic_constraints()}</div>
|
||||
<label>
|
||||
{m.mic_echoCancellation()}
|
||||
<select
|
||||
onchange={(e) => {
|
||||
const v = (e.target as HTMLSelectElement).value;
|
||||
reqEchoCancellation = v === 'default' ? null : v === 'on';
|
||||
}}
|
||||
>
|
||||
<option value="default" selected>{m.mic_default()}</option>
|
||||
<option value="on">{m.mic_on()}</option>
|
||||
<option value="off">{m.mic_off()}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
{m.mic_noiseSuppression()}
|
||||
<select
|
||||
onchange={(e) => {
|
||||
const v = (e.target as HTMLSelectElement).value;
|
||||
reqNoiseSuppression = v === 'default' ? null : v === 'on';
|
||||
}}
|
||||
>
|
||||
<option value="default" selected>{m.mic_default()}</option>
|
||||
<option value="on">{m.mic_on()}</option>
|
||||
<option value="off">{m.mic_off()}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
{m.mic_agc()}
|
||||
<select
|
||||
onchange={(e) => {
|
||||
const v = (e.target as HTMLSelectElement).value;
|
||||
reqAGC = v === 'default' ? null : v === 'on';
|
||||
}}
|
||||
>
|
||||
<option value="default" selected>{m.mic_default()}</option>
|
||||
<option value="on">{m.mic_on()}</option>
|
||||
<option value="off">{m.mic_off()}</option>
|
||||
</select>
|
||||
</label>
|
||||
<button
|
||||
class="button"
|
||||
onclick={() => {
|
||||
if (micActive) {
|
||||
const wasMonitoring = monitoring;
|
||||
stopMic();
|
||||
monitoring = wasMonitoring;
|
||||
}
|
||||
startMic();
|
||||
}}>{m.mic_applyConstraints()}</button
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="obtained">
|
||||
<div>
|
||||
<strong>{m.mic_requested()}:</strong> EC={reqEchoCancellation === null
|
||||
? '–'
|
||||
: reqEchoCancellation
|
||||
? 'on'
|
||||
: 'off'}, NS={reqNoiseSuppression === null
|
||||
? '–'
|
||||
: reqNoiseSuppression
|
||||
? 'on'
|
||||
: 'off'}, AGC={reqAGC === null ? '–' : reqAGC ? 'on' : 'off'}
|
||||
</div>
|
||||
<div>
|
||||
<strong>{m.mic_obtained()}:</strong> EC={(obtainedEchoCancellation ??
|
||||
'–') as unknown as string}, NS={(obtainedNoiseSuppression ?? '–') as unknown as string},
|
||||
AGC={(obtainedAGC ?? '–') as unknown as string}
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section class="info">
|
||||
<div><strong>{m.mic_sampleRate()}:</strong> {sampleRate ?? '–'} Hz</div>
|
||||
{#if deviceLabel}
|
||||
<div><strong>{m.mic_inputDevice()}:</strong> {deviceLabel}</div>
|
||||
{/if}
|
||||
<div>
|
||||
<strong>{m.mic_channels()}:</strong>
|
||||
{#if channels === null}
|
||||
–
|
||||
{:else if channels === 1}
|
||||
1 ({m.mic_mono()})
|
||||
{:else if channels === 2}
|
||||
2 ({m.mic_stereo()})
|
||||
{:else}
|
||||
{channels}
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="meter">
|
||||
<div class="label">{m.mic_volume()}</div>
|
||||
<div class="bar">
|
||||
<div class="fill" style={`transform: scaleX(${Math.min(1, volume).toFixed(3)})`}></div>
|
||||
{#if peakHold > 0}
|
||||
<div class="peak-hold" style={`left: ${Math.min(100, peakHold * 100)}%`}></div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="volval">
|
||||
{m.mic_peakNow()}: {(20 * Math.log10(Math.max(1e-5, peak))).toFixed(1)} dBFS ·
|
||||
{m.mic_peakHold()}: {(20 * Math.log10(Math.max(1e-5, peakHold))).toFixed(1)} dBFS · RMS: {(
|
||||
20 * Math.log10(Math.max(1e-5, volume))
|
||||
).toFixed(1)} dBFS
|
||||
<button class="button small" onclick={() => (peakHold = 0)}>{m.mic_resetPeaks()}</button>
|
||||
</div>
|
||||
{#if clipping}
|
||||
<div class="clip">{m.mic_clipping()}</div>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<section class="graphs">
|
||||
{#if analyserTime && analyserFreq}
|
||||
<Oscilloscope analyser={analyserTime} title={m.signalGen_scope()} />
|
||||
<Spectrum analyser={analyserFreq} title={m.signalGen_spectrum()} />
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<section class="recording">
|
||||
<h4>{m.mic_recording()}</h4>
|
||||
<div class="row">
|
||||
<button class="button" onclick={startRecording} disabled={!micActive || isRecording}
|
||||
>{m.mic_startRecording()}</button
|
||||
>
|
||||
<button class="button stop" onclick={stopRecording} disabled={!isRecording}
|
||||
>{m.mic_stopRecording()}</button
|
||||
>
|
||||
</div>
|
||||
{#if recordingUrl}
|
||||
<audio src={recordingUrl} controls></audio>
|
||||
<a class="button" href={recordingUrl} download="mic-test.webm">{m.mic_downloadRecording()}</a>
|
||||
{/if}
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<style>
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
article {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.controls .row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.controls .inputs {
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.controls label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.controls input[type='range'] {
|
||||
width: min(40vw, 280px);
|
||||
}
|
||||
|
||||
.value {
|
||||
opacity: 0.8;
|
||||
min-width: 4ch;
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.meter {
|
||||
display: grid;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.bar {
|
||||
position: relative;
|
||||
height: 14px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 999px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bar .fill {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(90deg, #5cb85c, #f0ad4e 70%, #d9534f);
|
||||
transform-origin: left center;
|
||||
}
|
||||
|
||||
/* Peak hold marker */
|
||||
.bar .peak-hold {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
background: #fff;
|
||||
opacity: 0.9;
|
||||
transform: translateX(-1px);
|
||||
}
|
||||
|
||||
.volval {
|
||||
opacity: 0.8;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.button.small {
|
||||
padding: 0.1rem 0.4rem;
|
||||
font-size: 0.85em;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.graphs {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.recording .row {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.obtained {
|
||||
display: grid;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.button.stop:not(:disabled) {
|
||||
background: darkred;
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
.graphs {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,545 @@
|
|||
<script lang="ts">
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
|
||||
// Geolocation
|
||||
let geoWatchId: number | null = $state(null);
|
||||
let geolocation: GeolocationPosition | null = $state(null);
|
||||
let geoError: string | null = $state(null);
|
||||
let geoSupported = $state<boolean>(false);
|
||||
|
||||
// DeviceMotion / DeviceOrientation (useful fallbacks on iOS/Safari)
|
||||
type Motion = { ax?: number; ay?: number; az?: number; gx?: number; gy?: number; gz?: number };
|
||||
let deviceMotion: Motion | null = $state(null);
|
||||
let deviceOrientation: { alpha?: number; beta?: number; gamma?: number } | null = $state(null);
|
||||
let motionSupported = $state(false);
|
||||
let orientationSupported = $state(false);
|
||||
|
||||
// iOS Safari permission flow for motion/orientation
|
||||
let motionPermissionAvailable = $state(false);
|
||||
let orientationPermissionAvailable = $state(false);
|
||||
let motionPermission: 'unknown' | 'granted' | 'denied' = $state('unknown');
|
||||
let orientationPermission: 'unknown' | 'granted' | 'denied' = $state('unknown');
|
||||
|
||||
// Generic Sensor API (subject to browser/flag support)
|
||||
type SensorHandle = {
|
||||
instance?: any;
|
||||
supported: boolean;
|
||||
active: boolean;
|
||||
error?: string | null;
|
||||
data: Record<string, number | string | undefined>;
|
||||
};
|
||||
|
||||
let accelerometer: SensorHandle = $state({ supported: false, active: false, data: {} });
|
||||
let gyroscope: SensorHandle = $state({ supported: false, active: false, data: {} });
|
||||
let magnetometer: SensorHandle = $state({ supported: false, active: false, data: {} });
|
||||
let ambientLight: SensorHandle = $state({ supported: false, active: false, data: {} });
|
||||
let barometer: SensorHandle = $state({ supported: false, active: false, data: {} });
|
||||
|
||||
const w = browser ? (window as any) : undefined;
|
||||
|
||||
function detectSupport() {
|
||||
geoSupported = browser && 'geolocation' in navigator;
|
||||
motionSupported = browser && 'DeviceMotionEvent' in (window as any);
|
||||
orientationSupported = browser && 'DeviceOrientationEvent' in (window as any);
|
||||
|
||||
accelerometer.supported = Boolean(w?.Accelerometer);
|
||||
gyroscope.supported = Boolean(w?.Gyroscope);
|
||||
magnetometer.supported = Boolean(w?.Magnetometer);
|
||||
ambientLight.supported = Boolean(w?.AmbientLightSensor);
|
||||
barometer.supported = Boolean(w?.Barometer);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
detectSupport();
|
||||
|
||||
// Check for iOS-style permission request APIs
|
||||
motionPermissionAvailable =
|
||||
browser &&
|
||||
typeof (window as any).DeviceMotionEvent !== 'undefined' &&
|
||||
typeof (DeviceMotionEvent as any).requestPermission === 'function';
|
||||
orientationPermissionAvailable =
|
||||
browser &&
|
||||
typeof (window as any).DeviceOrientationEvent !== 'undefined' &&
|
||||
typeof (DeviceOrientationEvent as any).requestPermission === 'function';
|
||||
|
||||
if (orientationSupported) {
|
||||
const handler = (e: DeviceOrientationEvent) => {
|
||||
deviceOrientation = {
|
||||
alpha: e.alpha ?? undefined,
|
||||
beta: e.beta ?? undefined,
|
||||
gamma: e.gamma ?? undefined
|
||||
};
|
||||
};
|
||||
window.addEventListener('deviceorientation', handler);
|
||||
onDestroy(() => window.removeEventListener('deviceorientation', handler));
|
||||
}
|
||||
|
||||
if (motionSupported) {
|
||||
const handler = (e: DeviceMotionEvent) => {
|
||||
deviceMotion = {
|
||||
ax: e.acceleration?.x ?? undefined,
|
||||
ay: e.acceleration?.y ?? undefined,
|
||||
az: e.acceleration?.z ?? undefined,
|
||||
gx: e.rotationRate?.alpha ?? undefined,
|
||||
gy: e.rotationRate?.beta ?? undefined,
|
||||
gz: e.rotationRate?.gamma ?? undefined
|
||||
};
|
||||
};
|
||||
window.addEventListener('devicemotion', handler);
|
||||
onDestroy(() => window.removeEventListener('devicemotion', handler));
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
stopGeolocation();
|
||||
stopSensor(accelerometer);
|
||||
stopSensor(gyroscope);
|
||||
stopSensor(magnetometer);
|
||||
stopSensor(ambientLight);
|
||||
stopSensor(barometer);
|
||||
});
|
||||
|
||||
// (Permissions are requested implicitly when starting sensors where applicable)
|
||||
|
||||
// Geolocation controls
|
||||
async function startGeolocation() {
|
||||
if (!geoSupported) return;
|
||||
try {
|
||||
geoError = null;
|
||||
geolocation = await new Promise<GeolocationPosition>((resolve, reject) => {
|
||||
navigator.geolocation.getCurrentPosition(resolve, reject, {
|
||||
enableHighAccuracy: true,
|
||||
timeout: 10000
|
||||
});
|
||||
});
|
||||
geoWatchId = navigator.geolocation.watchPosition(
|
||||
(pos) => (geolocation = pos),
|
||||
(err) => (geoError = err?.message || String(err)),
|
||||
{ enableHighAccuracy: true }
|
||||
);
|
||||
} catch (e: any) {
|
||||
geoError = e?.message ?? String(e);
|
||||
}
|
||||
}
|
||||
|
||||
function stopGeolocation() {
|
||||
if (geoWatchId != null) {
|
||||
navigator.geolocation.clearWatch(geoWatchId);
|
||||
geoWatchId = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Generic Sensor helpers
|
||||
function startSensor(handle: SensorHandle, ctorName: string, options: any = { frequency: 60 }) {
|
||||
try {
|
||||
handle.error = null;
|
||||
if (!w?.[ctorName]) {
|
||||
handle.supported = false;
|
||||
return;
|
||||
}
|
||||
handle.instance = new w[ctorName](options);
|
||||
handle.instance.addEventListener('reading', () => {
|
||||
// Populate based on sensor type
|
||||
if (ctorName === 'Accelerometer') {
|
||||
handle.data = { x: handle.instance.x, y: handle.instance.y, z: handle.instance.z };
|
||||
} else if (ctorName === 'Gyroscope') {
|
||||
handle.data = { x: handle.instance.x, y: handle.instance.y, z: handle.instance.z };
|
||||
} else if (ctorName === 'Magnetometer') {
|
||||
handle.data = { x: handle.instance.x, y: handle.instance.y, z: handle.instance.z };
|
||||
} else if (ctorName === 'AmbientLightSensor') {
|
||||
handle.data = { illuminance: handle.instance.illuminance };
|
||||
} else if (ctorName === 'Barometer') {
|
||||
handle.data = {
|
||||
pressure: handle.instance.pressure,
|
||||
temperature: handle.instance?.temperature
|
||||
};
|
||||
}
|
||||
});
|
||||
handle.instance.addEventListener('error', (event: any) => {
|
||||
handle.error = event?.error?.message || String(event);
|
||||
});
|
||||
handle.instance.start();
|
||||
handle.active = true;
|
||||
} catch (e: any) {
|
||||
handle.error = e?.message ?? String(e);
|
||||
handle.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
function stopSensor(handle: SensorHandle) {
|
||||
try {
|
||||
handle.instance?.stop?.();
|
||||
} catch {}
|
||||
handle.active = false;
|
||||
}
|
||||
|
||||
// UI helpers
|
||||
function toFixed(n: number | undefined, digits = 2) {
|
||||
return typeof n === 'number' && Number.isFinite(n) ? n.toFixed(digits) : '—';
|
||||
}
|
||||
|
||||
async function copyJSON(data: unknown) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(JSON.stringify(data, null, 2));
|
||||
alert(m.sensors_copied());
|
||||
} catch {}
|
||||
}
|
||||
|
||||
async function requestMotionOrientation() {
|
||||
try {
|
||||
if (motionPermissionAvailable) {
|
||||
const res = await (DeviceMotionEvent as any).requestPermission();
|
||||
motionPermission = res === 'granted' ? 'granted' : 'denied';
|
||||
}
|
||||
if (orientationPermissionAvailable) {
|
||||
const res2 = await (DeviceOrientationEvent as any).requestPermission();
|
||||
orientationPermission = res2 === 'granted' ? 'granted' : 'denied';
|
||||
}
|
||||
} catch (_) {
|
||||
if (motionPermissionAvailable && motionPermission === 'unknown') motionPermission = 'denied';
|
||||
if (orientationPermissionAvailable && orientationPermission === 'unknown')
|
||||
orientationPermission = 'denied';
|
||||
}
|
||||
}
|
||||
|
||||
// Kick off light permission checks lazily in UI; starting sensors will request permissions where needed.
|
||||
</script>
|
||||
|
||||
<h2><i class="ti ti-cpu-2"></i> {m.sensors_title()}</h2>
|
||||
|
||||
<div class="sections">
|
||||
{#if motionPermissionAvailable || orientationPermissionAvailable}
|
||||
<section>
|
||||
<h3>{m.sensors_permissions()}</h3>
|
||||
<div class="row">
|
||||
<button onclick={requestMotionOrientation}
|
||||
><i class="ti ti-key"></i> {m.sensors_enableMotionOrientation()}</button
|
||||
>
|
||||
</div>
|
||||
<ul class="kv">
|
||||
{#if motionPermissionAvailable}
|
||||
<li>
|
||||
<span class="key">{m.sensors_motion()}</span>
|
||||
<span>
|
||||
{#if motionPermission === 'granted'}{m.sensors_status_granted()}
|
||||
{:else if motionPermission === 'denied'}{m.sensors_status_denied()}
|
||||
{:else}{m.sensors_status_unknown()}{/if}
|
||||
</span>
|
||||
</li>
|
||||
{/if}
|
||||
{#if orientationPermissionAvailable}
|
||||
<li>
|
||||
<span class="key">{m.sensors_orientation()}</span>
|
||||
<span>
|
||||
{#if orientationPermission === 'granted'}{m.sensors_status_granted()}
|
||||
{:else if orientationPermission === 'denied'}{m.sensors_status_denied()}
|
||||
{:else}{m.sensors_status_unknown()}{/if}
|
||||
</span>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
</section>
|
||||
{/if}
|
||||
<section>
|
||||
<h3>{m.sensors_geolocation()}</h3>
|
||||
{#if geoSupported}
|
||||
<div class="row">
|
||||
<button onclick={startGeolocation} disabled={geoWatchId !== null}>
|
||||
<i class="ti ti-player-play"></i>
|
||||
{m.sensors_start()}
|
||||
</button>
|
||||
<button onclick={stopGeolocation} disabled={geoWatchId === null}>
|
||||
<i class="ti ti-player-stop"></i>
|
||||
{m.sensors_stop()}
|
||||
</button>
|
||||
</div>
|
||||
{#if geoError}
|
||||
<div class="error">{geoError}</div>
|
||||
{/if}
|
||||
{#if geolocation}
|
||||
<ul class="kv">
|
||||
<li><span class="key">lat</span><span>{geolocation.coords.latitude}</span></li>
|
||||
<li><span class="key">lon</span><span>{geolocation.coords.longitude}</span></li>
|
||||
<li>
|
||||
<span class="key">{m.sensors_accuracy()}</span><span
|
||||
>{toFixed(geolocation.coords.accuracy)}</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="key">{m.sensors_altitude()}</span><span
|
||||
>{geolocation.coords.altitude ?? '—'}</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="key">{m.sensors_heading()}</span><span
|
||||
>{toFixed(geolocation.coords.heading ?? undefined)}</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="key">{m.sensors_speed()}</span><span
|
||||
>{toFixed(geolocation.coords.speed ?? undefined)}</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="key">{m.sensors_timestamp()}</span><span
|
||||
>{new Date(geolocation.timestamp).toLocaleString()}</span
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="row">
|
||||
<button onclick={() => copyJSON(geolocation)}
|
||||
><i class="ti ti-copy"></i> {m.sensors_copy()}</button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="subdued">{m.sensors_notSupported()}</div>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>{m.sensors_deviceMotion()}</h3>
|
||||
{#if motionSupported}
|
||||
<ul class="kv">
|
||||
<li><span class="key">ax</span><span>{toFixed(deviceMotion?.ax)}</span></li>
|
||||
<li><span class="key">ay</span><span>{toFixed(deviceMotion?.ay)}</span></li>
|
||||
<li><span class="key">az</span><span>{toFixed(deviceMotion?.az)}</span></li>
|
||||
<li><span class="key">α</span><span>{toFixed(deviceMotion?.gx)}</span></li>
|
||||
<li><span class="key">β</span><span>{toFixed(deviceMotion?.gy)}</span></li>
|
||||
<li><span class="key">γ</span><span>{toFixed(deviceMotion?.gz)}</span></li>
|
||||
</ul>
|
||||
<div class="row">
|
||||
<button onclick={() => copyJSON(deviceMotion)}
|
||||
><i class="ti ti-copy"></i> {m.sensors_copy()}</button
|
||||
>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="subdued">{m.sensors_notSupported()}</div>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>{m.sensors_deviceOrientation()}</h3>
|
||||
{#if orientationSupported}
|
||||
<ul class="kv">
|
||||
<li><span class="key">alpha</span><span>{toFixed(deviceOrientation?.alpha)}</span></li>
|
||||
<li><span class="key">beta</span><span>{toFixed(deviceOrientation?.beta)}</span></li>
|
||||
<li><span class="key">gamma</span><span>{toFixed(deviceOrientation?.gamma)}</span></li>
|
||||
</ul>
|
||||
<div class="row">
|
||||
<button onclick={() => copyJSON(deviceOrientation)}
|
||||
><i class="ti ti-copy"></i> {m.sensors_copy()}</button
|
||||
>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="subdued">{m.sensors_notSupported()}</div>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>{m.sensors_accelerometer()}</h3>
|
||||
{#if accelerometer.supported}
|
||||
<div class="row">
|
||||
{#if !accelerometer.active}
|
||||
<button onclick={() => startSensor(accelerometer, 'Accelerometer')}
|
||||
><i class="ti ti-player-play"></i> {m.sensors_start()}</button
|
||||
>
|
||||
{:else}
|
||||
<button onclick={() => stopSensor(accelerometer)}
|
||||
><i class="ti ti-player-stop"></i> {m.sensors_stop()}</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{#if accelerometer.error}
|
||||
<div class="error">{accelerometer.error}</div>
|
||||
{/if}
|
||||
<ul class="kv">
|
||||
<li><span class="key">x</span><span>{toFixed(accelerometer.data.x as number)}</span></li>
|
||||
<li><span class="key">y</span><span>{toFixed(accelerometer.data.y as number)}</span></li>
|
||||
<li><span class="key">z</span><span>{toFixed(accelerometer.data.z as number)}</span></li>
|
||||
</ul>
|
||||
{:else}
|
||||
<div class="subdued">{m.sensors_notSupported()}</div>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>{m.sensors_gyroscope()}</h3>
|
||||
{#if gyroscope.supported}
|
||||
<div class="row">
|
||||
{#if !gyroscope.active}
|
||||
<button onclick={() => startSensor(gyroscope, 'Gyroscope')}
|
||||
><i class="ti ti-player-play"></i> {m.sensors_start()}</button
|
||||
>
|
||||
{:else}
|
||||
<button onclick={() => stopSensor(gyroscope)}
|
||||
><i class="ti ti-player-stop"></i> {m.sensors_stop()}</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{#if gyroscope.error}
|
||||
<div class="error">{gyroscope.error}</div>
|
||||
{/if}
|
||||
<ul class="kv">
|
||||
<li><span class="key">x</span><span>{toFixed(gyroscope.data.x as number)}</span></li>
|
||||
<li><span class="key">y</span><span>{toFixed(gyroscope.data.y as number)}</span></li>
|
||||
<li><span class="key">z</span><span>{toFixed(gyroscope.data.z as number)}</span></li>
|
||||
</ul>
|
||||
{:else}
|
||||
<div class="subdued">{m.sensors_notSupported()}</div>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>{m.sensors_magnetometer()}</h3>
|
||||
{#if magnetometer.supported}
|
||||
<div class="row">
|
||||
{#if !magnetometer.active}
|
||||
<button onclick={() => startSensor(magnetometer, 'Magnetometer', { frequency: 10 })}
|
||||
><i class="ti ti-player-play"></i> {m.sensors_start()}</button
|
||||
>
|
||||
{:else}
|
||||
<button onclick={() => stopSensor(magnetometer)}
|
||||
><i class="ti ti-player-stop"></i> {m.sensors_stop()}</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{#if magnetometer.error}
|
||||
<div class="error">{magnetometer.error}</div>
|
||||
{/if}
|
||||
<ul class="kv">
|
||||
<li><span class="key">x</span><span>{toFixed(magnetometer.data.x as number)}</span></li>
|
||||
<li><span class="key">y</span><span>{toFixed(magnetometer.data.y as number)}</span></li>
|
||||
<li><span class="key">z</span><span>{toFixed(magnetometer.data.z as number)}</span></li>
|
||||
</ul>
|
||||
{:else}
|
||||
<div class="subdued">{m.sensors_notSupported()}</div>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>{m.sensors_ambientLight()}</h3>
|
||||
{#if ambientLight.supported}
|
||||
<div class="row">
|
||||
{#if !ambientLight.active}
|
||||
<button onclick={() => startSensor(ambientLight, 'AmbientLightSensor')}
|
||||
><i class="ti ti-player-play"></i> {m.sensors_start()}</button
|
||||
>
|
||||
{:else}
|
||||
<button onclick={() => stopSensor(ambientLight)}
|
||||
><i class="ti ti-player-stop"></i> {m.sensors_stop()}</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{#if ambientLight.error}
|
||||
<div class="error">{ambientLight.error}</div>
|
||||
{/if}
|
||||
<ul class="kv">
|
||||
<li>
|
||||
<span class="key">{m.sensors_illuminance()}</span><span
|
||||
>{toFixed(ambientLight.data.illuminance as number)}</span
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
{:else}
|
||||
<div class="subdued">{m.sensors_notSupported()}</div>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>{m.sensors_barometer()}</h3>
|
||||
{#if barometer.supported}
|
||||
<div class="row">
|
||||
{#if !barometer.active}
|
||||
<button onclick={() => startSensor(barometer, 'Barometer')}
|
||||
><i class="ti ti-player-play"></i> {m.sensors_start()}</button
|
||||
>
|
||||
{:else}
|
||||
<button onclick={() => stopSensor(barometer)}
|
||||
><i class="ti ti-player-stop"></i> {m.sensors_stop()}</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{#if barometer.error}
|
||||
<div class="error">{barometer.error}</div>
|
||||
{/if}
|
||||
<ul class="kv">
|
||||
<li>
|
||||
<span class="key">{m.sensors_pressure()}</span><span
|
||||
>{toFixed(barometer.data.pressure as number)}</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="key">{m.sensors_temperature()}</span><span
|
||||
>{barometer.data.temperature ?? '—'}</span
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
{:else}
|
||||
<div class="subdued">{m.sensors_notSupported()}</div>
|
||||
{/if}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.sections {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
section {
|
||||
border: 1px solid currentColor;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.kv {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0.5rem 0 0 0;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 0.25rem 0.75rem;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.kv .key {
|
||||
opacity: 0.8;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #ff6b6b;
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
.subdued {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
button {
|
||||
background: none;
|
||||
color: inherit;
|
||||
border: 1px solid currentColor;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
</style>
|
354
src/routes/(pages)/signal-generator/+page.svelte
Normal file
354
src/routes/(pages)/signal-generator/+page.svelte
Normal file
|
@ -0,0 +1,354 @@
|
|||
<script lang="ts">
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import Oscilloscope from '$lib/audio/Oscilloscope.svelte';
|
||||
import Spectrum from '$lib/audio/Spectrum.svelte';
|
||||
|
||||
type GenType = 'off' | 'sine' | 'sweep' | 'white' | 'pink' | 'brown';
|
||||
|
||||
let ctx: AudioContext | null = null;
|
||||
let gainNode: GainNode | null = null;
|
||||
let analyser = $state<AnalyserNode | null>(null);
|
||||
let source: AudioNode | null = null; // OscillatorNode or AudioBufferSourceNode
|
||||
let started = $state(false);
|
||||
|
||||
let genType = $state<GenType>('sine');
|
||||
let frequency = $state(440);
|
||||
let volume = $state(0.2);
|
||||
|
||||
let sweepFrom = $state(20);
|
||||
let sweepTo = $state(20000);
|
||||
let sweepDuration = $state(10); // seconds
|
||||
let sweepLoop = $state(false);
|
||||
|
||||
function ensureAudio() {
|
||||
if (!ctx) {
|
||||
ctx = new AudioContext();
|
||||
gainNode = ctx.createGain();
|
||||
gainNode.gain.value = volume;
|
||||
analyser = ctx.createAnalyser();
|
||||
analyser.fftSize = 2048;
|
||||
analyser.smoothingTimeConstant = 0.7;
|
||||
gainNode.connect(analyser);
|
||||
analyser.connect(ctx.destination);
|
||||
}
|
||||
}
|
||||
|
||||
function connectSource(node: AudioNode) {
|
||||
ensureAudio();
|
||||
stopSource();
|
||||
source = node;
|
||||
node.connect(gainNode!);
|
||||
}
|
||||
|
||||
function stopSource() {
|
||||
if (!source) return;
|
||||
try {
|
||||
// Try to stop if it has a stop() method
|
||||
(source as any).stop?.();
|
||||
(source as any).disconnect?.();
|
||||
} catch {}
|
||||
source = null;
|
||||
}
|
||||
|
||||
function start() {
|
||||
ensureAudio();
|
||||
ctx!.resume();
|
||||
started = true;
|
||||
if (genType === 'sine') startSine();
|
||||
else if (genType === 'sweep') startSweep();
|
||||
else if (genType === 'white') startNoise('white');
|
||||
else if (genType === 'pink') startNoise('pink');
|
||||
else if (genType === 'brown') startNoise('brown');
|
||||
}
|
||||
|
||||
function stop() {
|
||||
stopSource();
|
||||
started = false;
|
||||
}
|
||||
|
||||
function onTypeChange(t: GenType) {
|
||||
genType = t;
|
||||
if (!started) return;
|
||||
// Restart with new type
|
||||
start();
|
||||
}
|
||||
|
||||
function onFrequencyChange(v: number) {
|
||||
frequency = v;
|
||||
if (source && (source as OscillatorNode).frequency) {
|
||||
(source as OscillatorNode).frequency.setValueAtTime(frequency, ctx!.currentTime);
|
||||
}
|
||||
}
|
||||
|
||||
function onVolumeChange(v: number) {
|
||||
volume = v;
|
||||
if (gainNode) gainNode.gain.setValueAtTime(volume, ctx!.currentTime);
|
||||
}
|
||||
|
||||
function startSine() {
|
||||
const osc = ctx!.createOscillator();
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(frequency, ctx!.currentTime);
|
||||
connectSource(osc);
|
||||
osc.start();
|
||||
}
|
||||
|
||||
function startSweep() {
|
||||
const osc = ctx!.createOscillator();
|
||||
osc.type = 'sine';
|
||||
const startF = Math.max(0.1, sweepFrom);
|
||||
const endF = Math.max(0.1, sweepTo);
|
||||
const now = ctx!.currentTime;
|
||||
osc.frequency.cancelScheduledValues(now);
|
||||
osc.frequency.setValueAtTime(startF, now);
|
||||
// Use exponential ramp if both positive and not equal; fall back to linear otherwise
|
||||
if (startF > 0 && endF > 0 && startF !== endF) {
|
||||
osc.frequency.exponentialRampToValueAtTime(endF, now + sweepDuration);
|
||||
} else {
|
||||
osc.frequency.linearRampToValueAtTime(endF, now + sweepDuration);
|
||||
}
|
||||
connectSource(osc);
|
||||
osc.start();
|
||||
// auto stop or loop after sweep
|
||||
const timeoutId = setTimeout(() => {
|
||||
try {
|
||||
osc.stop();
|
||||
} catch {}
|
||||
if (started && genType === 'sweep') {
|
||||
if (sweepLoop) {
|
||||
startSweep(); // restart for loop
|
||||
} else {
|
||||
started = false;
|
||||
}
|
||||
}
|
||||
}, sweepDuration * 1000);
|
||||
|
||||
// When the source is replaced, clear this timeout
|
||||
source?.addEventListener('disconnect', () => clearTimeout(timeoutId), { once: true });
|
||||
}
|
||||
|
||||
function makeNoiseBuffer(kind: 'white' | 'pink' | 'brown') {
|
||||
const sr = ctx!.sampleRate;
|
||||
const seconds = 2;
|
||||
const frameCount = sr * seconds;
|
||||
const buffer = ctx!.createBuffer(2, frameCount, sr);
|
||||
for (let ch = 0; ch < buffer.numberOfChannels; ch++) {
|
||||
const data = buffer.getChannelData(ch);
|
||||
if (kind === 'white') {
|
||||
for (let i = 0; i < frameCount; i++) data[i] = Math.random() * 2 - 1;
|
||||
} else if (kind === 'pink') {
|
||||
// Voss-McCartney algorithm approximation
|
||||
let b0 = 0,
|
||||
b1 = 0,
|
||||
b2 = 0,
|
||||
b3 = 0,
|
||||
b4 = 0,
|
||||
b5 = 0,
|
||||
b6 = 0;
|
||||
for (let i = 0; i < frameCount; i++) {
|
||||
const white = Math.random() * 2 - 1;
|
||||
b0 = 0.99886 * b0 + white * 0.0555179;
|
||||
b1 = 0.99332 * b1 + white * 0.0750759;
|
||||
b2 = 0.969 * b2 + white * 0.153852;
|
||||
b3 = 0.8665 * b3 + white * 0.3104856;
|
||||
b4 = 0.55 * b4 + white * 0.5329522;
|
||||
b5 = -0.7616 * b5 - white * 0.016898;
|
||||
const pink = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362;
|
||||
b6 = white * 0.115926;
|
||||
data[i] = pink * 0.11; // gain normalize
|
||||
}
|
||||
} else if (kind === 'brown') {
|
||||
let lastOut = 0;
|
||||
for (let i = 0; i < frameCount; i++) {
|
||||
const white = Math.random() * 2 - 1;
|
||||
const brown = (lastOut + 0.02 * white) / 1.02;
|
||||
lastOut = brown;
|
||||
data[i] = brown * 3.5; // gain normalize
|
||||
}
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
function startNoise(kind: 'white' | 'pink' | 'brown') {
|
||||
const buf = makeNoiseBuffer(kind);
|
||||
const src = ctx!.createBufferSource();
|
||||
src.buffer = buf;
|
||||
src.loop = true;
|
||||
connectSource(src);
|
||||
src.start();
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
// prepare audio on mount but start only on user gesture
|
||||
ensureAudio();
|
||||
|
||||
return () => {
|
||||
stop();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<h2><i class="ti ti-wave-sine"></i> {m.signalGen_title()}</h2>
|
||||
|
||||
<article class="layout">
|
||||
<section class="controls">
|
||||
<div class="row">
|
||||
<span class="label">{m.signalGen_type()}:</span>
|
||||
<div class="buttons">
|
||||
<button class:active={genType === 'sine'} onclick={() => onTypeChange('sine')}
|
||||
>{m.signalGen_sine()}</button
|
||||
>
|
||||
<button class:active={genType === 'sweep'} onclick={() => onTypeChange('sweep')}
|
||||
>{m.signalGen_sweep()}</button
|
||||
>
|
||||
<button class:active={genType === 'white'} onclick={() => onTypeChange('white')}
|
||||
>{m.signalGen_noiseWhite()}</button
|
||||
>
|
||||
<button class:active={genType === 'pink'} onclick={() => onTypeChange('pink')}
|
||||
>{m.signalGen_noisePink()}</button
|
||||
>
|
||||
<button class:active={genType === 'brown'} onclick={() => onTypeChange('brown')}
|
||||
>{m.signalGen_noiseBrown()}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if genType === 'sine'}
|
||||
<div class="row">
|
||||
<label for="freq">{m.signalGen_frequency()}:</label>
|
||||
<input
|
||||
id="freq"
|
||||
type="range"
|
||||
min="20"
|
||||
max="20000"
|
||||
step="1"
|
||||
bind:value={frequency}
|
||||
oninput={(e) => onFrequencyChange(+e.currentTarget.value)}
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
min="20"
|
||||
max="20000"
|
||||
class="exact-freq"
|
||||
bind:value={frequency}
|
||||
oninput={(e) => onFrequencyChange(+e.currentTarget.value)}
|
||||
/>
|
||||
<span class="value">Hz</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if genType === 'sweep'}
|
||||
<div class="row">
|
||||
<label for="sweep-from">{m.signalGen_from()}:</label>
|
||||
<input id="sweep-from" type="number" min="1" max="20000" bind:value={sweepFrom} />
|
||||
<label for="sweep-to">{m.signalGen_to()}:</label>
|
||||
<input id="sweep-to" type="number" min="1" max="20000" bind:value={sweepTo} />
|
||||
</div>
|
||||
<div class="row">
|
||||
<label for="sweep-duration">{m.signalGen_duration()}:</label>
|
||||
<input id="sweep-duration" type="number" min="1" max="120" bind:value={sweepDuration} />
|
||||
<span class="value">s</span>
|
||||
<label class="checkbox-label"
|
||||
><input type="checkbox" bind:checked={sweepLoop} />{m.signalGen_loop()}</label
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="row">
|
||||
<label for="gain">{m.signalGen_gain()}:</label>
|
||||
<input
|
||||
id="gain"
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
bind:value={volume}
|
||||
oninput={(e) => onVolumeChange(+e.currentTarget.value)}
|
||||
/>
|
||||
<span class="value">{(volume * 100) | 0}%</span>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
{#if !started}
|
||||
<button class="primary" onclick={start}
|
||||
><i class="ti ti-player-play"></i> {m.signalGen_start()}</button
|
||||
>
|
||||
{:else}
|
||||
<button onclick={stop}><i class="ti ti-player-stop"></i> {m.signalGen_stop()}</button>
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="displays" aria-hidden={!started}>
|
||||
{#if analyser}
|
||||
<Oscilloscope {analyser} title={m.signalGen_scope()} />
|
||||
<Spectrum {analyser} title={m.signalGen_spectrum()} />
|
||||
{/if}
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<style>
|
||||
.layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
.controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.controls .row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.controls label {
|
||||
opacity: 0.8;
|
||||
min-width: 6rem;
|
||||
}
|
||||
.controls .buttons {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.controls button {
|
||||
border: 1px solid rgba(255, 255, 255, 0.25);
|
||||
background: rgba(255, 255, 255, 0.07);
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.controls button.active,
|
||||
.controls button.primary {
|
||||
background: rgba(92, 184, 92, 0.2);
|
||||
border-color: #5cb85c;
|
||||
}
|
||||
.controls input[type='range'] {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.controls .value {
|
||||
min-width: 4rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.exact-freq {
|
||||
width: 5em;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.displays {
|
||||
display: grid;
|
||||
grid-template-rows: auto auto;
|
||||
gap: 1rem;
|
||||
}
|
||||
</style>
|
Loading…
Add table
Reference in a new issue