diff --git a/.tauri/myapp.key b/.tauri/myapp.key new file mode 100644 index 0000000..c4222c4 --- /dev/null +++ b/.tauri/myapp.key @@ -0,0 +1 @@ +dW50cnVzdGVkIGNvbW1lbnQ6IHJzaWduIGVuY3J5cHRlZCBzZWNyZXQga2V5ClJXUlRZMEl5b29vWWcwSnZkbzlYQWNNQzJuVEZ2dlJJd3A0MUhTcEpCYmZubFdlVmVvY0FBQkFBQUFBQUFBQUFBQUlBQUFBQXEveUNRTnFXUEE5Q3M5Und2YTFlQWdRUVh1L3hudGJaTnZlRlRHM3FwZkVHQXBhNlpQWmF3bkcwTGJ5N1MwbEFIRFVXM3lCc3QxdUVOeDJqeVdVK29lZkllRmxWZ2FxcW1CK2cvYTFwMUptQ0hMZEpjZXhpdU1Ndm9CVWcrTk91aXBiSHB4MVBKdU09Cg== \ No newline at end of file diff --git a/.tauri/myapp.key.pub b/.tauri/myapp.key.pub new file mode 100644 index 0000000..11546bd --- /dev/null +++ b/.tauri/myapp.key.pub @@ -0,0 +1 @@ +dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEYxRDZBQjc3QUQxNDBGNDUKUldSRkR4U3RkNnZXOGNtdHE4TS80d3p4UHlhaEVVMDFUTFRrQmhySnFVNWdZWHNXOWUraFFhRTcK \ No newline at end of file diff --git a/dev-vs.cmd b/dev-vs.cmd new file mode 100644 index 0000000..36e6053 --- /dev/null +++ b/dev-vs.cmd @@ -0,0 +1,6 @@ +@echo off +call "C:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build\vcvarsall.bat" arm64 +set PATH=C:\Users\OseasyVM\scoop\apps\llvm\current\bin;%PATH% +set CC=clang +cd /d "%~dp0" +pnpm dev:t diff --git a/package.json b/package.json index 200392b..8561ca6 100644 --- a/package.json +++ b/package.json @@ -12,15 +12,20 @@ "build:beta": "node scripts/switch-icon.js beta && tsc && vite build --mode beta", "preview": "vite preview", "tauri": "tauri", - "dev:t": "node scripts/switch-icon.js dev && tauri dev" + "dev:t": "node scripts/switch-icon.js dev && tauri dev", + "dev:vs": "./dev-vs.cmd" }, "dependencies": { "@base-ui/react": "^1.6.0", "@fontsource-variable/geist": "^5.2.9", "@fontsource-variable/inter": "^5.2.8", + "@react-three/fiber": "^9.6.1", "@tauri-apps/api": "^2", "@tauri-apps/plugin-opener": "^2", + "@tauri-apps/plugin-process": "^2.3.1", "@tauri-apps/plugin-shell": "^2.3.5", + "@tauri-apps/plugin-updater": "^2.10.1", + "@types/three": "^0.184.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^1.21.0", @@ -28,6 +33,7 @@ "react-dom": "^19.1.0", "shadcn": "^4.11.0", "tailwind-merge": "^3.6.0", + "three": "^0.184.0", "tw-animate-css": "^1.4.0", "zustand": "^5.0.14" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4cc539c..c167768 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,15 +17,27 @@ importers: '@fontsource-variable/inter': specifier: ^5.2.8 version: 5.2.8 + '@react-three/fiber': + specifier: ^9.6.1 + version: 9.6.1(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(three@0.184.0) '@tauri-apps/api': specifier: ^2 version: 2.11.1 '@tauri-apps/plugin-opener': specifier: ^2 version: 2.5.4 + '@tauri-apps/plugin-process': + specifier: ^2.3.1 + version: 2.3.1 '@tauri-apps/plugin-shell': specifier: ^2.3.5 version: 2.3.5 + '@tauri-apps/plugin-updater': + specifier: ^2.10.1 + version: 2.10.1 + '@types/three': + specifier: ^0.184.1 + version: 0.184.1 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -47,6 +59,9 @@ importers: tailwind-merge: specifier: ^3.6.0 version: 3.6.0 + three: + specifier: ^0.184.0 + version: 0.184.0 tw-animate-css: specifier: ^1.4.0 version: 1.4.0 @@ -253,6 +268,9 @@ packages: '@types/react': optional: true + '@dimforge/rapier3d-compat@0.12.0': + resolution: {integrity: sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==} + '@dotenvx/dotenvx@1.74.2': resolution: {integrity: sha512-UucMF9L95sVr/feRojWB/NIc5R+CzoiiXq7rd7sWppYA7j6CIH5bu5bQCuoJ0HJxQARR0Ecg4bYot3ilo100lw==} hasBin: true @@ -496,6 +514,31 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@react-three/fiber@9.6.1': + resolution: {integrity: sha512-zF0rsKcVYpcJwbFEnv2HkHX9cvOEgsfQo/X8lwmR2dn13S4qEQJXir9fxf5js2LQFoXqxOY7MDkOkYx2uZ4gSg==} + peerDependencies: + expo: '>=43.0' + expo-asset: '>=8.4' + expo-file-system: '>=11.0' + expo-gl: '>=11.0' + react: '>=19 <19.3' + react-dom: '>=19 <19.3' + react-native: '>=0.78' + three: '>=0.156' + peerDependenciesMeta: + expo: + optional: true + expo-asset: + optional: true + expo-file-system: + optional: true + expo-gl: + optional: true + react-dom: + optional: true + react-native: + optional: true + '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} @@ -820,12 +863,21 @@ packages: '@tauri-apps/plugin-opener@2.5.4': resolution: {integrity: sha512-1HnPkb+AmgO29HBazm4uPLKB+r7zzcTBW1d0fyYp1uP+jwtpoiNDGKMMzz58SFp49nOIrxdE3aUJtT57lfO9CQ==} + '@tauri-apps/plugin-process@2.3.1': + resolution: {integrity: sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA==} + '@tauri-apps/plugin-shell@2.3.5': resolution: {integrity: sha512-jewtULhiQ7lI7+owCKAjc8tYLJr92U16bPOeAa472LHJdgaibLP83NcfAF2e+wkEcA53FxKQAZ7byDzs2eeizg==} + '@tauri-apps/plugin-updater@2.10.1': + resolution: {integrity: sha512-NFYMg+tWOZPJdzE/PpFj2qfqwAWwNS3kXrb1tm1gnBJ9mYzZ4WDRrwy8udzWoAnfGCHLuePNLY1WVCNHnh3eRA==} + '@ts-morph/common@0.27.0': resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==} + '@tweenjs/tween.js@23.1.3': + resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -846,12 +898,26 @@ packages: peerDependencies: '@types/react': ^19.2.0 + '@types/react-reconciler@0.28.9': + resolution: {integrity: sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==} + peerDependencies: + '@types/react': '*' + '@types/react@19.2.17': resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} + '@types/stats.js@0.17.4': + resolution: {integrity: sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==} + + '@types/three@0.184.1': + resolution: {integrity: sha512-6q4VdiqVsrTRqmk62/BnlcAvIrnDM0zf2ZDVKI5kZiniWrSaOHaQzmbp+BNzoggc/8tgW412pL//wZIxu2PPTA==} + '@types/validate-npm-package-name@4.0.2': resolution: {integrity: sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw==} + '@types/webxr@0.5.24': + resolution: {integrity: sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==} + '@vitejs/plugin-react@4.7.0': resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} engines: {node: ^14.18.0 || >=16.0.0} @@ -912,6 +978,9 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + baseline-browser-mapping@2.10.38: resolution: {integrity: sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==} engines: {node: '>=6.0.0'} @@ -934,6 +1003,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} @@ -1225,6 +1297,9 @@ packages: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} @@ -1339,6 +1414,9 @@ packages: resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1446,6 +1524,11 @@ packages: resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==} engines: {node: '>=18'} + its-fine@2.0.0: + resolution: {integrity: sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==} + peerDependencies: + react: ^19.0.0 + jiti@2.7.0: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true @@ -1608,6 +1691,9 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + meshoptimizer@1.1.1: + resolution: {integrity: sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -1824,6 +1910,15 @@ packages: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} + react-use-measure@2.1.7: + resolution: {integrity: sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==} + peerDependencies: + react: '>=16.13' + react-dom: '>=16.13' + peerDependenciesMeta: + react-dom: + optional: true + react@19.2.7: resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} engines: {node: '>=0.10.0'} @@ -1975,6 +2070,11 @@ packages: resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} engines: {node: '>=18'} + suspend-react@0.1.3: + resolution: {integrity: sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==} + peerDependencies: + react: '>=17.0' + systeminformation@5.31.7: resolution: {integrity: sha512-/8NC53e5nP9nmhn42/ncdOkyJnOoue/Vy+tJOyUGd1Yv66G069wK4rrziwhrqDETgk78CudTQupw5z19S5uoZw==} engines: {node: '>=8.0.0'} @@ -1991,6 +2091,9 @@ packages: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} + three@0.184.0: + resolution: {integrity: sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg==} + tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -2387,6 +2490,8 @@ snapshots: optionalDependencies: '@types/react': 19.2.17 + '@dimforge/rapier3d-compat@0.12.0': {} + '@dotenvx/dotenvx@1.74.2': dependencies: commander: 11.1.0 @@ -2574,6 +2679,26 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 + '@react-three/fiber@9.6.1(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(three@0.184.0)': + dependencies: + '@babel/runtime': 7.29.7 + '@types/webxr': 0.5.24 + base64-js: 1.5.1 + buffer: 6.0.3 + its-fine: 2.0.0(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-use-measure: 2.1.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + scheduler: 0.27.0 + suspend-react: 0.1.3(react@19.2.7) + three: 0.184.0 + use-sync-external-store: 1.6.0(react@19.2.7) + zustand: 5.0.14(@types/react@19.2.17)(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)) + optionalDependencies: + react-dom: 19.2.7(react@19.2.7) + transitivePeerDependencies: + - '@types/react' + - immer + '@rolldown/pluginutils@1.0.0-beta.27': {} '@rollup/rollup-android-arm-eabi@4.62.0': @@ -2776,16 +2901,26 @@ snapshots: dependencies: '@tauri-apps/api': 2.11.1 + '@tauri-apps/plugin-process@2.3.1': + dependencies: + '@tauri-apps/api': 2.11.1 + '@tauri-apps/plugin-shell@2.3.5': dependencies: '@tauri-apps/api': 2.11.1 + '@tauri-apps/plugin-updater@2.10.1': + dependencies: + '@tauri-apps/api': 2.11.1 + '@ts-morph/common@0.27.0': dependencies: fast-glob: 3.3.3 minimatch: 10.2.5 path-browserify: 1.0.1 + '@tweenjs/tween.js@23.1.3': {} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.29.7 @@ -2813,12 +2948,29 @@ snapshots: dependencies: '@types/react': 19.2.17 + '@types/react-reconciler@0.28.9(@types/react@19.2.17)': + dependencies: + '@types/react': 19.2.17 + '@types/react@19.2.17': dependencies: csstype: 3.2.3 + '@types/stats.js@0.17.4': {} + + '@types/three@0.184.1': + dependencies: + '@dimforge/rapier3d-compat': 0.12.0 + '@tweenjs/tween.js': 23.1.3 + '@types/stats.js': 0.17.4 + '@types/webxr': 0.5.24 + fflate: 0.8.3 + meshoptimizer: 1.1.1 + '@types/validate-npm-package-name@4.0.2': {} + '@types/webxr@0.5.24': {} + '@vitejs/plugin-react@4.7.0(vite@7.3.5(jiti@2.7.0)(lightningcss@1.32.0))': dependencies: '@babel/core': 7.29.7 @@ -2869,6 +3021,8 @@ snapshots: balanced-match@4.0.4: {} + base64-js@1.5.1: {} + baseline-browser-mapping@2.10.38: {} body-parser@2.3.0: @@ -2901,6 +3055,11 @@ snapshots: node-releases: 2.0.48 update-browserslist-db: 1.2.3(browserslist@4.28.2) + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + bundle-name@4.1.0: dependencies: run-applescript: 7.1.0 @@ -3205,6 +3364,8 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 + fflate@0.8.3: {} + figures@6.1.0: dependencies: is-unicode-supported: 2.1.0 @@ -3319,6 +3480,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + ieee754@1.2.1: {} + ignore@5.3.2: {} import-fresh@3.3.1: @@ -3384,6 +3547,13 @@ snapshots: isexe@3.1.5: {} + its-fine@2.0.0(@types/react@19.2.17)(react@19.2.7): + dependencies: + '@types/react-reconciler': 0.28.9(@types/react@19.2.17) + react: 19.2.7 + transitivePeerDependencies: + - '@types/react' + jiti@2.7.0: {} jose@6.2.3: {} @@ -3499,6 +3669,8 @@ snapshots: merge2@1.4.1: {} + meshoptimizer@1.1.1: {} + micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -3692,6 +3864,12 @@ snapshots: react-refresh@0.17.0: {} + react-use-measure@2.1.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + dependencies: + react: 19.2.7 + optionalDependencies: + react-dom: 19.2.7(react@19.2.7) + react@19.2.7: {} recast@0.23.11: @@ -3912,6 +4090,10 @@ snapshots: strip-final-newline@4.0.0: {} + suspend-react@0.1.3(react@19.2.7): + dependencies: + react: 19.2.7 + systeminformation@5.31.7: {} tailwind-merge@3.6.0: {} @@ -3920,6 +4102,8 @@ snapshots: tapable@2.3.3: {} + three@0.184.0: {} + tiny-invariant@1.3.3: {} tinyglobby@0.2.17: diff --git a/splash.html b/splash.html index 3e03a4d..c6b62f1 100644 --- a/splash.html +++ b/splash.html @@ -78,7 +78,7 @@
Provided by Lingke Koring Studio - v0.1.0 + UI预览版本
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 756eb8b..7c6e37e 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -47,6 +47,15 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "async-broadcast" version = "0.7.2" @@ -412,9 +421,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.64" +version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", "shlex", @@ -670,6 +679,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -963,6 +983,16 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "filetime" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" +dependencies = [ + "cfg-if", + "libc", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -1507,6 +1537,21 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.20" @@ -1775,6 +1820,36 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link 0.2.1", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.118", +] + [[package]] name = "jni-sys" version = "0.3.1" @@ -1856,7 +1931,9 @@ dependencies = [ "tauri", "tauri-build", "tauri-plugin-opener", + "tauri-plugin-process", "tauri-plugin-shell", + "tauri-plugin-updater", "tokio", "uuid", ] @@ -1978,6 +2055,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minisign-verify" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f9645cb765ea72b8111f36c522475d2daa0d22c957a9826437e97534bc4e9e" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -2210,6 +2293,7 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.13.0", "block2", + "libc", "objc2", "objc2-core-foundation", ] @@ -2225,6 +2309,18 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "objc2-osa-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + [[package]] name = "objc2-quartz-core" version = "0.3.2" @@ -2300,6 +2396,12 @@ dependencies = [ "pathdiff", ] +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "option-ext" version = "0.2.0" @@ -2326,6 +2428,20 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "osakit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b" +dependencies = [ + "objc2", + "objc2-foundation", + "objc2-osa-kit", + "serde", + "serde_json", + "thiserror 2.0.18", +] + [[package]] name = "pango" version = "0.18.3" @@ -2723,15 +2839,20 @@ dependencies = [ "http-body", "http-body-util", "hyper", + "hyper-rustls", "hyper-util", "js-sys", "log", "percent-encoding", "pin-project-lite", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", "serde", "serde_json", "sync_wrapper", "tokio", + "tokio-rustls", "tokio-util", "tower", "tower-http", @@ -2743,6 +2864,20 @@ dependencies = [ "web-sys", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-hash" version = "2.1.2" @@ -2771,6 +2906,79 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni 0.22.4", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -2786,6 +2994,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "schemars" version = "0.8.22" @@ -2843,6 +3060,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.13.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "selectors" version = "0.36.1" @@ -3095,6 +3335,22 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "siphasher" version = "1.0.3" @@ -3207,6 +3463,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "swift-rs" version = "1.0.7" @@ -3290,7 +3552,7 @@ dependencies = [ "gdkwayland-sys", "gdkx11-sys", "gtk", - "jni", + "jni 0.21.1", "libc", "log", "ndk", @@ -3323,6 +3585,17 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "tar" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "target-lexicon" version = "0.12.16" @@ -3346,7 +3619,7 @@ dependencies = [ "gtk", "heck 0.5.0", "http", - "jni", + "jni 0.21.1", "libc", "log", "mime", @@ -3480,6 +3753,16 @@ dependencies = [ "zbus", ] +[[package]] +name = "tauri-plugin-process" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55511a7bf6cd70c8767b02c97bf8134fa434daf3926cfc1be0a0f94132d165a" +dependencies = [ + "tauri", + "tauri-plugin", +] + [[package]] name = "tauri-plugin-shell" version = "2.3.5" @@ -3501,6 +3784,39 @@ dependencies = [ "tokio", ] +[[package]] +name = "tauri-plugin-updater" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806d9dac662c2e4594ff03c647a552f2c9bd544e7d0f683ec58f872f952ce4af" +dependencies = [ + "base64 0.22.1", + "dirs", + "flate2", + "futures-util", + "http", + "infer", + "log", + "minisign-verify", + "osakit", + "percent-encoding", + "reqwest", + "rustls", + "semver", + "serde", + "serde_json", + "tar", + "tauri", + "tauri-plugin", + "tempfile", + "thiserror 2.0.18", + "time", + "tokio", + "url", + "windows-sys 0.60.2", + "zip", +] + [[package]] name = "tauri-runtime" version = "2.11.3" @@ -3511,7 +3827,7 @@ dependencies = [ "dpi", "gtk", "http", - "jni", + "jni 0.21.1", "objc2", "objc2-ui-kit", "objc2-web-kit", @@ -3534,7 +3850,7 @@ checksum = "fe41e015bf8fc4d6477ff4926a0ef769dc64ff34c7b0038b6f7cacae892acb5c" dependencies = [ "gtk", "http", - "jni", + "jni 0.21.1", "log", "objc2", "objc2-app-kit", @@ -3733,6 +4049,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.18" @@ -4046,6 +4372,12 @@ version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.8" @@ -4295,6 +4627,15 @@ dependencies = [ "system-deps", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webview2-com" version = "0.38.2" @@ -4525,6 +4866,15 @@ dependencies = [ "windows-targets 0.42.2", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -4820,7 +5170,7 @@ dependencies = [ "gtk", "http", "javascriptcore-rs", - "jni", + "jni 0.21.1", "libc", "ndk", "objc2", @@ -4867,6 +5217,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix", +] + [[package]] name = "yoke" version = "0.8.3" @@ -4972,6 +5332,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + [[package]] name = "zerotrie" version = "0.2.4" @@ -5005,6 +5371,18 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "zip" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1" +dependencies = [ + "arbitrary", + "crc32fast", + "indexmap 2.14.0", + "memchr", +] + [[package]] name = "zmij" version = "1.0.21" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index fe31a4c..99df4f7 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -20,7 +20,9 @@ tauri-build = { version = "2", features = [] } [dependencies] tauri = { version = "2", features = [] } tauri-plugin-opener = "2" +tauri-plugin-process = "2" tauri-plugin-shell = "2" +tauri-plugin-updater = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" uuid = { version = "1", features = ["v4"] } diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index a601cdc..c0d8b6b 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -17,7 +17,9 @@ "core:window:allow-set-focus", "core:webview:allow-create-webview-window", "opener:default", + "process:default", "shell:allow-execute", + "updater:default", { "identifier": "shell:allow-execute", "allow": [ diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico index cb43215..a0c4bf7 100644 Binary files a/src-tauri/icons/icon.ico and b/src-tauri/icons/icon.ico differ diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png index 7a24719..704755d 100644 Binary files a/src-tauri/icons/icon.png and b/src-tauri/icons/icon.png differ diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 33a95f8..ce73c96 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -9,7 +9,9 @@ use tauri::Manager; pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_opener::init()) + .plugin(tauri_plugin_process::init()) .plugin(tauri_plugin_shell::init()) + .plugin(tauri_plugin_updater::Builder::new().build()) .manage(Mutex::new(SidecarManager::new())) .invoke_handler(tauri::generate_handler![ commands::sidecar_request, diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 23061a3..1f179cd 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -52,6 +52,15 @@ ], "externalBin": [ "binaries/koring-sidecar" - ] + ], + "createUpdaterArtifacts": true + }, + "plugins": { + "updater": { + "endpoints": [ + "https://cdn.crabnebula.app/update/lingke-network/koring-launcher/{{target}}-{{arch}}/{{current_version}}" + ], + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEYxRDZBQjc3QUQxNDBGNDUKUldSRkR4U3RkNnZXOGNtdHE4TS80d3p4UHlhaEVVMDFUTFRrQmhySnFVNWdZWHNXOWUraFFhRTcK" + } } } diff --git a/src/App.tsx b/src/App.tsx index 74f0f15..cb161a6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,7 +6,10 @@ import { Store } from "./pages/store"; import { Today } from "./pages/today"; import { PlayLink } from "./pages/play-link"; import { Setting } from "./pages/setting"; -import { Debug } from "./pages/setting/debug"; +import { Debug } from "./pages/debug"; +import { SplashDebug } from "./pages/debug/splash-debug"; +import { DisplayDebug } from "./pages/debug/display-debug"; +import { VersionCardDebug } from "./pages/debug/version-card-debug"; const pageMap = { home: Home, @@ -15,6 +18,9 @@ const pageMap = { "play-link": PlayLink, setting: Setting, debug: Debug, + "debug-splash": SplashDebug, + "debug-display": DisplayDebug, + "debug-version-card": VersionCardDebug, } as const; function App() { diff --git a/src/api/update.ts b/src/api/update.ts new file mode 100644 index 0000000..519e4ce --- /dev/null +++ b/src/api/update.ts @@ -0,0 +1,43 @@ +import { check, type Update } from "@tauri-apps/plugin-updater"; +import { relaunch } from "@tauri-apps/plugin-process"; + +export interface DownloadProgress { + downloaded: number; + contentLength: number; + percent: number; +} + +export async function checkForUpdates(): Promise { + const update = await check(); + return update; +} + +export async function downloadAndInstall( + update: Update, + onProgress?: (progress: DownloadProgress) => void, +): Promise { + let downloaded = 0; + let contentLength = 0; + + await update.downloadAndInstall((event) => { + switch (event.event) { + case "Started": + contentLength = event.data.contentLength ?? 0; + break; + case "Progress": + downloaded += event.data.chunkLength; + onProgress?.({ + downloaded, + contentLength, + percent: contentLength > 0 ? Math.round((downloaded / contentLength) * 100) : 0, + }); + break; + case "Finished": + break; + } + }); +} + +export async function relaunchApp(): Promise { + await relaunch(); +} diff --git a/src/components/VersionCard.tsx b/src/components/VersionCard.tsx new file mode 100644 index 0000000..1c9e2af --- /dev/null +++ b/src/components/VersionCard.tsx @@ -0,0 +1,150 @@ +import { BUILD_MODE } from "@/lib/mode"; +import { useUpdateStore } from "@/stores/updateStore"; +import { relaunchApp } from "@/api/update"; +import Silk from "@/components/silk/Silk"; +import { Button } from "@/components/ui/button"; +import clsx from "clsx"; + +const modeColors: Record = { + dev: "#F59E0B", + beta: "#10B981", + run: "#3B82F6", +}; + +const modeLabels: Record = { + dev: "开发版", + beta: "测试版", + run: "正式版", +}; + +const VERSION = "0.1.0"; + +type UpdateState = "latest" | "hasUpdate" | "installed"; + +interface VersionCardProps { + className?: string; + /** 开发者模式:覆盖颜色 */ + overrideMode?: string | null; + /** 开发者模式:覆盖更新状态 */ + overrideState?: UpdateState | null; + /** 开发者模式:遮罩透明度 (0-100) */ + overlayOpacity?: number; + /** 开发者模式:模糊强度 (px) */ + blurAmount?: number; +} + +export function VersionCard({ + className, + overrideMode, + overrideState, + overlayOpacity = 30, + blurAmount = 12, +}: VersionCardProps) { + const color = modeColors[overrideMode ?? BUILD_MODE] ?? modeColors.run; + const label = modeLabels[overrideMode ?? BUILD_MODE] ?? modeLabels.run; + + const { checking, downloading, installed, update, check, install } = useUpdateStore(); + + const effectiveState: UpdateState = overrideState ?? (installed ? "installed" : update ? "hasUpdate" : "latest"); + + return ( +
+ {/* Silk 背景 */} +
+ +
+ + {/* 磨砂层 */} +
+ + {/* 内容层 */} +
+ {/* 版本类型 */} + + {label} + + + {/* Logo */} + Koring + + {/* 版本号 */} +

v{VERSION}

+ + {/* 按钮组 */} +
+ {effectiveState === "latest" && ( + <> + + + + )} + + {effectiveState === "hasUpdate" && ( + <> + + 有新的版本可用 + + + + + )} + + {effectiveState === "installed" && ( + <> + + 更新已下载 + + + + )} +
+
+
+ ); +} diff --git a/src/components/background/BackgroundLayer.tsx b/src/components/background/BackgroundLayer.tsx index 0d80e5b..780792b 100644 --- a/src/components/background/BackgroundLayer.tsx +++ b/src/components/background/BackgroundLayer.tsx @@ -9,7 +9,7 @@ export function BackgroundLayer() { const { type, image, color, blur, opacity, animationSpeed, fetchConfig } = useBackgroundStore(); const route = useRouteStore((s) => s.current); const forceDisableContentBlur = useDevStore((s) => s.forceDisableContentBlur); - const inSetting = route === "setting" || route === "debug"; + const showContentBlur = route !== "home"; useEffect(() => { fetchConfig(); @@ -82,7 +82,10 @@ export function BackgroundLayer() { } `}
- {inSetting && !forceDisableContentBlur &&
} +
); diff --git a/src/components/silk/Silk.tsx b/src/components/silk/Silk.tsx new file mode 100644 index 0000000..d534da3 --- /dev/null +++ b/src/components/silk/Silk.tsx @@ -0,0 +1,130 @@ +/* eslint-disable react/no-unknown-property */ +import { Canvas, useFrame, useThree } from "@react-three/fiber"; +import { forwardRef, useRef, useMemo, useLayoutEffect } from "react"; +import { Color, type Mesh, type ShaderMaterial } from "three"; + +const hexToNormalizedRGB = (hex: string) => { + hex = hex.replace("#", ""); + return [ + parseInt(hex.slice(0, 2), 16) / 255, + parseInt(hex.slice(2, 4), 16) / 255, + parseInt(hex.slice(4, 6), 16) / 255, + ]; +}; + +const vertexShader = ` +varying vec2 vUv; +varying vec3 vPosition; + +void main() { + vPosition = position; + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); +} +`; + +const fragmentShader = ` +varying vec2 vUv; +varying vec3 vPosition; + +uniform float uTime; +uniform vec3 uColor; +uniform float uSpeed; +uniform float uScale; +uniform float uRotation; +uniform float uNoiseIntensity; + +const float e = 2.71828182845904523536; + +float noise(vec2 texCoord) { + float G = e; + vec2 r = (G * sin(G * texCoord)); + return fract(r.x * r.y * (1.0 + texCoord.x)); +} + +vec2 rotateUvs(vec2 uv, float angle) { + float c = cos(angle); + float s = sin(angle); + mat2 rot = mat2(c, -s, s, c); + return rot * uv; +} + +void main() { + float rnd = noise(gl_FragCoord.xy); + vec2 uv = rotateUvs(vUv * uScale, uRotation); + vec2 tex = uv * uScale; + float tOffset = uSpeed * uTime; + + tex.y += 0.03 * sin(8.0 * tex.x - tOffset); + + float pattern = 0.6 + + 0.4 * sin(5.0 * (tex.x + tex.y + + cos(3.0 * tex.x + 5.0 * tex.y) + + 0.02 * tOffset) + + sin(20.0 * (tex.x + tex.y - 0.1 * tOffset))); + + vec4 col = vec4(uColor, 1.0) * vec4(pattern) - rnd / 15.0 * uNoiseIntensity; + col.a = 1.0; + gl_FragColor = col; +} +`; + +interface SilkPlaneProps { + uniforms: Record; +} + +const SilkPlane = forwardRef(function SilkPlane({ uniforms }: SilkPlaneProps, ref: React.Ref) { + const { viewport } = useThree(); + + useLayoutEffect(() => { + if (ref && typeof ref === "object" && ref.current) { + ref.current.scale.set(viewport.width, viewport.height, 1); + } + }, [ref, viewport]); + + useFrame((_, delta) => { + if (ref && typeof ref === "object" && ref.current) { + (ref.current.material as ShaderMaterial).uniforms.uTime.value += 0.1 * delta; + } + }); + + return ( + + + + + ); +}); +SilkPlane.displayName = "SilkPlane"; + +interface SilkProps { + speed?: number; + scale?: number; + color?: string; + noiseIntensity?: number; + rotation?: number; +} + +const Silk = ({ speed = 5, scale = 1, color = "#7B7481", noiseIntensity = 1.5, rotation = 0 }: SilkProps) => { + const meshRef = useRef(null); + + const uniforms = useMemo( + () => ({ + uSpeed: { value: speed }, + uScale: { value: scale }, + uNoiseIntensity: { value: noiseIntensity }, + uColor: { value: new Color(...hexToNormalizedRGB(color)) }, + uRotation: { value: rotation }, + uTime: { value: 0 }, + }), + [speed, scale, noiseIntensity, color, rotation], + ); + + return ( + + + + ); +}; + +export default Silk; diff --git a/src/components/system/WindowControls.tsx b/src/components/system/WindowControls.tsx index 3bbcdff..5e06d36 100644 --- a/src/components/system/WindowControls.tsx +++ b/src/components/system/WindowControls.tsx @@ -1,5 +1,7 @@ import { useState, useEffect } from "react"; import { getCurrentWindow } from "@tauri-apps/api/window"; +import { BUILD_MODE } from "@/lib/mode"; +import clsx from "clsx"; interface WindowControlsProps { showMinimize?: boolean; @@ -30,8 +32,23 @@ export function WindowControls({ const btnClass = "flex items-center justify-center w-[25px] h-[25px] rounded transition-colors cursor-default hover:bg-black/10 dark:hover:bg-white/15 text-black/70 dark:text-white/70 hover:text-black dark:hover:text-white"; const closeBtnClass = "flex items-center justify-center w-[25px] h-[25px] rounded transition-colors cursor-default hover:bg-red-500 text-black/70 dark:text-white/70 hover:text-white"; + const showBadge = BUILD_MODE !== "run"; + const badgeLabel = BUILD_MODE === "dev" ? "DEV" : "BETA"; + return (
+ {showBadge && ( + + {badgeLabel} + + )} {showMinimize && (
diff --git a/src/index.css b/src/index.css index 24fd10b..5feddb2 100644 --- a/src/index.css +++ b/src/index.css @@ -92,7 +92,7 @@ --sidebar-ring: oklch(0.708 0 0); /* 标题栏 & 导航 - 亮色 */ - --titlebar-bg: rgba(255, 255, 255, 0.1); + --titlebar-bg: rgba(255, 255, 255, 0.3); --titlebar-border: rgba(0, 0, 0, 0.08); --indicator-bg: rgba(0, 0, 0, 0.1); --indicator-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); @@ -200,6 +200,8 @@ animation: 0.3s cubic-bezier(0.4, 0, 0.2, 1) 0.05s both page-in-forward; } +/* 设置子页面转场(已弃用 View Transitions,改用 keyframe) */ + /* 内容区转场:后退方向 */ :root[data-transition-dir="backward"] ::view-transition-old(content) { animation-name: page-out-backward; @@ -226,6 +228,17 @@ to { opacity: 1; transform: scale(1); } } +/* ============================================ + 设置子页面切换 — keyframe 动画(仅内容区) + ============================================ */ +.setting-page-enter { + animation: setting-page-in 0.25s cubic-bezier(0.4, 0, 0.2, 1) both; +} +@keyframes setting-page-in { + from { opacity: 0; transform: translateY(6px); } + to { opacity: 1; transform: translateY(0); } +} + /* ============================================ 磨砂卡片 — 复用 --titlebar-bg / --titlebar-border ============================================ */ @@ -251,10 +264,11 @@ -webkit-backdrop-filter: blur(80px) saturate(120%); z-index: 0; pointer-events: none; - background: rgba(255, 255, 255, 0.5); + background: rgba(255, 255, 255, var(--content-blur-opacity, 0.5)); + transition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1); } :root.dark .content-blur-overlay { - background: rgba(0, 0, 0, 0.5); + background: rgba(0, 0, 0, var(--content-blur-opacity, 0.5)); } /* ============================================ diff --git a/src/layouts/RootLayout.tsx b/src/layouts/RootLayout.tsx index 4e9b74b..18061ad 100644 --- a/src/layouts/RootLayout.tsx +++ b/src/layouts/RootLayout.tsx @@ -17,7 +17,9 @@ export function RootLayout({ showMaximize = true, showClose = true, }: RootLayoutProps) { - const { reduceMotion, reduceTransparency, highContrast } = useA11yStore(); + const { reduceMotion, reduceTransparency, highContrast, contentBlurOpacity } = useA11yStore(); + + const blurOpacity = reduceTransparency ? 0 : contentBlurOpacity; return (
{/* Layer 0: Background */} diff --git a/src/pages/play-link/index.tsx b/src/pages/play-link/index.tsx index a63391b..09ef0b3 100644 --- a/src/pages/play-link/index.tsx +++ b/src/pages/play-link/index.tsx @@ -1,10 +1,5 @@ +import { UnderConstruction } from "@/components/UnderConstruction"; + export function PlayLink() { - return ( -
-
-

联机

-

多人联机与服务器

-
-
- ); + return ; } diff --git a/src/pages/setting/debug.tsx b/src/pages/setting/debug.tsx deleted file mode 100644 index d359729..0000000 --- a/src/pages/setting/debug.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { Button } from "@/components/ui/button"; -import { WebviewWindow } from "@tauri-apps/api/webviewWindow"; - -export function Debug() { - const openSplash = async () => { - try { - const existing = await WebviewWindow.getByLabel("splashscreen"); - if (existing) { - await existing.show(); - await existing.setFocus(); - return; - } - - const splash = new WebviewWindow("splashscreen", { - url: "/splash.html", - width: 480, - height: 320, - decorations: false, - transparent: true, - center: true, - visible: true, - resizable: false, - minWidth: 480, - maxWidth: 480, - minHeight: 320, - maxHeight: 320, - } as any); - - splash.once("tauri://error", (e) => { - console.error("Splash window error:", e); - }); - } catch (err) { - console.error("Failed to open splash:", err); - } - }; - - const closeSplash = async () => { - try { - const splash = await WebviewWindow.getByLabel("splashscreen"); - if (splash) { - await splash.close(); - } - } catch (err) { - console.error("Failed to close splash:", err); - } - }; - - return ( -
-
-

Debug

-
- - -
-
-
- ); -} diff --git a/src/pages/setting/game/advanced.tsx b/src/pages/setting/game/advanced.tsx index ff997cd..8d0a743 100644 --- a/src/pages/setting/game/advanced.tsx +++ b/src/pages/setting/game/advanced.tsx @@ -1,8 +1,167 @@ -export function AdvancedSetting() { +import { useState } from "react"; +import { Switch } from "@/components/ui/switch"; + +function GlassCard({ children }: { children: React.ReactNode }) { + return
{children}
; +} + +function SettingRow({ label, desc, children }: { label: string; desc?: string; children: React.ReactNode }) { return ( +
+
+

{label}

+ {desc &&

{desc}

} +
+
{children}
+
+ ); +} + +const launcherBehavior = [ + { value: "close", label: "关闭启动器" }, + { value: "minimize", label: "最小化到任务栏" }, + { value: "keep", label: "保持不变" }, +]; + +const windowSize = [ + { value: "default", label: "默认窗口大小" }, + { value: "fullscreen", label: "全屏启动" }, + { value: "custom", label: "自定义尺寸" }, +]; + +export function AdvancedSetting() { + const [afterLaunch, setAfterLaunch] = useState("close"); + const [winMode, setWinMode] = useState("default"); + const [customWidth, setCustomWidth] = useState("854"); + const [customHeight, setCustomHeight] = useState("480"); + const [gameArgs, setGameArgs] = useState(""); + const [preLaunchCmd, setPreLaunchCmd] = useState(""); + const [debugMode, setDebugMode] = useState(false); + + return (
-

高级设置

-

游戏高级选项与调试参数

+

高级设置

+

游戏高级启动参数、调试选项与实验性功能

+ +
+ {/* ===== 启动行为 ===== */} +
+

启动行为

+
+ +
+

启动后启动器行为

+

游戏启动后启动器的处理方式

+
+ {launcherBehavior.map((opt) => ( + + ))} +
+
+
+
+
+ + {/* ===== 窗口设置 ===== */} +
+

窗口设置

+
+ +
+
+

窗口大小

+
+ {windowSize.map((opt) => ( + + ))} +
+
+ {winMode === "custom" && ( +
+
+ + setCustomWidth(e.target.value)} + className="w-20 h-8 px-2 rounded-md border border-input bg-background text-sm text-center font-mono focus:outline-none focus:ring-1 focus:ring-ring" + /> +
+ × +
+ + setCustomHeight(e.target.value)} + className="w-20 h-8 px-2 rounded-md border border-input bg-background text-sm text-center font-mono focus:outline-none focus:ring-1 focus:ring-ring" + /> +
+
+ )} +
+
+
+
+ + {/* ===== 游戏参数 ===== */} +
+

游戏参数

+
+ +
+

额外游戏启动参数

+

附加到游戏启动命令末尾的参数

+ setGameArgs(e.target.value)} + placeholder="可选,例如 --demo" + className="w-full h-8 px-3 rounded-md border border-input bg-background text-sm font-mono placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring" + /> +
+
+
+
+ + {/* ===== 启动命令 ===== */} +
+

启动命令

+
+ +
+

启动前执行命令

+

游戏启动前自动执行的命令或程序路径

+ setPreLaunchCmd(e.target.value)} + placeholder="可选,例如 D:\scripts\pre-launch.bat" + className="w-full h-8 px-3 rounded-md border border-input bg-background text-sm font-mono placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring" + /> +
+
+
+
+ + {/* ===== 调试 ===== */} +
+

调试

+
+ + + + + +
+
+
); } diff --git a/src/pages/setting/game/game-account.tsx b/src/pages/setting/game/game-account.tsx index 3cd6203..8d4bac4 100644 --- a/src/pages/setting/game/game-account.tsx +++ b/src/pages/setting/game/game-account.tsx @@ -1,8 +1,8 @@ export function GameAccountSetting() { return (
-

游戏账户&档案

-

管理 Minecraft 账户与游戏档案

+

游戏账户&档案

+

管理 Minecraft 游戏内账户、正版验证与游戏档案配置

); } diff --git a/src/pages/setting/game/game-dir.tsx b/src/pages/setting/game/game-dir.tsx index fef00e9..5a2bdc1 100644 --- a/src/pages/setting/game/game-dir.tsx +++ b/src/pages/setting/game/game-dir.tsx @@ -1,8 +1,95 @@ +import { useState } from "react"; +import { Button } from "@/components/ui/button"; +import { RefreshCw } from "lucide-react"; + +function GlassCard({ children }: { children: React.ReactNode }) { + return
{children}
; +} + +function PathInput({ label, desc, value, onChange }: { label: string; desc: string; value: string; onChange: (v: string) => void }) { + return ( + +
+
+

{label}

+

{desc}

+
+
+ onChange(e.target.value)} + className="flex-1 h-8 px-3 rounded-md border border-input bg-background text-sm font-mono placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring" + /> + +
+
+
+ ); +} + export function GameDirSetting() { + const [gameDir, setGameDir] = useState(".minecraft"); + const [resourceDir, setResourceDir] = useState(""); + const [savesDir, setSavesDir] = useState(""); + return (
-

游戏目录

-

设置游戏安装与存档目录

+

游戏目录

+

设置 Minecraft 游戏安装路径、存档位置与资源包目录

+ +
+ {/* ===== 游戏路径 ===== */} +
+

游戏路径

+
+ + +
+
+

已安装版本

+

扫描当前目录下的游戏版本

+
+ +
+
+
+
+ + {/* ===== 资源包 ===== */} +
+

资源包目录

+
+ +
+
+ + {/* ===== 存档 ===== */} +
+

存档目录

+
+ +
+
+
); } diff --git a/src/pages/setting/game/java-mem.tsx b/src/pages/setting/game/java-mem.tsx index f313da6..d32d41e 100644 --- a/src/pages/setting/game/java-mem.tsx +++ b/src/pages/setting/game/java-mem.tsx @@ -1,8 +1,185 @@ -export function JavaMemSetting() { +import { useState } from "react"; +import { Button } from "@/components/ui/button"; +import { Slider } from "@/components/ui/slider"; +import { Cpu, FolderSearch } from "lucide-react"; + +function GlassCard({ children }: { children: React.ReactNode }) { + return
{children}
; +} + +function SettingRow({ label, desc, children }: { label: string; desc?: string; children: React.ReactNode }) { return ( +
+
+

{label}

+ {desc &&

{desc}

} +
+
{children}
+
+ ); +} + +interface JavaInfo { + path: string; + version: string; + vendor: string; +} + +const mockJavaList: JavaInfo[] = [ + { path: "C:\\Program Files\\Java\\jdk-21\\bin\\javaw.exe", version: "21.0.3", vendor: "Oracle OpenJDK" }, + { path: "C:\\Program Files\\Eclipse Adoptium\\jdk-17.0.10.7-hotspot\\bin\\javaw.exe", version: "17.0.10", vendor: "Eclipse Temurin" }, +]; + +const gcOptions = [ + { value: "auto", label: "不指定(由 Java 自动选择)" }, + { value: "zgc", label: "ZGC(低延迟,推荐 Java 21+)" }, + { value: "g1", label: "G1GC(标准,兼容性好)" }, +]; + +export function JavaMemSetting() { + const [javaPath, setJavaPath] = useState(""); + const [memMode, setMemMode] = useState<"auto" | "custom">("auto"); + const [memGB, setMemGB] = useState(4); + const [gc, setGc] = useState("auto"); + const [jvmArgs, setJvmArgs] = useState(""); + + return (
-

Java虚拟机与内存

-

配置 Java 路径与内存分配

+

Java 虚拟机与内存

+

配置 Java 运行环境路径、JVM 参数与游戏内存分配

+ +
+ {/* ===== Java 环境 ===== */} +
+

Java 环境

+
+ + + + + + + {/* 检测结果列表 */} + {mockJavaList.map((java) => ( + +
+ +
+

+ {java.vendor} {java.version} +

+

+ {java.path} +

+
+ +
+
+ ))} + + +
+

手动指定路径

+
+ setJavaPath(e.target.value)} + placeholder="输入 javaw.exe 完整路径" + className="flex-1 h-8 px-3 rounded-md border border-input bg-background text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring" + /> + +
+
+
+
+
+ + {/* ===== 内存分配 ===== */} +
+

内存分配

+
+ +
+
+ + +
+ {memMode === "custom" && ( +
+
+ 分配内存 + {memGB} GB +
+ setMemGB(Array.isArray(v) ? v[0] : v)} + min={1} + max={16} + step={1} + /> +
+ 1 GB + 16 GB +
+
+ )} +
+
+
+
+ + {/* ===== JVM 参数 ===== */} +
+

JVM 参数

+
+ +
+

额外 JVM 启动参数

+

每行一个参数,例如 -XX:+UseZGC

+