美化
This commit is contained in:
Generated
+380
-26
@@ -11,9 +11,12 @@
|
||||
"dependencies": {
|
||||
"@electron-toolkit/preload": "^3.0.2",
|
||||
"@electron-toolkit/utils": "^4.0.0",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"antd": "^5.28.0",
|
||||
"axios": "^1.13.2",
|
||||
"electron-updater": "^6.3.9",
|
||||
"framer-motion": "^12.23.24",
|
||||
"playwright": "^1.56.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -139,7 +142,6 @@
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
||||
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.27.1",
|
||||
@@ -195,7 +197,6 @@
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
|
||||
"integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.28.5",
|
||||
@@ -229,7 +230,6 @@
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
|
||||
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -239,7 +239,6 @@
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
|
||||
"integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/traverse": "^7.27.1",
|
||||
@@ -281,7 +280,6 @@
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -291,7 +289,6 @@
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
||||
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -325,7 +322,6 @@
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
|
||||
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.28.5"
|
||||
@@ -398,7 +394,6 @@
|
||||
"version": "7.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
|
||||
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
@@ -413,7 +408,6 @@
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
|
||||
"integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
@@ -432,7 +426,6 @@
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
|
||||
"integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.27.1",
|
||||
@@ -866,18 +859,197 @@
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin": {
|
||||
"version": "11.13.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
|
||||
"integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": "^7.16.7",
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/hash": "^0.9.2",
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"convert-source-map": "^1.5.0",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"find-root": "^1.1.0",
|
||||
"source-map": "^0.5.7",
|
||||
"stylis": "4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin/node_modules/@emotion/hash": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
|
||||
"integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin/node_modules/convert-source-map": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin/node_modules/source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin/node_modules/stylis": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
|
||||
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/cache": {
|
||||
"version": "11.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
|
||||
"integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
"@emotion/sheet": "^1.4.0",
|
||||
"@emotion/utils": "^1.4.2",
|
||||
"@emotion/weak-memoize": "^0.4.0",
|
||||
"stylis": "4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/cache/node_modules/stylis": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
|
||||
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/hash": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
|
||||
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/is-prop-valid": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz",
|
||||
"integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emotion/memoize": "^0.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/memoize": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
|
||||
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/react": {
|
||||
"version": "11.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
|
||||
"integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
"@emotion/cache": "^11.14.0",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
|
||||
"@emotion/utils": "^1.4.2",
|
||||
"@emotion/weak-memoize": "^0.4.0",
|
||||
"hoist-non-react-statics": "^3.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/serialize": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
|
||||
"integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emotion/hash": "^0.9.2",
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
"@emotion/unitless": "^0.10.0",
|
||||
"@emotion/utils": "^1.4.2",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/serialize/node_modules/@emotion/hash": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
|
||||
"integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/serialize/node_modules/@emotion/unitless": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
|
||||
"integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/sheet": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
|
||||
"integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/styled": {
|
||||
"version": "11.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz",
|
||||
"integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
"@emotion/is-prop-valid": "^1.3.0",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
|
||||
"@emotion/utils": "^1.4.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.0.0-rc.0",
|
||||
"react": ">=16.8.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/unitless": {
|
||||
"version": "0.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
|
||||
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/use-insertion-effect-with-fallbacks": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
|
||||
"integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/utils": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
|
||||
"integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/weak-memoize": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
|
||||
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
|
||||
@@ -1692,7 +1864,6 @@
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||
@@ -1714,7 +1885,6 @@
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
@@ -1724,14 +1894,12 @@
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.31",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
||||
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
@@ -2539,6 +2707,12 @@
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/parse-json": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
|
||||
"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/plist": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz",
|
||||
@@ -3543,6 +3717,41 @@
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-macros": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
|
||||
"integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"cosmiconfig": "^7.0.0",
|
||||
"resolve": "^1.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-macros/node_modules/resolve": {
|
||||
"version": "1.22.11",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
|
||||
"integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.16.1",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"resolve": "bin/resolve"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@@ -3968,7 +4177,6 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -4332,6 +4540,31 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cosmiconfig": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
|
||||
"integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
"parse-json": "^5.0.0",
|
||||
"path-type": "^4.0.0",
|
||||
"yaml": "^1.10.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/cosmiconfig/node_modules/yaml": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/crc": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz",
|
||||
@@ -5128,6 +5361,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/error-ex": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
|
||||
"integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/es-abstract": {
|
||||
"version": "1.24.0",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
|
||||
@@ -5364,7 +5606,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
@@ -5830,6 +6071,12 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/find-root": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
||||
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/find-up": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
|
||||
@@ -5950,6 +6197,33 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "12.23.24",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.24.tgz",
|
||||
"integrity": "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-dom": "^12.23.23",
|
||||
"motion-utils": "^12.23.6",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
@@ -6425,6 +6699,15 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/hoist-non-react-statics": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"react-is": "^16.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hosted-git-info": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
|
||||
@@ -6581,7 +6864,6 @@
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
||||
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"parent-module": "^1.0.0",
|
||||
@@ -6683,6 +6965,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-async-function": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
|
||||
@@ -6766,7 +7054,6 @@
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.2"
|
||||
@@ -7193,7 +7480,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
@@ -7212,7 +7498,6 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
|
||||
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jsesc": "bin/jsesc"
|
||||
@@ -7227,6 +7512,12 @@
|
||||
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-parse-even-better-errors": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
@@ -7382,6 +7673,12 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lines-and-columns": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||
@@ -7853,6 +8150,21 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "12.23.23",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz",
|
||||
"integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-utils": "^12.23.6"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-utils": {
|
||||
"version": "12.23.6",
|
||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
|
||||
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
@@ -8315,7 +8627,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"callsites": "^3.0.0"
|
||||
@@ -8324,6 +8635,24 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-json": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"error-ex": "^1.3.1",
|
||||
"json-parse-even-better-errors": "^2.3.0",
|
||||
"lines-and-columns": "^1.1.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
@@ -8358,7 +8687,6 @@
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
@@ -8395,6 +8723,15 @@
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/path-type": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/pe-library": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz",
|
||||
@@ -8420,7 +8757,6 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
@@ -9319,7 +9655,6 @@
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-refresh": {
|
||||
@@ -9502,7 +9837,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
@@ -10355,7 +10689,6 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -10607,6 +10940,12 @@
|
||||
"typescript": ">=4.8.4"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
@@ -11191,6 +11530,21 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
|
||||
"integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.6"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
|
||||
@@ -23,9 +23,12 @@
|
||||
"dependencies": {
|
||||
"@electron-toolkit/preload": "^3.0.2",
|
||||
"@electron-toolkit/utils": "^4.0.0",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"antd": "^5.28.0",
|
||||
"axios": "^1.13.2",
|
||||
"electron-updater": "^6.3.9",
|
||||
"framer-motion": "^12.23.24",
|
||||
"playwright": "^1.56.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
+29
-12
@@ -29,8 +29,8 @@ function createFloatingWindow(): void {
|
||||
const { width } = screen.getPrimaryDisplay().workAreaSize
|
||||
|
||||
floatingWindow = new BrowserWindow({
|
||||
width: 240,
|
||||
height: 210,
|
||||
width: 260,
|
||||
height: 160,
|
||||
x: width - 100,
|
||||
y: 20,
|
||||
frame: false,
|
||||
@@ -92,15 +92,23 @@ function createSettingsWindow(): void {
|
||||
}
|
||||
|
||||
function createChatWindow(initialText?: string): void {
|
||||
console.log('createChatWindow called with initialText:', initialText)
|
||||
|
||||
// If chat window already exists, focus it and send new text if provided
|
||||
if (chatWindow && !chatWindow.isDestroyed()) {
|
||||
console.log('Chat window already exists, focusing and sending text')
|
||||
chatWindow.focus()
|
||||
if (initialText) {
|
||||
chatWindow.webContents.send('set-initial-text', initialText)
|
||||
// Add a small delay to ensure the renderer is ready
|
||||
setTimeout(() => {
|
||||
chatWindow?.webContents.send('set-initial-text', initialText)
|
||||
console.log('Sent initial text to existing window:', initialText)
|
||||
}, 100)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
console.log('Creating new chat window')
|
||||
chatWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
@@ -122,8 +130,16 @@ function createChatWindow(initialText?: string): void {
|
||||
|
||||
// Send initial text after page loads
|
||||
if (initialText) {
|
||||
console.log('Setting up did-finish-load listener for initial text')
|
||||
chatWindow.webContents.once('did-finish-load', () => {
|
||||
chatWindow?.webContents.send('set-initial-text', initialText)
|
||||
console.log('Chat window did-finish-load event fired')
|
||||
// Add a small delay to ensure React components are mounted
|
||||
setTimeout(() => {
|
||||
if (chatWindow && !chatWindow.isDestroyed()) {
|
||||
console.log('Sending initial text to new window:', initialText)
|
||||
chatWindow.webContents.send('set-initial-text', initialText)
|
||||
}
|
||||
}, 200)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -338,15 +354,16 @@ function registerGlobalShortcuts(): void {
|
||||
|
||||
const registered = globalShortcut.register(shortcut, () => {
|
||||
if (floatingWindow && !floatingWindow.isDestroyed()) {
|
||||
// Get clipboard text for selected text
|
||||
const selectedText = clipboard.readText('selection')
|
||||
const text = selectedText || clipboard.readText()
|
||||
// Read clipboard content (user should copy text with Command+C first)
|
||||
// We only read the main clipboard, not the selection clipboard
|
||||
const text = clipboard.readText()
|
||||
|
||||
if (text && text.trim()) {
|
||||
// Send event to renderer to show prompt
|
||||
floatingWindow.webContents.send('show-text-prompt', text.trim())
|
||||
floatingWindow.focus()
|
||||
}
|
||||
console.log('Command+K pressed, clipboard content:', text?.substring(0, 50))
|
||||
|
||||
// Always send the event to toggle action menu
|
||||
// Pass clipboard text (empty string if clipboard is empty)
|
||||
floatingWindow.webContents.send('show-text-prompt', text || '')
|
||||
floatingWindow.focus()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import React, { useState, useEffect, useRef } from 'react'
|
||||
import { Input, Button, Typography, Space, Modal, message, Drawer, Skeleton } from 'antd'
|
||||
import { Input, Button, Typography, Modal, message, Drawer, Skeleton } from 'antd'
|
||||
import type { TextAreaRef } from 'antd/es/input/TextArea'
|
||||
import { SendOutlined, CommentOutlined, ReloadOutlined, SettingOutlined } from '@ant-design/icons'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
import styled from '@emotion/styled'
|
||||
import { lightTheme, darkTheme, Theme } from '../theme'
|
||||
|
||||
const { TextArea } = Input
|
||||
const { Text } = Typography
|
||||
@@ -35,10 +39,178 @@ interface CommentData {
|
||||
replies?: CommentData[]
|
||||
}
|
||||
|
||||
// Styled Components
|
||||
const ChatContainer = styled.div<{ theme: Theme }>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background: ${(props) => props.theme.colors.background};
|
||||
font-family: ${(props) => props.theme.typography.fontFamily};
|
||||
transition: background ${(props) => props.theme.animation.normal} ease;
|
||||
`
|
||||
|
||||
const Header = styled.div<{ theme: Theme }>`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: ${(props) => props.theme.spacing.md} ${(props) => props.theme.spacing.lg};
|
||||
background: ${(props) => props.theme.colors.glassBackground};
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid ${(props) => props.theme.colors.glassBorder};
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
`
|
||||
|
||||
const Title = styled.h1<{ theme: Theme }>`
|
||||
margin: 0;
|
||||
font-size: ${(props) => props.theme.typography.fontSize.xl};
|
||||
font-weight: ${(props) => props.theme.typography.fontWeight.semibold};
|
||||
color: ${(props) => props.theme.colors.textPrimary};
|
||||
`
|
||||
|
||||
const HeaderActions = styled.div`
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
`
|
||||
|
||||
const MessagesContainer = styled.div<{ theme: Theme }>`
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: ${(props) => props.theme.spacing.lg};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${(props) => props.theme.spacing.md};
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: ${(props) => props.theme.colors.border};
|
||||
border-radius: ${(props) => props.theme.borderRadius.full};
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb:hover {
|
||||
background: ${(props) => props.theme.colors.textTertiary};
|
||||
}
|
||||
`
|
||||
|
||||
const MessageBubble = styled(motion.div)<{ role: 'user' | 'assistant'; theme: Theme }>`
|
||||
max-width: 70%;
|
||||
padding: ${(props) => props.theme.spacing.md} ${(props) => props.theme.spacing.lg};
|
||||
border-radius: ${(props) => props.theme.borderRadius.md};
|
||||
align-self: ${(props) => (props.role === 'user' ? 'flex-end' : 'flex-start')};
|
||||
background: ${(props) =>
|
||||
props.role === 'user' ? props.theme.colors.userBubble : props.theme.colors.aiBubble};
|
||||
color: ${(props) =>
|
||||
props.role === 'user' ? props.theme.colors.userBubbleText : props.theme.colors.aiBubbleText};
|
||||
box-shadow: ${(props) => props.theme.shadows.md};
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
font-size: ${(props) => props.theme.typography.fontSize.base};
|
||||
line-height: 1.5;
|
||||
backdrop-filter: ${(props) => (props.role === 'assistant' ? 'blur(20px)' : 'none')};
|
||||
-webkit-backdrop-filter: ${(props) => (props.role === 'assistant' ? 'blur(20px)' : 'none')};
|
||||
border: ${(props) =>
|
||||
props.role === 'assistant' ? `1px solid ${props.theme.colors.glassBorder}` : 'none'};
|
||||
transition: transform ${(props) => props.theme.animation.fast} ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.01);
|
||||
}
|
||||
`
|
||||
|
||||
const MessageActions = styled(motion.div)<{ theme: Theme }>`
|
||||
display: flex;
|
||||
gap: ${(props) => props.theme.spacing.sm};
|
||||
margin-top: ${(props) => props.theme.spacing.sm};
|
||||
`
|
||||
|
||||
const InputContainer = styled.div<{ theme: Theme }>`
|
||||
padding: ${(props) => props.theme.spacing.lg};
|
||||
background: ${(props) => props.theme.colors.glassBackground};
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-top: 1px solid ${(props) => props.theme.colors.glassBorder};
|
||||
`
|
||||
|
||||
const InputWrapper = styled(motion.div)<{ theme: Theme; isMultiLine: boolean }>`
|
||||
background: ${(props) => props.theme.colors.surface};
|
||||
border-radius: ${(props) => props.theme.borderRadius.lg};
|
||||
box-shadow: ${(props) => props.theme.shadows.lg};
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: ${(props) => (props.isMultiLine ? 'column' : 'row')};
|
||||
align-items: ${(props) => (props.isMultiLine ? 'flex-end' : 'flex-end')};
|
||||
gap: ${(props) => props.theme.spacing.sm};
|
||||
padding: ${(props) => props.theme.spacing.sm};
|
||||
transition: box-shadow ${(props) => props.theme.animation.fast} ease;
|
||||
|
||||
&:focus-within {
|
||||
box-shadow: ${(props) => props.theme.shadows.xl};
|
||||
}
|
||||
`
|
||||
|
||||
const SendButton = styled(motion.button)<{ theme: Theme; disabled: boolean }>`
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
background: ${(props) =>
|
||||
props.disabled
|
||||
? props.theme.colors.border
|
||||
: `linear-gradient(135deg, ${props.theme.colors.primary} 0%, #0051d5 100%)`};
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: ${(props) => (props.disabled ? 'not-allowed' : 'pointer')};
|
||||
box-shadow: ${(props) =>
|
||||
props.disabled ? 'none' : '0 2px 8px rgba(0, 122, 255, 0.3)'};
|
||||
transition: all ${(props) => props.theme.animation.fast} ease;
|
||||
flex-shrink: 0;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 4px 12px rgba(0, 122, 255, 0.4);
|
||||
}
|
||||
|
||||
&:active:not(:disabled) {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
transform: translateX(1px);
|
||||
}
|
||||
`
|
||||
|
||||
const EmptyState = styled.div<{ theme: Theme }>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: ${(props) => props.theme.colors.textSecondary};
|
||||
font-size: ${(props) => props.theme.typography.fontSize.lg};
|
||||
`
|
||||
|
||||
const Chat: React.FC = () => {
|
||||
const [isDarkMode, setIsDarkMode] = useState(false)
|
||||
const theme = isDarkMode ? darkTheme : lightTheme
|
||||
const [messages, setMessages] = useState<Message[]>([])
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [isMultiLine, setIsMultiLine] = useState(false)
|
||||
const [currentArticleUrl, setCurrentArticleUrl] = useState<string>('')
|
||||
const [lastAiResponse, setLastAiResponse] = useState<string>('')
|
||||
const [isQrModalVisible, setIsQrModalVisible] = useState(false)
|
||||
@@ -46,6 +218,7 @@ const Chat: React.FC = () => {
|
||||
const [qrCodeError, setQrCodeError] = useState<string>('')
|
||||
const [isConfirmModalVisible, setIsConfirmModalVisible] = useState(false)
|
||||
const [confirmUsername, setConfirmUsername] = useState<string>('')
|
||||
const [editableComment, setEditableComment] = useState<string>('')
|
||||
const [regeneratingMessageId, setRegeneratingMessageId] = useState<string | null>(null)
|
||||
const [isAccountDrawerVisible, setIsAccountDrawerVisible] = useState(false)
|
||||
const [isCheckingLoginStatus, setIsCheckingLoginStatus] = useState(false)
|
||||
@@ -58,13 +231,28 @@ const Chat: React.FC = () => {
|
||||
const messagesContainerRef = useRef<HTMLDivElement>(null)
|
||||
const shouldAutoScrollRef = useRef(true)
|
||||
const isArticleRequestRef = useRef(false) // 使用 ref 而不是 state 以避免异步问题
|
||||
const inputRef = useRef<TextAreaRef>(null) // Ant Design TextArea ref
|
||||
|
||||
// Listen for initial text from main process
|
||||
useEffect(() => {
|
||||
const unsubscribe = window.electron.ipcRenderer.on(
|
||||
'set-initial-text',
|
||||
(_: unknown, text: string) => {
|
||||
setInputValue(text)
|
||||
console.log('Chat: Received initial text:', text?.substring(0, 50))
|
||||
if (text && text.trim()) {
|
||||
setInputValue(text)
|
||||
// Focus the input area after setting the value
|
||||
setTimeout(() => {
|
||||
if (inputRef.current) {
|
||||
inputRef.current.focus()
|
||||
// Move cursor to the end
|
||||
const textarea = inputRef.current.resizableTextArea?.textArea
|
||||
if (textarea) {
|
||||
textarea.setSelectionRange(textarea.value.length, textarea.value.length)
|
||||
}
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -417,9 +605,7 @@ const Chat: React.FC = () => {
|
||||
setRegeneratingMessageId(messageId)
|
||||
|
||||
// 清空当前消息内容,显示加载状态
|
||||
setMessages((prev) =>
|
||||
prev.map((msg) => (msg.id === messageId ? { ...msg, content: '' } : msg))
|
||||
)
|
||||
setMessages((prev) => prev.map((msg) => (msg.id === messageId ? { ...msg, content: '' } : msg)))
|
||||
|
||||
try {
|
||||
// 获取模型配置
|
||||
@@ -590,6 +776,7 @@ const Chat: React.FC = () => {
|
||||
const showConfirmDialog = (username?: string): void => {
|
||||
console.log('showConfirmDialog called with username:', username)
|
||||
setConfirmUsername(username || '当前用户')
|
||||
setEditableComment(lastAiResponse) // 初始化可编辑内容
|
||||
setIsConfirmModalVisible(true)
|
||||
}
|
||||
|
||||
@@ -597,7 +784,7 @@ const Chat: React.FC = () => {
|
||||
const handleConfirmOk = async (): Promise<void> => {
|
||||
console.log('handleConfirmOk called')
|
||||
console.log('currentArticleUrl:', currentArticleUrl)
|
||||
console.log('lastAiResponse length:', lastAiResponse?.length)
|
||||
console.log('editableComment length:', editableComment?.length)
|
||||
|
||||
setIsConfirmModalVisible(false)
|
||||
message.loading({ content: '正在发送评论...', key: 'posting', duration: 0 })
|
||||
@@ -606,7 +793,7 @@ const Chat: React.FC = () => {
|
||||
console.log('Invoking post-comment...')
|
||||
const result = await window.electron.ipcRenderer.invoke('post-comment', {
|
||||
url: currentArticleUrl,
|
||||
comment: lastAiResponse
|
||||
comment: editableComment // 使用编辑后的内容
|
||||
})
|
||||
|
||||
console.log('post-comment result:', result)
|
||||
@@ -629,7 +816,9 @@ const Chat: React.FC = () => {
|
||||
}
|
||||
|
||||
const handleConfirmCancel = (): void => {
|
||||
console.log('handleConfirmCancel called')
|
||||
setIsConfirmModalVisible(false)
|
||||
setEditableComment('')
|
||||
}
|
||||
|
||||
const handleQrModalCancel = (): void => {
|
||||
@@ -728,183 +917,205 @@ const Chat: React.FC = () => {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100vh',
|
||||
background: '#f5f5f5'
|
||||
}}
|
||||
>
|
||||
{/* Header */}
|
||||
<div
|
||||
style={{
|
||||
padding: '16px 24px',
|
||||
background: '#fff',
|
||||
borderBottom: '1px solid #e8e8e8',
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.06)',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<Text strong style={{ fontSize: '16px' }}>
|
||||
AI 对话
|
||||
</Text>
|
||||
<Button
|
||||
icon={<SettingOutlined />}
|
||||
onClick={() => {
|
||||
setIsAccountDrawerVisible(true)
|
||||
// 打开 Drawer 时检查登录状态
|
||||
checkXiaoheiheLoginStatus()
|
||||
}}
|
||||
>
|
||||
账号管理
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Messages List */}
|
||||
<div
|
||||
ref={messagesContainerRef}
|
||||
style={{
|
||||
flex: 1,
|
||||
overflowY: 'auto',
|
||||
padding: '24px'
|
||||
}}
|
||||
>
|
||||
{messages.length === 0 ? (
|
||||
<div
|
||||
/* 自定义 TextArea 滚动条样式 - WebKit 浏览器 */
|
||||
.ant-input::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.ant-input::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.ant-input::-webkit-scrollbar-thumb {
|
||||
background: ${theme.colors.border};
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.ant-input::-webkit-scrollbar-thumb:hover {
|
||||
background: ${theme.colors.textTertiary};
|
||||
}
|
||||
|
||||
/* Firefox 滚动条样式 */
|
||||
.ant-input {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: ${theme.colors.border} transparent;
|
||||
}
|
||||
`}</style>
|
||||
<ChatContainer theme={theme}>
|
||||
{/* Header */}
|
||||
<Header theme={theme}>
|
||||
<Title theme={theme}>AI 对话</Title>
|
||||
<HeaderActions>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<SettingOutlined />}
|
||||
onClick={() => setIsDarkMode(!isDarkMode)}
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
color: '#999'
|
||||
color: theme.colors.textPrimary,
|
||||
transition: `all ${theme.animation.fast} ease`
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SettingOutlined />}
|
||||
onClick={() => {
|
||||
setIsAccountDrawerVisible(true)
|
||||
checkXiaoheiheLoginStatus()
|
||||
}}
|
||||
>
|
||||
账号管理
|
||||
</Button>
|
||||
</HeaderActions>
|
||||
</Header>
|
||||
|
||||
{/* Messages List */}
|
||||
<MessagesContainer ref={messagesContainerRef} theme={theme}>
|
||||
{messages.length === 0 ? (
|
||||
<EmptyState theme={theme}>
|
||||
<Text type="secondary">开始新的对话...</Text>
|
||||
</div>
|
||||
</EmptyState>
|
||||
) : (
|
||||
<Space direction="vertical" style={{ width: '100%' }} size="large">
|
||||
{messages.map((message) => (
|
||||
<div
|
||||
<AnimatePresence mode="popLayout">
|
||||
{messages.map((message, index) => (
|
||||
<MessageBubble
|
||||
key={message.id}
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: message.role === 'user' ? 'flex-end' : 'flex-start'
|
||||
role={message.role}
|
||||
theme={theme}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, scale: 0.95 }}
|
||||
transition={{
|
||||
duration: 0.15,
|
||||
delay: index * 0.03,
|
||||
ease: [0.25, 0.1, 0.25, 1]
|
||||
}}
|
||||
whileHover={{ scale: 1.01 }}
|
||||
>
|
||||
<div
|
||||
<Text
|
||||
style={{
|
||||
maxWidth: '70%',
|
||||
padding: '12px 16px',
|
||||
borderRadius: '12px',
|
||||
background: message.role === 'user' ? '#1890ff' : '#fff',
|
||||
color: message.role === 'user' ? '#fff' : '#000',
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)',
|
||||
wordBreak: 'break-word',
|
||||
whiteSpace: 'pre-wrap'
|
||||
color:
|
||||
message.role === 'user'
|
||||
? theme.colors.userBubbleText
|
||||
: theme.colors.aiBubbleText
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: message.role === 'user' ? '#fff' : '#000' }}>
|
||||
{message.content}
|
||||
{message.role === 'assistant' && !message.content && isLoading && (
|
||||
<span
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: '8px',
|
||||
height: '16px',
|
||||
background: '#1890ff',
|
||||
marginLeft: '2px',
|
||||
animation: 'blink 1s infinite'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Text>
|
||||
<div
|
||||
style={{
|
||||
marginTop: '4px',
|
||||
fontSize: '11px',
|
||||
opacity: 0.7
|
||||
}}
|
||||
>
|
||||
{message.timestamp.toLocaleTimeString('zh-CN', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})}
|
||||
</div>
|
||||
{message.content}
|
||||
{message.role === 'assistant' && !message.content && isLoading && (
|
||||
<span
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: '8px',
|
||||
height: '16px',
|
||||
background: theme.colors.primary,
|
||||
marginLeft: '2px',
|
||||
animation: 'blink 1s infinite'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Text>
|
||||
<div
|
||||
style={{
|
||||
marginTop: '4px',
|
||||
fontSize: theme.typography.fontSize.xs,
|
||||
opacity: 0.7
|
||||
}}
|
||||
>
|
||||
{message.timestamp.toLocaleTimeString('zh-CN', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* AI 消息的操作按钮 */}
|
||||
{message.role === 'assistant' && message.content && (
|
||||
<div style={{ marginTop: '12px', display: 'flex', gap: '8px' }}>
|
||||
{/* AI 消息的操作按钮 */}
|
||||
{message.role === 'assistant' && message.content && (
|
||||
<MessageActions
|
||||
theme={theme}
|
||||
initial={{ opacity: 0, y: 5 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.1 }}
|
||||
>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<ReloadOutlined />}
|
||||
onClick={() => handleRegenerateMessage(message.id)}
|
||||
loading={regeneratingMessageId === message.id}
|
||||
disabled={regeneratingMessageId !== null}
|
||||
>
|
||||
重新生成
|
||||
</Button>
|
||||
{message.metadata?.type === 'article' && message.metadata.articleUrl && (
|
||||
<Button
|
||||
size="small"
|
||||
icon={<ReloadOutlined />}
|
||||
onClick={() => handleRegenerateMessage(message.id)}
|
||||
loading={regeneratingMessageId === message.id}
|
||||
disabled={regeneratingMessageId !== null}
|
||||
type="primary"
|
||||
icon={<CommentOutlined />}
|
||||
onClick={() => {
|
||||
setCurrentArticleUrl(message.metadata!.articleUrl!)
|
||||
setLastAiResponse(message.content)
|
||||
handlePostComment()
|
||||
}}
|
||||
>
|
||||
重新生成
|
||||
发送到评论区
|
||||
</Button>
|
||||
{message.metadata?.type === 'article' && message.metadata.articleUrl && (
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
icon={<CommentOutlined />}
|
||||
onClick={() => {
|
||||
// 更新当前文章 URL 和 AI 回复
|
||||
setCurrentArticleUrl(message.metadata!.articleUrl!)
|
||||
setLastAiResponse(message.content)
|
||||
handlePostComment()
|
||||
}}
|
||||
>
|
||||
发送到评论区
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</MessageActions>
|
||||
)}
|
||||
</MessageBubble>
|
||||
))}
|
||||
</Space>
|
||||
</AnimatePresence>
|
||||
)}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
</MessagesContainer>
|
||||
|
||||
{/* Input Area */}
|
||||
<div
|
||||
style={{
|
||||
padding: '16px 24px',
|
||||
background: '#fff',
|
||||
borderTop: '1px solid #e8e8e8',
|
||||
boxShadow: '0 -2px 8px rgba(0, 0, 0, 0.06)'
|
||||
}}
|
||||
>
|
||||
<Space direction="vertical" style={{ width: '100%' }} size="middle">
|
||||
<Space.Compact style={{ width: '100%' }}>
|
||||
<TextArea
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
onKeyPress={handleKeyPress}
|
||||
placeholder="输入消息... (Enter 发送, Shift+Enter 换行)"
|
||||
autoSize={{ minRows: 1, maxRows: 4 }}
|
||||
disabled={isLoading}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SendOutlined />}
|
||||
onClick={handleSend}
|
||||
disabled={!inputValue.trim() || isLoading}
|
||||
style={{ height: 'auto' }}
|
||||
>
|
||||
发送
|
||||
</Button>
|
||||
</Space.Compact>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
<InputContainer theme={theme}>
|
||||
<InputWrapper
|
||||
theme={theme}
|
||||
isMultiLine={isMultiLine}
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<TextArea
|
||||
rows={4}
|
||||
ref={inputRef}
|
||||
value={inputValue}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value
|
||||
setInputValue(value)
|
||||
// Check if text contains newline or has multiple lines
|
||||
setIsMultiLine(value.includes('\n') || value.split('\n').length > 1)
|
||||
}}
|
||||
onKeyPress={handleKeyPress}
|
||||
placeholder="输入消息... (Enter 发送, Shift+Enter 换行)"
|
||||
disabled={isLoading}
|
||||
style={{
|
||||
flex: 1,
|
||||
border: 'none',
|
||||
boxShadow: 'none',
|
||||
resize: 'none',
|
||||
outline: 'none'
|
||||
}}
|
||||
styles={{
|
||||
textarea: {
|
||||
scrollbarWidth: 'thin',
|
||||
scrollbarColor: `${theme.colors.border} transparent`
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<SendButton
|
||||
theme={theme}
|
||||
disabled={!inputValue.trim() || isLoading}
|
||||
onClick={handleSend}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
transition={{ duration: 0.15 }}
|
||||
>
|
||||
<SendOutlined />
|
||||
</SendButton>
|
||||
</InputWrapper>
|
||||
</InputContainer>
|
||||
</ChatContainer>
|
||||
|
||||
{/* QR Code Login Modal */}
|
||||
<Modal
|
||||
@@ -915,6 +1126,11 @@ const Chat: React.FC = () => {
|
||||
okText="已完成登录"
|
||||
cancelText="取消"
|
||||
width={400}
|
||||
styles={{
|
||||
mask: { backdropFilter: 'blur(10px)', background: 'rgba(0, 0, 0, 0.45)' }
|
||||
}}
|
||||
transitionName="zoom"
|
||||
maskTransitionName="fade"
|
||||
>
|
||||
<div style={{ textAlign: 'center', padding: '20px 0' }}>
|
||||
<p style={{ marginBottom: 16, color: '#666' }}>请使用小黑盒 APP 扫描二维码登录</p>
|
||||
@@ -952,27 +1168,35 @@ const Chat: React.FC = () => {
|
||||
onCancel={handleConfirmCancel}
|
||||
okText="确认发送"
|
||||
cancelText="取消"
|
||||
width={500}
|
||||
width={600}
|
||||
maskClosable={true}
|
||||
keyboard={true}
|
||||
styles={{
|
||||
mask: { backdropFilter: 'blur(10px)', background: 'rgba(0, 0, 0, 0.45)' }
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
将以用户 <strong>{confirmUsername}</strong> 的身份发送评论到:
|
||||
</p>
|
||||
<p style={{ wordBreak: 'break-all', color: '#666' }}>{currentArticleUrl}</p>
|
||||
<p style={{ marginTop: 16 }}>评论内容:</p>
|
||||
<div
|
||||
<p style={{ wordBreak: 'break-all', color: '#666', marginBottom: 16 }}>
|
||||
{currentArticleUrl}
|
||||
</p>
|
||||
<p style={{ marginTop: 16, marginBottom: 8, fontWeight: 500 }}>评论内容(可编辑):</p>
|
||||
<TextArea
|
||||
value={editableComment}
|
||||
onChange={(e) => setEditableComment(e.target.value)}
|
||||
placeholder="请输入评论内容"
|
||||
autoSize={{ minRows: 4, maxRows: 12 }}
|
||||
style={{
|
||||
maxHeight: 200,
|
||||
overflow: 'auto',
|
||||
padding: 8,
|
||||
background: '#f5f5f5',
|
||||
borderRadius: 4,
|
||||
whiteSpace: 'pre-wrap',
|
||||
fontSize: 12
|
||||
fontSize: 14,
|
||||
borderRadius: 8,
|
||||
border: '1px solid #d9d9d9'
|
||||
}}
|
||||
>
|
||||
{lastAiResponse}
|
||||
</div>
|
||||
/>
|
||||
<p style={{ marginTop: 8, fontSize: 12, color: '#999' }}>
|
||||
字数:{editableComment?.length || 0}
|
||||
</p>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
@@ -983,6 +1207,10 @@ const Chat: React.FC = () => {
|
||||
onClose={() => setIsAccountDrawerVisible(false)}
|
||||
open={isAccountDrawerVisible}
|
||||
width={400}
|
||||
styles={{
|
||||
mask: { backdropFilter: 'blur(10px)', background: 'rgba(0, 0, 0, 0.45)' },
|
||||
body: { padding: '24px' }
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import React, { useState, useRef, useEffect } from 'react'
|
||||
import ContextMenu from './ContextMenu'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
|
||||
const FloatingBall: React.FC = () => {
|
||||
const [isBlinking, setIsBlinking] = useState(false)
|
||||
const [isContextMenuOpen, setIsContextMenuOpen] = useState(false)
|
||||
const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, y: 0 })
|
||||
const [isMouseOverBall, setIsMouseOverBall] = useState(false)
|
||||
const [isActionMenuOpen, setIsActionMenuOpen] = useState(false)
|
||||
const [showTextPrompt, setShowTextPrompt] = useState(false)
|
||||
const [selectedText, setSelectedText] = useState('')
|
||||
const [selectedButtonIndex, setSelectedButtonIndex] = useState(0) // 0: 对话, 1: 设置, 2: 退出
|
||||
const isDraggingRef = useRef(false)
|
||||
const startPosRef = useRef({ x: 0, y: 0 })
|
||||
const windowStartRef = useRef({ x: 0, y: 0 })
|
||||
@@ -42,8 +40,15 @@ const FloatingBall: React.FC = () => {
|
||||
'show-text-prompt',
|
||||
(_: unknown, text: string) => {
|
||||
setSelectedText(text)
|
||||
setShowTextPrompt(true)
|
||||
setIsActionMenuOpen(true)
|
||||
// 切换按钮显示状态
|
||||
setIsActionMenuOpen((prev) => {
|
||||
const newState = !prev
|
||||
if (newState) {
|
||||
// When opening menu, reset to first button
|
||||
setSelectedButtonIndex(0)
|
||||
}
|
||||
return newState
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
@@ -54,6 +59,61 @@ const FloatingBall: React.FC = () => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Handle keyboard navigation when menu is open
|
||||
useEffect(() => {
|
||||
if (!isActionMenuOpen) return
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent): void => {
|
||||
switch (e.key) {
|
||||
case 'ArrowUp':
|
||||
e.preventDefault()
|
||||
setSelectedButtonIndex((prev) => (prev === 0 ? 2 : prev - 1))
|
||||
break
|
||||
case 'ArrowDown':
|
||||
e.preventDefault()
|
||||
setSelectedButtonIndex((prev) => (prev === 2 ? 0 : prev + 1))
|
||||
break
|
||||
case 'Tab':
|
||||
e.preventDefault()
|
||||
// Tab cycles through options
|
||||
setSelectedButtonIndex((prev) => (prev === 2 ? 0 : prev + 1))
|
||||
break
|
||||
case 'Enter':
|
||||
e.preventDefault()
|
||||
executeSelectedAction()
|
||||
break
|
||||
case 'Escape':
|
||||
e.preventDefault()
|
||||
setIsActionMenuOpen(false)
|
||||
setSelectedText('')
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown)
|
||||
return () => window.removeEventListener('keydown', handleKeyDown)
|
||||
}, [isActionMenuOpen, selectedButtonIndex, selectedText])
|
||||
|
||||
// Execute the action for the currently selected button
|
||||
const executeSelectedAction = (): void => {
|
||||
switch (selectedButtonIndex) {
|
||||
case 0: // 对话
|
||||
console.log('对话按钮选中 - 打开聊天窗口')
|
||||
window.electron.ipcRenderer.send('open-chat', selectedText || undefined)
|
||||
break
|
||||
case 1: // 设置
|
||||
console.log('设置按钮选中 - 打开设置窗口')
|
||||
window.electron.ipcRenderer.send('open-settings')
|
||||
break
|
||||
case 2: // 退出
|
||||
console.log('退出按钮选中 - 退出应用')
|
||||
window.electron.ipcRenderer.send('quit-app')
|
||||
break
|
||||
}
|
||||
setIsActionMenuOpen(false)
|
||||
setSelectedText('')
|
||||
}
|
||||
|
||||
const handleMouseEnterBall = (): void => {
|
||||
setIsMouseOverBall(true)
|
||||
// When mouse enters the ball area, stop ignoring mouse events
|
||||
@@ -63,45 +123,14 @@ const FloatingBall: React.FC = () => {
|
||||
const handleMouseLeaveBall = (): void => {
|
||||
setIsMouseOverBall(false)
|
||||
// When mouse leaves the ball area, always restore click-through
|
||||
if (!isContextMenuOpen) {
|
||||
window.electron.ipcRenderer.send('set-ignore-mouse-events', true, { forward: true })
|
||||
}
|
||||
}
|
||||
|
||||
const handleContextMenu = (e: React.MouseEvent): void => {
|
||||
e.preventDefault()
|
||||
// Show custom context menu at cursor position
|
||||
setContextMenuPosition({ x: e.clientX, y: e.clientY })
|
||||
setIsContextMenuOpen(true)
|
||||
// Disable mouse events pass-through when menu is open
|
||||
window.electron.ipcRenderer.send('set-ignore-mouse-events', false)
|
||||
}
|
||||
|
||||
const handleCloseContextMenu = (): void => {
|
||||
setIsContextMenuOpen(false)
|
||||
// Re-enable mouse events pass-through when menu closes, but only if mouse is not over the ball
|
||||
setTimeout(() => {
|
||||
if (!isMouseOverBall) {
|
||||
window.electron.ipcRenderer.send('set-ignore-mouse-events', true, { forward: true })
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
|
||||
const handleSettingsClick = (): void => {
|
||||
// Send message to main process to open settings
|
||||
window.electron.ipcRenderer.send('open-settings')
|
||||
}
|
||||
|
||||
const handleQuitClick = (): void => {
|
||||
// Send message to main process to quit app
|
||||
window.electron.ipcRenderer.send('quit-app')
|
||||
window.electron.ipcRenderer.send('set-ignore-mouse-events', true, { forward: true })
|
||||
}
|
||||
|
||||
const handleMouseDown = async (e: React.MouseEvent): Promise<void> => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
// Ignore right click for context menu
|
||||
// Ignore right click
|
||||
if (e.button === 2) {
|
||||
return
|
||||
}
|
||||
@@ -196,23 +225,6 @@ const FloatingBall: React.FC = () => {
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
<ContextMenu
|
||||
isOpen={isContextMenuOpen}
|
||||
position={contextMenuPosition}
|
||||
onClose={handleCloseContextMenu}
|
||||
onSettings={handleSettingsClick}
|
||||
onQuit={handleQuitClick}
|
||||
onMouseEnter={() => {
|
||||
// Keep mouse events enabled when hovering over menu
|
||||
window.electron.ipcRenderer.send('set-ignore-mouse-events', false)
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
// When mouse leaves menu, restore click-through
|
||||
if (!isMouseOverBall) {
|
||||
window.electron.ipcRenderer.send('set-ignore-mouse-events', true, { forward: true })
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
@@ -224,202 +236,201 @@ const FloatingBall: React.FC = () => {
|
||||
pointerEvents: 'none'
|
||||
}}
|
||||
>
|
||||
{/* Text Prompt */}
|
||||
{showTextPrompt && selectedText && (
|
||||
<div
|
||||
onMouseEnter={() => {
|
||||
window.electron.ipcRenderer.send('set-ignore-mouse-events', false)
|
||||
}}
|
||||
onMouseDown={(e) => {
|
||||
e.stopPropagation()
|
||||
}}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 'calc(50% - 100px)',
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
background: 'rgba(33, 150, 243, 0.95)',
|
||||
color: 'white',
|
||||
padding: '8px 16px',
|
||||
borderRadius: '20px',
|
||||
fontSize: '12px',
|
||||
fontWeight: 500,
|
||||
whiteSpace: 'nowrap',
|
||||
boxShadow: '0 2px 12px rgba(33, 150, 243, 0.4)',
|
||||
pointerEvents: 'auto',
|
||||
animation: 'fadeInDown 0.3s ease-out',
|
||||
zIndex: 10,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px'
|
||||
}}
|
||||
>
|
||||
<span>要对这段文本做什么?</span>
|
||||
<button
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
console.log('Close button clicked')
|
||||
setShowTextPrompt(false)
|
||||
setSelectedText('')
|
||||
setIsActionMenuOpen(false)
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.background = 'rgba(255, 255, 255, 0.4)'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.background = 'rgba(255, 255, 255, 0.2)'
|
||||
}}
|
||||
style={{
|
||||
background: 'rgba(255, 255, 255, 0.2)',
|
||||
border: 'none',
|
||||
borderRadius: '50%',
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '10px',
|
||||
color: 'white',
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: 0,
|
||||
flexShrink: 0,
|
||||
transition: 'background 0.2s ease'
|
||||
}}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Action Menu Items */}
|
||||
{isActionMenuOpen && (
|
||||
<>
|
||||
{/* Action Item 1 - Top Left */}
|
||||
<div
|
||||
style={{
|
||||
<AnimatePresence>
|
||||
{isActionMenuOpen && (
|
||||
<>
|
||||
{/* Action Item 1 - 对话 (Top Left) */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.3, x: 30, y: 30 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: selectedButtonIndex === 0 ? 1.1 : 1,
|
||||
x: 0,
|
||||
y: 0
|
||||
}}
|
||||
exit={{ opacity: 0, scale: 0.3, x: 30, y: 30 }}
|
||||
transition={{ duration: 0.2, ease: 'easeOut' }}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 'calc(50% - 90px)',
|
||||
top: 'calc(50% - 60px)',
|
||||
width: '40px',
|
||||
height: '40px',
|
||||
left: 'calc(50% - 105px)',
|
||||
top: 'calc(50% - 75px)',
|
||||
width: '44px',
|
||||
height: '44px',
|
||||
borderRadius: '50%',
|
||||
background: 'linear-gradient(135deg, #4CAF50 0%, #45a049 100%)',
|
||||
background: selectedButtonIndex === 0
|
||||
? 'rgba(0, 122, 255, 0.15)'
|
||||
: 'rgba(255, 255, 255, 0.95)',
|
||||
backdropFilter: 'blur(20px)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
cursor: 'pointer',
|
||||
boxShadow: '0 2px 8px rgba(76, 175, 80, 0.4)',
|
||||
boxShadow: selectedButtonIndex === 0
|
||||
? '0 4px 16px rgba(0, 122, 255, 0.3)'
|
||||
: '0 2px 8px rgba(0, 0, 0, 0.1)',
|
||||
pointerEvents: 'auto',
|
||||
animation: 'slideIn1 0.3s ease-out',
|
||||
transition: 'transform 0.2s ease, box-shadow 0.2s ease'
|
||||
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
border: selectedButtonIndex === 0
|
||||
? '2px solid #007AFF'
|
||||
: '0.5px solid rgba(0, 0, 0, 0.04)'
|
||||
}}
|
||||
onClick={() => {
|
||||
console.log('Action 1 clicked - Opening chat window')
|
||||
// Open chat window with selected text
|
||||
console.log('对话按钮点击 - 打开聊天窗口')
|
||||
window.electron.ipcRenderer.send('open-chat', selectedText || undefined)
|
||||
setIsActionMenuOpen(false)
|
||||
setShowTextPrompt(false)
|
||||
setSelectedText('')
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
setSelectedButtonIndex(0)
|
||||
window.electron.ipcRenderer.send('set-ignore-mouse-events', false)
|
||||
e.currentTarget.style.transform = 'scale(1.1)'
|
||||
e.currentTarget.style.boxShadow = '0 4px 12px rgba(76, 175, 80, 0.6)'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.transform = 'scale(1)'
|
||||
e.currentTarget.style.boxShadow = '0 2px 8px rgba(76, 175, 80, 0.4)'
|
||||
onMouseLeave={() => {
|
||||
window.electron.ipcRenderer.send('set-ignore-mouse-events', true, { forward: true })
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: '20px', color: 'white' }}>✓</span>
|
||||
</div>
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="#007AFF"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
|
||||
</svg>
|
||||
</motion.div>
|
||||
|
||||
{/* Action Item 2 - Middle Left */}
|
||||
<div
|
||||
{/* Action Item 2 - 设置 (Middle Left) */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.3, x: 40 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: selectedButtonIndex === 1 ? 1.1 : 1,
|
||||
x: 0
|
||||
}}
|
||||
exit={{ opacity: 0, scale: 0.3, x: 40 }}
|
||||
transition={{ duration: 0.2, ease: 'easeOut', delay: 0.05 }}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 'calc(50% - 100px)',
|
||||
top: 'calc(50% - 20px)',
|
||||
width: '40px',
|
||||
height: '40px',
|
||||
left: 'calc(50% - 120px)',
|
||||
top: 'calc(50% - 22px)',
|
||||
width: '44px',
|
||||
height: '44px',
|
||||
borderRadius: '50%',
|
||||
background: 'linear-gradient(135deg, #FF9800 0%, #F57C00 100%)',
|
||||
background: selectedButtonIndex === 1
|
||||
? 'rgba(142, 142, 147, 0.15)'
|
||||
: 'rgba(255, 255, 255, 0.95)',
|
||||
backdropFilter: 'blur(20px)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
cursor: 'pointer',
|
||||
boxShadow: '0 2px 8px rgba(255, 152, 0, 0.4)',
|
||||
boxShadow: selectedButtonIndex === 1
|
||||
? '0 4px 16px rgba(142, 142, 147, 0.3)'
|
||||
: '0 2px 8px rgba(0, 0, 0, 0.1)',
|
||||
pointerEvents: 'auto',
|
||||
animation: 'slideIn2 0.3s ease-out',
|
||||
transition: 'transform 0.2s ease, box-shadow 0.2s ease'
|
||||
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
border: selectedButtonIndex === 1
|
||||
? '2px solid #8E8E93'
|
||||
: '0.5px solid rgba(0, 0, 0, 0.04)'
|
||||
}}
|
||||
onClick={() => {
|
||||
console.log('Action 2 clicked')
|
||||
if (selectedText) {
|
||||
console.log('Selected text:', selectedText)
|
||||
}
|
||||
setIsActionMenuOpen(false)
|
||||
setShowTextPrompt(false)
|
||||
setSelectedText('')
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.transform = 'scale(1.1)'
|
||||
e.currentTarget.style.boxShadow = '0 4px 12px rgba(255, 152, 0, 0.6)'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.transform = 'scale(1)'
|
||||
e.currentTarget.style.boxShadow = '0 2px 8px rgba(255, 152, 0, 0.4)'
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: '20px', color: 'white' }}>★</span>
|
||||
</div>
|
||||
|
||||
{/* Action Item 3 - Bottom Left */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 'calc(50% - 90px)',
|
||||
top: 'calc(50% + 20px)',
|
||||
width: '40px',
|
||||
height: '40px',
|
||||
borderRadius: '50%',
|
||||
background: 'linear-gradient(135deg, #f44336 0%, #d32f2f 100%)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
cursor: 'pointer',
|
||||
boxShadow: '0 2px 8px rgba(244, 67, 54, 0.4)',
|
||||
pointerEvents: 'auto',
|
||||
animation: 'slideIn3 0.3s ease-out',
|
||||
transition: 'transform 0.2s ease, box-shadow 0.2s ease'
|
||||
}}
|
||||
onClick={() => {
|
||||
console.log('Action 3 clicked - Opening settings window')
|
||||
// Open settings window
|
||||
console.log('设置按钮点击 - 打开设置窗口')
|
||||
window.electron.ipcRenderer.send('open-settings')
|
||||
setIsActionMenuOpen(false)
|
||||
setShowTextPrompt(false)
|
||||
setSelectedText('')
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
onMouseEnter={() => {
|
||||
setSelectedButtonIndex(1)
|
||||
window.electron.ipcRenderer.send('set-ignore-mouse-events', false)
|
||||
e.currentTarget.style.transform = 'scale(1.1)'
|
||||
e.currentTarget.style.boxShadow = '0 4px 12px rgba(244, 67, 54, 0.6)'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.transform = 'scale(1)'
|
||||
e.currentTarget.style.boxShadow = '0 2px 8px rgba(244, 67, 54, 0.4)'
|
||||
onMouseLeave={() => {
|
||||
window.electron.ipcRenderer.send('set-ignore-mouse-events', true, { forward: true })
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: '20px', color: 'white' }}>✕</span>
|
||||
</div>
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="#8E8E93"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
<path d="M12 1v6M12 17v6M4.22 4.22l4.24 4.24M15.54 15.54l4.24 4.24M1 12h6M17 12h6M4.22 19.78l4.24-4.24M15.54 8.46l4.24-4.24" />
|
||||
</svg>
|
||||
</motion.div>
|
||||
|
||||
{/* Action Item 3 - 退出 (Bottom Left) */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.3, x: 30, y: -30 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: selectedButtonIndex === 2 ? 1.1 : 1,
|
||||
x: 0,
|
||||
y: 0
|
||||
}}
|
||||
exit={{ opacity: 0, scale: 0.3, x: 30, y: -30 }}
|
||||
transition={{ duration: 0.2, ease: 'easeOut', delay: 0.1 }}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 'calc(50% - 105px)',
|
||||
top: 'calc(50% + 31px)',
|
||||
width: '44px',
|
||||
height: '44px',
|
||||
borderRadius: '50%',
|
||||
background: selectedButtonIndex === 2
|
||||
? 'rgba(255, 59, 48, 0.15)'
|
||||
: 'rgba(255, 255, 255, 0.95)',
|
||||
backdropFilter: 'blur(20px)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
cursor: 'pointer',
|
||||
boxShadow: selectedButtonIndex === 2
|
||||
? '0 4px 16px rgba(255, 59, 48, 0.3)'
|
||||
: '0 2px 8px rgba(0, 0, 0, 0.1)',
|
||||
pointerEvents: 'auto',
|
||||
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
border: selectedButtonIndex === 2
|
||||
? '2px solid #FF3B30'
|
||||
: '0.5px solid rgba(0, 0, 0, 0.04)'
|
||||
}}
|
||||
onClick={() => {
|
||||
console.log('退出按钮点击 - 退出应用')
|
||||
window.electron.ipcRenderer.send('quit-app')
|
||||
setIsActionMenuOpen(false)
|
||||
setSelectedText('')
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
setSelectedButtonIndex(2)
|
||||
window.electron.ipcRenderer.send('set-ignore-mouse-events', false)
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
window.electron.ipcRenderer.send('set-ignore-mouse-events', true, { forward: true })
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="#FF3B30"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<polyline points="15 18 9 12 15 6" />
|
||||
</svg>
|
||||
</motion.div>
|
||||
</>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
{/* Robot Ball Container */}
|
||||
<div
|
||||
@@ -433,7 +444,6 @@ const FloatingBall: React.FC = () => {
|
||||
{/* Robot Ball */}
|
||||
<div
|
||||
onMouseDown={handleMouseDown}
|
||||
onContextMenu={handleContextMenu}
|
||||
onMouseEnter={(e) => {
|
||||
handleMouseEnterBall()
|
||||
e.currentTarget.style.boxShadow = '0 4px 14px rgba(33, 150, 243, 0.6)'
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
// Apple-inspired design system
|
||||
|
||||
export interface Theme {
|
||||
colors: {
|
||||
// Surfaces
|
||||
background: string
|
||||
surface: string
|
||||
surfaceElevated: string
|
||||
surfaceHover: string
|
||||
|
||||
// Text
|
||||
textPrimary: string
|
||||
textSecondary: string
|
||||
textTertiary: string
|
||||
|
||||
// Brand
|
||||
primary: string
|
||||
primaryHover: string
|
||||
primaryActive: string
|
||||
|
||||
// Message bubbles
|
||||
userBubble: string
|
||||
userBubbleText: string
|
||||
aiBubble: string
|
||||
aiBubbleText: string
|
||||
|
||||
// Borders
|
||||
border: string
|
||||
borderLight: string
|
||||
|
||||
// States
|
||||
success: string
|
||||
error: string
|
||||
warning: string
|
||||
|
||||
// Glass effect
|
||||
glassBackground: string
|
||||
glassBorder: string
|
||||
}
|
||||
|
||||
shadows: {
|
||||
sm: string
|
||||
md: string
|
||||
lg: string
|
||||
xl: string
|
||||
}
|
||||
|
||||
spacing: {
|
||||
xs: string
|
||||
sm: string
|
||||
md: string
|
||||
lg: string
|
||||
xl: string
|
||||
xxl: string
|
||||
}
|
||||
|
||||
borderRadius: {
|
||||
sm: string
|
||||
md: string
|
||||
lg: string
|
||||
xl: string
|
||||
full: string
|
||||
}
|
||||
|
||||
typography: {
|
||||
fontFamily: string
|
||||
fontSize: {
|
||||
xs: string
|
||||
sm: string
|
||||
base: string
|
||||
lg: string
|
||||
xl: string
|
||||
xxl: string
|
||||
}
|
||||
fontWeight: {
|
||||
normal: number
|
||||
medium: number
|
||||
semibold: number
|
||||
bold: number
|
||||
}
|
||||
}
|
||||
|
||||
animation: {
|
||||
fast: string
|
||||
normal: string
|
||||
slow: string
|
||||
}
|
||||
}
|
||||
|
||||
export const lightTheme: Theme = {
|
||||
colors: {
|
||||
// Surfaces - Apple's clean light grays
|
||||
background: '#f5f5f7',
|
||||
surface: '#ffffff',
|
||||
surfaceElevated: '#ffffff',
|
||||
surfaceHover: '#f9f9f9',
|
||||
|
||||
// Text - Apple's neutral grays
|
||||
textPrimary: '#1d1d1f',
|
||||
textSecondary: '#6e6e73',
|
||||
textTertiary: '#86868b',
|
||||
|
||||
// Brand - Apple's blue accent
|
||||
primary: '#007aff',
|
||||
primaryHover: '#0051d5',
|
||||
primaryActive: '#004ecb',
|
||||
|
||||
// Message bubbles
|
||||
userBubble: 'linear-gradient(135deg, #007aff 0%, #0051d5 100%)',
|
||||
userBubbleText: '#ffffff',
|
||||
aiBubble: 'rgba(255, 255, 255, 0.7)',
|
||||
aiBubbleText: '#1d1d1f',
|
||||
|
||||
// Borders
|
||||
border: '#d2d2d7',
|
||||
borderLight: '#e5e5ea',
|
||||
|
||||
// States
|
||||
success: '#34c759',
|
||||
error: '#ff3b30',
|
||||
warning: '#ff9500',
|
||||
|
||||
// Glass effect
|
||||
glassBackground: 'rgba(255, 255, 255, 0.7)',
|
||||
glassBorder: 'rgba(255, 255, 255, 0.18)',
|
||||
},
|
||||
|
||||
shadows: {
|
||||
sm: '0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.02)',
|
||||
md: '0 4px 6px rgba(0, 0, 0, 0.04), 0 2px 4px rgba(0, 0, 0, 0.02)',
|
||||
lg: '0 10px 15px rgba(0, 0, 0, 0.06), 0 4px 6px rgba(0, 0, 0, 0.03)',
|
||||
xl: '0 20px 25px rgba(0, 0, 0, 0.08), 0 10px 10px rgba(0, 0, 0, 0.04)',
|
||||
},
|
||||
|
||||
spacing: {
|
||||
xs: '4px',
|
||||
sm: '8px',
|
||||
md: '16px',
|
||||
lg: '24px',
|
||||
xl: '32px',
|
||||
xxl: '48px',
|
||||
},
|
||||
|
||||
borderRadius: {
|
||||
sm: '6px',
|
||||
md: '12px',
|
||||
lg: '16px',
|
||||
xl: '24px',
|
||||
full: '9999px',
|
||||
},
|
||||
|
||||
typography: {
|
||||
fontFamily: '-apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Helvetica Neue", Arial, sans-serif',
|
||||
fontSize: {
|
||||
xs: '12px',
|
||||
sm: '14px',
|
||||
base: '16px',
|
||||
lg: '18px',
|
||||
xl: '20px',
|
||||
xxl: '24px',
|
||||
},
|
||||
fontWeight: {
|
||||
normal: 400,
|
||||
medium: 500,
|
||||
semibold: 600,
|
||||
bold: 700,
|
||||
},
|
||||
},
|
||||
|
||||
animation: {
|
||||
fast: '150ms',
|
||||
normal: '250ms',
|
||||
slow: '350ms',
|
||||
},
|
||||
}
|
||||
|
||||
export const darkTheme: Theme = {
|
||||
colors: {
|
||||
// Surfaces - Apple's dark mode grays
|
||||
background: '#000000',
|
||||
surface: '#1c1c1e',
|
||||
surfaceElevated: '#2c2c2e',
|
||||
surfaceHover: '#3a3a3c',
|
||||
|
||||
// Text - Apple's light grays for dark mode
|
||||
textPrimary: '#f5f5f7',
|
||||
textSecondary: '#98989d',
|
||||
textTertiary: '#636366',
|
||||
|
||||
// Brand - Apple's blue accent (slightly brighter for dark mode)
|
||||
primary: '#0a84ff',
|
||||
primaryHover: '#409cff',
|
||||
primaryActive: '#0077ed',
|
||||
|
||||
// Message bubbles
|
||||
userBubble: 'linear-gradient(135deg, #0a84ff 0%, #0077ed 100%)',
|
||||
userBubbleText: '#ffffff',
|
||||
aiBubble: 'rgba(44, 44, 46, 0.9)',
|
||||
aiBubbleText: '#f5f5f7',
|
||||
|
||||
// Borders
|
||||
border: '#38383a',
|
||||
borderLight: '#48484a',
|
||||
|
||||
// States
|
||||
success: '#32d74b',
|
||||
error: '#ff453a',
|
||||
warning: '#ff9f0a',
|
||||
|
||||
// Glass effect
|
||||
glassBackground: 'rgba(44, 44, 46, 0.7)',
|
||||
glassBorder: 'rgba(255, 255, 255, 0.1)',
|
||||
},
|
||||
|
||||
shadows: {
|
||||
sm: '0 1px 3px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2)',
|
||||
md: '0 4px 6px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.2)',
|
||||
lg: '0 10px 15px rgba(0, 0, 0, 0.4), 0 4px 6px rgba(0, 0, 0, 0.3)',
|
||||
xl: '0 20px 25px rgba(0, 0, 0, 0.5), 0 10px 10px rgba(0, 0, 0, 0.4)',
|
||||
},
|
||||
|
||||
spacing: {
|
||||
xs: '4px',
|
||||
sm: '8px',
|
||||
md: '16px',
|
||||
lg: '24px',
|
||||
xl: '32px',
|
||||
xxl: '48px',
|
||||
},
|
||||
|
||||
borderRadius: {
|
||||
sm: '6px',
|
||||
md: '12px',
|
||||
lg: '16px',
|
||||
xl: '24px',
|
||||
full: '9999px',
|
||||
},
|
||||
|
||||
typography: {
|
||||
fontFamily: '-apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Helvetica Neue", Arial, sans-serif',
|
||||
fontSize: {
|
||||
xs: '12px',
|
||||
sm: '14px',
|
||||
base: '16px',
|
||||
lg: '18px',
|
||||
xl: '20px',
|
||||
xxl: '24px',
|
||||
},
|
||||
fontWeight: {
|
||||
normal: 400,
|
||||
medium: 500,
|
||||
semibold: 600,
|
||||
bold: 700,
|
||||
},
|
||||
},
|
||||
|
||||
animation: {
|
||||
fast: '150ms',
|
||||
normal: '250ms',
|
||||
slow: '350ms',
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user