├── .gitignore ├── LICENSE ├── README.md ├── build.sh ├── package-lock.json ├── package.json ├── screenshot.png └── src ├── background ├── fetch-sse.mjs ├── index.mjs └── stream-async-iterable.mjs ├── content-script └── index.mjs ├── logo.png ├── manifest.json └── styles.css /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | background.js 4 | .DS_Store 5 | *.zip -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 wong2 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChatGPT for Search 2 | 3 | A browser extension to display ChatGPT response alongside **Google/Baidu/Toutiao/Bing** Search results. 4 | 5 |  6 | 7 | ## Credit 8 | 9 | This project is based on [wong2/chat-gpt-google-extension](https://github.com/wong2/chat-gpt-google-extension) and brings the same functionality to **Baidu/Toutiao/Bing** Search. 10 | 11 | 12 | ## Installation 13 | 14 | ### Install to Chrome/Edge 15 | 16 | 1. Download `build.zip` from [Releases](https://github.com/doragd/chat-gpt-search-extension/releases) 17 | 2. Unzip the file 18 | 3. In Chrome/Edge go to the extensions page (`chrome://extensions` or `edge://extensions`). 19 | 4. Enable Developer Mode. 20 | 5. Drag the unzipped folder anywhere on the page to import it (do not delete the folder afterwards). 21 | 22 | ## Build from source 23 | 24 | 1. Clone the repo 25 | 2. Install dependencies with `npm` 26 | 3. Run `./build.sh` for Chrome 27 | 4. Load the `build` directory to your browser 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf build 4 | npx esbuild src/content-script/index.mjs src/background/index.mjs --bundle --outdir=build 5 | cp src/*.css build/ 6 | case $1 in 7 | firefox) 8 | cp src/manifest.v2.json build/manifest.json 9 | ;; 10 | *) 11 | cp src/manifest.json build/ 12 | ;; 13 | esac 14 | cp src/*.png build/ 15 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat-gpt-s'e'a'r'c'h-extension", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "chat-gpt-search-extension", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "esbuild": "^0.15.17", 13 | "eventsource-parser": "^0.0.5", 14 | "expiry-map": "^2.0.0", 15 | "uuid": "^9.0.0" 16 | }, 17 | "devDependencies": { 18 | "webextension-polyfill": "^0.10.0" 19 | } 20 | }, 21 | "node_modules/@esbuild/android-arm": { 22 | "version": "0.15.17", 23 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.17.tgz", 24 | "integrity": "sha512-ay6Ken4u+JStjYmqIgh71jMT0bs/rXpCCDKaMfl78B20QYWJglT5P6Ejfm4hWf6Zi+uUWNe7ZmqakRs2BQYIeg==", 25 | "cpu": [ 26 | "arm" 27 | ], 28 | "optional": true, 29 | "os": [ 30 | "android" 31 | ], 32 | "engines": { 33 | "node": ">=12" 34 | } 35 | }, 36 | "node_modules/@esbuild/linux-loong64": { 37 | "version": "0.15.17", 38 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.17.tgz", 39 | "integrity": "sha512-IA1O7f7qxw2DX8oqTpugHElr926phs7Rq8ULXleBMk4go5K05BU0mI8BfCkWcYAvcmVaMc13bv5W3LIUlU6Y9w==", 40 | "cpu": [ 41 | "loong64" 42 | ], 43 | "optional": true, 44 | "os": [ 45 | "linux" 46 | ], 47 | "engines": { 48 | "node": ">=12" 49 | } 50 | }, 51 | "node_modules/esbuild": { 52 | "version": "0.15.17", 53 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.17.tgz", 54 | "integrity": "sha512-8MbkDX+kh0kaeYGd6klMbn1uTOXHoDw7UYMd1dQYA5cqBZivf5+pzfaXZSL1RNamJfXW/uWC5+9wX5ejDgpSqg==", 55 | "hasInstallScript": true, 56 | "bin": { 57 | "esbuild": "bin/esbuild" 58 | }, 59 | "engines": { 60 | "node": ">=12" 61 | }, 62 | "optionalDependencies": { 63 | "@esbuild/android-arm": "0.15.17", 64 | "@esbuild/linux-loong64": "0.15.17", 65 | "esbuild-android-64": "0.15.17", 66 | "esbuild-android-arm64": "0.15.17", 67 | "esbuild-darwin-64": "0.15.17", 68 | "esbuild-darwin-arm64": "0.15.17", 69 | "esbuild-freebsd-64": "0.15.17", 70 | "esbuild-freebsd-arm64": "0.15.17", 71 | "esbuild-linux-32": "0.15.17", 72 | "esbuild-linux-64": "0.15.17", 73 | "esbuild-linux-arm": "0.15.17", 74 | "esbuild-linux-arm64": "0.15.17", 75 | "esbuild-linux-mips64le": "0.15.17", 76 | "esbuild-linux-ppc64le": "0.15.17", 77 | "esbuild-linux-riscv64": "0.15.17", 78 | "esbuild-linux-s390x": "0.15.17", 79 | "esbuild-netbsd-64": "0.15.17", 80 | "esbuild-openbsd-64": "0.15.17", 81 | "esbuild-sunos-64": "0.15.17", 82 | "esbuild-windows-32": "0.15.17", 83 | "esbuild-windows-64": "0.15.17", 84 | "esbuild-windows-arm64": "0.15.17" 85 | } 86 | }, 87 | "node_modules/esbuild-android-64": { 88 | "version": "0.15.17", 89 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.17.tgz", 90 | "integrity": "sha512-sUs6cKMAuAyWnJ/66ezWVr9SMRGFSwoMagxzdhXYggSA12zF7krXSuc1Y9JwxHq56wtv/gFAVo97TFm7RBc1Ig==", 91 | "cpu": [ 92 | "x64" 93 | ], 94 | "optional": true, 95 | "os": [ 96 | "android" 97 | ], 98 | "engines": { 99 | "node": ">=12" 100 | } 101 | }, 102 | "node_modules/esbuild-android-arm64": { 103 | "version": "0.15.17", 104 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.17.tgz", 105 | "integrity": "sha512-RLZuCgIx1rexwxwsXTEW40ZiZzdBI1MBphwDRFyms/iiJGwLxqCH7v75iSJk5s6AF6oa80KC6r/RmzyaX/uJNg==", 106 | "cpu": [ 107 | "arm64" 108 | ], 109 | "optional": true, 110 | "os": [ 111 | "android" 112 | ], 113 | "engines": { 114 | "node": ">=12" 115 | } 116 | }, 117 | "node_modules/esbuild-darwin-64": { 118 | "version": "0.15.17", 119 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.17.tgz", 120 | "integrity": "sha512-+6RTCZ0hfAb+RqTNq1uVsBcP441yZOSi6CyV9BIBryGGVg8RM3Bc6L45e5b68jdRloddN92ekS50e4ElI+cHQA==", 121 | "cpu": [ 122 | "x64" 123 | ], 124 | "optional": true, 125 | "os": [ 126 | "darwin" 127 | ], 128 | "engines": { 129 | "node": ">=12" 130 | } 131 | }, 132 | "node_modules/esbuild-darwin-arm64": { 133 | "version": "0.15.17", 134 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.17.tgz", 135 | "integrity": "sha512-ne4UWUHEKWLgYSE5SLr0/TBcID3k9LPnrzzRXzFLTfD+ygjnW1pMEgdMfmOKIe8jYBUYv8x/YoksriTdQb9r/Q==", 136 | "cpu": [ 137 | "arm64" 138 | ], 139 | "optional": true, 140 | "os": [ 141 | "darwin" 142 | ], 143 | "engines": { 144 | "node": ">=12" 145 | } 146 | }, 147 | "node_modules/esbuild-freebsd-64": { 148 | "version": "0.15.17", 149 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.17.tgz", 150 | "integrity": "sha512-6my3DrwLOe1zhR8UzVRKeo9AFM9XkApJBcx0IE+qKaEbKKBxYAiDBtd2ZMtRA2agqIwRP0kuHofTiDEzpfA+ZA==", 151 | "cpu": [ 152 | "x64" 153 | ], 154 | "optional": true, 155 | "os": [ 156 | "freebsd" 157 | ], 158 | "engines": { 159 | "node": ">=12" 160 | } 161 | }, 162 | "node_modules/esbuild-freebsd-arm64": { 163 | "version": "0.15.17", 164 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.17.tgz", 165 | "integrity": "sha512-LQL7+f+bz+xmAu1FcDBB304Wm2CjONUcOeF4f3TqG7wYXMxjjYQZBFv+0OVapNXyYrM2vy9JMDbps+SheuOnHg==", 166 | "cpu": [ 167 | "arm64" 168 | ], 169 | "optional": true, 170 | "os": [ 171 | "freebsd" 172 | ], 173 | "engines": { 174 | "node": ">=12" 175 | } 176 | }, 177 | "node_modules/esbuild-linux-32": { 178 | "version": "0.15.17", 179 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.17.tgz", 180 | "integrity": "sha512-7E9vZXMZhINQ4/KcxBxioJ2ao5gbXJ6Pa4/LEUd102g3gadSalpg0LrityFgw7ao6qmjcNWwdEYrXaDnOzyyYA==", 181 | "cpu": [ 182 | "ia32" 183 | ], 184 | "optional": true, 185 | "os": [ 186 | "linux" 187 | ], 188 | "engines": { 189 | "node": ">=12" 190 | } 191 | }, 192 | "node_modules/esbuild-linux-64": { 193 | "version": "0.15.17", 194 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.17.tgz", 195 | "integrity": "sha512-TnedHtFQSUVlc0J0D4ZMMalYaQ0Zbt7HSwGy4sav7BlXVqDVc/rchJ/a9dathK51apzLgRyXQMseLf6bkloaSQ==", 196 | "cpu": [ 197 | "x64" 198 | ], 199 | "optional": true, 200 | "os": [ 201 | "linux" 202 | ], 203 | "engines": { 204 | "node": ">=12" 205 | } 206 | }, 207 | "node_modules/esbuild-linux-arm": { 208 | "version": "0.15.17", 209 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.17.tgz", 210 | "integrity": "sha512-+ugCmBTTDIlh+UuC7E/GvyJqjGTX2pNOA+g3isG78aYcfgswrHjvstTtIfljaU95AS30qrVNLgI5h/8TsRWTrg==", 211 | "cpu": [ 212 | "arm" 213 | ], 214 | "optional": true, 215 | "os": [ 216 | "linux" 217 | ], 218 | "engines": { 219 | "node": ">=12" 220 | } 221 | }, 222 | "node_modules/esbuild-linux-arm64": { 223 | "version": "0.15.17", 224 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.17.tgz", 225 | "integrity": "sha512-oupYfh0lTHg+F/2ZoTNrioB+KLd6x0Zlhjz2Oa1jhl8wCGkNvwe25RytR2/SGPYpoNVcvCeoayWQRwwRuWGgfQ==", 226 | "cpu": [ 227 | "arm64" 228 | ], 229 | "optional": true, 230 | "os": [ 231 | "linux" 232 | ], 233 | "engines": { 234 | "node": ">=12" 235 | } 236 | }, 237 | "node_modules/esbuild-linux-mips64le": { 238 | "version": "0.15.17", 239 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.17.tgz", 240 | "integrity": "sha512-aUVyHwUXJF1hi9jsAT+At+cBxZh2yGICi/e757N6d/zzOD+eVK3PKQj68tAvIflx6/ZpnuCTKol1GpgGYrzERg==", 241 | "cpu": [ 242 | "mips64el" 243 | ], 244 | "optional": true, 245 | "os": [ 246 | "linux" 247 | ], 248 | "engines": { 249 | "node": ">=12" 250 | } 251 | }, 252 | "node_modules/esbuild-linux-ppc64le": { 253 | "version": "0.15.17", 254 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.17.tgz", 255 | "integrity": "sha512-i7789iFTLfLccHPNADCbaZPx9CuQblsBqv2j4XqIBN1jKIJbpQ8iqCkWoHep4PLqqKLtBLtTWh919GsrFGdeJA==", 256 | "cpu": [ 257 | "ppc64" 258 | ], 259 | "optional": true, 260 | "os": [ 261 | "linux" 262 | ], 263 | "engines": { 264 | "node": ">=12" 265 | } 266 | }, 267 | "node_modules/esbuild-linux-riscv64": { 268 | "version": "0.15.17", 269 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.17.tgz", 270 | "integrity": "sha512-fEQ/8tnZ2sDniBlPfTXEdg+0OP1olps96HvYdwl8ywJdAlD7AK761EL3lRbRdfMHNOId2N6+CVca43/Fiu/0AQ==", 271 | "cpu": [ 272 | "riscv64" 273 | ], 274 | "optional": true, 275 | "os": [ 276 | "linux" 277 | ], 278 | "engines": { 279 | "node": ">=12" 280 | } 281 | }, 282 | "node_modules/esbuild-linux-s390x": { 283 | "version": "0.15.17", 284 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.17.tgz", 285 | "integrity": "sha512-ZBQekST4gYgTKHAvUJtR1kFFulHTDlRZSE8T0wRQCmQqydNkC1teWxlR31xS6MZevjZGfa7OMVJD24bBhei/2Q==", 286 | "cpu": [ 287 | "s390x" 288 | ], 289 | "optional": true, 290 | "os": [ 291 | "linux" 292 | ], 293 | "engines": { 294 | "node": ">=12" 295 | } 296 | }, 297 | "node_modules/esbuild-netbsd-64": { 298 | "version": "0.15.17", 299 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.17.tgz", 300 | "integrity": "sha512-onNBFaZVN9GzGJMm3aZJJv74n/Q8FjW20G9OfSDhHjvamqJ5vbd42hNk6igQX4lgBCHTZvvBlWDJAMy+tbJAAw==", 301 | "cpu": [ 302 | "x64" 303 | ], 304 | "optional": true, 305 | "os": [ 306 | "netbsd" 307 | ], 308 | "engines": { 309 | "node": ">=12" 310 | } 311 | }, 312 | "node_modules/esbuild-openbsd-64": { 313 | "version": "0.15.17", 314 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.17.tgz", 315 | "integrity": "sha512-QFxHmvjaRrmTCvH/A3EmzqKUSZHRQ7/pbrJeATsb/Q6qckCeL9e7zg/1A3HiZqDXeBUV3yNeBeV1GJBjY6yVyA==", 316 | "cpu": [ 317 | "x64" 318 | ], 319 | "optional": true, 320 | "os": [ 321 | "openbsd" 322 | ], 323 | "engines": { 324 | "node": ">=12" 325 | } 326 | }, 327 | "node_modules/esbuild-sunos-64": { 328 | "version": "0.15.17", 329 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.17.tgz", 330 | "integrity": "sha512-7dHZA8Kc6U8rBTKojJatXtzHTUKJ3CRYimvOGIQQ1yUDOqGx/zZkCH/HkEi3Zg5SWyDj/57E5e1YJPo4ySSw/w==", 331 | "cpu": [ 332 | "x64" 333 | ], 334 | "optional": true, 335 | "os": [ 336 | "sunos" 337 | ], 338 | "engines": { 339 | "node": ">=12" 340 | } 341 | }, 342 | "node_modules/esbuild-windows-32": { 343 | "version": "0.15.17", 344 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.17.tgz", 345 | "integrity": "sha512-yDrNrwQ/0k4N3OZItZ6k6YnBUch8+of06YRYc3hFI8VDm7X1rkNZwhttZNAzF6+TtbnK4cIz7H2/EwdSoaGZ3g==", 346 | "cpu": [ 347 | "ia32" 348 | ], 349 | "optional": true, 350 | "os": [ 351 | "win32" 352 | ], 353 | "engines": { 354 | "node": ">=12" 355 | } 356 | }, 357 | "node_modules/esbuild-windows-64": { 358 | "version": "0.15.17", 359 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.17.tgz", 360 | "integrity": "sha512-jPnXvB4zMMToNPpCBdt+OEQiYFVs9wlQ5G8vMoJkrYJBp1aEt070MRpBFa6pfBFrgXquqgUiNAohMcTdy+JVFg==", 361 | "cpu": [ 362 | "x64" 363 | ], 364 | "optional": true, 365 | "os": [ 366 | "win32" 367 | ], 368 | "engines": { 369 | "node": ">=12" 370 | } 371 | }, 372 | "node_modules/esbuild-windows-arm64": { 373 | "version": "0.15.17", 374 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.17.tgz", 375 | "integrity": "sha512-I5QeSsz0X66V8rxVhmw03Wzn8Tz63H3L9GrsA7C5wvBXMk3qahLWuEL+l7SZ2DleKkFeZZMu1dPxOak9f1TZ4A==", 376 | "cpu": [ 377 | "arm64" 378 | ], 379 | "optional": true, 380 | "os": [ 381 | "win32" 382 | ], 383 | "engines": { 384 | "node": ">=12" 385 | } 386 | }, 387 | "node_modules/eventsource-parser": { 388 | "version": "0.0.5", 389 | "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-0.0.5.tgz", 390 | "integrity": "sha512-BAq82bC3ZW9fPYYZlofXBOAfbpmDzXIOsj+GOehQwgTUYsQZ6HtHs6zuRtge7Ph8OhS6lNH1kJF8q9dj17RcmA==", 391 | "engines": { 392 | "node": ">=12" 393 | } 394 | }, 395 | "node_modules/expiry-map": { 396 | "version": "2.0.0", 397 | "resolved": "https://registry.npmjs.org/expiry-map/-/expiry-map-2.0.0.tgz", 398 | "integrity": "sha512-K1I5wJe2fiqjyUZf/xhxwTpaopw3F+19DsO7Oggl20+3SVTXDIevVRJav0aBMfposQdkl2E4+gnuOKd3j2X0sA==", 399 | "dependencies": { 400 | "map-age-cleaner": "^0.2.0" 401 | }, 402 | "engines": { 403 | "node": ">=8" 404 | } 405 | }, 406 | "node_modules/map-age-cleaner": { 407 | "version": "0.2.0", 408 | "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.2.0.tgz", 409 | "integrity": "sha512-AvxTC6id0fzSf6OyNBTp1syyCuKO7nOJvHgYlhT0Qkkjvk40zZo+av3ayVgXlxnF/DxEzEfY9mMdd7FHsd+wKQ==", 410 | "dependencies": { 411 | "p-defer": "^1.0.0" 412 | }, 413 | "engines": { 414 | "node": ">=7.6" 415 | } 416 | }, 417 | "node_modules/p-defer": { 418 | "version": "1.0.0", 419 | "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", 420 | "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", 421 | "engines": { 422 | "node": ">=4" 423 | } 424 | }, 425 | "node_modules/uuid": { 426 | "version": "9.0.0", 427 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", 428 | "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", 429 | "bin": { 430 | "uuid": "dist/bin/uuid" 431 | } 432 | }, 433 | "node_modules/webextension-polyfill": { 434 | "version": "0.10.0", 435 | "resolved": "https://registry.npmjs.org/webextension-polyfill/-/webextension-polyfill-0.10.0.tgz", 436 | "integrity": "sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==", 437 | "dev": true 438 | } 439 | }, 440 | "dependencies": { 441 | "@esbuild/android-arm": { 442 | "version": "0.15.17", 443 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.17.tgz", 444 | "integrity": "sha512-ay6Ken4u+JStjYmqIgh71jMT0bs/rXpCCDKaMfl78B20QYWJglT5P6Ejfm4hWf6Zi+uUWNe7ZmqakRs2BQYIeg==", 445 | "optional": true 446 | }, 447 | "@esbuild/linux-loong64": { 448 | "version": "0.15.17", 449 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.17.tgz", 450 | "integrity": "sha512-IA1O7f7qxw2DX8oqTpugHElr926phs7Rq8ULXleBMk4go5K05BU0mI8BfCkWcYAvcmVaMc13bv5W3LIUlU6Y9w==", 451 | "optional": true 452 | }, 453 | "esbuild": { 454 | "version": "0.15.17", 455 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.17.tgz", 456 | "integrity": "sha512-8MbkDX+kh0kaeYGd6klMbn1uTOXHoDw7UYMd1dQYA5cqBZivf5+pzfaXZSL1RNamJfXW/uWC5+9wX5ejDgpSqg==", 457 | "requires": { 458 | "@esbuild/android-arm": "0.15.17", 459 | "@esbuild/linux-loong64": "0.15.17", 460 | "esbuild-android-64": "0.15.17", 461 | "esbuild-android-arm64": "0.15.17", 462 | "esbuild-darwin-64": "0.15.17", 463 | "esbuild-darwin-arm64": "0.15.17", 464 | "esbuild-freebsd-64": "0.15.17", 465 | "esbuild-freebsd-arm64": "0.15.17", 466 | "esbuild-linux-32": "0.15.17", 467 | "esbuild-linux-64": "0.15.17", 468 | "esbuild-linux-arm": "0.15.17", 469 | "esbuild-linux-arm64": "0.15.17", 470 | "esbuild-linux-mips64le": "0.15.17", 471 | "esbuild-linux-ppc64le": "0.15.17", 472 | "esbuild-linux-riscv64": "0.15.17", 473 | "esbuild-linux-s390x": "0.15.17", 474 | "esbuild-netbsd-64": "0.15.17", 475 | "esbuild-openbsd-64": "0.15.17", 476 | "esbuild-sunos-64": "0.15.17", 477 | "esbuild-windows-32": "0.15.17", 478 | "esbuild-windows-64": "0.15.17", 479 | "esbuild-windows-arm64": "0.15.17" 480 | } 481 | }, 482 | "esbuild-android-64": { 483 | "version": "0.15.17", 484 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.17.tgz", 485 | "integrity": "sha512-sUs6cKMAuAyWnJ/66ezWVr9SMRGFSwoMagxzdhXYggSA12zF7krXSuc1Y9JwxHq56wtv/gFAVo97TFm7RBc1Ig==", 486 | "optional": true 487 | }, 488 | "esbuild-android-arm64": { 489 | "version": "0.15.17", 490 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.17.tgz", 491 | "integrity": "sha512-RLZuCgIx1rexwxwsXTEW40ZiZzdBI1MBphwDRFyms/iiJGwLxqCH7v75iSJk5s6AF6oa80KC6r/RmzyaX/uJNg==", 492 | "optional": true 493 | }, 494 | "esbuild-darwin-64": { 495 | "version": "0.15.17", 496 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.17.tgz", 497 | "integrity": "sha512-+6RTCZ0hfAb+RqTNq1uVsBcP441yZOSi6CyV9BIBryGGVg8RM3Bc6L45e5b68jdRloddN92ekS50e4ElI+cHQA==", 498 | "optional": true 499 | }, 500 | "esbuild-darwin-arm64": { 501 | "version": "0.15.17", 502 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.17.tgz", 503 | "integrity": "sha512-ne4UWUHEKWLgYSE5SLr0/TBcID3k9LPnrzzRXzFLTfD+ygjnW1pMEgdMfmOKIe8jYBUYv8x/YoksriTdQb9r/Q==", 504 | "optional": true 505 | }, 506 | "esbuild-freebsd-64": { 507 | "version": "0.15.17", 508 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.17.tgz", 509 | "integrity": "sha512-6my3DrwLOe1zhR8UzVRKeo9AFM9XkApJBcx0IE+qKaEbKKBxYAiDBtd2ZMtRA2agqIwRP0kuHofTiDEzpfA+ZA==", 510 | "optional": true 511 | }, 512 | "esbuild-freebsd-arm64": { 513 | "version": "0.15.17", 514 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.17.tgz", 515 | "integrity": "sha512-LQL7+f+bz+xmAu1FcDBB304Wm2CjONUcOeF4f3TqG7wYXMxjjYQZBFv+0OVapNXyYrM2vy9JMDbps+SheuOnHg==", 516 | "optional": true 517 | }, 518 | "esbuild-linux-32": { 519 | "version": "0.15.17", 520 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.17.tgz", 521 | "integrity": "sha512-7E9vZXMZhINQ4/KcxBxioJ2ao5gbXJ6Pa4/LEUd102g3gadSalpg0LrityFgw7ao6qmjcNWwdEYrXaDnOzyyYA==", 522 | "optional": true 523 | }, 524 | "esbuild-linux-64": { 525 | "version": "0.15.17", 526 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.17.tgz", 527 | "integrity": "sha512-TnedHtFQSUVlc0J0D4ZMMalYaQ0Zbt7HSwGy4sav7BlXVqDVc/rchJ/a9dathK51apzLgRyXQMseLf6bkloaSQ==", 528 | "optional": true 529 | }, 530 | "esbuild-linux-arm": { 531 | "version": "0.15.17", 532 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.17.tgz", 533 | "integrity": "sha512-+ugCmBTTDIlh+UuC7E/GvyJqjGTX2pNOA+g3isG78aYcfgswrHjvstTtIfljaU95AS30qrVNLgI5h/8TsRWTrg==", 534 | "optional": true 535 | }, 536 | "esbuild-linux-arm64": { 537 | "version": "0.15.17", 538 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.17.tgz", 539 | "integrity": "sha512-oupYfh0lTHg+F/2ZoTNrioB+KLd6x0Zlhjz2Oa1jhl8wCGkNvwe25RytR2/SGPYpoNVcvCeoayWQRwwRuWGgfQ==", 540 | "optional": true 541 | }, 542 | "esbuild-linux-mips64le": { 543 | "version": "0.15.17", 544 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.17.tgz", 545 | "integrity": "sha512-aUVyHwUXJF1hi9jsAT+At+cBxZh2yGICi/e757N6d/zzOD+eVK3PKQj68tAvIflx6/ZpnuCTKol1GpgGYrzERg==", 546 | "optional": true 547 | }, 548 | "esbuild-linux-ppc64le": { 549 | "version": "0.15.17", 550 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.17.tgz", 551 | "integrity": "sha512-i7789iFTLfLccHPNADCbaZPx9CuQblsBqv2j4XqIBN1jKIJbpQ8iqCkWoHep4PLqqKLtBLtTWh919GsrFGdeJA==", 552 | "optional": true 553 | }, 554 | "esbuild-linux-riscv64": { 555 | "version": "0.15.17", 556 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.17.tgz", 557 | "integrity": "sha512-fEQ/8tnZ2sDniBlPfTXEdg+0OP1olps96HvYdwl8ywJdAlD7AK761EL3lRbRdfMHNOId2N6+CVca43/Fiu/0AQ==", 558 | "optional": true 559 | }, 560 | "esbuild-linux-s390x": { 561 | "version": "0.15.17", 562 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.17.tgz", 563 | "integrity": "sha512-ZBQekST4gYgTKHAvUJtR1kFFulHTDlRZSE8T0wRQCmQqydNkC1teWxlR31xS6MZevjZGfa7OMVJD24bBhei/2Q==", 564 | "optional": true 565 | }, 566 | "esbuild-netbsd-64": { 567 | "version": "0.15.17", 568 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.17.tgz", 569 | "integrity": "sha512-onNBFaZVN9GzGJMm3aZJJv74n/Q8FjW20G9OfSDhHjvamqJ5vbd42hNk6igQX4lgBCHTZvvBlWDJAMy+tbJAAw==", 570 | "optional": true 571 | }, 572 | "esbuild-openbsd-64": { 573 | "version": "0.15.17", 574 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.17.tgz", 575 | "integrity": "sha512-QFxHmvjaRrmTCvH/A3EmzqKUSZHRQ7/pbrJeATsb/Q6qckCeL9e7zg/1A3HiZqDXeBUV3yNeBeV1GJBjY6yVyA==", 576 | "optional": true 577 | }, 578 | "esbuild-sunos-64": { 579 | "version": "0.15.17", 580 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.17.tgz", 581 | "integrity": "sha512-7dHZA8Kc6U8rBTKojJatXtzHTUKJ3CRYimvOGIQQ1yUDOqGx/zZkCH/HkEi3Zg5SWyDj/57E5e1YJPo4ySSw/w==", 582 | "optional": true 583 | }, 584 | "esbuild-windows-32": { 585 | "version": "0.15.17", 586 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.17.tgz", 587 | "integrity": "sha512-yDrNrwQ/0k4N3OZItZ6k6YnBUch8+of06YRYc3hFI8VDm7X1rkNZwhttZNAzF6+TtbnK4cIz7H2/EwdSoaGZ3g==", 588 | "optional": true 589 | }, 590 | "esbuild-windows-64": { 591 | "version": "0.15.17", 592 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.17.tgz", 593 | "integrity": "sha512-jPnXvB4zMMToNPpCBdt+OEQiYFVs9wlQ5G8vMoJkrYJBp1aEt070MRpBFa6pfBFrgXquqgUiNAohMcTdy+JVFg==", 594 | "optional": true 595 | }, 596 | "esbuild-windows-arm64": { 597 | "version": "0.15.17", 598 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.17.tgz", 599 | "integrity": "sha512-I5QeSsz0X66V8rxVhmw03Wzn8Tz63H3L9GrsA7C5wvBXMk3qahLWuEL+l7SZ2DleKkFeZZMu1dPxOak9f1TZ4A==", 600 | "optional": true 601 | }, 602 | "eventsource-parser": { 603 | "version": "0.0.5", 604 | "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-0.0.5.tgz", 605 | "integrity": "sha512-BAq82bC3ZW9fPYYZlofXBOAfbpmDzXIOsj+GOehQwgTUYsQZ6HtHs6zuRtge7Ph8OhS6lNH1kJF8q9dj17RcmA==" 606 | }, 607 | "expiry-map": { 608 | "version": "2.0.0", 609 | "resolved": "https://registry.npmjs.org/expiry-map/-/expiry-map-2.0.0.tgz", 610 | "integrity": "sha512-K1I5wJe2fiqjyUZf/xhxwTpaopw3F+19DsO7Oggl20+3SVTXDIevVRJav0aBMfposQdkl2E4+gnuOKd3j2X0sA==", 611 | "requires": { 612 | "map-age-cleaner": "^0.2.0" 613 | } 614 | }, 615 | "map-age-cleaner": { 616 | "version": "0.2.0", 617 | "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.2.0.tgz", 618 | "integrity": "sha512-AvxTC6id0fzSf6OyNBTp1syyCuKO7nOJvHgYlhT0Qkkjvk40zZo+av3ayVgXlxnF/DxEzEfY9mMdd7FHsd+wKQ==", 619 | "requires": { 620 | "p-defer": "^1.0.0" 621 | } 622 | }, 623 | "p-defer": { 624 | "version": "1.0.0", 625 | "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", 626 | "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==" 627 | }, 628 | "uuid": { 629 | "version": "9.0.0", 630 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", 631 | "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" 632 | }, 633 | "webextension-polyfill": { 634 | "version": "0.10.0", 635 | "resolved": "https://registry.npmjs.org/webextension-polyfill/-/webextension-polyfill-0.10.0.tgz", 636 | "integrity": "sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==", 637 | "dev": true 638 | } 639 | } 640 | } 641 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat-gpt-search-extension", 3 | "version": "1.0.0", 4 | "main": "background.js", 5 | "keywords": [], 6 | "author": "doragd", 7 | "license": "ISC", 8 | "dependencies": { 9 | "esbuild": "^0.15.17", 10 | "eventsource-parser": "^0.0.5", 11 | "expiry-map": "^2.0.0", 12 | "uuid": "^9.0.0" 13 | }, 14 | "devDependencies": { 15 | "webextension-polyfill": "^0.10.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doragd/chat-gpt-search-extension/c223a9daf06a2dfc52b10880fdf5b0ddb1cbdc5c/screenshot.png -------------------------------------------------------------------------------- /src/background/fetch-sse.mjs: -------------------------------------------------------------------------------- 1 | import { createParser } from "eventsource-parser"; 2 | import { streamAsyncIterable } from "./stream-async-iterable.mjs"; 3 | 4 | export async function fetchSSE(resource, options) { 5 | const { onMessage, ...fetchOptions } = options; 6 | const resp = await fetch(resource, fetchOptions); 7 | const parser = createParser((event) => { 8 | if (event.type === "event") { 9 | onMessage(event.data); 10 | } 11 | }); 12 | for await (const chunk of streamAsyncIterable(resp.body)) { 13 | const str = new TextDecoder().decode(chunk); 14 | parser.feed(str); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/background/index.mjs: -------------------------------------------------------------------------------- 1 | import ExpiryMap from "expiry-map"; 2 | import { v4 as uuidv4 } from "uuid"; 3 | import Browser from "webextension-polyfill"; 4 | import { fetchSSE } from "./fetch-sse.mjs"; 5 | 6 | const KEY_ACCESS_TOKEN = "accessToken"; 7 | 8 | const cache = new ExpiryMap(10 * 1000); 9 | 10 | async function getAccessToken() { 11 | if (cache.get(KEY_ACCESS_TOKEN)) { 12 | return cache.get(KEY_ACCESS_TOKEN); 13 | } 14 | const resp = await fetch("https://chat.openai.com/api/auth/session") 15 | .then((r) => r.json()) 16 | .catch(() => ({})); 17 | if (!resp.accessToken) { 18 | throw new Error("UNAUTHORIZED"); 19 | } 20 | cache.set(KEY_ACCESS_TOKEN, resp.accessToken); 21 | return resp.accessToken; 22 | } 23 | 24 | async function getAnswer(question, callback) { 25 | const accessToken = await getAccessToken(); 26 | await fetchSSE("https://chat.openai.com/backend-api/conversation", { 27 | method: "POST", 28 | headers: { 29 | "Content-Type": "application/json", 30 | Authorization: `Bearer ${accessToken}`, 31 | }, 32 | body: JSON.stringify({ 33 | action: "next", 34 | messages: [ 35 | { 36 | id: uuidv4(), 37 | role: "user", 38 | content: { 39 | content_type: "text", 40 | parts: [question], 41 | }, 42 | }, 43 | ], 44 | model: "text-davinci-002-render", 45 | parent_message_id: uuidv4(), 46 | }), 47 | onMessage(message) { 48 | console.debug("sse message", message); 49 | if (message === "[DONE]") { 50 | return; 51 | } 52 | const data = JSON.parse(message); 53 | const text = data.message?.content?.parts?.[0]; 54 | if (text) { 55 | callback(text); 56 | } 57 | }, 58 | }); 59 | } 60 | 61 | Browser.runtime.onConnect.addListener((port) => { 62 | port.onMessage.addListener(async (msg) => { 63 | console.debug("received msg", msg); 64 | try { 65 | await getAnswer(msg.question, (answer) => { 66 | port.postMessage({ answer }); 67 | }); 68 | } catch (err) { 69 | console.error(err); 70 | port.postMessage({ error: err.message }); 71 | cache.delete(KEY_ACCESS_TOKEN); 72 | } 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /src/background/stream-async-iterable.mjs: -------------------------------------------------------------------------------- 1 | export async function* streamAsyncIterable(stream) { 2 | const reader = stream.getReader(); 3 | try { 4 | while (true) { 5 | const { done, value } = await reader.read(); 6 | if (done) { 7 | return; 8 | } 9 | yield value; 10 | } 11 | } finally { 12 | reader.releaseLock(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/content-script/index.mjs: -------------------------------------------------------------------------------- 1 | import Browser from "webextension-polyfill"; 2 | 3 | const container = document.createElement("div"); 4 | 5 | async function run(question) { 6 | container.className = "chat-gpt-container"; 7 | container.innerHTML = '
Waiting for ChatGPT response...
'; 8 | 9 | const port = Browser.runtime.connect(); 10 | port.onMessage.addListener(function (msg) { 11 | if (msg.answer) { 12 | container.innerHTML = 'ChatGPT:
'; 13 | container.querySelector("pre").textContent = msg.answer; 14 | } else if (msg.error === "UNAUTHORIZED") { 15 | container.innerHTML = 16 | 'Please login at chat.openai.com first
'; 17 | } else { 18 | container.innerHTML = "Failed to load response from ChatGPT
"; 19 | } 20 | }); 21 | port.postMessage({ question }); 22 | } 23 | 24 | 25 | 26 | if(location.hostname.match("google")){ 27 | const siderbarContainer = document.getElementById("rhs"); 28 | if (siderbarContainer) { 29 | siderbarContainer.prepend(container); 30 | } else { 31 | container.classList.add("sidebar-free"); 32 | document.getElementById("rcnt").appendChild(container); 33 | } 34 | const searchInput = document.getElementsByName("q")[0]; 35 | if (searchInput && searchInput.value) { 36 | // only run on first page 37 | const startParam = new URL(location.href).searchParams.get("start") || "0"; 38 | if (startParam === "0") { 39 | run(searchInput.value); 40 | } 41 | } 42 | }else if(location.hostname.match("toutiao")){ 43 | const siderbarContainer = document.getElementsByClassName("s-side-list")[0]; 44 | siderbarContainer.prepend(container); 45 | const searchInput = document.querySelector('input[type]'); 46 | if (searchInput && searchInput.value) { 47 | run(searchInput.value); 48 | } 49 | }else if(location.hostname.match("baidu")){ 50 | const siderbarContainer = document.getElementById("con-ceiling-wrapper"); 51 | if(siderbarContainer){ 52 | siderbarContainer.prepend(container); 53 | }else{ 54 | const content_right = document.createElement("div"); 55 | const content_left = document.getElementById("content_left"); 56 | container.classList.add("sidebar-free"); 57 | content_right.setAttribute("id","content_right"); 58 | content_right.setAttribute("tabindex","1"); 59 | content_right.className = "cr-offset"; 60 | content_left.parentNode.insertBefore(content_right,content_left.nextSibling); 61 | document.getElementById("content_right").appendChild(container); 62 | } 63 | const searchInput = document.getElementById("kw"); 64 | if (searchInput && searchInput.value) { 65 | run(searchInput.value); 66 | } 67 | }else if(location.hostname.match("bing")){ 68 | const siderbarContainer = document.getElementById("b_context"); 69 | siderbarContainer.prepend(container); 70 | const searchInput = document.getElementsByName("q")[0]; 71 | if (searchInput && searchInput.value) { 72 | run(searchInput.value); 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doragd/chat-gpt-search-extension/c223a9daf06a2dfc52b10880fdf5b0ddb1cbdc5c/src/logo.png -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ChatGPT for Search", 3 | "description": "Display ChatGPT response alongside Search results", 4 | "version": "1.0.0", 5 | "manifest_version": 3, 6 | "icons": { 7 | "16": "logo.png", 8 | "32": "logo.png", 9 | "48": "logo.png", 10 | "128": "logo.png" 11 | }, 12 | "host_permissions": ["https://*.openai.com/"], 13 | "background": { 14 | "service_worker": "background/index.js" 15 | }, 16 | "content_scripts": [ 17 | { 18 | "matches": [ 19 | "https://*/search*", 20 | "https://*/s?*" 21 | ], 22 | "include_globs": [ 23 | "*.google.*/*", 24 | "*.toutiao.*/*", 25 | "*.baidu.*/*", 26 | "*.bing.*/*" 27 | ], 28 | "js": ["content-script/index.js"], 29 | "css": ["styles.css"] 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | .chat-gpt-container { 2 | margin-bottom: 30px; 3 | border-radius: 8px; 4 | border: 1px solid #dadce0; 5 | padding: 15px; 6 | flex-basis: 0; 7 | flex-grow: 1; 8 | } 9 | 10 | .chat-gpt-container p { 11 | margin: 0; 12 | } 13 | 14 | .chat-gpt-container .prefix { 15 | font-weight: bold; 16 | } 17 | 18 | .chat-gpt-container .loading { 19 | color: #b6b8ba; 20 | animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; 21 | } 22 | 23 | @keyframes pulse { 24 | 25 | 0%, 26 | 100% { 27 | opacity: 1; 28 | } 29 | 30 | 50% { 31 | opacity: 0.5; 32 | } 33 | } 34 | 35 | .chat-gpt-container.sidebar-free { 36 | height: fit-content; 37 | } 38 | 39 | .chat-gpt-container pre { 40 | white-space: pre-wrap; 41 | min-width: 0; 42 | margin-bottom: 0; 43 | line-height: 20px; 44 | } --------------------------------------------------------------------------------