├── .cursorrules
├── .eslintignore
├── .eslintrc.js
├── .github
├── ISSUE_TEMPLATE
│ ├── bug-report.yml
│ ├── config.yml
│ └── feature.yml
├── dependabot.yml
└── workflows
│ ├── lint.yaml
│ ├── playwright.yml
│ ├── release.yaml
│ ├── test-build.yaml
│ ├── unit-test.yaml
│ └── winget.yml
├── .gitignore
├── .plasmo
├── cache
│ └── parcel
│ │ ├── data.mdb
│ │ └── lock.mdb
└── chrome-mv3.plasmo.manifest.json
├── .prettierrc.js
├── 8.gif
├── CHANGELOG-CN.md
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README-CN.md
├── README.md
├── README.md.backup
├── clip-extensions
├── popclip
│ ├── Config.plist
│ ├── icon.png
│ └── openai-translator.sh
└── snipdo
│ ├── icon.png
│ ├── openai-translator.json
│ └── openai-translator.ps1
├── e2e
├── common.ts
├── fixtures.ts
├── hotkey.spec.ts
├── index.spec.ts
└── test.html
├── jest.config.js
├── make
├── package.json
├── playwright.config.ts
├── pnpm-lock.yaml
├── public
├── 8.gif
├── icon.png
├── image-1.png
├── image-10.png
├── image-11.png
├── image-12.png
├── image-13.png
├── image-14.png
├── image-15.png
├── image-2.png
├── image-3.png
├── image-4.png
├── image-5.png
├── image-6.png
├── image-7.png
├── image-8.png
├── image-9.png
└── rules.json
├── readingMode_1.gif
├── scripts
└── release.py
├── 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
│ ├── favicon.ico
│ ├── icon.icns
│ ├── icon.ico
│ └── icon.png
├── resources
│ ├── bin
│ │ ├── ocr_apple
│ │ └── ocr_intel
│ ├── get-selected-text-by-ax.applescript
│ └── get-selected-text.applescript
├── src
│ ├── config.rs
│ ├── hotkey.rs
│ ├── lang.rs
│ ├── main.rs
│ ├── ocr.rs
│ ├── tray.rs
│ ├── utils.rs
│ └── windows.rs
└── tauri.conf.json
├── src
├── browser-extension
│ ├── background
│ │ └── index.ts
│ ├── content_script
│ │ ├── consts.ts
│ │ ├── index.tsx
│ │ └── utils.ts
│ ├── manifest.firefox.json
│ ├── manifest.json
│ ├── options
│ │ ├── index.css
│ │ ├── index.html
│ │ └── index.tsx
│ ├── popup
│ │ ├── index.css
│ │ ├── index.html
│ │ └── index.tsx
│ └── youglish
│ │ └── youglish.html
├── common
│ ├── AnswerTabs.tsx
│ ├── __tests__
│ │ └── translate.test.ts
│ ├── analysis.ts
│ ├── anki
│ │ └── anki-connect.js
│ ├── arkose
│ │ ├── generator.js
│ │ └── index.ts
│ ├── assets
│ │ ├── gif
│ │ │ └── drag_to_resize.gif
│ │ └── images
│ │ │ ├── beams.jpg
│ │ │ ├── chatglm.svg
│ │ │ ├── claude.svg
│ │ │ ├── deepseek.svg
│ │ │ ├── groq.svg
│ │ │ ├── icon-large.png
│ │ │ ├── icon.png
│ │ │ ├── kimi.svg
│ │ │ ├── moonshot-dark.png
│ │ │ ├── moonshot-light.png
│ │ │ ├── ollama.svg
│ │ │ ├── openrouter.png
│ │ │ ├── party-popper.gif
│ │ │ └── rocket.gif
│ ├── background
│ │ ├── eventnames.ts
│ │ ├── fetch.ts
│ │ └── services
│ │ │ ├── arkoseToken.ts
│ │ │ ├── base.ts
│ │ │ └── vocabulary.ts
│ ├── components
│ │ ├── ActionForm.tsx
│ │ ├── ActionGroupForm.tsx
│ │ ├── ActionList.tsx
│ │ ├── ActionManager.tsx
│ │ ├── ActionStore.tsx
│ │ ├── AnswerManager.tsx
│ │ ├── AppTutorial.tsx
│ │ ├── AuthModal.tsx
│ │ ├── CategorySelector.tsx
│ │ ├── CheckBox.tsx
│ │ ├── ConversationView.tsx
│ │ ├── CopyButton.tsx
│ │ ├── ErrorFallback.tsx
│ │ ├── Form
│ │ │ ├── form.ts
│ │ │ ├── index.module.css
│ │ │ ├── index.ts
│ │ │ ├── item.tsx
│ │ │ ├── typings.ts
│ │ │ └── validators.ts
│ │ ├── GlobalSuspense.tsx
│ │ ├── GroupSelect.tsx
│ │ ├── IconPicker.tsx
│ │ ├── IpLocationNotification.tsx
│ │ ├── Markdown.tsx
│ │ ├── MessageCard.tsx
│ │ ├── ModelSelect.tsx
│ │ ├── NumberInput.tsx
│ │ ├── QuickActionBar.tsx
│ │ ├── QuotePreview.tsx
│ │ ├── RenderingFormatSelector.tsx
│ │ ├── ReviewSettings.tsx
│ │ ├── Settings.tsx
│ │ ├── SpeakerMotion.tsx
│ │ ├── StudyChart.tsx
│ │ ├── SystemMessage.tsx
│ │ ├── TextAreaWithActions.tsx
│ │ ├── TextParser.tsx
│ │ ├── Tooltip.tsx
│ │ ├── Translator.tsx
│ │ ├── WordBookViewer.tsx
│ │ ├── WordListMenu.tsx
│ │ ├── WordListUploader.tsx
│ │ ├── icons
│ │ │ ├── ChatGLMIcon.tsx
│ │ │ ├── ClaudeIcon.tsx
│ │ │ ├── DeepSeekIcon.tsx
│ │ │ ├── GroqIcon.tsx
│ │ │ ├── KimiIcon.tsx
│ │ │ ├── MoonshotIcon.tsx
│ │ │ ├── OllamaIcon.tsx
│ │ │ ├── OneAPIIcon.tsx
│ │ │ └── OpenRouterIcon.tsx
│ │ └── lang
│ │ │ ├── data.ts
│ │ │ └── lang.ts
│ ├── constants.ts
│ ├── docs
│ │ ├── actionstore.markdown
│ │ └── autoCompletePrompt.markdown
│ ├── engines
│ │ ├── abstract-engine.ts
│ │ ├── abstract-openai.ts
│ │ ├── azure.ts
│ │ ├── chatglm.ts
│ │ ├── chatgpt.ts
│ │ ├── claude.ts
│ │ ├── deepseek.ts
│ │ ├── gemini.ts
│ │ ├── groq.ts
│ │ ├── index.ts
│ │ ├── interfaces.ts
│ │ ├── kimi.ts
│ │ ├── minimax.ts
│ │ ├── moonshot.ts
│ │ ├── ollama.ts
│ │ ├── oneapi.ts
│ │ ├── openai.ts
│ │ └── openrouter.ts
│ ├── geo-data.ts
│ ├── geo.ts
│ ├── highlight-in-textarea
│ │ ├── index.css
│ │ └── index.ts
│ ├── hooks
│ │ ├── global.ts
│ │ ├── useAutoExpand.ts
│ │ ├── useCollectedWordTotal.ts
│ │ ├── useCurrentThemeType.ts
│ │ ├── useMemoWindow.ts
│ │ ├── useSettings.ts
│ │ ├── useStoredAction.ts
│ │ ├── useTheme.ts
│ │ ├── useThemeDetector.ts
│ │ └── useThemeType.ts
│ ├── i18n.js
│ ├── i18n
│ │ └── locales
│ │ │ ├── ar
│ │ │ └── translation.json
│ │ │ ├── de
│ │ │ └── translation.json
│ │ │ ├── en
│ │ │ └── translation.json
│ │ │ ├── fr
│ │ │ └── translation.json
│ │ │ ├── hi
│ │ │ └── translation.json
│ │ │ ├── ja
│ │ │ └── translation.json
│ │ │ ├── ko
│ │ │ └── translation.json
│ │ │ ├── ru
│ │ │ └── translation.json
│ │ │ ├── th
│ │ │ └── translation.json
│ │ │ ├── zh-Hans
│ │ │ └── translation.json
│ │ │ └── zh-Hant
│ │ │ └── translation.json
│ ├── internal-services
│ │ ├── action.ts
│ │ └── db.ts
│ ├── polyfills
│ │ ├── electron.ts
│ │ ├── tauri.ts
│ │ └── userscript.ts
│ ├── services
│ │ ├── Arabic.json
│ │ ├── Chinese.json
│ │ ├── English.json
│ │ ├── French.json
│ │ ├── German.json
│ │ ├── Hindi.json
│ │ ├── Japanese.json
│ │ ├── Korean.json
│ │ ├── Russian.json
│ │ ├── Thai.json
│ │ ├── TraditionalChinese.json
│ │ ├── github.ts
│ │ └── vocabulary.ts
│ ├── state
│ │ └── UserProvider.tsx
│ ├── traditional-or-simplified.ts
│ ├── translate.ts
│ ├── tts
│ │ ├── edge-tts.ts
│ │ ├── index.ts
│ │ └── types.ts
│ ├── types.ts
│ ├── universal-fetch.ts
│ ├── utils.ts
│ ├── utils
│ │ └── format.ts
│ └── youglish
│ │ ├── widget.js
│ │ └── youglish.jsx
├── const
│ ├── layoutTokens.ts
│ ├── message.ts
│ └── meta.ts
├── hooks
│ └── useClerkUser.ts
├── index.d.ts
├── openai-translator.lnk
├── store
│ └── file
│ │ ├── helpers.ts
│ │ ├── index.ts
│ │ ├── initialState.ts
│ │ ├── middleware
│ │ ├── createDevtools.ts
│ │ └── createHyperStorage
│ │ │ ├── index.test.ts
│ │ │ ├── index.ts
│ │ │ ├── indexedDB.test.ts
│ │ │ ├── indexedDB.ts
│ │ │ ├── keyMapper.ts
│ │ │ ├── localStorage.ts
│ │ │ ├── type.ts
│ │ │ ├── urlStorage.test.ts
│ │ │ └── urlStorage.ts
│ │ ├── selectors.ts
│ │ ├── slices
│ │ ├── action
│ │ │ ├── action.ts
│ │ │ └── initialState.ts
│ │ ├── chat
│ │ │ ├── action.ts
│ │ │ └── initialState.ts
│ │ ├── component
│ │ │ ├── action.ts
│ │ │ └── initialState.ts
│ │ ├── file
│ │ │ ├── action.ts
│ │ │ ├── initialState.ts
│ │ │ └── selectors.ts
│ │ ├── user
│ │ │ ├── action.ts
│ │ │ └── initialState.ts
│ │ └── word
│ │ │ ├── __tests__
│ │ │ ├── addMessageToHistory.test.ts
│ │ │ └── setupTests.ts
│ │ │ ├── action.ts
│ │ │ └── initialState.ts
│ │ └── store.ts
├── tauri
│ ├── App.tsx
│ ├── Window.tsx
│ ├── action_manager.html
│ ├── action_manager.tsx
│ ├── index.html
│ ├── index.tsx
│ ├── thumb.html
│ ├── thumb.tsx
│ └── utils.ts
├── types
│ ├── agent
│ │ └── index.ts
│ ├── fetch.ts
│ ├── llm.ts
│ ├── meta.ts
│ ├── openai
│ │ ├── chat.ts
│ │ ├── functionCall.ts
│ │ ├── image.ts
│ │ └── plugin.ts
│ ├── session.ts
│ ├── settings.ts
│ └── topic.ts
└── utils
│ ├── auth.ts
│ ├── merge.ts
│ ├── storeDebug.ts
│ ├── swr
│ └── index.ts
│ └── uuid.ts
├── tsconfig.json
├── typos.toml
├── vite
├── vite-env.d.ts
├── vite.config.chromium.ts
├── vite.config.firefox.ts
├── vite.config.tauri.ts
└── vite.config.userscript.ts
/.cursorrules:
--------------------------------------------------------------------------------
1 | ## 项目概述
2 |
3 | 这是一个基于 React + TypeScript 构建的浏览器扩展。
4 |
5 | ## 技术栈
6 |
7 | - 前端框架: React + TypeScript
8 | - 状态管理: Zustand
9 | - UI 组件: Base Web UI
10 | - 国际化: i18next
11 | - 存储: 本地存储 + IndexedDB、
12 | - 代码规范: ESLint + Prettier
13 | - 测试: Jest
14 |
15 | src/
16 | ├── browser-extension/ # 浏览器扩展相关代码
17 | ├── common/
18 | │ ├── components/ # 公共组件
19 | │ ├── engines/ # AI 引擎适配层
20 | │ ├── hooks/ # 自定义 Hooks
21 | │ ├── i18n/ # 国际化配置
22 | │ ├── internal-services/ # 内部服务
23 | │ └── store/ # 状态管理
24 | │
25 | ├── public/ # 公共资源
26 | ├── types/ # 类型
27 | ├── utils/ # 工具函数
28 | └── vite.config.ts # Vite 配置
29 | └── jest.config.ts # Jest 配置
30 |
31 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | public
2 | dist
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es2021: true,
5 | jest: true,
6 | node: true,
7 | },
8 | extends: ['eslint:recommended', 'plugin:react/recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
9 | parser: '@typescript-eslint/parser',
10 | parserOptions: {
11 | project: true,
12 | tsconfigRootDir: __dirname,
13 | ecmaVersion: 'latest',
14 | sourceType: 'module',
15 | },
16 | root: true,
17 | plugins: ['react', 'react-hooks', '@typescript-eslint', 'prettier', 'baseui'],
18 | rules: {
19 | 'react/prop-types': 'off',
20 | 'react/react-in-jsx-scope': 'off',
21 | 'camelcase': 'off',
22 | 'react/display-name': 'off',
23 | 'eqeqeq': ['error', 'always'],
24 | 'spaced-comment': 'error',
25 | 'no-duplicate-imports': 'error',
26 | 'baseui/deprecated-theme-api': 'warn',
27 | 'baseui/deprecated-component-api': 'warn',
28 | 'baseui/no-deep-imports': 'warn',
29 | 'prettier/prettier': 'error',
30 | 'react-hooks/rules-of-hooks': 'error',
31 | 'react-hooks/exhaustive-deps': 'warn',
32 | },
33 | settings: {
34 | 'import/resolver': {
35 | typescript: {},
36 | },
37 | 'react': {
38 | version: 'detect',
39 | },
40 | },
41 | }
42 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Ask a question or get support
4 | url: https://github.com/GPT-language/gpt-tutor-for-chrome/discussions/categories/q-a
5 | about: Ask a question or request support for GPT-Tutor
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature.yml:
--------------------------------------------------------------------------------
1 | name: Feature
2 | description: Add new feature, improve code, and more
3 | labels: [ "enhancement" ]
4 | body:
5 | - type: markdown
6 | attributes:
7 | value: |
8 | **非常感谢您的功能建议!Thank you very much for your feature proposal!**
9 | - type: checkboxes
10 | attributes:
11 | label: Search before asking
12 | description: >
13 | 请在提交前搜索 [issues](https://github.com/yetone/openai-translator/issues),以查看您的问题是否已经被提交。
14 |
15 | Please search [issues](https://github.com/yetone/openai-translator/issues) to check if your issue has already been reported.
16 | options:
17 | - label: >
18 | 在 [issues](https://github.com/yetone/openai-translator/issues) 中没有找到类似的内容。
19 |
20 | I searched in the [issues](https://github.com/yetone/openai-translator/issues) and found nothing similar.
21 | required: true
22 | - type: input
23 | attributes:
24 | label: feature
25 | description: >
26 | 一句话概括你的功能建议。Please provide a brief description of your feature proposal.
27 | validations:
28 | required: true
29 | - type: textarea
30 | attributes:
31 | label: 描述 Motivation
32 | description: >
33 | 解释一下这个功能将如何解决您的问题。
34 |
35 | Explain how this feature will resolve your problem, including the way it addresses the issue that you are facing.
36 | validations:
37 | required: true
38 | - type: textarea
39 | attributes:
40 | label: Solution
41 | description: 描述建议的解决方案。Describe the proposed solution. (if you have any additional information, please add it here.)
42 | - type: textarea
43 | attributes:
44 | label: 还有其他内容吗?Anything else?
45 | - type: checkboxes
46 | attributes:
47 | label: 你是否愿意提交一份 PR?Are you willing to submit a PR?
48 | description: >
49 | 我们期待开发人员和用户的帮助,以解决在 GPT-Tutor 中发现的任何问题。 如果您愿意通过提交 PR 来解决此问题,请勾选。We eagerly anticipate developers' and users' support and collaboration in resolving any issues found in GPT-Tutor. If you are willing to offer a solution by submitting a PR to fix this matter, kindly mark the checkbox provided.
50 | options:
51 | - label: 我愿意提供 PR! I'm willing to submit a PR!
52 | - type: markdown
53 | attributes:
54 | value: "非常感谢您的功能建议!Thank you very much for your feature proposal!"
55 |
56 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: github-actions
4 | directory: /
5 | schedule:
6 | interval: weekly
7 | - package-ecosystem: npm
8 | directory: /
9 | schedule:
10 | interval: weekly
11 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yaml:
--------------------------------------------------------------------------------
1 | name: Lint
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | jobs:
12 | typos-check:
13 | name: Spell Check with Typos
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: Checkout Actions Repository
17 | uses: actions/checkout@v4
18 | - name: Check spelling with custom config file
19 | uses: crate-ci/typos@v1.24.5
20 | with:
21 | config: ./typos.toml
22 |
23 | eslint:
24 | runs-on: ubuntu-latest
25 |
26 | steps:
27 | - uses: actions/checkout@v4
28 |
29 | - uses: pnpm/action-setup@v4
30 | with:
31 | version: 8.6.0
32 |
33 | - uses: actions/setup-node@v3
34 | with:
35 | node-version: 18
36 | cache: 'pnpm'
37 |
38 | - name: Install packages
39 | run: pnpm install --no-frozen-lockfile
40 |
41 | - name: Run type check
42 | run: pnpm tsc
43 |
44 | - name: Run eslint
45 | run: pnpm lint
46 |
--------------------------------------------------------------------------------
/.github/workflows/playwright.yml:
--------------------------------------------------------------------------------
1 | name: Playwright Tests
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | jobs:
12 | e2e-test:
13 | timeout-minutes: 60
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v4
17 |
18 | - uses: pnpm/action-setup@v4
19 | with:
20 | version: 8.6.0
21 |
22 | - uses: actions/setup-node@v3
23 | with:
24 | node-version: 18
25 | cache: 'pnpm'
26 |
27 | - name: Install dependencies
28 | run: pnpm install --no-frozen-lockfile
29 |
30 | - name: Install Playwright Browsers
31 | run: pnpm exec playwright install chromium --with-deps
32 |
33 | - name: Build Chromium extension
34 | run: pnpm vite build -c vite.config.chromium.ts
35 |
36 | - name: Run Playwright tests
37 | run: pnpm test:e2e --reporter github
38 |
--------------------------------------------------------------------------------
/.github/workflows/test-build.yaml:
--------------------------------------------------------------------------------
1 | name: Test Build
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | build-tauri:
8 | permissions:
9 | contents: write
10 | strategy:
11 | fail-fast: false
12 | matrix:
13 | platform: [macos-latest, ubuntu-20.04, windows-latest]
14 |
15 | runs-on: ${{ matrix.platform }}
16 | steps:
17 | - uses: actions/checkout@v4
18 |
19 | - uses: pnpm/action-setup@v4
20 | with:
21 | version: 8.6.0
22 |
23 | - uses: actions/setup-node@v3
24 | with:
25 | node-version: 18
26 | cache: 'pnpm'
27 |
28 | - name: install Rust stable
29 | uses: dtolnay/rust-toolchain@stable
30 |
31 | - name: install dependencies (ubuntu only)
32 | if: matrix.platform == 'ubuntu-20.04'
33 | run: |
34 | sudo apt-get update
35 | sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf libx11-dev libxdo-dev libxcb-shape0-dev libxcb-xfixes0-dev
36 |
37 | - name: install dependencies (mac only)
38 | if: matrix.platform == 'macos-latest'
39 | run: |
40 | rustup target add aarch64-apple-darwin
41 |
42 | - name: install frontend dependencies
43 | run: pnpm install --no-frozen-lockfile # change this to npm or pnpm depending on which one you use
44 |
45 | - name: Build Tauri App (MacOS Universal)
46 | uses: tauri-apps/tauri-action@dev
47 | if: matrix.platform == 'macos-latest'
48 | id: tauri-action-mac
49 | env:
50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
51 | with:
52 | releaseId: test-release
53 | args: --target universal-apple-darwin
54 |
55 | - name: Build Tauri App
56 | uses: tauri-apps/tauri-action@dev
57 | if: matrix.platform != 'macos-latest'
58 | id: tauri-action
59 | env:
60 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
61 | with:
62 | releaseId: test-release
63 |
64 | - name: Upload artifact
65 | uses: actions/upload-artifact@v4
66 | with:
67 | name: tauri-client-app-artifact
68 | path: |
69 | ${{ fromJSON(steps.tauri-action-mac.outputs.artifactPaths)[0] }}
70 | ${{ fromJSON(steps.tauri-action.outputs.artifactPaths)[0] }}
71 |
72 |
73 | build-browser-extension:
74 | runs-on: ubuntu-22.04
75 |
76 | steps:
77 | - uses: actions/checkout@v4
78 |
79 | - uses: pnpm/action-setup@v4
80 | with:
81 | version: 8.6.0
82 |
83 | - uses: actions/setup-node@v3
84 | with:
85 | node-version: 18
86 | cache: 'pnpm'
87 |
88 | - name: Install dependencies
89 | run: pnpm install --no-frozen-lockfile
90 |
91 | - name: Build
92 | run: make build-browser-extension
93 | # todo: upload browser extension artifacts
94 |
--------------------------------------------------------------------------------
/.github/workflows/unit-test.yaml:
--------------------------------------------------------------------------------
1 | name: Unit Test
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | jobs:
12 | unit-test:
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v4
17 |
18 | - uses: pnpm/action-setup@v4
19 | with:
20 | version: 8.6.0
21 |
22 | - uses: actions/setup-node@v3
23 | with:
24 | node-version: 18
25 | cache: 'pnpm'
26 |
27 | - name: Install packages
28 | run: pnpm install --no-frozen-lockfile
29 |
30 | - name: Run test
31 | run: pnpm test
32 |
--------------------------------------------------------------------------------
/.github/workflows/winget.yml:
--------------------------------------------------------------------------------
1 | name: Publish to WinGet
2 | on:
3 | release:
4 | types: [released]
5 | jobs:
6 | publish:
7 | # Action can only be run on windows
8 | runs-on: windows-latest
9 | steps:
10 | - uses: vedantmgoyal2009/winget-releaser@v2
11 | with:
12 | identifier: yetone.OpenAITranslator
13 | max-versions-to-keep: 5 # keep only latest 5 versions
14 | installers-regex: '\.msi$' # Only .msi files
15 | token: ${{ secrets.WINGET_TOKEN }}
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | npm-debug.log
2 | node_modules/
3 | dist/
4 | tmp/
5 | release/
6 | yarn-error.log
7 | npm-error.log
8 | .DS_Store
9 | *.json-e
10 | *.js-e
11 |
12 | # 环境变量文件
13 | .env
14 | .env.local
15 | .env.*
16 | !.env.example
17 |
18 | .vscode
19 | .idea
20 | .eslintcache
21 | .yarn
22 | .yarnrc.yml
23 | .env
24 | /test-results/
25 | /playwright-report/
26 | /playwright/.cache/
27 |
--------------------------------------------------------------------------------
/.plasmo/cache/parcel/data.mdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/.plasmo/cache/parcel/data.mdb
--------------------------------------------------------------------------------
/.plasmo/cache/parcel/lock.mdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/.plasmo/cache/parcel/lock.mdb
--------------------------------------------------------------------------------
/.plasmo/chrome-mv3.plasmo.manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "icons": {
3 | "16": "./gen-assets/icon16.plasmo.png",
4 | "32": "./gen-assets/icon32.plasmo.png",
5 | "48": "./gen-assets/icon48.plasmo.png",
6 | "64": "./gen-assets/icon64.plasmo.png",
7 | "128": "./gen-assets/icon128.plasmo.png"
8 | },
9 | "manifest_version": 3,
10 | "action": {
11 | "default_icon": {
12 | "16": "./gen-assets/icon16.plasmo.png",
13 | "32": "./gen-assets/icon32.plasmo.png",
14 | "48": "./gen-assets/icon48.plasmo.png",
15 | "64": "./gen-assets/icon64.plasmo.png",
16 | "128": "./gen-assets/icon128.plasmo.png"
17 | }
18 | },
19 | "version": "1.0.1.7",
20 | "author": "BlackStar1453",
21 | "name": "DEV | ",
22 | "description": "gpt-tutor",
23 | "permissions": [
24 | "storage"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | singleQuote: true,
3 | jsxSingleQuote: true,
4 | semi: false,
5 | trailingComma: 'es5',
6 | tabWidth: 4,
7 | useTabs: false,
8 | quoteProps: 'consistent',
9 | bracketSpacing: true,
10 | printWidth: 120,
11 | }
12 |
--------------------------------------------------------------------------------
/8.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/8.gif
--------------------------------------------------------------------------------
/CHANGELOG-CN.md:
--------------------------------------------------------------------------------
1 | # 更新日志
2 |
3 | 本项目的所有显著变化都将记录在此文件中。
4 |
5 | 格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
6 | 并且本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
7 |
8 | ## [Unreleased]
9 |
10 | ### 待完成
11 | - [x] 阅读模式。
12 | - [x] 功能组商店。
13 | - [ ] 内置oneAPI。
14 | - [x] 设计新的UI,现在将区分单词、句子、写作三部分,每一部分使用不同的UI和功能组。
15 | - [ ] 完成雅思学习的功能组设计。
16 | - [ ] 实现打印功能。
17 | - [ ] 实现通过AI快速实现一个功能和功能组的功能。
18 | - [ ] 实现通过GitHub来管理和远程存储功能组,避免丢失。
19 |
20 | ### 待修复
21 | - [x] 应该将添加到复习和添加到anki的按钮移动到一个更合适的位置,区分添加整个单词和单词部分内容的功能。
22 |
23 |
24 | ## [1.0.1.7] - 2024-12-08
25 |
26 | ### 🎉新增
27 |
28 | - 新增操作指引。解释主界面的使用。
29 | - 新增功能组商店。支持根据自己的需要购买和更新、定制需要的功能组。
30 |
31 | ### 🐛修复
32 | - 修复了在阅读模式下,无法使用@符号来呼出GPT-Tutor功能的问题。
33 | - 修复了答案被错误保存的问题。
34 |
35 | ### 🎉新增
36 | - 新增阅读模式。更方便地通过gpt-tutor来阅读网页内容。
37 |
38 | ## [1.0.1.4] - 2024-11-09
39 |
40 | ### 🎉新增
41 | - 新增阅读模式。更方便地通过gpt-tutor来阅读网页内容。
42 | - 新增第三方API提供商:openRouter。
43 |
44 | 
45 |
46 | ### 🔄变更
47 | - 删除内置的复习功能,改为通过Anki来实现。
48 | - 删除初始页面中clerk的使用,现在不再需要登录使用。
49 | - 使用zustand来完成整个状态的管理。
50 |
51 | ### 🐛修复
52 |
53 |
54 | ## [1.0.1.3] - 2024-09-14
55 |
56 | ### 🎉新增
57 | - 重设了主页面的UI。现在使用起来将更加直观和简洁。
58 | - 通过选择顶部的Tab来选择功能组,点击右侧的“更多”按钮来查看剩余的功能组和其它设置。
59 | - 现在通过@符号可以快速呼出GPT-Tutor的相关功能,比如在选择单词的tab后,输入@可以获取单词相关的功能。
60 | - 历史记录、复习记录和单词列表默认将显示在侧边栏中,通常为隐藏状态,你可以通过点击左上角的|<来打开。
61 |
62 | 
63 |
64 | - 在功能管理器中,在内置的功能组中新增了一个反馈按钮,你可以在这里提交对某个功能的反馈。
65 | - 在回答框中新增了一个?按钮,如果你对当前回答有疑问,可以点击它来查看完善方案。
66 |
67 | ### 🔄变更
68 | - 将添加到复习的按钮移动到了答案框中,点击后将该单词的所有内容添加到复习中。
69 | - 现在非管理员和订阅用户无法修改内置的功能,以免出现不必要的错误。
70 | - 将上传词书的功能移动到了下拉菜单中。
71 |
72 | ### 🐛修复
73 | - 修复Youglish组件在隐藏时仍然会触发的问题。
74 |
75 |
76 |
77 |
78 | ## [1.0.1.2] - 2024-09-04
79 |
80 | ### 🎉新增
81 | - 所有内置的功能组现在将通过远程仓库获取(之前从本地加载),并且在更新后可以随时获取到最新版本。
82 | - 在动作管理器(ActionManager)中添加了 商店(Store) 组件,后续你可以在这里上传你的功能组来获得API Key的使用额度,也可以在这里购买和定制功能组 (`src/common/components/ActionStore.tsx`)。
83 | - 新增反馈功能的设置。当对某个内置的功能存在疑问时,可以在动作管理器中或者点击回答页面中的问号按钮提交反馈。
84 |
85 | ### 🔄变更
86 | - 现在区分了用户自己创建的功能和GPT-Tutor内置的功能。为保证功能能够正常使用,用户将无法删除或修改内置的功能(但能够查看)。
87 | - 重新设置了底部按钮的使用逻辑。删除“继续”和“下一个”按钮,只保留添加到复习的按钮。
88 | - 删除了动作管理器中辅助动作的设置。
89 | - 删除了动作管理器中输出格式中的JSON的设置。
90 |
91 | ### 🐛修复
92 | - 修复了在通过输入来查询(而不是通过选择右侧List中的单词)时,生成的回答没有正确显示的问题。
93 | - 修复使用ChatGLM或Kimi时,初次打开页面时会跳转到设置页面的问题。
94 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | VERSION ?= 1.0.1.7
2 |
3 | clean:
4 | rm -rf dist
5 |
6 | change-version:
7 | sed -i -e "s/\"version\": \".*\"/\"version\": \"$(VERSION)\"/" src/browser-extension/manifest.json
8 |
9 |
10 | change-package-version:
11 | sed -i -e "s/\"version\": \".*\"/\"version\": \"$(VERSION)\"/" package.json
12 |
13 | build-browser-extension: change-version change-package-version
14 | pnpm vite build -c vite.config.chromium.ts
15 | cd dist/browser-extension/chromium && zip -r ../chromium.zip .
16 |
17 | build-userscript: change-package-version
18 | pnpm vite build -c vite.config.userscript.ts
19 |
20 | build-popclip-extension:
21 | rm -f dist/openai-translator.popclipextz
22 | mkdir -p dist/openai-translator.popclipext
23 | cp -r clip-extensions/popclip/* dist/openai-translator.popclipext
24 | cd dist && zip -r openai-translator.popclipextz openai-translator.popclipext && rm -r openai-translator.popclipext
25 |
26 | build-snipdo-extension:
27 | rm -f dist/openai-translator.pbar
28 | zip -j -r dist/openai-translator.pbar clip-extensions/snipdo/*
29 |
--------------------------------------------------------------------------------
/clip-extensions/popclip/Config.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Actions
6 |
7 |
8 | Shell Script File
9 | openai-translator.sh
10 | Image File
11 | icon.png
12 | Title
13 | OpenAI Translator
14 |
15 |
16 | Credits
17 |
18 |
19 | Link
20 | https://github.com/openai-translator/openai-translator
21 | Name
22 | OpenAI Translator
23 |
24 |
25 | Extension Description
26 | Translate text with OpenAI Translator.
27 | Extension Identifier
28 | xyz.yetone.apps.openai-translator.clip-extensions.popclip
29 | Extension Image File
30 | openai-translator.png
31 | Extension Name
32 | OpenAI Translator
33 | Required OS Version
34 | 10.13
35 |
36 |
37 |
--------------------------------------------------------------------------------
/clip-extensions/popclip/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/clip-extensions/popclip/icon.png
--------------------------------------------------------------------------------
/clip-extensions/popclip/openai-translator.sh:
--------------------------------------------------------------------------------
1 | send_text() {
2 | curl -d "$POPCLIP_TEXT" --unix-socket /tmp/openai-translator.sock http://openai-translator
3 | }
4 |
5 | if ! send_text; then
6 | open -g -a OpenAI\ Translator
7 | sleep 2
8 | send_text
9 | fi
10 |
--------------------------------------------------------------------------------
/clip-extensions/snipdo/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/clip-extensions/snipdo/icon.png
--------------------------------------------------------------------------------
/clip-extensions/snipdo/openai-translator.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "OpenAI Translator",
3 | "identifier": "xyz.yetone.apps.openai-translator.clip-extensions.snipdo",
4 | "icon": "icon.png",
5 | "actions": [
6 | {
7 | "title": "OpenAI Translator",
8 | "icon": "icon.png",
9 | "powershellFile": "openai-translator.ps1"
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/clip-extensions/snipdo/openai-translator.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:62007 -Method POST -Body $encode_text -UseBasicParsing
8 |
--------------------------------------------------------------------------------
/e2e/common.ts:
--------------------------------------------------------------------------------
1 | import { Page } from '@playwright/test'
2 |
3 | export function getOptionsPageUrl(extensionId: string) {
4 | return `chrome-extension://${extensionId}/src/browser-extension/options/index.html`
5 | }
6 |
7 | export function getPopupPageUrl(extensionId: string) {
8 | return `chrome-extension://${extensionId}/src/browser-extension/popup/index.html`
9 | }
10 |
11 | export async function selectExampleText(page: Page) {
12 | const textLocator = page.getByTestId('example-text')
13 | const boundingBox = await textLocator.boundingBox()
14 | if (boundingBox) {
15 | // select text
16 | await page.mouse.move(boundingBox.x, boundingBox.y)
17 | await page.mouse.down()
18 | await page.mouse.move(boundingBox.x + boundingBox.width, boundingBox.y + boundingBox.height)
19 | await page.mouse.up()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/e2e/fixtures.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path'
2 | import { type BrowserContext, test as base, chromium } from '@playwright/test'
3 |
4 | export const extensionPath = path.join(__dirname, '../dist/browser-extension/chromium')
5 |
6 | export const test = base.extend<{
7 | context: BrowserContext
8 | extensionId: string
9 | }>({
10 | context: async ({ headless }, use) => {
11 | const context = await chromium.launchPersistentContext('', {
12 | headless,
13 | args: [
14 | ...(headless ? ['--headless=new'] : []),
15 | `--disable-extensions-except=${extensionPath}`,
16 | `--load-extension=${extensionPath}`,
17 | ],
18 | })
19 | await use(context)
20 | await context.close()
21 | },
22 | extensionId: async ({ context }, use) => {
23 | // for manifest v3:
24 | let [background] = context.serviceWorkers()
25 | if (!background) background = await context.waitForEvent('serviceworker')
26 |
27 | const extensionId = background.url().split('/')[2]
28 | await use(extensionId)
29 | },
30 | })
31 |
32 | export const expect = test.expect
33 |
--------------------------------------------------------------------------------
/e2e/hotkey.spec.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path'
2 | import { getOptionsPageUrl, selectExampleText } from './common'
3 | import { expect, test } from './fixtures'
4 | import { containerID, popupCardID } from '../src/browser-extension/content_script/consts'
5 |
6 | test.fixme('hotkey should work', async ({ page, extensionId }) => {
7 | await test.step('set hotkey', async () => {
8 | await page.goto(getOptionsPageUrl(extensionId))
9 | const input = page.locator('input[name="apiKey"]')
10 | await input.fill('fake-api-key')
11 | await page.getByTestId('hotkey-recorder').click()
12 | await page.keyboard.down('Alt')
13 | await page.keyboard.down('x')
14 | await page.keyboard.up('Alt')
15 | await page.keyboard.up('x')
16 | await page.getByText('Save').click()
17 | })
18 |
19 | const popupCard = await test.step('select example text', async () => {
20 | await page.goto(`file:${path.join(__dirname, 'test.html')}`)
21 | await selectExampleText(page)
22 |
23 | const container = page.locator(`#${containerID}`)
24 | await container.waitFor({ state: 'attached' })
25 |
26 | await page.keyboard.down('Alt')
27 | await page.keyboard.press('x')
28 |
29 | return container.locator(`#${popupCardID}`)
30 | })
31 |
32 | await expect(popupCard).toBeVisible()
33 | })
34 |
--------------------------------------------------------------------------------
/e2e/index.spec.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path'
2 | import { expect, test } from './fixtures'
3 | import { getOptionsPageUrl, getPopupPageUrl, selectExampleText } from './common'
4 | import { containerID, popupThumbID, popupCardID } from '../src/browser-extension/content_script/consts'
5 |
6 | test('popup card should be visible', async ({ page }) => {
7 | await page.goto(`file:${path.join(__dirname, 'test.html')}`)
8 | await selectExampleText(page)
9 |
10 | const container = page.locator(`#${containerID}`)
11 | const thumb = container.locator(`#${popupThumbID}`)
12 | await expect(thumb).toBeVisible()
13 | await thumb.click()
14 | const popupCard = container.locator(`#${popupCardID}`)
15 | await expect(popupCard).toBeVisible()
16 | })
17 |
18 | test('popup page should be opened', async ({ page, extensionId }) => {
19 | await page.goto(getPopupPageUrl(extensionId))
20 | await expect(page.getByTestId('popup-container')).toBeVisible()
21 | })
22 |
23 | test('options page should be opened', async ({ page, extensionId }) => {
24 | await page.goto(getOptionsPageUrl(extensionId))
25 | await expect(page.getByTestId('settings-container')).toBeVisible()
26 | })
27 |
--------------------------------------------------------------------------------
/e2e/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | E2E Testing
9 |
10 |
11 |
12 | example text
13 |
14 |
15 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'ts-jest',
3 | testEnvironment: 'jsdom',
4 | moduleNameMapper: {
5 | '^@/(.*)$': '/src/$1'
6 | },
7 | transform: {
8 | '^.+\\.tsx?$': ['ts-jest', {
9 | tsconfig: {
10 | "jsx": "react",
11 | "esModuleInterop": true,
12 | }
13 | }]
14 | },
15 | testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
16 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
17 | };
18 |
--------------------------------------------------------------------------------
/make:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/make
--------------------------------------------------------------------------------
/playwright.config.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @see {@link https://playwright.dev/docs/chrome-extensions Chrome extensions | Playwright}
3 | */
4 | import { defineConfig } from '@playwright/test'
5 |
6 | export default defineConfig({
7 | testDir: './e2e',
8 | retries: 2,
9 | })
10 |
--------------------------------------------------------------------------------
/public/8.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/8.gif
--------------------------------------------------------------------------------
/public/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/icon.png
--------------------------------------------------------------------------------
/public/image-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/image-1.png
--------------------------------------------------------------------------------
/public/image-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/image-10.png
--------------------------------------------------------------------------------
/public/image-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/image-11.png
--------------------------------------------------------------------------------
/public/image-12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/image-12.png
--------------------------------------------------------------------------------
/public/image-13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/image-13.png
--------------------------------------------------------------------------------
/public/image-14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/image-14.png
--------------------------------------------------------------------------------
/public/image-15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/image-15.png
--------------------------------------------------------------------------------
/public/image-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/image-2.png
--------------------------------------------------------------------------------
/public/image-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/image-3.png
--------------------------------------------------------------------------------
/public/image-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/image-4.png
--------------------------------------------------------------------------------
/public/image-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/image-5.png
--------------------------------------------------------------------------------
/public/image-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/image-6.png
--------------------------------------------------------------------------------
/public/image-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/image-7.png
--------------------------------------------------------------------------------
/public/image-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/image-8.png
--------------------------------------------------------------------------------
/public/image-9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/public/image-9.png
--------------------------------------------------------------------------------
/public/rules.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 1,
4 | "action": {
5 | "type": "modifyHeaders",
6 | "requestHeaders": [
7 | {
8 | "operation": "set",
9 | "header": "origin",
10 | "value": "https://www.bing.com"
11 | },
12 | {
13 | "operation": "set",
14 | "header": "referer",
15 | "value": "https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx"
16 | }
17 | ]
18 | },
19 | "condition": {
20 | "requestDomains": ["sydney.bing.com", "www.bing.com"],
21 | "resourceTypes": ["xmlhttprequest", "websocket"]
22 | }
23 | },
24 | {
25 | "id": 2,
26 | "action": {
27 | "type": "modifyHeaders",
28 | "requestHeaders": [
29 | {
30 | "operation": "set",
31 | "header": "origin",
32 | "value": "https://chatgpt.com"
33 | },
34 | {
35 | "operation": "set",
36 | "header": "referer",
37 | "value": "https://chatgpt.com"
38 | }
39 | ]
40 | },
41 | "condition": {
42 | "requestDomains": ["chatgpt.com"],
43 | "resourceTypes": ["xmlhttprequest"]
44 | }
45 | },
46 | {
47 | "id": 3,
48 | "action": {
49 | "type": "modifyHeaders",
50 | "requestHeaders": [
51 | {
52 | "operation": "set",
53 | "header": "origin",
54 | "value": "https://tcr9i.chat.openai.com"
55 | },
56 | {
57 | "operation": "set",
58 | "header": "referer",
59 | "value": "https://tcr9i.chat.openai.com/v2/2.5.0/enforcement.13af146b6f5532afc450f0718859ea0f.html"
60 | }
61 | ]
62 | },
63 | "condition": {
64 | "requestDomains": ["https://tcr9i.chat.openai.com"],
65 | "resourceTypes": ["xmlhttprequest"]
66 | }
67 | }
68 | ]
69 |
--------------------------------------------------------------------------------
/readingMode_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/readingMode_1.gif
--------------------------------------------------------------------------------
/scripts/release.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 | from functools import reduce
3 |
4 | def get_current_version_from_tag():
5 | subprocess.check_output(['git', 'pull', 'origin', 'main', '-r'])
6 | subprocess.check_output(['git', 'fetch', '--tags'])
7 | """Get the current version from the latest tag in the repository."""
8 | # Get the latest tag in the repository.
9 | tag = subprocess.check_output(['git', 'describe', '--tags', '--abbrev=0']).decode('utf-8').strip()
10 | # Get the current version from the tag.
11 | version = tag.lstrip('v')
12 | # Return the current version.
13 | return version
14 |
15 | def generate_new_version():
16 | """Generate the new version for the current release."""
17 | # Get the current version.
18 | previous_version = get_current_version_from_tag()
19 | # Get the new version.
20 | new_version = previous_version.split('.')
21 | new_version[-1] = str(int(new_version[-1]) + 1)
22 | new_version = '.'.join(new_version)
23 | # Return the new version.
24 | return new_version
25 |
26 | def generate_release_note():
27 | """Generate the release note for the current version."""
28 | # Get the current version.
29 | previous_version = get_current_version_from_tag()
30 | # Get the release note.
31 | release_note = subprocess.check_output(['git', 'log', '--pretty="%s"', 'v' + previous_version + '..HEAD']).decode('utf-8').strip()
32 | # Return the release note.
33 | return release_note
34 |
35 | def create_new_tag():
36 | """Create a new tag for the current release."""
37 | # Get the current version.
38 | new_version = generate_new_version()
39 | release_note = generate_release_note()
40 |
41 | release_notes = []
42 | seen = set()
43 | for line in release_note.split('\n'):
44 | line = line.strip().strip('"')
45 | t_, _, _ = line.partition(':')
46 | if t_.lower() not in ('fix', 'feat', 'docs', 'refactor'):
47 | continue
48 | if line in seen:
49 | continue
50 | release_notes.append(line)
51 | seen.add(line)
52 | args = ['git', 'tag', '-a', 'v' + new_version] + flatten([['-m', line] for line in release_notes])
53 | # Create a new tag for the current release.
54 | return subprocess.check_output(args)
55 |
56 | def flatten(l):
57 | return reduce(lambda x, y: x + y, l)
58 |
59 | def main():
60 | print(create_new_tag())
61 |
62 | if __name__ == '__main__':
63 | main()
64 |
--------------------------------------------------------------------------------
/src-tauri/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | /target/
4 |
--------------------------------------------------------------------------------
/src-tauri/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "app"
3 | version = "0.1.0"
4 | description = "A Tauri App"
5 | authors = ["you"]
6 | license = ""
7 | repository = ""
8 | default-run = "app"
9 | edition = "2021"
10 | rust-version = "1.59"
11 |
12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
13 |
14 | [build-dependencies]
15 | tauri-build = { version = "1.2.1", features = [] }
16 |
17 | [dependencies]
18 | serde_json = "1.0"
19 | serde = { version = "1.0", features = ["derive"] }
20 | tauri = { version = "1.2.4", features = ["clipboard-all", "dialog-message", "fs-read-dir", "fs-read-file", "fs-write-file", "global-shortcut-all", "notification-all", "shell-open", "system-tray", "updater", "window-all", "windows7-compat"] }
21 | window-shadows = "0.2.1"
22 | once_cell = "1.17.1"
23 | clipboard = "0.5.0"
24 | enigo = {git = "https://github.com/enigo-rs/enigo"}
25 | mouse_position = "0.1.3"
26 | rdev = "0.5.2"
27 | tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev", version = "0.1.0" }
28 | tauri-plugin-autostart = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" }
29 | cpuid = "0.1.1"
30 | sysinfo = "0.28.3"
31 | parking_lot = "0.12.1"
32 | mouce = "0.2.41"
33 | tauri-plugin-window-state = "0.1.0"
34 | whatlang = "0.16.2"
35 | arboard = "3.2.0"
36 | tiny_http = "0.12.0"
37 |
38 | [target.'cfg(target_os = "macos")'.dependencies]
39 | cocoa = "0.24"
40 | objc = "0.2.7"
41 | macos-accessibility-client = "0.0.1"
42 | core-graphics = "0.22.3"
43 |
44 | [target.'cfg(windows)'.dependencies]
45 | windows = {version="0.44.0",features= ["Win32_UI_WindowsAndMessaging", "Win32_Foundation"] }
46 |
47 | [features]
48 | # by default Tauri runs in production mode
49 | # when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
50 | default = ["custom-protocol"]
51 | # this feature is used for production builds where `devPath` points to the filesystem
52 | # DO NOT remove this
53 | custom-protocol = ["tauri/custom-protocol"]
54 |
--------------------------------------------------------------------------------
/src-tauri/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | tauri_build::build()
3 | }
4 |
--------------------------------------------------------------------------------
/src-tauri/icons/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/128x128.png
--------------------------------------------------------------------------------
/src-tauri/icons/128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/128x128@2x.png
--------------------------------------------------------------------------------
/src-tauri/icons/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/32x32.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square107x107Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/Square107x107Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square142x142Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/Square142x142Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square150x150Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/Square150x150Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square284x284Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/Square284x284Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square30x30Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/Square30x30Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square310x310Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/Square310x310Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square44x44Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/Square44x44Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square71x71Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/Square71x71Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square89x89Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/Square89x89Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/StoreLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/StoreLogo.png
--------------------------------------------------------------------------------
/src-tauri/icons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/favicon.ico
--------------------------------------------------------------------------------
/src-tauri/icons/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/icon.icns
--------------------------------------------------------------------------------
/src-tauri/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/icon.ico
--------------------------------------------------------------------------------
/src-tauri/icons/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/icons/icon.png
--------------------------------------------------------------------------------
/src-tauri/resources/bin/ocr_apple:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/resources/bin/ocr_apple
--------------------------------------------------------------------------------
/src-tauri/resources/bin/ocr_intel:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GPT-language/gpt-tutor-for-chrome/3c51f2d13773890de2a17319066758938b7181f8/src-tauri/resources/bin/ocr_intel
--------------------------------------------------------------------------------
/src-tauri/resources/get-selected-text-by-ax.applescript:
--------------------------------------------------------------------------------
1 | use sys : application "System Events"
2 |
3 | -- Use the following delay to choose an application window
4 | -- and highlight some text. Then ensure that the window remains
5 | -- in focus until the script terminates.
6 | -- delay 5
7 |
8 | set P to the first application process whose frontmost is true
9 |
10 | set appName to name of P
11 |
12 | if appName is equal to "Mail" then
13 | error "not support " & appName
14 | end
15 |
16 | if appName is equal to "Safari" then
17 | try
18 | tell application "Safari"
19 | set theText to (do JavaScript "getSelection().toString()" in document 1)
20 | end tell
21 | return theText
22 | end try
23 | error "not support Safari"
24 | end
25 |
26 | set _W to a reference to the first window of P
27 |
28 | set _U to a reference to ¬
29 | (UI elements of P whose ¬
30 | name of attributes contains "AXSelectedText" and ¬
31 | value of attribute "AXSelectedText" is not "" and ¬
32 | class of value of attribute "AXSelectedText" is not class)
33 |
34 | tell sys to if (count _U) ≠ 0 then ¬
35 | return the value of ¬
36 | attribute "AXSelectedText" of ¬
37 | _U's contents's first item
38 |
39 | set _U to a reference to UI elements of _W
40 |
41 | with timeout of 1 seconds
42 | tell sys to repeat while (_U exists)
43 | tell (a reference to ¬
44 | (_U whose ¬
45 | name of attributes contains "AXSelectedText" and ¬
46 | value of attribute "AXSelectedText" is not "" and ¬
47 | class of value of attribute "AXSelectedText" is not class)) ¬
48 | to if (count) ≠ 0 then return the value of ¬
49 | attribute "AXSelectedText" of its contents's first item
50 |
51 | set _U to a reference to (UI elements of _U)
52 | end repeat
53 | end timeout
54 |
55 | error "not found AXSelectedText"
56 |
--------------------------------------------------------------------------------
/src-tauri/resources/get-selected-text.applescript:
--------------------------------------------------------------------------------
1 | use AppleScript version "2.4"
2 | use scripting additions
3 | use framework "Foundation"
4 | use framework "AppKit"
5 |
6 | tell application "System Events"
7 | set frontmostProcess to first process whose frontmost is true
8 | set appName to name of frontmostProcess
9 | end tell
10 |
11 | if appName is equal to "OpenAI Translator" then
12 | return
13 | end if
14 |
15 | -- Back up clipboard contents:
16 | set savedClipboard to the clipboard
17 |
18 | set thePasteboard to current application's NSPasteboard's generalPasteboard()
19 | set theCount to thePasteboard's changeCount()
20 |
21 | -- Copy selected text to clipboard:
22 | tell application "System Events" to keystroke "c" using {command down}
23 | delay 0.1 -- Without this, the clipboard may have stale data.
24 |
25 | if thePasteboard's changeCount() is theCount then
26 | return ""
27 | end if
28 |
29 | set theSelectedText to the clipboard
30 |
31 | set the clipboard to savedClipboard
32 |
33 | theSelectedText
34 |
--------------------------------------------------------------------------------
/src-tauri/src/config.rs:
--------------------------------------------------------------------------------
1 | use parking_lot::Mutex;
2 | use tauri::api::path::config_dir;
3 |
4 | use serde::{Deserialize, Serialize};
5 |
6 | #[derive(Debug, Serialize, Deserialize, Clone)]
7 | #[serde(rename_all = "camelCase")]
8 | pub struct Config {
9 | pub hotkey: Option,
10 | pub ocr_hotkey: Option,
11 | pub restore_previous_position: Option,
12 | pub always_show_icons: Option,
13 | pub allow_using_clipboard_when_selected_text_not_available: Option,
14 | }
15 |
16 | static CONFIG_CACHE: Mutex