Compare commits

..

25 Commits

Author SHA1 Message Date
Gabe Yuan
63f989b31a v1.8.7 2024-04-18 10:07:53 +08:00
Gabe Yuan
2ae5d01d5c fix: custom option 2024-04-18 09:48:07 +08:00
Gabe Yuan
130f1deed1 fix: remove encodeURIComponent 2024-04-18 00:31:36 +08:00
Gabe Yuan
5880d85b48 fix: encodeURIComponent text 2024-04-17 23:44:53 +08:00
Gabe Yuan
9455670e80 feat: custom request 2024-04-17 22:35:12 +08:00
Gabe Yuan
e369321c66 feat: custom request 2024-04-17 17:38:54 +08:00
Gabe Yuan
efc51b0d46 feat: extend styles for transbox 2024-04-17 15:35:44 +08:00
Gabe Yuan
d6f3b23b88 fix: tranbox ui 2024-04-17 10:31:37 +08:00
Gabe Yuan
0a4fa7b9f8 fix: tranbox ui 2024-04-17 10:03:56 +08:00
Gabe Yuan
2b3e4a8d25 fix: reaplce loading button 2024-04-16 16:39:11 +08:00
Gabe Yuan
bf3a16f96d feat: export word & translation 2024-04-16 16:29:59 +08:00
Gabe Yuan
b416e72820 fix: transbox ui 2024-04-16 15:22:27 +08:00
Gabe Yuan
ca84bdb227 feat: tranbox hover trigger 2024-04-16 12:47:55 +08:00
Gabe Yuan
148a4e97a6 fix: tranbox ui 2024-04-16 11:25:04 +08:00
Gabe Yuan
a13493ebc2 feat: simple style tranbox 2024-04-16 00:54:37 +08:00
Gabe Yuan
ce4ac79e5f fix: optimization tranbox 2024-04-15 18:04:35 +08:00
Gabe Yuan
8f76ea49e7 fix: loading icon 2024-04-13 21:23:58 +08:00
Gabe Yuan
923d3293cd fix: limit selection btn in window & click to hide 2024-04-12 22:28:40 +08:00
Gabe Yuan
7379ff8d15 v1.8.6 2024-04-12 14:39:17 +08:00
Gabe Yuan
18ebec350d fix: clean env 2024-04-12 14:33:29 +08:00
Gabe Yuan
3b0cbc53aa fix: response err data: url 2024-04-12 11:47:22 +08:00
Gabe Yuan
f00e8ffa4d feat: add niutrans api 2024-04-12 11:31:01 +08:00
Gabe Yuan
d6f7aad1c3 fix: utils func 2024-04-11 10:44:25 +08:00
Gabe Yuan
092ea6e836 fix: custom api 2024-04-10 13:37:16 +08:00
Gabe Yuan
d565e2464a feat: tranbox: mobile support 2024-04-07 16:55:54 +08:00
34 changed files with 1145 additions and 700 deletions

10
.env
View File

@@ -2,26 +2,18 @@ GENERATE_SOURCEMAP=false
REACT_APP_NAME=KISS Translator
REACT_APP_NAME_CN=简约翻译
REACT_APP_VERSION=1.8.5
REACT_APP_VERSION=1.8.7
REACT_APP_HOMEPAGE=https://github.com/fishjar/kiss-translator
REACT_APP_OPTIONSPAGE=https://fishjar.github.io/kiss-translator/options.html
REACT_APP_OPTIONSPAGE2=https://kiss-translator.rayjar.com/options
REACT_APP_OPTIONSPAGE_DEV=http://localhost:3000/options.html
REACT_APP_LOGOURL=https://fishjar.github.io/kiss-translator/images/logo192.png
REACT_APP_LOGOURL2=https://kiss-translator.rayjar.com/images/logo192.png
REACT_APP_RULESURL=https://fishjar.github.io/kiss-rules/kiss-rules.json
REACT_APP_RULESURL_ON=https://fishjar.github.io/kiss-rules/kiss-rules-on.json
REACT_APP_RULESURL_OFF=https://fishjar.github.io/kiss-rules/kiss-rules-off.json
REACT_APP_VERSIONFILE=https://fishjar.github.io/kiss-translator/version.txt
REACT_APP_VERSIONFILE2=https://kiss-translator.rayjar.com/version.txt
REACT_APP_USERSCRIPT_DOWNLOADURL=https://fishjar.github.io/kiss-translator/kiss-translator.user.js
REACT_APP_USERSCRIPT_DOWNLOADURL2=https://kiss-translator.rayjar.com/kiss-translator.user.js
REACT_APP_USERSCRIPT_IOS_DOWNLOADURL=https://fishjar.github.io/kiss-translator/kiss-translator-ios-safari.user.js
REACT_APP_USERSCRIPT_IOS_DOWNLOADURL2=https://kiss-translator.rayjar.com/kiss-translator-ios-safari.user.js

View File

