├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug.yml
│ └── feature.yml
├── actions
│ ├── build-for-linux
│ │ ├── Dockerfile
│ │ ├── action.yml
│ │ └── entrypoint.sh
│ └── build.sh
└── workflows
│ └── package.yml
├── .gitignore
├── .node-version
├── .prettierignore
├── .prettierrc.json
├── .scripts
├── popclip
│ ├── Config.plist
│ ├── Pot.png
│ ├── Pot.sh
│ └── build.sh
└── snipdo
│ ├── build.sh
│ ├── pot.json
│ ├── pot.png
│ └── pot.ps1
├── .vscode
├── extensions.json
├── launch.json
└── tasks.json
├── CHANGELOG
├── LICENSE
├── README.md
├── README_EN.md
├── README_KR.md
├── asset
├── 1.png
├── 2.png
├── 3.png
├── eg1.gif
├── eg2.gif
├── eg3.gif
├── eg4.gif
├── eg5.gif
├── eg6.gif
└── header.png
├── com.pot_app.pot.metainfo.xml
├── daemon.html
├── index.html
├── package.json
├── patches
└── hyprland.patch
├── pnpm-lock.yaml
├── postcss.config.js
├── public
├── icon.png
├── icon.svg
├── logo
│ ├── Darwin.svg
│ ├── Linux.svg
│ ├── Windows_NT.svg
│ ├── alibaba.svg
│ ├── anki.svg
│ ├── baidu.svg
│ ├── bing.svg
│ ├── caiyun.svg
│ ├── cambridge_dict.svg
│ ├── chatglm.png
│ ├── deepl.svg
│ ├── ecdict.svg
│ ├── eudic.png
│ ├── geminipro.webp
│ ├── google.svg
│ ├── iflytek.png
│ ├── lingva.svg
│ ├── niutrans.svg
│ ├── ollama.png
│ ├── openai.svg
│ ├── paddle.png
│ ├── qrcode.svg
│ ├── simple_latex.png
│ ├── tencent.svg
│ ├── tencent_cloud.png
│ ├── tesseract.png
│ ├── transmart.svg
│ ├── volcengine.svg
│ ├── yandex.svg
│ └── youdao.svg
├── tesseract-core-simd-lstm.wasm.js
└── worker.min.js
├── src-tauri
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── build.rs
├── icons
│ ├── 128x128.png
│ ├── 128x128@2x.png
│ ├── 32x32.png
│ ├── Square107x107Logo.png
│ ├── Square142x142Logo.png
│ ├── Square150x150Logo.png
│ ├── Square284x284Logo.png
│ ├── Square30x30Logo.png
│ ├── Square310x310Logo.png
│ ├── Square44x44Logo.png
│ ├── Square71x71Logo.png
│ ├── Square89x89Logo.png
│ ├── StoreLogo.png
│ ├── icon.icns
│ ├── icon.ico
│ ├── icon.png
│ └── icon_mac.ico
├── icons_mac
│ ├── 128x128.png
│ ├── 128x128@2x.png
│ ├── 32x32.png
│ ├── Square107x107Logo.png
│ ├── Square142x142Logo.png
│ ├── Square150x150Logo.png
│ ├── Square284x284Logo.png
│ ├── Square30x30Logo.png
│ ├── Square310x310Logo.png
│ ├── Square44x44Logo.png
│ ├── Square71x71Logo.png
│ ├── Square89x89Logo.png
│ ├── StoreLogo.png
│ ├── icon.icns
│ ├── icon.ico
│ ├── icon.png
│ └── tray.ico
├── resources
│ ├── ocr-aarch64-apple-darwin
│ └── ocr-x86_64-apple-darwin
├── src
│ ├── backup.rs
│ ├── clipboard.rs
│ ├── cmd.rs
│ ├── config.rs
│ ├── error.rs
│ ├── hotkey.rs
│ ├── lang_detect.rs
│ ├── main.rs
│ ├── screenshot.rs
│ ├── server.rs
│ ├── system_ocr.rs
│ ├── tray.rs
│ ├── updater.rs
│ └── window.rs
├── tauri.conf.json
├── tauri.linux.conf.json
├── tauri.macos.conf.json
├── tauri.windows.conf.json
├── webview.arm64.json
├── webview.x64.json
└── webview.x86.json
├── src
├── App.jsx
├── components
│ └── WindowControl
│ │ ├── index.jsx
│ │ └── style.css
├── hooks
│ ├── index.jsx
│ ├── useConfig.jsx
│ ├── useGetState.jsx
│ ├── useSyncAtom.jsx
│ ├── useToastStyle.jsx
│ └── useVoice.jsx
├── i18n
│ ├── index.jsx
│ └── locales
│ │ ├── ar_AE.json
│ │ ├── de_DE.json
│ │ ├── en_US.json
│ │ ├── es_ES.json
│ │ ├── fa_IR.json
│ │ ├── fr_FR.json
│ │ ├── he_IL.json
│ │ ├── it_IT.json
│ │ ├── ja_JP.json
│ │ ├── ko_KR.json
│ │ ├── nb_NO.json
│ │ ├── nn_NO.json
│ │ ├── pt_BR.json
│ │ ├── pt_PT.json
│ │ ├── ru_RU.json
│ │ ├── ta_IN.json
│ │ ├── tk_TM.json
│ │ ├── tr_TR.json
│ │ ├── uk_UA.json
│ │ ├── zh_CN.json
│ │ └── zh_TW.json
├── main.jsx
├── services
│ ├── collection
│ │ ├── anki
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── eudic
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ └── index.jsx
│ ├── recognize
│ │ ├── baidu
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── baidu_accurate
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── baidu_img
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── iflytek
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── iflytek_intsig
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── iflytek_latex
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── index.jsx
│ │ ├── qrcode
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── simple_latex
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── system
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── tencent
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── tencent_accurate
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── tencent_img
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── tesseract
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── volcengine
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ └── volcengine_multi_lang
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ ├── translate
│ │ ├── alibaba
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── baidu
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── baidu_field
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── bing
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── bing_dict
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── caiyun
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── cambridge_dict
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── chatglm
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── deepl
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── ecdict
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── geminipro
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── google
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── index.jsx
│ │ ├── lingva
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── niutrans
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── ollama
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── openai
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── tencent
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── transmart
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── volcengine
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ ├── yandex
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ │ └── youdao
│ │ │ ├── Config.jsx
│ │ │ ├── index.jsx
│ │ │ └── info.ts
│ └── tts
│ │ ├── index.jsx
│ │ └── lingva
│ │ ├── Config.jsx
│ │ ├── index.jsx
│ │ └── info.ts
├── style.css
├── utils
│ ├── env.js
│ ├── index.js
│ ├── invoke_plugin.js
│ ├── lang_detect.js
│ ├── language.ts
│ ├── service_instance.ts
│ └── store.js
└── window
│ ├── Config
│ ├── components
│ │ └── SideBar
│ │ │ └── index.jsx
│ ├── index.jsx
│ ├── pages
│ │ ├── About
│ │ │ └── index.jsx
│ │ ├── Backup
│ │ │ ├── AliyunModal
│ │ │ │ └── index.jsx
│ │ │ ├── WebDavModal
│ │ │ │ └── index.jsx
│ │ │ ├── index.jsx
│ │ │ └── utils
│ │ │ │ ├── aliyun.jsx
│ │ │ │ ├── local.jsx
│ │ │ │ └── webdav.jsx
│ │ ├── General
│ │ │ └── index.jsx
│ │ ├── History
│ │ │ └── index.jsx
│ │ ├── Hotkey
│ │ │ └── index.jsx
│ │ ├── Recognize
│ │ │ └── index.jsx
│ │ ├── Service
│ │ │ ├── Collection
│ │ │ │ ├── ConfigModal
│ │ │ │ │ └── index.jsx
│ │ │ │ ├── SelectModal
│ │ │ │ │ └── index.jsx
│ │ │ │ ├── ServiceItem
│ │ │ │ │ └── index.jsx
│ │ │ │ └── index.jsx
│ │ │ ├── PluginConfig
│ │ │ │ └── index.jsx
│ │ │ ├── Recognize
│ │ │ │ ├── ConfigModal
│ │ │ │ │ └── index.jsx
│ │ │ │ ├── SelectModal
│ │ │ │ │ └── index.jsx
│ │ │ │ ├── ServiceItem
│ │ │ │ │ └── index.jsx
│ │ │ │ └── index.jsx
│ │ │ ├── SelectPluginModal
│ │ │ │ └── index.jsx
│ │ │ ├── Translate
│ │ │ │ ├── ConfigModal
│ │ │ │ │ └── index.jsx
│ │ │ │ ├── SelectModal
│ │ │ │ │ └── index.jsx
│ │ │ │ ├── ServiceItem
│ │ │ │ │ └── index.jsx
│ │ │ │ └── index.jsx
│ │ │ ├── Tts
│ │ │ │ ├── ConfigModal
│ │ │ │ │ └── index.jsx
│ │ │ │ ├── SelectModal
│ │ │ │ │ └── index.jsx
│ │ │ │ ├── ServiceItem
│ │ │ │ │ └── index.jsx
│ │ │ │ └── index.jsx
│ │ │ └── index.jsx
│ │ └── Translate
│ │ │ └── index.jsx
│ ├── routes
│ │ └── index.jsx
│ └── style.css
│ ├── Recognize
│ ├── ControlArea
│ │ └── index.jsx
│ ├── ImageArea
│ │ └── index.jsx
│ ├── TextArea
│ │ └── index.jsx
│ └── index.jsx
│ ├── Screenshot
│ └── index.jsx
│ ├── Translate
│ ├── components
│ │ ├── LanguageArea
│ │ │ └── index.jsx
│ │ ├── SourceArea
│ │ │ └── index.jsx
│ │ └── TargetArea
│ │ │ └── index.jsx
│ └── index.jsx
│ └── Updater
│ └── index.jsx
├── tailwind.config.cjs
├── updater
├── updater-for-fix-runtime.mjs
└── updater.mjs
└── vite.config.js
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | custom: ['https://afdian.com/a/pylogmon'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
14 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug.yml:
--------------------------------------------------------------------------------
1 | name: 🐞 Bug Report
2 | description: Report a bug
3 | title: '[BUG]: title'
4 | labels: ['type: bug']
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | # ⬆️ Please give a concise and clear title ⬆️
10 | ## Thank you for your feedback, pot has become better because of it!
11 | ### Please confirm the following before submitting a new issue.
12 | - I have carefully reviewed the official website's usage documentation.
13 | - I have searched through the historical issues but could not find an answer.
14 | - type: textarea
15 | attributes:
16 | label: Description
17 | description: Simply describe the problem.
18 | validations:
19 | required: true
20 | - type: textarea
21 | attributes:
22 | label: Reproduction
23 | description: Steps to reproduce the behaviour.
24 | validations:
25 | required: true
26 | - type: dropdown
27 | attributes:
28 | label: Platform
29 | options:
30 | - Windows
31 | - Linux
32 | - MacOS
33 | validations:
34 | required: true
35 | - type: input
36 | attributes:
37 | label: System Version
38 | placeholder: 'Eg: Windows 11 Home Edition 22621.1702'
39 | validations:
40 | required: true
41 | - type: dropdown
42 | attributes:
43 | label: Window System (Linux Only)
44 | options:
45 | - X11
46 | - Wayland
47 | - type: input
48 | attributes:
49 | label: Software Version
50 | placeholder: 'Eg: 2.0.0'
51 | validations:
52 | required: true
53 | - type: textarea
54 | attributes:
55 | label: Log File
56 | description: 'pot.log file content'
57 | - type: textarea
58 | attributes:
59 | label: Additional Information
60 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature.yml:
--------------------------------------------------------------------------------
1 | name: 🌟 Feature Request
2 | description: Suggest an idea
3 | title: '[Feature]: title'
4 | labels: ['type: enhancement']
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | # ⬆️ Please give a concise and clear title ⬆️
10 | ## Thank you for your feedback, pot has become better because of it!
11 | ### Please confirm the following before submitting a new issue.
12 | - I have carefully reviewed the official website's usage documentation.
13 | - I have searched through the historical issues but could not find an answer.
14 | - type: textarea
15 | attributes:
16 | label: Description
17 | description: Simply describe your idea.
18 | validations:
19 | required: true
20 | - type: textarea
21 | attributes:
22 | label: Application Scenario
23 | description: Why is there such a demand?
24 | validations:
25 | required: true
26 | - type: textarea
27 | attributes:
28 | label: References
29 | description: Provide implementation ideas or reference documents when necessary.
30 |
--------------------------------------------------------------------------------
/.github/actions/build-for-linux/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM rust:bullseye
2 | COPY entrypoint.sh /entrypoint.sh
3 | RUN chmod a+x /entrypoint.sh
4 | ENTRYPOINT ["/entrypoint.sh"]
--------------------------------------------------------------------------------
/.github/actions/build-for-linux/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Build for Linux'
2 | branding:
3 | icon: user-check
4 | color: gray-dark
5 | inputs:
6 | target:
7 | required: true
8 | description: 'Rust Target'
9 | toolchain:
10 | required: true
11 | description: 'Rust Toolchain'
12 | runs:
13 | using: 'docker'
14 | image: 'Dockerfile'
15 | args:
16 | - ${{ inputs.target }}
17 | - ${{ inputs.toolchain }}
18 |
--------------------------------------------------------------------------------
/.github/actions/build-for-linux/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | wget https://nodejs.org/dist/v19.8.1/node-v19.8.1-linux-x64.tar.xz
4 | tar -Jxvf ./node-v19.8.1-linux-x64.tar.xz
5 | export PATH=$(pwd)/node-v19.8.1-linux-x64/bin:$PATH
6 | npm install pnpm -g
7 |
8 | rustup target add "$INPUT_TARGET"
9 | rustup toolchain install --force-non-host "$INPUT_TOOLCHAIN"
10 |
11 | if [ "$INPUT_TARGET" = "x86_64-unknown-linux-gnu" ]; then
12 | apt-get update
13 | apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libayatana-appindicator3-dev librsvg2-dev patchelf libxdo-dev libxcb1 libxrandr2 libdbus-1-3
14 | elif [ "$INPUT_TARGET" = "i686-unknown-linux-gnu" ]; then
15 | dpkg --add-architecture i386
16 | apt-get update
17 | apt-get install -y libstdc++6:i386 libgdk-pixbuf2.0-dev:i386 libatomic1:i386 gcc-multilib g++-multilib libwebkit2gtk-4.0-dev:i386 libssl-dev:i386 libgtk-3-dev:i386 librsvg2-dev:i386 patchelf:i386 libxdo-dev:i386 libxcb1:i386 libxrandr2:i386 libdbus-1-3:i386 libayatana-appindicator3-dev:i386
18 | export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig/:$PKG_CONFIG_PATH
19 | export PKG_CONFIG_SYSROOT_DIR=/
20 | elif [ "$INPUT_TARGET" = "aarch64-unknown-linux-gnu" ]; then
21 | dpkg --add-architecture arm64
22 | apt-get update
23 | apt-get install -y libncurses6:arm64 libtinfo6:arm64 linux-libc-dev:arm64 libncursesw6:arm64 libcups2:arm64
24 | apt-get install -y --no-install-recommends g++-aarch64-linux-gnu libc6-dev-arm64-cross libssl-dev:arm64 libwebkit2gtk-4.0-dev:arm64 libgtk-3-dev:arm64 patchelf:arm64 librsvg2-dev:arm64 libxdo-dev:arm64 libxcb1:arm64 libxrandr2:arm64 libdbus-1-3:arm64 libayatana-appindicator3-dev:arm64
25 | export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
26 | export CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc
27 | export CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++
28 | export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
29 | export PKG_CONFIG_ALLOW_CROSS=1
30 | elif [ "$INPUT_TARGET" = "armv7-unknown-linux-gnueabihf" ]; then
31 | dpkg --add-architecture armhf
32 | apt-get update
33 | apt-get install -y libncurses6:armhf libtinfo6:armhf linux-libc-dev:armhf libncursesw6:armhf libcups2:armhf
34 | apt-get install -y --no-install-recommends g++-arm-linux-gnueabihf libc6-dev-armhf-cross libssl-dev:armhf libwebkit2gtk-4.0-dev:armhf libgtk-3-dev:armhf patchelf:armhf librsvg2-dev:armhf libxdo-dev:armhf libxcb1:armhf libxrandr2:armhf libdbus-1-3:armhf libayatana-appindicator3-dev:armhf
35 | export CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc
36 | export CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc
37 | export CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++
38 | export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig
39 | export PKG_CONFIG_ALLOW_CROSS=1
40 | else
41 | echo "Unknown target: $INPUT_TARGET" && exit 1
42 | fi
43 |
44 | bash .github/actions/build.sh
--------------------------------------------------------------------------------
/.github/actions/build.sh:
--------------------------------------------------------------------------------
1 | # pnpm install --resolution-only
2 | pnpm install
3 | sed -i "s/#openssl/openssl={version=\"0.10\",features=[\"vendored\"]}/g" src-tauri/Cargo.toml
4 | if [ "$INPUT_TARGET" = "x86_64-unknown-linux-gnu" ]; then
5 | pnpm tauri build --target $INPUT_TARGET
6 | else
7 | pnpm tauri build --target $INPUT_TARGET -b deb rpm
8 | fi
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .idea
17 | .DS_Store
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | 21
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | asset
2 | dist
3 | node_modules
4 | public
5 | target
6 | *-lock*
7 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "arrowParens": "always",
3 | "bracketSameLine": false,
4 | "bracketSpacing": true,
5 | "embeddedLanguageFormatting": "auto",
6 | "htmlWhitespaceSensitivity": "css",
7 | "insertPragma": false,
8 | "jsxSingleQuote": true,
9 | "printWidth": 120,
10 | "proseWrap": "preserve",
11 | "quoteProps": "as-needed",
12 | "requirePragma": false,
13 | "semi": true,
14 | "singleAttributePerLine": true,
15 | "singleQuote": true,
16 | "tabWidth": 4,
17 | "trailingComma": "es5",
18 | "useTabs": false,
19 | "vueIndentScriptAndStyle": false,
20 | "endOfLine": "lf"
21 | }
22 |
--------------------------------------------------------------------------------
/.scripts/popclip/Config.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Actions
6 |
7 |
8 | Shell Script File
9 | Pot.sh
10 | Image File
11 | Pot.png
12 | Title
13 | Pot Translate
14 |
15 |
16 | Credits
17 |
18 |
19 | Link
20 | https://pot-app.com/
21 | Name
22 | pot-app
23 |
24 |
25 | Extension Description
26 | Translate text with Pot.
27 | Extension Identifier
28 | com.pot-app.popclip.extension.desktop
29 | Extension Image File
30 | Pot.png
31 | Extension Name
32 | Pot
33 | Required OS Version
34 | 10.13
35 |
36 |
--------------------------------------------------------------------------------
/.scripts/popclip/Pot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/.scripts/popclip/Pot.png
--------------------------------------------------------------------------------
/.scripts/popclip/Pot.sh:
--------------------------------------------------------------------------------
1 | curl -Lsd "$POPCLIP_TEXT" "127.0.0.1:60828"
2 |
3 | if [ $? -eq 0 ]; then
4 | exit 0
5 | else
6 | open -g -a pot
7 | sleep 2
8 | curl -Lsd "$POPCLIP_TEXT" "127.0.0.1:60828/translate"
9 | fi
--------------------------------------------------------------------------------
/.scripts/popclip/build.sh:
--------------------------------------------------------------------------------
1 | rm Pot.popclipextz
2 | mkdir Pot.popclipext
3 | cp Config.plist Pot.popclipext
4 | cp Pot.png Pot.popclipext
5 | cp Pot.sh Pot.popclipext
6 | zip -r Pot.popclipextz Pot.popclipext
7 | rm -r Pot.popclipext
8 |
--------------------------------------------------------------------------------
/.scripts/snipdo/build.sh:
--------------------------------------------------------------------------------
1 | zip pot.pbar pot.json pot.png pot.ps1
--------------------------------------------------------------------------------
/.scripts/snipdo/pot.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Pot Translation",
3 | "identifier": "com.pot-app.snipdoext.desktop",
4 | "icon": "pot.png",
5 | "actions": [
6 | {
7 | "title": "Pot Translation",
8 | "icon": "pot.png",
9 | "powershellFile": "pot.ps1"
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/.scripts/snipdo/pot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/.scripts/snipdo/pot.png
--------------------------------------------------------------------------------
/.scripts/snipdo/pot.ps1:
--------------------------------------------------------------------------------
1 | param(
2 | [string]$PLAIN_TEXT
3 | )
4 |
5 | $encode_text = [System.Text.Encoding]::UTF8.GetBytes($PLAIN_TEXT)
6 |
7 | curl 127.0.0.1:60828/translate -Method POST -Body $encode_text -UseBasicParsing
8 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer", "vadimcn.vscode-lldb"]
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "type": "lldb",
6 | "request": "launch",
7 | "name": "Tauri Development Debug",
8 | "cargo": {
9 | "args": ["build", "--manifest-path=./src-tauri/Cargo.toml", "--no-default-features"]
10 | },
11 | "preLaunchTask": "ui:dev"
12 | },
13 | {
14 | "type": "lldb",
15 | "request": "launch",
16 | "name": "Tauri Production Debug",
17 | "cargo": {
18 | "args": ["build", "--release", "--manifest-path=./src-tauri/Cargo.toml"]
19 | },
20 | "preLaunchTask": "ui:build"
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "tauri:dev",
6 | "type": "shell",
7 | "command": "pnpm",
8 | "args": ["tauri", "dev"]
9 | },
10 | {
11 | "label": "tauri:build",
12 | "type": "shell",
13 | "command": "pnpm",
14 | "args": ["tauri", "build"]
15 | },
16 | {
17 | "label": "ui:dev",
18 | "type": "shell",
19 | "isBackground": true,
20 | "command": "pnpm",
21 | "args": ["dev"]
22 | },
23 | {
24 | "label": "ui:build",
25 | "type": "shell",
26 | "command": "pnpm",
27 | "args": ["build"]
28 | }
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/CHANGELOG:
--------------------------------------------------------------------------------
1 | ## 3.0.7 (2025-5-10)
2 |
3 | ### New feature:
4 |
5 | - signed macOS app
6 |
7 | ### Bugs fixed:
8 |
9 | - fix screenshot on macOS
10 | - rm tray click event on macOS
11 |
--------------------------------------------------------------------------------
/asset/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/asset/1.png
--------------------------------------------------------------------------------
/asset/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/asset/2.png
--------------------------------------------------------------------------------
/asset/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/asset/3.png
--------------------------------------------------------------------------------
/asset/eg1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/asset/eg1.gif
--------------------------------------------------------------------------------
/asset/eg2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/asset/eg2.gif
--------------------------------------------------------------------------------
/asset/eg3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/asset/eg3.gif
--------------------------------------------------------------------------------
/asset/eg4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/asset/eg4.gif
--------------------------------------------------------------------------------
/asset/eg5.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/asset/eg5.gif
--------------------------------------------------------------------------------
/asset/eg6.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/asset/eg6.gif
--------------------------------------------------------------------------------
/asset/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/asset/header.png
--------------------------------------------------------------------------------
/daemon.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 | Pot
11 |
12 |
13 |
14 | Daemon
15 |
16 |
17 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
14 |
17 | Pot
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pot",
3 | "private": true,
4 | "version": "3.0.7",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview",
10 | "tauri": "tauri",
11 | "updater": "node updater/updater.mjs",
12 | "updater:fixRuntime": "node updater/updater-for-fix-runtime.mjs"
13 | },
14 | "dependencies": {
15 | "@nextui-org/react": "^2.4.8",
16 | "@nextui-org/theme": "^2.2.11",
17 | "@react-spring/web": "^9.7.5",
18 | "@tauri-apps/api": "^1.6.0",
19 | "crypto-js": "^4.2.0",
20 | "flag-icons": "^7.2.3",
21 | "framer-motion": "^11.11.10",
22 | "i18next": "^23.16.4",
23 | "jose": "^5.9.6",
24 | "jotai": "^2.10.1",
25 | "jsqr": "^1.4.0",
26 | "md5": "^2.3.0",
27 | "nanoid": "^5.0.8",
28 | "next-themes": "^0.3.0",
29 | "ollama": "^0.5.9",
30 | "react": "^18.3.1",
31 | "react-beautiful-dnd": "^13.1.1",
32 | "react-dom": "^18.3.1",
33 | "react-hot-toast": "^2.4.1",
34 | "react-i18next": "^15.1.0",
35 | "react-icons": "^5.3.0",
36 | "react-markdown": "^9.0.1",
37 | "react-router-dom": "^6.27.0",
38 | "react-spinners": "^0.14.1",
39 | "react-use-measure": "^2.1.1",
40 | "tauri-plugin-autostart-api": "github:tauri-apps/tauri-plugin-autostart#v1",
41 | "tauri-plugin-fs-watch-api": "github:tauri-apps/tauri-plugin-fs-watch#v1",
42 | "tauri-plugin-log-api": "github:tauri-apps/tauri-plugin-log#v1",
43 | "tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql#v1",
44 | "tauri-plugin-store-api": "github:tauri-apps/tauri-plugin-store#v1",
45 | "tesseract.js": "^5.1.1",
46 | "uuid": "^11.0.2"
47 | },
48 | "devDependencies": {
49 | "@tauri-apps/cli": "^1.6.3",
50 | "@vitejs/plugin-react": "^4.3.3",
51 | "autoprefixer": "^10.4.20",
52 | "node-fetch": "^3.3.2",
53 | "postcss": "^8.4.47",
54 | "prettier": "3.3.2",
55 | "tailwindcss": "^3.4.14",
56 | "typescript": "^5.6.3",
57 | "vite": "^5.4.10"
58 | }
59 | }
--------------------------------------------------------------------------------
/patches/hyprland.patch:
--------------------------------------------------------------------------------
1 | From ef74289f660bcb9d407056a42a045804f6b8ecbf Mon Sep 17 00:00:00 2001
2 | From: Pylogmon
3 | Date: Wed, 22 Nov 2023 10:31:59 +0800
4 | Subject: [PATCH] fix: Patch for Hyprland
5 |
6 | #596
7 | ---
8 | src/window/Translate/index.jsx | 14 +++++++-------
9 | 1 file changed, 7 insertions(+), 7 deletions(-)
10 |
11 | diff --git a/src/window/Translate/index.jsx b/src/window/Translate/index.jsx
12 | index ddbf885..04579a7 100644
13 | --- a/src/window/Translate/index.jsx
14 | +++ b/src/window/Translate/index.jsx
15 | @@ -54,13 +54,13 @@ void listen('tauri://focus', () => {
16 | }
17 | });
18 | // 监听 move 事件取消 blurTimeout 时间之内的关闭窗口
19 | -void listen('tauri://move', () => {
20 | - info('Move');
21 | - if (blurTimeout) {
22 | - info('Cancel Close');
23 | - clearTimeout(blurTimeout);
24 | - }
25 | -});
26 | +// void listen('tauri://move', () => {
27 | +// info('Move');
28 | +// if (blurTimeout) {
29 | +// info('Cancel Close');
30 | +// clearTimeout(blurTimeout);
31 | +// }
32 | +// });
33 |
34 | export default function Translate() {
35 | const [closeOnBlur] = useConfig('translate_close_on_blur', true);
36 | --
37 | 2.43.0
38 |
39 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/public/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/public/icon.png
--------------------------------------------------------------------------------
/public/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/logo/Darwin.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/logo/Windows_NT.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/logo/alibaba.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
35 |
--------------------------------------------------------------------------------
/public/logo/anki.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/logo/baidu.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/logo/bing.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/logo/chatglm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/public/logo/chatglm.png
--------------------------------------------------------------------------------
/public/logo/deepl.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/public/logo/ecdict.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/logo/eudic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/public/logo/eudic.png
--------------------------------------------------------------------------------
/public/logo/geminipro.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/public/logo/geminipro.webp
--------------------------------------------------------------------------------
/public/logo/google.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/logo/iflytek.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/public/logo/iflytek.png
--------------------------------------------------------------------------------
/public/logo/ollama.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/public/logo/ollama.png
--------------------------------------------------------------------------------
/public/logo/openai.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/logo/paddle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/public/logo/paddle.png
--------------------------------------------------------------------------------
/public/logo/qrcode.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/logo/simple_latex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/public/logo/simple_latex.png
--------------------------------------------------------------------------------
/public/logo/tencent_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/public/logo/tencent_cloud.png
--------------------------------------------------------------------------------
/public/logo/tesseract.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/public/logo/tesseract.png
--------------------------------------------------------------------------------
/src-tauri/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | /target/
4 |
5 |
--------------------------------------------------------------------------------
/src-tauri/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "pot"
3 | version = "0.0.0"
4 | description = "Pot App"
5 | authors = ["pot-app"]
6 | license = "GPL-3.0-only"
7 | repository = "https://github.com/pot-app/pot-desktop"
8 | edition = "2021"
9 |
10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
11 |
12 | [build-dependencies]
13 | tauri-build = { version = "1.5", features = [] }
14 |
15 | [dependencies]
16 |
17 | tauri = { version = "1.8", features = [ "dialog-save", "dialog-open", "fs-all", "protocol-asset", "shell-all", "clipboard-all", "os-all", "http-all", "http-multipart", "updater", "notification-all", "global-shortcut-all", "window-all", "path-all", "system-tray", "devtools"] }
18 | tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
19 | tauri-plugin-autostart = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
20 | tauri-plugin-fs-watch = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
21 | tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
22 | tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
23 | tauri-plugin-sql = { git= "https://github.com/tauri-apps/plugins-workspace", branch = "v1",features = ["sqlite"] }
24 | serde = { version = "1.0", features = ["derive"] }
25 | selection = "1.2.0"
26 | serde_json = "1.0"
27 | dirs = "5.0.1"
28 | once_cell = "1.19.0"
29 | mouse_position = "0.1.4"
30 | log = "0.4"
31 | tiny_http = "0.12.0"
32 | screenshots = "=0.7.2"
33 | base64 = "0.22"
34 | arboard = "3.4"
35 | lingua = { version = "1.6.2", default-features = false, features = ["chinese", "japanese", "english", "korean", "french", "spanish", "german", "russian", "italian", "portuguese", "turkish", "arabic", "vietnamese", "thai", "indonesian", "malay", "hindi", "mongolian", "persian", "nynorsk", "bokmal", "ukrainian"] }
36 | reqwest = { version = "0.12", features = ["json"] }
37 | reqwest_dav = "=0.1.5"
38 | zip = "2.2.0"
39 | walkdir = "2.5"
40 | thiserror = "1.0"
41 | font-kit = "0.14.2"
42 | image = "0.25.4"
43 |
44 | [target.'cfg(target_os = "macos")'.dependencies]
45 | macos-accessibility-client = "0.0.1"
46 | window-shadows = "0.2"
47 |
48 | [target.'cfg(windows)'.dependencies]
49 | windows = {version="0.58.0",features= ["Win32_UI_WindowsAndMessaging", "Win32_Foundation", "Graphics_Imaging", "Media_Ocr", "Foundation", "Globalization", "Storage", "Storage_Streams"] }
50 | window-shadows = "0.2"
51 |
52 | [target.'cfg(target_os = "linux")'.dependencies]
53 | #openssl
54 |
55 | [features]
56 | # this feature is used for production builds or when `devPath` points to the filesystem
57 | # DO NOT REMOVE!!
58 | custom-protocol = ["tauri/custom-protocol"]
59 |
--------------------------------------------------------------------------------
/src-tauri/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | tauri_build::build()
3 | }
4 |
--------------------------------------------------------------------------------
/src-tauri/icons/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/128x128.png
--------------------------------------------------------------------------------
/src-tauri/icons/128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/128x128@2x.png
--------------------------------------------------------------------------------
/src-tauri/icons/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/32x32.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square107x107Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/Square107x107Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square142x142Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/Square142x142Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square150x150Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/Square150x150Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square284x284Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/Square284x284Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square30x30Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/Square30x30Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square310x310Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/Square310x310Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square44x44Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/Square44x44Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square71x71Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/Square71x71Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square89x89Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/Square89x89Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/StoreLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/StoreLogo.png
--------------------------------------------------------------------------------
/src-tauri/icons/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/icon.icns
--------------------------------------------------------------------------------
/src-tauri/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/icon.ico
--------------------------------------------------------------------------------
/src-tauri/icons/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/icon.png
--------------------------------------------------------------------------------
/src-tauri/icons/icon_mac.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons/icon_mac.ico
--------------------------------------------------------------------------------
/src-tauri/icons_mac/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/128x128.png
--------------------------------------------------------------------------------
/src-tauri/icons_mac/128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/128x128@2x.png
--------------------------------------------------------------------------------
/src-tauri/icons_mac/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/32x32.png
--------------------------------------------------------------------------------
/src-tauri/icons_mac/Square107x107Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/Square107x107Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons_mac/Square142x142Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/Square142x142Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons_mac/Square150x150Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/Square150x150Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons_mac/Square284x284Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/Square284x284Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons_mac/Square30x30Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/Square30x30Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons_mac/Square310x310Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/Square310x310Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons_mac/Square44x44Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/Square44x44Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons_mac/Square71x71Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/Square71x71Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons_mac/Square89x89Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/Square89x89Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons_mac/StoreLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/StoreLogo.png
--------------------------------------------------------------------------------
/src-tauri/icons_mac/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/icon.icns
--------------------------------------------------------------------------------
/src-tauri/icons_mac/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/icon.ico
--------------------------------------------------------------------------------
/src-tauri/icons_mac/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/icon.png
--------------------------------------------------------------------------------
/src-tauri/icons_mac/tray.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/icons_mac/tray.ico
--------------------------------------------------------------------------------
/src-tauri/resources/ocr-aarch64-apple-darwin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/resources/ocr-aarch64-apple-darwin
--------------------------------------------------------------------------------
/src-tauri/resources/ocr-x86_64-apple-darwin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pot-app/pot-desktop/d05a436c9f426a94c38e176f096aeca04c3bf06a/src-tauri/resources/ocr-x86_64-apple-darwin
--------------------------------------------------------------------------------
/src-tauri/src/clipboard.rs:
--------------------------------------------------------------------------------
1 | use crate::window::text_translate;
2 | use std::sync::Mutex;
3 | use tauri::{ClipboardManager, Manager};
4 |
5 | pub struct ClipboardMonitorEnableWrapper(pub Mutex);
6 |
7 | pub fn start_clipboard_monitor(app_handle: tauri::AppHandle) {
8 | tauri::async_runtime::spawn(async move {
9 | let mut pre_text = "".to_string();
10 | loop {
11 | let handle = app_handle.app_handle();
12 | let state = handle.state::();
13 | if let Ok(clipboard_monitor) = state.0.try_lock() {
14 | if clipboard_monitor.contains("true") {
15 | if let Ok(result) = app_handle.clipboard_manager().read_text() {
16 | match result {
17 | Some(v) => {
18 | if v != pre_text {
19 | text_translate(v.clone());
20 | pre_text = v;
21 | }
22 | }
23 | None => {}
24 | }
25 | }
26 | } else {
27 | break;
28 | }
29 | }
30 | std::thread::sleep(std::time::Duration::from_millis(500));
31 | }
32 | });
33 | }
34 |
--------------------------------------------------------------------------------
/src-tauri/src/error.rs:
--------------------------------------------------------------------------------
1 | // create the error type that represents all errors possible in our program
2 | #[derive(Debug, thiserror::Error)]
3 | pub enum Error {
4 | #[error(transparent)]
5 | Io(#[from] std::io::Error),
6 | #[error(transparent)]
7 | Error(#[from] Box),
8 | #[error(transparent)]
9 | Dav(#[from] reqwest_dav::Error),
10 | #[error(transparent)]
11 | DavRe(#[from] reqwest_dav::re_exports::reqwest::Error),
12 | #[error(transparent)]
13 | Serde(#[from] serde_json::Error),
14 | #[error(transparent)]
15 | Zip(#[from] zip::result::ZipError),
16 | #[error(transparent)]
17 | WalkDir(#[from] walkdir::Error),
18 | #[error(transparent)]
19 | Tauri(#[from] tauri::Error),
20 | #[error(transparent)]
21 | StripPrefix(#[from] std::path::StripPrefixError),
22 | #[error(transparent)]
23 | Arboard(#[from] arboard::Error),
24 | #[error(transparent)]
25 | Image(#[from] image::ImageError),
26 | #[error(transparent)]
27 | Selection(#[from] font_kit::error::SelectionError),
28 | #[error(transparent)]
29 | Reqwest(#[from] reqwest::Error),
30 | }
31 |
32 | // we must manually implement serde::Serialize
33 | impl serde::Serialize for Error {
34 | fn serialize(&self, serializer: S) -> Result
35 | where
36 | S: serde::ser::Serializer,
37 | {
38 | serializer.serialize_str(self.to_string().as_ref())
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src-tauri/src/lang_detect.rs:
--------------------------------------------------------------------------------
1 | pub fn init_lang_detect() {
2 | // https://crates.io/crates/lingua
3 | use lingua::{Language, LanguageDetectorBuilder};
4 | let languages = vec![
5 | Language::Chinese,
6 | Language::Japanese,
7 | Language::English,
8 | Language::Korean,
9 | Language::French,
10 | Language::Spanish,
11 | Language::German,
12 | Language::Russian,
13 | Language::Italian,
14 | Language::Portuguese,
15 | Language::Turkish,
16 | Language::Arabic,
17 | Language::Vietnamese,
18 | Language::Thai,
19 | Language::Indonesian,
20 | Language::Malay,
21 | Language::Hindi,
22 | Language::Mongolian,
23 | Language::Bokmal,
24 | Language::Nynorsk,
25 | Language::Persian,
26 | Language::Ukrainian,
27 | ];
28 | let detector = LanguageDetectorBuilder::from_languages(&languages).build();
29 | let _ = detector.detect_language_of("Hello Language");
30 | }
31 | #[tauri::command]
32 | pub fn lang_detect(text: &str) -> Result<&str, ()> {
33 | use lingua::{Language, LanguageDetectorBuilder};
34 | let languages = vec![
35 | Language::Chinese,
36 | Language::Japanese,
37 | Language::English,
38 | Language::Korean,
39 | Language::French,
40 | Language::Spanish,
41 | Language::German,
42 | Language::Russian,
43 | Language::Italian,
44 | Language::Portuguese,
45 | Language::Turkish,
46 | Language::Arabic,
47 | Language::Vietnamese,
48 | Language::Thai,
49 | Language::Indonesian,
50 | Language::Malay,
51 | Language::Hindi,
52 | Language::Mongolian,
53 | Language::Bokmal,
54 | Language::Nynorsk,
55 | Language::Persian,
56 | ];
57 | let detector = LanguageDetectorBuilder::from_languages(&languages).build();
58 | if let Some(lang) = detector.detect_language_of(text) {
59 | match lang {
60 | Language::Chinese => Ok("zh_cn"),
61 | Language::Japanese => Ok("ja"),
62 | Language::English => Ok("en"),
63 | Language::Korean => Ok("ko"),
64 | Language::French => Ok("fr"),
65 | Language::Spanish => Ok("es"),
66 | Language::German => Ok("de"),
67 | Language::Russian => Ok("ru"),
68 | Language::Italian => Ok("it"),
69 | Language::Portuguese => Ok("pt_pt"),
70 | Language::Turkish => Ok("tr"),
71 | Language::Arabic => Ok("ar"),
72 | Language::Vietnamese => Ok("vi"),
73 | Language::Thai => Ok("th"),
74 | Language::Indonesian => Ok("id"),
75 | Language::Malay => Ok("ms"),
76 | Language::Hindi => Ok("hi"),
77 | Language::Mongolian => Ok("mn_cy"),
78 | Language::Bokmal => Ok("nb_no"),
79 | Language::Nynorsk => Ok("nn_no"),
80 | Language::Persian => Ok("fa"),
81 | Language::Ukrainian => Ok("uk"),
82 | }
83 | } else {
84 | return Ok("en");
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src-tauri/src/screenshot.rs:
--------------------------------------------------------------------------------
1 | use log::info;
2 |
3 | #[tauri::command]
4 | pub fn screenshot(x: i32, y: i32) {
5 | use crate::APP;
6 | use dirs::cache_dir;
7 | use screenshots::{Compression, Screen};
8 | use std::fs;
9 | info!("Screenshot screen with position: x={}, y={}", x, y);
10 | let screens = Screen::all().unwrap();
11 | for screen in screens {
12 | let info = screen.display_info;
13 | info!("Screen: {:?}", info);
14 | if info.x == x && info.y == y {
15 | let handle = APP.get().unwrap();
16 | let mut app_cache_dir_path = cache_dir().expect("Get Cache Dir Failed");
17 | app_cache_dir_path.push(&handle.config().tauri.bundle.identifier);
18 | if !app_cache_dir_path.exists() {
19 | // 创建目录
20 | fs::create_dir_all(&app_cache_dir_path).expect("Create Cache Dir Failed");
21 | }
22 | app_cache_dir_path.push("pot_screenshot.png");
23 |
24 | let image = screen.capture().unwrap();
25 | let buffer = image.to_png(Compression::Fast).unwrap();
26 | fs::write(app_cache_dir_path, buffer).unwrap();
27 | break;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src-tauri/src/server.rs:
--------------------------------------------------------------------------------
1 | use crate::config::{get, set};
2 | use crate::window::*;
3 | use log::{info, warn};
4 | use std::thread;
5 | use tauri::api::notification;
6 | use tiny_http::{Request, Response, Server};
7 |
8 | pub fn start_server() {
9 | let port = match get("server_port") {
10 | Some(v) => v.as_i64().unwrap(),
11 | None => {
12 | set("server_port", 60828);
13 | 60828
14 | }
15 | };
16 | thread::spawn(move || {
17 | let server = match Server::http(format!("127.0.0.1:{port}")) {
18 | Ok(v) => v,
19 | Err(e) => {
20 | let _ = notification::Notification::new("com.pot-spp.com")
21 | .title("Server start failed")
22 | .body("Please Change Server Port and restart the application")
23 | .show();
24 | warn!("Server start failed: {}", e);
25 | return;
26 | }
27 | };
28 | for request in server.incoming_requests() {
29 | http_handle(request);
30 | }
31 | });
32 | }
33 |
34 | fn http_handle(request: Request) {
35 | info!("Handle {} request", request.url());
36 | match request.url() {
37 | "/" => handle_translate(request),
38 | "/config" => handle_config(request),
39 | "/translate" => handle_translate(request),
40 | "/selection_translate" => handle_selection_translate(request),
41 | "/input_translate" => handle_input_translate(request),
42 | "/ocr_recognize" => handle_ocr_recognize(request),
43 | "/ocr_translate" => handle_ocr_translate(request),
44 | "/ocr_recognize?screenshot=false" => handle_ocr_recognize(request),
45 | "/ocr_translate?screenshot=false" => handle_ocr_translate(request),
46 | "/ocr_recognize?screenshot=true" => handle_ocr_recognize(request),
47 | "/ocr_translate?screenshot=true" => handle_ocr_translate(request),
48 | _ => warn!("Unknown request url: {}", request.url()),
49 | }
50 | }
51 |
52 | fn handle_config(request: Request) {
53 | config_window();
54 | response_ok(request);
55 | }
56 |
57 | fn handle_translate(mut request: Request) {
58 | let mut content = String::new();
59 | request.as_reader().read_to_string(&mut content).unwrap();
60 | text_translate(content);
61 | response_ok(request);
62 | }
63 |
64 | fn handle_selection_translate(request: Request) {
65 | selection_translate();
66 | response_ok(request);
67 | }
68 |
69 | fn handle_input_translate(request: Request) {
70 | input_translate();
71 | response_ok(request);
72 | }
73 |
74 | fn handle_ocr_recognize(request: Request) {
75 | if request.url().ends_with("false") {
76 | recognize_window();
77 | } else {
78 | ocr_recognize();
79 | }
80 | response_ok(request);
81 | }
82 |
83 | fn handle_ocr_translate(request: Request) {
84 | if request.url().ends_with("false") {
85 | image_translate();
86 | } else {
87 | ocr_translate();
88 | }
89 | response_ok(request);
90 | }
91 |
92 | fn response_ok(request: Request) {
93 | let response = Response::from_string("ok");
94 | request.respond(response).unwrap();
95 | }
96 |
--------------------------------------------------------------------------------
/src-tauri/src/updater.rs:
--------------------------------------------------------------------------------
1 | use crate::config::{get, set};
2 | use crate::window::updater_window;
3 | use log::{info, warn};
4 |
5 | pub fn check_update(app_handle: tauri::AppHandle) {
6 | let enable = match get("check_update") {
7 | Some(v) => v.as_bool().unwrap(),
8 | None => {
9 | set("check_update", true);
10 | true
11 | }
12 | };
13 | if enable {
14 | tauri::async_runtime::spawn(async move {
15 | match tauri::updater::builder(app_handle).check().await {
16 | Ok(update) => {
17 | if update.is_update_available() {
18 | info!("New version available");
19 | updater_window();
20 | }
21 | }
22 | Err(e) => {
23 | warn!("Failed to check update: {}", e);
24 | }
25 | }
26 | });
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src-tauri/tauri.linux.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "tauri": {
3 | "systemTray": {
4 | "iconPath": "icons/icon.ico",
5 | "iconAsTemplate": true
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src-tauri/tauri.macos.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "tauri": {
3 | "systemTray": {
4 | "iconPath": "icons_mac/tray.ico",
5 | "iconAsTemplate": true
6 | },
7 | "bundle": {
8 | "resources": [
9 | "resources/*"
10 | ],
11 | "icon": [
12 | "icons_mac/32x32.png",
13 | "icons_mac/128x128.png",
14 | "icons_mac/128x128@2x.png",
15 | "icons_mac/icon.icns",
16 | "icons_mac/icon.ico"
17 | ]
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/src-tauri/tauri.windows.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "tauri": {
3 | "systemTray": {
4 | "iconPath": "icons/icon.ico",
5 | "iconAsTemplate": true
6 | },
7 | "bundle": {
8 | "windows": {
9 | "certificateThumbprint": null,
10 | "digestAlgorithm": "sha256",
11 | "timestampUrl": "",
12 | "webviewInstallMode": {
13 | "silent": true,
14 | "type": "embedBootstrapper"
15 | },
16 | "nsis": {
17 | "displayLanguageSelector": true,
18 | "installerIcon": "icons/icon.ico",
19 | "license": "../LICENSE",
20 | "installMode": "perMachine",
21 | "languages": ["SimpChinese", "TradChinese", "English"]
22 | }
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src-tauri/webview.arm64.json:
--------------------------------------------------------------------------------
1 | {
2 | "tauri": {
3 | "systemTray": {
4 | "iconPath": "icons/icon.ico",
5 | "iconAsTemplate": true
6 | },
7 | "bundle": {
8 | "windows": {
9 | "certificateThumbprint": null,
10 | "digestAlgorithm": "sha256",
11 | "timestampUrl": "",
12 | "webviewInstallMode": {
13 | "type": "fixedRuntime",
14 | "path": "./Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.arm64/"
15 | },
16 | "nsis": {
17 | "displayLanguageSelector": true,
18 | "installerIcon": "icons/icon.ico",
19 | "license": "../LICENSE",
20 | "installMode": "perMachine",
21 | "languages": [
22 | "SimpChinese",
23 | "TradChinese",
24 | "English"
25 | ]
26 | }
27 | }
28 | },
29 | "updater": {
30 | "active": true,
31 | "dialog": false,
32 | "endpoints": [
33 | "https://dl.pot-app.com/https://github.com/pot-app/pot-desktop/releases/download/updater/update-fix-runtime.json",
34 | "https://github.com/pot-app/pot-desktop/releases/download/updater/update-fix-runtime.json"
35 | ],
36 | "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDVBRTQxQTNDQjM5QzQzM0EKUldRNlE1eXpQQnJrV21mM1Bram5LRlF6UDA3K0Jab2FYL2lZSWhXTE5McWs2NUdJS0dtYkd5VGMK"
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src-tauri/webview.x64.json:
--------------------------------------------------------------------------------
1 | {
2 | "tauri": {
3 | "systemTray": {
4 | "iconPath": "icons/icon.ico",
5 | "iconAsTemplate": true
6 | },
7 | "bundle": {
8 | "windows": {
9 | "certificateThumbprint": null,
10 | "digestAlgorithm": "sha256",
11 | "timestampUrl": "",
12 | "webviewInstallMode": {
13 | "type": "fixedRuntime",
14 | "path": "./Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.x64/"
15 | },
16 | "nsis": {
17 | "displayLanguageSelector": true,
18 | "installerIcon": "icons/icon.ico",
19 | "license": "../LICENSE",
20 | "installMode": "perMachine",
21 | "languages": [
22 | "SimpChinese",
23 | "TradChinese",
24 | "English"
25 | ]
26 | }
27 | }
28 | },
29 | "updater": {
30 | "active": true,
31 | "dialog": false,
32 | "endpoints": [
33 | "https://dl.pot-app.com/https://github.com/pot-app/pot-desktop/releases/download/updater/update-fix-runtime.json",
34 | "https://github.com/pot-app/pot-desktop/releases/download/updater/update-fix-runtime.json"
35 | ],
36 | "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDVBRTQxQTNDQjM5QzQzM0EKUldRNlE1eXpQQnJrV21mM1Bram5LRlF6UDA3K0Jab2FYL2lZSWhXTE5McWs2NUdJS0dtYkd5VGMK"
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src-tauri/webview.x86.json:
--------------------------------------------------------------------------------
1 | {
2 | "tauri": {
3 | "systemTray": {
4 | "iconPath": "icons/icon.ico",
5 | "iconAsTemplate": true
6 | },
7 | "bundle": {
8 | "windows": {
9 | "certificateThumbprint": null,
10 | "digestAlgorithm": "sha256",
11 | "timestampUrl": "",
12 | "webviewInstallMode": {
13 | "type": "fixedRuntime",
14 | "path": "./Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.x86/"
15 | },
16 | "nsis": {
17 | "displayLanguageSelector": true,
18 | "installerIcon": "icons/icon.ico",
19 | "license": "../LICENSE",
20 | "installMode": "perMachine",
21 | "languages": [
22 | "SimpChinese",
23 | "TradChinese",
24 | "English"
25 | ]
26 | }
27 | }
28 | },
29 | "updater": {
30 | "active": true,
31 | "dialog": false,
32 | "endpoints": [
33 | "https://dl.pot-app.com/https://github.com/pot-app/pot-desktop/releases/download/updater/update-fix-runtime.json",
34 | "https://github.com/pot-app/pot-desktop/releases/download/updater/update-fix-runtime.json"
35 | ],
36 | "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDVBRTQxQTNDQjM5QzQzM0EKUldRNlE1eXpQQnJrV21mM1Bram5LRlF6UDA3K0Jab2FYL2lZSWhXTE5McWs2NUdJS0dtYkd5VGMK"
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/components/WindowControl/index.jsx:
--------------------------------------------------------------------------------
1 | import { VscChromeClose, VscChromeMinimize, VscChromeMaximize, VscChromeRestore } from 'react-icons/vsc';
2 | import React, { useEffect, useState } from 'react';
3 | import { appWindow } from '@tauri-apps/api/window';
4 | import { listen } from '@tauri-apps/api/event';
5 | import { Button } from '@nextui-org/react';
6 |
7 | import { osType } from '../../utils/env';
8 | import './style.css';
9 |
10 | export default function WindowControl() {
11 | const [isMax, setIsMax] = useState(false);
12 |
13 | useEffect(() => {
14 | listen('tauri://resize', async () => {
15 | if (await appWindow.isMaximized()) {
16 | setIsMax(true);
17 | } else {
18 | setIsMax(false);
19 | }
20 | });
21 | }, []);
22 |
23 | return (
24 |
25 |
33 |
47 |
55 |
56 | );
57 | }
58 |
--------------------------------------------------------------------------------
/src/components/WindowControl/style.css:
--------------------------------------------------------------------------------
1 | .close-button:hover {
2 | background-color: #c42b1c !important;
3 | }
4 |
--------------------------------------------------------------------------------
/src/hooks/index.jsx:
--------------------------------------------------------------------------------
1 | export * from './useConfig';
2 | export * from './useToastStyle';
3 | export * from './useSyncAtom';
4 | export * from './useVoice';
5 |
--------------------------------------------------------------------------------
/src/hooks/useConfig.jsx:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect } from 'react';
2 | import { listen, emit } from '@tauri-apps/api/event';
3 | import { useGetState } from './useGetState';
4 | import { store } from '../utils/store';
5 | import { debounce } from '../utils';
6 |
7 | export const useConfig = (key, defaultValue, options = {}) => {
8 | const [property, setPropertyState, getProperty] = useGetState(null);
9 | const { sync = true } = options;
10 |
11 | // 同步到Store (State -> Store)
12 | const syncToStore = useCallback(
13 | debounce((v) => {
14 | store.set(key, v);
15 | store.save();
16 | let eventKey = key.replaceAll('.', '_').replaceAll('@', ':');
17 | emit(`${eventKey}_changed`, v);
18 | }),
19 | []
20 | );
21 |
22 | // 同步到State (Store -> State)
23 | const syncToState = useCallback((v) => {
24 | if (v !== null) {
25 | setPropertyState(v);
26 | } else {
27 | store.get(key).then((v) => {
28 | if (v === null) {
29 | setPropertyState(defaultValue);
30 | store.set(key, defaultValue);
31 | store.save();
32 | } else {
33 | setPropertyState(v);
34 | }
35 | });
36 | }
37 | }, []);
38 |
39 | const setProperty = useCallback((v, forceSync = false) => {
40 | setPropertyState(v);
41 | const isSync = forceSync || sync;
42 | isSync && syncToStore(v);
43 | }, []);
44 |
45 | // 初始化
46 | useEffect(() => {
47 | syncToState(null);
48 | const eventKey = key.replaceAll('.', '_').replaceAll('@', ':');
49 | const unlisten = listen(`${eventKey}_changed`, (e) => {
50 | syncToState(e.payload);
51 | });
52 | return () => {
53 | unlisten.then((f) => {
54 | f();
55 | });
56 | };
57 | }, []);
58 |
59 | return [property, setProperty, getProperty];
60 | };
61 |
62 | export const deleteKey = (key) => {
63 | if (store.has(key)) {
64 | store.delete(key);
65 | store.save();
66 | }
67 | };
68 |
--------------------------------------------------------------------------------
/src/hooks/useGetState.jsx:
--------------------------------------------------------------------------------
1 | import { useState, useRef, useCallback } from 'react';
2 |
3 | export const useGetState = (initState) => {
4 | const [state, setState] = useState(initState);
5 | const stateRef = useRef(state);
6 | stateRef.current = state;
7 | const getState = useCallback(() => stateRef.current, []);
8 | return [state, setState, getState];
9 | };
10 |
--------------------------------------------------------------------------------
/src/hooks/useSyncAtom.jsx:
--------------------------------------------------------------------------------
1 | import { useAtom } from 'jotai';
2 |
3 | import { useGetState } from './useGetState';
4 |
5 | export const useSyncAtom = (atom) => {
6 | const [atomValue, setAtomValue] = useAtom(atom);
7 | const [localValue, setLocalValue, getLocalValue] = useGetState(atomValue);
8 |
9 | const syncAtom = () => setAtomValue(getLocalValue());
10 |
11 | return [localValue, setLocalValue, syncAtom];
12 | };
13 |
--------------------------------------------------------------------------------
/src/hooks/useToastStyle.jsx:
--------------------------------------------------------------------------------
1 | import { semanticColors } from '@nextui-org/theme';
2 | import { useTheme } from 'next-themes';
3 |
4 | export const useToastStyle = () => {
5 | const { theme } = useTheme();
6 | const toastStyle = {
7 | background: theme == 'dark' ? semanticColors.dark.content1.DEFAULT : semanticColors.light.content1.DEFAULT,
8 | color: theme == 'dark' ? semanticColors.dark.foreground.DEFAULT : semanticColors.light.foreground.DEFAULT,
9 | wordBreak: 'break-all',
10 | select: 'text',
11 | };
12 |
13 | return toastStyle;
14 | };
15 |
--------------------------------------------------------------------------------
/src/hooks/useVoice.jsx:
--------------------------------------------------------------------------------
1 | import { useCallback } from 'react';
2 | let audioContext = new (window.AudioContext || window.webkitAudioContext)();
3 | let source = null;
4 |
5 | export const useVoice = () => {
6 | const playOrStop = useCallback((data) => {
7 | if (source) {
8 | // 如果正在播放,停止播放
9 | source.stop();
10 | source.disconnect();
11 | source = null;
12 | } else {
13 | // 如果没在播放,开始播放
14 | audioContext.decodeAudioData(new Uint8Array(data).buffer, (buffer) => {
15 | source = audioContext.createBufferSource();
16 | source.buffer = buffer;
17 | source.connect(audioContext.destination);
18 | source.start();
19 | source.onended = () => {
20 | source.disconnect();
21 | source = null;
22 | };
23 | });
24 | }
25 | });
26 |
27 | return playOrStop;
28 | };
29 |
--------------------------------------------------------------------------------
/src/i18n/index.jsx:
--------------------------------------------------------------------------------
1 | import { initReactI18next } from 'react-i18next';
2 | import i18n from 'i18next';
3 | import zh_CN from './locales/zh_CN.json';
4 | import zh_TW from './locales/zh_TW.json';
5 | import en_US from './locales/en_US.json';
6 | import ru_RU from './locales/ru_RU.json';
7 | import pt_BR from './locales/pt_BR.json';
8 | import de_DE from './locales/de_DE.json';
9 | import es_ES from './locales/es_ES.json';
10 | import fr_FR from './locales/fr_FR.json';
11 | import it_IT from './locales/it_IT.json';
12 | import ja_JP from './locales/ja_JP.json';
13 | import ko_KR from './locales/ko_KR.json';
14 | import pt_PT from './locales/pt_PT.json';
15 | import tr_TR from './locales/tr_TR.json';
16 | import nb_NO from './locales/nb_NO.json';
17 | import nn_NO from './locales/nn_NO.json';
18 | import fa_IR from './locales/fa_IR.json';
19 | import uk_UA from './locales/uk_UA.json';
20 | import ar_AE from './locales/ar_AE.json';
21 | import he_IL from './locales/he_IL.json';
22 |
23 | // http://www.lingoes.net/zh/translator/langcode.htm
24 |
25 | i18n.use(initReactI18next).init({
26 | fallbackLng: {
27 | zh_tw: ['zh_cn'],
28 | zh_cn: ['zh_tw'],
29 | pt_pt: ['pt_br'],
30 | pt_br: ['pt_pt'],
31 | nb_no: ['nn_no'],
32 | nn_no: ['nb_no'],
33 | default: ['en'],
34 | },
35 | debug: false,
36 | interpolation: {
37 | escapeValue: false,
38 | },
39 | resources: {
40 | en: en_US,
41 | zh_cn: zh_CN,
42 | zh_tw: zh_TW,
43 | ja: ja_JP,
44 | ko: ko_KR,
45 | fr: fr_FR,
46 | es: es_ES,
47 | ru: ru_RU,
48 | de: de_DE,
49 | it: it_IT,
50 | tr: tr_TR,
51 | pt_pt: pt_PT,
52 | pt_br: pt_BR,
53 | nb_no: nb_NO,
54 | nn_no: nn_NO,
55 | fa: fa_IR,
56 | uk: uk_UA,
57 | ar: ar_AE,
58 | he: he_IL,
59 | },
60 | });
61 |
62 | export default i18n;
63 |
--------------------------------------------------------------------------------
/src/i18n/locales/ko_KR.json:
--------------------------------------------------------------------------------
1 | {
2 | "translation": {
3 | "common": {
4 | "ok": "Ok",
5 | "cancel": "취소",
6 | "save": "저장",
7 | "write_clipboard": "클립보드에 작성",
8 | "plugin": "플러그인"
9 | },
10 | "services": {
11 | "translate": {
12 | "lingva": {
13 | "title": "Lingva"
14 | }
15 | }
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/i18n/locales/nb_NO.json:
--------------------------------------------------------------------------------
1 | {
2 | "translation": {
3 | "common": {
4 | "ok": "Ok"
5 | },
6 | "services": {
7 | "translate": {
8 | "youdao": {},
9 | "deepl": {},
10 | "openai": {},
11 | "alibaba": {},
12 | "chatglm": {},
13 | "geminipro_polish": {},
14 | "transmart": {},
15 | "caiyun": {},
16 | "baidu_field": {},
17 | "cambridge_dict": {},
18 | "geminipro": {},
19 | "openai_custom": {},
20 | "baidu": {},
21 | "niutrans": {},
22 | "tencent": {},
23 | "geminipro_custom": {},
24 | "volcengine": {},
25 | "google": {},
26 | "yandex": {},
27 | "bing_dict": {},
28 | "chatglm_custom": {},
29 | "chatglm_polish": {},
30 | "chatglm_summary": {},
31 | "openai_polish": {},
32 | "geminipro_summary": {},
33 | "lingva": {
34 | "title": "Lingva"
35 | },
36 | "bing": {},
37 | "openai_summary": {}
38 | },
39 | "recognize": {
40 | "iflytek_latex_ocr": {},
41 | "baidu_ocr": {},
42 | "simple_latex_ocr": {},
43 | "volcengine_ocr": {},
44 | "iflytek_intsig_ocr": {},
45 | "baidu_accurate_ocr": {},
46 | "baidu_img_ocr": {},
47 | "volcengine_multi_lang_ocr": {},
48 | "tencent_ocr": {},
49 | "tencent_accurate_ocr": {},
50 | "iflytek_ocr": {},
51 | "qrcode": {},
52 | "tesseract": {},
53 | "tencent_img_ocr": {},
54 | "system": {}
55 | },
56 | "collection": {
57 | "anki": {},
58 | "eudic": {}
59 | },
60 | "tts": {
61 | "lingva_tts": {}
62 | }
63 | },
64 | "languages": {},
65 | "config": {
66 | "general": {
67 | "proxy": {},
68 | "event": {},
69 | "theme": {},
70 | "font_size": {}
71 | },
72 | "translate": {
73 | "font_size": {}
74 | },
75 | "backup": {},
76 | "recognize": {},
77 | "hotkey": {},
78 | "service": {},
79 | "about": {},
80 | "history": {}
81 | },
82 | "translate": {},
83 | "updater": {},
84 | "recognize": {}
85 | }
86 | }
--------------------------------------------------------------------------------
/src/i18n/locales/nn_NO.json:
--------------------------------------------------------------------------------
1 | {
2 | "translation": {
3 | "common": {
4 | "ok": "Ok"
5 | },
6 | "services": {
7 | "translate": {
8 | "youdao": {},
9 | "deepl": {},
10 | "openai": {},
11 | "alibaba": {},
12 | "chatglm": {},
13 | "geminipro_polish": {},
14 | "transmart": {},
15 | "caiyun": {},
16 | "baidu_field": {},
17 | "cambridge_dict": {},
18 | "geminipro": {},
19 | "openai_custom": {},
20 | "baidu": {},
21 | "niutrans": {},
22 | "tencent": {},
23 | "geminipro_custom": {},
24 | "volcengine": {},
25 | "google": {},
26 | "yandex": {},
27 | "bing_dict": {},
28 | "chatglm_custom": {},
29 | "chatglm_polish": {},
30 | "chatglm_summary": {},
31 | "openai_polish": {},
32 | "geminipro_summary": {},
33 | "lingva": {
34 | "title": "Lingva"
35 | },
36 | "bing": {},
37 | "openai_summary": {}
38 | },
39 | "recognize": {
40 | "iflytek_latex_ocr": {},
41 | "baidu_ocr": {},
42 | "simple_latex_ocr": {},
43 | "volcengine_ocr": {},
44 | "iflytek_intsig_ocr": {},
45 | "baidu_accurate_ocr": {},
46 | "baidu_img_ocr": {},
47 | "volcengine_multi_lang_ocr": {},
48 | "tencent_ocr": {},
49 | "tencent_accurate_ocr": {},
50 | "iflytek_ocr": {},
51 | "qrcode": {},
52 | "tesseract": {},
53 | "tencent_img_ocr": {},
54 | "system": {}
55 | },
56 | "collection": {
57 | "anki": {},
58 | "eudic": {}
59 | },
60 | "tts": {
61 | "lingva_tts": {}
62 | }
63 | },
64 | "languages": {},
65 | "config": {
66 | "general": {
67 | "proxy": {},
68 | "event": {},
69 | "theme": {},
70 | "font_size": {}
71 | },
72 | "translate": {
73 | "font_size": {}
74 | },
75 | "backup": {},
76 | "recognize": {},
77 | "hotkey": {},
78 | "service": {},
79 | "about": {},
80 | "history": {}
81 | },
82 | "translate": {},
83 | "updater": {},
84 | "recognize": {}
85 | }
86 | }
--------------------------------------------------------------------------------
/src/i18n/locales/tk_TM.json:
--------------------------------------------------------------------------------
1 | {
2 | "translation": {
3 | "config": {
4 | "general": {
5 | "default_font": "Bellenen",
6 | "theme": {
7 | "system": "Ulgam",
8 | "dark": "Garañky",
9 | "light": "Ýagty"
10 | },
11 | "font_size": {
12 | "14": "Kiçi (14px)",
13 | "12": "Örän kiçi (12px)",
14 | "10": "Goşmaça kiçi (10px)",
15 | "title": "Şrift ölçegi",
16 | "16": "Standart (16px)",
17 | "18": "Uly (18px)",
18 | "20": "Örän uly (20px)",
19 | "24": "Goşmaça Uly (24px)"
20 | },
21 | "transparent": "Transparent Täsiri",
22 | "proxy": {
23 | "password": "Parol",
24 | "title": "Proksy",
25 | "username": "Ulanyjy ady",
26 | "no_proxy": "Proksi ýok",
27 | "host": "Host",
28 | "port": "Port"
29 | },
30 | "title": "Umumy Sazlamalar",
31 | "label": "Umumy",
32 | "auto_start": "Awto Başlangyç",
33 | "check_update": "Täzelenmäni Barlaň",
34 | "server_port": "Serwer porty",
35 | "app_language": "Dili",
36 | "app_theme": "Tema",
37 | "app_font": "Şrift",
38 | "app_fallback_font": "Yza gaýdýan şrift",
39 | "dev_mode": "Developer mody",
40 | "server_port_change": "Serwer porty üýtgedildi, üýtgeşmeleriň güýje girmegi üçin programmany täzeden açyň"
41 | }
42 | },
43 | "languages": {
44 | "ar": "Arapça",
45 | "hi": "Hindi Dili",
46 | "mn_mo": "Mongol",
47 | "pt_br": "Portugaliýa (Braziliýa)",
48 | "auto": "Awto Gözläň",
49 | "zh_cn": "Ýönekeýleşdirilen Hytaý",
50 | "zh_tw": "Adaty Hytaý",
51 | "en": "Iñlis dili",
52 | "ja": "Ýapon dili",
53 | "ko": "Koreýs dili",
54 | "fr": "Fransuz",
55 | "es": "Ispan",
56 | "ru": "Rus",
57 | "de": "Nemes",
58 | "it": "Italýan",
59 | "tr": "Türk",
60 | "pt_pt": "Portugaliýa",
61 | "mn_cy": "Mongol (kiril)",
62 | "km": "Khmer",
63 | "nb_no": "Norwegiýaly Bokmål",
64 | "nn_no": "Norwegiýaly Nynorsk",
65 | "fa": "Pars",
66 | "sv": "Şwesiýa",
67 | "pl": "Polýak",
68 | "nl": "Gollandiýa",
69 | "uk": "Ukrainiýa",
70 | "he": "Hebrewýçe",
71 | "vi": "Wýetnam",
72 | "th": "Taý Dili",
73 | "id": "Indoneziýaly",
74 | "ms": "Malaý"
75 | },
76 | "common": {
77 | "ok": "Bolýa",
78 | "cancel": "Ýatyr",
79 | "save": "Saklaň",
80 | "write_clipboard": "Ýat paneline ýazyň",
81 | "plugin": "Plugin",
82 | "coming": "Tiz ýakynda…",
83 | "clear": "Arassala"
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/i18n/locales/tr_TR.json:
--------------------------------------------------------------------------------
1 | {
2 | "translation": {
3 | "common": {
4 | "ok": "Ok"
5 | },
6 | "services": {
7 | "translate": {
8 | "youdao": {},
9 | "deepl": {},
10 | "openai": {},
11 | "alibaba": {},
12 | "chatglm": {},
13 | "geminipro_polish": {},
14 | "transmart": {},
15 | "caiyun": {},
16 | "baidu_field": {},
17 | "cambridge_dict": {},
18 | "geminipro": {},
19 | "openai_custom": {},
20 | "baidu": {},
21 | "niutrans": {},
22 | "tencent": {},
23 | "geminipro_custom": {},
24 | "volcengine": {},
25 | "google": {},
26 | "yandex": {},
27 | "bing_dict": {},
28 | "chatglm_custom": {},
29 | "chatglm_polish": {},
30 | "chatglm_summary": {},
31 | "openai_polish": {},
32 | "geminipro_summary": {},
33 | "lingva": {
34 | "title": "Lingva"
35 | },
36 | "bing": {},
37 | "openai_summary": {}
38 | },
39 | "recognize": {
40 | "iflytek_latex_ocr": {},
41 | "baidu_ocr": {},
42 | "simple_latex_ocr": {},
43 | "volcengine_ocr": {},
44 | "iflytek_intsig_ocr": {},
45 | "baidu_accurate_ocr": {},
46 | "baidu_img_ocr": {},
47 | "volcengine_multi_lang_ocr": {},
48 | "tencent_ocr": {},
49 | "tencent_accurate_ocr": {},
50 | "iflytek_ocr": {},
51 | "qrcode": {},
52 | "tesseract": {},
53 | "tencent_img_ocr": {},
54 | "system": {}
55 | },
56 | "collection": {
57 | "anki": {},
58 | "eudic": {}
59 | },
60 | "tts": {
61 | "lingva_tts": {}
62 | }
63 | },
64 | "languages": {},
65 | "config": {
66 | "general": {
67 | "proxy": {},
68 | "event": {},
69 | "theme": {},
70 | "font_size": {}
71 | },
72 | "translate": {
73 | "font_size": {}
74 | },
75 | "backup": {},
76 | "recognize": {},
77 | "hotkey": {},
78 | "service": {},
79 | "about": {},
80 | "history": {}
81 | },
82 | "translate": {},
83 | "updater": {},
84 | "recognize": {}
85 | }
86 | }
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import { ThemeProvider as NextThemesProvider } from 'next-themes';
2 | import { appWindow } from '@tauri-apps/api/window';
3 | import { NextUIProvider } from '@nextui-org/react';
4 | import ReactDOM from 'react-dom/client';
5 | import React from 'react';
6 |
7 | import { initStore } from './utils/store';
8 | import { initEnv } from './utils/env';
9 | import App from './App';
10 |
11 | if (import.meta.env.PROD) {
12 | document.addEventListener('contextmenu', (e) => {
13 | e.preventDefault();
14 | });
15 | }
16 |
17 | initStore().then(async () => {
18 | await initEnv();
19 | const rootElement = document.getElementById('root');
20 | const root = ReactDOM.createRoot(rootElement);
21 | root.render(
22 |
23 |
24 |
25 |
26 |
27 | );
28 | });
29 |
--------------------------------------------------------------------------------
/src/services/collection/anki/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'anki',
3 | icon: 'logo/anki.svg',
4 | };
5 |
--------------------------------------------------------------------------------
/src/services/collection/eudic/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch, Body } from '@tauri-apps/api/http';
2 |
3 | export async function collection(source, target, options = {}) {
4 | const { config } = options;
5 | const name = config['name'] ?? 'pot';
6 | const token = config['token'] ?? '';
7 |
8 | let categoryId = await checkCategory(name, token);
9 | return await addWordToCategory(categoryId, source, token);
10 | }
11 |
12 | async function checkCategory(name, token) {
13 | let res = await fetch('https://api.frdic.com/api/open/v1/studylist/category', {
14 | method: 'GET',
15 | query: {
16 | language: 'en',
17 | },
18 | headers: {
19 | 'Content-Type': 'application/json',
20 | Authorization: token,
21 | },
22 | });
23 |
24 | let result = res.data;
25 | if (result.data) {
26 | for (let i of result.data) {
27 | if (i.name === name) {
28 | return i.id;
29 | }
30 | }
31 |
32 | let res1 = await fetch('https://api.frdic.com/api/open/v1/studylist/category', {
33 | method: 'POST',
34 | headers: {
35 | 'Content-Type': 'application/json',
36 | Authorization: token,
37 | },
38 | body: Body.json({
39 | language: 'en',
40 | name: name,
41 | }),
42 | });
43 | let result1 = res1.data;
44 | if (result1.data) {
45 | return result1.data.id;
46 | } else {
47 | throw 'Create Category Failed';
48 | }
49 | } else {
50 | throw 'Get Category Failed';
51 | }
52 | }
53 |
54 | async function addWordToCategory(id, word, token) {
55 | let res = await fetch('https://api.frdic.com/api/open/v1/studylist/words', {
56 | method: 'POST',
57 | headers: {
58 | 'Content-Type': 'application/json',
59 | Authorization: token,
60 | },
61 | body: Body.json({
62 | id: id,
63 | language: 'en',
64 | words: [word],
65 | }),
66 | });
67 | let result = res.data;
68 | return result.message;
69 | }
70 |
71 | export * from './Config';
72 | export * from './info';
73 |
--------------------------------------------------------------------------------
/src/services/collection/eudic/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'eudic',
3 | icon: 'logo/eudic.png',
4 | };
5 |
--------------------------------------------------------------------------------
/src/services/collection/index.jsx:
--------------------------------------------------------------------------------
1 | import * as _anki from './anki';
2 | import * as _eudic from './eudic';
3 |
4 | export const anki = _anki;
5 | export const eudic = _eudic;
6 |
--------------------------------------------------------------------------------
/src/services/recognize/baidu/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch, Body } from '@tauri-apps/api/http';
2 |
3 | export async function recognize(base64, language, options = {}) {
4 | const { config } = options;
5 |
6 | const { client_id, client_secret } = config;
7 |
8 | const url = 'https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic';
9 | const token_url = 'https://aip.baidubce.com/oauth/2.0/token';
10 |
11 | const token_res = await fetch(token_url, {
12 | method: 'POST',
13 | query: {
14 | grant_type: 'client_credentials',
15 | client_id,
16 | client_secret,
17 | },
18 | headers: {
19 | 'Content-Type': 'application/json',
20 | Accept: 'application/json',
21 | },
22 | });
23 | if (token_res.ok) {
24 | if (token_res.data.access_token) {
25 | let token = token_res.data.access_token;
26 |
27 | const res = await fetch(url, {
28 | method: 'POST',
29 | headers: {
30 | 'Content-Type': 'application/x-www-form-urlencoded',
31 | },
32 | query: {
33 | access_token: token,
34 | },
35 | body: Body.form({
36 | language_type: language,
37 | detect_direction: 'false',
38 | image: base64,
39 | }),
40 | });
41 | if (res.ok) {
42 | let result = res.data;
43 | if (result['words_result']) {
44 | let target = '';
45 | for (let i of result['words_result']) {
46 | target += i['words'] + '\n';
47 | }
48 | return target.trim();
49 | } else {
50 | throw JSON.stringify(result);
51 | }
52 | } else {
53 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
54 | }
55 | } else {
56 | throw 'Get Access Token Failed!';
57 | }
58 | } else {
59 | throw `Http Request Error\nHttp Status: ${token_res.status}\n${JSON.stringify(token_res.data)}`;
60 | }
61 | }
62 |
63 | export * from './Config';
64 | export * from './info';
65 |
--------------------------------------------------------------------------------
/src/services/recognize/baidu/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'baidu_ocr',
3 | icon: 'logo/baidu.svg',
4 | };
5 |
6 | export enum Language {
7 | auto = 'CHN_ENG',
8 | zh_cn = 'CHN_ENG',
9 | zh_tw = 'CHN_ENG',
10 | en = 'ENG',
11 | yue = 'CHN_ENG',
12 | ja = 'JAP',
13 | ko = 'KOR',
14 | fr = 'FRE',
15 | es = 'SPA',
16 | ru = 'RUS',
17 | de = 'GER',
18 | it = 'ITA',
19 | pt_pt = 'POR',
20 | pt_br = 'POR',
21 | }
22 |
--------------------------------------------------------------------------------
/src/services/recognize/baidu_accurate/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch, Body } from '@tauri-apps/api/http';
2 |
3 | export async function recognize(base64, language, options = {}) {
4 | const { config } = options;
5 |
6 | const { client_id, client_secret } = config;
7 |
8 | const url = 'https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic';
9 | const token_url = 'https://aip.baidubce.com/oauth/2.0/token';
10 |
11 | const token_res = await fetch(token_url, {
12 | method: 'POST',
13 | query: {
14 | grant_type: 'client_credentials',
15 | client_id,
16 | client_secret,
17 | },
18 | headers: {
19 | 'Content-Type': 'application/json',
20 | Accept: 'application/json',
21 | },
22 | });
23 | if (token_res.ok) {
24 | if (token_res.data.access_token) {
25 | let token = token_res.data.access_token;
26 |
27 | const res = await fetch(url, {
28 | method: 'POST',
29 | headers: {
30 | 'Content-Type': 'application/x-www-form-urlencoded',
31 | },
32 | query: {
33 | access_token: token,
34 | },
35 | body: Body.form({
36 | language_type: language,
37 | detect_direction: 'false',
38 | image: base64,
39 | }),
40 | });
41 | if (res.ok) {
42 | let result = res.data;
43 | if (result['words_result']) {
44 | let target = '';
45 | for (let i of result['words_result']) {
46 | target += i['words'] + '\n';
47 | }
48 | return target.trim();
49 | } else {
50 | throw JSON.stringify(result);
51 | }
52 | } else {
53 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
54 | }
55 | } else {
56 | throw 'Get Access Token Failed!';
57 | }
58 | } else {
59 | throw `Http Request Error\nHttp Status: ${token_res.status}\n${JSON.stringify(token_res.data)}`;
60 | }
61 | }
62 |
63 | export * from './Config';
64 | export * from './info';
65 |
--------------------------------------------------------------------------------
/src/services/recognize/baidu_accurate/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'baidu_accurate_ocr',
3 | icon: 'logo/baidu.svg',
4 | };
5 |
6 | export enum Language {
7 | auto = 'auto_detect',
8 | zh_cn = 'CHN_ENG',
9 | zh_tw = 'CHN_ENG',
10 | en = 'ENG',
11 | yue = 'CHN_ENG',
12 | ja = 'JAP',
13 | ko = 'KOR',
14 | fr = 'FRE',
15 | es = 'SPA',
16 | ru = 'RUS',
17 | de = 'GER',
18 | it = 'ITA',
19 | tr = 'TUR',
20 | pt_pt = 'POR',
21 | pt_br = 'POR',
22 | vi = 'VIE',
23 | id = 'IND',
24 | th = 'THA',
25 | ms = 'MAL',
26 | ar = 'ARA',
27 | hi = 'HIN',
28 | }
29 |
--------------------------------------------------------------------------------
/src/services/recognize/baidu_img/index.jsx:
--------------------------------------------------------------------------------
1 | import { readBinaryFile, BaseDirectory } from '@tauri-apps/api/fs';
2 | import { fetch, Body } from '@tauri-apps/api/http';
3 | import { nanoid } from 'nanoid';
4 | import md5 from 'md5';
5 |
6 | export async function recognize(base64, language, options = {}) {
7 | const { config } = options;
8 |
9 | const { appid, secret } = config;
10 |
11 | const url = 'https://fanyi-api.baidu.com/api/trans/sdk/picture';
12 |
13 | const salt = nanoid();
14 | if (appid === '' || secret === '') {
15 | throw 'Please configure appid and secret';
16 | }
17 |
18 | let file = await readBinaryFile('pot_screenshot_cut.png', { dir: BaseDirectory.AppCache });
19 | const str = appid + md5(file) + salt + 'APICUIDmac' + secret;
20 | const sign = md5(str);
21 |
22 | let res = await fetch(url, {
23 | method: 'POST',
24 | headers: {
25 | 'Content-Type': 'multipart/form-data',
26 | },
27 | body: Body.form({
28 | image: {
29 | file: file,
30 | mime: 'image/png',
31 | fileName: 'pot_screenshot_cut.png',
32 | },
33 | from: 'auto',
34 | to: language === 'auto' ? 'zh' : language,
35 | appid: appid,
36 | salt: salt,
37 | cuid: 'APICUID',
38 | mac: 'mac',
39 | version: '3',
40 | sign: sign,
41 | }),
42 | });
43 |
44 | if (res.ok) {
45 | let result = res.data;
46 | if (result['data'] && result['data']['sumSrc'] && result['data']['sumDst']) {
47 | if (language === 'auto') {
48 | return result['data']['sumSrc'].trim();
49 | } else {
50 | return result['data']['sumDst'].trim();
51 | }
52 | } else {
53 | throw JSON.stringify(result);
54 | }
55 | } else {
56 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
57 | }
58 | }
59 |
60 | export * from './Config';
61 | export * from './info';
62 |
--------------------------------------------------------------------------------
/src/services/recognize/baidu_img/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'baidu_img_ocr',
3 | icon: 'logo/baidu.svg',
4 | };
5 |
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh',
9 | zh_tw = 'cht',
10 | yue = 'yue',
11 | en = 'en',
12 | ja = 'jp',
13 | ko = 'kor',
14 | fr = 'fra',
15 | es = 'spa',
16 | ru = 'ru',
17 | de = 'de',
18 | it = 'it',
19 | tr = 'tr',
20 | pt_pt = 'pt',
21 | pt_br = 'pot',
22 | vi = 'vie',
23 | id = 'id',
24 | th = 'th',
25 | ms = 'may',
26 | ar = 'ar',
27 | hi = 'hi',
28 | }
29 |
--------------------------------------------------------------------------------
/src/services/recognize/iflytek/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'iflytek_ocr',
3 | icon: 'logo/iflytek.png',
4 | };
5 |
6 | export enum Language {
7 | auto = 'zh_cn',
8 | zh_cn = 'zh_cn',
9 | zh_tw = 'zh_tw',
10 | en = 'en',
11 | }
12 |
--------------------------------------------------------------------------------
/src/services/recognize/iflytek_intsig/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch } from '@tauri-apps/api/http';
2 | import CryptoJS from 'crypto-js';
3 | import { iflytek_auth } from '../iflytek';
4 |
5 | export async function recognize(base64, language, options = {}) {
6 | const { config } = options;
7 |
8 | const { appid, apisecret, apikey } = config;
9 |
10 | const host = 'api.xf-yun.com';
11 | const today = new Date();
12 | const date = today.toUTCString();
13 | const request_line = 'POST /v1/private/hh_ocr_recognize_doc HTTP/1.1';
14 |
15 | let auth = iflytek_auth(apikey, apisecret, host, date, request_line);
16 |
17 | let request_url =
18 | 'https://api.xf-yun.com/v1/private/hh_ocr_recognize_doc?' +
19 | 'authorization=' +
20 | auth +
21 | '&host=' +
22 | host +
23 | '&date=' +
24 | encodeURIComponent(date);
25 |
26 | let request_body = {
27 | header: {
28 | app_id: appid, // 在讯飞开放平台申请的appid信息
29 | status: 3, // 请求状态,取值为:3(一次传完)
30 | },
31 | parameter: {
32 | hh_ocr_recognize_doc: {
33 | recognizeDocumentRes: {
34 | encoding: 'utf8',
35 | compress: 'raw',
36 | format: 'json',
37 | },
38 | },
39 | },
40 | payload: {
41 | image: {
42 | image: base64,
43 | },
44 | },
45 | };
46 |
47 | // 发送请求
48 | let res = await fetch(request_url, {
49 | method: 'POST',
50 | headers: { 'content-type': 'application/json' },
51 | body: { type: 'Text', payload: JSON.stringify(request_body) },
52 | });
53 |
54 | // 处理结果
55 | if (!res.ok) {
56 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
57 | }
58 | let data = res['data'];
59 | if (!data) {
60 | throw `Result data not found\nResult:\n${JSON.stringify(res)}`;
61 | }
62 | let res_payload = data['payload'];
63 | if (!res_payload) {
64 | throw `Result payload not found\nResult:\n${JSON.stringify(res)}`;
65 | }
66 |
67 | let text = CryptoJS.enc.Base64.parse(res_payload['recognizeDocumentRes']['text']); // Base64解码
68 | let text_string = CryptoJS.enc.Utf8.stringify(text);
69 | let text_json = JSON.parse(text_string);
70 | let return_content = text_json['whole_text']; // 最终结果
71 | if (!return_content) {
72 | return_content = '';
73 | }
74 | return return_content.trim();
75 | }
76 |
77 | export * from './Config';
78 | export * from './info';
79 |
--------------------------------------------------------------------------------
/src/services/recognize/iflytek_intsig/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'iflytek_intsig_ocr',
3 | icon: 'logo/iflytek.png',
4 | };
5 |
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh_cn',
9 | zh_tw = 'zh_tw',
10 | en = 'en',
11 | ja = 'ja',
12 | ko = 'ko',
13 | fr = 'fr',
14 | es = 'es',
15 | ru = 'ru',
16 | de = 'de',
17 | it = 'it',
18 | tr = 'tr',
19 | pt_pt = 'pt_pt',
20 | pt_br = 'pt_br',
21 | vi = 'vi',
22 | id = 'id',
23 | th = 'th',
24 | ms = 'ms',
25 | ar = 'ar',
26 | hi = 'hi',
27 | }
28 |
--------------------------------------------------------------------------------
/src/services/recognize/iflytek_latex/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch } from '@tauri-apps/api/http';
2 | import hmacSHA256 from 'crypto-js/hmac-sha256';
3 | import hashSHA256 from 'crypto-js/sha256';
4 | import Base64 from 'crypto-js/enc-base64';
5 |
6 | export async function recognize(base64, language, options = {}) {
7 | const { config } = options;
8 |
9 | const { appid, apisecret, apikey } = config;
10 |
11 | const url = 'https://rest-api.xfyun.cn/v2/itr';
12 |
13 | const body = {
14 | common: {
15 | app_id: appid,
16 | },
17 | business: {
18 | ent: 'teach-photo-print',
19 | aue: 'raw',
20 | },
21 | data: {
22 | image: base64,
23 | },
24 | };
25 | const host = 'rest-api.xfyun.cn';
26 | const date = new Date().toUTCString();
27 | const request_line = 'POST /v2/itr HTTP/1.1';
28 | const digest = 'SHA-256=' + Base64.stringify(hashSHA256(JSON.stringify(body)));
29 | const signature_origin = `host: ${host}\ndate: ${date}\n${request_line}\ndigest: ${digest}`;
30 | const signature_sha = hmacSHA256(signature_origin, apisecret);
31 | const signature = Base64.stringify(signature_sha);
32 | const authorization = `api_key="${apikey}", algorithm="hmac-sha256", headers="host date request-line digest", signature="${signature}"`;
33 | const headers = {
34 | 'Content-Type': 'application/json',
35 | Accept: 'application/json,version=1.0',
36 | Host: host,
37 | Date: date,
38 | Digest: digest,
39 | Authorization: authorization,
40 | };
41 | const res = await fetch(url, {
42 | method: 'POST',
43 | headers,
44 | body: {
45 | type: 'Text',
46 | payload: JSON.stringify(body),
47 | },
48 | });
49 | if (res.ok) {
50 | let result = res.data;
51 | if (result.data['region']) {
52 | let target = '';
53 | for (let i of result.data['region']) {
54 | target += i['recog']['content'] + '\n';
55 | }
56 | target = target.replaceAll(' ifly-latex-begin ', '');
57 | target = target.replaceAll(' ifly-latex-end ', '');
58 | return target.trim();
59 | } else {
60 | throw JSON.stringify(result);
61 | }
62 | } else {
63 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
64 | }
65 | }
66 |
67 | export * from './Config';
68 | export * from './info';
69 |
--------------------------------------------------------------------------------
/src/services/recognize/iflytek_latex/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'iflytek_latex_ocr',
3 | icon: 'logo/iflytek.png',
4 | };
5 |
6 | export enum Language {
7 | auto = 'zh_cn',
8 | zh_cn = 'zh_cn',
9 | zh_tw = 'zh_tw',
10 | en = 'en',
11 | }
12 |
--------------------------------------------------------------------------------
/src/services/recognize/index.jsx:
--------------------------------------------------------------------------------
1 | import * as _system from './system';
2 | import * as _tesseract from './tesseract';
3 | import * as _baidu_ocr from './baidu';
4 | import * as _baidu_accurate_ocr from './baidu_accurate';
5 | import * as _baidu_img_ocr from './baidu_img';
6 | import * as _iflytek_ocr from './iflytek';
7 | import * as _iflytek_intsig_ocr from './iflytek_intsig';
8 | import * as _iflytek_latex_ocr from './iflytek_latex';
9 | import * as _simple_latex_ocr from './simple_latex';
10 | import * as _tencent_ocr from './tencent';
11 | import * as _tencent_accurate_ocr from './tencent_accurate';
12 | import * as _tencent_img_ocr from './tencent_img';
13 | import * as _volcengine_ocr from './volcengine';
14 | import * as _volcengine_multi_lang_ocr from './volcengine_multi_lang';
15 | import * as _qrcode from './qrcode';
16 |
17 | export const system = _system;
18 | export const tesseract = _tesseract;
19 | export const baidu_ocr = _baidu_ocr;
20 | export const baidu_accurate_ocr = _baidu_accurate_ocr;
21 | export const baidu_img_ocr = _baidu_img_ocr;
22 | export const iflytek_ocr = _iflytek_ocr;
23 | export const iflytek_intsig_ocr = _iflytek_intsig_ocr;
24 | export const iflytek_latex_ocr = _iflytek_latex_ocr;
25 | export const simple_latex_ocr = _simple_latex_ocr;
26 | export const tencent_ocr = _tencent_ocr;
27 | export const tencent_accurate_ocr = _tencent_accurate_ocr;
28 | export const tencent_img_ocr = _tencent_img_ocr;
29 | export const volcengine_ocr = _volcengine_ocr;
30 | export const volcengine_multi_lang_ocr = _volcengine_multi_lang_ocr;
31 | export const qrcode = _qrcode;
32 |
--------------------------------------------------------------------------------
/src/services/recognize/qrcode/Config.jsx:
--------------------------------------------------------------------------------
1 | import { useTranslation } from 'react-i18next';
2 | import { Button } from '@nextui-org/react';
3 | import React from 'react';
4 |
5 | export function Config(props) {
6 | const { updateServiceList, onClose } = props;
7 | const { t } = useTranslation();
8 |
9 | return (
10 | <>
11 | {t('services.no_need')}
12 |
13 |
22 |
23 | >
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/src/services/recognize/qrcode/index.jsx:
--------------------------------------------------------------------------------
1 | import jsQR from 'jsqr';
2 |
3 | export async function recognize(base64, language, options = {}) {
4 | let canvas = document.createElement('CANVAS');
5 | let ctx = canvas.getContext('2d');
6 | base64 = 'data:image/png;base64,' + base64;
7 | let img = new Image();
8 | img.src = base64;
9 | let imgdata = await new Promise((resolve, reject) => {
10 | img.onload = () => {
11 | img.crossOrigin = 'anonymous';
12 | canvas.height = img.height;
13 | canvas.width = img.width;
14 | ctx.drawImage(img, 0, 0);
15 | const height = img.height;
16 | const width = img.width;
17 | const data = ctx.getImageData(0, 0, width, height);
18 | if (height !== 0 && width !== 0) {
19 | resolve({ data, height, width });
20 | }
21 | };
22 | });
23 |
24 | const code = jsQR(imgdata.data.data, imgdata.width, imgdata.height);
25 | if (code) {
26 | return code.data;
27 | } else {
28 | throw 'QR code not recognized or multiple QR codes exist';
29 | }
30 | }
31 |
32 | export * from './Config';
33 | export * from './info';
34 |
--------------------------------------------------------------------------------
/src/services/recognize/qrcode/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'qrcode',
3 | icon: 'logo/qrcode.svg',
4 | };
5 |
6 | export enum Language {
7 | auto = '',
8 | zh_cn = '',
9 | zh_tw = '',
10 | en = '',
11 | ja = '',
12 | ko = '',
13 | fr = '',
14 | es = '',
15 | ru = '',
16 | de = '',
17 | it = '',
18 | tr = '',
19 | pt_pt = '',
20 | pt_br = '',
21 | vi = '',
22 | id = '',
23 | th = '',
24 | ms = '',
25 | ar = '',
26 | hi = '',
27 | }
28 |
--------------------------------------------------------------------------------
/src/services/recognize/simple_latex/index.jsx:
--------------------------------------------------------------------------------
1 | import { readBinaryFile, BaseDirectory } from '@tauri-apps/api/fs';
2 | import { fetch, Body } from '@tauri-apps/api/http';
3 |
4 | export async function recognize(base64, language, options = {}) {
5 | const { config } = options;
6 |
7 | const { token } = config;
8 |
9 | const url = 'https://server.simpletex.cn/api/latex_ocr/v2';
10 |
11 | let file = await readBinaryFile('pot_screenshot_cut.png', { dir: BaseDirectory.AppCache });
12 |
13 | const res = await fetch(url, {
14 | method: 'POST',
15 | headers: {
16 | token,
17 | 'content-type': 'multipart/form-data',
18 | },
19 | body: Body.form({
20 | file: {
21 | file: file,
22 | fileName: 'pot_screenshot_cut.png',
23 | },
24 | }),
25 | });
26 | if (res.ok) {
27 | let result = res.data;
28 | if (result['res'] && result['res']['latex']) {
29 | return result['res']['latex'].trim();
30 | } else {
31 | throw JSON.stringify(result);
32 | }
33 | } else {
34 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
35 | }
36 | }
37 |
38 | export * from './Config';
39 | export * from './info';
40 |
--------------------------------------------------------------------------------
/src/services/recognize/simple_latex/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'simple_latex_ocr',
3 | icon: 'logo/simple_latex.png',
4 | };
5 |
6 | export enum Language {
7 | auto = 'zh_cn',
8 | zh_cn = 'zh_cn',
9 | zh_tw = 'zh_tw',
10 | en = 'en',
11 | }
12 |
--------------------------------------------------------------------------------
/src/services/recognize/system/Config.jsx:
--------------------------------------------------------------------------------
1 | import { useTranslation } from 'react-i18next';
2 | import { Button } from '@nextui-org/react';
3 | import React from 'react';
4 |
5 | export function Config(props) {
6 | const { updateServiceList, onClose } = props;
7 | const { t } = useTranslation();
8 |
9 | return (
10 | <>
11 | {t('services.no_need')}
12 |
13 |
22 |
23 | >
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/src/services/recognize/system/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'system',
3 | icon: `system`,
4 | };
5 |
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh_cn',
9 | zh_tw = 'zh_tw',
10 | en = 'en',
11 | ja = 'ja',
12 | ko = 'ko',
13 | fr = 'fr',
14 | es = 'es',
15 | ru = 'ru',
16 | de = 'de',
17 | it = 'it',
18 | tr = 'tr',
19 | pt_pt = 'pt_pt',
20 | pt_br = 'pt_br',
21 | vi = 'vi',
22 | id = 'id',
23 | th = 'th',
24 | ms = 'ms',
25 | ar = 'ar',
26 | hi = 'hi',
27 | uk = 'uk',
28 | he = 'he',
29 | }
30 |
--------------------------------------------------------------------------------
/src/services/recognize/tencent/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'tencent_ocr',
3 | icon: 'logo/tencent_cloud.png',
4 | };
5 |
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh',
9 | zh_tw = 'zh_rare',
10 | yue = 'zh_rare',
11 | en = 'auto',
12 | ja = 'jap',
13 | ko = 'kor',
14 | fr = 'fre',
15 | es = 'spa',
16 | ru = 'rus',
17 | de = 'ger',
18 | it = 'ita',
19 | pt_pt = 'por',
20 | pt_br = 'por',
21 | vi = 'vie',
22 | th = 'tha',
23 | ms = 'may',
24 | ar = 'ara',
25 | hi = 'hi',
26 | }
27 |
--------------------------------------------------------------------------------
/src/services/recognize/tencent_accurate/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'tencent_accurate_ocr',
3 | icon: 'logo/tencent_cloud.png',
4 | };
5 |
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh',
9 | zh_tw = 'zh_rare',
10 | yue = 'zh_rare',
11 | en = 'auto',
12 | }
13 |
--------------------------------------------------------------------------------
/src/services/recognize/tencent_img/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'tencent_img_ocr',
3 | icon: 'logo/tencent_cloud.png',
4 | };
5 |
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh',
9 | zh_tw = 'zh-TW',
10 | yue = 'zh-TW',
11 | en = 'en',
12 | ja = 'ja',
13 | ko = 'ko',
14 | fr = 'fr',
15 | es = 'es',
16 | ru = 'ru',
17 | de = 'de',
18 | it = 'it',
19 | pt_pt = 'pt',
20 | pt_br = 'pt',
21 | vi = 'vi',
22 | th = 'th',
23 | ms = 'ms',
24 | }
25 |
--------------------------------------------------------------------------------
/src/services/recognize/tesseract/Config.jsx:
--------------------------------------------------------------------------------
1 | import { useTranslation } from 'react-i18next';
2 | import { Button } from '@nextui-org/react';
3 | import React from 'react';
4 |
5 | export function Config(props) {
6 | const { updateServiceList, onClose } = props;
7 | const { t } = useTranslation();
8 |
9 | return (
10 | <>
11 | {t('services.no_need')}
12 |
13 |
22 |
23 | >
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/src/services/recognize/tesseract/index.jsx:
--------------------------------------------------------------------------------
1 | import Tesseract from 'tesseract.js';
2 | import { Language } from './info';
3 |
4 | export async function recognize(base64, language) {
5 | const {
6 | data: { text },
7 | } = await Tesseract.recognize('data:image/png;base64,' + base64, language, {
8 | workerPath: '/worker.min.js',
9 | corePath: '/tesseract-core-simd-lstm.wasm.js',
10 | langPath: 'https://pub-f6afb74f13c64cd89561b4714dca1c27.r2.dev',
11 | });
12 | if (language === Language.zh_cn || language === Language.zh_tw) {
13 | return text.replaceAll(' ', '').trim();
14 | } else {
15 | return text.trim();
16 | }
17 | }
18 |
19 | export * from './Config';
20 | export * from './info';
21 |
--------------------------------------------------------------------------------
/src/services/recognize/tesseract/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'tesseract',
3 | icon: 'logo/tesseract.png',
4 | };
5 |
6 | export enum Language {
7 | auto = 'eng',
8 | zh_cn = 'chi_sim',
9 | zh_tw = 'chi_tra',
10 | en = 'eng',
11 | yue = 'chi_sim',
12 | ja = 'jpn',
13 | ko = 'kor',
14 | fr = 'fra',
15 | es = 'spa',
16 | ru = 'rus',
17 | de = 'deu',
18 | it = 'ita',
19 | tr = 'tur',
20 | pt_pt = 'por',
21 | pt_br = 'por',
22 | vi = 'vie',
23 | id = 'ind',
24 | th = 'tha',
25 | ms = 'msa',
26 | ar = 'ara',
27 | hi = 'hin',
28 | uk = 'ukr',
29 | he = 'heb',
30 | }
31 |
--------------------------------------------------------------------------------
/src/services/recognize/volcengine/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'volcengine_ocr',
3 | icon: 'logo/volcengine.svg',
4 | };
5 |
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh_cn',
9 | zh_tw = 'zh_tw',
10 | yue = 'yue',
11 | en = 'en',
12 | }
13 |
--------------------------------------------------------------------------------
/src/services/recognize/volcengine_multi_lang/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'volcengine_multi_lang_ocr',
3 | icon: 'logo/volcengine.svg',
4 | };
5 |
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh_cn',
9 | zh_tw = 'zh_tw',
10 | en = 'en',
11 | ja = 'ja',
12 | ko = 'ko',
13 | fr = 'fr',
14 | es = 'es',
15 | ru = 'ru',
16 | de = 'de',
17 | it = 'it',
18 | tr = 'tr',
19 | pt_pt = 'pt_pt',
20 | pt_br = 'pt_br',
21 | vi = 'vi',
22 | id = 'id',
23 | th = 'th',
24 | ms = 'ms',
25 | ar = 'ar',
26 | hi = 'hi',
27 | }
28 |
--------------------------------------------------------------------------------
/src/services/translate/alibaba/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch } from '@tauri-apps/api/http';
2 | import HmacSHA1 from 'crypto-js/hmac-sha1';
3 | import base64 from 'crypto-js/enc-base64';
4 |
5 | export async function translate(text, from, to, options = {}) {
6 | const { config } = options;
7 |
8 | const { accesskey_id, accesskey_secret } = config;
9 |
10 | function getRandomNumber() {
11 | const rand = Math.floor(Math.random() * 99999) + 100000;
12 | return rand * 1000;
13 | }
14 | if (accesskey_id === '' || accesskey_secret === '') {
15 | throw 'Please configure AccessKey ID and AccessKey Secret';
16 | }
17 |
18 | let today = new Date();
19 | let timestamp = today.toISOString().replaceAll(/\.[0-9]*/g, '');
20 | let endpoint = 'http://mt.cn-hangzhou.aliyuncs.com/';
21 | let url_path = 'api/translate/web/general';
22 |
23 | let query = `AccessKeyId=${accesskey_id}&Action=TranslateGeneral&Format=JSON&FormatType=text&Scene=general&SignatureMethod=HMAC-SHA1&SignatureNonce=${getRandomNumber()}&SignatureVersion=1.0&SourceLanguage=${from}&SourceText=${encodeURIComponent(
24 | text
25 | )}&TargetLanguage=${to}&Timestamp=${encodeURIComponent(timestamp)}&Version=2018-10-12`;
26 |
27 | let CanonicalizedQueryString = endpoint + url_path + '?' + query;
28 |
29 | let stringToSign = 'GET' + '&' + encodeURIComponent('/') + '&' + encodeURIComponent(query);
30 |
31 | stringToSign = stringToSign.replaceAll('!', '%2521');
32 | stringToSign = stringToSign.replaceAll("'", '%2527');
33 | stringToSign = stringToSign.replaceAll('(', '%2528');
34 | stringToSign = stringToSign.replaceAll(')', '%2529');
35 | stringToSign = stringToSign.replaceAll('*', '%252A');
36 | stringToSign = stringToSign.replaceAll('+', '%252B');
37 | stringToSign = stringToSign.replaceAll(',', '%252C');
38 |
39 | let signature = base64.stringify(HmacSHA1(stringToSign, accesskey_secret + '&'));
40 |
41 | CanonicalizedQueryString = CanonicalizedQueryString + '&Signature=' + encodeURIComponent(signature);
42 |
43 | let res = await fetch(CanonicalizedQueryString, {
44 | method: 'GET',
45 | });
46 |
47 | if (res.ok) {
48 | let result = res.data;
49 | if (result['Code'] === '200') {
50 | return result['Data']['Translated'].trim();
51 | } else {
52 | throw JSON.stringify(result);
53 | }
54 | } else {
55 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
56 | }
57 | }
58 |
59 | export * from './Config';
60 | export * from './info';
61 |
--------------------------------------------------------------------------------
/src/services/translate/alibaba/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'alibaba',
3 | icon: 'logo/alibaba.svg',
4 | };
5 | // https://help.aliyun.com/document_detail/215387.html?spm=a2c4g.158269.0.0.ddfc4f62vEpa38
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh',
9 | zh_tw = 'zh-tw',
10 | yue = 'yue',
11 | ja = 'ja',
12 | en = 'en',
13 | ko = 'ko',
14 | fr = 'fr',
15 | es = 'es',
16 | ru = 'ru',
17 | de = 'de',
18 | it = 'it',
19 | tr = 'tr',
20 | pt_pt = 'pt',
21 | pt_br = 'pt',
22 | vi = 'vi',
23 | id = 'id',
24 | th = 'th',
25 | ms = 'ms',
26 | ar = 'ar',
27 | hi = 'hi',
28 | mn_mo = 'mn',
29 | km = 'km',
30 | nb_no = 'no',
31 | nn_no = 'no',
32 | fa = 'fa',
33 | sv = 'sv',
34 | pl = 'pl',
35 | nl = 'nl',
36 | he = 'he',
37 | }
38 |
--------------------------------------------------------------------------------
/src/services/translate/baidu/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch } from '@tauri-apps/api/http';
2 | import { nanoid } from 'nanoid';
3 | import md5 from 'md5';
4 |
5 | export async function translate(text, from, to, options = {}) {
6 | const { config } = options;
7 |
8 | const { appid, secret } = config;
9 |
10 | const url = 'https://fanyi-api.baidu.com/api/trans/vip/translate';
11 |
12 | const salt = nanoid();
13 | if (appid === '' || secret === '') {
14 | throw 'Please configure appid and secret';
15 | }
16 |
17 | const str = appid + text + salt + secret;
18 | const sign = md5(str);
19 |
20 | let res = await fetch(url, {
21 | query: {
22 | q: text,
23 | from: from,
24 | to: to,
25 | appid: appid,
26 | salt: salt,
27 | sign: sign,
28 | },
29 | });
30 | if (res.ok) {
31 | let result = res.data;
32 | let target = '';
33 |
34 | const { trans_result } = result;
35 | if (trans_result) {
36 | for (let i in trans_result) {
37 | target = target + trans_result[i]['dst'] + '\n';
38 | }
39 | return target.trim();
40 | } else {
41 | throw JSON.stringify(result);
42 | }
43 | } else {
44 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
45 | }
46 | }
47 |
48 | export * from './Config';
49 | export * from './info';
50 |
--------------------------------------------------------------------------------
/src/services/translate/baidu/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'baidu',
3 | icon: 'logo/baidu.svg',
4 | };
5 | // https://fanyi-api.baidu.com/product/113
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh',
9 | zh_tw = 'cht',
10 | yue = 'yue',
11 | en = 'en',
12 | ja = 'jp',
13 | ko = 'kor',
14 | fr = 'fra',
15 | es = 'spa',
16 | ru = 'ru',
17 | de = 'de',
18 | it = 'it',
19 | tr = 'tr',
20 | pt_pt = 'pt',
21 | pt_br = 'pot',
22 | vi = 'vie',
23 | id = 'id',
24 | th = 'th',
25 | ms = 'may',
26 | ar = 'ar',
27 | hi = 'hi',
28 | km = 'hkm',
29 | nb_no = 'nob',
30 | nn_no = 'nno',
31 | fa = 'per',
32 | sv = 'swe',
33 | pl = 'pl',
34 | nl = 'nl',
35 | uk = 'ukr',
36 | he = 'heb',
37 | }
38 |
--------------------------------------------------------------------------------
/src/services/translate/baidu_field/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch } from '@tauri-apps/api/http';
2 | import { nanoid } from 'nanoid';
3 | import md5 from 'md5';
4 |
5 | export async function translate(text, from, to, options = {}) {
6 | const { config } = options;
7 |
8 | const { appid, secret, field } = config;
9 |
10 | const url = 'https://fanyi-api.baidu.com/api/trans/vip/fieldtranslate';
11 |
12 | const salt = nanoid();
13 | if (appid === '' || secret === '') {
14 | throw 'Please configure appid and secret';
15 | }
16 |
17 | const str = appid + text + salt + field + secret;
18 | const sign = md5(str);
19 |
20 | let res = await fetch(url, {
21 | query: {
22 | q: text,
23 | from: from,
24 | to: to,
25 | appid: appid,
26 | salt: salt,
27 | sign: sign,
28 | domain: field,
29 | },
30 | });
31 |
32 | if (res.ok) {
33 | let result = res.data;
34 | let target = '';
35 |
36 | const { trans_result } = result;
37 | if (trans_result) {
38 | for (let i in trans_result) {
39 | target = target + trans_result[i]['dst'] + '\n';
40 | }
41 | return target.trim();
42 | } else {
43 | throw JSON.stringify(result);
44 | }
45 | } else {
46 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
47 | }
48 | }
49 |
50 | export * from './Config';
51 | export * from './info';
52 |
--------------------------------------------------------------------------------
/src/services/translate/baidu_field/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'baidu_field',
3 | icon: 'logo/baidu.svg',
4 | };
5 | // https://fanyi-api.baidu.com/product/113
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh',
9 | zh_tw = 'cht',
10 | yue = 'yue',
11 | en = 'en',
12 | ja = 'jp',
13 | ko = 'kor',
14 | fr = 'fra',
15 | es = 'spa',
16 | ru = 'ru',
17 | de = 'de',
18 | it = 'it',
19 | tr = 'tr',
20 | pt_pt = 'pt',
21 | pt_br = 'pot',
22 | vi = 'vie',
23 | id = 'id',
24 | th = 'th',
25 | ms = 'may',
26 | ar = 'ar',
27 | hi = 'hi',
28 | km = 'hkm',
29 | nb_no = 'nob',
30 | nn_no = 'nno',
31 | fa = 'per',
32 | sv = 'swe',
33 | pl = 'pl',
34 | nl = 'nl',
35 | uk = 'ukr',
36 | he = 'heb',
37 | }
38 |
--------------------------------------------------------------------------------
/src/services/translate/bing/Config.jsx:
--------------------------------------------------------------------------------
1 | import { useTranslation } from 'react-i18next';
2 | import { Button } from '@nextui-org/react';
3 | import React from 'react';
4 |
5 | export function Config(props) {
6 | const { updateServiceList, onClose } = props;
7 | const { t } = useTranslation();
8 |
9 | return (
10 | <>
11 | {t('services.no_need')}
12 |
13 |
23 |
24 | >
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/services/translate/bing/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch } from '@tauri-apps/api/http';
2 |
3 | export async function translate(text, from, to) {
4 | const token_url = 'https://edge.microsoft.com/translate/auth';
5 |
6 | let token = await fetch(token_url, {
7 | method: 'GET',
8 | headers: {
9 | 'User-Agent':
10 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.42',
11 | },
12 | responseType: 2,
13 | });
14 |
15 | if (token.ok) {
16 | const url = 'https://api-edge.cognitive.microsofttranslator.com/translate';
17 |
18 | let res = await fetch(url, {
19 | method: 'POST',
20 | headers: {
21 | accept: '*/*',
22 | 'accept-language': 'zh-TW,zh;q=0.9,ja;q=0.8,zh-CN;q=0.7,en-US;q=0.6,en;q=0.5',
23 | authorization: 'Bearer ' + token.data,
24 | 'cache-control': 'no-cache',
25 | 'content-type': 'application/json',
26 | pragma: 'no-cache',
27 | 'sec-ch-ua': '"Microsoft Edge";v="113", "Chromium";v="113", "Not-A.Brand";v="24"',
28 | 'sec-ch-ua-mobile': '?0',
29 | 'sec-ch-ua-platform': '"Windows"',
30 | 'sec-fetch-dest': 'empty',
31 | 'sec-fetch-mode': 'cors',
32 | 'sec-fetch-site': 'cross-site',
33 | Referer: 'https://appsumo.com/',
34 | 'Referrer-Policy': 'strict-origin-when-cross-origin',
35 | 'User-Agent':
36 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.42',
37 | },
38 | query: {
39 | from: from,
40 | to: to,
41 | 'api-version': '3.0',
42 | includeSentenceLength: 'true',
43 | },
44 | body: { type: 'Json', payload: [{ Text: text }] },
45 | });
46 |
47 | if (res.ok) {
48 | let result = res.data;
49 | if (result[0].translations) {
50 | return result[0].translations[0].text.trim();
51 | } else {
52 | throw JSON.stringify(result);
53 | }
54 | } else {
55 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
56 | }
57 | } else {
58 | throw 'Get Token Failed';
59 | }
60 | }
61 |
62 | export * from './Config';
63 | export * from './info';
64 |
--------------------------------------------------------------------------------
/src/services/translate/bing/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'bing',
3 | icon: 'logo/bing.svg',
4 | };
5 | // https://learn.microsoft.com/en-us/azure/ai-services/translator/language-support
6 | export enum Language {
7 | auto = '',
8 | zh_cn = 'zh-Hans',
9 | zh_tw = 'zh-Hant',
10 | yue = 'yue',
11 | en = 'en',
12 | ja = 'ja',
13 | ko = 'ko',
14 | fr = 'fr',
15 | es = 'es',
16 | ru = 'ru',
17 | de = 'de',
18 | it = 'it',
19 | tr = 'tr',
20 | pt_pt = 'pt-pt',
21 | pt_br = 'pt',
22 | vi = 'vi',
23 | id = 'id',
24 | th = 'th',
25 | ms = 'ms',
26 | ar = 'ar',
27 | hi = 'hi',
28 | mn_cy = 'mn-Cyrl',
29 | mn_mo = 'mn-Mong',
30 | km = 'km',
31 | nb_no = 'nb',
32 | fa = 'fa',
33 | sv = 'sv',
34 | pl = 'pl',
35 | nl = 'nl',
36 | uk = 'uk',
37 | he = 'he',
38 | }
39 |
--------------------------------------------------------------------------------
/src/services/translate/bing_dict/Config.jsx:
--------------------------------------------------------------------------------
1 | import { useTranslation } from 'react-i18next';
2 | import { Button } from '@nextui-org/react';
3 | import React from 'react';
4 |
5 | export function Config(props) {
6 | const { updateServiceList, onClose } = props;
7 | const { t } = useTranslation();
8 |
9 | return (
10 | <>
11 | {t('services.no_need')}
12 |
13 |
23 |
24 | >
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/services/translate/bing_dict/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch } from '@tauri-apps/api/http';
2 | const DISPLAY_FORMAT_DEFAULT = '发音, 快速释义, 变形';
3 |
4 | export async function translate(text, from, to) {
5 | if (from == 'auto') {
6 | if (/^[\u4e00-\u9fff]/.test(text)) {
7 | from = 'zh-cn';
8 | } else if (/^[A-Za-z]/.test(text)) {
9 | from = 'en-us';
10 | }
11 | }
12 | if (from == to) {
13 | return text;
14 | }
15 | // only supports word translation
16 | // if (text.split(/[\s,,]/).length > 1) {
17 | // return '';
18 | // }
19 |
20 | const res = await fetch(
21 | `https://www.bing.com/api/v6/dictionarywords/search?q=${text}&appid=371E7B2AF0F9B84EC491D731DF90A55719C7D209&mkt=zh-cn&pname=bingdict`
22 | );
23 | if (res.ok) {
24 | const result = res.data;
25 | const meaningGroups = result.value[0].meaningGroups;
26 | if (meaningGroups.length === 0) {
27 | throw `Words not yet included: ${text}`;
28 | }
29 | const formats = DISPLAY_FORMAT_DEFAULT.trim().split(/,\s*/);
30 | const formatGroups = meaningGroups.reduce(
31 | (acc, cur) => {
32 | const group = acc[cur.partsOfSpeech?.[0]?.description || cur.partsOfSpeech?.[0]?.name];
33 | if (Array.isArray(group)) {
34 | group.push(cur);
35 | }
36 | return acc;
37 | },
38 | formats.reduce((acc, cur) => {
39 | acc[cur] = [];
40 | return acc;
41 | }, {})
42 | );
43 | let target = { pronunciations: [], explanations: [], associations: [], sentence: [] };
44 | for (const pronunciation of formatGroups['发音']) {
45 | target.pronunciations.push({
46 | region: pronunciation.partsOfSpeech[0].name,
47 | symbol: pronunciation.meanings[0].richDefinitions[0].fragments[0].text,
48 | voice: '',
49 | });
50 | }
51 | for (const explanation of formatGroups['快速释义']) {
52 | target.explanations.push({
53 | trait: explanation.partsOfSpeech[0].name,
54 | explains: explanation.meanings[0].richDefinitions[0].fragments.map((x) => {
55 | return x.text;
56 | }),
57 | });
58 | }
59 | if (formatGroups['变形'][0]) {
60 | for (const association of formatGroups['变形'][0].meanings[0].richDefinitions[0].fragments) {
61 | target.associations.push(association.text);
62 | }
63 | }
64 | return target;
65 | } else {
66 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
67 | }
68 | }
69 |
70 | export * from './Config';
71 | export * from './info';
72 |
--------------------------------------------------------------------------------
/src/services/translate/bing_dict/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'bing_dict',
3 | icon: 'logo/bing.svg',
4 | };
5 |
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh-cn',
9 | zh_tw = 'zh-cn',
10 | en = 'en-us',
11 | }
12 |
--------------------------------------------------------------------------------
/src/services/translate/caiyun/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch, Body } from '@tauri-apps/api/http';
2 |
3 | export async function translate(text, from, to, options = {}) {
4 | const { config } = options;
5 |
6 | const { token } = config;
7 |
8 | const url = 'https://api.interpreter.caiyunai.com/v1/translator';
9 |
10 | if (token === '') {
11 | throw 'Please configure token';
12 | }
13 |
14 | const body = {
15 | source: [text],
16 | trans_type: `${from}2${to}`,
17 | request_id: 'demo',
18 | detect: true,
19 | };
20 |
21 | const headers = {
22 | 'content-type': 'application/json',
23 | 'x-authorization': 'token ' + token,
24 | };
25 |
26 | let res = await fetch(url, {
27 | method: 'POST',
28 | headers: headers,
29 | body: Body.json(body),
30 | });
31 |
32 | if (res.ok) {
33 | let result = res.data;
34 | const { target } = result;
35 | if (target[0]) {
36 | return target[0];
37 | } else {
38 | throw JSON.stringify(result.trim());
39 | }
40 | } else {
41 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
42 | }
43 | }
44 |
45 | export * from './Config';
46 | export * from './info';
47 |
--------------------------------------------------------------------------------
/src/services/translate/caiyun/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'caiyun',
3 | icon: 'logo/caiyun.svg',
4 | };
5 |
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh',
9 | zh_tw = 'zh',
10 | en = 'en',
11 | ja = 'ja',
12 | }
13 |
--------------------------------------------------------------------------------
/src/services/translate/cambridge_dict/Config.jsx:
--------------------------------------------------------------------------------
1 | import { useTranslation } from 'react-i18next';
2 | import { Button } from '@nextui-org/react';
3 | import React from 'react';
4 |
5 | export function Config(props) {
6 | const { updateServiceList, onClose } = props;
7 | const { t } = useTranslation();
8 |
9 | return (
10 | <>
11 | {t('services.no_need')}
12 |
13 |
23 |
24 | >
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/services/translate/cambridge_dict/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'cambridge_dict',
3 | icon: 'logo/cambridge_dict.svg',
4 | };
5 |
6 | export enum Language {
7 | auto = 'auto',
8 | en = 'english',
9 | zh_cn = 'chinese-simplified',
10 | zh_tw = 'chinese-traditional',
11 | }
12 |
--------------------------------------------------------------------------------
/src/services/translate/chatglm/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'chatglm',
3 | icon: 'logo/chatglm.png',
4 | };
5 |
6 | export enum Language {
7 | auto = 'Auto',
8 | zh_cn = 'Simplified Chinese',
9 | zh_tw = 'Traditional Chinese',
10 | yue = 'Cantonese',
11 | ja = 'Japanese',
12 | en = 'English',
13 | ko = 'Korean',
14 | fr = 'French',
15 | es = 'Spanish',
16 | ru = 'Russian',
17 | de = 'German',
18 | it = 'Italian',
19 | tr = 'Turkish',
20 | pt_pt = 'Portuguese',
21 | pt_br = 'Brazilian Portuguese',
22 | vi = 'Vietnamese',
23 | id = 'Indonesian',
24 | th = 'Thai',
25 | ms = 'Malay',
26 | ar = 'Arabic',
27 | hi = 'Hindi',
28 | mn_mo = 'Mongolian',
29 | mn_cy = 'Mongolian(Cyrillic)',
30 | km = 'Khmer',
31 | nb_no = 'Norwegian Bokmål',
32 | nn_no = 'Norwegian Nynorsk',
33 | fa = 'Persian',
34 | sv = 'Swedish',
35 | pl = 'Polish',
36 | nl = 'Dutch',
37 | uk = 'Ukrainian',
38 | he = 'Hebrew',
39 | }
40 |
--------------------------------------------------------------------------------
/src/services/translate/deepl/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'deepl',
3 | icon: 'logo/deepl.svg',
4 | };
5 |
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'ZH',
9 | zh_tw = 'ZH',
10 | ja = 'JA',
11 | en = 'EN',
12 | ko = 'KO',
13 | fr = 'FR',
14 | es = 'ES',
15 | ru = 'RU',
16 | de = 'DE',
17 | it = 'IT',
18 | tr = 'TR',
19 | pt_pt = 'PT-PT',
20 | pt_br = 'PT-BR',
21 | id = 'ID',
22 | sv = 'SV',
23 | pl = 'PL',
24 | nl = 'NL',
25 | uk = 'UK',
26 | }
27 |
--------------------------------------------------------------------------------
/src/services/translate/ecdict/Config.jsx:
--------------------------------------------------------------------------------
1 | import { useTranslation } from 'react-i18next';
2 | import { Button } from '@nextui-org/react';
3 | import React from 'react';
4 |
5 | export function Config(props) {
6 | const { updateServiceList, onClose } = props;
7 | const { t } = useTranslation();
8 |
9 | return (
10 | <>
11 | {t('services.no_need')}
12 |
13 |
23 |
24 | >
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/services/translate/ecdict/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch, Body } from '@tauri-apps/api/http';
2 |
3 | export async function translate(text, _from, _to) {
4 | const res = await fetch(`https://pot-app.com/api/dict`, {
5 | method: 'POST',
6 | body: Body.json({ text }),
7 | });
8 |
9 | if (res.ok) {
10 | let result = res.data;
11 | return result;
12 | } else {
13 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
14 | }
15 | }
16 |
17 | export * from './Config';
18 | export * from './info';
19 |
--------------------------------------------------------------------------------
/src/services/translate/ecdict/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'ecdict',
3 | icon: 'logo/ecdict.svg',
4 | };
5 |
6 | export enum Language {
7 | auto = '',
8 | zh_cn = 'zh',
9 | zh_tw = 'zh',
10 | en = 'en',
11 | }
12 |
--------------------------------------------------------------------------------
/src/services/translate/geminipro/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'geminipro',
3 | icon: 'logo/geminipro.webp',
4 | };
5 |
6 | export enum Language {
7 | auto = 'Auto',
8 | zh_cn = 'Simplified Chinese',
9 | zh_tw = 'Traditional Chinese',
10 | yue = 'Cantonese',
11 | ja = 'Japanese',
12 | en = 'English',
13 | ko = 'Korean',
14 | fr = 'French',
15 | es = 'Spanish',
16 | ru = 'Russian',
17 | de = 'German',
18 | it = 'Italian',
19 | tr = 'Turkish',
20 | pt_pt = 'Portuguese',
21 | pt_br = 'Brazilian Portuguese',
22 | vi = 'Vietnamese',
23 | id = 'Indonesian',
24 | th = 'Thai',
25 | ms = 'Malay',
26 | ar = 'Arabic',
27 | hi = 'Hindi',
28 | mn_mo = 'Mongolian',
29 | mn_cy = 'Mongolian(Cyrillic)',
30 | km = 'Khmer',
31 | nb_no = 'Norwegian Bokmål',
32 | nn_no = 'Norwegian Nynorsk',
33 | fa = 'Persian',
34 | sv = 'Swedish',
35 | pl = 'Polish',
36 | nl = 'Dutch',
37 | uk = 'Ukrainian',
38 | he = 'Hebrew',
39 | }
40 |
--------------------------------------------------------------------------------
/src/services/translate/google/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch } from '@tauri-apps/api/http';
2 |
3 | export async function translate(text, from, to, options = {}) {
4 | const { config } = options;
5 |
6 | let { custom_url } = config;
7 |
8 | if (custom_url === undefined || custom_url === '') {
9 | custom_url = 'https://translate.google.com';
10 | }
11 | if (!custom_url.startsWith('http')) {
12 | custom_url = 'https://' + custom_url;
13 | }
14 |
15 | let res = await fetch(
16 | `${custom_url}/translate_a/single?dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t`,
17 | {
18 | method: 'GET',
19 | headers: { 'content-type': 'application/json' },
20 | query: {
21 | client: 'gtx',
22 | sl: from,
23 | tl: to,
24 | hl: to,
25 | ie: 'UTF-8',
26 | oe: 'UTF-8',
27 | otf: '1',
28 | ssel: '0',
29 | tsel: '0',
30 | kc: '7',
31 | q: text,
32 | },
33 | }
34 | );
35 | if (res.ok) {
36 | let result = res.data;
37 | // 词典模式
38 | if (result[1]) {
39 | let target = { pronunciations: [], explanations: [], associations: [], sentence: [] };
40 | // 发音
41 | if (result[0][1][3]) {
42 | target.pronunciations.push({ symbol: result[0][1][3], voice: '' });
43 | }
44 | // 释义
45 | for (let i of result[1]) {
46 | target.explanations.push({
47 | trait: i[0],
48 | explains: i[2].map((x) => {
49 | return x[0];
50 | }),
51 | });
52 | }
53 | // 例句
54 | if (result[13]) {
55 | for (let i of result[13][0]) {
56 | target.sentence.push({ source: i[0] });
57 | }
58 | }
59 | return target;
60 | } else {
61 | // 翻译模式
62 | let target = '';
63 | for (let r of result[0]) {
64 | if (r[0]) {
65 | target = target + r[0];
66 | }
67 | }
68 | return target.trim();
69 | }
70 | } else {
71 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
72 | }
73 | }
74 |
75 | export * from './Config';
76 | export * from './info';
77 |
--------------------------------------------------------------------------------
/src/services/translate/google/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'google',
3 | icon: 'logo/google.svg',
4 | };
5 | // https://cloud.google.com/translate/docs/languages?hl=zh-cn
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh-CN',
9 | zh_tw = 'zh-TW',
10 | ja = 'ja',
11 | en = 'en',
12 | ko = 'ko',
13 | fr = 'fr',
14 | es = 'es',
15 | ru = 'ru',
16 | de = 'de',
17 | it = 'it',
18 | tr = 'tr',
19 | pt_pt = 'pt',
20 | pt_br = 'pt',
21 | vi = 'vi',
22 | id = 'id',
23 | th = 'th',
24 | ms = 'ms',
25 | ar = 'ar',
26 | hi = 'hi',
27 | mn_cy = 'mn',
28 | km = 'km',
29 | nb_no = 'no',
30 | nn_no = 'no',
31 | fa = 'fa',
32 | sv = 'sv',
33 | pl = 'pl',
34 | nl = 'nl',
35 | uk = 'uk',
36 | he = 'he',
37 | }
38 |
--------------------------------------------------------------------------------
/src/services/translate/index.jsx:
--------------------------------------------------------------------------------
1 | import * as _deepl from './deepl';
2 | import * as _bing from './bing';
3 | import * as _yandex from './yandex';
4 | import * as _openai from './openai';
5 | import * as _google from './google';
6 | import * as _transmart from './transmart';
7 | import * as _alibaba from './alibaba';
8 | import * as _baidu from './baidu';
9 | import * as _baidu_field from './baidu_field';
10 | import * as _tencent from './tencent';
11 | import * as _volcengine from './volcengine';
12 | import * as _niutrans from './niutrans';
13 | import * as _youdao from './youdao';
14 | import * as _bing_dict from './bing_dict';
15 | import * as _cambridge_dict from './cambridge_dict';
16 | import * as _caiyun from './caiyun';
17 | import * as _chatglm from './chatglm';
18 | import * as _geminipro from './geminipro';
19 | import * as _ollama from './ollama';
20 | import * as _ecdict from './ecdict';
21 | import * as _lingva from './lingva';
22 |
23 | export const deepl = _deepl;
24 | export const bing = _bing;
25 | export const yandex = _yandex;
26 | export const openai = _openai;
27 | export const google = _google;
28 | export const transmart = _transmart;
29 | export const alibaba = _alibaba;
30 | export const baidu = _baidu;
31 | export const baidu_field = _baidu_field;
32 | export const tencent = _tencent;
33 | export const volcengine = _volcengine;
34 | export const niutrans = _niutrans;
35 | export const youdao = _youdao;
36 | export const bing_dict = _bing_dict;
37 | export const cambridge_dict = _cambridge_dict;
38 | export const caiyun = _caiyun;
39 | export const chatglm = _chatglm;
40 | export const geminipro = _geminipro;
41 | export const ollama = _ollama;
42 | export const ecdict = _ecdict;
43 | export const lingva = _lingva;
44 |
--------------------------------------------------------------------------------
/src/services/translate/lingva/Config.jsx:
--------------------------------------------------------------------------------
1 | import { useTranslation } from 'react-i18next';
2 | import { Button } from '@nextui-org/react';
3 | import React from 'react';
4 |
5 | export function Config(props) {
6 | const { updateServiceList, onClose } = props;
7 | const { t } = useTranslation();
8 |
9 | return (
10 | <>
11 | {t('services.no_need')}
12 |
13 |
23 |
24 | >
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/services/translate/lingva/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch, Body } from '@tauri-apps/api/http';
2 |
3 | export async function translate(text, from, to) {
4 | let plain_text = text.replaceAll('/', '@@');
5 | let encode_text = encodeURIComponent(plain_text);
6 | const res = await fetch(`https://lingva.pot-app.com/api/v1/${from}/${to}/${encode_text}`, {
7 | method: 'GET',
8 | });
9 |
10 | if (res.ok) {
11 | let result = res.data;
12 | const { translation } = result;
13 | if (translation) {
14 | return translation.replaceAll('@@', '/');
15 | } else {
16 | throw JSON.stringify(result.trim());
17 | }
18 | } else {
19 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
20 | }
21 | }
22 |
23 | export * from './Config';
24 | export * from './info';
25 |
--------------------------------------------------------------------------------
/src/services/translate/lingva/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'lingva',
3 | icon: 'logo/lingva.svg',
4 | };
5 |
6 | // https://cloud.google.com/translate/docs/languages?hl=zh-cn
7 | export enum Language {
8 | auto = 'auto',
9 | zh_cn = 'zh',
10 | zh_tw = 'zh_HANT',
11 | en = 'en',
12 | ja = 'ja',
13 | ko = 'ko',
14 | fr = 'fr',
15 | es = 'es',
16 | ru = 'ru',
17 | de = 'de',
18 | it = 'it',
19 | tr = 'tr',
20 | pt_pt = 'pt',
21 | pt_br = 'pt',
22 | vi = 'vi',
23 | id = 'id',
24 | th = 'th',
25 | ms = 'ms',
26 | ar = 'ar',
27 | hi = 'hi',
28 | mn_cy = 'mn',
29 | mn_mo = 'mn',
30 | km = 'km',
31 | nb_no = 'no',
32 | nn_no = 'no',
33 | fa = 'fa',
34 | sv = 'sv',
35 | pl = 'pl',
36 | nl = 'nl',
37 | uk = 'uk',
38 | he = 'he',
39 | }
40 |
--------------------------------------------------------------------------------
/src/services/translate/niutrans/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch, Body } from '@tauri-apps/api/http';
2 |
3 | export async function translate(text, from, to, options = {}) {
4 | const { config } = options;
5 |
6 | const { https, apikey } = config;
7 |
8 | const url = `${https ? 'https' : 'http'}://api.niutrans.com/NiuTransServer/translation`;
9 |
10 | let res = await fetch(url, {
11 | method: 'POST',
12 | headers: {
13 | 'Content-Type': 'application/json',
14 | },
15 | body: Body.json({
16 | from: from,
17 | to: to,
18 | apikey: apikey,
19 | src_text: text,
20 | }),
21 | });
22 |
23 | // 返回翻译结果
24 | if (res.ok) {
25 | let result = res.data;
26 | if (result && result['tgt_text']) {
27 | return result['tgt_text'].trim();
28 | } else {
29 | throw JSON.stringify(result);
30 | }
31 | } else {
32 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
33 | }
34 | }
35 |
36 | export * from './Config';
37 | export * from './info';
38 |
--------------------------------------------------------------------------------
/src/services/translate/niutrans/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'niutrans',
3 | icon: 'logo/niutrans.svg',
4 | };
5 | // https://niutrans.com/documents/contents/trans_text#languageList
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh',
9 | zh_tw = 'cht',
10 | yue = 'yue',
11 | en = 'en',
12 | ja = 'ja',
13 | ko = 'ko',
14 | fr = 'fr',
15 | es = 'es',
16 | ru = 'ru',
17 | de = 'de',
18 | it = 'it',
19 | tr = 'tr',
20 | pt_pt = 'pt',
21 | pt_br = 'pt',
22 | vi = 'vi',
23 | id = 'id',
24 | th = 'th',
25 | ms = 'ms',
26 | ar = 'ar',
27 | hi = 'hi',
28 | mn_cy = 'mn',
29 | mn_mo = 'mo',
30 | km = 'km',
31 | nb_no = 'nb',
32 | nn_no = 'nn',
33 | fa = 'fa',
34 | sv = 'sv',
35 | pl = 'pl',
36 | nl = 'nl',
37 | uk = 'uk',
38 | he = 'he',
39 | }
40 |
--------------------------------------------------------------------------------
/src/services/translate/ollama/index.jsx:
--------------------------------------------------------------------------------
1 | import { Language } from './info';
2 | import { Ollama } from 'ollama/browser';
3 |
4 | export async function translate(text, from, to, options = {}) {
5 | const { config, setResult, detect } = options;
6 |
7 | let { stream, promptList, requestPath, model } = config;
8 |
9 | if (!/https?:\/\/.+/.test(requestPath)) {
10 | requestPath = `https://${requestPath}`;
11 | }
12 | if (requestPath.endsWith('/')) {
13 | requestPath = requestPath.slice(0, -1);
14 | }
15 | const ollama = new Ollama({ host: requestPath });
16 |
17 | promptList = promptList.map((item) => {
18 | return {
19 | ...item,
20 | content: item.content
21 | .replaceAll('$text', text)
22 | .replaceAll('$from', from)
23 | .replaceAll('$to', to)
24 | .replaceAll('$detect', Language[detect]),
25 | };
26 | });
27 |
28 | const response = await ollama.chat({ model, messages: promptList, stream: stream });
29 |
30 | if (stream) {
31 | let target = '';
32 | for await (const part of response) {
33 | target += part.message.content;
34 | if (setResult) {
35 | setResult(target + '_');
36 | } else {
37 | ollama.abort();
38 | return '[STREAM]';
39 | }
40 | }
41 | setResult(target.trim());
42 | return target.trim();
43 | } else {
44 | return response.message.content;
45 | }
46 | }
47 |
48 | export * from './Config';
49 | export * from './info';
50 |
--------------------------------------------------------------------------------
/src/services/translate/ollama/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'ollama',
3 | icon: 'logo/ollama.png',
4 | };
5 |
6 | export enum Language {
7 | auto = 'Auto',
8 | zh_cn = 'Simplified Chinese',
9 | zh_tw = 'Traditional Chinese',
10 | yue = 'Cantonese',
11 | ja = 'Japanese',
12 | en = 'English',
13 | ko = 'Korean',
14 | fr = 'French',
15 | es = 'Spanish',
16 | ru = 'Russian',
17 | de = 'German',
18 | it = 'Italian',
19 | tr = 'Turkish',
20 | pt_pt = 'Portuguese',
21 | pt_br = 'Brazilian Portuguese',
22 | vi = 'Vietnamese',
23 | id = 'Indonesian',
24 | th = 'Thai',
25 | ms = 'Malay',
26 | ar = 'Arabic',
27 | hi = 'Hindi',
28 | mn_mo = 'Mongolian',
29 | mn_cy = 'Mongolian(Cyrillic)',
30 | km = 'Khmer',
31 | nb_no = 'Norwegian Bokmål',
32 | nn_no = 'Norwegian Nynorsk',
33 | fa = 'Persian',
34 | sv = 'Swedish',
35 | pl = 'Polish',
36 | nl = 'Dutch',
37 | uk = 'Ukrainian',
38 | he = 'Hebrew',
39 | }
40 |
--------------------------------------------------------------------------------
/src/services/translate/openai/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'openai',
3 | icon: 'logo/openai.svg',
4 | };
5 |
6 | export enum Language {
7 | auto = 'Auto',
8 | zh_cn = 'Simplified Chinese',
9 | zh_tw = 'Traditional Chinese',
10 | yue = 'Cantonese',
11 | ja = 'Japanese',
12 | en = 'English',
13 | ko = 'Korean',
14 | fr = 'French',
15 | es = 'Spanish',
16 | ru = 'Russian',
17 | de = 'German',
18 | it = 'Italian',
19 | tr = 'Turkish',
20 | pt_pt = 'Portuguese',
21 | pt_br = 'Brazilian Portuguese',
22 | vi = 'Vietnamese',
23 | id = 'Indonesian',
24 | th = 'Thai',
25 | ms = 'Malay',
26 | ar = 'Arabic',
27 | hi = 'Hindi',
28 | mn_mo = 'Mongolian',
29 | mn_cy = 'Mongolian(Cyrillic)',
30 | km = 'Khmer',
31 | nb_no = 'Norwegian Bokmål',
32 | nn_no = 'Norwegian Nynorsk',
33 | fa = 'Persian',
34 | sv = 'Swedish',
35 | pl = 'Polish',
36 | nl = 'Dutch',
37 | uk = 'Ukrainian',
38 | he = 'Hebrew',
39 | }
40 |
--------------------------------------------------------------------------------
/src/services/translate/tencent/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'tencent',
3 | icon: 'logo/tencent.svg',
4 | };
5 | // 腾讯只支持这么多语言
6 | // https://cloud.tencent.com/document/product/551/15619
7 | export enum Language {
8 | auto = 'auto',
9 | zh_cn = 'zh',
10 | zh_tw = 'zh-TW',
11 | en = 'en',
12 | ja = 'ja',
13 | ko = 'ko',
14 | fr = 'fr',
15 | es = 'es',
16 | ru = 'ru',
17 | de = 'de',
18 | it = 'it',
19 | tr = 'tr',
20 | pt_pt = 'pt',
21 | pt_br = 'pt',
22 | vi = 'vi',
23 | id = 'id',
24 | th = 'th',
25 | ms = 'ms',
26 | ar = 'ar',
27 | hi = 'hi',
28 | }
29 |
--------------------------------------------------------------------------------
/src/services/translate/transmart/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch, Body } from '@tauri-apps/api/http';
2 |
3 | export async function translate(text, from, to, options = {}) {
4 | const { config } = options;
5 |
6 | const { username: user, token } = config;
7 |
8 | let header = {};
9 | if (user !== '' && token !== '') {
10 | header['user'] = user;
11 | header['token'] = token;
12 | }
13 |
14 | const url = 'https://transmart.qq.com/api/imt';
15 |
16 | const res = await fetch(url, {
17 | method: 'POST',
18 | body: Body.json({
19 | header: {
20 | fn: 'auto_translation',
21 | ...header,
22 | },
23 | type: 'plain',
24 | source: {
25 | lang: from,
26 | text_list: [text],
27 | },
28 | target: {
29 | lang: to,
30 | },
31 | }),
32 | });
33 | if (res.ok) {
34 | const result = res.data;
35 | if (result['auto_translation']) {
36 | let target = '';
37 | for (let line of result['auto_translation']) {
38 | target += line;
39 | target += '\n';
40 | }
41 | return target.trim();
42 | } else {
43 | throw JSON.stringify(result);
44 | }
45 | } else {
46 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
47 | }
48 | }
49 |
50 | export * from './Config';
51 | export * from './info';
52 |
--------------------------------------------------------------------------------
/src/services/translate/transmart/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'transmart',
3 | icon: 'logo/transmart.svg',
4 | };
5 | // https://docs.qq.com/doc/DY2NxUWpmdnB2RXV3
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh',
9 | zh_tw = 'zh-TW',
10 | en = 'en',
11 | ja = 'ja',
12 | ko = 'ko',
13 | fr = 'fr',
14 | es = 'es',
15 | ru = 'ru',
16 | de = 'de',
17 | it = 'it',
18 | tr = 'tr',
19 | pt_pt = 'pt',
20 | pt_br = 'pt',
21 | vi = 'vi',
22 | id = 'id',
23 | th = 'th',
24 | ms = 'ms',
25 | ar = 'ar',
26 | km = 'km',
27 | }
28 |
--------------------------------------------------------------------------------
/src/services/translate/volcengine/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'volcengine',
3 | icon: 'logo/volcengine.svg',
4 | };
5 | // https://www.volcengine.com/docs/4640/35107
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh',
9 | zh_tw = 'zh-Hant',
10 | ja = 'ja',
11 | en = 'en',
12 | ko = 'ko',
13 | fr = 'fr',
14 | es = 'es',
15 | ru = 'ru',
16 | de = 'de',
17 | it = 'it',
18 | tr = 'tr',
19 | pt_pt = 'pt',
20 | pt_br = 'pt',
21 | vi = 'vi',
22 | id = 'id',
23 | th = 'th',
24 | ms = 'ms',
25 | ar = 'ar',
26 | hi = 'hi',
27 | mn_cy = 'mn',
28 | nb_no = 'nb',
29 | nn_no = 'no',
30 | fa = 'fa',
31 | sv = 'sv',
32 | pl = 'pl',
33 | nl = 'nl',
34 | uk = 'uk',
35 | he = 'he',
36 | }
37 |
--------------------------------------------------------------------------------
/src/services/translate/yandex/Config.jsx:
--------------------------------------------------------------------------------
1 | import { useTranslation } from 'react-i18next';
2 | import { Button } from '@nextui-org/react';
3 | import React from 'react';
4 |
5 | export function Config(props) {
6 | const { updateServiceList, onClose } = props;
7 | const { t } = useTranslation();
8 |
9 | return (
10 | <>
11 | {t('services.no_need')}
12 |
13 |
23 |
24 | >
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/services/translate/yandex/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch, Body } from '@tauri-apps/api/http';
2 | import { v4 as uuidv4 } from 'uuid';
3 |
4 | export async function translate(text, from, to) {
5 | const url = 'https://translate.yandex.net/api/v1/tr.json/translate';
6 | const res = await fetch(url, {
7 | method: 'POST',
8 | headers: {
9 | 'Content-Type': 'application/x-www-form-urlencoded',
10 | },
11 | query: {
12 | id: uuidv4().replaceAll('-', '') + '-0-0',
13 | srv: 'android',
14 | },
15 | body: Body.form({
16 | source_lang: from,
17 | target_lang: to,
18 | text,
19 | }),
20 | });
21 | if (res.ok) {
22 | const result = res.data;
23 | if (result.text) {
24 | return result.text[0];
25 | } else {
26 | throw JSON.stringify(result);
27 | }
28 | } else {
29 | throw `Http Request Error\nHttp Status: ${res.status}\n${JSON.stringify(res.data)}`;
30 | }
31 | }
32 |
33 | export * from './Config';
34 | export * from './info';
35 |
--------------------------------------------------------------------------------
/src/services/translate/yandex/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'yandex',
3 | icon: 'logo/yandex.svg',
4 | };
5 | // https://yandex.com/dev/translate/doc/en/concepts/api-overview
6 | export enum Language {
7 | auto = '',
8 | zh_cn = 'zh',
9 | zh_tw = 'zh',
10 | en = 'en',
11 | ja = 'ja',
12 | ko = 'ko',
13 | fr = 'fr',
14 | es = 'es',
15 | ru = 'ru',
16 | de = 'de',
17 | it = 'it',
18 | tr = 'tr',
19 | pt_pt = 'pt',
20 | pt_br = 'pt',
21 | vi = 'vi',
22 | id = 'id',
23 | th = 'th',
24 | ms = 'ms',
25 | ar = 'ar',
26 | hi = 'hi',
27 | nb_no = 'no',
28 | nn_no = 'no',
29 | fa = 'fa',
30 | sv = 'sv',
31 | pl = 'pl',
32 | nl = 'nl',
33 | uk = 'uk',
34 | he = 'he',
35 | }
36 |
--------------------------------------------------------------------------------
/src/services/translate/youdao/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'youdao',
3 | icon: 'logo/youdao.svg',
4 | };
5 | // https://ai.youdao.com/DOCSIRMA/html/trans/api/wbfy/index.html
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh-CHS',
9 | zh_tw = 'zh-CHT',
10 | yue = 'yue',
11 | en = 'en',
12 | ja = 'jp',
13 | ko = 'kor',
14 | fr = 'fra',
15 | es = 'spa',
16 | ru = 'ru',
17 | de = 'de',
18 | it = 'it',
19 | tr = 'tr',
20 | pt_pt = 'pt',
21 | pt_br = 'pt',
22 | vi = 'vie',
23 | id = 'id',
24 | th = 'th',
25 | ms = 'may',
26 | ar = 'ar',
27 | hi = 'hi',
28 | mn_mo = 'mn',
29 | km = 'km',
30 | nb_no = 'no',
31 | nn_no = 'no',
32 | fa = 'fa',
33 | sv = 'sv',
34 | pl = 'pl',
35 | nl = 'nl',
36 | uk = 'uk',
37 | he = 'he',
38 | }
39 |
--------------------------------------------------------------------------------
/src/services/tts/index.jsx:
--------------------------------------------------------------------------------
1 | import * as _lingva_tts from './lingva';
2 |
3 | export const lingva_tts = _lingva_tts;
4 |
--------------------------------------------------------------------------------
/src/services/tts/lingva/index.jsx:
--------------------------------------------------------------------------------
1 | import { fetch } from '@tauri-apps/api/http';
2 |
3 | export async function tts(text, lang, options = {}) {
4 | const { config } = options;
5 |
6 | let { requestPath = 'lingva.pot-app.com' } = config;
7 |
8 | if (requestPath.length === 0) {
9 | requestPath = 'lingva.pot-app.com';
10 | }
11 |
12 | if (!requestPath.startsWith('http')) {
13 | requestPath = 'https://' + requestPath;
14 | }
15 | const res = await fetch(`${requestPath}/api/v1/audio/${lang}/${encodeURIComponent(text)}`);
16 |
17 | if (res.ok) {
18 | return res.data['audio'];
19 | }
20 | }
21 |
22 | export * from './Config';
23 | export * from './info';
24 |
--------------------------------------------------------------------------------
/src/services/tts/lingva/info.ts:
--------------------------------------------------------------------------------
1 | export const info = {
2 | name: 'lingva_tts',
3 | icon: 'logo/lingva.svg',
4 | };
5 |
6 | export enum Language {
7 | auto = 'auto',
8 | zh_cn = 'zh',
9 | zh_tw = 'zh_HANT',
10 | ja = 'ja',
11 | en = 'en',
12 | ko = 'ko',
13 | fr = 'fr',
14 | es = 'es',
15 | ru = 'ru',
16 | de = 'de',
17 | it = 'it',
18 | tr = 'tr',
19 | pt_pt = 'pt',
20 | pt_br = 'pt',
21 | vi = 'vi',
22 | id = 'id',
23 | th = 'th',
24 | ms = 'ms',
25 | ar = 'ar',
26 | hi = 'hi',
27 | mn_cy = 'mn',
28 | km = 'km',
29 | nb_no = 'no',
30 | nn_no = 'no',
31 | fa = 'fa',
32 | sv = 'sv',
33 | pl = 'pl',
34 | nl = 'nl',
35 | uk = 'uk',
36 | he = 'he',
37 | }
38 |
--------------------------------------------------------------------------------
/src/style.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | * {
6 | margin: 0;
7 | padding: 0;
8 | }
9 |
10 | html {
11 | border-radius: 10px !important;
12 | background: transparent !important;
13 | overflow: hidden;
14 | }
15 |
16 | *::-webkit-scrollbar {
17 | width: 5px;
18 | }
19 |
20 | *::-webkit-scrollbar-thumb {
21 | background: #c0c1c550;
22 | border-radius: 5px;
23 | }
24 |
25 | *::-webkit-scrollbar-thumb:hover {
26 | background: #c0c1c550;
27 | }
28 |
--------------------------------------------------------------------------------
/src/utils/env.js:
--------------------------------------------------------------------------------
1 | import { type, arch as archFn, version } from '@tauri-apps/api/os';
2 | import { getVersion } from '@tauri-apps/api/app';
3 |
4 | export let osType = '';
5 | export let arch = '';
6 | export let osVersion = '';
7 | export let appVersion = '';
8 |
9 | export async function initEnv() {
10 | osType = await type();
11 | arch = await archFn();
12 | osVersion = await version();
13 | appVersion = await getVersion();
14 | }
15 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | export const debounce = (fn, delay = 500) => {
2 | let timer = null;
3 | return (...args) => {
4 | timer && clearTimeout(timer);
5 | timer = setTimeout(() => fn(...args), delay);
6 | };
7 | };
8 |
--------------------------------------------------------------------------------
/src/utils/invoke_plugin.js:
--------------------------------------------------------------------------------
1 | import { appCacheDir, appConfigDir, join } from "@tauri-apps/api/path";
2 | import { readBinaryFile, readTextFile } from "@tauri-apps/api/fs";
3 | import { invoke } from "@tauri-apps/api/tauri";
4 | import Database from "tauri-plugin-sql-api";
5 | import { http } from "@tauri-apps/api";
6 | import CryptoJS from "crypto-js";
7 | import { osType } from "./env";
8 |
9 | export async function invoke_plugin(pluginType, pluginName) {
10 | let configDir = await appConfigDir();
11 | let cacheDir = await appCacheDir();
12 | let pluginDir = await join(configDir, "plugins", pluginType, pluginName);
13 | let entryFile = await join(pluginDir, "main.js");
14 | let script = await readTextFile(entryFile);
15 | async function run(cmdName, args) {
16 | return await invoke("run_binary", {
17 | pluginType,
18 | pluginName,
19 | cmdName,
20 | args
21 | });
22 | }
23 | const utils = {
24 | tauriFetch: http.fetch,
25 | http,
26 | readBinaryFile,
27 | readTextFile,
28 | Database,
29 | CryptoJS,
30 | run,
31 | cacheDir, // String
32 | pluginDir, // String
33 | osType,// "Windows_NT", "Darwin", "Linux"
34 | }
35 | return [eval(`${script} ${pluginType}`), utils];
36 | }
--------------------------------------------------------------------------------
/src/utils/language.ts:
--------------------------------------------------------------------------------
1 | // ISO-639-1 + Country Code (Option)
2 | // https://zh.wikipedia.org/wiki/ISO_639-1%E4%BB%A3%E7%A0%81%E8%A1%A8
3 | export const languageList = [
4 | 'zh_cn',
5 | 'zh_tw',
6 | 'mn_mo',
7 | 'en',
8 | 'ja',
9 | 'ko',
10 | 'fr',
11 | 'es',
12 | 'ru',
13 | 'de',
14 | 'it',
15 | 'tr',
16 | 'pt_pt',
17 | 'pt_br',
18 | 'vi',
19 | 'id',
20 | 'th',
21 | 'ms',
22 | 'ar',
23 | 'hi',
24 | 'km',
25 | 'mn_cy',
26 | 'nb_no',
27 | 'nn_no',
28 | 'fa',
29 | 'sv',
30 | 'pl',
31 | 'nl',
32 | 'uk',
33 | 'he',
34 | ];
35 |
36 | // https://flagicons.lipis.dev/
37 | export enum LanguageFlag {
38 | zh_cn = 'cn',
39 | zh_tw = 'cn',
40 | mn_mo = 'cn',
41 | en = 'gb',
42 | ja = 'jp',
43 | ko = 'kr',
44 | fr = 'fr',
45 | es = 'es',
46 | ru = 'ru',
47 | de = 'de',
48 | it = 'it',
49 | tr = 'tr',
50 | pt_pt = 'pt',
51 | pt_br = 'br',
52 | vi = 'vn',
53 | id = 'id',
54 | th = 'th',
55 | ms = 'ms',
56 | ar = 'ae',
57 | hi = 'in',
58 | km = 'kh',
59 | mn_cy = 'mn',
60 | nb_no = 'no',
61 | nn_no = 'no',
62 | fa = 'ir',
63 | sv = 'se',
64 | pl = 'pl',
65 | nl = 'nl',
66 | uk = 'ua',
67 | he = 'il',
68 | }
69 |
--------------------------------------------------------------------------------
/src/utils/service_instance.ts:
--------------------------------------------------------------------------------
1 | export enum ServiceType {
2 | TRANSLATE = 'translate',
3 | RECOGNIZE = 'recognize',
4 | TTS = 'tts',
5 | COLLECTION = 'collection',
6 | }
7 |
8 | export enum ServiceSourceType {
9 | BUILDIN = 'buildin',
10 | PLUGIN = 'plugin',
11 | }
12 |
13 | export function getServiceSouceType(serviceInstanceKey: string): ServiceSourceType {
14 | if (serviceInstanceKey.startsWith('plugin')) {
15 | return ServiceSourceType.PLUGIN;
16 | } else {
17 | return ServiceSourceType.BUILDIN;
18 | }
19 | }
20 |
21 | export function whetherPluginService(serviceInstanceKey: string): boolean {
22 | return getServiceSouceType(serviceInstanceKey) === ServiceSourceType.PLUGIN;
23 | }
24 |
25 | // The serviceInstanceKey consists of the service name and it's id, separated by @
26 | // In earlier versions, the @ separator and id were optional, so they all have only one instance.
27 | export function createServiceInstanceKey(serviceName: string): string {
28 | const randomId = Math.random().toString(36).substring(2);
29 | return `${serviceName}@${randomId}`;
30 | }
31 |
32 | // if the serviceInstanceKey is from a plugin, serviceName is it's pluginId
33 | export function getServiceName(serviceInstanceKey: string): string {
34 | return serviceInstanceKey.split('@')[0];
35 | }
36 |
37 | export function getDisplayInstanceName(instanceName: string, serviceNameSupplier: () => string): string {
38 | return instanceName || serviceNameSupplier();
39 | }
40 |
41 | export const INSTANCE_NAME_CONFIG_KEY = 'instanceName';
42 |
43 | export function whetherAvailableService(
44 | serviceInstanceKey: string,
45 | availableServices: Record>
46 | ) {
47 | const serviceSourceType = getServiceSouceType(serviceInstanceKey);
48 | const serviceName = getServiceName(serviceInstanceKey);
49 | return availableServices[serviceSourceType]?.[serviceName] !== undefined;
50 | }
51 |
--------------------------------------------------------------------------------
/src/utils/store.js:
--------------------------------------------------------------------------------
1 | import { Store } from 'tauri-plugin-store-api';
2 | import { appConfigDir, join } from '@tauri-apps/api/path';
3 | import { watch } from 'tauri-plugin-fs-watch-api';
4 | import { invoke } from '@tauri-apps/api';
5 |
6 | export let store = new Store();
7 |
8 | export async function initStore() {
9 | const appConfigDirPath = await appConfigDir();
10 | const appConfigPath = await join(appConfigDirPath, 'config.json');
11 | store = new Store(appConfigPath);
12 | const _ = await watch(appConfigPath, async () => {
13 | await store.load();
14 | await invoke('reload_store');
15 | });
16 | }
17 |
--------------------------------------------------------------------------------
/src/window/Config/index.jsx:
--------------------------------------------------------------------------------
1 | import { useLocation, useRoutes } from 'react-router-dom';
2 | import React, { useEffect, useState } from 'react';
3 | import { appWindow } from '@tauri-apps/api/window';
4 | import { Card, Divider } from '@nextui-org/react';
5 | import { useTranslation } from 'react-i18next';
6 |
7 | import WindowControl from '../../components/WindowControl';
8 | import SideBar from './components/SideBar';
9 | import { osType } from '../../utils/env';
10 | import { useConfig } from '../../hooks';
11 | import routes from './routes';
12 | import './style.css';
13 |
14 | export default function Config() {
15 | const [transparent] = useConfig('transparent', true);
16 | const { t } = useTranslation();
17 | const location = useLocation();
18 | const page = useRoutes(routes);
19 |
20 | useEffect(() => {
21 | if (appWindow.label === 'config') {
22 | appWindow.show();
23 | }
24 | }, []);
25 |
26 | return (
27 | <>
28 |
36 |
42 |
43 |
44 |

50 |
51 |
52 |
53 |
54 |
59 |
63 |
64 |
65 |
{t(`config.${location.pathname.slice(1)}.title`)}
66 |
67 |
68 |
{osType !== 'Darwin' && }
69 |
70 |
71 |
76 | {page}
77 |
78 |
79 | >
80 | );
81 | }
82 |
--------------------------------------------------------------------------------
/src/window/Config/pages/Backup/utils/local.jsx:
--------------------------------------------------------------------------------
1 | import { save, open } from '@tauri-apps/api/dialog';
2 | import { invoke } from '@tauri-apps/api';
3 |
4 | export async function backup() {
5 | const selected = await save({
6 | filters: [
7 | {
8 | name: 'Backup',
9 | extensions: ['zip'],
10 | },
11 | ],
12 | });
13 | if (selected !== null) {
14 | return await invoke('local', {
15 | operate: 'put',
16 | path: selected,
17 | });
18 | } else {
19 | throw 'Invalid File';
20 | }
21 | }
22 |
23 | export async function get() {
24 | const selected = await open({
25 | multiple: false,
26 | directory: false,
27 | filters: [
28 | {
29 | name: '*.zip',
30 | extensions: ['zip'],
31 | },
32 | ],
33 | });
34 |
35 | if (selected !== null && selected.endsWith('zip')) {
36 | return await invoke('local', {
37 | operate: 'get',
38 | path: selected,
39 | });
40 | } else {
41 | throw 'Invalid File';
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/window/Config/pages/Backup/utils/webdav.jsx:
--------------------------------------------------------------------------------
1 | import { invoke } from '@tauri-apps/api';
2 |
3 | export async function backup(url, username, password, name) {
4 | return await invoke('webdav', {
5 | operate: 'put',
6 | url,
7 | username,
8 | password,
9 | name,
10 | });
11 | }
12 |
13 | export async function list(url, username, password) {
14 | const backup_list_text = await invoke('webdav', {
15 | operate: 'list',
16 | url,
17 | username,
18 | password,
19 | });
20 | let backup_list = JSON.parse(backup_list_text);
21 | backup_list = backup_list.filter((item) => {
22 | return item.hasOwnProperty('File');
23 | });
24 | return backup_list.map((file) => {
25 | return file.File.href.split('/').slice(-1)[0];
26 | });
27 | }
28 |
29 | export async function get(url, username, password, name) {
30 | const _ = await invoke('webdav', {
31 | operate: 'get',
32 | url,
33 | username,
34 | password,
35 | name,
36 | });
37 | }
38 |
39 | export async function remove(url, username, password, name) {
40 | return await invoke('webdav', {
41 | operate: 'delete',
42 | url,
43 | username,
44 | password,
45 | name,
46 | });
47 | }
48 |
--------------------------------------------------------------------------------
/src/window/Config/pages/Service/Collection/SelectModal/index.jsx:
--------------------------------------------------------------------------------
1 | import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@nextui-org/react';
2 | import { useTranslation } from 'react-i18next';
3 | import React from 'react';
4 |
5 | import { createServiceInstanceKey } from '../../../../../../utils/service_instance';
6 | import * as builtinServices from '../../../../../../services/collection';
7 |
8 | export default function SelectModal(props) {
9 | const { isOpen, onOpenChange, setCurrentConfigKey, onConfigOpen } = props;
10 | const { t } = useTranslation();
11 |
12 | return (
13 |
18 |
19 | {(onClose) => (
20 | <>
21 | {t('config.service.add_service')}
22 |
23 | {Object.keys(builtinServices).map((x) => {
24 | return (
25 |
26 |
43 |
44 | );
45 | })}
46 |
47 |
48 |
55 |
56 | >
57 | )}
58 |
59 |
60 | );
61 | }
62 |
--------------------------------------------------------------------------------
/src/window/Config/pages/Service/Recognize/SelectModal/index.jsx:
--------------------------------------------------------------------------------
1 | import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@nextui-org/react';
2 | import { useTranslation } from 'react-i18next';
3 | import React from 'react';
4 |
5 | import { createServiceInstanceKey } from '../../../../../../utils/service_instance';
6 | import * as builtinServices from '../../../../../../services/recognize';
7 | import { osType } from '../../../../../../utils/env';
8 |
9 | export default function SelectModal(props) {
10 | const { isOpen, onOpenChange, setCurrentConfigKey, onConfigOpen } = props;
11 | const { t } = useTranslation();
12 |
13 | return (
14 |
19 |
20 | {(onClose) => (
21 | <>
22 | {t('config.service.add_service')}
23 |
24 | {Object.keys(builtinServices).map((x) => {
25 | return (
26 |
27 |
48 |
49 | );
50 | })}
51 |
52 |
53 |
60 |
61 | >
62 | )}
63 |
64 |
65 | );
66 | }
67 |
--------------------------------------------------------------------------------
/src/window/Config/pages/Service/Translate/SelectModal/index.jsx:
--------------------------------------------------------------------------------
1 | import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@nextui-org/react';
2 | import { useTranslation } from 'react-i18next';
3 | import React from 'react';
4 |
5 | import * as builtinServices from '../../../../../../services/translate';
6 | import { createServiceInstanceKey } from '../../../../../../utils/service_instance';
7 |
8 | export default function SelectModal(props) {
9 | const { isOpen, onOpenChange, setCurrentConfigKey, onConfigOpen } = props;
10 | const { t } = useTranslation();
11 |
12 | return (
13 |
18 |
19 | {(onClose) => (
20 | <>
21 | {t('config.service.add_service')}
22 |
23 | {Object.keys(builtinServices).map((x) => {
24 | return (
25 |
26 |
43 |
44 | );
45 | })}
46 |
47 |
48 |
55 |
56 | >
57 | )}
58 |
59 |
60 | );
61 | }
62 |
--------------------------------------------------------------------------------
/src/window/Config/pages/Service/Tts/SelectModal/index.jsx:
--------------------------------------------------------------------------------
1 | import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@nextui-org/react';
2 | import { useTranslation } from 'react-i18next';
3 | import React from 'react';
4 |
5 | import { createServiceInstanceKey } from '../../../../../../utils/service_instance';
6 | import * as builtinServices from '../../../../../../services/tts';
7 |
8 | export default function SelectModal(props) {
9 | const { isOpen, onOpenChange, setCurrentConfigKey, onConfigOpen } = props;
10 | const { t } = useTranslation();
11 |
12 | return (
13 |
18 |
19 | {(onClose) => (
20 | <>
21 | {t('config.service.add_service')}
22 |
23 | {Object.keys(builtinServices).map((x) => {
24 | return (
25 |
26 |
43 |
44 | );
45 | })}
46 |
47 |
48 |
55 |
56 | >
57 | )}
58 |
59 |
60 | );
61 | }
62 |
--------------------------------------------------------------------------------
/src/window/Config/routes/index.jsx:
--------------------------------------------------------------------------------
1 | import { Navigate } from 'react-router-dom';
2 |
3 | import Translate from '../pages/Translate';
4 | import Recognize from '../pages/Recognize';
5 | import General from '../pages/General';
6 | import Service from '../pages/Service';
7 | import History from '../pages/History';
8 | import Hotkey from '../pages/Hotkey';
9 | import Backup from '../pages/Backup';
10 | import About from '../pages/About';
11 |
12 | const routes = [
13 | {
14 | path: '/general',
15 | element: ,
16 | },
17 | {
18 | path: '/translate',
19 | element: ,
20 | },
21 | {
22 | path: '/recognize',
23 | element: ,
24 | },
25 | {
26 | path: '/hotkey',
27 | element: ,
28 | },
29 | {
30 | path: '/service',
31 | element: ,
32 | },
33 | {
34 | path: '/history',
35 | element: ,
36 | },
37 | {
38 | path: '/backup',
39 | element: ,
40 | },
41 | {
42 | path: '/about',
43 | element: ,
44 | },
45 | {
46 | path: '/',
47 | element: ,
48 | },
49 | ];
50 |
51 | export default routes;
52 |
--------------------------------------------------------------------------------
/src/window/Config/style.css:
--------------------------------------------------------------------------------
1 | .config-item {
2 | margin: 10px 0;
3 | display: flex;
4 | justify-content: space-between;
5 | }
6 |
--------------------------------------------------------------------------------
/src/window/Recognize/ImageArea/index.jsx:
--------------------------------------------------------------------------------
1 | import { Card, CardBody, CardFooter, Button, Tooltip } from '@nextui-org/react';
2 | import { appWindow } from '@tauri-apps/api/window';
3 | import React, { useEffect, useRef } from 'react';
4 | import { listen } from '@tauri-apps/api/event';
5 | import { MdContentCopy } from 'react-icons/md';
6 | import { useTranslation } from 'react-i18next';
7 | import { invoke } from '@tauri-apps/api';
8 | import { atom, useAtom } from 'jotai';
9 |
10 | import { useConfig } from '../../../hooks';
11 |
12 | export const base64Atom = atom('');
13 | let unlisten = null;
14 |
15 | export default function ImageArea() {
16 | const [hideWindow] = useConfig('recognize_hide_window', false);
17 | const [base64, setBase64] = useAtom(base64Atom);
18 | const imgRef = useRef();
19 | const { t } = useTranslation();
20 | const load_img = () => {
21 | invoke('get_base64').then((v) => {
22 | setBase64(v);
23 | if (hideWindow) {
24 | appWindow.hide();
25 | } else {
26 | appWindow.show();
27 | appWindow.setFocus(true);
28 | }
29 | });
30 | };
31 |
32 | useEffect(() => {
33 | if (hideWindow !== null) {
34 | load_img();
35 | if (unlisten) {
36 | unlisten.then((f) => {
37 | f();
38 | });
39 | }
40 | unlisten = listen('new_image', (_) => {
41 | load_img();
42 | });
43 | }
44 | }, [hideWindow]);
45 |
46 | return (
47 |
52 |
53 | {base64 !== '' && (
54 |
60 | )}
61 |
62 |
63 |
64 |
77 |
78 |
79 |
80 | );
81 | }
82 |
--------------------------------------------------------------------------------
/tailwind.config.cjs:
--------------------------------------------------------------------------------
1 | // tailwind.config.js
2 | const { nextui } = require('@nextui-org/react');
3 |
4 | /** @type {import('tailwindcss').Config} */
5 | module.exports = {
6 | content: [
7 | // ...
8 | './index.html',
9 | './src/**/*.{js,ts,jsx,tsx}',
10 | './node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}',
11 | ],
12 | theme: {
13 | extend: {},
14 | },
15 | darkMode: 'class',
16 | plugins: [
17 | nextui({
18 | themes: {
19 | dark: {
20 | colors: {
21 | background: '#202020',
22 | foreground: '#e7e7e7',
23 | content1: '#282828',
24 | content2: '#303030',
25 | content3: '#383838',
26 | content4: '#404040',
27 | default: {
28 | DEFAULT: '#484848',
29 | 50: '#282828',
30 | 100: '#383838',
31 | 200: '#484848',
32 | 300: '#585858',
33 | 400: '#686868',
34 | 500: '#a7a7a7',
35 | 600: '#b7b7b7',
36 | 700: '#c7c7c7',
37 | 800: '#d7d7d7',
38 | 900: '#e7e7e7',
39 | },
40 | primary: {
41 | DEFAULT: '#49cee9',
42 | foreground: '#181818',
43 | },
44 | },
45 | },
46 | light: {
47 | colors: {
48 | background: '#ffffff',
49 | foreground: '#181818',
50 | content1: '#eeeeee',
51 | content2: '#dddddd',
52 | content3: '#cccccc',
53 | content4: '#bbbbbb',
54 | default: {
55 | DEFAULT: '#999999',
56 | 50: '#eeeeee',
57 | 100: '#cccccc',
58 | 200: '#aaaaaa',
59 | 300: '#999999',
60 | 400: '#888888',
61 | 500: '#686868',
62 | 600: '#585858',
63 | 700: '#484848',
64 | 800: '#383838',
65 | 900: '#282828',
66 | },
67 | primary: {
68 | foreground: '#ffffff',
69 | DEFAULT: '#3578e5',
70 | },
71 | },
72 | },
73 | },
74 | }),
75 | ],
76 | };
77 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react';
2 | import { defineConfig } from 'vite';
3 | import { resolve } from 'path';
4 |
5 | // https://vitejs.dev/config/
6 | export default defineConfig(async () => ({
7 | plugins: [react()],
8 |
9 | // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
10 | // prevent vite from obscuring rust errors
11 | clearScreen: false,
12 | // tauri expects a fixed port, fail if that port is not available
13 | server: {
14 | port: 1420,
15 | strictPort: true,
16 | },
17 | // to make use of `TAURI_DEBUG` and other env variables
18 | // https://tauri.studio/v1/api/config#buildconfig.beforedevcommand
19 | envPrefix: ['VITE_', 'TAURI_'],
20 | build: {
21 | rollupOptions: {
22 | input: {
23 | index: resolve(__dirname, 'index.html'),
24 | daemon: resolve(__dirname, 'daemon.html'),
25 | },
26 | },
27 | // Tauri supports es2021
28 | target: process.env.TAURI_PLATFORM == 'windows' ? 'chrome105' : 'safari11',
29 | // don't minify for debug builds
30 | minify: !process.env.TAURI_DEBUG ? 'esbuild' : false,
31 | // produce sourcemaps for debug builds
32 | sourcemap: !!process.env.TAURI_DEBUG,
33 | },
34 | }));
35 |
--------------------------------------------------------------------------------