neptune-web commited on
Commit ·
04e85f3
1
Parent(s): a27c5a5
feature(#72): improve the select item functionality and UI/UX
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- Extension/LICENSE +21 -0
- Extension/build.mjs +0 -311
- Extension/package-lock.json +0 -0
- Extension/package.json +50 -76
- Extension/src/_locales/en/main.json +0 -125
- Extension/src/_locales/i18n-react.mjs +0 -11
- Extension/src/_locales/i18n.mjs +0 -7
- Extension/src/_locales/resources.mjs +0 -7
- Extension/src/assets/desc.png +0 -0
- Extension/src/assets/img/icon-128.png +0 -0
- Extension/src/assets/img/icon-34.png +0 -0
- Extension/src/assets/img/logo.svg +7 -0
- Extension/src/background/commands.mjs +0 -27
- Extension/src/background/index.mjs +0 -77
- Extension/src/background/menus.mjs +0 -75
- Extension/src/components/ConfirmButton/index.jsx +0 -58
- Extension/src/components/ConversationCard/index.jsx +0 -483
- Extension/src/components/ConversationItem/index.jsx +0 -140
- Extension/src/components/CopyButton/index.jsx +0 -38
- Extension/src/components/DecisionCard/index.jsx +0 -125
- Extension/src/components/DeleteButton/index.jsx +0 -59
- Extension/src/components/FeedbackForChatGPTWeb/index.jsx +0 -68
- Extension/src/components/FloatingToolbar/index.jsx +0 -103
- Extension/src/components/InputBox/index.jsx +0 -117
- Extension/src/components/MarkdownRender/Hyperlink.jsx +0 -37
- Extension/src/components/MarkdownRender/markdown-without-katex.jsx +0 -38
- Extension/src/components/MarkdownRender/markdown.jsx +0 -42
- Extension/src/components/NotificationForChatGPTWeb/index.jsx +0 -90
- Extension/src/components/ReadButton/index.jsx +0 -61
- Extension/src/components/index.mjs +0 -10
- Extension/src/config/index.mjs +0 -170
- Extension/src/config/language.mjs +0 -26
- Extension/src/containers/Greetings/Greetings.jsx +19 -0
- Extension/src/content-script/index.jsx +0 -203
- Extension/src/content-script/menu-tools/index.mjs +0 -72
- Extension/src/content-script/site-adapters/index.jsx +0 -23
- Extension/src/content-script/styles.scss +0 -365
- Extension/src/fonts/SLXVc1nY6HkvangtZmpQdkhzfH5lkSscQyyS4J0.woff2 +0 -0
- Extension/src/fonts/SLXVc1nY6HkvangtZmpQdkhzfH5lkSscRiyS.woff2 +0 -0
- Extension/src/fonts/SLXVc1nY6HkvangtZmpQdkhzfH5lkSscSCyS4J0.woff2 +0 -0
- Extension/src/fonts/styles.css +0 -58
- Extension/src/hooks/use-clamp-window-size.mjs +0 -8
- Extension/src/hooks/use-config.mjs +0 -30
- Extension/src/hooks/use-theme.mjs +0 -8
- Extension/src/hooks/use-window-size.mjs +0 -16
- Extension/src/hooks/use-window-theme.mjs +0 -21
- Extension/src/logo.png +0 -0
- Extension/src/logo1.png +0 -0
- Extension/src/manifest.json +22 -69
- 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": "
|
|
|
|
|
|
|
|
|
|
| 3 |
"scripts": {
|
| 4 |
-
"build": "node build.
|
| 5 |
-
"
|
| 6 |
-
"
|
| 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 |
-
"@
|
| 22 |
-
"
|
| 23 |
-
"
|
| 24 |
-
"
|
| 25 |
-
"
|
| 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.
|
| 56 |
-
"@babel/plugin-
|
| 57 |
-
"@babel/
|
| 58 |
-
"@babel/preset-
|
| 59 |
-
"@
|
| 60 |
-
"@types/
|
| 61 |
-
"@types/
|
| 62 |
-
"@types/
|
| 63 |
-
"
|
| 64 |
"babel-loader": "^9.1.2",
|
|
|
|
|
|
|
|
|
|
| 65 |
"css-loader": "^6.7.3",
|
| 66 |
-
"
|
| 67 |
-
"eslint": "^
|
| 68 |
-
"eslint-plugin-
|
| 69 |
-
"
|
| 70 |
-
"
|
| 71 |
-
"
|
| 72 |
-
"
|
| 73 |
-
"
|
| 74 |
-
"
|
| 75 |
-
"
|
| 76 |
-
"
|
| 77 |
-
"
|
| 78 |
-
"
|
| 79 |
-
"
|
| 80 |
-
"sass
|
| 81 |
-
"
|
| 82 |
-
"
|
| 83 |
-
"
|
| 84 |
-
"webpack-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
"
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 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 |
-
"
|
| 35 |
-
"
|
| 36 |
-
"open_in_tab": true
|
| 37 |
},
|
| 38 |
"content_scripts": [
|
| 39 |
{
|
| 40 |
"matches": [
|
| 41 |
-
"https://*/*",
|
| 42 |
"http://*/*",
|
| 43 |
-
"
|
|
|
|
| 44 |
],
|
| 45 |
"js": [
|
| 46 |
-
"
|
| 47 |
-
"content-script.js"
|
| 48 |
],
|
| 49 |
"css": [
|
| 50 |
-
"content
|
| 51 |
]
|
| 52 |
}
|
| 53 |
],
|
|
|
|
| 54 |
"web_accessible_resources": [
|
| 55 |
{
|
| 56 |
"resources": [
|
| 57 |
-
"
|
|
|
|
|
|
|
| 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 |
+
});
|