@@ -1,15 +1,16 @@
{
"name": "kiss-translator",
"description": "A minimalist bilingual translation Extension & Greasemonkey Script",
"version": "1.8.5",
"version": "1.8.7",
"author": "Gabe<yugang2002@gmail.com>",
"private": true,
"dependencies": {
"@emotion/cache": "^11.11.0",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.9",
"@mui/material": "^5.14.10",
"@mui/icons-material": "^5.15.15",
"@mui/lab": "5.0.0-alpha.170",
"@mui/material": "^5.15.15",
"query-string": "^8.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",

369
pnpm-lock.yaml generated
View File

@@ -10,16 +10,19 @@ dependencies:
version: 11.11.0
'@emotion/react':
specifier: ^11.11.1
version: 11.11.1(@types/react@18.2.67)(react@18.2.0)
version: 11.11.1(@types/react@18.2.79)(react@18.2.0)
'@emotion/styled':
specifier: ^11.11.0
version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.67)(react@18.2.0)
version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.79)(react@18.2.0)
'@mui/icons-material':
specifier: ^5.14.9
version: 5.14.9(@mui/material@5.14.10)(@types/react@18.2.67)(react@18.2.0)
specifier: ^5.15.15
version: 5.15.15(@mui/material@5.15.15)(@types/react@18.2.79)(react@18.2.0)
'@mui/lab':
specifier: 5.0.0-alpha.170
version: 5.0.0-alpha.170(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@mui/material@5.15.15)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0)
'@mui/material':
specifier: ^5.14.10
version: 5.14.10(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.67)(react-dom@18.2.0)(react@18.2.0)
specifier: ^5.15.15
version: 5.15.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0)
query-string:
specifier: ^8.1.0
version: 8.1.0
@@ -31,13 +34,13 @@ dependencies:
version: 18.2.0(react@18.2.0)
react-markdown:
specifier: ^8.0.7
version: 8.0.7(@types/react@18.2.67)(react@18.2.0)
version: 8.0.7(@types/react@18.2.79)(react@18.2.0)
react-router-dom:
specifier: ^6.16.0
version: 6.16.0(react-dom@18.2.0)(react@18.2.0)
react-scripts:
specifier: 5.0.1
version: 5.0.1(@babel/plugin-syntax-flow@7.24.1)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.57.0)(react@18.2.0)(typescript@5.4.3)
version: 5.0.1(@babel/plugin-syntax-flow@7.24.1)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.57.0)(react@18.2.0)(typescript@5.4.5)
webdav:
specifier: ^5.3.0
version: 5.3.0
@@ -1434,6 +1437,12 @@ packages:
dependencies:
regenerator-runtime: 0.14.0
/@babel/runtime@7.24.4:
resolution: {integrity: sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.14.0
/@babel/template@7.22.15:
resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==}
engines: {node: '>=6.9.0'}
@@ -1665,7 +1674,7 @@ packages:
resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==}
dev: false
/@emotion/react@11.11.1(@types/react@18.2.67)(react@18.2.0):
/@emotion/react@11.11.1(@types/react@18.2.79)(react@18.2.0):
resolution: {integrity: sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==}
peerDependencies:
'@types/react': '*'
@@ -1681,7 +1690,7 @@ packages:
'@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0)
'@emotion/utils': 1.2.1
'@emotion/weak-memoize': 0.3.1
'@types/react': 18.2.67
'@types/react': 18.2.79
hoist-non-react-statics: 3.3.2
react: 18.2.0
dev: false
@@ -1700,7 +1709,7 @@ packages:
resolution: {integrity: sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==}
dev: false
/@emotion/styled@11.11.0(@emotion/react@11.11.1)(@types/react@18.2.67)(react@18.2.0):
/@emotion/styled@11.11.0(@emotion/react@11.11.1)(@types/react@18.2.79)(react@18.2.0):
resolution: {integrity: sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==}
peerDependencies:
'@emotion/react': ^11.0.0-rc.0
@@ -1713,11 +1722,11 @@ packages:
'@babel/runtime': 7.22.15
'@emotion/babel-plugin': 11.11.0
'@emotion/is-prop-valid': 1.2.1
'@emotion/react': 11.11.1(@types/react@18.2.67)(react@18.2.0)
'@emotion/react': 11.11.1(@types/react@18.2.79)(react@18.2.0)
'@emotion/serialize': 1.1.2
'@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0)
'@emotion/utils': 1.2.1
'@types/react': 18.2.67
'@types/react': 18.2.79
react: 18.2.0
dev: false
@@ -1778,39 +1787,39 @@ packages:
resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
/@floating-ui/core@1.5.0:
resolution: {integrity: sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==}
/@floating-ui/core@1.6.0:
resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==}
dependencies:
'@floating-ui/utils': 0.1.4
'@floating-ui/utils': 0.2.1
dev: false
/@floating-ui/dom@1.5.3:
resolution: {integrity: sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==}
/@floating-ui/dom@1.6.3:
resolution: {integrity: sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==}
dependencies:
'@floating-ui/core': 1.5.0
'@floating-ui/utils': 0.1.4
'@floating-ui/core': 1.6.0
'@floating-ui/utils': 0.2.1
dev: false
/@floating-ui/react-dom@2.0.2(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-5qhlDvjaLmAst/rKb3VdlCinwTF4EYMiVxuuc/HVUjs46W0zgtbMmAZ1UTsDrRTxRmUEzl92mOtWbeeXL26lSQ==}
/@floating-ui/react-dom@2.0.8(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
dependencies:
'@floating-ui/dom': 1.5.3
'@floating-ui/dom': 1.6.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@floating-ui/utils@0.1.4:
resolution: {integrity: sha512-qprfWkn82Iw821mcKofJ5Pk9wgioHicxcQMxx+5zt5GSKoqdWvgG5AxVmpmUUjzTLPVSH5auBrhI93Deayn/DA==}
/@floating-ui/utils@0.2.1:
resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==}
dev: false
/@humanwhocodes/config-array@0.11.14:
resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
engines: {node: '>=10.10.0'}
dependencies:
'@humanwhocodes/object-schema': 2.0.2
'@humanwhocodes/object-schema': 2.0.3
debug: 4.3.4
minimatch: 3.1.2
transitivePeerDependencies:
@@ -1820,8 +1829,8 @@ packages:
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
engines: {node: '>=12.22'}
/@humanwhocodes/object-schema@2.0.2:
resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
/@humanwhocodes/object-schema@2.0.3:
resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
/@istanbuljs/load-nyc-config@1.1.0:
resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
@@ -2088,8 +2097,8 @@ packages:
/@leichtgewicht/ip-codec@2.0.4:
resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==}
/@mui/base@5.0.0-beta.16(@types/react@18.2.67)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-OYxhC81c9bO0wobGcM8rrY5bRwpCXAI21BL0P2wz/2vTv4ek7ALz9+U5M8wgdmtRNUhmCmAB4L2WRwFRf5Cd8Q==}
/@mui/base@5.0.0-beta.40(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@types/react': ^17.0.0 || ^18.0.0
@@ -2099,24 +2108,24 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.15
'@floating-ui/react-dom': 2.0.2(react-dom@18.2.0)(react@18.2.0)
'@mui/types': 7.2.4(@types/react@18.2.67)
'@mui/utils': 5.14.10(@types/react@18.2.67)(react@18.2.0)
'@babel/runtime': 7.24.4
'@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0)
'@mui/types': 7.2.14(@types/react@18.2.79)
'@mui/utils': 5.15.14(@types/react@18.2.79)(react@18.2.0)
'@popperjs/core': 2.11.8
'@types/react': 18.2.67
clsx: 2.0.0
'@types/react': 18.2.79
clsx: 2.1.0
prop-types: 15.8.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@mui/core-downloads-tracker@5.14.10:
resolution: {integrity: sha512-kPHu/NhZq1k+vSZR5wq3AyUfD4bnfWAeuKpps0+8PS7ZHQ2Lyv1cXJh+PlFdCIOa0PK98rk3JPwMzS8BMhdHwQ==}
/@mui/core-downloads-tracker@5.15.15:
resolution: {integrity: sha512-aXnw29OWQ6I5A47iuWEI6qSSUfH6G/aCsW9KmW3LiFqr7uXZBK4Ks+z8G+qeIub8k0T5CMqlT2q0L+ZJTMrqpg==}
dev: false
/@mui/icons-material@5.14.9(@mui/material@5.14.10)(@types/react@18.2.67)(react@18.2.0):
resolution: {integrity: sha512-xTRQbDsogsJo7tY5Og8R9zbuG2q+KIPVIM6JQoKxtJlz9DPOw1u0T2fGrvwD+XAOVifQf6epNMcGCDLfJAz4Nw==}
/@mui/icons-material@5.15.15(@mui/material@5.15.15)(@types/react@18.2.79)(react@18.2.0):
resolution: {integrity: sha512-kkeU/pe+hABcYDH6Uqy8RmIsr2S/y5bP2rp+Gat4CcRjCcVne6KudS1NrZQhUCRysrTDCAhcbcf9gt+/+pGO2g==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@mui/material': ^5.0.0
@@ -2126,14 +2135,47 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.15
'@mui/material': 5.14.10(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.67)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.67
'@babel/runtime': 7.24.4
'@mui/material': 5.15.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.79
react: 18.2.0
dev: false
/@mui/material@5.14.10(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.67)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-ejFMppnO+lzBXpzju+N4SSz0Mhmi5sihXUGcr5FxpgB6bfUP0Lpe32O0Sw/3s8xlmLEvG1fqVT0rRyAVMlCA+A==}
/@mui/lab@5.0.0-alpha.170(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@mui/material@5.15.15)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-0bDVECGmrNjd3+bLdcLiwYZ0O4HP5j5WSQm5DV6iA/Z9kr8O6AnvZ1bv9ImQbbX7Gj3pX4o43EKwCutj3EQxQg==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@emotion/react': ^11.5.0
'@emotion/styled': ^11.3.0
'@mui/material': '>=5.15.0'
'@types/react': ^17.0.0 || ^18.0.0
react: ^17.0.0 || ^18.0.0
react-dom: ^17.0.0 || ^18.0.0
peerDependenciesMeta:
'@emotion/react':
optional: true
'@emotion/styled':
optional: true
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.24.4
'@emotion/react': 11.11.1(@types/react@18.2.79)(react@18.2.0)
'@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.79)(react@18.2.0)
'@mui/base': 5.0.0-beta.40(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0)
'@mui/material': 5.15.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0)
'@mui/system': 5.15.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.79)(react@18.2.0)
'@mui/types': 7.2.14(@types/react@18.2.79)
'@mui/utils': 5.15.14(@types/react@18.2.79)(react@18.2.0)
'@types/react': 18.2.79
clsx: 2.1.0
prop-types: 15.8.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@mui/material@5.15.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-3zvWayJ+E1kzoIsvwyEvkTUKVKt1AjchFFns+JtluHCuvxgKcLSRJTADw37k0doaRtVAsyh8bz9Afqzv+KYrIA==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@emotion/react': ^11.5.0
@@ -2149,18 +2191,18 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.15
'@emotion/react': 11.11.1(@types/react@18.2.67)(react@18.2.0)
'@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.67)(react@18.2.0)
'@mui/base': 5.0.0-beta.16(@types/react@18.2.67)(react-dom@18.2.0)(react@18.2.0)
'@mui/core-downloads-tracker': 5.14.10
'@mui/system': 5.14.10(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.67)(react@18.2.0)
'@mui/types': 7.2.4(@types/react@18.2.67)
'@mui/utils': 5.14.10(@types/react@18.2.67)(react@18.2.0)
'@types/react': 18.2.67
'@types/react-transition-group': 4.4.6
clsx: 2.0.0
csstype: 3.1.2
'@babel/runtime': 7.24.4
'@emotion/react': 11.11.1(@types/react@18.2.79)(react@18.2.0)
'@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.79)(react@18.2.0)
'@mui/base': 5.0.0-beta.40(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0)
'@mui/core-downloads-tracker': 5.15.15
'@mui/system': 5.15.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.79)(react@18.2.0)
'@mui/types': 7.2.14(@types/react@18.2.79)
'@mui/utils': 5.15.14(@types/react@18.2.79)(react@18.2.0)
'@types/react': 18.2.79
'@types/react-transition-group': 4.4.10
clsx: 2.1.0
csstype: 3.1.3
prop-types: 15.8.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
@@ -2168,8 +2210,8 @@ packages:
react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0)
dev: false
/@mui/private-theming@5.14.10(@types/react@18.2.67)(react@18.2.0):
resolution: {integrity: sha512-f67xOj3H06wWDT9xBg7hVL/HSKNF+HG1Kx0Pm23skkbEqD2Ef2Lif64e5nPdmWVv+7cISCYtSuE2aeuzrZe78w==}
/@mui/private-theming@5.15.14(@types/react@18.2.79)(react@18.2.0):
resolution: {integrity: sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@types/react': ^17.0.0 || ^18.0.0
@@ -2178,15 +2220,15 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.15
'@mui/utils': 5.14.10(@types/react@18.2.67)(react@18.2.0)
'@types/react': 18.2.67
'@babel/runtime': 7.24.4
'@mui/utils': 5.15.14(@types/react@18.2.79)(react@18.2.0)
'@types/react': 18.2.79
prop-types: 15.8.1
react: 18.2.0
dev: false
/@mui/styled-engine@5.14.10(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0):
resolution: {integrity: sha512-EJckxmQHrsBvDbFu1trJkvjNw/1R7jfNarnqPSnL+jEQawCkQIqVELWLrlOa611TFtxSJGkdUfCFXeJC203HVg==}
/@mui/styled-engine@5.15.14(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0):
resolution: {integrity: sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@emotion/react': ^11.4.1
@@ -2198,17 +2240,17 @@ packages:
'@emotion/styled':
optional: true
dependencies:
'@babel/runtime': 7.22.15
'@babel/runtime': 7.24.4
'@emotion/cache': 11.11.0
'@emotion/react': 11.11.1(@types/react@18.2.67)(react@18.2.0)
'@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.67)(react@18.2.0)
csstype: 3.1.2
'@emotion/react': 11.11.1(@types/react@18.2.79)(react@18.2.0)
'@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.79)(react@18.2.0)
csstype: 3.1.3
prop-types: 15.8.1
react: 18.2.0
dev: false
/@mui/system@5.14.10(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.67)(react@18.2.0):
resolution: {integrity: sha512-QQmtTG/R4gjmLiL5ECQ7kRxLKDm8aKKD7seGZfbINtRVJDyFhKChA1a+K2bfqIAaBo1EMDv+6FWNT1Q5cRKjFA==}
/@mui/system@5.15.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.79)(react@18.2.0):
resolution: {integrity: sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@emotion/react': ^11.5.0
@@ -2223,33 +2265,33 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.15
'@emotion/react': 11.11.1(@types/react@18.2.67)(react@18.2.0)
'@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.67)(react@18.2.0)
'@mui/private-theming': 5.14.10(@types/react@18.2.67)(react@18.2.0)
'@mui/styled-engine': 5.14.10(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0)
'@mui/types': 7.2.4(@types/react@18.2.67)
'@mui/utils': 5.14.10(@types/react@18.2.67)(react@18.2.0)
'@types/react': 18.2.67
clsx: 2.0.0
csstype: 3.1.2
'@babel/runtime': 7.24.4
'@emotion/react': 11.11.1(@types/react@18.2.79)(react@18.2.0)
'@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.79)(react@18.2.0)
'@mui/private-theming': 5.15.14(@types/react@18.2.79)(react@18.2.0)
'@mui/styled-engine': 5.15.14(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0)
'@mui/types': 7.2.14(@types/react@18.2.79)
'@mui/utils': 5.15.14(@types/react@18.2.79)(react@18.2.0)
'@types/react': 18.2.79
clsx: 2.1.0
csstype: 3.1.3
prop-types: 15.8.1
react: 18.2.0
dev: false
/@mui/types@7.2.4(@types/react@18.2.67):
resolution: {integrity: sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA==}
/@mui/types@7.2.14(@types/react@18.2.79):
resolution: {integrity: sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==}
peerDependencies:
'@types/react': '*'
'@types/react': ^17.0.0 || ^18.0.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@types/react': 18.2.67
'@types/react': 18.2.79
dev: false
/@mui/utils@5.14.10(@types/react@18.2.67)(react@18.2.0):
resolution: {integrity: sha512-Rn+vYQX7FxkcW0riDX/clNUwKuOJFH45HiULxwmpgnzQoQr3A0lb+QYwaZ+FAkZrR7qLoHKmLQlcItu6LT0y/Q==}
/@mui/utils@5.15.14(@types/react@18.2.79)(react@18.2.0):
resolution: {integrity: sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@types/react': ^17.0.0 || ^18.0.0
@@ -2258,9 +2300,9 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.15
'@types/prop-types': 15.7.6
'@types/react': 18.2.67
'@babel/runtime': 7.24.4
'@types/prop-types': 15.7.11
'@types/react': 18.2.79
prop-types: 15.8.1
react: 18.2.0
react-is: 18.2.0
@@ -2671,6 +2713,10 @@ packages:
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
dev: false
/@types/prop-types@15.7.12:
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
dev: false
/@types/prop-types@15.7.6:
resolution: {integrity: sha512-RK/kBbYOQQHLYj9Z95eh7S6t7gq4Ojt/NT8HTk8bWVhA5DaF+5SMnxHKkP4gPNN3wAZkKP+VjAf0ebtYzf+fxg==}
dev: false
@@ -2684,25 +2730,16 @@ packages:
/@types/range-parser@1.2.4:
resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==}
/@types/react-transition-group@4.4.6:
resolution: {integrity: sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==}
/@types/react-transition-group@4.4.10:
resolution: {integrity: sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==}
dependencies:
'@types/react': 18.2.22
'@types/react': 18.2.79
dev: false
/@types/react@18.2.22:
resolution: {integrity: sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==}
/@types/react@18.2.79:
resolution: {integrity: sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==}
dependencies:
'@types/prop-types': 15.7.6
'@types/scheduler': 0.16.3
csstype: 3.1.2
dev: false
/@types/react@18.2.67:
resolution: {integrity: sha512-vkIE2vTIMHQ/xL0rgmuoECBCkZFZeHr49HeWSc24AptMbNRo7pwSBvj73rlJJs9fGKj0koS+V7kQB1jHS0uCgw==}
dependencies:
'@types/prop-types': 15.7.11
'@types/scheduler': 0.16.8
'@types/prop-types': 15.7.12
csstype: 3.1.3
dev: false
@@ -2714,14 +2751,6 @@ packages:
/@types/retry@0.12.0:
resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==}
/@types/scheduler@0.16.3:
resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==}
dev: false
/@types/scheduler@0.16.8:
resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==}
dev: false
/@types/semver@7.5.2:
resolution: {integrity: sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==}
@@ -2776,7 +2805,7 @@ packages:
dependencies:
'@types/yargs-parser': 21.0.0
/@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.3):
/@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.5):
resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -2788,34 +2817,34 @@ packages:
optional: true
dependencies:
'@eslint-community/regexpp': 4.8.1
'@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.3)
'@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5)
'@typescript-eslint/scope-manager': 5.62.0
'@typescript-eslint/type-utils': 5.62.0(eslint@8.57.0)(typescript@5.4.3)
'@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.3)
'@typescript-eslint/type-utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5)
'@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5)
debug: 4.3.4
eslint: 8.57.0
graphemer: 1.4.0
ignore: 5.2.4
natural-compare-lite: 1.4.0
semver: 7.5.4
tsutils: 3.21.0(typescript@5.4.3)
typescript: 5.4.3
tsutils: 3.21.0(typescript@5.4.5)
typescript: 5.4.5
transitivePeerDependencies:
- supports-color
/@typescript-eslint/experimental-utils@5.62.0(eslint@8.57.0)(typescript@5.4.3):
/@typescript-eslint/experimental-utils@5.62.0(eslint@8.57.0)(typescript@5.4.5):
resolution: {integrity: sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
'@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.3)
'@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5)
eslint: 8.57.0
transitivePeerDependencies:
- supports-color
- typescript
/@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.3):
/@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5):
resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -2827,10 +2856,10 @@ packages:
dependencies:
'@typescript-eslint/scope-manager': 5.62.0
'@typescript-eslint/types': 5.62.0
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.3)
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5)
debug: 4.3.4
eslint: 8.57.0
typescript: 5.4.3
typescript: 5.4.5
transitivePeerDependencies:
- supports-color
@@ -2841,7 +2870,7 @@ packages:
'@typescript-eslint/types': 5.62.0
'@typescript-eslint/visitor-keys': 5.62.0
/@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.4.3):
/@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.4.5):
resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -2851,12 +2880,12 @@ packages:
typescript:
optional: true
dependencies:
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.3)
'@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.3)
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5)
'@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5)
debug: 4.3.4
eslint: 8.57.0
tsutils: 3.21.0(typescript@5.4.3)
typescript: 5.4.3
tsutils: 3.21.0(typescript@5.4.5)
typescript: 5.4.5
transitivePeerDependencies:
- supports-color
@@ -2864,7 +2893,7 @@ packages:
resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
/@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.3):
/@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.5):
resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -2879,12 +2908,12 @@ packages:
globby: 11.1.0
is-glob: 4.0.3
semver: 7.5.4
tsutils: 3.21.0(typescript@5.4.3)
typescript: 5.4.3
tsutils: 3.21.0(typescript@5.4.5)
typescript: 5.4.5
transitivePeerDependencies:
- supports-color
/@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.4.3):
/@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.4.5):
resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -2895,7 +2924,7 @@ packages:
'@types/semver': 7.5.2
'@typescript-eslint/scope-manager': 5.62.0
'@typescript-eslint/types': 5.62.0
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.3)
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5)
eslint: 8.57.0
eslint-scope: 5.1.1
semver: 7.5.4
@@ -3464,7 +3493,7 @@ packages:
'@babel/preset-env': 7.22.20(@babel/core@7.22.20)
'@babel/preset-react': 7.22.15(@babel/core@7.22.20)
'@babel/preset-typescript': 7.22.15(@babel/core@7.22.20)
'@babel/runtime': 7.22.15
'@babel/runtime': 7.24.4
babel-plugin-macros: 3.1.0
babel-plugin-transform-react-remove-prop-types: 0.4.24
transitivePeerDependencies:
@@ -3712,8 +3741,8 @@ packages:
shallow-clone: 3.0.1
dev: true
/clsx@2.0.0:
resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==}
/clsx@2.1.0:
resolution: {integrity: sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==}
engines: {node: '>=6'}
dev: false
@@ -4290,8 +4319,8 @@ packages:
/dom-helpers@5.2.1:
resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
dependencies:
'@babel/runtime': 7.22.15
csstype: 3.1.2
'@babel/runtime': 7.24.4
csstype: 3.1.3
dev: false
/dom-serializer@0.2.2:
@@ -4544,7 +4573,7 @@ packages:
optionalDependencies:
source-map: 0.6.1
/eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.24.1)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.57.0)(jest@27.5.1)(typescript@5.4.3):
/eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.24.1)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.57.0)(jest@27.5.1)(typescript@5.4.5):
resolution: {integrity: sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==}
engines: {node: '>=14.0.0'}
peerDependencies:
@@ -4557,19 +4586,19 @@ packages:
'@babel/core': 7.22.20
'@babel/eslint-parser': 7.22.15(@babel/core@7.22.20)(eslint@8.57.0)
'@rushstack/eslint-patch': 1.4.0
'@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.3)
'@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.3)
'@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.5)
'@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5)
babel-preset-react-app: 10.0.1
confusing-browser-globals: 1.0.11
eslint: 8.57.0
eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.24.1)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.57.0)
eslint-plugin-import: 2.28.1(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)
eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint@8.57.0)(jest@27.5.1)(typescript@5.4.3)
eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint@8.57.0)(jest@27.5.1)(typescript@5.4.5)
eslint-plugin-jsx-a11y: 6.7.1(eslint@8.57.0)
eslint-plugin-react: 7.33.2(eslint@8.57.0)
eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0)
eslint-plugin-testing-library: 5.11.1(eslint@8.57.0)(typescript@5.4.3)
typescript: 5.4.3
eslint-plugin-testing-library: 5.11.1(eslint@8.57.0)(typescript@5.4.5)
typescript: 5.4.5
transitivePeerDependencies:
- '@babel/plugin-syntax-flow'
- '@babel/plugin-transform-react-jsx'
@@ -4608,7 +4637,7 @@ packages:
eslint-import-resolver-webpack:
optional: true
dependencies:
'@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.3)
'@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5)
debug: 3.2.7
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
@@ -4639,7 +4668,7 @@ packages:
'@typescript-eslint/parser':
optional: true
dependencies:
'@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.3)
'@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5)
array-includes: 3.1.7
array.prototype.findlastindex: 1.2.3
array.prototype.flat: 1.3.2
@@ -4663,7 +4692,7 @@ packages:
- eslint-import-resolver-webpack
- supports-color
/eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint@8.57.0)(jest@27.5.1)(typescript@5.4.3):
/eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint@8.57.0)(jest@27.5.1)(typescript@5.4.5):
resolution: {integrity: sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
peerDependencies:
@@ -4676,8 +4705,8 @@ packages:
jest:
optional: true
dependencies:
'@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.3)
'@typescript-eslint/experimental-utils': 5.62.0(eslint@8.57.0)(typescript@5.4.3)
'@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.5)
'@typescript-eslint/experimental-utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5)
eslint: 8.57.0
jest: 27.5.1
transitivePeerDependencies:
@@ -4690,7 +4719,7 @@ packages:
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
dependencies:
'@babel/runtime': 7.22.15
'@babel/runtime': 7.24.4
aria-query: 5.3.0
array-includes: 3.1.7
array.prototype.flatmap: 1.3.2
@@ -4740,13 +4769,13 @@ packages:
semver: 6.3.1
string.prototype.matchall: 4.0.10
/eslint-plugin-testing-library@5.11.1(eslint@8.57.0)(typescript@5.4.3):
/eslint-plugin-testing-library@5.11.1(eslint@8.57.0)(typescript@5.4.5):
resolution: {integrity: sha512-5eX9e1Kc2PqVRed3taaLnAAqPZGEX75C+M/rXzUAI3wIg/ZxzUm1OVAwfe/O+vE+6YXOLetSe9g5GKD2ecXipw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6'}
peerDependencies:
eslint: ^7.5.0 || ^8.0.0
dependencies:
'@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.3)
'@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5)
eslint: 8.57.0
transitivePeerDependencies:
- supports-color
@@ -5128,7 +5157,7 @@ packages:
dependencies:
is-callable: 1.2.7
/fork-ts-checker-webpack-plugin@6.5.3(eslint@8.57.0)(typescript@5.4.3)(webpack@5.88.2):
/fork-ts-checker-webpack-plugin@6.5.3(eslint@8.57.0)(typescript@5.4.5)(webpack@5.88.2):
resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==}
engines: {node: '>=10', yarn: '>=1.0.0'}
peerDependencies:
@@ -5156,7 +5185,7 @@ packages:
schema-utils: 2.7.0
semver: 7.5.4
tapable: 1.1.3
typescript: 5.4.3
typescript: 5.4.5
webpack: 5.88.2
/form-data@3.0.1:
@@ -8313,11 +8342,11 @@ packages:
peerDependencies:
react-scripts: '>=2.1.3'
dependencies:
react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.24.1)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.57.0)(react@18.2.0)(typescript@5.4.3)
react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.24.1)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.57.0)(react@18.2.0)(typescript@5.4.5)
semver: 5.7.2
dev: true
/react-dev-utils@12.0.1(eslint@8.57.0)(typescript@5.4.3)(webpack@5.88.2):
/react-dev-utils@12.0.1(eslint@8.57.0)(typescript@5.4.5)(webpack@5.88.2):
resolution: {integrity: sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==}
engines: {node: '>=14'}
peerDependencies:
@@ -8336,7 +8365,7 @@ packages:
escape-string-regexp: 4.0.0
filesize: 8.0.7
find-up: 5.0.0
fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.57.0)(typescript@5.4.3)(webpack@5.88.2)
fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.57.0)(typescript@5.4.5)(webpack@5.88.2)
global-modules: 2.0.0
globby: 11.1.0
gzip-size: 6.0.0
@@ -8351,7 +8380,7 @@ packages:
shell-quote: 1.8.1
strip-ansi: 6.0.1
text-table: 0.2.0
typescript: 5.4.3
typescript: 5.4.5
webpack: 5.88.2
transitivePeerDependencies:
- eslint
@@ -8380,7 +8409,7 @@ packages:
/react-is@18.2.0:
resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
/react-markdown@8.0.7(@types/react@18.2.67)(react@18.2.0):
/react-markdown@8.0.7(@types/react@18.2.79)(react@18.2.0):
resolution: {integrity: sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==}
peerDependencies:
'@types/react': '>=16'
@@ -8388,7 +8417,7 @@ packages:
dependencies:
'@types/hast': 2.3.6
'@types/prop-types': 15.7.6
'@types/react': 18.2.67
'@types/react': 18.2.79
'@types/unist': 2.0.8
comma-separated-tokens: 2.0.3
hast-util-whitespace: 2.0.1
@@ -8434,7 +8463,7 @@ packages:
react: 18.2.0
dev: false
/react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.1)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.57.0)(react@18.2.0)(typescript@5.4.3):
/react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.1)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.57.0)(react@18.2.0)(typescript@5.4.5):
resolution: {integrity: sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==}
engines: {node: '>=14.0.0'}
hasBin: true
@@ -8462,7 +8491,7 @@ packages:
dotenv: 10.0.0
dotenv-expand: 5.1.0
eslint: 8.57.0
eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.24.1)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.57.0)(jest@27.5.1)(typescript@5.4.3)
eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.24.1)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.57.0)(jest@27.5.1)(typescript@5.4.5)
eslint-webpack-plugin: 3.2.0(eslint@8.57.0)(webpack@5.88.2)
file-loader: 6.2.0(webpack@5.88.2)
fs-extra: 10.1.0
@@ -8480,7 +8509,7 @@ packages:
prompts: 2.4.2
react: 18.2.0
react-app-polyfill: 3.0.0
react-dev-utils: 12.0.1(eslint@8.57.0)(typescript@5.4.3)(webpack@5.88.2)
react-dev-utils: 12.0.1(eslint@8.57.0)(typescript@5.4.5)(webpack@5.88.2)
react-refresh: 0.11.0
resolve: 1.22.6
resolve-url-loader: 4.0.0
@@ -8490,7 +8519,7 @@ packages:
style-loader: 3.3.3(webpack@5.88.2)
tailwindcss: 3.3.3
terser-webpack-plugin: 5.3.9(webpack@5.88.2)
typescript: 5.4.3
typescript: 5.4.5
webpack: 5.88.2
webpack-dev-server: 4.15.1(webpack@5.88.2)
webpack-manifest-plugin: 4.1.1(webpack@5.88.2)
@@ -8536,7 +8565,7 @@ packages:
react: '>=16.6.0'
react-dom: '>=16.6.0'
dependencies:
'@babel/runtime': 7.22.15
'@babel/runtime': 7.24.4
dom-helpers: 5.2.1
loose-envify: 1.4.0
prop-types: 15.8.1
@@ -9542,14 +9571,14 @@ packages:
/tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
/tsutils@3.21.0(typescript@5.4.3):
/tsutils@3.21.0(typescript@5.4.5):
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
engines: {node: '>= 6'}
peerDependencies:
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
dependencies:
tslib: 1.14.1
typescript: 5.4.3
typescript: 5.4.5
/type-check@0.3.2:
resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==}
@@ -9625,8 +9654,8 @@ packages:
dependencies:
is-typedarray: 1.0.0
/typescript@5.4.3:
resolution: {integrity: sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==}
/typescript@5.4.5:
resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==}
engines: {node: '>=14.17'}
hasBin: true
@@ -10144,7 +10173,7 @@ packages:
'@apideck/better-ajv-errors': 0.3.6(ajv@8.12.0)
'@babel/core': 7.22.20
'@babel/preset-env': 7.22.20(@babel/core@7.22.20)
'@babel/runtime': 7.22.15
'@babel/runtime': 7.24.4
'@rollup/plugin-babel': 5.3.1(@babel/core@7.22.20)(rollup@2.79.1)
'@rollup/plugin-node-resolve': 11.2.1(rollup@2.79.1)
'@rollup/plugin-replace': 2.4.2(rollup@2.79.1)

