neptune-web commited on
Commit
04e85f3
·
1 Parent(s): a27c5a5

feature(#72): improve the select item functionality and UI/UX

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. Extension/LICENSE +21 -0
  2. Extension/build.mjs +0 -311
  3. Extension/package-lock.json +0 -0
  4. Extension/package.json +50 -76
  5. Extension/src/_locales/en/main.json +0 -125
  6. Extension/src/_locales/i18n-react.mjs +0 -11
  7. Extension/src/_locales/i18n.mjs +0 -7
  8. Extension/src/_locales/resources.mjs +0 -7
  9. Extension/src/assets/desc.png +0 -0
  10. Extension/src/assets/img/icon-128.png +0 -0
  11. Extension/src/assets/img/icon-34.png +0 -0
  12. Extension/src/assets/img/logo.svg +7 -0
  13. Extension/src/background/commands.mjs +0 -27
  14. Extension/src/background/index.mjs +0 -77
  15. Extension/src/background/menus.mjs +0 -75
  16. Extension/src/components/ConfirmButton/index.jsx +0 -58
  17. Extension/src/components/ConversationCard/index.jsx +0 -483
  18. Extension/src/components/ConversationItem/index.jsx +0 -140
  19. Extension/src/components/CopyButton/index.jsx +0 -38
  20. Extension/src/components/DecisionCard/index.jsx +0 -125
  21. Extension/src/components/DeleteButton/index.jsx +0 -59
  22. Extension/src/components/FeedbackForChatGPTWeb/index.jsx +0 -68
  23. Extension/src/components/FloatingToolbar/index.jsx +0 -103
  24. Extension/src/components/InputBox/index.jsx +0 -117
  25. Extension/src/components/MarkdownRender/Hyperlink.jsx +0 -37
  26. Extension/src/components/MarkdownRender/markdown-without-katex.jsx +0 -38
  27. Extension/src/components/MarkdownRender/markdown.jsx +0 -42
  28. Extension/src/components/NotificationForChatGPTWeb/index.jsx +0 -90
  29. Extension/src/components/ReadButton/index.jsx +0 -61
  30. Extension/src/components/index.mjs +0 -10
  31. Extension/src/config/index.mjs +0 -170
  32. Extension/src/config/language.mjs +0 -26
  33. Extension/src/containers/Greetings/Greetings.jsx +19 -0
  34. Extension/src/content-script/index.jsx +0 -203
  35. Extension/src/content-script/menu-tools/index.mjs +0 -72
  36. Extension/src/content-script/site-adapters/index.jsx +0 -23
  37. Extension/src/content-script/styles.scss +0 -365
  38. Extension/src/fonts/SLXVc1nY6HkvangtZmpQdkhzfH5lkSscQyyS4J0.woff2 +0 -0
  39. Extension/src/fonts/SLXVc1nY6HkvangtZmpQdkhzfH5lkSscRiyS.woff2 +0 -0
  40. Extension/src/fonts/SLXVc1nY6HkvangtZmpQdkhzfH5lkSscSCyS4J0.woff2 +0 -0
  41. Extension/src/fonts/styles.css +0 -58
  42. Extension/src/hooks/use-clamp-window-size.mjs +0 -8
  43. Extension/src/hooks/use-config.mjs +0 -30
  44. Extension/src/hooks/use-theme.mjs +0 -8
  45. Extension/src/hooks/use-window-size.mjs +0 -16
  46. Extension/src/hooks/use-window-theme.mjs +0 -21
  47. Extension/src/logo.png +0 -0
  48. Extension/src/logo1.png +0 -0
  49. Extension/src/manifest.json +22 -69
  50. Extension/src/pages/Background/index.js +17 -0
Extension/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Michael Xieyang Liu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
Extension/build.mjs DELETED
@@ -1,311 +0,0 @@
1
- import archiver from 'archiver'
2
- import fs from 'fs-extra'
3
- import path from 'path'
4
- import webpack from 'webpack'
5
- import ProgressBarPlugin from 'progress-bar-webpack-plugin'
6
- import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'
7
- import MiniCssExtractPlugin from 'mini-css-extract-plugin'
8
- import TerserPlugin from 'terser-webpack-plugin'
9
- import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
10
-
11
- const outdir = 'build'
12
-
13
- const __dirname = path.resolve()
14
- const isProduction = process.argv[2] !== '--development' // --production and --analyze are both production
15
- const isAnalyzing = process.argv[2] === '--analyze'
16
-
17
- async function deleteOldDir() {
18
- await fs.rm(outdir, { recursive: true, force: true })
19
- }
20
-
21
- async function runWebpack(isWithoutKatex, isWithoutTiktoken, callback) {
22
- const shared = [
23
- 'preact',
24
- 'webextension-polyfill',
25
- '@primer/octicons-react',
26
- 'react-bootstrap-icons',
27
- 'i18next',
28
- 'react-i18next',
29
- 'react-tabs',
30
- './src/utils',
31
- './src/_locales/i18n-react',
32
- ]
33
- if (isWithoutKatex) shared.push('./src/components')
34
-
35
- const compiler = webpack({
36
- entry: {
37
- 'content-script': {
38
- import: './src/content-script/index.jsx',
39
- dependOn: 'shared',
40
- },
41
- background: {
42
- import: './src/background/index.mjs',
43
- },
44
- popup: {
45
- import: './src/popup/index.jsx',
46
- dependOn: 'shared',
47
- },
48
- IndependentPanel: {
49
- import: './src/pages/IndependentPanel/index.jsx',
50
- dependOn: 'shared',
51
- },
52
- shared: shared,
53
- },
54
- output: {
55
- filename: '[name].js',
56
- path: path.resolve(__dirname, outdir),
57
- },
58
- mode: isProduction ? 'production' : 'development',
59
- devtool: isProduction ? false : 'inline-source-map',
60
- optimization: {
61
- minimizer: [
62
- new TerserPlugin({
63
- terserOptions: {
64
- output: { ascii_only: true },
65
- },
66
- }),
67
- new CssMinimizerPlugin(),
68
- ],
69
- concatenateModules: !isAnalyzing,
70
- },
71
- plugins: [
72
- new ProgressBarPlugin({
73
- format: ' build [:bar] :percent (:elapsed seconds)',
74
- clear: false,
75
- }),
76
- new MiniCssExtractPlugin({
77
- filename: '[name].css',
78
- }),
79
- new BundleAnalyzerPlugin({
80
- analyzerMode: isAnalyzing ? 'static' : 'disable',
81
- }),
82
- ...(isWithoutKatex
83
- ? [
84
- new webpack.NormalModuleReplacementPlugin(/markdown\.jsx/, (result) => {
85
- if (result.request) {
86
- result.request = result.request.replace(
87
- 'markdown.jsx',
88
- 'markdown-without-katex.jsx',
89
- )
90
- }
91
- }),
92
- ]
93
- : []),
94
- ],
95
- resolve: {
96
- extensions: ['.jsx', '.mjs', '.js'],
97
- alias: {
98
- parse5: path.resolve(__dirname, 'node_modules/parse5'),
99
- },
100
- },
101
- module: {
102
- rules: [
103
- {
104
- test: /\.m?jsx?$/,
105
- exclude: /(node_modules)/,
106
- resolve: {
107
- fullySpecified: false,
108
- },
109
- use: [
110
- {
111
- loader: 'babel-loader',
112
- options: {
113
- presets: [
114
- '@babel/preset-env',
115
- {
116
- plugins: ['@babel/plugin-transform-runtime'],
117
- },
118
- ],
119
- plugins: [
120
- [
121
- '@babel/plugin-transform-react-jsx',
122
- {
123
- runtime: 'automatic',
124
- importSource: 'preact',
125
- },
126
- ],
127
- ],
128
- },
129
- },
130
- ],
131
- },
132
- {
133
- test: /\.s[ac]ss$/,
134
- use: [
135
- MiniCssExtractPlugin.loader,
136
- {
137
- loader: 'css-loader',
138
- options: {
139
- importLoaders: 1,
140
- },
141
- },
142
- {
143
- loader: 'sass-loader',
144
- },
145
- ],
146
- },
147
- {
148
- test: /\.less$/,
149
- use: [
150
- MiniCssExtractPlugin.loader,
151
- {
152
- loader: 'css-loader',
153
- options: {
154
- importLoaders: 1,
155
- },
156
- },
157
- {
158
- loader: 'less-loader',
159
- },
160
- ],
161
- },
162
- {
163
- test: /\.css$/,
164
- use: [
165
- MiniCssExtractPlugin.loader,
166
- {
167
- loader: 'css-loader',
168
- },
169
- ],
170
- },
171
- {
172
- test: /\.(woff|ttf)$/,
173
- type: 'asset/resource',
174
- generator: {
175
- emit: false,
176
- },
177
- },
178
- {
179
- test: /\.woff2$/,
180
- type: 'asset/inline',
181
- },
182
- {
183
- test: /\.(jpg|png|svg)$/,
184
- type: 'asset/inline',
185
- },
186
- {
187
- test: /\.(graphql|gql)$/,
188
- loader: 'graphql-tag/loader',
189
- },
190
- isWithoutTiktoken
191
- ? {
192
- test: /crop-text\.mjs$/,
193
- loader: 'string-replace-loader',
194
- options: {
195
- multiple: [
196
- {
197
- search: "import { encode } from '@nem035/gpt-3-encoder'",
198
- replace: '',
199
- },
200
- {
201
- search: 'encode(',
202
- replace: 'String(',
203
- },
204
- ],
205
- },
206
- }
207
- : {},
208
- isWithoutKatex && isWithoutTiktoken
209
- ? {
210
- test: /styles\.scss$/,
211
- loader: 'string-replace-loader',
212
- options: {
213
- multiple: [
214
- {
215
- search: "@import '../fonts/styles.css';",
216
- replace: '',
217
- },
218
- ],
219
- },
220
- }
221
- : {},
222
- ],
223
- },
224
- })
225
- if (isProduction) compiler.run(callback)
226
- else compiler.watch({}, callback)
227
- }
228
-
229
- async function zipFolder(dir) {
230
- const output = fs.createWriteStream(`${dir}.zip`)
231
- const archive = archiver('zip', {
232
- zlib: { level: 9 },
233
- })
234
- archive.pipe(output)
235
- archive.directory(dir, false)
236
- await archive.finalize()
237
- }
238
-
239
- async function copyFiles(entryPoints, targetDir) {
240
- if (!fs.existsSync(targetDir)) await fs.mkdir(targetDir)
241
- await Promise.all(
242
- entryPoints.map(async (entryPoint) => {
243
- await fs.copy(entryPoint.src, `${targetDir}/${entryPoint.dst}`)
244
- }),
245
- )
246
- }
247
-
248
- async function finishOutput(outputDirSuffix) {
249
- const commonFiles = [
250
- { src: 'src/logo1.png', dst: 'logo1.png' },
251
-
252
- { src: 'build/shared.js', dst: 'shared.js' },
253
- { src: 'build/content-script.css', dst: 'content-script.css' }, // shared
254
-
255
- { src: 'build/content-script.js', dst: 'content-script.js' },
256
-
257
- { src: 'build/background.js', dst: 'background.js' },
258
-
259
- { src: 'build/popup.js', dst: 'popup.js' },
260
- { src: 'build/popup.css', dst: 'popup.css' },
261
- { src: 'src/popup/index.html', dst: 'popup.html' },
262
-
263
- { src: 'build/IndependentPanel.js', dst: 'IndependentPanel.js' },
264
- { src: 'src/pages/IndependentPanel/index.html', dst: 'IndependentPanel.html' },
265
- ]
266
-
267
- // chromium
268
- const chromiumOutputDir = `./${outdir}/chromium${outputDirSuffix}`
269
- await copyFiles(
270
- [...commonFiles, { src: 'src/manifest.json', dst: 'manifest.json' }],
271
- chromiumOutputDir,
272
- )
273
- if (isProduction) await zipFolder(chromiumOutputDir)
274
- }
275
-
276
- function generateWebpackCallback(finishOutputFunc) {
277
- return async function webpackCallback(err, stats) {
278
- if (err || stats.hasErrors()) {
279
- console.error(err || stats.toString())
280
- return
281
- }
282
- // console.log(stats.toString())
283
-
284
- await finishOutputFunc()
285
- }
286
- }
287
-
288
- async function build() {
289
- await deleteOldDir()
290
- if (isProduction && !isAnalyzing) {
291
- await runWebpack(
292
- true,
293
- false,
294
- generateWebpackCallback(() => finishOutput('-without-katex')),
295
- )
296
- await new Promise((r) => setTimeout(r, 5000))
297
- await runWebpack(
298
- true,
299
- true,
300
- generateWebpackCallback(() => finishOutput('-without-katex-and-tiktoken')),
301
- )
302
- await new Promise((r) => setTimeout(r, 5000))
303
- }
304
- await runWebpack(
305
- false,
306
- false,
307
- generateWebpackCallback(() => finishOutput('')),
308
- )
309
- }
310
-
311
- build()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/package-lock.json CHANGED
The diff for this file is too large to render. See raw diff
 
Extension/package.json CHANGED
@@ -1,86 +1,60 @@
1
  {
2
- "name": "RisingBrowser",
 
 
 
3
  "scripts": {
4
- "build": "node build.mjs --production",
5
- "dev": "node build.mjs --development",
6
- "analyze": "node build.mjs --analyze",
7
- "lint": "eslint --ext .js,.mjs,.jsx .",
8
- "lint:fix": "eslint --ext .js,.mjs,.jsx . --fix",
9
- "pretty": "prettier --write ./**/*.{js,mjs,jsx,json,css,scss}",
10
- "stage": "run-script-os",
11
- "stage:default": "git add $(git diff --name-only --cached --diff-filter=d)",
12
- "stage:win32": "powershell git add $(git diff --name-only --cached --diff-filter=d)",
13
- "verify": "node .github/workflows/scripts/verify-search-engine-configs.mjs"
14
  },
15
- "pre-commit": [
16
- "pretty",
17
- "stage",
18
- "lint"
19
- ],
20
  "dependencies": {
21
- "@nem035/gpt-3-encoder": "^1.1.7",
22
- "@picocss/pico": "^1.5.9",
23
- "@primer/octicons-react": "^18.3.0",
24
- "diff": "^5.1.0",
25
- "eventsource-parser": "^1.0.0",
26
- "file-saver": "^2.0.5",
27
- "github-markdown-css": "^5.2.0",
28
- "gpt-3-encoder": "^1.1.4",
29
- "graphql": "^16.6.0",
30
- "i18next": "^22.4.15",
31
- "katex": "^0.16.6",
32
- "lodash-es": "^4.17.21",
33
- "md5": "^2.3.0",
34
- "parse5": "^6.0.1",
35
- "preact": "^10.13.2",
36
- "prop-types": "^15.8.1",
37
- "react": "npm:@preact/compat@^17.1.2",
38
- "react-bootstrap-icons": "^1.10.3",
39
- "react-dom": "npm:@preact/compat@^17.1.2",
40
- "react-draggable": "^4.4.5",
41
- "react-i18next": "^12.2.0",
42
- "react-markdown": "^8.0.7",
43
- "react-tabs": "^4.2.1",
44
- "react-toastify": "^9.1.2",
45
- "rehype-highlight": "^6.0.0",
46
- "rehype-katex": "^6.0.2",
47
- "rehype-raw": "^6.1.1",
48
- "remark-breaks": "^3.0.2",
49
- "remark-gfm": "^3.0.1",
50
- "remark-math": "^5.1.1",
51
- "uuid": "^9.0.0",
52
- "webextension-polyfill": "^0.10.0"
53
  },
54
  "devDependencies": {
55
- "@babel/core": "^7.21.4",
56
- "@babel/plugin-transform-react-jsx": "^7.21.0",
57
- "@babel/plugin-transform-runtime": "^7.21.4",
58
- "@babel/preset-env": "^7.21.4",
59
- "@types/archiver": "^5.3.2",
60
- "@types/fs-extra": "^11.0.1",
61
- "@types/jsdom": "^21.1.1",
62
- "@types/webextension-polyfill": "^0.10.0",
63
- "archiver": "^5.3.1",
64
  "babel-loader": "^9.1.2",
 
 
 
65
  "css-loader": "^6.7.3",
66
- "css-minimizer-webpack-plugin": "^5.0.0",
67
- "eslint": "^8.39.0",
68
- "eslint-plugin-react": "^7.32.2",
69
- "fs-extra": "^11.1.1",
70
- "graphql-tag": "^2.12.6",
71
- "jsdom": "^21.1.1",
72
- "less-loader": "^11.1.0",
73
- "mini-css-extract-plugin": "^2.7.5",
74
- "node-fetch": "^3.3.1",
75
- "pre-commit": "^1.2.2",
76
- "prettier": "^2.8.7",
77
- "progress-bar-webpack-plugin": "^2.1.0",
78
- "run-script-os": "^1.1.6",
79
- "sass": "^1.62.0",
80
- "sass-loader": "^13.2.2",
81
- "string-replace-loader": "^3.1.0",
82
- "terser-webpack-plugin": "^5.3.7",
83
- "webpack": "^5.80.0",
84
- "webpack-bundle-analyzer": "^4.8.0"
 
 
 
 
 
 
 
85
  }
86
  }
 
1
  {
2
+ "name": "rising-browser",
3
+ "version": "1.0.0",
4
+ "description": "A chatbot chrome extension was developed by Saeid Nobahari.",
5
+ "license": "MIT",
6
  "scripts": {
7
+ "build": "node utils/build.js",
8
+ "start": "node utils/webserver.js",
9
+ "prettier": "prettier --write '**/*.{js,jsx,ts,tsx,json,css,scss,md}'"
 
 
 
 
 
 
 
10
  },
 
 
 
 
 
11
  "dependencies": {
12
+ "@ant-design/icons": "^5.1.4",
13
+ "antd": "^5.3.2",
14
+ "axios": "^1.4.0",
15
+ "react": "^18.2.0",
16
+ "react-dom": "^18.2.0"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  },
18
  "devDependencies": {
19
+ "@babel/core": "^7.20.12",
20
+ "@babel/plugin-proposal-class-properties": "^7.18.6",
21
+ "@babel/preset-env": "^7.20.2",
22
+ "@babel/preset-react": "^7.18.6",
23
+ "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
24
+ "@types/chrome": "^0.0.202",
25
+ "@types/react": "^18.0.26",
26
+ "@types/react-dom": "^18.0.10",
27
+ "babel-eslint": "^10.1.0",
28
  "babel-loader": "^9.1.2",
29
+ "babel-preset-react-app": "^10.0.1",
30
+ "clean-webpack-plugin": "^4.0.0",
31
+ "copy-webpack-plugin": "^11.0.0",
32
  "css-loader": "^6.7.3",
33
+ "eslint": "^8.31.0",
34
+ "eslint-config-react-app": "^7.0.1",
35
+ "eslint-plugin-flowtype": "^8.0.3",
36
+ "eslint-plugin-import": "^2.27.4",
37
+ "eslint-plugin-jsx-a11y": "^6.7.1",
38
+ "eslint-plugin-react": "^7.32.0",
39
+ "eslint-plugin-react-hooks": "^4.6.0",
40
+ "file-loader": "^6.2.0",
41
+ "fs-extra": "^11.1.0",
42
+ "html-loader": "^4.2.0",
43
+ "html-webpack-plugin": "^5.5.0",
44
+ "prettier": "^2.8.3",
45
+ "react-refresh": "^0.14.0",
46
+ "react-refresh-typescript": "^2.0.7",
47
+ "sass": "^1.57.1",
48
+ "sass-loader": "^13.2.0",
49
+ "source-map-loader": "^3.0.1",
50
+ "style-loader": "^3.3.1",
51
+ "terser-webpack-plugin": "^5.3.6",
52
+ "ts-loader": "^9.4.2",
53
+ "type-fest": "^3.5.2",
54
+ "typescript": "^4.9.4",
55
+ "webpack": "^5.75.0",
56
+ "webpack-cli": "^4.10.0",
57
+ "webpack-dev-server": "^4.11.1",
58
+ "zip-webpack-plugin": "^4.0.1"
59
  }
60
  }
Extension/src/_locales/en/main.json DELETED
@@ -1,125 +0,0 @@
1
- {
2
- "General": "General",
3
- "Selection Tools": "Selection Tools",
4
- "Sites": "Sites",
5
- "Advanced": "Advanced",
6
- "Donate": "Donate",
7
- "Triggers": "Triggers",
8
- "Theme": "Theme",
9
- "API Mode": "API Mode",
10
- "Get": "Get",
11
- "Balance": "Balance",
12
- "Preferred Language": "Preferred Language",
13
- "Insert ChatGPT at the top of search results": "Insert ChatGPT at the top of search results",
14
- "Lock scrollbar while answering": "Lock scrollbar while answering",
15
- "Current Version": "Current Version",
16
- "Latest": "Latest",
17
- "Help | Changelog ": "Help | Changelog ",
18
- "Custom ChatGPT Web API Url": "Custom ChatGPT Web API Url",
19
- "Custom ChatGPT Web API Path": "Custom ChatGPT Web API Path",
20
- "Custom OpenAI API Url": "Custom OpenAI API Url",
21
- "Custom Site Regex": "Custom Site Regex",
22
- "Exclusively use Custom Site Regex for website matching, ignoring built-in rules": "Exclusively use Custom Site Regex for website matching, ignoring built-in rules",
23
- "Input Query": "Input Query",
24
- "Append Query": "Append Query",
25
- "Prepend Query": "Prepend Query",
26
- "Wechat Pay": "Wechat Pay",
27
- "Type your question here\nEnter to send, shift + enter to break line": "Type your question here\nEnter to send\nShift + enter to break line",
28
- "Type your question here\nEnter to stop generating\nShift + enter to break line": "Type your question here\nEnter to stop generating\nShift + enter to break line",
29
- "Ask ChatGPT": "Ask ChatGPT",
30
- "No Input Found": "No Input Found",
31
- "You": "You",
32
- "Collapse": "Collapse",
33
- "Expand": "Expand",
34
- "Stop": "Stop",
35
- "Continue on official website": "Continue on official website",
36
- "Error": "Error",
37
- "Copy": "Copy",
38
- "Question": "Question",
39
- "Answer": "Answer",
40
- "Waiting for response...": "Waiting for response...",
41
- "Close the Window": "Close the Window",
42
- "Pin the Window": "Pin the Window",
43
- "Float the Window": "Float the Window",
44
- "Save Conversation": "Save Conversation",
45
- "UNAUTHORIZED": "UNAUTHORIZED",
46
- "Please login at https://chat.openai.com first": "Please login at https://chat.openai.com first",
47
- "Then open https://chat.openai.com/api/auth/session": "Then open https://chat.openai.com/api/auth/session",
48
- "And refresh this page or type you question again": "And click the retry button in the top right corner",
49
- "Consider creating an api key at https://platform.openai.com/account/api-keys": "Consider creating an api key at https://platform.openai.com/account/api-keys",
50
- "OpenAI Security Check Required": "OpenAI Security Check Required",
51
- "Please open https://chat.openai.com/api/auth/session": "Please open https://chat.openai.com/api/auth/session",
52
- "Please open https://chat.openai.com": "Please open https://chat.openai.com",
53
- "New Chat": "New Chat",
54
- "Summarize Page": "Summarize Page",
55
- "Translate": "Translate",
56
- "Translate (Bidirectional)": "Translate (Bidirectional)",
57
- "Translate (To English)": "Translate (To English)",
58
- "Summary": "Summary",
59
- "Polish": "Polish",
60
- "Sentiment Analysis": "Sentiment Analysis",
61
- "Divide Paragraphs": "Divide Paragraphs",
62
- "Code Explain": "Code Explain",
63
- "Ask": "Ask",
64
- "Always": "Always",
65
- "Manually": "Manually",
66
- "When query ends with question mark (?)": "When query ends with question mark (?)",
67
- "Light": "Light",
68
- "Dark": "Dark",
69
- "Auto": "Auto",
70
- "ChatGPT (Web)": "ChatGPT (Web)",
71
- "ChatGPT (Web, GPT-4)": "ChatGPT (Web, GPT-4)",
72
- "Bing (Web, GPT-4)": "Bing (Web, GPT-4)",
73
- "ChatGPT (GPT-3.5-turbo)": "ChatGPT (GPT-3.5-turbo)",
74
- "ChatGPT (GPT-4-8k)": "ChatGPT (GPT-4-8k)",
75
- "ChatGPT (GPT-4-32k)": "ChatGPT (GPT-4-32k)",
76
- "GPT-3.5": "GPT-3.5",
77
- "Custom Model": "Custom Model",
78
- "Balanced": "Balanced",
79
- "Creative": "Creative",
80
- "Precise": "Precise",
81
- "Fast": "Fast",
82
- "API Key": "API Key",
83
- "Model Name": "Model Name",
84
- "Custom Model API Url": "Custom Model API Url",
85
- "Loading...": "Loading...",
86
- "Feedback": "Feedback",
87
- "Confirm": "Confirm",
88
- "Clear Conversation": "Clear Conversation",
89
- "Retry": "Retry",
90
- "Exceeded maximum context length": "Exceeded maximum context length, please clear the conversation and try again",
91
- "Regenerate the answer after switching model": "Regenerate the answer after switching model",
92
- "Pin": "Pin",
93
- "Unpin": "Unpin",
94
- "Delete Conversation": "Delete Conversation",
95
- "Clear conversations": "Clear conversations",
96
- "Settings": "Settings",
97
- "Feature Pages": "Feature Pages",
98
- "Keyboard Shortcuts": "Keyboard Shortcuts",
99
- "Open Conversation Page": "Open Conversation Page",
100
- "Open Conversation Window": "Open Conversation Window",
101
- "Store to Independent Conversation Page": "Store to Independent Conversation Page",
102
- "Keep Conversation Window in Background": "Keep conversation window in background, so that you can use shortcut keys to call it up in any program",
103
- "Max Response Token Length": "Max Response Token Length",
104
- "Max Conversation Length": "Max Conversation Length",
105
- "Always pin the floating window": "Always pin the floating window",
106
- "Export": "Export",
107
- "Always Create New Conversation Window": "Always Create New Conversation Window",
108
- "Please keep this tab open. You can now use the web mode of ChatGPTBox": "Please keep this tab open. You can now use the web mode of ChatGPTBox",
109
- "Go Back": "Go Back",
110
- "Pin Tab": "Pin Tab",
111
- "Modules": "Modules",
112
- "API Params": "API Params",
113
- "API Url": "API Url",
114
- "Others": "Others",
115
- "API Modes": "API Modes",
116
- "Disable web mode history for better privacy protection, but it will result in unavailable conversations after a period of time": "Disable web mode history for better privacy protection, but it will result in unavailable conversations after a period of time",
117
- "Display selection tools next to input box to avoid blocking": "Display selection tools next to input box to avoid blocking",
118
- "Close All Chats In This Page": "Close All Chats In This Page",
119
- "When Icon Clicked": "When Icon Clicked",
120
- "Open Settings": "Open Settings",
121
- "Focus to input box after answering": "Focus to input box after answering",
122
- "Bing CaptchaChallenge": "You must pass Bing's verification. Please go to https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx and send a message",
123
- "Exceeded quota": "You exceeded your current quota, check https://platform.openai.com/account/usage",
124
- "Rate limit": "Rate limit exceeded"
125
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/_locales/i18n-react.mjs DELETED
@@ -1,11 +0,0 @@
1
- import i18n from 'i18next'
2
- import { initReactI18next } from 'react-i18next'
3
- import { resources } from './resources'
4
-
5
- i18n.use(initReactI18next).init({
6
- resources,
7
- fallbackLng: 'en',
8
- interpolation: {
9
- escapeValue: false, // not needed for react as it escapes by default
10
- },
11
- })
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/_locales/i18n.mjs DELETED
@@ -1,7 +0,0 @@
1
- import i18n from 'i18next'
2
- import { resources } from './resources'
3
-
4
- i18n.init({
5
- resources,
6
- fallbackLng: 'en',
7
- })
 
 
 
 
 
 
 
 
Extension/src/_locales/resources.mjs DELETED
@@ -1,7 +0,0 @@
1
- import en from './en/main.json'
2
-
3
- export const resources = {
4
- en: {
5
- translation: en,
6
- },
7
- }
 
 
 
 
 
 
 
 
Extension/src/assets/desc.png DELETED
Binary file (228 kB)
 
Extension/src/assets/img/icon-128.png ADDED
Extension/src/assets/img/icon-34.png ADDED
Extension/src/assets/img/logo.svg ADDED
Extension/src/background/commands.mjs DELETED
@@ -1,27 +0,0 @@
1
- import Browser from 'webextension-polyfill'
2
- import { config as menuConfig } from '../content-script/menu-tools/index.mjs'
3
-
4
- export function registerCommands() {
5
- Browser.commands.onCommand.addListener(async (command) => {
6
- const message = {
7
- itemId: command,
8
- selectionText: '',
9
- useMenuPosition: false,
10
- }
11
- console.debug('command triggered', message)
12
-
13
- if (command in menuConfig) {
14
- if (menuConfig[command].action) {
15
- menuConfig[command].action(true)
16
- }
17
-
18
- if (menuConfig[command].genPrompt) {
19
- const currentTab = (await Browser.tabs.query({ active: true, currentWindow: true }))[0]
20
- Browser.tabs.sendMessage(currentTab.id, {
21
- type: 'CREATE_CHAT',
22
- data: message,
23
- })
24
- }
25
- }
26
- })
27
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/background/index.mjs DELETED
@@ -1,77 +0,0 @@
1
- import Browser from 'webextension-polyfill'
2
- import { getUserConfig, setUserConfig } from '../config/index.mjs'
3
- import '../_locales/i18n'
4
- import { openUrl } from '../utils/open-url'
5
- import { registerPortListener } from '../services/wrappers.mjs'
6
- import { refreshMenu } from './menus.mjs'
7
- import { registerCommands } from './commands.mjs'
8
- import { getManagementBrowser } from '../services/apis/rising-api.mjs'
9
-
10
- async function executeApi(session, port, config) {
11
- // ### the part for integrating with backend ###
12
- console.debug('config', config)
13
- await getManagementBrowser(port, session.question, session, '')
14
- }
15
-
16
- Browser.runtime.onMessage.addListener(async (message, sender) => {
17
- switch (message.type) {
18
- case 'NEW_URL': {
19
- const newTab = await Browser.tabs.create({
20
- url: message.data.url,
21
- pinned: message.data.pinned,
22
- })
23
- if (message.data.saveAsChatgptConfig) {
24
- await setUserConfig({
25
- chatgptTabId: newTab.id,
26
- chatgptJumpBackTabId: sender.tab.id,
27
- })
28
- }
29
- break
30
- }
31
- case 'SET_CHATGPT_TAB': {
32
- await setUserConfig({
33
- chatgptTabId: sender.tab.id,
34
- })
35
- break
36
- }
37
- case 'ACTIVATE_URL':
38
- await Browser.tabs.update(message.data.tabId, { active: true })
39
- break
40
- case 'OPEN_URL':
41
- openUrl(message.data.url)
42
- break
43
- case 'OPEN_CHAT_WINDOW': {
44
- const config = await getUserConfig()
45
- const url = Browser.runtime.getURL('IndependentPanel.html')
46
- const tabs = await Browser.tabs.query({ url: url, windowType: 'popup' })
47
- if (!config.alwaysCreateNewConversationWindow && tabs.length > 0)
48
- await Browser.windows.update(tabs[0].windowId, { focused: true })
49
- else
50
- await Browser.windows.create({
51
- url: url,
52
- type: 'popup',
53
- width: 500,
54
- height: 650,
55
- })
56
- break
57
- }
58
- case 'REFRESH_MENU':
59
- refreshMenu()
60
- break
61
- case 'PIN_TAB': {
62
- let tabId
63
- if (message.data.tabId) tabId = message.data.tabId
64
- else tabId = sender.tab.id
65
-
66
- await Browser.tabs.update(tabId, { pinned: true })
67
- if (message.data.saveAsChatgptConfig) {
68
- await setUserConfig({ chatgptTabId: tabId })
69
- }
70
- break
71
- }
72
- }
73
- })
74
-
75
- registerPortListener(async (session, port, config) => await executeApi(session, port, config))
76
- registerCommands()
77
- refreshMenu()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/background/menus.mjs DELETED
@@ -1,75 +0,0 @@
1
- import Browser from 'webextension-polyfill'
2
- import { defaultConfig, getPreferredLanguageKey } from '../config/index.mjs'
3
- import { changeLanguage, t } from 'i18next'
4
- import { config as menuConfig } from '../content-script/menu-tools/index.mjs'
5
-
6
- export function refreshMenu() {
7
- Browser.contextMenus.removeAll().then(async () => {
8
- await getPreferredLanguageKey().then((lang) => {
9
- changeLanguage(lang)
10
- })
11
- const menuId = 'ChatGPTBox-Menu'
12
- Browser.contextMenus.create({
13
- id: menuId,
14
- title: 'RisingBrowser',
15
- contexts: ['all'],
16
- })
17
-
18
- for (const [k, v] of Object.entries(menuConfig)) {
19
- Browser.contextMenus.create({
20
- id: menuId + k,
21
- parentId: menuId,
22
- title: t(v.label),
23
- contexts: ['all'],
24
- })
25
- }
26
-
27
- Browser.contextMenus.create({
28
- id: menuId + 'separator1',
29
- parentId: menuId,
30
- contexts: ['selection'],
31
- type: 'separator',
32
- })
33
-
34
- for (const index in defaultConfig.selectionTools) {
35
- const key = defaultConfig.selectionTools[index]
36
- const desc = defaultConfig.selectionToolsDesc[index]
37
- Browser.contextMenus.create({
38
- id: menuId + key,
39
- parentId: menuId,
40
- title: t(desc),
41
- contexts: ['selection'],
42
- })
43
- }
44
-
45
- Browser.contextMenus.onClicked.addListener((info, tab) => {
46
- Browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
47
- const currentTab = tabs[0]
48
- const message = {
49
- itemId: info.menuItemId.replace(menuId, ''),
50
- selectionText: info.selectionText,
51
- useMenuPosition: tab.id === currentTab.id,
52
- }
53
- console.debug('menu clicked', message)
54
-
55
- if (defaultConfig.selectionTools.includes(message.itemId)) {
56
- Browser.tabs.sendMessage(currentTab.id, {
57
- type: 'CREATE_CHAT',
58
- data: message,
59
- })
60
- } else if (message.itemId in menuConfig) {
61
- if (menuConfig[message.itemId].action) {
62
- menuConfig[message.itemId].action(true)
63
- }
64
-
65
- if (menuConfig[message.itemId].genPrompt) {
66
- Browser.tabs.sendMessage(currentTab.id, {
67
- type: 'CREATE_CHAT',
68
- data: message,
69
- })
70
- }
71
- }
72
- })
73
- })
74
- })
75
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/components/ConfirmButton/index.jsx DELETED
@@ -1,58 +0,0 @@
1
- import { useTranslation } from 'react-i18next'
2
- import { useEffect, useRef, useState } from 'react'
3
- import PropTypes from 'prop-types'
4
-
5
- ConfirmButton.propTypes = {
6
- onConfirm: PropTypes.func.isRequired,
7
- text: PropTypes.string.isRequired,
8
- }
9
-
10
- function ConfirmButton({ onConfirm, text }) {
11
- const { t } = useTranslation()
12
- const [waitConfirm, setWaitConfirm] = useState(false)
13
- const confirmRef = useRef(null)
14
-
15
- useEffect(() => {
16
- if (waitConfirm) confirmRef.current.focus()
17
- }, [waitConfirm])
18
-
19
- return (
20
- <span>
21
- <button
22
- ref={confirmRef}
23
- type="button"
24
- className="normal-button"
25
- style={{
26
- ...(waitConfirm ? {} : { display: 'none' }),
27
- }}
28
- onMouseDown={(e) => {
29
- e.preventDefault()
30
- e.stopPropagation()
31
- }}
32
- onBlur={() => {
33
- setWaitConfirm(false)
34
- }}
35
- onClick={() => {
36
- setWaitConfirm(false)
37
- onConfirm()
38
- }}
39
- >
40
- {t('Confirm')}
41
- </button>
42
- <button
43
- type="button"
44
- className="normal-button"
45
- style={{
46
- ...(waitConfirm ? { display: 'none' } : {}),
47
- }}
48
- onClick={() => {
49
- setWaitConfirm(true)
50
- }}
51
- >
52
- {text}
53
- </button>
54
- </span>
55
- )
56
- }
57
-
58
- export default ConfirmButton
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/components/ConversationCard/index.jsx DELETED
@@ -1,483 +0,0 @@
1
- import { memo, useEffect, useRef, useState } from 'react'
2
- import PropTypes from 'prop-types'
3
- import Browser from 'webextension-polyfill'
4
- import InputBox from '../InputBox'
5
- import ConversationItem from '../ConversationItem'
6
- import { createElementAtPosition } from '../../utils'
7
- import { DownloadIcon, LinkExternalIcon, ArchiveIcon } from '@primer/octicons-react'
8
- import { WindowDesktop, XLg, Pin } from 'react-bootstrap-icons'
9
- import FileSaver from 'file-saver'
10
- import { render } from 'preact'
11
- import FloatingToolbar from '../FloatingToolbar'
12
- import { useClampWindowSize } from '../../hooks/use-clamp-window-size'
13
- import { ModelMode, Models } from '../../config/index.mjs'
14
- import { useTranslation } from 'react-i18next'
15
- import DeleteButton from '../DeleteButton'
16
- import { useConfig } from '../../hooks/use-config.mjs'
17
- import { createSession } from '../../services/local-session.mjs'
18
- import { v4 as uuidv4 } from 'uuid'
19
- import { initSession } from '../../services/init-session.mjs'
20
- import { findLastIndex } from 'lodash-es'
21
-
22
- const logo = Browser.runtime.getURL('logo1.png')
23
-
24
- class ConversationItemData extends Object {
25
- /**
26
- * @param {'question'|'answer'|'error'} type
27
- * @param {string} content
28
- * @param {bool} done
29
- */
30
- constructor(type, content, done = false) {
31
- super()
32
- this.type = type
33
- this.content = content
34
- this.done = done
35
- }
36
- }
37
-
38
- function ConversationCard(props) {
39
- const { t } = useTranslation()
40
- const [isReady, setIsReady] = useState(!props.question)
41
- const [port, setPort] = useState(() => Browser.runtime.connect())
42
- const [session, setSession] = useState(props.session)
43
- const windowSize = useClampWindowSize([750, 1500], [250, 1100])
44
- const bodyRef = useRef(null)
45
-
46
- /**
47
- * @type {[ConversationItemData[], (conversationItemData: ConversationItemData[]) => void]}
48
- */
49
- const [conversationItemData, setConversationItemData] = useState(
50
- (() => {
51
- if (session.conversationRecords.length === 0)
52
- if (props.question)
53
- return [
54
- new ConversationItemData(
55
- 'answer',
56
- `<p class="gpt-loading">${t(`Waiting for response...`)}</p>`,
57
- ),
58
- ]
59
- else return []
60
- else {
61
- const ret = []
62
- for (const record of session.conversationRecords) {
63
- ret.push(new ConversationItemData('question', record.question, true))
64
- ret.push(new ConversationItemData('answer', record.answer, true))
65
- }
66
- return ret
67
- }
68
- })(),
69
- )
70
- const config = useConfig()
71
-
72
- useEffect(() => {
73
- if (props.onUpdate) props.onUpdate(port, session, conversationItemData)
74
- }, [session, conversationItemData])
75
-
76
- useEffect(() => {
77
- bodyRef.current.scrollTop = bodyRef.current.scrollHeight
78
- }, [session])
79
-
80
- useEffect(() => {
81
- if (config.lockWhenAnswer) bodyRef.current.scrollTop = bodyRef.current.scrollHeight
82
- }, [conversationItemData])
83
-
84
- useEffect(() => {
85
- // when the page is responsive, session may accumulate redundant data and needs to be cleared after remounting and before making a new request
86
- if (props.question) {
87
- const newSession = initSession({ question: props.question })
88
- setSession(newSession)
89
- port.postMessage({ session: newSession })
90
- }
91
- }, [props.question]) // usually only triggered once
92
-
93
- /**
94
- * @param {string} value
95
- * @param {boolean} appended
96
- * @param {'question'|'answer'|'error'} newType
97
- * @param {boolean} done
98
- */
99
- const updateAnswer = (value, appended, newType, done = false) => {
100
- setConversationItemData((old) => {
101
- const copy = [...old]
102
- const index = findLastIndex(copy, (v) => v.type === 'answer' || v.type === 'error')
103
- if (index === -1) return copy
104
- copy[index] = new ConversationItemData(
105
- newType,
106
- appended ? copy[index].content + value : value,
107
- )
108
- copy[index].done = done
109
- return copy
110
- })
111
- }
112
-
113
- useEffect(() => {
114
- const portListener = () => {
115
- setPort(Browser.runtime.connect())
116
- setIsReady(true)
117
- }
118
-
119
- const closeChatsListener = (message) => {
120
- if (message.type === 'CLOSE_CHATS') {
121
- port.disconnect()
122
- if (props.onClose) props.onClose()
123
- }
124
- }
125
-
126
- if (props.closeable) Browser.runtime.onMessage.addListener(closeChatsListener)
127
- port.onDisconnect.addListener(portListener)
128
- return () => {
129
- if (props.closeable) Browser.runtime.onMessage.removeListener(closeChatsListener)
130
- port.onDisconnect.removeListener(portListener)
131
- }
132
- }, [port])
133
-
134
- useEffect(() => {
135
- const listener = (msg) => {
136
- let answer = {}
137
- if (msg.answer) answer = JSON.parse(msg.answer?.replace(/'/g, '"'))
138
-
139
- if (answer?.program) {
140
- /**
141
- * Tab and Page Manage - Open, Search, Close, Next/Previous Page, Scroll
142
- */
143
- const search = answer?.content
144
- let page = 0
145
- const currentUrl = window.location.href
146
-
147
- switch (answer.program) {
148
- case 'open_tab':
149
- window.open('https://google.com/search?q=', '_blank', 'noreferrer')
150
- break
151
- case 'open_tab_search':
152
- window.open('https://google.com/search?q=' + search, '_blank', 'noreferrer')
153
- break
154
- case 'close_tab':
155
- //window.focus()
156
- window.close()
157
- break
158
- case 'previous_page':
159
- if (page === 0) {
160
- page = 0
161
- } else {
162
- page -= 10
163
- }
164
-
165
- window.location.assign(currentUrl + '&start=' + page)
166
- break
167
- case 'next_page':
168
- page += 10
169
-
170
- window.location.assign(currentUrl + '&start=' + page)
171
- break
172
- case 'scroll_up':
173
- window.scrollBy(0, -300)
174
- break
175
- case 'scroll_down':
176
- window.scrollBy(0, 300)
177
- break
178
- case 'scroll_top':
179
- window.scrollTo(0, 0)
180
- break
181
- case 'scroll_bottom':
182
- window.scrollTo(0, document.body.scrollHeight)
183
- break
184
- default:
185
- break
186
- }
187
-
188
- updateAnswer(answer.program, false, 'answer')
189
- }
190
-
191
- if (msg.session) {
192
- if (msg.done) msg.session = { ...msg.session, isRetry: false }
193
- setSession(msg.session)
194
- }
195
-
196
- if (msg.done) {
197
- updateAnswer('', true, 'answer', true)
198
- setIsReady(true)
199
- }
200
-
201
- if (msg.error) {
202
- switch (msg.error) {
203
- case 'UNAUTHORIZED':
204
- updateAnswer(
205
- `${t('UNAUTHORIZED')}<br>${t('Please login at https://chat.openai.com first')}
206
- <br>${t('And refresh this page or type you question again')}` +
207
- `<br><br>${t(
208
- 'Consider creating an api key at https://platform.openai.com/account/api-keys',
209
- )}`,
210
- false,
211
- 'error',
212
- )
213
- break
214
- case 'CLOUDFLARE':
215
- updateAnswer(
216
- `${t('OpenAI Security Check Required')}<br><br>${t(
217
- 'And refresh this page or type you question again',
218
- )}` +
219
- `<br><br>${t(
220
- 'Consider creating an api key at https://platform.openai.com/account/api-keys',
221
- )}`,
222
- false,
223
- 'error',
224
- )
225
- break
226
- default:
227
- if (
228
- conversationItemData[conversationItemData.length - 1].content.includes('gpt-loading')
229
- )
230
- updateAnswer(msg.error, false, 'error')
231
- else
232
- setConversationItemData([
233
- ...conversationItemData,
234
- new ConversationItemData('error', msg.error),
235
- ])
236
- break
237
- }
238
- setIsReady(true)
239
- }
240
- }
241
- port.onMessage.addListener(listener)
242
- return () => {
243
- port.onMessage.removeListener(listener)
244
- }
245
- }, [conversationItemData])
246
-
247
- const getRetryFn = (session) => () => {
248
- updateAnswer(`<p class="gpt-loading">${t('Waiting for response...')}</p>`, false, 'answer')
249
- setIsReady(false)
250
-
251
- const newSession = { ...session, isRetry: true }
252
- setSession(newSession)
253
- try {
254
- port.postMessage({ stop: true })
255
- port.postMessage({ session: newSession })
256
- } catch (e) {
257
- updateAnswer(e, false, 'error')
258
- }
259
- }
260
-
261
- return (
262
- <div className="gpt-inner">
263
- <div
264
- className={props.draggable ? 'gpt-header draggable' : 'gpt-header'}
265
- style="padding:15px;user-select:none;"
266
- >
267
- <span className="gpt-util-group" style={props.notClampSize ? {} : { flexGrow: 1 }}>
268
- {props.closeable ? (
269
- <XLg
270
- className="gpt-util-icon"
271
- title={t('Close the Window')}
272
- size={16}
273
- onClick={() => {
274
- port.disconnect()
275
- if (props.onClose) props.onClose()
276
- }}
277
- />
278
- ) : props.dockable ? (
279
- <Pin
280
- className="gpt-util-icon"
281
- title={t('Pin the Window')}
282
- size={16}
283
- onClick={() => {
284
- if (props.onDock) props.onDock()
285
- }}
286
- />
287
- ) : (
288
- <img src={logo} style="user-select:none;width:20px;height:20px;" alt="no image" />
289
- )}
290
- <select
291
- style={props.notClampSize ? {} : { width: 0, flexGrow: 1 }}
292
- className="normal-button"
293
- required
294
- onChange={(e) => {
295
- const modelName = e.target.value
296
- const newSession = { ...session, modelName, aiName: Models[modelName].desc }
297
- if (config.autoRegenAfterSwitchModel && conversationItemData.length > 0)
298
- getRetryFn(newSession)()
299
- else setSession(newSession)
300
- }}
301
- >
302
- {config.activeApiModes.map((modelName) => {
303
- let desc
304
- if (modelName.includes('-')) {
305
- const splits = modelName.split('-')
306
- if (splits[0] in Models)
307
- desc = `${t(Models[splits[0]].desc)} (${t(ModelMode[splits[1]])})`
308
- } else {
309
- if (modelName in Models) desc = t(Models[modelName].desc)
310
- }
311
- if (desc)
312
- return (
313
- <option
314
- value={modelName}
315
- key={modelName}
316
- selected={modelName === session.modelName}
317
- >
318
- {desc}
319
- </option>
320
- )
321
- })}
322
- </select>
323
- </span>
324
- <span className="gpt-util-group" style={{ flexGrow: 1, justifyContent: 'flex-end' }}>
325
- {session && session.conversationId && (
326
- <a
327
- title={t('Continue on official website')}
328
- href={'https://chat.openai.com/chat/' + session.conversationId}
329
- target="_blank"
330
- rel="nofollow noopener noreferrer"
331
- className="gpt-util-icon"
332
- style="color: inherit;"
333
- >
334
- <LinkExternalIcon size={16} />
335
- </a>
336
- )}
337
- <WindowDesktop
338
- className="gpt-util-icon"
339
- title={t('Float the Window')}
340
- size={16}
341
- onClick={() => {
342
- const position = { x: window.innerWidth / 2 - 300, y: window.innerHeight / 2 - 200 }
343
- const toolbarContainer = createElementAtPosition(position.x, position.y)
344
- toolbarContainer.className = 'chatgptbox-toolbar-container-not-queryable'
345
- render(
346
- <FloatingToolbar
347
- session={session}
348
- selection=""
349
- container={toolbarContainer}
350
- closeable={true}
351
- triggered={true}
352
- />,
353
- toolbarContainer,
354
- )
355
- }}
356
- />
357
- <DeleteButton
358
- size={16}
359
- text={t('Clear Conversation')}
360
- onConfirm={() => {
361
- port.postMessage({ stop: true })
362
- Browser.runtime.sendMessage({
363
- type: 'DELETE_CONVERSATION',
364
- data: {
365
- conversationId: session.conversationId,
366
- },
367
- })
368
- setConversationItemData([])
369
- const newSession = initSession({
370
- ...session,
371
- question: null,
372
- conversationRecords: [],
373
- })
374
- newSession.sessionId = session.sessionId
375
- setSession(newSession)
376
- }}
377
- />
378
- {!props.pageMode && (
379
- <span
380
- title={t('Store to Independent Conversation Page')}
381
- className="gpt-util-icon"
382
- onClick={() => {
383
- const newSession = {
384
- ...session,
385
- sessionName: new Date().toLocaleString(),
386
- autoClean: false,
387
- sessionId: uuidv4(),
388
- }
389
- setSession(newSession)
390
- createSession(newSession).then(() =>
391
- Browser.runtime.sendMessage({
392
- type: 'OPEN_URL',
393
- data: {
394
- url: Browser.runtime.getURL('IndependentPanel.html'),
395
- },
396
- }),
397
- )
398
- }}
399
- >
400
- <ArchiveIcon size={16} />
401
- </span>
402
- )}
403
- <span
404
- title={t('Save Conversation')}
405
- className="gpt-util-icon"
406
- onClick={() => {
407
- let output = ''
408
- session.conversationRecords.forEach((data) => {
409
- output += `${t('Question')}:\n\n${data.question}\n\n${t('Answer')}:\n\n${
410
- data.answer
411
- }\n\n<hr/>\n\n`
412
- })
413
- const blob = new Blob([output], { type: 'text/plain;charset=utf-8' })
414
- FileSaver.saveAs(blob, 'conversation.md')
415
- }}
416
- >
417
- <DownloadIcon size={16} />
418
- </span>
419
- </span>
420
- </div>
421
- <hr />
422
- <div
423
- ref={bodyRef}
424
- className="markdown-body"
425
- style={
426
- props.notClampSize
427
- ? { flexGrow: 1 }
428
- : { maxHeight: windowSize[1] * 0.55 + 'px', resize: 'vertical' }
429
- }
430
- >
431
- {conversationItemData.map((data, idx) => (
432
- <ConversationItem
433
- content={data.content}
434
- key={idx}
435
- type={data.type}
436
- session={session}
437
- done={data.done}
438
- port={port}
439
- onRetry={idx === conversationItemData.length - 1 ? getRetryFn(session) : null}
440
- />
441
- ))}
442
- </div>
443
- <InputBox
444
- enabled={isReady}
445
- port={port}
446
- reverseResizeDir={props.pageMode}
447
- onSubmit={(question) => {
448
- console.log('new quetion------------->', question)
449
- const newQuestion = new ConversationItemData('question', question)
450
- const newAnswer = new ConversationItemData(
451
- 'answer',
452
- `<p class="gpt-loading">${t('Waiting for response...')}</p>`,
453
- )
454
- setConversationItemData([...conversationItemData, newQuestion, newAnswer])
455
- setIsReady(false)
456
-
457
- const newSession = { ...session, question, isRetry: false }
458
- setSession(newSession)
459
- try {
460
- port.postMessage({ session: newSession })
461
- } catch (e) {
462
- updateAnswer(e, false, 'error')
463
- }
464
- }}
465
- />
466
- </div>
467
- )
468
- }
469
-
470
- ConversationCard.propTypes = {
471
- session: PropTypes.object.isRequired,
472
- question: PropTypes.string.isRequired,
473
- onUpdate: PropTypes.func,
474
- draggable: PropTypes.bool,
475
- closeable: PropTypes.bool,
476
- onClose: PropTypes.func,
477
- dockable: PropTypes.bool,
478
- onDock: PropTypes.func,
479
- notClampSize: PropTypes.bool,
480
- pageMode: PropTypes.bool,
481
- }
482
-
483
- export default memo(ConversationCard)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/components/ConversationItem/index.jsx DELETED
@@ -1,140 +0,0 @@
1
- import { useState } from 'react'
2
- import { ChevronDownIcon, XCircleIcon, SyncIcon } from '@primer/octicons-react'
3
- import CopyButton from '../CopyButton'
4
- import ReadButton from '../ReadButton'
5
- import PropTypes from 'prop-types'
6
- import MarkdownRender from '../MarkdownRender/markdown.jsx'
7
- import { useTranslation } from 'react-i18next'
8
-
9
- export function ConversationItem({ type, content, session, done, port, onRetry }) {
10
- const { t } = useTranslation()
11
- const [collapsed, setCollapsed] = useState(false)
12
-
13
- switch (type) {
14
- case 'question':
15
- return (
16
- <div className={type} dir="auto">
17
- <div className="gpt-header">
18
- <p>{t('You')}:</p>
19
- <div className="gpt-util-group">
20
- <CopyButton contentFn={() => content.replace(/\n<hr\/>$/, '')} size={14} />
21
- <ReadButton contentFn={() => content} size={14} />
22
- {!collapsed ? (
23
- <span
24
- title={t('Collapse')}
25
- className="gpt-util-icon"
26
- onClick={() => setCollapsed(true)}
27
- >
28
- <XCircleIcon size={14} />
29
- </span>
30
- ) : (
31
- <span
32
- title={t('Expand')}
33
- className="gpt-util-icon"
34
- onClick={() => setCollapsed(false)}
35
- >
36
- <ChevronDownIcon size={14} />
37
- </span>
38
- )}
39
- </div>
40
- </div>
41
- {!collapsed && <MarkdownRender>{content}</MarkdownRender>}
42
- </div>
43
- )
44
- case 'answer':
45
- return (
46
- <div className={type} dir="auto">
47
- <div className="gpt-header">
48
- <p style="white-space: nowrap;">
49
- {session && session.aiName ? `${t(session.aiName)}` : t('Loading...')}
50
- </p>
51
- <div className="gpt-util-group">
52
- {!done && (
53
- <button
54
- type="button"
55
- className="normal-button"
56
- onClick={() => {
57
- port.postMessage({ stop: true })
58
- }}
59
- >
60
- {t('Stop')}
61
- </button>
62
- )}
63
- {onRetry && (
64
- <span title={t('Retry')} className="gpt-util-icon" onClick={onRetry}>
65
- <SyncIcon size={14} />
66
- </span>
67
- )}
68
- {session && (
69
- <CopyButton contentFn={() => content.replace(/\n<hr\/>$/, '')} size={14} />
70
- )}
71
- {session && <ReadButton contentFn={() => content} size={14} />}
72
- {!collapsed ? (
73
- <span
74
- title={t('Collapse')}
75
- className="gpt-util-icon"
76
- onClick={() => setCollapsed(true)}
77
- >
78
- <XCircleIcon size={14} />
79
- </span>
80
- ) : (
81
- <span
82
- title={t('Expand')}
83
- className="gpt-util-icon"
84
- onClick={() => setCollapsed(false)}
85
- >
86
- <ChevronDownIcon size={14} />
87
- </span>
88
- )}
89
- </div>
90
- </div>
91
- {!collapsed && <MarkdownRender>{content}</MarkdownRender>}
92
- </div>
93
- )
94
- case 'error':
95
- return (
96
- <div className={type} dir="auto">
97
- <div className="gpt-header">
98
- <p>{t('Error')}:</p>
99
- <div className="gpt-util-group">
100
- {onRetry && (
101
- <span title={t('Retry')} className="gpt-util-icon" onClick={onRetry}>
102
- <SyncIcon size={14} />
103
- </span>
104
- )}
105
- <CopyButton contentFn={() => content.replace(/\n<hr\/>$/, '')} size={14} />
106
- {!collapsed ? (
107
- <span
108
- title={t('Collapse')}
109
- className="gpt-util-icon"
110
- onClick={() => setCollapsed(true)}
111
- >
112
- <XCircleIcon size={14} />
113
- </span>
114
- ) : (
115
- <span
116
- title={t('Expand')}
117
- className="gpt-util-icon"
118
- onClick={() => setCollapsed(false)}
119
- >
120
- <ChevronDownIcon size={14} />
121
- </span>
122
- )}
123
- </div>
124
- </div>
125
- {!collapsed && <MarkdownRender>{content}</MarkdownRender>}
126
- </div>
127
- )
128
- }
129
- }
130
-
131
- ConversationItem.propTypes = {
132
- type: PropTypes.oneOf(['question', 'answer', 'error']).isRequired,
133
- content: PropTypes.string.isRequired,
134
- session: PropTypes.object.isRequired,
135
- done: PropTypes.bool.isRequired,
136
- port: PropTypes.object.isRequired,
137
- onRetry: PropTypes.func,
138
- }
139
-
140
- export default ConversationItem
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/components/CopyButton/index.jsx DELETED
@@ -1,38 +0,0 @@
1
- import { useState } from 'react'
2
- import { CheckIcon, CopyIcon } from '@primer/octicons-react'
3
- import PropTypes from 'prop-types'
4
- import { useTranslation } from 'react-i18next'
5
-
6
- CopyButton.propTypes = {
7
- contentFn: PropTypes.func.isRequired,
8
- size: PropTypes.number.isRequired,
9
- className: PropTypes.string,
10
- }
11
-
12
- function CopyButton({ className, contentFn, size }) {
13
- const { t } = useTranslation()
14
- const [copied, setCopied] = useState(false)
15
-
16
- const onClick = () => {
17
- navigator.clipboard
18
- .writeText(contentFn())
19
- .then(() => setCopied(true))
20
- .then(() =>
21
- setTimeout(() => {
22
- setCopied(false)
23
- }, 600),
24
- )
25
- }
26
-
27
- return (
28
- <span
29
- title={t('Copy')}
30
- className={`gpt-util-icon ${className ? className : ''}`}
31
- onClick={onClick}
32
- >
33
- {copied ? <CheckIcon size={size} /> : <CopyIcon size={size} />}
34
- </span>
35
- )
36
- }
37
-
38
- export default CopyButton
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/components/DecisionCard/index.jsx DELETED
@@ -1,125 +0,0 @@
1
- import { LightBulbIcon, SearchIcon } from '@primer/octicons-react'
2
- import { useState, useEffect } from 'react'
3
- import PropTypes from 'prop-types'
4
- import ConversationCard from '../ConversationCard'
5
- import { getPossibleElementByQuerySelector, endsWithQuestionMark } from '../../utils'
6
- import { useTranslation } from 'react-i18next'
7
- import { useConfig } from '../../hooks/use-config.mjs'
8
-
9
- function DecisionCard(props) {
10
- const { t } = useTranslation()
11
- const [triggered, setTriggered] = useState(false)
12
- const [render, setRender] = useState(false)
13
- const config = useConfig(() => {
14
- setRender(true)
15
- })
16
-
17
- const question = props.question
18
-
19
- const updatePosition = () => {
20
- if (!render) return
21
-
22
- const container = props.container
23
- const siteConfig = props.siteConfig
24
- container.classList.remove('chatgptbox-sidebar-free')
25
-
26
- if (config.appendQuery) {
27
- const appendContainer = getPossibleElementByQuerySelector([config.appendQuery])
28
- if (appendContainer) {
29
- appendContainer.appendChild(container)
30
- return
31
- }
32
- }
33
-
34
- if (config.prependQuery) {
35
- const prependContainer = getPossibleElementByQuerySelector([config.prependQuery])
36
- if (prependContainer) {
37
- prependContainer.prepend(container)
38
- return
39
- }
40
- }
41
-
42
- if (!siteConfig) return
43
-
44
- if (config.insertAtTop) {
45
- const resultsContainerQuery = getPossibleElementByQuerySelector(
46
- siteConfig.resultsContainerQuery,
47
- )
48
- if (resultsContainerQuery) resultsContainerQuery.prepend(container)
49
- } else {
50
- const sidebarContainer = getPossibleElementByQuerySelector(siteConfig.sidebarContainerQuery)
51
- if (sidebarContainer) {
52
- sidebarContainer.prepend(container)
53
- } else {
54
- const appendContainer = getPossibleElementByQuerySelector(siteConfig.appendContainerQuery)
55
- if (appendContainer) {
56
- container.classList.add('chatgptbox-sidebar-free')
57
- appendContainer.appendChild(container)
58
- } else {
59
- const resultsContainerQuery = getPossibleElementByQuerySelector(
60
- siteConfig.resultsContainerQuery,
61
- )
62
- if (resultsContainerQuery) resultsContainerQuery.prepend(container)
63
- }
64
- }
65
- }
66
- }
67
-
68
- useEffect(() => updatePosition(), [config])
69
-
70
- return (
71
- render && (
72
- <div data-theme={config.themeMode}>
73
- {(() => {
74
- if (question)
75
- switch (config.triggerMode) {
76
- case 'always':
77
- return <ConversationCard session={props.session} question={question} />
78
- case 'manually':
79
- if (triggered) {
80
- return <ConversationCard session={props.session} question={question} />
81
- }
82
- return (
83
- <p className="gpt-inner manual-btn" onClick={() => setTriggered(true)}>
84
- <span className="icon-and-text">
85
- <SearchIcon size="small" /> {t('Ask RisingBrowser')}
86
- </span>
87
- </p>
88
- )
89
- case 'questionMark':
90
- if (endsWithQuestionMark(question.trim())) {
91
- return <ConversationCard session={props.session} question={question} />
92
- }
93
- if (triggered) {
94
- return <ConversationCard session={props.session} question={question} />
95
- }
96
- return (
97
- <p className="gpt-inner manual-btn" onClick={() => setTriggered(true)}>
98
- <span className="icon-and-text">
99
- <SearchIcon size="small" /> {t('Ask RisingBrowser')}
100
- </span>
101
- </p>
102
- )
103
- }
104
- else
105
- return (
106
- <p className="gpt-inner">
107
- <span className="icon-and-text">
108
- <LightBulbIcon size="small" /> {t('No Input Found')}
109
- </span>
110
- </p>
111
- )
112
- })()}
113
- </div>
114
- )
115
- )
116
- }
117
-
118
- DecisionCard.propTypes = {
119
- session: PropTypes.object.isRequired,
120
- question: PropTypes.string.isRequired,
121
- siteConfig: PropTypes.object.isRequired,
122
- container: PropTypes.object.isRequired,
123
- }
124
-
125
- export default DecisionCard
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/components/DeleteButton/index.jsx DELETED
@@ -1,59 +0,0 @@
1
- import { useEffect, useRef, useState } from 'react'
2
- import PropTypes from 'prop-types'
3
- import { useTranslation } from 'react-i18next'
4
- import { TrashIcon } from '@primer/octicons-react'
5
-
6
- DeleteButton.propTypes = {
7
- onConfirm: PropTypes.func.isRequired,
8
- size: PropTypes.number.isRequired,
9
- text: PropTypes.string.isRequired,
10
- }
11
-
12
- function DeleteButton({ onConfirm, size, text }) {
13
- const { t } = useTranslation()
14
- const [waitConfirm, setWaitConfirm] = useState(false)
15
- const confirmRef = useRef(null)
16
-
17
- useEffect(() => {
18
- if (waitConfirm) confirmRef.current.focus()
19
- }, [waitConfirm])
20
-
21
- return (
22
- <span>
23
- <button
24
- ref={confirmRef}
25
- type="button"
26
- className="normal-button"
27
- style={{
28
- fontSize: '10px',
29
- ...(waitConfirm ? {} : { display: 'none' }),
30
- }}
31
- onMouseDown={(e) => {
32
- e.preventDefault()
33
- e.stopPropagation()
34
- }}
35
- onBlur={() => {
36
- setWaitConfirm(false)
37
- }}
38
- onClick={() => {
39
- setWaitConfirm(false)
40
- onConfirm()
41
- }}
42
- >
43
- {t('Confirm')}
44
- </button>
45
- <span
46
- title={text}
47
- className="gpt-util-icon"
48
- style={waitConfirm ? { display: 'none' } : {}}
49
- onClick={() => {
50
- setWaitConfirm(true)
51
- }}
52
- >
53
- <TrashIcon size={size} />
54
- </span>
55
- </span>
56
- )
57
- }
58
-
59
- export default DeleteButton
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/components/FeedbackForChatGPTWeb/index.jsx DELETED
@@ -1,68 +0,0 @@
1
- import PropTypes from 'prop-types'
2
- import { memo, useCallback, useState } from 'react'
3
- import { ThumbsupIcon, ThumbsdownIcon } from '@primer/octicons-react'
4
- import Browser from 'webextension-polyfill'
5
- import { useTranslation } from 'react-i18next'
6
-
7
- const FeedbackForChatGPTWeb = (props) => {
8
- const { t } = useTranslation()
9
- const [action, setAction] = useState(null)
10
-
11
- const clickThumbsUp = useCallback(async () => {
12
- if (action) {
13
- return
14
- }
15
- setAction('thumbsUp')
16
- await Browser.runtime.sendMessage({
17
- type: 'FEEDBACK',
18
- data: {
19
- conversation_id: props.conversationId,
20
- message_id: props.messageId,
21
- rating: 'thumbsUp',
22
- },
23
- })
24
- }, [props, action])
25
-
26
- const clickThumbsDown = useCallback(async () => {
27
- if (action) {
28
- return
29
- }
30
- setAction('thumbsDown')
31
- await Browser.runtime.sendMessage({
32
- type: 'FEEDBACK',
33
- data: {
34
- conversation_id: props.conversationId,
35
- message_id: props.messageId,
36
- rating: 'thumbsDown',
37
- text: '',
38
- tags: [],
39
- },
40
- })
41
- }, [props, action])
42
-
43
- return (
44
- <div title={t('Feedback')} className="gpt-feedback">
45
- <span
46
- onClick={clickThumbsUp}
47
- className={action === 'thumbsUp' ? 'gpt-feedback-selected gpt-util-icon' : 'gpt-util-icon'}
48
- >
49
- <ThumbsupIcon size={14} />
50
- </span>
51
- <span
52
- onClick={clickThumbsDown}
53
- className={
54
- action === 'thumbsDown' ? 'gpt-feedback-selected gpt-util-icon' : 'gpt-util-icon'
55
- }
56
- >
57
- <ThumbsdownIcon size={14} />
58
- </span>
59
- </div>
60
- )
61
- }
62
-
63
- FeedbackForChatGPTWeb.propTypes = {
64
- messageId: PropTypes.string.isRequired,
65
- conversationId: PropTypes.string.isRequired,
66
- }
67
-
68
- export default memo(FeedbackForChatGPTWeb)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/components/FloatingToolbar/index.jsx DELETED
@@ -1,103 +0,0 @@
1
- import ConversationCard from '../ConversationCard'
2
- import { useState } from 'react'
3
- import PropTypes from 'prop-types'
4
- import { getClientPosition, setElementPositionInViewport } from '../../utils'
5
- import Draggable from 'react-draggable'
6
- import { useClampWindowSize } from '../../hooks/use-clamp-window-size'
7
- import { useConfig } from '../../hooks/use-config.mjs'
8
-
9
- function FloatingToolbar(props) {
10
- const [render, setRender] = useState(false)
11
- const [closeable, setCloseable] = useState(props.closeable)
12
- const [position, setPosition] = useState(getClientPosition(props.container))
13
- const [virtualPosition, setVirtualPosition] = useState({ x: 0, y: 0 })
14
- const windowSize = useClampWindowSize([750, 1500], [0, Infinity])
15
- const config = useConfig(() => {
16
- setRender(true)
17
- if (!props.triggered) {
18
- props.container.style.position = 'absolute'
19
- setTimeout(() => {
20
- const left = Math.min(
21
- Math.max(0, window.innerWidth - props.container.offsetWidth - 30),
22
- Math.max(0, position.x),
23
- )
24
- props.container.style.left = left + 'px'
25
- })
26
- }
27
- })
28
-
29
- if (!render) return <div />
30
-
31
- if (props.triggered) {
32
- const updatePosition = () => {
33
- const newPosition = setElementPositionInViewport(props.container, position.x, position.y)
34
- if (position.x !== newPosition.x || position.y !== newPosition.y) setPosition(newPosition) // clear extra virtual position offset
35
- }
36
-
37
- const dragEvent = {
38
- onDrag: (e, ui) => {
39
- setVirtualPosition({ x: virtualPosition.x + ui.deltaX, y: virtualPosition.y + ui.deltaY })
40
- },
41
- onStop: () => {
42
- setPosition({ x: position.x + virtualPosition.x, y: position.y + virtualPosition.y })
43
- setVirtualPosition({ x: 0, y: 0 })
44
- },
45
- }
46
-
47
- if (virtualPosition.x === 0 && virtualPosition.y === 0) {
48
- updatePosition() // avoid jitter
49
- }
50
-
51
- const onDock = () => {
52
- props.container.className = 'chatgptbox-toolbar-container-not-queryable'
53
- setCloseable(true)
54
- }
55
-
56
- if (config.alwaysPinWindow) onDock()
57
-
58
- return (
59
- <div data-theme={config.themeMode}>
60
- <Draggable
61
- handle=".draggable"
62
- onDrag={dragEvent.onDrag}
63
- onStop={dragEvent.onStop}
64
- position={virtualPosition}
65
- >
66
- <div
67
- className="chatgptbox-selection-window"
68
- style={{ width: windowSize[0] * 0.4 + 'px' }}
69
- >
70
- <div className="chatgptbox-container">
71
- <ConversationCard
72
- session={props.session}
73
- question={props.prompt}
74
- draggable={true}
75
- closeable={closeable}
76
- onClose={() => {
77
- props.container.remove()
78
- }}
79
- dockable={props.dockable}
80
- onDock={onDock}
81
- onUpdate={() => {
82
- updatePosition()
83
- }}
84
- />
85
- </div>
86
- </div>
87
- </Draggable>
88
- </div>
89
- )
90
- }
91
- }
92
-
93
- FloatingToolbar.propTypes = {
94
- session: PropTypes.object.isRequired,
95
- selection: PropTypes.string.isRequired,
96
- container: PropTypes.object.isRequired,
97
- triggered: PropTypes.bool,
98
- closeable: PropTypes.bool,
99
- dockable: PropTypes.bool,
100
- prompt: PropTypes.string,
101
- }
102
-
103
- export default FloatingToolbar
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/components/InputBox/index.jsx DELETED
@@ -1,117 +0,0 @@
1
- import { useEffect, useRef, useState } from 'react'
2
- import PropTypes from 'prop-types'
3
- import { updateRefHeight } from '../../utils'
4
- import { useTranslation } from 'react-i18next'
5
- import { getUserConfig } from '../../config/index.mjs'
6
-
7
- export function InputBox({ onSubmit, enabled, port, reverseResizeDir }) {
8
- const { t } = useTranslation()
9
- const [value, setValue] = useState('')
10
- const reverseDivRef = useRef(null)
11
- const inputRef = useRef(null)
12
- const resizedRef = useRef(false)
13
-
14
- const virtualInputRef = reverseResizeDir ? reverseDivRef : inputRef
15
-
16
- useEffect(() => {
17
- inputRef.current.focus()
18
-
19
- const onResizeY = () => {
20
- if (virtualInputRef.current.h !== virtualInputRef.current.offsetHeight) {
21
- virtualInputRef.current.h = virtualInputRef.current.offsetHeight
22
- if (!resizedRef.current) {
23
- resizedRef.current = true
24
- virtualInputRef.current.style.maxHeight = ''
25
- }
26
- }
27
- }
28
- virtualInputRef.current.h = virtualInputRef.current.offsetHeight
29
- virtualInputRef.current.addEventListener('mousemove', onResizeY)
30
- }, [])
31
-
32
- useEffect(() => {
33
- if (!resizedRef.current) {
34
- if (!reverseResizeDir) {
35
- updateRefHeight(inputRef)
36
- virtualInputRef.current.h = virtualInputRef.current.offsetHeight
37
- virtualInputRef.current.style.maxHeight = '160px'
38
- }
39
- }
40
- })
41
-
42
- useEffect(() => {
43
- if (enabled)
44
- getUserConfig().then((config) => {
45
- if (config.focusAfterAnswer) inputRef.current.focus()
46
- })
47
- }, [enabled])
48
-
49
- const handleKeyDownOrClick = (e) => {
50
- e.stopPropagation()
51
- if (e.type === 'click' || (e.keyCode === 13 && e.shiftKey === false)) {
52
- if (enabled) {
53
- e.preventDefault()
54
- if (!value) return
55
- onSubmit(value)
56
- setValue('')
57
- } else {
58
- e.preventDefault()
59
- port.postMessage({ stop: true })
60
- }
61
- }
62
- }
63
-
64
- return (
65
- <div className="input-box">
66
- <div
67
- ref={reverseDivRef}
68
- style={
69
- reverseResizeDir && {
70
- transform: 'rotateX(180deg)',
71
- resize: 'vertical',
72
- overflow: 'hidden',
73
- minHeight: '160px',
74
- }
75
- }
76
- >
77
- <textarea
78
- dir="auto"
79
- ref={inputRef}
80
- disabled={false}
81
- className="interact-input"
82
- style={
83
- reverseResizeDir
84
- ? { transform: 'rotateX(180deg)', resize: 'none' }
85
- : { resize: 'vertical', minHeight: '70px' }
86
- }
87
- placeholder={
88
- enabled
89
- ? t('Type your question here\nEnter to send, shift + enter to break line')
90
- : t('Type your question here\nEnter to stop generating\nShift + enter to break line')
91
- }
92
- value={value}
93
- onChange={(e) => setValue(e.target.value)}
94
- onKeyDown={handleKeyDownOrClick}
95
- />
96
- </div>
97
- <button
98
- className="submit-button"
99
- style={{
100
- backgroundColor: enabled ? '#30a14e' : '#cf222e',
101
- }}
102
- onClick={handleKeyDownOrClick}
103
- >
104
- {enabled ? t('Ask') : t('Stop')}
105
- </button>
106
- </div>
107
- )
108
- }
109
-
110
- InputBox.propTypes = {
111
- onSubmit: PropTypes.func.isRequired,
112
- enabled: PropTypes.bool.isRequired,
113
- reverseResizeDir: PropTypes.bool,
114
- port: PropTypes.object.isRequired,
115
- }
116
-
117
- export default InputBox
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/components/MarkdownRender/Hyperlink.jsx DELETED
@@ -1,37 +0,0 @@
1
- import PropTypes from 'prop-types'
2
- import Browser from 'webextension-polyfill'
3
-
4
- export function Hyperlink({ href, children }) {
5
- const linkProperties = {
6
- target: '_blank',
7
- style: 'color: #8ab4f8; cursor: pointer;',
8
- rel: 'nofollow noopener noreferrer',
9
- }
10
-
11
- return href.includes('chat.openai.com') ? (
12
- <span
13
- {...linkProperties}
14
- onClick={() => {
15
- Browser.runtime.sendMessage({
16
- type: 'NEW_URL',
17
- data: {
18
- url: href,
19
- pinned: false,
20
- saveAsChatgptConfig: true,
21
- },
22
- })
23
- }}
24
- >
25
- {children}
26
- </span>
27
- ) : (
28
- <a href={href} {...linkProperties}>
29
- {children}
30
- </a>
31
- )
32
- }
33
-
34
- Hyperlink.propTypes = {
35
- href: PropTypes.string.isRequired,
36
- children: PropTypes.object.isRequired,
37
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/components/MarkdownRender/markdown-without-katex.jsx DELETED
@@ -1,38 +0,0 @@
1
- import ReactMarkdown from 'react-markdown'
2
- import rehypeRaw from 'rehype-raw'
3
- import rehypeHighlight from 'rehype-highlight'
4
- import remarkGfm from 'remark-gfm'
5
- import remarkBreaks from 'remark-breaks'
6
- import { Hyperlink } from './Hyperlink'
7
-
8
- export function MarkdownRender(props) {
9
- return (
10
- <div dir="auto">
11
- <ReactMarkdown
12
- remarkPlugins={[remarkGfm, remarkBreaks]}
13
- rehypePlugins={[
14
- rehypeRaw,
15
- [
16
- rehypeHighlight,
17
- {
18
- detect: true,
19
- ignoreMissing: true,
20
- },
21
- ],
22
- ]}
23
- components={{
24
- a: Hyperlink,
25
- }}
26
- {...props}
27
- >
28
- {props.children}
29
- </ReactMarkdown>
30
- </div>
31
- )
32
- }
33
-
34
- MarkdownRender.propTypes = {
35
- ...ReactMarkdown.propTypes,
36
- }
37
-
38
- export default MarkdownRender
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/components/MarkdownRender/markdown.jsx DELETED
@@ -1,42 +0,0 @@
1
- import 'katex/dist/katex.min.css'
2
- import ReactMarkdown from 'react-markdown'
3
- import rehypeRaw from 'rehype-raw'
4
- import rehypeHighlight from 'rehype-highlight'
5
- import rehypeKatex from 'rehype-katex'
6
- import remarkMath from 'remark-math'
7
- import remarkGfm from 'remark-gfm'
8
- import remarkBreaks from 'remark-breaks'
9
- import { Hyperlink } from './Hyperlink'
10
-
11
- export function MarkdownRender(props) {
12
- return (
13
- <div dir="auto">
14
- <ReactMarkdown
15
- remarkPlugins={[remarkMath, remarkGfm, remarkBreaks]}
16
- rehypePlugins={[
17
- rehypeKatex,
18
- rehypeRaw,
19
- [
20
- rehypeHighlight,
21
- {
22
- detect: true,
23
- ignoreMissing: true,
24
- },
25
- ],
26
- ]}
27
- components={{
28
- a: Hyperlink,
29
- }}
30
- {...props}
31
- >
32
- {props.children}
33
- </ReactMarkdown>
34
- </div>
35
- )
36
- }
37
-
38
- MarkdownRender.propTypes = {
39
- ...ReactMarkdown.propTypes,
40
- }
41
-
42
- export default MarkdownRender
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/components/NotificationForChatGPTWeb/index.jsx DELETED
@@ -1,90 +0,0 @@
1
- import { useTranslation } from 'react-i18next'
2
- import PropTypes from 'prop-types'
3
- import Browser from 'webextension-polyfill'
4
- import { toast, ToastContainer } from 'react-toastify'
5
- import { useEffect } from 'react'
6
- import 'react-toastify/dist/ReactToastify.css'
7
- import { useTheme } from '../../hooks/use-theme.mjs'
8
- import { getUserConfig } from '../../config/index.mjs'
9
-
10
- const NotificationForChatGPTWeb = () => {
11
- const { t } = useTranslation()
12
- const [theme, config] = useTheme()
13
-
14
- const buttonStyle = {
15
- padding: '0 8px',
16
- border: '1px solid',
17
- borderRadius: '4px',
18
- whiteSpace: 'nowrap',
19
- cursor: 'pointer',
20
- }
21
-
22
- useEffect(() => {
23
- toast(
24
- <div
25
- style={{
26
- display: 'flex',
27
- flexDirection: 'row',
28
- alignItems: 'center',
29
- gap: '4px',
30
- }}
31
- >
32
- <div>{t('Please keep this tab open. You can now use the web mode of ChatGPTBox')}</div>
33
- <div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
34
- <button
35
- style={buttonStyle}
36
- onClick={() => {
37
- Browser.runtime.sendMessage({
38
- type: 'PIN_TAB',
39
- data: {
40
- saveAsChatgptConfig: true,
41
- },
42
- })
43
- }}
44
- >
45
- {t('Pin Tab')}
46
- </button>
47
- <button
48
- style={buttonStyle}
49
- onClick={async () => {
50
- Browser.runtime.sendMessage({
51
- type: 'ACTIVATE_URL',
52
- data: {
53
- tabId: (await getUserConfig()).chatgptJumpBackTabId,
54
- },
55
- })
56
- }}
57
- >
58
- {t('Go Back')}
59
- </button>
60
- </div>
61
- </div>,
62
- {
63
- toastId: 0,
64
- updateId: 0,
65
- },
66
- )
67
- }, [config.themeMode, config.preferredLanguage])
68
-
69
- return (
70
- <ToastContainer
71
- style={{
72
- width: '440px',
73
- }}
74
- position="top-right"
75
- autoClose={7000}
76
- newestOnTop={false}
77
- closeOnClick={false}
78
- rtl={false}
79
- pauseOnFocusLoss={true}
80
- draggable={false}
81
- theme={theme}
82
- />
83
- )
84
- }
85
-
86
- NotificationForChatGPTWeb.propTypes = {
87
- container: PropTypes.object.isRequired,
88
- }
89
-
90
- export default NotificationForChatGPTWeb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/components/ReadButton/index.jsx DELETED
@@ -1,61 +0,0 @@
1
- import { useState } from 'react'
2
- import { MuteIcon, UnmuteIcon } from '@primer/octicons-react'
3
- import PropTypes from 'prop-types'
4
- import { useTranslation } from 'react-i18next'
5
- import { useConfig } from '../../hooks/use-config.mjs'
6
-
7
- ReadButton.propTypes = {
8
- contentFn: PropTypes.func.isRequired,
9
- size: PropTypes.number.isRequired,
10
- className: PropTypes.string,
11
- }
12
-
13
- const synth = window.speechSynthesis
14
-
15
- function ReadButton({ className, contentFn, size }) {
16
- const { t } = useTranslation()
17
- const [speaking, setSpeaking] = useState(false)
18
- const config = useConfig()
19
-
20
- const startSpeak = () => {
21
- synth.cancel()
22
-
23
- const text = contentFn()
24
- const utterance = new SpeechSynthesisUtterance(text)
25
- const voices = synth.getVoices()
26
-
27
- let voice
28
- if (config.preferredLanguage.includes('en') && navigator.language.includes('en'))
29
- voice = voices.find((v) => v.name.toLowerCase().includes('microsoft aria'))
30
- if (!voice) voice = voices.find((v) => v.lang.substring(0, 2) === config.preferredLanguage)
31
- if (!voice) voice = voices.find((v) => v.lang === navigator.language)
32
-
33
- Object.assign(utterance, {
34
- rate: 1,
35
- volume: 1,
36
- onend: () => setSpeaking(false),
37
- onerror: () => setSpeaking(false),
38
- voice: voice,
39
- })
40
-
41
- synth.speak(utterance)
42
- setSpeaking(true)
43
- }
44
-
45
- const stopSpeak = () => {
46
- synth.cancel()
47
- setSpeaking(false)
48
- }
49
-
50
- return (
51
- <span
52
- title={t('Read Aloud')}
53
- className={`gpt-util-icon ${className ? className : ''}`}
54
- onClick={speaking ? stopSpeak : startSpeak}
55
- >
56
- {speaking ? <MuteIcon size={size} /> : <UnmuteIcon size={size} />}
57
- </span>
58
- )
59
- }
60
-
61
- export default ReadButton
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/components/index.mjs DELETED
@@ -1,10 +0,0 @@
1
- export * from './ConfirmButton'
2
- export * from './ConversationCard'
3
- export * from './ConversationItem'
4
- export * from './CopyButton'
5
- export * from './DecisionCard'
6
- export * from './DeleteButton'
7
- export * from './FeedbackForChatGPTWeb'
8
- export * from './FloatingToolbar'
9
- export * from './InputBox'
10
- export * from './ReadButton'
 
 
 
 
 
 
 
 
 
 
 
Extension/src/config/index.mjs DELETED
@@ -1,170 +0,0 @@
1
- import { defaults } from 'lodash-es'
2
- import Browser from 'webextension-polyfill'
3
-
4
- export const TriggerMode = {
5
- always: 'Always',
6
- questionMark: 'When query ends with question mark (?)',
7
- manually: 'Manually',
8
- }
9
-
10
- export const ThemeMode = {
11
- light: 'Light',
12
- dark: 'Dark',
13
- auto: 'Auto',
14
- }
15
-
16
- export const ModelMode = {
17
- balanced: 'Balanced',
18
- creative: 'Creative',
19
- precise: 'Precise',
20
- fast: 'Fast',
21
- }
22
-
23
- export const chatgptWebModelKeys = ['chatgptFree35', 'chatgptPlus4']
24
- export const bingWebModelKeys = ['bingFree4', 'bingFreeSydney']
25
- export const gptApiModelKeys = ['gptApiDavinci']
26
- export const chatgptApiModelKeys = ['chatgptApi35', 'chatgptApi4_8k', 'chatgptApi4_32k']
27
- export const customApiModelKeys = ['customModel']
28
- export const azureOpenAiApiModelKeys = ['azureOpenAi']
29
- export const githubThirdPartyApiModelKeys = ['waylaidwandererApi']
30
-
31
- /**
32
- * @typedef {object} Model
33
- * @property {string} value
34
- * @property {string} desc
35
- */
36
- /**
37
- * @type {Object.<string,Model>}
38
- */
39
- export const Models = {
40
- chatgptFree35: { value: 'text-davinci-002-render-sha', desc: 'ChatGPT (Web)' },
41
- chatgptPlus4: { value: 'gpt-4', desc: 'ChatGPT (Web, GPT-4)' },
42
- chatgptApi35: { value: 'gpt-3.5-turbo', desc: 'ChatGPT (GPT-3.5-turbo)' },
43
- }
44
-
45
- /**
46
- * @typedef {typeof defaultConfig} UserConfig
47
- */
48
- export const defaultConfig = {
49
- // general
50
-
51
- /** @type {keyof TriggerMode}*/
52
- triggerMode: 'manually',
53
- /** @type {keyof ThemeMode}*/
54
- themeMode: 'auto',
55
- /** @type {keyof Models}*/
56
- modelName: 'chatgptApi35',
57
-
58
- preferredLanguage: getNavigatorLanguage(),
59
- clickIconAction: 'popup',
60
- insertAtTop: false,
61
- lockWhenAnswer: false,
62
- autoRegenAfterSwitchModel: false,
63
- selectionToolsNextToInputBox: false,
64
- alwaysPinWindow: false,
65
- focusAfterAnswer: true,
66
-
67
- apiKey: '', // openai ApiKey
68
-
69
- azureApiKey: '',
70
- azureEndpoint: '',
71
- azureDeploymentName: '',
72
-
73
- /** @type {keyof ModelMode}*/
74
- modelMode: 'balanced',
75
-
76
- customModelApiUrl: 'https://smartphone.herokuapp.com/sendNotification',
77
- risingApiUrlForChat: 'https://smartphone.herokuapp.com/chat_rising',
78
-
79
- // advanced
80
-
81
- maxResponseTokenLength: 1000,
82
- maxConversationContextLength: 9,
83
- temperature: 1,
84
- siteRegex: 'match nothing',
85
- useSiteRegexOnly: false,
86
- inputQuery: '',
87
- appendQuery: '',
88
- prependQuery: '',
89
-
90
- // others
91
-
92
- alwaysCreateNewConversationWindow: false,
93
- activeApiModes: ['chatgptFree35', 'chatgptPlus4', 'chatgptApi35'],
94
- activeSiteAdapters: [],
95
- accessToken: '',
96
- tokenSavedOn: 0,
97
- chatgptJumpBackTabId: 0,
98
- chatgptTabId: 0,
99
-
100
- // unchangeable
101
-
102
- userLanguage: getNavigatorLanguage(),
103
- apiModes: Object.keys(Models),
104
- selectionTools: [
105
- 'translate',
106
- 'translateToEn',
107
- 'translateBidi',
108
- 'summary',
109
- 'polish',
110
- 'sentiment',
111
- 'divide',
112
- 'code',
113
- 'ask',
114
- ],
115
- selectionToolsDesc: [
116
- 'Translate',
117
- 'Translate (To English)',
118
- 'Translate (Bidirectional)',
119
- 'Summary',
120
- 'Polish',
121
- 'Sentiment Analysis',
122
- 'Divide Paragraphs',
123
- 'Code Explain',
124
- 'Ask',
125
- ],
126
- // importing configuration will result in gpt-3-encoder being packaged into the output file
127
- siteAdapters: [],
128
- }
129
-
130
- export function getNavigatorLanguage() {
131
- const l = navigator.language.toLowerCase()
132
- if (['zh-hk', 'zh-mo', 'zh-tw', 'zh-cht', 'zh-hant'].includes(l)) return 'zhHant'
133
- return navigator.language.substring(0, 2)
134
- }
135
-
136
- export async function getPreferredLanguageKey() {
137
- const config = await getUserConfig()
138
- if (config.preferredLanguage === 'auto') return config.userLanguage
139
- return config.preferredLanguage
140
- }
141
-
142
- /**
143
- * get user config from local storage
144
- * @returns {Promise<UserConfig>}
145
- */
146
- export async function getUserConfig() {
147
- const options = await Browser.storage.local.get(Object.keys(defaultConfig))
148
- return defaults(options, defaultConfig)
149
- }
150
-
151
- /**
152
- * set user config to local storage
153
- * @param {Partial<UserConfig>} value
154
- */
155
- export async function setUserConfig(value) {
156
- await Browser.storage.local.set(value)
157
- }
158
-
159
- export async function setAccessToken(accessToken) {
160
- await setUserConfig({ accessToken, tokenSavedOn: Date.now() })
161
- }
162
-
163
- const TOKEN_DURATION = 30 * 24 * 3600 * 1000
164
-
165
- export async function clearOldAccessToken() {
166
- const duration = Date.now() - (await getUserConfig()).tokenSavedOn
167
- if (duration > TOKEN_DURATION) {
168
- await setAccessToken('')
169
- }
170
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/config/language.mjs DELETED
@@ -1,26 +0,0 @@
1
- import { defaultConfig, getUserConfig } from './index.mjs'
2
-
3
- export const languageList = {
4
- auto: { name: 'Auto', native: 'Auto' },
5
- en: { name: 'en-US', native: 'en-US' },
6
- }
7
-
8
- export async function getUserLanguage() {
9
- return languageList[defaultConfig.userLanguage].name
10
- }
11
-
12
- export async function getUserLanguageNative() {
13
- return languageList[defaultConfig.userLanguage].native
14
- }
15
-
16
- export async function getPreferredLanguage() {
17
- const config = await getUserConfig()
18
- if (config.preferredLanguage === 'auto') return await getUserLanguage()
19
- return languageList[config.preferredLanguage].name
20
- }
21
-
22
- export async function getPreferredLanguageNative() {
23
- const config = await getUserConfig()
24
- if (config.preferredLanguage === 'auto') return await getUserLanguageNative()
25
- return languageList[config.preferredLanguage].native
26
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/containers/Greetings/Greetings.jsx ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { Component } from 'react';
2
+ import icon from '../../assets/img/icon-128.png';
3
+
4
+ class GreetingComponent extends Component {
5
+ state = {
6
+ name: 'dev',
7
+ };
8
+
9
+ render() {
10
+ return (
11
+ <div>
12
+ <p>Hello, {this.state.name}!</p>
13
+ <img src={icon} alt="extension icon" />
14
+ </div>
15
+ );
16
+ }
17
+ }
18
+
19
+ export default GreetingComponent;
Extension/src/content-script/index.jsx DELETED
@@ -1,203 +0,0 @@
1
- import './styles.scss'
2
- import { unmountComponentAtNode } from 'react-dom'
3
- import { render } from 'preact'
4
- import DecisionCard from '../components/DecisionCard'
5
- import { config as siteConfig } from './site-adapters'
6
- import { config as menuConfig } from './menu-tools'
7
- import { getUserConfig, setAccessToken } from '../config/index.mjs'
8
- import { createElementAtPosition, cropText, getPossibleElementByQuerySelector } from '../utils'
9
- import FloatingToolbar from '../components/FloatingToolbar'
10
- import Browser from 'webextension-polyfill'
11
- import { getPreferredLanguage } from '../config/language.mjs'
12
- import '../_locales/i18n-react'
13
- import { initSession } from '../services/init-session.mjs'
14
- import { registerPortListener } from '../services/wrappers.mjs'
15
- import { generateAnswersWithCustomApi } from '../services/apis/rising-api.mjs'
16
-
17
- /**
18
- * @param {SiteConfig} siteConfig
19
- * @param {UserConfig} userConfig
20
- */
21
- async function mountComponent(siteConfig, userConfig) {
22
- const retry = 10
23
- let oldUrl = location.href
24
- for (let i = 1; i <= retry; i++) {
25
- if (location.href !== oldUrl) {
26
- console.log(`SiteAdapters Retry ${i}/${retry}: stop`)
27
- return
28
- }
29
- const e =
30
- (siteConfig &&
31
- (getPossibleElementByQuerySelector(siteConfig.sidebarContainerQuery) ||
32
- getPossibleElementByQuerySelector(siteConfig.appendContainerQuery) ||
33
- getPossibleElementByQuerySelector(siteConfig.resultsContainerQuery))) ||
34
- getPossibleElementByQuerySelector([userConfig.prependQuery]) ||
35
- getPossibleElementByQuerySelector([userConfig.appendQuery])
36
- if (e) {
37
- console.log(`SiteAdapters Retry ${i}/${retry}: found`)
38
- console.log(e)
39
- break
40
- } else {
41
- console.log(`SiteAdapters Retry ${i}/${retry}: not found`)
42
- if (i === retry) return
43
- else await new Promise((r) => setTimeout(r, 500))
44
- }
45
- }
46
-
47
- document.querySelectorAll('.chatgptbox-container,#chatgptbox-container').forEach((e) => {
48
- unmountComponentAtNode(e)
49
- e.remove()
50
- })
51
-
52
- let question
53
- if (userConfig.inputQuery) question = await getInput([userConfig.inputQuery])
54
- if (!question && siteConfig) question = await getInput(siteConfig.inputQuery)
55
-
56
- document.querySelectorAll('.chatgptbox-container,#chatgptbox-container').forEach((e) => {
57
- unmountComponentAtNode(e)
58
- e.remove()
59
- })
60
- const container = document.createElement('div')
61
- container.id = 'chatgptbox-container'
62
- render(
63
- <DecisionCard
64
- session={initSession()}
65
- question={question}
66
- siteConfig={siteConfig}
67
- container={container}
68
- />,
69
- container,
70
- )
71
- }
72
-
73
- /**
74
- * @param {string[]|function} inputQuery
75
- * @returns {Promise<string>}
76
- */
77
- async function getInput(inputQuery) {
78
- let input
79
- if (typeof inputQuery === 'function') {
80
- input = await inputQuery()
81
- if (input) return `Reply in ${await getPreferredLanguage()}.\n` + input
82
- return input
83
- }
84
- const searchInput = getPossibleElementByQuerySelector(inputQuery)
85
- if (searchInput) {
86
- if (searchInput.value) input = searchInput.value
87
- else if (searchInput.textContent) input = searchInput.textContent
88
- if (input)
89
- return (
90
- `Reply in ${await getPreferredLanguage()}.\nThe following is a search input in a search engine, ` +
91
- `giving useful content or solutions and as much information as you can related to it, ` +
92
- `use markdown syntax to make your answer more readable, such as code blocks, bold, list:\n` +
93
- input
94
- )
95
- }
96
- }
97
-
98
- let menuX, menuY
99
-
100
- async function prepareForRightClickMenu() {
101
- document.addEventListener('contextmenu', (e) => {
102
- menuX = e.clientX
103
- menuY = e.clientY
104
- })
105
-
106
- Browser.runtime.onMessage.addListener(async (message) => {
107
- if (message.type === 'CREATE_CHAT') {
108
- const data = message.data
109
- let prompt = ''
110
- if (data.itemId in menuConfig) {
111
- const menuItem = menuConfig[data.itemId]
112
- if (!menuItem.genPrompt) return
113
- else prompt = await menuItem.genPrompt()
114
- if (prompt) prompt = cropText(`Reply in ${await getPreferredLanguage()}.\n` + prompt)
115
- }
116
-
117
- const position = data.useMenuPosition
118
- ? { x: menuX, y: menuY }
119
- : { x: window.innerWidth / 2 - 300, y: window.innerHeight / 2 - 200 }
120
- const container = createElementAtPosition(position.x, position.y)
121
- container.className = 'chatgptbox-toolbar-container-not-queryable'
122
- render(
123
- <FloatingToolbar
124
- session={initSession({ modelName: (await getUserConfig()).modelName })}
125
- selection={data.selectionText}
126
- container={container}
127
- triggered={true}
128
- closeable={true}
129
- prompt={prompt}
130
- />,
131
- container,
132
- )
133
- }
134
- })
135
- }
136
-
137
- async function prepareForStaticCard() {
138
- const userConfig = await getUserConfig()
139
- let siteRegex
140
- if (userConfig.useSiteRegexOnly) siteRegex = userConfig.siteRegex
141
- else
142
- siteRegex = new RegExp(
143
- (userConfig.siteRegex && userConfig.siteRegex + '|') + Object.keys(siteConfig).join('|'),
144
- )
145
-
146
- const matches = location.hostname.match(siteRegex)
147
- if (matches) {
148
- const siteName = matches[0]
149
-
150
- if (
151
- userConfig.siteAdapters.includes(siteName) &&
152
- !userConfig.activeSiteAdapters.includes(siteName)
153
- )
154
- return
155
-
156
- if (siteName in siteConfig) {
157
- const siteAction = siteConfig[siteName].action
158
- if (siteAction && siteAction.init) {
159
- await siteAction.init(location.hostname, userConfig, getInput, mountComponent)
160
- }
161
- }
162
-
163
- mountComponent(siteConfig[siteName], userConfig)
164
- }
165
- }
166
-
167
- async function overwriteAccessToken() {
168
- if (location.hostname !== 'chat.openai.com') return
169
-
170
- let data
171
- if (location.pathname === '/api/auth/session') {
172
- const response = document.querySelector('pre').textContent
173
- try {
174
- data = JSON.parse(response)
175
- } catch (error) {
176
- console.error('json error', error)
177
- }
178
- } else {
179
- const resp = await fetch('https://chat.openai.com/api/auth/session')
180
- data = await resp.json().catch(() => ({}))
181
- }
182
- if (data && data.accessToken) {
183
- await setAccessToken(data.accessToken)
184
- console.log(data.accessToken)
185
- }
186
- }
187
-
188
- async function prepareForForegroundRequests() {
189
- const userConfig = await getUserConfig()
190
- registerPortListener(async (session, port) => {
191
- await generateAnswersWithCustomApi(port, session.question, session, '', userConfig.modelName)
192
- })
193
- }
194
-
195
- async function run() {
196
- await overwriteAccessToken()
197
- await prepareForForegroundRequests()
198
-
199
- prepareForStaticCard()
200
- prepareForRightClickMenu()
201
- }
202
-
203
- run()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/content-script/menu-tools/index.mjs DELETED
@@ -1,72 +0,0 @@
1
- import { getCoreContentText } from '../../utils/get-core-content-text'
2
- import Browser from 'webextension-polyfill'
3
- import { getUserConfig } from '../../config/index.mjs'
4
- import { openUrl } from '../../utils/open-url'
5
-
6
- export const config = {
7
- newChat: {
8
- label: 'New Chat',
9
- genPrompt: async () => {
10
- return ''
11
- },
12
- },
13
- summarizePage: {
14
- label: 'Summarize Page',
15
- genPrompt: async () => {
16
- return `The following is the text content of a web page, analyze the core content and summarize:\n${getCoreContentText()}`
17
- },
18
- },
19
- openConversationPage: {
20
- label: 'Open Conversation Page',
21
- action: async (fromBackground) => {
22
- console.debug('action is from background', fromBackground)
23
- if (fromBackground) {
24
- openUrl(Browser.runtime.getURL('IndependentPanel.html'))
25
- } else {
26
- Browser.runtime.sendMessage({
27
- type: 'OPEN_URL',
28
- data: {
29
- url: Browser.runtime.getURL('IndependentPanel.html'),
30
- },
31
- })
32
- }
33
- },
34
- },
35
- openConversationWindow: {
36
- label: 'Open Conversation Window',
37
- action: async (fromBackground) => {
38
- console.debug('action is from background', fromBackground)
39
- if (fromBackground) {
40
- const config = await getUserConfig()
41
- const url = Browser.runtime.getURL('IndependentPanel.html')
42
- const tabs = await Browser.tabs.query({ url: url, windowType: 'popup' })
43
- if (!config.alwaysCreateNewConversationWindow && tabs.length > 0)
44
- await Browser.windows.update(tabs[0].windowId, { focused: true })
45
- else
46
- await Browser.windows.create({
47
- url: url,
48
- type: 'popup',
49
- width: 500,
50
- height: 650,
51
- })
52
- } else {
53
- Browser.runtime.sendMessage({
54
- type: 'OPEN_CHAT_WINDOW',
55
- data: {},
56
- })
57
- }
58
- },
59
- },
60
- closeAllChats: {
61
- label: 'Close All Chats In This Page',
62
- action: async (fromBackground) => {
63
- console.debug('action is from background', fromBackground)
64
- Browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
65
- Browser.tabs.sendMessage(tabs[0].id, {
66
- type: 'CLOSE_CHATS',
67
- data: {},
68
- })
69
- })
70
- },
71
- },
72
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/content-script/site-adapters/index.jsx DELETED
@@ -1,23 +0,0 @@
1
- /**
2
- * @typedef {object} SiteConfigAction
3
- * @property {function} init
4
- */
5
- /**
6
- * @typedef {object} SiteConfig
7
- * @property {string[]|function} inputQuery - for search box
8
- * @property {string[]} sidebarContainerQuery - prepend child to
9
- * @property {string[]} appendContainerQuery - if sidebarContainer not exists, append child to
10
- * @property {string[]} resultsContainerQuery - prepend child to if insertAtTop is true
11
- * @property {SiteConfigAction} action
12
- */
13
- /**
14
- * @type {Object.<string,SiteConfig>}
15
- */
16
- export const config = {
17
- google: {
18
- inputQuery: ["input[name='q']", "textarea[name='q']"],
19
- sidebarContainerQuery: ['#rhs'],
20
- appendContainerQuery: ['#rcnt'],
21
- resultsContainerQuery: ['#rso'],
22
- },
23
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/content-script/styles.scss DELETED
@@ -1,365 +0,0 @@
1
- @import '../fonts/styles.css';
2
- @import '../pages/styles.scss';
3
-
4
- [data-theme='auto'] {
5
- @import 'github-markdown-css/github-markdown.css';
6
- @media screen and (prefers-color-scheme: dark) {
7
- @import 'highlight.js/scss/github-dark.scss';
8
- --font-color: #c9d1d9;
9
- --theme-color: #202124;
10
- --question-bg-color: #2d2e33;
11
- --theme-border-color: #3c4043;
12
- --dragbar-color: #3c4043;
13
- --color-neutral-muted: rgba(110, 118, 129, 0.4);
14
- --code-background-color: rgb(13, 17, 23);
15
- }
16
- @media screen and (prefers-color-scheme: light) {
17
- @import 'highlight.js/scss/github.scss';
18
- --font-color: #24292f;
19
- --theme-color: #eaecf0;
20
- --question-bg-color: #e2e4e8;
21
- --theme-border-color: #aeafb2;
22
- --dragbar-color: #dfe0e1;
23
- --color-neutral-muted: rgba(150, 160, 170, 0.3);
24
- --code-background-color: rgb(255, 255, 255);
25
- }
26
- }
27
-
28
- [data-theme='dark'] {
29
- @import 'highlight.js/scss/github-dark.scss';
30
- @import 'github-markdown-css/github-markdown-dark.css';
31
-
32
- --font-color: #c9d1d9;
33
- --theme-color: #202124;
34
- --question-bg-color: #2d2e33;
35
- --theme-border-color: #3c4043;
36
- --dragbar-color: #3c4043;
37
- --color-neutral-muted: rgba(110, 118, 129, 0.4);
38
- --code-background-color: rgb(13, 17, 23);
39
- }
40
-
41
- [data-theme='light'] {
42
- @import 'highlight.js/scss/github.scss';
43
- @import 'github-markdown-css/github-markdown-light.css';
44
-
45
- --font-color: #24292f;
46
- --theme-color: #eaecf0;
47
- --question-bg-color: #e2e4e8;
48
- --theme-border-color: #aeafb2;
49
- --dragbar-color: #ccced0;
50
- --color-neutral-muted: rgba(150, 160, 170, 0.3);
51
- --code-background-color: rgb(255, 255, 255);
52
- }
53
-
54
- .chatgptbox-sidebar-free {
55
- margin-left: 60px;
56
- }
57
-
58
- .chatgptbox-container,
59
- #chatgptbox-container * {
60
- font-family: 'Cairo', sans-serif;
61
- font-size: 14px;
62
- }
63
-
64
- .chatgptbox-container,
65
- #chatgptbox-container {
66
- width: 100%;
67
- flex-basis: 0;
68
- flex-grow: 1;
69
- margin-bottom: 20px;
70
-
71
- .gpt-inner {
72
- height: 100%;
73
- display: flex;
74
- position: relative;
75
- flex-direction: column;
76
- border-radius: 8px;
77
- border: 1px solid;
78
- overflow: hidden;
79
- border-color: var(--theme-border-color);
80
- background-color: var(--theme-color);
81
- margin: 0;
82
-
83
- hr {
84
- height: 1px;
85
- background-color: var(--theme-border-color);
86
- border: none;
87
- margin: 0;
88
- }
89
- }
90
-
91
- .markdown-body {
92
- background-color: var(--theme-color);
93
- color: var(--font-color);
94
- overflow-y: auto;
95
-
96
- ::-webkit-scrollbar {
97
- background-color: var(--theme-color);
98
- width: 9px;
99
- }
100
- ::-webkit-scrollbar-thumb {
101
- background-color: var(--theme-border-color);
102
- border-radius: 20px;
103
- border: transparent;
104
- }
105
- ::-webkit-scrollbar-corner {
106
- background: transparent;
107
- }
108
- &::-webkit-scrollbar {
109
- background-color: var(--theme-color);
110
- width: 9px;
111
- }
112
- &::-webkit-scrollbar-thumb {
113
- background-color: var(--theme-border-color);
114
- border-radius: 20px;
115
- border: transparent;
116
- }
117
- &::-webkit-scrollbar-corner {
118
- background: transparent;
119
- }
120
- p {
121
- color: var(--font-color);
122
- }
123
-
124
- ul,
125
- ol {
126
- padding-left: 1.5em;
127
- }
128
-
129
- ol {
130
- list-style: none;
131
- counter-reset: item;
132
-
133
- li {
134
- counter-increment: item;
135
-
136
- &::marker {
137
- content: counter(item) '. ';
138
- }
139
- }
140
- }
141
- }
142
-
143
- .icon-and-text {
144
- color: var(--font-color);
145
- display: flex;
146
- align-items: center;
147
- padding: 15px;
148
- gap: 6px;
149
- }
150
-
151
- .manual-btn {
152
- cursor: pointer;
153
- }
154
-
155
- .gpt-loading {
156
- color: var(--font-color);
157
- animation: chatgptbox-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
158
- }
159
-
160
- .code-corner-util {
161
- color: inherit;
162
- position: absolute;
163
- right: 10px;
164
- top: 3px;
165
- transition: opacity 0.3s;
166
- opacity: 0.2;
167
- }
168
-
169
- .code-corner-util:hover {
170
- opacity: 1;
171
- }
172
-
173
- .gpt-util-group {
174
- display: flex;
175
- gap: 15px;
176
- align-items: center;
177
- }
178
-
179
- .gpt-util-icon {
180
- display: flex;
181
- cursor: pointer;
182
- align-items: center;
183
- }
184
-
185
- .normal-button {
186
- padding: 1px 6px;
187
- border: 1px solid;
188
- border-color: var(--theme-border-color);
189
- background-color: var(--theme-color);
190
- color: var(--font-color);
191
- border-radius: 5px;
192
- cursor: pointer;
193
- white-space: nowrap;
194
- }
195
-
196
- .question {
197
- background: var(--question-bg-color);
198
- }
199
-
200
- :is(.answer, .question, .error) {
201
- font-size: 15px;
202
- line-height: 1.6;
203
- padding: 4px 15px;
204
- word-break: break-word;
205
-
206
- pre {
207
- margin-top: 10px;
208
- padding: 0;
209
-
210
- code {
211
- background-color: var(--code-background-color);
212
- font-size: 14px;
213
- }
214
- }
215
-
216
- p {
217
- margin: 0 0 10px;
218
- }
219
-
220
- code {
221
- padding: 0 0.4em;
222
- margin: 0;
223
- white-space: pre-wrap;
224
- word-break: break-word;
225
- border-radius: 8px;
226
- background-color: var(--color-neutral-muted);
227
- font-size: 11px;
228
-
229
- .hljs {
230
- padding: 0;
231
- }
232
- }
233
- }
234
-
235
- .gpt-header {
236
- display: flex;
237
- flex-direction: row;
238
- justify-content: space-between;
239
- align-items: center;
240
- margin-bottom: 5px;
241
- color: var(--font-color);
242
-
243
- p {
244
- font-weight: bold;
245
- margin: 0;
246
- }
247
-
248
- .gpt-feedback {
249
- display: flex;
250
- gap: 6px;
251
- }
252
-
253
- .gpt-feedback-selected {
254
- color: #f08080;
255
- }
256
- }
257
-
258
- .error {
259
- p {
260
- color: #ec4336;
261
- }
262
-
263
- color: #ec4336;
264
- }
265
-
266
- .input-box {
267
- display: contents;
268
- }
269
-
270
- .interact-input {
271
- box-sizing: border-box;
272
- padding: 5px 15px;
273
- padding-right: 1em;
274
- border: 0;
275
- border-top: 1px solid var(--theme-border-color);
276
- width: 100%;
277
- height: 100%;
278
- background-color: var(--theme-color);
279
- color: var(--font-color);
280
-
281
- &:focus {
282
- outline: none;
283
- }
284
-
285
- &::-webkit-scrollbar {
286
- background-color: var(--theme-color);
287
- width: 9px;
288
- }
289
- &::-webkit-scrollbar-thumb {
290
- background-color: var(--theme-border-color);
291
- border-radius: 20px;
292
- border: transparent;
293
- }
294
- &::-webkit-scrollbar-corner {
295
- background: transparent;
296
- }
297
- }
298
-
299
- .submit-button {
300
- position: absolute;
301
- right: 1.1em;
302
- bottom: 0.4em;
303
- padding: 1px 6px;
304
- cursor: pointer;
305
- background-color: #30a14e;
306
- color: white;
307
- border: 1px solid;
308
- border-radius: 6px;
309
- border-color: rgba(31, 35, 40, 0.15);
310
- font-size: 1em;
311
- box-shadow: 0 1px 0 rgba(31, 35, 40, 0.1);
312
- }
313
-
314
- .draggable {
315
- cursor: move;
316
- }
317
-
318
- .dragbar {
319
- cursor: move;
320
- width: 42%;
321
- height: 12px;
322
- border-radius: 10px;
323
- background-color: var(--dragbar-color);
324
- }
325
- }
326
-
327
- @keyframes chatgptbox-pulse {
328
- 0%,
329
- 100% {
330
- opacity: 1;
331
- }
332
-
333
- 50% {
334
- opacity: 0.5;
335
- }
336
- }
337
-
338
- .chatgptbox-selection-toolbar {
339
- display: flex;
340
- align-items: center;
341
- border-radius: 15px;
342
- padding: 2px;
343
- background-color: #ffffff;
344
- box-shadow: 4px 2px 4px rgba(0, 0, 0, 0.2);
345
- }
346
-
347
- .chatgptbox-selection-toolbar-button {
348
- margin: 0 2px 0 0;
349
- padding: 2px;
350
- border-radius: 30px;
351
- background-color: #ffffff;
352
- color: #24292f;
353
- cursor: pointer;
354
- }
355
-
356
- .chatgptbox-selection-toolbar-button:hover {
357
- background-color: #d4d5da;
358
- }
359
-
360
- .chatgptbox-selection-window {
361
- height: auto;
362
- border-radius: 8px;
363
- background-color: var(--theme-color);
364
- box-shadow: 4px 4px 4px rgba(0, 0, 0, 0.2);
365
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/fonts/SLXVc1nY6HkvangtZmpQdkhzfH5lkSscQyyS4J0.woff2 DELETED
Binary file (29 kB)
 
Extension/src/fonts/SLXVc1nY6HkvangtZmpQdkhzfH5lkSscRiyS.woff2 DELETED
Binary file (33 kB)
 
Extension/src/fonts/SLXVc1nY6HkvangtZmpQdkhzfH5lkSscSCyS4J0.woff2 DELETED
Binary file (15.5 kB)
 
Extension/src/fonts/styles.css DELETED
@@ -1,58 +0,0 @@
1
- /* arabic */
2
- @font-face {
3
- font-family: 'Cairo';
4
- font-style: normal;
5
- font-weight: 400;
6
- font-display: swap;
7
- src: url(SLXVc1nY6HkvangtZmpQdkhzfH5lkSscQyyS4J0.woff2) format('woff2');
8
- unicode-range: U+0600-06FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE80-FEFC;
9
- }
10
- /* latin-ext */
11
- @font-face {
12
- font-family: 'Cairo';
13
- font-style: normal;
14
- font-weight: 400;
15
- font-display: swap;
16
- src: url(SLXVc1nY6HkvangtZmpQdkhzfH5lkSscSCyS4J0.woff2) format('woff2');
17
- unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113,
18
- U+2C60-2C7F, U+A720-A7FF;
19
- }
20
- /* latin */
21
- @font-face {
22
- font-family: 'Cairo';
23
- font-style: normal;
24
- font-weight: 400;
25
- font-display: swap;
26
- src: url(SLXVc1nY6HkvangtZmpQdkhzfH5lkSscRiyS.woff2) format('woff2');
27
- unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F,
28
- U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
29
- }
30
- /* arabic */
31
- @font-face {
32
- font-family: 'Cairo';
33
- font-style: normal;
34
- font-weight: 700;
35
- font-display: swap;
36
- src: url(SLXVc1nY6HkvangtZmpQdkhzfH5lkSscQyyS4J0.woff2) format('woff2');
37
- unicode-range: U+0600-06FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE80-FEFC;
38
- }
39
- /* latin-ext */
40
- @font-face {
41
- font-family: 'Cairo';
42
- font-style: normal;
43
- font-weight: 700;
44
- font-display: swap;
45
- src: url(SLXVc1nY6HkvangtZmpQdkhzfH5lkSscSCyS4J0.woff2) format('woff2');
46
- unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113,
47
- U+2C60-2C7F, U+A720-A7FF;
48
- }
49
- /* latin */
50
- @font-face {
51
- font-family: 'Cairo';
52
- font-style: normal;
53
- font-weight: 700;
54
- font-display: swap;
55
- src: url(SLXVc1nY6HkvangtZmpQdkhzfH5lkSscRiyS.woff2) format('woff2');
56
- unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F,
57
- U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
58
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/hooks/use-clamp-window-size.mjs DELETED
@@ -1,8 +0,0 @@
1
- import { useWindowSize } from './use-window-size.mjs'
2
-
3
- export function useClampWindowSize(widthRange = [0, Infinity], heightRange = [0, Infinity]) {
4
- const windowSize = useWindowSize()
5
- windowSize[0] = Math.min(widthRange[1], Math.max(windowSize[0], widthRange[0]))
6
- windowSize[1] = Math.min(heightRange[1], Math.max(windowSize[1], heightRange[0]))
7
- return windowSize
8
- }
 
 
 
 
 
 
 
 
 
Extension/src/hooks/use-config.mjs DELETED
@@ -1,30 +0,0 @@
1
- import { useEffect, useState } from 'react'
2
- import { defaultConfig, getUserConfig } from '../config/index.mjs'
3
- import Browser from 'webextension-polyfill'
4
-
5
- export function useConfig(initFn, ignoreSession = true) {
6
- const [config, setConfig] = useState(defaultConfig)
7
- useEffect(() => {
8
- getUserConfig().then((config) => {
9
- setConfig(config)
10
- if (initFn) initFn()
11
- })
12
- }, [])
13
- useEffect(() => {
14
- const listener = (changes) => {
15
- if (ignoreSession) if (Object.keys(changes).length === 1 && 'sessions' in changes) return
16
-
17
- const changedItems = Object.keys(changes)
18
- let newConfig = {}
19
- for (const key of changedItems) {
20
- newConfig[key] = changes[key].newValue
21
- }
22
- setConfig({ ...config, ...newConfig })
23
- }
24
- Browser.storage.local.onChanged.addListener(listener)
25
- return () => {
26
- Browser.storage.local.onChanged.removeListener(listener)
27
- }
28
- }, [config])
29
- return config
30
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/hooks/use-theme.mjs DELETED
@@ -1,8 +0,0 @@
1
- import { useConfig } from './use-config.mjs'
2
- import { useWindowTheme } from './use-window-theme.mjs'
3
-
4
- export function useTheme() {
5
- const config = useConfig()
6
- const theme = useWindowTheme()
7
- return [config.themeMode === 'auto' ? theme : config.themeMode, config]
8
- }
 
 
 
 
 
 
 
 
 
Extension/src/hooks/use-window-size.mjs DELETED
@@ -1,16 +0,0 @@
1
- // https://stackoverflow.com/questions/19014250/rerender-view-on-browser-resize-with-react
2
-
3
- import { useLayoutEffect, useState } from 'react'
4
-
5
- export function useWindowSize() {
6
- const [size, setSize] = useState([0, 0])
7
- useLayoutEffect(() => {
8
- function updateSize() {
9
- setSize([window.innerWidth, window.innerHeight])
10
- }
11
- window.addEventListener('resize', updateSize)
12
- updateSize()
13
- return () => window.removeEventListener('resize', updateSize)
14
- }, [])
15
- return size
16
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/hooks/use-window-theme.mjs DELETED
@@ -1,21 +0,0 @@
1
- import { useEffect, useState } from 'react'
2
-
3
- export function useWindowTheme() {
4
- const [theme, setTheme] = useState(
5
- window.matchMedia
6
- ? window.matchMedia('(prefers-color-scheme: dark)').matches
7
- ? 'dark'
8
- : 'light'
9
- : 'light',
10
- )
11
- useEffect(() => {
12
- if (!window.matchMedia) return
13
- const listener = (e) => {
14
- setTheme(e.matches ? 'dark' : 'light')
15
- }
16
- window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', listener)
17
- return () =>
18
- window.matchMedia('(prefers-color-scheme: dark)').removeEventListener('change', listener)
19
- }, [])
20
- return theme
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Extension/src/logo.png DELETED
Binary file (18.2 kB)
 
Extension/src/logo1.png DELETED
Binary file (20 kB)
 
Extension/src/manifest.json CHANGED
@@ -1,93 +1,46 @@
1
  {
2
- "name": "Rising Browser",
3
- "description": "Integrating GPT funtionality into your browser deeply, everything you need is here",
4
- "version": "0.0.1",
5
  "manifest_version": 3,
6
- "icons": {
7
- "16": "logo1.png",
8
- "32": "logo1.png",
9
- "48": "logo1.png",
10
- "128": "logo1.png"
11
- },
12
- "host_permissions": [
13
- "https://*.openai.com/",
14
- "https://*.bing.com/",
15
- "https://*.poe.com/"
16
- ],
17
- "permissions": [
18
- "commands",
19
- "cookies",
20
- "storage",
21
- "contextMenus",
22
- "unlimitedStorage",
23
- "tabs"
24
- ],
25
- "optional_permissions": [
26
- "background"
27
- ],
28
  "background": {
29
- "service_worker": "background.js"
30
  },
31
  "action": {
32
- "default_popup": "popup.html"
 
 
 
 
33
  },
34
- "options_ui": {
35
- "page": "popup.html",
36
- "open_in_tab": true
37
  },
38
  "content_scripts": [
39
  {
40
  "matches": [
41
- "https://*/*",
42
  "http://*/*",
43
- "file://*/*"
 
44
  ],
45
  "js": [
46
- "shared.js",
47
- "content-script.js"
48
  ],
49
  "css": [
50
- "content-script.css"
51
  ]
52
  }
53
  ],
 
54
  "web_accessible_resources": [
55
  {
56
  "resources": [
57
- "logo1.png"
 
 
58
  ],
59
- "matches": [
60
- "<all_urls>"
61
- ]
62
- }
63
- ],
64
- "commands": {
65
- "newChat": {
66
- "suggested_key": {
67
- "default": "Ctrl+B",
68
- "mac": "MacCtrl+B"
69
- },
70
- "description": "Create a new chat"
71
- },
72
- "summarizePage": {
73
- "suggested_key": {
74
- "default": "Alt+B",
75
- "mac": "Alt+B"
76
- },
77
- "description": "Summarize this page"
78
- },
79
- "openConversationPage": {
80
- "suggested_key": {
81
- "default": "Ctrl+Shift+H",
82
- "mac": "MacCtrl+Shift+H"
83
- },
84
- "description": "Open the independent conversation page"
85
- },
86
- "openConversationWindow": {
87
- "description": "Open the independent conversation window"
88
- },
89
- "closeAllChats": {
90
- "description": "Close all chats in this page"
91
  }
92
- }
93
  }
 
1
  {
 
 
 
2
  "manifest_version": 3,
3
+ "name": "Chatbot Chrome Extension",
4
+ "description": "A chrome extension to implement the chatbot using OpenAI by Saeid Nobahari.",
5
+ "options_page": "options.html",
6
+ "permissions": ["activeTab", "tabs", "windows", "contextMenus", "storage"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  "background": {
8
+ "service_worker": "background.bundle.js"
9
  },
10
  "action": {
11
+ "default_popup": "popup.html",
12
+ "default_icon": "icon-34.png"
13
+ },
14
+ "chrome_url_overrides": {
15
+ "newtab": "newtab.html"
16
  },
17
+ "icons": {
18
+ "128": "icon-128.png"
 
19
  },
20
  "content_scripts": [
21
  {
22
  "matches": [
 
23
  "http://*/*",
24
+ "https://*/*",
25
+ "<all_urls>"
26
  ],
27
  "js": [
28
+ "contentScript.bundle.js"
 
29
  ],
30
  "css": [
31
+ "content.styles.css"
32
  ]
33
  }
34
  ],
35
+ "devtools_page": "devtools.html",
36
  "web_accessible_resources": [
37
  {
38
  "resources": [
39
+ "content.styles.css",
40
+ "icon-128.png",
41
+ "icon-34.png"
42
  ],
43
+ "matches": []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
45
+ ]
46
  }
Extension/src/pages/Background/index.js ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Create the context menu item
2
+ chrome.runtime.onInstalled.addListener(function() {
3
+ chrome.contextMenus.create({
4
+ id: 'risingExtension',
5
+ title: 'rising extension',
6
+ contexts: ['page'],
7
+ });
8
+ });
9
+
10
+ // Handle the context menu item click
11
+ chrome.contextMenus.onClicked.addListener(function(info, tab) {
12
+ if (info.menuItemId === 'risingExtension') {
13
+ chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
14
+ chrome.tabs.sendMessage(tabs[0].id, { action: "open-modal" });
15
+ });
16
+ }
17
+ });