diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000..4ea8c76 Binary files /dev/null and b/assets/icon.png differ diff --git a/i18n/ar.json b/i18n/ar.json new file mode 100755 index 0000000..0db3279 --- /dev/null +++ b/i18n/ar.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/i18n/de.json b/i18n/de.json new file mode 100755 index 0000000..2c63c08 --- /dev/null +++ b/i18n/de.json @@ -0,0 +1,2 @@ +{ +} diff --git a/i18n/en.json b/i18n/en.json new file mode 100755 index 0000000..5efbe30 --- /dev/null +++ b/i18n/en.json @@ -0,0 +1,3 @@ +{ + "settings": "Settings" +} diff --git a/i18n/es.json b/i18n/es.json new file mode 100755 index 0000000..2c63c08 --- /dev/null +++ b/i18n/es.json @@ -0,0 +1,2 @@ +{ +} diff --git a/i18n/fr.json b/i18n/fr.json new file mode 100755 index 0000000..2c63c08 --- /dev/null +++ b/i18n/fr.json @@ -0,0 +1,2 @@ +{ +} diff --git a/i18n/it.json b/i18n/it.json new file mode 100755 index 0000000..2c63c08 --- /dev/null +++ b/i18n/it.json @@ -0,0 +1,2 @@ +{ +} diff --git a/i18n/ja.json b/i18n/ja.json new file mode 100644 index 0000000..0db3279 --- /dev/null +++ b/i18n/ja.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/i18n/ko.json b/i18n/ko.json new file mode 100755 index 0000000..0db3279 --- /dev/null +++ b/i18n/ko.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/i18n/zh.json b/i18n/zh.json new file mode 100644 index 0000000..7a7ef51 --- /dev/null +++ b/i18n/zh.json @@ -0,0 +1,3 @@ +{ + "settings": "设置" +} diff --git a/package.json b/package.json index 35b0618..908a073 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,6 @@ "version": "1.0.0", "type": "module", "description": "", - "main": "src/main.js", "scripts": { "dev": "cross-env NODE_ENV=dev bun run dev:all", "dev:all": "concurrently -n=react,electron -c='#ff3e00',blue \"bun run dev:react\" \"bun run dev:electron\"", @@ -19,8 +18,14 @@ "electron-reloader": "^1.2.3", "electron-serve": "^2.1.1", "electron-window-state": "^5.0.3", + "i18next": "^24.0.2", + "i18next-browser-languagedetector": "^8.0.0", + "i18next-icu": "^2.3.0", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "react-i18next": "^15.1.2", + "react-router-dom": "^7.0.1", + "vite-tsconfig-paths": "^5.1.3" }, "devDependencies": { "@eslint/js": "^9.13.0", diff --git a/pages/settings/index.tsx b/pages/settings/index.tsx new file mode 100644 index 0000000..926f1c2 --- /dev/null +++ b/pages/settings/index.tsx @@ -0,0 +1,10 @@ +import { useTranslation } from "react-i18next"; + +export default function SettingsPage() { + const { t } = useTranslation(); + return ( +
+

{t('settings')}

+
+ ) +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5d5bc24..3ef8f67 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,12 +23,30 @@ importers: electron-window-state: specifier: ^5.0.3 version: 5.0.3 + i18next: + specifier: ^24.0.2 + version: 24.0.2(typescript@5.6.3) + i18next-browser-languagedetector: + specifier: ^8.0.0 + version: 8.0.0 + i18next-icu: + specifier: ^2.3.0 + version: 2.3.0(intl-messageformat@10.7.7) react: specifier: ^18.3.1 version: 18.3.1 react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + react-i18next: + specifier: ^15.1.2 + version: 15.1.2(i18next@24.0.2(typescript@5.6.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-router-dom: + specifier: ^7.0.1 + version: 7.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + vite-tsconfig-paths: + specifier: ^5.1.3 + version: 5.1.3(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.7)) devDependencies: '@eslint/js': specifier: ^9.13.0 @@ -50,13 +68,13 @@ importers: version: 7.0.3 eslint: specifier: ^9.13.0 - version: 9.15.0 + version: 9.15.0(jiti@1.21.6) eslint-plugin-react-hooks: specifier: ^5.0.0 - version: 5.0.0(eslint@9.15.0) + version: 5.0.0(eslint@9.15.0(jiti@1.21.6)) eslint-plugin-react-refresh: specifier: ^0.4.14 - version: 0.4.14(eslint@9.15.0) + version: 0.4.14(eslint@9.15.0(jiti@1.21.6)) globals: specifier: ^15.11.0 version: 15.12.0 @@ -65,7 +83,7 @@ importers: version: 5.6.3 typescript-eslint: specifier: ^8.11.0 - version: 8.15.0(eslint@9.15.0)(typescript@5.6.3) + version: 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) vite: specifier: ^5.4.10 version: 5.4.11(@types/node@20.17.7) @@ -143,6 +161,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/runtime@7.26.0': + resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} + engines: {node: '>=6.9.0'} + '@babel/template@7.25.9': resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} engines: {node: '>=6.9.0'} @@ -331,6 +353,21 @@ packages: resolution: {integrity: sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@formatjs/ecma402-abstract@2.2.4': + resolution: {integrity: sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==} + + '@formatjs/fast-memoize@2.2.3': + resolution: {integrity: sha512-3jeJ+HyOfu8osl3GNSL4vVHUuWFXR03Iz9jjgI7RwjG6ysu/Ymdr0JRCPHfF5yGbTE6JCrd63EpvX1/WybYRbA==} + + '@formatjs/icu-messageformat-parser@2.9.4': + resolution: {integrity: sha512-Tbvp5a9IWuxUcpWNIW6GlMQYEc4rwNHR259uUFoKWNN1jM9obf9Ul0e+7r7MvFOBNcN+13K7NuKCKqQiAn1QEg==} + + '@formatjs/icu-skeleton-parser@1.8.8': + resolution: {integrity: sha512-vHwK3piXwamFcx5YQdCdJxUQ1WdTl6ANclt5xba5zLGDv5Bsur7qz8AD7BevaKxITwpgDeU0u8My3AIibW9ywA==} + + '@formatjs/intl-localematcher@0.5.8': + resolution: {integrity: sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==} + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -494,6 +531,9 @@ packages: '@types/cacheable-request@6.0.3': resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} @@ -709,6 +749,10 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + cross-env@7.0.3: resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} @@ -999,6 +1043,9 @@ packages: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} @@ -1031,6 +1078,9 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + html-parse-stringify@3.0.1: + resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} + http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} @@ -1038,6 +1088,22 @@ packages: resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} engines: {node: '>=10.19.0'} + i18next-browser-languagedetector@8.0.0: + resolution: {integrity: sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==} + + i18next-icu@2.3.0: + resolution: {integrity: sha512-x+j7kd5nDJCfbU53uwsMfXD7ALPu5uv0bqjAMQ5nVvXRoj1L7gkmswKtM3XDWYo4YUHf1jznlhSdPyy0xEwU+Q==} + peerDependencies: + intl-messageformat: ^10.3.3 + + i18next@24.0.2: + resolution: {integrity: sha512-D88xyIGcWAKwBTAs4RSqASi8NXR/NhCVSTM4LDbdoU8qb/5dcEZjNCLDhtQBB7Epw/Cp1w2vH/3ujoTbqLSs5g==} + peerDependencies: + typescript: ^5 + peerDependenciesMeta: + typescript: + optional: true + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1050,6 +1116,9 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + intl-messageformat@10.7.7: + resolution: {integrity: sha512-F134jIoeYMro/3I0h08D0Yt4N9o9pjddU/4IIxMMURqbAtI2wu70X8hvG1V48W49zXHXv3RKSF/po+0fDfsGjA==} + is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -1081,6 +1150,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + jiti@1.21.6: + resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + hasBin: true + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1285,10 +1358,40 @@ packages: peerDependencies: react: ^18.3.1 + react-i18next@15.1.2: + resolution: {integrity: sha512-tl7AfbWyz9a4BefFXnVooc+gvQBVlavUkVTphGUcvhsNmbRf5UixJVdHeSFkE4gUyQkmFPYHVwTuxIdHjfQgiA==} + peerDependencies: + i18next: '>= 23.2.3' + react: '>= 16.8.0' + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-router-dom@7.0.1: + resolution: {integrity: sha512-duBzwAAiIabhFPZfDjcYpJ+f08TMbPMETgq254GWne2NW1ZwRHhZLj7tpSp8KGb7JvZzlLcjGUnqLxpZQVEPng==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.0.1: + resolution: {integrity: sha512-WVAhv9oWCNsja5AkK6KLpXJDSJCQizOIyOd4vvB/+eHGbYx5vkhcmcmwWjQ9yqkRClogi+xjEg9fNEOd5EX/tw==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -1297,6 +1400,9 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -1349,6 +1455,9 @@ packages: resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} engines: {node: '>=10'} + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1429,9 +1538,22 @@ packages: peerDependencies: typescript: '>=4.2.0' + tsconfck@3.1.4: + resolution: {integrity: sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + turbo-stream@2.4.0: + resolution: {integrity: sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -1475,6 +1597,14 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + vite-tsconfig-paths@5.1.3: + resolution: {integrity: sha512-0bz+PDlLpGfP2CigeSKL9NFTF1KtXkeHGZSSaGQSuPZH77GhoiQaA8IjYgOaynSuwlDTolSUEU0ErVvju3NURg==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + vite@5.4.11: resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==} engines: {node: ^18.0.0 || >=20.0.0} @@ -1506,6 +1636,10 @@ packages: terser: optional: true + void-elements@3.1.0: + resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} + engines: {node: '>=0.10.0'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1638,6 +1772,10 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.25.9 + '@babel/runtime@7.26.0': + dependencies: + regenerator-runtime: 0.14.1 + '@babel/template@7.25.9': dependencies: '@babel/code-frame': 7.26.2 @@ -1744,9 +1882,9 @@ snapshots: '@esbuild/win32-x64@0.21.5': optional: true - '@eslint-community/eslint-utils@4.4.1(eslint@9.15.0)': + '@eslint-community/eslint-utils@4.4.1(eslint@9.15.0(jiti@1.21.6))': dependencies: - eslint: 9.15.0 + eslint: 9.15.0(jiti@1.21.6) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -1783,6 +1921,31 @@ snapshots: dependencies: levn: 0.4.1 + '@formatjs/ecma402-abstract@2.2.4': + dependencies: + '@formatjs/fast-memoize': 2.2.3 + '@formatjs/intl-localematcher': 0.5.8 + tslib: 2.8.1 + + '@formatjs/fast-memoize@2.2.3': + dependencies: + tslib: 2.8.1 + + '@formatjs/icu-messageformat-parser@2.9.4': + dependencies: + '@formatjs/ecma402-abstract': 2.2.4 + '@formatjs/icu-skeleton-parser': 1.8.8 + tslib: 2.8.1 + + '@formatjs/icu-skeleton-parser@1.8.8': + dependencies: + '@formatjs/ecma402-abstract': 2.2.4 + tslib: 2.8.1 + + '@formatjs/intl-localematcher@0.5.8': + dependencies: + tslib: 2.8.1 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -1913,6 +2076,8 @@ snapshots: '@types/node': 20.17.7 '@types/responselike': 1.0.3 + '@types/cookie@0.6.0': {} + '@types/estree@1.0.6': {} '@types/http-cache-semantics@4.0.4': {} @@ -1947,15 +2112,15 @@ snapshots: '@types/node': 20.17.7 optional: true - '@typescript-eslint/eslint-plugin@8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.6.3))(eslint@9.15.0)(typescript@5.6.3)': + '@typescript-eslint/eslint-plugin@8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.15.0(eslint@9.15.0)(typescript@5.6.3) + '@typescript-eslint/parser': 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) '@typescript-eslint/scope-manager': 8.15.0 - '@typescript-eslint/type-utils': 8.15.0(eslint@9.15.0)(typescript@5.6.3) - '@typescript-eslint/utils': 8.15.0(eslint@9.15.0)(typescript@5.6.3) + '@typescript-eslint/type-utils': 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/utils': 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) '@typescript-eslint/visitor-keys': 8.15.0 - eslint: 9.15.0 + eslint: 9.15.0(jiti@1.21.6) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -1965,14 +2130,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.6.3)': + '@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': dependencies: '@typescript-eslint/scope-manager': 8.15.0 '@typescript-eslint/types': 8.15.0 '@typescript-eslint/typescript-estree': 8.15.0(typescript@5.6.3) '@typescript-eslint/visitor-keys': 8.15.0 debug: 4.3.7 - eslint: 9.15.0 + eslint: 9.15.0(jiti@1.21.6) optionalDependencies: typescript: 5.6.3 transitivePeerDependencies: @@ -1983,12 +2148,12 @@ snapshots: '@typescript-eslint/types': 8.15.0 '@typescript-eslint/visitor-keys': 8.15.0 - '@typescript-eslint/type-utils@8.15.0(eslint@9.15.0)(typescript@5.6.3)': + '@typescript-eslint/type-utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': dependencies: '@typescript-eslint/typescript-estree': 8.15.0(typescript@5.6.3) - '@typescript-eslint/utils': 8.15.0(eslint@9.15.0)(typescript@5.6.3) + '@typescript-eslint/utils': 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) debug: 4.3.7 - eslint: 9.15.0 + eslint: 9.15.0(jiti@1.21.6) ts-api-utils: 1.4.0(typescript@5.6.3) optionalDependencies: typescript: 5.6.3 @@ -2012,13 +2177,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.15.0(eslint@9.15.0)(typescript@5.6.3)': + '@typescript-eslint/utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.15.0) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.15.0(jiti@1.21.6)) '@typescript-eslint/scope-manager': 8.15.0 '@typescript-eslint/types': 8.15.0 '@typescript-eslint/typescript-estree': 8.15.0(typescript@5.6.3) - eslint: 9.15.0 + eslint: 9.15.0(jiti@1.21.6) optionalDependencies: typescript: 5.6.3 transitivePeerDependencies: @@ -2167,6 +2332,8 @@ snapshots: convert-source-map@2.0.0: {} + cookie@1.0.2: {} + cross-env@7.0.3: dependencies: cross-spawn: 7.0.6 @@ -2308,13 +2475,13 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-plugin-react-hooks@5.0.0(eslint@9.15.0): + eslint-plugin-react-hooks@5.0.0(eslint@9.15.0(jiti@1.21.6)): dependencies: - eslint: 9.15.0 + eslint: 9.15.0(jiti@1.21.6) - eslint-plugin-react-refresh@0.4.14(eslint@9.15.0): + eslint-plugin-react-refresh@0.4.14(eslint@9.15.0(jiti@1.21.6)): dependencies: - eslint: 9.15.0 + eslint: 9.15.0(jiti@1.21.6) eslint-scope@8.2.0: dependencies: @@ -2325,9 +2492,9 @@ snapshots: eslint-visitor-keys@4.2.0: {} - eslint@9.15.0: + eslint@9.15.0(jiti@1.21.6): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.15.0) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.15.0(jiti@1.21.6)) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.19.0 '@eslint/core': 0.9.0 @@ -2361,6 +2528,8 @@ snapshots: minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.4 + optionalDependencies: + jiti: 1.21.6 transitivePeerDependencies: - supports-color @@ -2504,6 +2673,8 @@ snapshots: gopd: 1.0.1 optional: true + globrex@0.1.2: {} + gopd@1.0.1: dependencies: get-intrinsic: 1.2.4 @@ -2545,6 +2716,10 @@ snapshots: function-bind: 1.1.2 optional: true + html-parse-stringify@3.0.1: + dependencies: + void-elements: 3.1.0 + http-cache-semantics@4.1.1: {} http2-wrapper@1.0.3: @@ -2552,6 +2727,20 @@ snapshots: quick-lru: 5.1.1 resolve-alpn: 1.2.1 + i18next-browser-languagedetector@8.0.0: + dependencies: + '@babel/runtime': 7.26.0 + + i18next-icu@2.3.0(intl-messageformat@10.7.7): + dependencies: + intl-messageformat: 10.7.7 + + i18next@24.0.2(typescript@5.6.3): + dependencies: + '@babel/runtime': 7.26.0 + optionalDependencies: + typescript: 5.6.3 + ignore@5.3.2: {} import-fresh@3.3.0: @@ -2561,6 +2750,13 @@ snapshots: imurmurhash@0.1.4: {} + intl-messageformat@10.7.7: + dependencies: + '@formatjs/ecma402-abstract': 2.2.4 + '@formatjs/fast-memoize': 2.2.3 + '@formatjs/icu-messageformat-parser': 2.9.4 + tslib: 2.8.1 + is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 @@ -2581,6 +2777,9 @@ snapshots: isexe@2.0.0: {} + jiti@1.21.6: + optional: true + js-tokens@4.0.0: {} js-yaml@4.1.0: @@ -2748,8 +2947,33 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 + react-i18next@15.1.2(i18next@24.0.2(typescript@5.6.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + html-parse-stringify: 3.0.1 + i18next: 24.0.2(typescript@5.6.3) + react: 18.3.1 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + react-refresh@0.14.2: {} + react-router-dom@7.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 7.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + + react-router@7.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@types/cookie': 0.6.0 + cookie: 1.0.2 + react: 18.3.1 + set-cookie-parser: 2.7.1 + turbo-stream: 2.4.0 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + react@18.3.1: dependencies: loose-envify: 1.4.0 @@ -2758,6 +2982,8 @@ snapshots: dependencies: picomatch: 2.3.1 + regenerator-runtime@0.14.1: {} + require-directory@2.1.1: {} resolve-alpn@1.2.1: {} @@ -2828,6 +3054,8 @@ snapshots: type-fest: 0.13.1 optional: true + set-cookie-parser@2.7.1: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -2902,8 +3130,14 @@ snapshots: dependencies: typescript: 5.6.3 + tsconfck@3.1.4(typescript@5.6.3): + optionalDependencies: + typescript: 5.6.3 + tslib@2.8.1: {} + turbo-stream@2.4.0: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -2911,12 +3145,12 @@ snapshots: type-fest@0.13.1: optional: true - typescript-eslint@8.15.0(eslint@9.15.0)(typescript@5.6.3): + typescript-eslint@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0)(typescript@5.6.3))(eslint@9.15.0)(typescript@5.6.3) - '@typescript-eslint/parser': 8.15.0(eslint@9.15.0)(typescript@5.6.3) - '@typescript-eslint/utils': 8.15.0(eslint@9.15.0)(typescript@5.6.3) - eslint: 9.15.0 + '@typescript-eslint/eslint-plugin': 8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/parser': 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/utils': 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + eslint: 9.15.0(jiti@1.21.6) optionalDependencies: typescript: 5.6.3 transitivePeerDependencies: @@ -2943,6 +3177,17 @@ snapshots: dependencies: punycode: 2.3.1 + vite-tsconfig-paths@5.1.3(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.7)): + dependencies: + debug: 4.3.7 + globrex: 0.1.2 + tsconfck: 3.1.4(typescript@5.6.3) + optionalDependencies: + vite: 5.4.11(@types/node@20.17.7) + transitivePeerDependencies: + - supports-color + - typescript + vite@5.4.11(@types/node@20.17.7): dependencies: esbuild: 0.21.5 @@ -2952,6 +3197,8 @@ snapshots: '@types/node': 20.17.7 fsevents: 2.3.3 + void-elements@3.1.0: {} + which@2.0.2: dependencies: isexe: 2.0.0 diff --git a/screen.png b/screen.png deleted file mode 100644 index 9000131..0000000 Binary files a/screen.png and /dev/null differ diff --git a/src/App.css b/src/App.css deleted file mode 100644 index b9d355d..0000000 --- a/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/src/App.tsx b/src/App.tsx deleted file mode 100644 index dde66ca..0000000 --- a/src/App.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import {useState} from 'react' - -function App() { - const [count, setCount] = useState(0) - - return ( -
-

Vite + React

-
- -

- Edit src/App.tsx and save to test HMR -

s -
-

- Click on the Vite and React logos to learn more -

-
- ) -} - -export default App diff --git a/src/app.tsx b/src/app.tsx new file mode 100644 index 0000000..3954f16 --- /dev/null +++ b/src/app.tsx @@ -0,0 +1,18 @@ +import { createBrowserRouter, RouterProvider } from "react-router-dom"; +import SettingsPage from "pages/settings"; +import "./i18n.ts"; + +const router = createBrowserRouter([ + { + path: "/settings", + element: + } +]); + +export function App() { + return ( +
+ +
+ ); +} diff --git a/src/electron.js b/src/electron.js index 1bf329e..2d18722 100644 --- a/src/electron.js +++ b/src/electron.js @@ -1,20 +1,96 @@ import windowStateManager from 'electron-window-state'; -import { app, BrowserWindow, ipcMain, screen, globalShortcut } from 'electron'; +import { app, BrowserWindow, screen,ipcMain, globalShortcut, Tray, Menu } from 'electron'; import contextMenu from 'electron-context-menu'; import serve from 'electron-serve'; -try { - require('electron-reloader')(module); -} catch (e) { - console.error(e); +let tray = null + +function createTray() { + // 创建托盘图标 + tray = new Tray('./assets/icon.png') + + // 创建托盘菜单 + const contextMenu = Menu.buildFromTemplate([ + { + label: '显示主窗口', + click: () => { + if (!mainWindow) createMainWindow(); + mainWindow.show(); + } + }, + { + label: '显示设置', + click: () => { + if (!settingsWindow) createSettingsWindow(); + settingsWindow.show(); + } + }, + { type: 'separator' }, + { + label: '退出', + click: () => { + app.quit() + } + } + ]) + + // 设置托盘的上下文菜单 + tray.setContextMenu(contextMenu) + + // 设置托盘的提示文字 + tray.setToolTip('我的应用程序') + + // 点击托盘图标时显示主窗口 + // tray.on('click', () => { + // mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show() + // }) } const serveURL = serve({ directory: '.' }); -const port = process.env.PORT || 5173; +const port = process.env.PORT || "5173"; const dev = !app.isPackaged; -let mainWindow; -function createWindow() { +let mainWindow; +let settingsWindow; + + +function createSettingsWindow() { + const window = new BrowserWindow({ + width: 400, + height: 600, + webPreferences: { + nodeIntegration: true, + contextIsolation: false + }, + }); + window.once('ready-to-show', () => { + window.show(); + window.focus(); + }); + + settingsWindow = window; + + if (dev) loadVite(window ,port, "settings"); + else serveURL(mainWindow); +} + +contextMenu({ + showLookUpSelection: true, + showSearchWithGoogle: true, + showCopyImage: true, +}); + +function loadVite(window, port, path = "") { + console.log(`http://localhost:${port}/${path}`); + window.loadURL(`http://localhost:${port}/${path}`).catch((e) => { + console.log('Error loading URL, retrying', e); + setTimeout(() => { + loadVite(window, port, path); + }, 1000); + }); +} + +function createMainWindow() { const display = screen.getPrimaryDisplay(); const { width, height } = display.bounds; let windowState = windowStateManager({ @@ -22,7 +98,7 @@ function createWindow() { defaultHeight: height, }); - const mainWindow = new BrowserWindow({ + const window = new BrowserWindow({ width, height, x: 0, @@ -40,62 +116,44 @@ function createWindow() { roundedCorners: false }); - windowState.manage(mainWindow); + windowState.manage(window); - mainWindow.once('ready-to-show', () => { - mainWindow.show(); - mainWindow.setAlwaysOnTop(true, 'screen-saver'); - mainWindow.setBounds({ x: 0, y: 0, width, height }); - mainWindow.focus(); + window.once('ready-to-show', () => { + window.show(); + window.setAlwaysOnTop(true, 'screen-saver'); + window.setBounds({ x: 0, y: 0, width, height }); + window.focus(); }); - mainWindow.on('close', () => { - windowState.saveState(mainWindow); + window.on('close', () => { + windowState.saveState(window); }); - - return mainWindow; -} - -contextMenu({ - showLookUpSelection: true, - showSearchWithGoogle: true, - showCopyImage: true, -}); - -function loadVite(port) { - mainWindow.loadURL(`http://localhost:${port}`).catch((e) => { - console.log('Error loading URL, retrying', e); - setTimeout(() => { - loadVite(port); - }, 200); - }); -} - -function createMainWindow() { - mainWindow = createWindow(); - mainWindow.once('close', () => { + window.once('close', () => { mainWindow = null; }); + mainWindow = window; + if (dev) loadVite(port); else serveURL(mainWindow); } -app.once('ready', createMainWindow); -app.on('activate', () => { - if (!mainWindow) { - createMainWindow(); - } +app.once('ready', () => { + app.dock.hide(); }); +app.on('activate', () => { +}); + app.on('ready', () => { + createTray(); globalShortcut.register('Escape', () => { - app.quit(); + mainWindow.hide(); }); }); app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); }); -ipcMain.on('to-main', (event, count) => { +ipcMain.on('to-main', (_event, count) => { return mainWindow.webContents.send('from-main', `next count is ${count + 1}`); }); \ No newline at end of file diff --git a/src/i18n.ts b/src/i18n.ts new file mode 100644 index 0000000..16b49c6 --- /dev/null +++ b/src/i18n.ts @@ -0,0 +1,53 @@ +import * as en from "i18n/en.json"; +import * as zh from "i18n/zh.json"; +import * as ja from "i18n/ja.json"; +import * as de from "i18n/de.json"; +import * as es from "i18n/es.json"; +import * as fr from "i18n/fr.json"; +import * as it from "i18n/it.json"; +import * as ko from "i18n/ko.json"; +import i18n from "i18next"; +import { initReactI18next } from "react-i18next"; +import LanguageDetector from "i18next-browser-languagedetector"; +import ICU from "i18next-icu"; +i18n.use(initReactI18next) // passes i18n down to react-i18next + .use(LanguageDetector) + .use(ICU) + .init({ + resources: { + en: { + translation: en + }, + zh: { + translation: zh + }, + ja: { + translation: ja + }, + de: { + translation: de + }, + es: { + translation: es + }, + fr: { + translation: fr + }, + it: { + translation: it + }, + ko: { + translation: ko + }, + }, + fallbackLng: "en", + + interpolation: { + escapeValue: false // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape + }, + + detection: { + order: ["navigator"], + caches: [] + } + }); diff --git a/src/index.css b/src/index.css index 6119ad9..b5c61c9 100644 --- a/src/index.css +++ b/src/index.css @@ -1,68 +1,3 @@ -:root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/src/main.tsx b/src/main.tsx index bef5202..ea4e6af 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,10 +1,12 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.tsx' +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { App } from "./app"; +import "./index.css"; -createRoot(document.getElementById('root')!).render( - - - , -) +const app = createRoot(document.getElementById("root")!); + +app.render( + + + +); diff --git a/tailwind.config.ts b/tailwind.config.ts new file mode 100644 index 0000000..90c1bed --- /dev/null +++ b/tailwind.config.ts @@ -0,0 +1,19 @@ +import type { Config } from "tailwindcss"; + +const config: Config = { + content: [ + "./pages/**/*.{js,ts,jsx,tsx,mdx}", + "./components/**/*.{js,ts,jsx,tsx,mdx}", + "./src/**/*.{js,ts,jsx,tsx,mdx}" + ], + theme: { + extend: { + backgroundImage: { + "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", + "gradient-conic": "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))" + } + } + }, + plugins: [] +}; +export default config; diff --git a/tsconfig.app.json b/tsconfig.app.json index 88664c8..78479d1 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -1,6 +1,7 @@ { "compilerOptions": { "baseUrl": ".", + "composite": true, "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "target": "ES2020", "useDefineForClassFields": true, @@ -9,8 +10,9 @@ "skipLibCheck": true, /* Bundler mode */ - "moduleResolution": "Bundler", + "moduleResolution": "bundler", "allowImportingTsExtensions": true, + "resolveJsonModule": true, "isolatedModules": true, "moduleDetection": "force", "noEmit": true, @@ -20,10 +22,7 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true + "noFallthroughCasesInSwitch": true }, - "include": [ - "src" - ] + "include": ["src", "**/*.ts", "**/*.tsx", "global.d.ts"] } diff --git a/vite.config.ts b/vite.config.ts index 8b0f57b..46d3965 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,8 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' +import tsconfigPaths from "vite-tsconfig-paths"; // https://vite.dev/config/ export default defineConfig({ - plugins: [react()], + plugins: [react(),tsconfigPaths()], })