View File

@@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "__MSG_app_name__",
"description": "__MSG_app_description__",
"version": "1.8.5",
"version": "1.8.7",
"default_locale": "en",
"author": "Gabe<yugang2002@gmail.com>",
"homepage_url": "https://github.com/fishjar/kiss-translator",

View File

@@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "__MSG_app_name__",
"description": "__MSG_app_description__",
"version": "1.8.5",
"version": "1.8.7",
"default_locale": "en",
"author": "Gabe<yugang2002@gmail.com>",
"homepage_url": "https://github.com/fishjar/kiss-translator",

View File

@@ -4,6 +4,7 @@ import {
URL_BAIDU_WEB,
URL_BAIDU_TRANSAPI_V2,
URL_BAIDU_TRANSAPI,
DEFAULT_USER_AGENT,
} from "../config";
import { fetchApi } from "../libs/fetch";
@@ -248,7 +249,9 @@ export const genBaidu = async ({ text, from, to }) => {
const init = {
headers: {
// Origin: "https://fanyi.baidu.com",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"User-Agent": DEFAULT_USER_AGENT,
},
method: "POST",
body: queryString.stringify(data),

View File

@@ -6,12 +6,17 @@ import {
OPT_TRANS_DEEPL,
OPT_TRANS_DEEPLFREE,
OPT_TRANS_DEEPLX,
OPT_TRANS_NIUTRANS,
OPT_TRANS_BAIDU,
OPT_TRANS_TENCENT,
OPT_TRANS_OPENAI,
OPT_TRANS_GEMINI,
OPT_TRANS_CLOUDFLAREAI,
OPT_TRANS_CUSTOMIZE,
OPT_TRANS_CUSTOMIZE_2,
OPT_TRANS_CUSTOMIZE_3,
OPT_TRANS_CUSTOMIZE_4,
OPT_TRANS_CUSTOMIZE_5,
URL_CACHE_TRAN,
KV_SALT_SYNC,
URL_BAIDU_LANGDETECT,
@@ -215,6 +220,14 @@ export const apiTranslate = async ({
trText = res.data;
isSame = to === res.source_lang;
break;
case OPT_TRANS_NIUTRANS:
const json = JSON.parse(res);
if (json.error_msg) {
throw new Error(json.error_msg);
}
trText = json.tgt_text;
isSame = to === json.from;
break;
case OPT_TRANS_BAIDU:
// trText = res.trans_result?.data.map((item) => item.dst).join(" ");
// isSame = res.trans_result?.to === res.trans_result?.from;
@@ -245,8 +258,31 @@ export const apiTranslate = async ({
isSame = text === trText;
break;
case OPT_TRANS_CUSTOMIZE:
case OPT_TRANS_CUSTOMIZE_2:
case OPT_TRANS_CUSTOMIZE_3:
case OPT_TRANS_CUSTOMIZE_4:
case OPT_TRANS_CUSTOMIZE_5:
trText = res.text;
isSame = to === res.from;
const { customOption } = apiSetting;
if (customOption?.trim()) {
try {
const opt = JSON.parse(customOption);
const textPattern = opt.resPattern?.text;
const fromPattern = opt.resPattern?.from;
if (textPattern) {
trText = textPattern.split(".").reduce((pre, cur) => pre[cur], res);
}
if (fromPattern) {
isSame =
to === fromPattern.split(".").reduce((pre, cur) => pre[cur], res);
}
} catch (err) {
throw new Error(`custom option parse err: ${err}`);
}
}
break;
default:
}

View File

@@ -139,6 +139,7 @@ function showTransbox({
contextMenuType,
tranboxSetting = DEFAULT_TRANBOX_SETTING,
transApis,
darkMode,
}) {
if (!tranboxSetting?.transOpen) {
return;
@@ -153,6 +154,8 @@ function showTransbox({
const shadowContainer = $tranbox.attachShadow({ mode: "closed" });
const emotionRoot = document.createElement("style");
const shadowRootElement = document.createElement("div");
shadowRootElement.classList.add(`KT-transbox`);
shadowRootElement.classList.add(`KT-transbox_${darkMode ? "dark" : "light"}`);
shadowContainer.appendChild(emotionRoot);
shadowContainer.appendChild(shadowRootElement);
const cache = createCache({
@@ -213,8 +216,7 @@ export async function run(isUserscript = false) {
if (
isUserscript &&
(href.includes(process.env.REACT_APP_OPTIONSPAGE_DEV) ||
href.includes(process.env.REACT_APP_OPTIONSPAGE) ||
href.includes(process.env.REACT_APP_OPTIONSPAGE2))
href.includes(process.env.REACT_APP_OPTIONSPAGE))
) {
runSettingPage();
return;

View File

@@ -42,24 +42,28 @@ const customApiLangs = `["en", "English - English"],
["vi", "Vietnamese - Tiếng Việt"],
`;
const customApiHelpZH = `/// 自定义翻译源接口说明
// 请求Request数据将按下面规范发送
{
url: {{YOUR_URL}},
method: "POST",
headers: {
const customDefaultOption = `{
"url": "{{url}}",
"method": "POST",
"headers": {
"Content-type": "application/json",
"Authorization": "Bearer {{YOUR_KEY}}",
"Authorization": "Bearer {{key}}"
},
body: {
text: "", // 需要翻译的文字
from: "", // 源语言,可能为空,表示需要接口自动识别语言
to: "", // 目标语言
"body": {
"text": "{{text}}",
"from": "{{from}}",
"to": "{{to}}"
},
"resPattern": {
"text": "text",
"from": "from"
}
}
}`;
// 返回Response数据需符合下面的JSON规范
const customApiHelpZH = `// 自定义选项范例
${customDefaultOption}
// 返回数据默认格式
{
text: "", // 翻译后的文字
from: "", // 识别的源语言
@@ -70,24 +74,10 @@ const customApiHelpZH = `/// 自定义翻译源接口说明
${customApiLangs}
`;
const customApiHelpEN = `/// Custom translation source interface description
const customApiHelpEN = `// Example of custom options
${customDefaultOption}
// Request data will be sent according to the following specifications
{
url: {{YOUR_URL}},
method: "POST",
headers: {
"Content-type": "application/json",
"Authorization": "Bearer {{YOUR_KEY}}",
},
body: {
text: "", // text to be translated
from: "", // Source language, may be empty
to: "", // Target language
}
}
// The returned data must conform to the following JSON specification
// Return data default format
{
text: "", // translated text
from: "", // Recognized source language
@@ -463,6 +453,10 @@ export const I18N = {
zh: `导出`,
en: `Export`,
},
export_translation: {
zh: `导出释义`,
en: `Export Translation`,
},
error_cant_be_blank: {
zh: `不能为空`,
en: `Can not be blank`,
@@ -627,6 +621,10 @@ export const I18N = {
zh: `点击外部关闭弹窗`,
en: `Click outside to close the pop-up window`,
},
use_simple_style: {
zh: `使用简洁界面`,
en: `Click outside to close the pop-up window`,
},
show: {
zh: `显示`,
en: `Show`,
@@ -819,4 +817,32 @@ export const I18N = {
zh: `网页修复选择器`,
en: `Fixer Selector`,
},
reg_niutrans: {
zh: `获取小牛翻译密钥`,
en: `Get NiuTrans APIKey`,
},
trigger_mode: {
zh: `触发方式`,
en: `Trigger Mode`,
},
trigger_click: {
zh: `点击触发`,
en: `Click Trigger`,
},
trigger_hover: {
zh: `鼠标悬停触发`,
en: `Hover Trigger`,
},
trigger_select: {
zh: `选中触发`,
en: `Select Trigger`,
},
extend_styles: {
zh: `附加样式`,
en: `Extend Styles`,
},
custom_option: {
zh: `自定义选项`,
en: `Custom Option`,
},
};

View File

@@ -90,12 +90,18 @@ export const URL_BAIDU_TRANSAPI = "https://fanyi.baidu.com/transapi";
export const URL_BAIDU_TRANSAPI_V2 = "https://fanyi.baidu.com/v2transapi";
export const URL_DEEPLFREE_TRAN = "https://www2.deepl.com/jsonrpc";
export const URL_TENCENT_TRANSMART = "https://transmart.qq.com/api/imt";
export const URL_NIUTRANS_REG =
"https://niutrans.com/login?active=3&userSource=kiss-translator";
export const DEFAULT_USER_AGENT =
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36";
export const OPT_TRANS_GOOGLE = "Google";
export const OPT_TRANS_MICROSOFT = "Microsoft";
export const OPT_TRANS_DEEPL = "DeepL";
export const OPT_TRANS_DEEPLX = "DeepLX";
export const OPT_TRANS_DEEPLFREE = "DeepLFree";
export const OPT_TRANS_NIUTRANS = "NiuTrans";
export const OPT_TRANS_BAIDU = "Baidu";
export const OPT_TRANS_TENCENT = "Tencent";
export const OPT_TRANS_OPENAI = "OpenAI";
@@ -114,6 +120,7 @@ export const OPT_TRANS_ALL = [
OPT_TRANS_DEEPL,
OPT_TRANS_DEEPLFREE,
OPT_TRANS_DEEPLX,
OPT_TRANS_NIUTRANS,
OPT_TRANS_OPENAI,
OPT_TRANS_GEMINI,
OPT_TRANS_CLOUDFLAREAI,
@@ -190,6 +197,12 @@ export const OPT_LANGS_SPECIAL = {
["zh-CN", "ZH"],
["zh-TW", "ZH"],
]),
[OPT_TRANS_NIUTRANS]: new Map([
...OPT_LANGS_FROM.map(([key]) => [key, key]),
["auto", "auto"],
["zh-CN", "zh"],
["zh-TW", "cht"],
]),
[OPT_TRANS_BAIDU]: new Map([
...OPT_LANGS_FROM.map(([key]) => [key, key]),
["zh-CN", "zh"],
@@ -263,6 +276,22 @@ export const OPT_LANGS_SPECIAL = {
...OPT_LANGS_FROM.map(([key]) => [key, key]),
["auto", ""],
]),
[OPT_TRANS_CUSTOMIZE_2]: new Map([
...OPT_LANGS_FROM.map(([key]) => [key, key]),
["auto", ""],
]),
[OPT_TRANS_CUSTOMIZE_3]: new Map([
...OPT_LANGS_FROM.map(([key]) => [key, key]),
["auto", ""],
]),
[OPT_TRANS_CUSTOMIZE_4]: new Map([
...OPT_LANGS_FROM.map(([key]) => [key, key]),
["auto", ""],
]),
[OPT_TRANS_CUSTOMIZE_5]: new Map([
...OPT_LANGS_FROM.map(([key]) => [key, key]),
["auto", ""],
]),
};
export const OPT_LANGS_LIST = OPT_LANGS_TO.map(([lang]) => lang);
export const OPT_LANGS_BAIDU = new Map(
@@ -326,9 +355,11 @@ export const OPT_TIMING_ALL = [
export const DEFAULT_FETCH_LIMIT = 10; // 默认最大任务数量
export const DEFAULT_FETCH_INTERVAL = 100; // 默认任务间隔时间
export const PROMPT_PLACE_FROM = "{{from}}"; // 占位符
export const PROMPT_PLACE_TO = "{{to}}"; // 占位符
export const PROMPT_PLACE_TEXT = "{{text}}"; // 占位符
export const INPUT_PLACE_URL = "{{url}}"; // 占位符
export const INPUT_PLACE_FROM = "{{from}}"; // 占位符
export const INPUT_PLACE_TO = "{{to}}"; // 占位符
export const INPUT_PLACE_TEXT = "{{text}}"; // 占位符
export const INPUT_PLACE_KEY = "{{key}}"; // 占位符
export const DEFAULT_COLOR = "#209CEE"; // 默认高亮背景色/线条颜色
@@ -378,6 +409,18 @@ export const DEFAULT_INPUT_RULE = {
};
// 划词翻译
export const PHONIC_MAP = {
en_phonic: ["英", "uk"],
us_phonic: ["美", "en"],
};
export const OPT_TRANBOX_TRIGGER_CLICK = "click";
export const OPT_TRANBOX_TRIGGER_HOVER = "hover";
export const OPT_TRANBOX_TRIGGER_SELECT = "select";
export const OPT_TRANBOX_TRIGGER_ALL = [
OPT_TRANBOX_TRIGGER_CLICK,
OPT_TRANBOX_TRIGGER_HOVER,
OPT_TRANBOX_TRIGGER_SELECT,
];
export const DEFAULT_TRANBOX_SHORTCUT = ["AltLeft", "KeyS"];
export const DEFAULT_TRANBOX_SETTING = {
transOpen: true,
@@ -390,6 +433,9 @@ export const DEFAULT_TRANBOX_SETTING = {
btnOffsetY: 10,
hideTranBtn: false, // 是否隐藏翻译按钮
hideClickAway: false, // 是否点击外部关闭弹窗
simpleStyle: false, // 是否简洁界面
triggerMode: OPT_TRANBOX_TRIGGER_CLICK, // 触发翻译方式
extStyles: "", // 附加样式
};
// 订阅列表
@@ -412,6 +458,7 @@ export const DEFAULT_SUBRULES_LIST = [
const defaultCustomApi = {
url: "",
key: "",
customOption: "",
fetchLimit: DEFAULT_FETCH_LIMIT,
fetchInterval: DEFAULT_FETCH_INTERVAL,
};
@@ -450,11 +497,19 @@ export const DEFAULT_TRANS_APIS = {
fetchLimit: 1,
fetchInterval: 500,
},
[OPT_TRANS_NIUTRANS]: {
url: "https://api.niutrans.com/NiuTransServer/translation",
key: "",
dictNo: "",
memoryNo: "",
fetchLimit: DEFAULT_FETCH_LIMIT,
fetchInterval: DEFAULT_FETCH_INTERVAL,
},
[OPT_TRANS_OPENAI]: {
url: "https://api.openai.com/v1/chat/completions",
key: "",
model: "gpt-4",
prompt: `You will be provided with a sentence in ${PROMPT_PLACE_FROM}, and your task is to translate it into ${PROMPT_PLACE_TO}.`,
prompt: `You will be provided with a sentence in ${INPUT_PLACE_FROM}, and your task is to translate it into ${INPUT_PLACE_TO}.`,
fetchLimit: 1,
fetchInterval: 500,
},
@@ -462,7 +517,7 @@ export const DEFAULT_TRANS_APIS = {
url: "https://generativelanguage.googleapis.com/v1/models",
key: "",
model: "gemini-pro",
prompt: `Translate the following text from ${PROMPT_PLACE_FROM} to ${PROMPT_PLACE_TO}:\n\n${PROMPT_PLACE_TEXT}`,
prompt: `Translate the following text from ${INPUT_PLACE_FROM} to ${INPUT_PLACE_TO}:\n\n${INPUT_PLACE_TEXT}`,
fetchLimit: 1,
fetchInterval: 500,
},

View File

@@ -8,7 +8,10 @@ export function useApi(translator) {
const updateApi = useCallback(
async (obj) => {
const api = transApis[translator] || {};
const api = {
...DEFAULT_TRANS_APIS[translator],
...(transApis[translator] || {}),
};
Object.assign(transApis, { [translator]: { ...api, ...obj } });
await updateSetting({ transApis });
},
@@ -20,5 +23,12 @@ export function useApi(translator) {
await updateSetting({ transApis });
}, [translator, transApis, updateSetting]);
return { api: transApis[translator] || {}, updateApi, resetApi };
return {
api: {
...DEFAULT_TRANS_APIS[translator],
...(transApis[translator] || {}),
},
updateApi,
resetApi,
};
}

View File

@@ -1,6 +1,6 @@
import { useMemo } from "react";
import { ThemeProvider, createTheme } from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline";
import { CssBaseline, GlobalStyles } from "@mui/material";
import { useDarkMode } from "./ColorMode";
import { THEME_DARK, THEME_LIGHT } from "../config";
@@ -9,7 +9,7 @@ import { THEME_DARK, THEME_LIGHT } from "../config";
* @param {*} param0
* @returns
*/
export default function Theme({ children, options }) {
export default function Theme({ children, options, styles }) {
const { darkMode } = useDarkMode();
const theme = useMemo(() => {
let htmlFontSize = 16;
@@ -38,6 +38,7 @@ export default function Theme({ children, options }) {
<ThemeProvider theme={theme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
<GlobalStyles styles={styles} />
{children}
</ThemeProvider>
);

View File

@@ -39,8 +39,10 @@ export function useTranslate(q, rule, setting) {
text: q,
fromLang,
toLang,
apiSetting:
setting.transApis?.[translator] || DEFAULT_TRANS_APIS[translator],
apiSetting: {
...DEFAULT_TRANS_APIS[translator],
...(setting.transApis[translator] || {}),
},
});
setText(trText);
setSamelang(isSame);

View File

@@ -36,19 +36,10 @@ function App() {
<Link href={process.env.REACT_APP_USERSCRIPT_DOWNLOADURL}>
Install/Update Userscript for Tampermonkey/Violentmonkey
</Link>
{/* <Link href={process.env.REACT_APP_USERSCRIPT_DOWNLOADURL2}>
Install/Update Userscript for Tampermonkey/Violentmonkey 2
</Link> */}
<Link href={process.env.REACT_APP_USERSCRIPT_IOS_DOWNLOADURL}>
Install/Update Userscript for iOS Safari
</Link>
{/* <Link href={process.env.REACT_APP_USERSCRIPT_IOS_DOWNLOADURL2}>
Install/Update Userscript for iOS Safari 2
</Link> */}
<Link href={process.env.REACT_APP_OPTIONSPAGE}>Open Options Page</Link>
{/* <Link href={process.env.REACT_APP_OPTIONSPAGE2}>
Open Options Page 2
</Link> */}
</Stack>
{loading ? (

View File

@@ -139,9 +139,11 @@ export const fetchData = async (
res = await fetchApi({ input, init, transOpts, apiSetting });
}
if (!res?.ok) {
if (!res) {
throw new Error("Unknow error");
} else if (!res.ok) {
const msg = {
url: input,
url: res.url,
status: res.status,
};
if (res.headers.get("Content-Type")?.includes("json")) {

View File

@@ -5,17 +5,24 @@ import {
OPT_TRANS_DEEPL,
OPT_TRANS_DEEPLFREE,
OPT_TRANS_DEEPLX,
OPT_TRANS_NIUTRANS,
OPT_TRANS_BAIDU,
OPT_TRANS_TENCENT,
OPT_TRANS_OPENAI,
OPT_TRANS_GEMINI,
OPT_TRANS_CLOUDFLAREAI,
OPT_TRANS_CUSTOMIZE,
OPT_TRANS_CUSTOMIZE_2,
OPT_TRANS_CUSTOMIZE_3,
OPT_TRANS_CUSTOMIZE_4,
OPT_TRANS_CUSTOMIZE_5,
URL_MICROSOFT_TRAN,
URL_TENCENT_TRANSMART,
PROMPT_PLACE_FROM,
PROMPT_PLACE_TO,
PROMPT_PLACE_TEXT,
INPUT_PLACE_URL,
INPUT_PLACE_FROM,
INPUT_PLACE_TO,
INPUT_PLACE_TEXT,
INPUT_PLACE_KEY,
} from "../config";
import { msAuth } from "./auth";
import { genDeeplFree } from "../apis/deepl";
@@ -141,6 +148,27 @@ const genDeeplX = ({ text, from, to, url, key }) => {
return [url, init];
};
const genNiuTrans = ({ text, from, to, url, key, dictNo, memoryNo }) => {
const data = {
from,
to,
apikey: key,
src_text: text,
dictNo,
memoryNo,
};
const init = {
headers: {
"Content-type": "application/json",
},
method: "POST",
body: JSON.stringify(data),
};
return [url, init];
};
const genTencent = ({ text, from, to }) => {
const data = {
header: {
@@ -168,8 +196,8 @@ const genTencent = ({ text, from, to }) => {
const genOpenAI = ({ text, from, to, url, key, prompt, model }) => {
prompt = prompt
.replaceAll(PROMPT_PLACE_FROM, from)
.replaceAll(PROMPT_PLACE_TO, to);
.replaceAll(INPUT_PLACE_FROM, from)
.replaceAll(INPUT_PLACE_TO, to);
const data = {
model,
@@ -202,9 +230,9 @@ const genOpenAI = ({ text, from, to, url, key, prompt, model }) => {
const genGemini = ({ text, from, to, url, key, prompt, model }) => {
prompt = prompt
.replaceAll(PROMPT_PLACE_FROM, from)
.replaceAll(PROMPT_PLACE_TO, to)
.replaceAll(PROMPT_PLACE_TEXT, text);
.replaceAll(INPUT_PLACE_FROM, from)
.replaceAll(INPUT_PLACE_TO, to)
.replaceAll(INPUT_PLACE_TEXT, text);
const data = {
contents: [
@@ -250,7 +278,14 @@ const genCloudflareAI = ({ text, from, to, url, key }) => {
return [url, init];
};
const genCustom = ({ text, from, to, url, key }) => {
const genCustom = ({ text, from, to, url, key, customOption }) => {
const replaceInput = (str) =>
str
.replaceAll(INPUT_PLACE_URL, url)
.replaceAll(INPUT_PLACE_FROM, from)
.replaceAll(INPUT_PLACE_TO, to)
.replaceAll(INPUT_PLACE_TEXT, text.replaceAll(`"`, `\n`))
.replaceAll(INPUT_PLACE_KEY, key);
const data = {
text,
from,
@@ -266,6 +301,23 @@ const genCustom = ({ text, from, to, url, key }) => {
if (key) {
init.headers.Authorization = `Bearer ${key}`;
}
url = replaceInput(url);
if (customOption?.trim()) {
try {
const opt = JSON.parse(replaceInput(customOption));
opt.url && (url = opt.url);
opt.headers && (init.headers = opt.headers);
opt.method && (init.method = opt.method);
if (init.method === "GET") {
delete init.body;
} else {
opt.body && (init.body = JSON.stringify(opt.body));
}
} catch (err) {
throw new Error(`custom option parse err: ${err}`);
}
}
return [url, init];
};
@@ -283,6 +335,7 @@ export const newTransReq = ({ translator, text, from, to }, apiSetting) => {
case OPT_TRANS_OPENAI:
case OPT_TRANS_GEMINI:
case OPT_TRANS_CLOUDFLAREAI:
case OPT_TRANS_NIUTRANS:
args.key = keyPick(translator, args.key);
break;
default:
@@ -299,6 +352,8 @@ export const newTransReq = ({ translator, text, from, to }, apiSetting) => {
return genDeeplFree(args);
case OPT_TRANS_DEEPLX:
return genDeeplX(args);
case OPT_TRANS_NIUTRANS:
return genNiuTrans(args);
case OPT_TRANS_BAIDU:
return genBaidu(args);
case OPT_TRANS_TENCENT:
@@ -310,6 +365,10 @@ export const newTransReq = ({ translator, text, from, to }, apiSetting) => {
case OPT_TRANS_CLOUDFLAREAI:
return genCloudflareAI(args);
case OPT_TRANS_CUSTOMIZE:
case OPT_TRANS_CUSTOMIZE_2:
case OPT_TRANS_CUSTOMIZE_3:
case OPT_TRANS_CUSTOMIZE_4:
case OPT_TRANS_CUSTOMIZE_5:
return genCustom(args);
default:
throw new Error(`[trans] translator: ${translator} not support`);

View File

@@ -202,26 +202,20 @@ export const removeEndchar = (s, c, count = 1) => {
* @returns
*/
export const matchInputStr = (str, sign) => {
let reg = /\/([\w-]+)\s+([^]+)/;
switch (sign) {
case "//":
reg = /\/\/([\w-]+)\s+([^]+)/;
break;
return str.match(/\/\/([\w-]+)\s+([^]+)/);
case "\\":
reg = /\\([\w-]+)\s+([^]+)/;
break;
return str.match(/\\([\w-]+)\s+([^]+)/);
case "\\\\":
reg = /\\\\([\w-]+)\s+([^]+)/;
break;
return str.match(/\\\\([\w-]+)\s+([^]+)/);
case ">":
reg = />([\w-]+)\s+([^]+)/;
break;
return str.match(/>([\w-]+)\s+([^]+)/);
case ">>":
reg = />>([\w-]+)\s+([^]+)/;
break;
return str.match(/>>([\w-]+)\s+([^]+)/);
default:
}
return str.match(reg);
return str.match(/\/([\w-]+)\s+([^]+)/);
};
/**

View File

@@ -1,7 +1,7 @@
import Stack from "@mui/material/Stack";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
import CircularProgress from "@mui/material/CircularProgress";
import LoadingButton from "@mui/lab/LoadingButton";
import {
OPT_TRANS_ALL,
OPT_TRANS_MICROSOFT,
@@ -13,7 +13,9 @@ import {
OPT_TRANS_GEMINI,
OPT_TRANS_CLOUDFLAREAI,
OPT_TRANS_CUSTOMIZE,
OPT_TRANS_NIUTRANS,
URL_KISS_PROXY,
URL_NIUTRANS_REG,
DEFAULT_FETCH_LIMIT,
DEFAULT_FETCH_INTERVAL,
} from "../../config";
@@ -62,14 +64,24 @@ function TestButton({ translator, api }) {
alert.error(
<>
<div>{i18n("test_failed")}</div>
<pre
style={{
maxWidth: 400,
overflow: "auto",
}}
>
{msg}
</pre>
{msg === err.message ? (
<div
style={{
maxWidth: 400,
}}
>
{msg}
</div>
) : (
<pre
style={{
maxWidth: 400,
overflow: "auto",
}}
>
{msg}
</pre>
)}
</>
);
} finally {
@@ -77,14 +89,15 @@ function TestButton({ translator, api }) {
}
};
if (loading) {
return <CircularProgress size={16} />;
}
return (
<Button size="small" variant="contained" onClick={handleApiTest}>
<LoadingButton
size="small"
variant="contained"
onClick={handleApiTest}
loading={loading}
>
{i18n("click_test")}
</Button>
</LoadingButton>
);
}
@@ -98,6 +111,9 @@ function ApiFields({ translator }) {
prompt = "",
fetchLimit = DEFAULT_FETCH_LIMIT,
fetchInterval = DEFAULT_FETCH_INTERVAL,
dictNo = "",
memoryNo = "",
customOption = "",
} = api;
const handleChange = (e) => {
@@ -128,8 +144,23 @@ function ApiFields({ translator }) {
OPT_TRANS_OPENAI,
OPT_TRANS_GEMINI,
OPT_TRANS_CLOUDFLAREAI,
OPT_TRANS_NIUTRANS,
];
const keyHelper =
translator === OPT_TRANS_NIUTRANS ? (
<>
{i18n("mulkeys_help")}
<Link href={URL_NIUTRANS_REG} target="_blank">
{i18n("reg_niutrans")}
</Link>
</>
) : mulkeysTranslators.includes(translator) ? (
i18n("mulkeys_help")
) : (
""
);
return (
<Stack spacing={3}>
{!buildinTranslators.includes(translator) && (
@@ -148,11 +179,7 @@ function ApiFields({ translator }) {
value={key}
onChange={handleChange}
multiline={mulkeysTranslators.includes(translator)}
helperText={
mulkeysTranslators.includes(translator)
? i18n("mulkeys_help")
: ""
}
helperText={keyHelper}
/>
</>
)}
@@ -177,6 +204,36 @@ function ApiFields({ translator }) {
</>
)}
{translator === OPT_TRANS_NIUTRANS && (
<>
<TextField
size="small"
label={"DictNo"}
name="dictNo"
value={dictNo}
onChange={handleChange}
/>
<TextField
size="small"
label={"MemoryNo"}
name="memoryNo"
value={memoryNo}
onChange={handleChange}
/>
</>
)}
{translator.startsWith(OPT_TRANS_CUSTOMIZE) && (
<TextField
size="small"
label={i18n("custom_option")}
name="customOption"
value={customOption}
onChange={handleChange}
multiline
/>
)}
<TextField
size="small"
label={i18n("fetch_limit")}

View File

@@ -1,10 +1,15 @@
import FileDownloadIcon from "@mui/icons-material/FileDownload";
import Button from "@mui/material/Button";
import LoadingButton from "@mui/lab/LoadingButton";
import { useState } from "react";
import { kissLog } from "../../libs/log";
export default function DownloadButton({ data, text, fileName }) {
const handleClick = (e) => {
export default function DownloadButton({ handleData, text, fileName }) {
const [loading, setLoading] = useState(false);
const handleClick = async (e) => {
e.preventDefault();
if (data) {
try {
setLoading(true);
const data = await handleData();
const url = window.URL.createObjectURL(new Blob([data]));
const link = document.createElement("a");
link.href = url;
@@ -12,16 +17,21 @@ export default function DownloadButton({ data, text, fileName }) {
document.body.appendChild(link);
link.click();
link.remove();
} catch (err) {
kissLog(err, "download");
} finally {
setLoading(false);
}
};
return (
<Button
<LoadingButton
size="small"
variant="outlined"
onClick={handleClick}
loading={loading}
startIcon={<FileDownloadIcon />}
>
{text}
</Button>
</LoadingButton>
);
}

View File

@@ -1,6 +1,5 @@
import Stack from "@mui/material/Stack";
import { OPT_TRANS_BAIDU } from "../../config";
import { useEffect, useState } from "react";
import { useState } from "react";
import Typography from "@mui/material/Typography";
import Accordion from "@mui/material/Accordion";
import AccordionSummary from "@mui/material/AccordionSummary";
@@ -8,53 +7,18 @@ import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import CircularProgress from "@mui/material/CircularProgress";
import { useI18n } from "../../hooks/I18n";
import Alert from "@mui/material/Alert";
import { apiTranslate } from "../../apis";
import Box from "@mui/material/Box";
import { useFavWords } from "../../hooks/FavWords";
import DictCont from "../Selection/DictCont";
import SugCont from "../Selection/SugCont";
import DownloadButton from "./DownloadButton";
import UploadButton from "./UploadButton";
import Button from "@mui/material/Button";
import ClearAllIcon from "@mui/icons-material/ClearAll";
import { isValidWord } from "../../libs/utils";
import { kissLog } from "../../libs/log";
function DictField({ word }) {
const [dictResult, setDictResult] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
useEffect(() => {
(async () => {
try {
setLoading(true);
setError("");
const dictRes = await apiTranslate({
text: word,
translator: OPT_TRANS_BAIDU,
fromLang: "en",
toLang: "zh-CN",
});
dictRes[2].type === 1 && setDictResult(JSON.parse(dictRes[2].result));
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
})();
}, [word]);
if (loading) {
return <CircularProgress size={24} />;
}
if (error) {
return <Alert severity="error">{error}</Alert>;
}
return <DictCont dictResult={dictResult} />;
}
import { apiTranslate } from "../../apis";
import { OPT_TRANS_BAIDU, PHONIC_MAP } from "../../config";
function FavAccordion({ word, index }) {
const [expanded, setExpanded] = useState(false);
@@ -72,7 +36,12 @@ function FavAccordion({ word, index }) {
<Typography>{`${index + 1}. ${word}`}</Typography>
</AccordionSummary>
<AccordionDetails>
{expanded && <DictField word={word} />}
{expanded && (
<Stack spacing={2}>
<DictCont text={word} />
<SugCont text={word} />
</Stack>
)}
</AccordionDetails>
</Accordion>
);
@@ -98,6 +67,45 @@ export default function FavWords() {
}
};
const handleTranslation = async () => {
const tranList = [];
for (const text of downloadList) {
try {
const dictRes = await apiTranslate({
text,
translator: OPT_TRANS_BAIDU,
fromLang: "en",
toLang: "zh-CN",
});
if (dictRes[2]?.type === 1) {
tranList.push(JSON.parse(dictRes[2].result));
}
} catch (err) {
// skip
}
}
return tranList
.map((dictResult) =>
[
`## ${dictResult.src}`,
dictResult.voice
?.map(Object.entries)
.map((item) => item[0])
.map(([key, val]) => `${PHONIC_MAP[key]?.[0] || key} ${val}`)
.join(" "),
dictResult.content[0].mean
.map(({ pre, cont }) => {
return ` - ${pre ? `[${pre}] ` : ""}${Object.keys(cont).join(
"; "
)}`;
})
.join("\n"),
].join("\n\n")
)
.join("\n\n");
};
return (
<Box>
<Stack spacing={3}>
@@ -115,10 +123,15 @@ export default function FavWords() {
fileExts={[".txt", ".csv"]}
/>
<DownloadButton
data={downloadList.join("\n")}
handleData={() => downloadList.join("\n")}
text={i18n("export")}
fileName={`kiss-words_${Date.now()}.txt`}
/>
<DownloadButton
handleData={handleTranslation}
text={i18n("export_translation")}
fileName={`kiss-words_${Date.now()}.md`}
/>
<Button
size="small"
variant="outlined"

View File

@@ -366,7 +366,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
fullWidth
name="transTiming"
value={transTiming}
label={i18n("translate_timing")}
label={i18n("trigger_mode")}
disabled={disabled}
onChange={handleChange}
>
@@ -776,7 +776,7 @@ function UserRules({ subRules }) {
<UploadButton text={i18n("import")} handleImport={handleImport} />
<DownloadButton
data={JSON.stringify([...rules.list].reverse(), null, 2)}
handleData={() => JSON.stringify([...rules.list].reverse(), null, 2)}
text={i18n("export")}
fileName={`kiss-rules_${Date.now()}.json`}
/>

View File

@@ -278,6 +278,7 @@ export default function Settings() {
name="blacklist"
defaultValue={blacklist}
onChange={handleChange}
maxRows={10}
multiline
/>
</Stack>

View File

@@ -6,6 +6,7 @@ import { useSync } from "../../hooks/Sync";
import Alert from "@mui/material/Alert";
import Link from "@mui/material/Link";
import MenuItem from "@mui/material/MenuItem";
import LoadingButton from "@mui/lab/LoadingButton";
import {
URL_KISS_WORKER,
OPT_SYNCTYPE_ALL,
@@ -14,10 +15,8 @@ import {
} from "../../config";
import { useState } from "react";
import { syncSettingAndRules } from "../../libs/sync";
import Button from "@mui/material/Button";
import { useAlert } from "../../hooks/Alert";
import SyncIcon from "@mui/icons-material/Sync";
import CircularProgress from "@mui/material/CircularProgress";
import { useSetting } from "../../hooks/Setting";
import { kissLog } from "../../libs/log";
@@ -123,16 +122,16 @@ export default function SyncSetting() {
useFlexGap
flexWrap="wrap"
>
<Button
<LoadingButton
size="small"
variant="contained"
disabled={!syncUrl || !syncKey || loading}
onClick={handleSyncTest}
startIcon={<SyncIcon />}
loading={loading}
>
{i18n("sync_now")}
</Button>
{loading && <CircularProgress size={16} />}
</LoadingButton>
</Stack>
</Stack>
</Box>

View File

@@ -3,7 +3,13 @@ import Stack from "@mui/material/Stack";
import TextField from "@mui/material/TextField";
import MenuItem from "@mui/material/MenuItem";
import { useI18n } from "../../hooks/I18n";
import { OPT_TRANS_ALL, OPT_LANGS_FROM, OPT_LANGS_TO } from "../../config";
import {
OPT_TRANS_ALL,
OPT_LANGS_FROM,
OPT_LANGS_TO,
OPT_TRANBOX_TRIGGER_CLICK,
OPT_TRANBOX_TRIGGER_ALL,
} from "../../config";
import ShortcutInput from "./ShortcutInput";
import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch";
@@ -51,6 +57,9 @@ export default function Tranbox() {
btnOffsetY,
hideTranBtn = false,
hideClickAway = false,
simpleStyle = false,
triggerMode = OPT_TRANBOX_TRIGGER_CLICK,
extStyles = "",
} = tranboxSetting;
return (
@@ -173,6 +182,43 @@ export default function Tranbox() {
<MenuItem value={true}>{i18n("enable")}</MenuItem>
</TextField>
<TextField
select
size="small"
name="simpleStyle"
value={simpleStyle}
label={i18n("use_simple_style")}
onChange={handleChange}
>
<MenuItem value={false}>{i18n("disable")}</MenuItem>
<MenuItem value={true}>{i18n("enable")}</MenuItem>
</TextField>
<TextField
select
size="small"
name="triggerMode"
value={triggerMode}
label={i18n("trigger_mode")}
onChange={handleChange}
>
{OPT_TRANBOX_TRIGGER_ALL.map((item) => (
<MenuItem key={item} value={item}>
{i18n(`trigger_${item}`)}
</MenuItem>
))}
</TextField>
<TextField
size="small"
label={i18n("extend_styles")}
name="extStyles"
defaultValue={extStyles}
onChange={handleChange}
maxRows={10}
multiline
/>
{!isExt && (
<ShortcutInput
value={tranboxShortcut}

View File

@@ -81,15 +81,9 @@ export default function Options() {
<Link href={process.env.REACT_APP_USERSCRIPT_DOWNLOADURL}>
Install/Update Userscript for Tampermonkey/Violentmonkey
</Link>
{/* <Link href={process.env.REACT_APP_USERSCRIPT_DOWNLOADURL2}>
Install/Update Userscript for Tampermonkey/Violentmonkey 2
</Link> */}
<Link href={process.env.REACT_APP_USERSCRIPT_IOS_DOWNLOADURL}>
Install/Update Userscript for iOS Safari
</Link>
{/* <Link href={process.env.REACT_APP_USERSCRIPT_IOS_DOWNLOADURL2}>
Install/Update Userscript for iOS Safari 2
</Link> */}
</Stack>
</center>
);

View File

@@ -7,23 +7,23 @@ export default function AudioBtn({ text, lan = "uk" }) {
if (error || !ready) {
return (
<IconButton disabled>
<VolumeUpIcon />
<IconButton disabled size="small">
<VolumeUpIcon fontSize="inherit" />
</IconButton>
);
}
if (playing) {
return (
<IconButton color="primary">
<VolumeUpIcon />
<IconButton color="primary" size="small">
<VolumeUpIcon fontSize="inherit" />
</IconButton>
);
}
return (
<IconButton onClick={onPlay}>
<VolumeUpIcon />
<IconButton onClick={onPlay} size="small">
<VolumeUpIcon fontSize="inherit" />
</IconButton>
);
}

View File

@@ -1,53 +1,115 @@
import Box from "@mui/material/Box";
import { useState, useEffect } from "react";
import Stack from "@mui/material/Stack";
import FavBtn from "./FavBtn";
import Typography from "@mui/material/Typography";
import AudioBtn from "./AudioBtn";
import CircularProgress from "@mui/material/CircularProgress";
import Alert from "@mui/material/Alert";
import { OPT_TRANS_BAIDU, PHONIC_MAP } from "../../config";
import { apiTranslate } from "../../apis";
import { isValidWord } from "../../libs/utils";
import CopyBtn from "./CopyBtn";
const phonicMap = {
en_phonic: ["英", "uk"],
us_phonic: ["美", "en"],
};
export default function DictCont({ text }) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const [dictResult, setDictResult] = useState(null);
export default function DictCont({ dictResult }) {
if (!dictResult) {
useEffect(() => {
(async () => {
try {
setLoading(true);
setError("");
setDictResult(null);
if (!isValidWord(text)) {
return;
}
const dictRes = await apiTranslate({
text,
translator: OPT_TRANS_BAIDU,
fromLang: "en",
toLang: "zh-CN",
});
if (dictRes[2]?.type === 1) {
setDictResult(JSON.parse(dictRes[2].result));
}
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
})();
}, [text]);
if (error) {
return <Alert severity="error">{error}</Alert>;
}
if (loading) {
return <CircularProgress size={16} />;
}
if (!text || !dictResult) {
return;
}
const copyText = [
dictResult.src,
dictResult.voice
?.map(Object.entries)
.map((item) => item[0])
.map(([key, val]) => `${PHONIC_MAP[key]?.[0] || key} ${val}`)
.join(" "),
dictResult.content[0].mean
.map(({ pre, cont }) => {
return `${pre ? `[${pre}] ` : ""}${Object.keys(cont).join("; ")}`;
})
.join("\n"),
].join("\n");
return (
<Box>
<Stack
direction="row"
justifyContent="space-between"
alignItems="flex-start"
>
<Stack className="KT-transbox-dict" spacing={1}>
<Stack direction="row" justifyContent="space-between">
<Typography variant="subtitle1" style={{ fontWeight: "bold" }}>
{dictResult.src}
</Typography>
<FavBtn word={dictResult.src} />
<Stack direction="row" justifyContent="space-between">
<CopyBtn text={copyText} />
<FavBtn word={dictResult.src} />
</Stack>
</Stack>
<Typography component="div">
<Stack direction="row">
<Typography component="div">
{dictResult.voice
?.map(Object.entries)
.map((item) => item[0])
.map(([key, val]) => (
<span>
<span>{`${phonicMap[key]?.[0] || key} ${val}`}</span>
<AudioBtn text={dictResult.src} lan={phonicMap[key]?.[1]} />
</span>
<Typography
component="div"
key={key}
style={{ display: "inline-block" }}
>
<Typography component="span">{`${
PHONIC_MAP[key]?.[0] || key
} ${val}`}</Typography>
<AudioBtn text={dictResult.src} lan={PHONIC_MAP[key]?.[1]} />
</Typography>
))}
</Stack>
<ul style={{ margin: "0.5em 0" }}>
</Typography>
<Typography component="ul">
{dictResult.content[0].mean.map(({ pre, cont }, idx) => (
<li key={idx}>
<Typography component="li" key={idx}>
{pre && `[${pre}] `}
{Object.keys(cont).join("; ")}
</li>
</Typography>
))}
</ul>
</Typography>
</Typography>
</Box>
</Stack>
);
}

View File

@@ -1,65 +0,0 @@
import Box from "@mui/material/Box";
import Chip from "@mui/material/Chip";
import Stack from "@mui/material/Stack";
import FavBtn from "./FavBtn";
import Typography from "@mui/material/Typography";
const exchangeMap = {
word_third: "第三人称单数",
word_ing: "现在分词",
word_done: "过去式",
word_past: "过去分词",
word_pl: "复数",
word_proto: "词源",
};
export default function DictCont({ dictResult }) {
if (!dictResult) {
return;
}
return (
<Box>
<Stack
direction="row"
justifyContent="space-between"
alignItems="flex-start"
>
<Typography variant="subtitle1" style={{ fontWeight: "bold" }}>
{dictResult.simple_means?.word_name}
</Typography>
<FavBtn word={dictResult.simple_means?.word_name} />
</Stack>
{dictResult.simple_means?.symbols?.map(({ ph_en, ph_am, parts }, idx) => (
<Typography key={idx} component="div">
{(ph_en || ph_am) && (
<Typography>{`英 /${ph_en || ""}/ 美 /${ph_am || ""}/`}</Typography>
)}
<ul style={{ margin: "0.5em 0" }}>
{parts.map(({ part, means }, idx) => (
<li key={idx}>
{part ? `[${part}] ${means.join("; ")}` : means.join("; ")}
</li>
))}
</ul>
</Typography>
))}
<Typography>
{Object.entries(dictResult.simple_means?.exchange || {})
.map(([key, val]) => `${exchangeMap[key] || key}: ${val.join(", ")}`)
.join("; ")}
</Typography>
<Stack direction="row" spacing={1} flexWrap="wrap" useFlexGap>
{Object.values(dictResult.simple_means?.tags || {})
.flat()
.filter((item) => item)
.map((item) => (
<Chip label={item} size="small" />
))}
</Stack>
</Box>
);
}

View File

@@ -173,6 +173,7 @@ export default function DraggableResizable({
return (
<Box
className="KT-draggable"
style={{
touchAction: "none",
position: "fixed",
@@ -219,11 +220,17 @@ export default function DraggableResizable({
}}
{...opts}
/>
<Paper elevation={4}>
<Pointer direction="Header" style={{ cursor: "move" }} {...opts}>
<Paper className="KT-draggable-body" elevation={4}>
<Pointer
className="KT-draggable-header"
direction="Header"
style={{ cursor: "move" }}
{...opts}
>
{header}
</Pointer>
<div
<Box
className="KT-draggable-container"
style={{
width: size.w,
height: size.h,
@@ -231,7 +238,7 @@ export default function DraggableResizable({
}}
>
{children}
</div>
</Box>
</Paper>
<Pointer
direction="Right"

View File

@@ -1,17 +1,35 @@
import Box from "@mui/material/Box";
import { useState, useEffect } from "react";
import Typography from "@mui/material/Typography";
import { apiBaiduSuggest } from "../../apis";
import Stack from "@mui/material/Stack";
export default function SugCont({ text }) {
const [sugs, setSugs] = useState([]);
useEffect(() => {
(async () => {
try {
setSugs(await apiBaiduSuggest(text));
} catch (err) {
// skip
}
})();
}, [text]);
if (sugs.length === 0) {
return;
}
export default function SugCont({ sugs }) {
return (
<Box>
<Stack className="KT-transbox-sug" spacing={1}>
{sugs.map(({ k, v }) => (
<Typography component="div" key={k}>
<Typography>{k}</Typography>
<ul style={{ margin: "0" }}>
<li>{v}</li>
</ul>
<Typography component="ul" style={{ margin: "0" }}>
<Typography component="li">{v}</Typography>
</Typography>
</Typography>
))}
</Box>
</Stack>
);
}

View File

@@ -1,7 +1,6 @@
import { SettingProvider } from "../../hooks/Setting";
import ThemeProvider from "../../hooks/Theme";
import DraggableResizable from "./DraggableResizable";
import Header from "../Popup/Header";
import Stack from "@mui/material/Stack";
import TextField from "@mui/material/TextField";
import MenuItem from "@mui/material/MenuItem";
@@ -10,13 +9,73 @@ import Box from "@mui/material/Box";
import Divider from "@mui/material/Divider";
import IconButton from "@mui/material/IconButton";
import DoneIcon from "@mui/icons-material/Done";
import DragIndicatorIcon from "@mui/icons-material/DragIndicator";
import UnfoldLessIcon from "@mui/icons-material/UnfoldLess";
import UnfoldMoreIcon from "@mui/icons-material/UnfoldMore";
import PushPinIcon from "@mui/icons-material/PushPin";
import PushPinOutlinedIcon from "@mui/icons-material/PushPinOutlined";
import CloseIcon from "@mui/icons-material/Close";
import { useI18n } from "../../hooks/I18n";
import { OPT_TRANS_ALL, OPT_LANGS_FROM, OPT_LANGS_TO } from "../../config";
import { useState, useRef } from "react";
import TranCont from "./TranCont";
import DictCont from "./DictCont";
import SugCont from "./SugCont";
import CopyBtn from "./CopyBtn";
import { isValidWord } from "../../libs/utils";
function TranForm({ text, setText, tranboxSetting, transApis }) {
function Header({
setShowPopup,
simpleStyle,
setSimpleStyle,
hideClickAway,
setHideClickAway,
}) {
return (
<Box className="KT-transbox-header">
<Stack direction="row" justifyContent="space-between" alignItems="center">
<DragIndicatorIcon fontSize="small" />
<Stack direction="row" alignItems="center">
<IconButton
size="small"
onClick={() => {
setHideClickAway((pre) => !pre);
}}
>
{hideClickAway ? (
<PushPinOutlinedIcon fontSize="small" />
) : (
<PushPinIcon fontSize="small" />
)}
</IconButton>
<IconButton
size="small"
onClick={() => {
setSimpleStyle((pre) => !pre);
}}
>
{simpleStyle ? (
<UnfoldMoreIcon fontSize="small" />
) : (
<UnfoldLessIcon fontSize="small" />
)}
</IconButton>
<IconButton
size="small"
onClick={() => {
setShowPopup(false);
}}
>
<CloseIcon fontSize="small" />
</IconButton>
</Stack>
</Stack>
<Divider />
</Box>
);
}
function TranForm({ text, setText, tranboxSetting, transApis, simpleStyle }) {
const i18n = useI18n();
const [editMode, setEditMode] = useState(false);
@@ -24,133 +83,144 @@ function TranForm({ text, setText, tranboxSetting, transApis }) {
const [translator, setTranslator] = useState(tranboxSetting.translator);
const [fromLang, setFromLang] = useState(tranboxSetting.fromLang);
const [toLang, setToLang] = useState(tranboxSetting.toLang);
const [toLang2, setToLang2] = useState(tranboxSetting.toLang2);
const inputRef = useRef(null);
return (
<Stack sx={{ p: 2 }} spacing={2}>
<Box>
<Grid container spacing={2} columns={12}>
<Grid item xs={4} sm={4} md={4} lg={4}>
<TextField
select
SelectProps={{ MenuProps: { disablePortal: true } }}
fullWidth
size="small"
name="fromLang"
value={fromLang}
label={i18n("from_lang")}
onChange={(e) => {
setFromLang(e.target.value);
}}
>
{OPT_LANGS_FROM.map(([lang, name]) => (
<MenuItem key={lang} value={lang}>
{name}
</MenuItem>
))}
</TextField>
</Grid>
<Grid item xs={4} sm={4} md={4} lg={4}>
<TextField
select
SelectProps={{ MenuProps: { disablePortal: true } }}
fullWidth
size="small"
name="toLang"
value={toLang}
label={i18n("to_lang")}
onChange={(e) => {
setToLang(e.target.value);
}}
>
{OPT_LANGS_TO.map(([lang, name]) => (
<MenuItem key={lang} value={lang}>
{name}
</MenuItem>
))}
</TextField>
</Grid>
<Grid item xs={4} sm={4} md={4} lg={4}>
<TextField
select
SelectProps={{ MenuProps: { disablePortal: true } }}
fullWidth
size="small"
value={translator}
name="translator"
label={i18n("translate_service")}
onChange={(e) => {
setTranslator(e.target.value);
}}
>
{OPT_TRANS_ALL.map((item) => (
<MenuItem key={item} value={item}>
{item}
</MenuItem>
))}
</TextField>
</Grid>
</Grid>
</Box>
<Stack
className="KT-transbox-container"
sx={{ p: simpleStyle ? 1 : 2 }}
spacing={simpleStyle ? 1 : 2}
>
{!simpleStyle && (
<>
<Box className="KT-transbox-select">
<Grid container spacing={simpleStyle ? 1 : 2} columns={12}>
<Grid item xs={4} sm={4} md={4} lg={4}>
<TextField
select
SelectProps={{ MenuProps: { disablePortal: true } }}
fullWidth
size="small"
name="fromLang"
value={fromLang}
label={i18n("from_lang")}
onChange={(e) => {
setFromLang(e.target.value);
}}
>
{OPT_LANGS_FROM.map(([lang, name]) => (
<MenuItem key={lang} value={lang}>
{name}
</MenuItem>
))}
</TextField>
</Grid>
<Grid item xs={4} sm={4} md={4} lg={4}>
<TextField
select
SelectProps={{ MenuProps: { disablePortal: true } }}
fullWidth
size="small"
name="toLang"
value={toLang}
label={i18n("to_lang")}
onChange={(e) => {
setToLang(e.target.value);
}}
>
{OPT_LANGS_TO.map(([lang, name]) => (
<MenuItem key={lang} value={lang}>
{name}
</MenuItem>
))}
</TextField>
</Grid>
<Grid item xs={4} sm={4} md={4} lg={4}>
<TextField
select
SelectProps={{ MenuProps: { disablePortal: true } }}
fullWidth
size="small"
value={translator}
name="translator"
label={i18n("translate_service")}
onChange={(e) => {
setTranslator(e.target.value);
}}
>
{OPT_TRANS_ALL.map((item) => (
<MenuItem key={item} value={item}>
{item}
</MenuItem>
))}
</TextField>
</Grid>
</Grid>
</Box>
<Box>
<TextField
size="small"
label={i18n("original_text")}
inputRef={inputRef}
fullWidth
multiline
value={editMode ? editText : text}
onChange={(e) => {
setEditText(e.target.value);
}}
onFocus={() => {
setEditMode(true);
setEditText(text);
}}
onBlur={() => {
setEditMode(false);
setText(editText.trim());
}}
InputProps={{
endAdornment: (
<Stack
direction="row"
sx={{
position: "absolute",
right: 0,
top: 0,
}}
>
{editMode ? (
<IconButton
size="small"
onClick={(e) => {
e.stopPropagation();
<Box className="KT-transbox-origin">
<TextField
size="small"
label={i18n("original_text")}
inputRef={inputRef}
fullWidth
multiline
value={editMode ? editText : text}
onChange={(e) => {
setEditText(e.target.value);
}}
onFocus={() => {
setEditMode(true);
setEditText(text);
}}
onBlur={() => {
setEditMode(false);
setText(editText.trim());
}}
InputProps={{
endAdornment: (
<Stack
direction="row"
sx={{
position: "absolute",
right: 0,
top: 0,
}}
>
<DoneIcon fontSize="inherit" />
</IconButton>
) : (
<CopyBtn text={text} />
)}
</Stack>
),
}}
/>
</Box>
{editMode ? (
<IconButton
size="small"
onClick={(e) => {
e.stopPropagation();
}}
>
<DoneIcon fontSize="inherit" />
</IconButton>
) : (
<CopyBtn text={text} />
)}
</Stack>
),
}}
/>
</Box>
</>
)}
<TranCont
text={text}
translator={translator}
fromLang={fromLang}
toLang={toLang}
toLang2={toLang2}
setToLang={setToLang}
setToLang2={setToLang2}
transApis={transApis}
/>
{(!simpleStyle || !isValidWord(text) || !toLang.startsWith("zh")) && (
<TranCont
text={text}
translator={translator}
fromLang={fromLang}
toLang={toLang}
toLang2={tranboxSetting.toLang2}
transApis={transApis}
simpleStyle={simpleStyle}
/>
)}
<DictCont text={text} />
<SugCont text={text} />
</Stack>
);
}
@@ -165,24 +235,37 @@ export default function TranBox({
setBoxSize,
boxPosition,
setBoxPosition,
simpleStyle,
setSimpleStyle,
hideClickAway,
setHideClickAway,
extStyles,
}) {
return (
<SettingProvider>
<ThemeProvider>
<ThemeProvider styles={extStyles}>
<DraggableResizable
defaultPosition={boxPosition}
defaultSize={boxSize}
header={<Header setShowPopup={setShowBox} />}
header={
<Header
setShowPopup={setShowBox}
simpleStyle={simpleStyle}
setSimpleStyle={setSimpleStyle}
hideClickAway={hideClickAway}
setHideClickAway={setHideClickAway}
/>
}
onChangeSize={setBoxSize}
onChangePosition={setBoxPosition}
onClick={(e) => e.stopPropagation()}
>
<Divider />
<TranForm
text={text}
setText={setText}
tranboxSetting={tranboxSetting}
transApis={transApis}
simpleStyle={simpleStyle}
/>
</DraggableResizable>
</ThemeProvider>

View File

@@ -1,32 +1,40 @@
import { isMobile } from "../../libs/mobile";
import { limitNumber } from "../../libs/utils";
export default function TranBtn({ onClick, position, tranboxSetting }) {
const left = position.x + tranboxSetting.btnOffsetX;
const top = position.y + tranboxSetting.btnOffsetY;
const touchProps = isMobile
? {
onTouchEnd: onClick,
}
: {
onMouseUp: onClick,
};
export default function TranBtn({
onTrigger,
btnEvent,
position,
tranboxSetting,
}) {
const left = limitNumber(
position.x + tranboxSetting.btnOffsetX,
0,
window.innerWidth - 32
);
const top = limitNumber(
position.y + tranboxSetting.btnOffsetY,
0,
window.innerHeight - 32
);
return (
<div
className="KT-tranbtn"
style={{
cursor: "pointer",
position: "absolute",
// position: "absolute",
position: "fixed",
left,
top,
zIndex: 2147483647,
}}
{...touchProps}
{...{ [btnEvent]: onTrigger }}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
width={isMobile ? "32" : "20"}
height={isMobile ? "32" : "20"}
viewBox="0 0 32 32"
version="1.1"
>

View File

@@ -1,16 +1,14 @@
import TextField from "@mui/material/TextField";
import Box from "@mui/material/Box";
import Alert from "@mui/material/Alert";
import CircularProgress from "@mui/material/CircularProgress";
import Stack from "@mui/material/Stack";
import { useI18n } from "../../hooks/I18n";
import { DEFAULT_TRANS_APIS, OPT_TRANS_BAIDU } from "../../config";
import { DEFAULT_TRANS_APIS } from "../../config";
import { useEffect, useState } from "react";
import { apiTranslate, apiBaiduLangdetect, apiBaiduSuggest } from "../../apis";
import { isValidWord } from "../../libs/utils";
import { apiTranslate, apiBaiduLangdetect } from "../../apis";
import CopyBtn from "./CopyBtn";
import DictCont from "./DictCont";
import SugCont from "./SugCont";
import Typography from "@mui/material/Typography";
import Alert from "@mui/material/Alert";
export default function TranCont({
text,
@@ -18,16 +16,13 @@ export default function TranCont({
fromLang,
toLang,
toLang2 = "en",
setToLang,
setToLang2,
transApis,
simpleStyle,
}) {
const i18n = useI18n();
const [trText, setTrText] = useState("");
const [loading, setLoading] = useState(false);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const [dictResult, setDictResult] = useState(null);
const [sugs, setSugs] = useState([]);
useEffect(() => {
(async () => {
@@ -35,100 +30,73 @@ export default function TranCont({
setLoading(true);
setTrText("");
setError("");
setDictResult(null);
setSugs([]);
// 互译
let to = toLang;
if (toLang !== toLang2 && toLang2 !== "none") {
const detectLang = await apiBaiduLangdetect(text);
if (detectLang === toLang) {
setToLang(toLang2);
setToLang2(toLang);
return;
to = toLang2;
}
}
// 翻译
const apiSetting =
transApis[translator] || DEFAULT_TRANS_APIS[translator];
const tranRes = await apiTranslate({
text,
translator,
fromLang,
toLang,
toLang: to,
apiSetting,
});
setTrText(tranRes[0]);
// 词典
if (isValidWord(text) && toLang.startsWith("zh")) {
if (fromLang === "en" && translator === OPT_TRANS_BAIDU) {
tranRes[2].type === 1 &&
setDictResult(JSON.parse(tranRes[2].result));
} else {
const dictRes = await apiTranslate({
text,
translator: OPT_TRANS_BAIDU,
fromLang: "en",
toLang: "zh-CN",
});
dictRes[2].type === 1 &&
setDictResult(JSON.parse(dictRes[2].result));
}
}
// 建议
if (text.length < 20) {
setSugs(await apiBaiduSuggest(text));
}
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
})();
}, [
text,
translator,
fromLang,
toLang,
toLang2,
setToLang,
setToLang2,
transApis,
]);
}, [text, translator, fromLang, toLang, toLang2, transApis]);
if (simpleStyle) {
return (
<Box className="KT-transbox-target KT-transbox-target_simple">
{error ? (
<Alert severity="error">{error}</Alert>
) : loading ? (
<CircularProgress size={16} />
) : (
<Typography style={{ whiteSpace: "pre-line" }}>{trText}</Typography>
)}
</Box>
);
}
return (
<>
<Box>
<TextField
size="small"
label={i18n("translated_text")}
// disabled
fullWidth
multiline
value={trText}
InputProps={{
endAdornment: (
<Stack
direction="row"
sx={{
position: "absolute",
right: 0,
top: 0,
}}
>
<CopyBtn text={trText} />
</Stack>
),
}}
/>
</Box>
{loading && <CircularProgress size={24} />}
{error && <Alert severity="error">{error}</Alert>}
{dictResult && <DictCont dictResult={dictResult} />}
{sugs.length > 0 && <SugCont sugs={sugs} />}
</>
<Box className="KT-transbox-target KT-transbox-target_default">
<TextField
size="small"
label={i18n("translated_text")}
// disabled
fullWidth
multiline
value={trText}
helperText={error}
InputProps={{
startAdornment: loading ? <CircularProgress size={16} /> : null,
endAdornment: (
<Stack
direction="row"
sx={{
position: "absolute",
right: 0,
top: 0,
}}
>
<CopyBtn text={trText} />
</Stack>
),
}}
/>
</Box>
);
}

View File

@@ -1,10 +1,16 @@
import { useState, useEffect, useCallback } from "react";
import { useState, useEffect, useCallback, useMemo } from "react";
import TranBtn from "./TranBtn";
import TranBox from "./TranBox";
import { shortcutRegister } from "../../libs/shortcut";
import { sleep, limitNumber } from "../../libs/utils";
import { isGm, isExt } from "../../libs/client";
import { MSG_OPEN_TRANBOX, DEFAULT_TRANBOX_SHORTCUT } from "../../config";
import {
MSG_OPEN_TRANBOX,
DEFAULT_TRANBOX_SHORTCUT,
OPT_TRANBOX_TRIGGER_CLICK,
OPT_TRANBOX_TRIGGER_HOVER,
OPT_TRANBOX_TRIGGER_SELECT,
} from "../../config";
import { isMobile } from "../../libs/mobile";
import { kissLog } from "../../libs/log";
@@ -13,8 +19,23 @@ export default function Slection({
tranboxSetting,
transApis,
}) {
const boxWidth = limitNumber(window.innerWidth, 300, 600);
const boxHeight = limitNumber(window.innerHeight, 200, 400);
const {
hideTranBtn = false,
simpleStyle: initSimpleStyle = false,
hideClickAway: initHideClickAway = false,
tranboxShortcut = DEFAULT_TRANBOX_SHORTCUT,
triggerMode = OPT_TRANBOX_TRIGGER_CLICK,
extStyles,
} = tranboxSetting;
const boxWidth =
isMobile || initSimpleStyle
? 300
: limitNumber(window.innerWidth, 300, 600);
const boxHeight =
isMobile || initSimpleStyle
? 200
: limitNumber(window.innerHeight, 200, 400);
const [showBox, setShowBox] = useState(false);
const [showBtn, setShowBtn] = useState(false);
@@ -29,14 +50,17 @@ export default function Slection({
x: (window.innerWidth - boxWidth) / 2,
y: (window.innerHeight - boxHeight) / 2,
});
const [simpleStyle, setSimpleStyle] = useState(initSimpleStyle);
const [hideClickAway, setHideClickAway] = useState(initHideClickAway);
const handleClick = (e) => {
e.stopPropagation();
setShowBtn(false);
setText(selectedText);
setShowBox(true);
};
const handleTrigger = useCallback(
(text) => {
setShowBtn(false);
setText(text || selectedText);
setShowBox(true);
},
[selectedText]
);
const handleTranbox = useCallback(() => {
setShowBtn(false);
@@ -52,10 +76,19 @@ export default function Slection({
setShowBox(true);
}, []);
const btnEvent = useMemo(() => {
if (isMobile) {
return "onTouchEnd";
} else if (triggerMode === OPT_TRANBOX_TRIGGER_HOVER) {
return "onMouseOver";
}
return "onMouseUp";
}, [triggerMode]);
useEffect(() => {
async function handleMouseup(e) {
e.stopPropagation();
await sleep(10);
await sleep(200);
const selectedText = window.getSelection()?.toString()?.trim() || "";
setSelText(selectedText);
@@ -64,37 +97,36 @@ export default function Slection({
return;
}
const { pageX, pageY } = isMobile ? e.changedTouches[0] : e;
!tranboxSetting.hideTranBtn && setShowBtn(true);
// setPosition({ x: e.clientX, y: e.clientY });
setPosition({ x: pageX, y: pageY });
if (triggerMode === OPT_TRANBOX_TRIGGER_SELECT) {
handleTrigger(selectedText);
return;
}
const { clientX, clientY } = isMobile ? e.changedTouches[0] : e;
setShowBtn(!hideTranBtn);
setPosition({ x: clientX, y: clientY });
}
// todo: mobile support
window.addEventListener("mouseup", handleMouseup);
// window.addEventListener(isMobile ? "touchend" : "mouseup", handleMouseup);
// window.addEventListener("mouseup", handleMouseup);
window.addEventListener(isMobile ? "touchend" : "mouseup", handleMouseup);
return () => {
window.removeEventListener(
isMobile ? "touchend" : "mouseup",
handleMouseup
);
};
}, [tranboxSetting.hideTranBtn]);
}, [hideTranBtn, triggerMode, handleTrigger]);
useEffect(() => {
if (isExt) {
return;
}
const clearShortcut = shortcutRegister(
tranboxSetting.tranboxShortcut || DEFAULT_TRANBOX_SHORTCUT,
handleTranbox
);
const clearShortcut = shortcutRegister(tranboxShortcut, handleTranbox);
return () => {
clearShortcut();
};
}, [tranboxSetting.tranboxShortcut, handleTranbox]);
}, [tranboxShortcut, handleTranbox]);
useEffect(() => {
window.addEventListener(MSG_OPEN_TRANBOX, handleTranbox);
@@ -133,7 +165,7 @@ export default function Slection({
}, [handleTranbox, contextMenuType]);
useEffect(() => {
if (tranboxSetting.hideClickAway) {
if (hideClickAway) {
const handleHideBox = () => {
setShowBox(false);
};
@@ -142,7 +174,7 @@ export default function Slection({
window.removeEventListener("click", handleHideBox);
};
}
}, [tranboxSetting.hideClickAway]);
}, [hideClickAway]);
return (
<>
@@ -157,6 +189,11 @@ export default function Slection({
tranboxSetting={tranboxSetting}
transApis={transApis}
setShowBox={setShowBox}
simpleStyle={simpleStyle}
setSimpleStyle={setSimpleStyle}
hideClickAway={hideClickAway}
setHideClickAway={setHideClickAway}
extStyles={extStyles}
/>
)}
@@ -164,7 +201,11 @@ export default function Slection({
<TranBtn
position={position}
tranboxSetting={tranboxSetting}
onClick={handleClick}
btnEvent={btnEvent}
onTrigger={(e) => {
e.stopPropagation();
handleTrigger();
}}
/>
)}
</>