@kudoai/chatgpt.js 3.4.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +153 -86
  3. package/chatgpt.js +91 -76
  4. package/dist/chatgpt.min.js +4 -4
  5. package/docs/README.md +153 -86
  6. package/docs/SECURITY.md +16 -18
  7. package/docs/USERGUIDE.md +19 -5
  8. package/package.json +9 -6
  9. package/starters/chrome/LICENSE.md +3 -3
  10. package/starters/chrome/docs/README.md +5 -5
  11. package/starters/chrome/docs/SECURITY.md +3 -5
  12. package/starters/chrome/extension/components/icons.js +4 -5
  13. package/starters/chrome/extension/components/modals.js +98 -65
  14. package/starters/chrome/extension/content.js +41 -47
  15. package/starters/chrome/extension/icons/faded/icon128.png +0 -0
  16. package/starters/chrome/extension/icons/faded/icon16.png +0 -0
  17. package/starters/chrome/extension/icons/faded/icon32.png +0 -0
  18. package/starters/chrome/extension/icons/faded/icon64.png +0 -0
  19. package/starters/chrome/extension/lib/chatgpt.js +91 -76
  20. package/starters/chrome/extension/lib/dom.js +70 -11
  21. package/starters/chrome/extension/lib/settings.js +8 -9
  22. package/starters/chrome/extension/manifest.json +2 -5
  23. package/starters/chrome/extension/popup/controller.js +18 -16
  24. package/starters/chrome/extension/popup/index.html +1 -1
  25. package/starters/chrome/extension/popup/style.css +26 -13
  26. package/starters/chrome/extension/service-worker.js +7 -4
  27. package/starters/chrome/images/icons/question-mark/icon16.png +0 -0
  28. package/starters/chrome/images/icons/question-mark/icon512.png +0 -0
  29. package/starters/docs/LICENSE.md +21 -1
  30. package/starters/docs/README.md +19 -6
  31. package/starters/greasemonkey/LICENSE.md +3 -3
  32. package/starters/greasemonkey/chatgpt.js-greasemonkey-starter.user.js +5 -6
  33. package/starters/greasemonkey/docs/README.md +1 -1
  34. package/starters/greasemonkey/docs/SECURITY.md +3 -5
  35. /package/starters/greasemonkey/{media → assets}/images/icons/robot/icon48.png +0 -0
  36. /package/starters/greasemonkey/{media → assets}/images/icons/robot/icon64.png +0 -0
  37. /package/starters/greasemonkey/{media → assets}/images/screenshots/chatgpt-userscript-on.png +0 -0
package/docs/USERGUIDE.md CHANGED
@@ -1,8 +1,8 @@
1
1
  <div align="center">
2
2
 
3
3
  <picture>
4
- <source type="image/png" media="(prefers-color-scheme: dark)" srcset="https://media.chatgptjs.org/images/logos/chatgpt.js/with-reflection/darkmode.png?bc68d0c">
5
- <img width=700 src="https://media.chatgptjs.org/images/logos/chatgpt.js/with-reflection/lightmode.png?bc68d0c">
4
+ <source type="image/png" media="(prefers-color-scheme: dark)" srcset="https://assets.chatgptjs.org/images/logos/chatgpt.js/with-reflection/darkmode.png?v=e638eac">
5
+ <img width=700 src="https://assets.chatgptjs.org/images/logos/chatgpt.js/with-reflection/lightmode.png?v=e638eac">
6
6
  </picture>
7
7
 
8
8
  **chatgpt.js** is a powerful JavaScript library that allows for super easy interaction w/ the ChatGPT DOM.
@@ -58,6 +58,7 @@
58
58
  - [getResponseFromAPI `async`](#getresponsefromapi-async)
59
59
  - [getResponseFromDOM](#getresponsefromdom)
60
60
  - [isIdle `async`](#isidle-async)
61
+ - [isTyping](#istyping)
61
62
  - [regenerate](#regenerate)
62
63
  - [resend `async`](#resend-async)
63
64
  - [scrollToBottom](#scrolltobottom)
@@ -159,7 +160,7 @@
159
160
 
160
161
  ```js
161
162
  (async () => {
162
- await import('https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.4.0/dist/chatgpt.min.js');
163
+ await import('https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.6.0/dist/chatgpt.min.js');
163
164
  // Your code here...
164
165
  })();
165
166
  ```
@@ -168,7 +169,7 @@
168
169
 
169
170
  ```js
170
171
  var xhr = new XMLHttpRequest();
171
- xhr.open('GET', 'https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.4.0/dist/chatgpt.min.js');
172
+ xhr.open('GET', 'https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.6.0/dist/chatgpt.min.js');
172
173
  xhr.onload = function () {
173
174
  if (xhr.status === 200) {
174
175
  var chatgptJS = document.createElement('script');
@@ -190,7 +191,7 @@ function yourCode() {
190
191
 
191
192
  ```js
192
193
  ...
193
- // @require https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.4.0/dist/chatgpt.min.js
194
+ // @require https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.6.0/dist/chatgpt.min.js
194
195
  // ==/UserScript==
195
196
 
196
197
  // Your code here...
@@ -929,6 +930,18 @@ Example code:
929
930
  })();
930
931
  ```
931
932
 
933
+ ### isTyping
934
+
935
+ Returns a boolean value. `true` if ChatGPT is generating a response, `false` otherwise.
936
+
937
+ Example code:
938
+
939
+ ```js
940
+ console.log(`ChatGPT is ${!chatgpt.isTyping() ? 'not' : ''} typing`)
941
+ ```
942
+
943
+ ###### _See also: [`isIdle`](#isidle-async)_
944
+
932
945
  ### regenerate
933
946
 
934
947
  Regenerates ChatGPT's response.
@@ -1023,6 +1036,7 @@ Available options:
1023
1036
  - `voice`: A number representing the index of voices available on the user device.
1024
1037
  - `pitch`: A float representing the pitch of the speech. From `0` to `2`.
1025
1038
  - `speed`: A float representing the speed of the speech. From `0.1` to `10`.
1039
+ - `onend`: A callback function invoked when speech finishes playing.
1026
1040
 
1027
1041
  Example code:
1028
1042
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kudoai/chatgpt.js",
3
- "version": "3.4.0",
3
+ "version": "3.6.0",
4
4
  "description": "Client-side JavaScript library for ChatGPT",
5
5
  "author": {
6
6
  "name": "KudoAI & contributors",
@@ -67,12 +67,15 @@
67
67
  "bugs": "https://github.com/KudoAI/chatgpt.js/issues",
68
68
  "devDependencies": {
69
69
  "@adamlui/minify.js": "^1.8.5",
70
- "@adamlui/scss-to-css": "^1.10.22",
71
- "@eslint/json": "^0.9.0",
72
- "@eslint/markdown": "^6.2.1",
73
- "@stylistic/eslint-plugin-js": "^2.12.1",
70
+ "@adamlui/scss-to-css": "^1.10.24",
71
+ "@eslint/css": "^0.2.0",
72
+ "@eslint/json": "^0.10.0",
73
+ "@eslint/markdown": "^6.2.2",
74
+ "@html-eslint/eslint-plugin": "^0.33.1",
75
+ "@html-eslint/parser": "^0.33.0",
76
+ "@stylistic/eslint-plugin-js": "^3.0.1",
74
77
  "docsify-cli": "^4.4.4",
75
- "eslint": "^9.17.0",
78
+ "eslint": "^9.19.0",
76
79
  "eslint-plugin-import": "^2.31.0",
77
80
  "eslint-plugin-regexp": "^2.7.0",
78
81
  "eslint-plugin-yml": "^1.16.0",
@@ -1,8 +1,8 @@
1
1
  <div align="center">
2
2
  <h6>
3
3
  <picture>
4
- <source type="image/svg+xml" media="(prefers-color-scheme: dark)" srcset="https://media.chatgptjs.org/images/icons/earth-americas-white-icon32.svg?main">
5
- <img height=14 src="https://media.chatgptjs.org/images/icons/earth-americas-icon32.svg?main">
4
+ <source type="image/svg+xml" media="(prefers-color-scheme: dark)" srcset="https://assets.chatgptjs.org/images/icons/earth/white/icon32.svg?v=e638eac">
5
+ <img height=14 src="https://assets.chatgptjs.org/images/icons/earth/black/icon32.svg?v=e638eac">
6
6
  </picture>
7
7
  &nbsp;English |
8
8
  <a href="docs/zh-cn/LICENSE.md">简体中文</a> |
@@ -22,7 +22,7 @@
22
22
 
23
23
  # 🏛️ MIT License
24
24
 
25
- **Copyright © 2023–2024 [KudoAI](https://github.com/KudoAI) & contributors**
25
+ **Copyright © 2023–2025 [KudoAI](https://github.com/KudoAI) & contributors**
26
26
 
27
27
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
28
28
 
@@ -3,8 +3,8 @@
3
3
  <div align="center">
4
4
  <h6>
5
5
  <picture>
6
- <source type="image/svg+xml" media="(prefers-color-scheme: dark)" srcset="https://media.chatgptjs.org/images/icons/earth-americas-white-icon32.svg?main">
7
- <img height=14 src="https://media.chatgptjs.org/images/icons/earth-americas-icon32.svg?main">
6
+ <source type="image/svg+xml" media="(prefers-color-scheme: dark)" srcset="https://assets.chatgptjs.org/images/icons/earth/white/icon32.svg?v=e638eac">
7
+ <img height=14 src="https://assets.chatgptjs.org/images/icons/earth/black/icon32.svg?v=e638eac">
8
8
  </picture>
9
9
  &nbsp;English |
10
10
  <a href="zh-cn#readme">简体中文</a> |
@@ -22,7 +22,7 @@
22
22
  </h6>
23
23
  </div>
24
24
 
25
- # <img height=21 src="https://media.chatgptjs.org/images/icons/platforms/chrome/icon32.png?8c852fa5"> chatgpt.js-chrome-starter
25
+ # <img height=21 src="https://assets.chatgptjs.org/images/icons/platforms/chrome/icon32.png?v=e638eac"> chatgpt.js-chrome-starter
26
26
 
27
27
  <h3>A starting point for developing your own Chrome extension using <a href="https://github.com/KudoAI/chatgpt.js">chatgpt.js</a></h3>
28
28
 
@@ -81,13 +81,13 @@ These are some of the extensions featured by Google that use chatgpt.js:
81
81
 
82
82
 
83
83
  <a href="https://chatgptinfinity.com" target="_blank" rel="noopener">
84
- <img width=777 src="https://cdn.jsdelivr.net/gh/adamlui/chatgpt-infinity@0f48c4e/chrome/media/images/tiles/marquee-promo-tile-1400x560.png">
84
+ <img width=777 src="https://assets.chatgptinfinity.com/images/tiles/marquee/tile-1400x560.png?v=34b428b">
85
85
  </a>
86
86
 
87
87
  <p><br>
88
88
 
89
89
  <a href="https://chatgptwidescreen.com" target="_blank" rel="noopener">
90
- <img width=777 src="https://cdn.jsdelivr.net/gh/adamlui/chatgpt-widescreen@3ed0950/chrome/media/images/tiles/marquee-promo-tile-1400x560.png">
90
+ <img width=777 src="https://assets.chatgptwidescreen.com/images/tiles/marquee/tile-1400x560.png?v=4c5d018">
91
91
  </a>
92
92
 
93
93
  </div>
@@ -1,8 +1,8 @@
1
1
  <div align="right">
2
2
  <h6>
3
3
  <picture>
4
- <source type="image/svg+xml" media="(prefers-color-scheme: dark)" srcset="https://media.chatgptjs.org/images/icons/earth-americas-white-icon32.svg?main">
5
- <img height=14 src="https://media.chatgptjs.org/images/icons/earth-americas-icon32.svg?main">
4
+ <source type="image/svg+xml" media="(prefers-color-scheme: dark)" srcset="https://assets.chatgptjs.org/images/icons/earth/white/icon32.svg?v=e638eac">
5
+ <img height=14 src="https://assets.chatgptjs.org/images/icons/earth/black/icon32.svg?v=e638eac">
6
6
  </picture>
7
7
  &nbsp;English |
8
8
  <a href="https://github.com/KudoAI/chatgpt.js-chrome-starter/blob/main/docs/zh-cn/SECURITY.md">简体中文</a> |
@@ -12,6 +12,4 @@
12
12
 
13
13
  # 🛡️ Security Policy
14
14
 
15
- If you find a vulnerability, please open a [draft security advisory](https://github.com/KudoAI/chatgpt.js-chrome-starter/security/advisories/new).
16
-
17
- Pull requests are also welcome, but for safety reasons, send an email to <security@kudoai.com> and wait for a response before making it public.
15
+ If you find a vulnerability, please follow the reporting instructions @ https://tidelift.com/security
@@ -1,10 +1,9 @@
1
1
  // Requires lib/dom.js
2
2
 
3
3
  window.icons = {
4
-
5
- dependencies: {
6
- import(dependencies) { // { app }
7
- for (const name in dependencies) this[name] = dependencies[name] }
4
+ imports: {
5
+ import(deps) { // { app }
6
+ for (const depName in deps) this[depName] = deps[depName] }
8
7
  },
9
8
 
10
9
  create({ name, size = 16, width, height, ...additionalAttrs }) {
@@ -27,6 +26,6 @@ window.icons = {
27
26
 
28
27
  questionMark: {
29
28
  type: 'png',
30
- get src() { return `${icons.dependencies.app.urls.assetHost}@b5551ac/images/icons/question-mark/icon16.png` }
29
+ get src() { return `${icons.imports.app.urls.assetHost}@b5551ac/images/icons/question-mark/icon16.png` }
31
30
  }
32
31
  };
@@ -2,27 +2,89 @@
2
2
 
3
3
  window.modals = {
4
4
  stack: [], // of types of undismissed modals
5
+ get class() { return `${this.imports.app.cssPrefix}-modal` },
5
6
 
6
- dependencies: {
7
- import(dependencies) { // { app, siteAlert }
8
- for (const name in dependencies) this[name] = dependencies[name] }
7
+ imports: {
8
+ import(deps) { // { app, env }
9
+ for (const depName in deps) this[depName] = deps[depName] }
10
+ },
11
+
12
+ alert(title = '', msg = '', btns = '', checkbox = '', width = '') { // generic one from chatgpt.alert()
13
+ const alertID = chatgpt.alert(title, msg, btns, checkbox, width),
14
+ alert = document.getElementById(alertID).firstChild
15
+ this.init(alert) // add classes + rising particles bg
16
+ return alert
9
17
  },
10
18
 
11
19
  open(modalType) {
12
- this.stack.unshift(modalType) // add to stack
13
20
  const modal = this[modalType]() // show modal
14
- modal.classList.add('chatgpt-infinity-modal')
15
- modal.onmousedown = this.dragHandlers.mousedown
16
- dom.fillStarryBG(modal) // fill BG w/ rising stars
21
+ this.stack.unshift(modalType) // add to stack
22
+ this.init(modal) // add classes + rising particles bg
17
23
  this.observeRemoval(modal, modalType) // to maintain stack for proper nav
18
24
  },
19
25
 
26
+ init(modal) {
27
+ if (!modal) return // to support non-div this.open()s
28
+ if (!this.styles) this.stylize() // to init/append stylesheet
29
+ modal.classList.add('no-user-select', this.class) ; modal.parentNode.classList.add(`${this.class}-bg`)
30
+ dom.addRisingParticles(modal)
31
+ },
32
+
33
+ stylize() {
34
+ if (!this.styles) {
35
+ this.styles = dom.create.elem('style') ; this.styles.id = `${this.class}-styles`
36
+ document.head.append(this.styles)
37
+ }
38
+ this.styles.innerText = (
39
+ `.no-user-select {
40
+ user-select: none ; -webkit-user-select: none ; -moz-user-select: none ; -ms-user-select: none }`
41
+ + `.${this.class} {` // modals
42
+ + 'font-family: -apple-system, system-ui, BlinkMacSystemFont, Segoe UI, Roboto,'
43
+ + 'Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, sans-serif ;'
44
+ + 'padding: 20px 25px 24px 25px !important ; font-size: 20px ;'
45
+ + `color: ${ this.imports.env.ui.scheme == 'dark' ? 'white' : 'black' } !important ;`
46
+ + `background-image: linear-gradient(180deg, ${
47
+ this.imports.env.ui.scheme == 'dark' ? '#99a8a6 -200px, black 200px'
48
+ : '#b6ebff -296px, white 171px' }) }`
49
+ + `.${this.class} [class*=modal-close-btn] {`
50
+ + 'position: absolute !important ; float: right ; top: 14px !important ; right: 16px !important ;'
51
+ + 'cursor: pointer ; width: 33px ; height: 33px ; border-radius: 20px }'
52
+ + `.${this.class} [class*=modal-close-btn] svg { height: 10px }`
53
+ + `.${this.class} [class*=modal-close-btn] path {`
54
+ + `${ this.imports.env.ui.scheme == 'dark' ? 'stroke: white ; fill: white'
55
+ : 'stroke: #9f9f9f ; fill: #9f9f9f' }}`
56
+ + ( this.imports.env.ui.scheme == 'dark' ? // invert dark mode hover paths
57
+ `.${this.class} [class*=modal-close-btn]:hover path { stroke: black ; fill: black }` : '' )
58
+ + `.${this.class} [class*=modal-close-btn]:hover { background-color: #f2f2f2 }` // hover underlay
59
+ + `.${this.class} [class*=modal-close-btn] svg { margin: 11.5px }` // center SVG for hover underlay
60
+ + `.${this.class} a {`
61
+ + `color: #${ this.imports.env.ui.scheme == 'dark' ? '00cfff' : '1e9ebb' } !important }`
62
+ + `.${this.class} h2 { font-weight: bold }`
63
+ + `.${this.class} button {`
64
+ + '--btn-transition: transform 0.1s ease-in-out, box-shadow 0.1s ease-in-out ;'
65
+ + 'font-size: 14px ; text-transform: uppercase ;' // shrink/uppercase labels
66
+ + 'border-radius: 0 !important ;' // square borders
67
+ + 'transition: var(--btn-transition) ;' // smoothen hover fx
68
+ + '-webkit-transition: var(--btn-transition) ; -moz-transition: var(--btn-transition) ;'
69
+ + '-o-transition: var(--btn-transition) ; -ms-transition: var(--btn-transition) ;'
70
+ + 'cursor: pointer !important ;' // add finger cursor
71
+ + `border: 1px solid ${ this.imports.env.ui.scheme == 'dark' ? 'white' : 'black' } !important ;`
72
+ + 'padding: 8px !important ; min-width: 102px }' // resize
73
+ + `.${this.class} button:hover {` // add zoom, re-scheme
74
+ + 'transform: scale(1.055) ; color: black !important ;'
75
+ + `background-color: #${ this.imports.env.ui.scheme == 'dark' ? '00cfff' : '9cdaff' } !important }`
76
+ + ( !this.imports.env.browser.isMobile ?
77
+ `.${this.class} .modal-buttons { margin-left: -13px !important }` : '' )
78
+ + `.about-em { color: ${ this.imports.env.ui.scheme == 'dark' ? 'white' : 'green' } !important }`
79
+ )
80
+ },
81
+
20
82
  observeRemoval(modal, modalType) { // to maintain stack for proper nav
21
83
  const modalBG = modal.parentNode
22
84
  new MutationObserver(([mutation], obs) => {
23
85
  mutation.removedNodes.forEach(removedNode => { if (removedNode == modalBG) {
24
86
  if (this.stack[0] == modalType) { // new modal not launched, implement nav back logic
25
- this.stack.shift() // remove this modal type from stack
87
+ this.stack.shift() // remove this modal type from stack 1st
26
88
  const prevModalType = this.stack[0]
27
89
  if (prevModalType) { // open it
28
90
  this.stack.shift() // remove type from stack since re-added on open
@@ -34,73 +96,44 @@ window.modals = {
34
96
  }).observe(modalBG.parentNode, { childList: true, subtree: true })
35
97
  },
36
98
 
37
- dragHandlers: {
38
- mousedown(event) { // find modal, attach listeners, init XY offsets
39
- if (event.button != 0) return // prevent non-left-click drag
40
- if (getComputedStyle(event.target).cursor == 'pointer') return // prevent drag on interactive elems
41
- modals.dragHandlers.draggableElem = event.currentTarget
42
- modals.dragHandlers.draggableElem.style.cursor = 'grabbing'
43
- event.preventDefault(); // prevent sub-elems like icons being draggable
44
- ['mousemove', 'mouseup'].forEach(event => document.addEventListener(event, modals.dragHandlers[event]))
45
- const draggableElemRect = modals.dragHandlers.draggableElem.getBoundingClientRect()
46
- modals.dragHandlers.offsetX = event.clientX - draggableElemRect.left +21
47
- modals.dragHandlers.offsetY = event.clientY - draggableElemRect.top +12
48
- },
49
-
50
- mousemove(event) { // drag modal
51
- if (modals.dragHandlers.draggableElem) {
52
- const newX = event.clientX - modals.dragHandlers.offsetX,
53
- newY = event.clientY - modals.dragHandlers.offsetY
54
- Object.assign(modals.dragHandlers.draggableElem.style, { left: `${newX}px`, top: `${newY}px` })
55
- }
56
- },
57
-
58
- mouseup() { // remove listeners, reset modals.dragHandlers.draggableElem
59
- modals.dragHandlers.draggableElem.style.cursor = 'inherit';
60
- ['mousemove', 'mouseup'].forEach(event =>
61
- document.removeEventListener(event, modals.dragHandlers[event]))
62
- modals.dragHandlers.draggableElem = null
63
- }
64
- },
65
-
66
99
  about() {
67
100
 
68
- // Init styles
69
- const headingStyle = 'font-size: 1.15rem',
70
- pStyle = 'position: relative ; left: 3px',
71
- pBrStyle = 'position: relative ; left: 4px ',
72
- aStyle = 'color: ' + ( chatgpt.isDarkMode() ? '#c67afb' : '#8325c4' ) // purple
73
-
74
- // Init buttons
75
- const modalBtns = [
76
- function getSupport(){ modals.safeWinOpen(`${modals.dependencies.app.urls.gitHub}/issues`) },
77
- function rateUs() { modals.safeWinOpen(`${modals.dependencies.app.urls.gitHub}/discussions`) },
78
- function moreAiExtensions(){ modals.safeWinOpen(modals.dependencies.app.urls.relatedExtensions) }
79
- ]
80
-
81
101
  // Show modal
82
- const aboutModal = this.dependencies.siteAlert(
83
- `${this.dependencies.app.symbol} ${chrome.runtime.getManifest().name}`, // title
84
- `<span style="${headingStyle}"><b>🏷️ <i>Version</i></b>: </span>`
85
- + `<span style="${pStyle}">${this.dependencies.app.version}</span>\n`
86
- + `<span style="${headingStyle}"><b>⚡ <i>Powered by</i></b>: </span>`
87
- + `<span style="${pStyle}">`
88
- + `<a style="${aStyle}" href="${this.dependencies.app.urls.chatgptJS}" target="_blank"`
89
- + ' rel="noopener">chatgpt.js</a></span>\n'
90
- + `<span style="${headingStyle}"><b>📜 <i>Open source code</i></b>:</span>\n`
91
- + `<span style="${pBrStyle}"><a href="${this.dependencies.app.urls.gitHub}" target="_blank"`
92
- + ` rel="nopener">${this.dependencies.app.urls.gitHub}</a></span>`,
93
- modalBtns, '', 451
102
+ const aboutModal = this.alert(
103
+ `${this.imports.app.symbol} ${chrome.runtime.getManifest().name}`, // title
104
+ '🧠 Author: ' // msg
105
+ + `<a href="${this.imports.app.author.url}">${this.imports.app.author.name}</a> `
106
+ + `& <a href="${this.imports.app.urls.contributors}">contributors</a>\n`
107
+ + `🏷️ Version: <span class="about-em">${this.imports.app.version}</span>\n`
108
+ + '📜 Open source code: '
109
+ + `<a href="${this.imports.app.urls.gitHub}" target="_blank" rel="nopener">`
110
+ + this.imports.app.urls.gitHub + '</a>\n'
111
+ + '⚡ Powered by: '
112
+ + `<a href="${this.imports.app.urls.chatgptJS}" target="_blank" rel="noopener">chatgpt.js</a>`,
113
+ [ function getSupport(){}, function rateUs(){}, function moreAiExtensions(){} ], // button labels
114
+ '', 656 // modal width
94
115
  )
95
116
 
96
117
  // Format text
97
- aboutModal.querySelector('h2').style.cssText = 'text-align: center ; font-size: 37px ; padding: 9px'
98
- aboutModal.querySelector('p').style.cssText = 'text-align: center'
118
+ aboutModal.querySelector('h2').style.cssText = (
119
+ 'text-align: center ; font-size: 51px ; line-height: 46px ; padding: 15px 0' )
120
+ aboutModal.querySelector('p').style.cssText = (
121
+ 'text-align: center ; overflow-wrap: anywhere ;'
122
+ + `margin: ${ this.imports.env.browser.isPortrait ? '6px 0 -16px' : '3px 0 0' }` )
99
123
 
100
124
  // Hack buttons
101
125
  aboutModal.querySelector('.modal-buttons').style.justifyContent = 'center'
102
126
  aboutModal.querySelectorAll('button').forEach(btn => {
103
- btn.style.cssText = 'cursor: pointer !important ; height: 43px'
127
+ btn.style.cssText = 'height: 55px ; min-width: 136px ; text-align: center'
128
+
129
+ // Replace buttons w/ clones that don't dismiss modal
130
+ const btnClone = btn.cloneNode(true)
131
+ btn.parentNode.replaceChild(btnClone, btn) ; btn = btnClone
132
+ btn.onclick = () => this.safeWinOpen(
133
+ btn.textContent == 'Get Support' ? `${modals.imports.app.urls.gitHub}/issues`
134
+ : btn.textContent == 'Rate Us' ? `${modals.imports.app.urls.gitHub}/discussions`
135
+ : modals.imports.app.urls.relatedExtensions
136
+ )
104
137
 
105
138
  // Prepend emoji
106
139
  if (/support/i.test(btn.textContent))
@@ -1,5 +1,5 @@
1
1
  // NOTE: This script relies on the powerful chatgpt.js library @ https://chatgpt.js.org
2
- // © 2023–2024 KudoAI & contributors under the MIT license
2
+ // © 2023–2025 KudoAI & contributors under the MIT license
3
3
 
4
4
  (async () => {
5
5
 
@@ -8,18 +8,22 @@
8
8
  await import(chrome.runtime.getURL(resource))
9
9
 
10
10
  // Init ENV context
11
- const env = { browser: { isMobile: chatgpt.browser.isMobile() }}
11
+ const env = { browser: { isMobile: chatgpt.browser.isMobile() }, ui: { scheme: getScheme() }}
12
+ env.browser.isPortrait = env.browser.isMobile && (window.innerWidth < window.innerHeight)
12
13
 
13
14
  // Import APP data
14
15
  const { app } = await chrome.storage.sync.get('app')
15
- modals.dependencies.import({ app, siteAlert })
16
+
17
+ // Export DEPENDENCIES to imported resources
18
+ dom.imports.import({ env }) // for env.ui.scheme
19
+ modals.imports.import({ app, env }) // for app data + env.ui.scheme
16
20
 
17
21
  // Add CHROME MSG listener
18
22
  chrome.runtime.onMessage.addListener(req => { // from service-worker.js + popup/index.html
19
23
  if (req.action == 'notify')
20
24
  notify(...['msg', 'pos', 'notifDuration', 'shadow'].map(arg => req.options[arg]))
21
25
  else if (req.action == 'alert')
22
- siteAlert(...['title', 'msg', 'btns', 'checkbox', 'width'].map(arg => req.options[arg]))
26
+ modals.alert(...['title', 'msg', 'btns', 'checkbox', 'width'].map(arg => req.options[arg]))
23
27
  else if (req.action == 'showAbout') chatgpt.isLoaded().then(() => { modals.open('about') })
24
28
  else if (req.action == 'syncConfigToUI') syncConfigToUI(req.options)
25
29
  })
@@ -27,7 +31,7 @@
27
31
  // Init SETTINGS
28
32
  await settings.load(Object.keys(settings.controls), 'skipAlert')
29
33
 
30
- // Define FEEDBACK functions
34
+ // Define FUNCTIONS
31
35
 
32
36
  function notify(msg, pos = '', notifDuration = '', shadow = '') {
33
37
 
@@ -36,27 +40,27 @@
36
40
  if (foundState) msg = msg.replace(foundState, '')
37
41
 
38
42
  // Show notification
39
- siteAlert(`${app.symbol} ${msg}`, pos, notifDuration,
40
- shadow || chatgpt.isDarkMode() ? '' : 'shadow' )
43
+ chatgpt.notify(`${app.symbol} ${msg}`, pos, notifDuration, shadow || env.ui.scheme == 'dark' ? '' : 'shadow')
41
44
  const notif = document.querySelector('.chatgpt-notif:last-child')
42
45
 
43
46
  // Append styled state word
44
47
  if (foundState) {
48
+ const stateStyles = {
49
+ on: {
50
+ light: 'color: #5cef48 ; text-shadow: rgba(255,250,169,0.38) 2px 1px 5px',
51
+ dark: 'color: #5cef48 ; text-shadow: rgb(55, 255, 0) 3px 0 10px'
52
+ },
53
+ off: {
54
+ light: 'color: #ef4848 ; text-shadow: rgba(255,169,225,0.44) 2px 1px 5px',
55
+ dark: 'color: #ef4848 ; text-shadow: rgba(255, 116, 116, 0.87) 3px 0 9px'
56
+ }
57
+ }
45
58
  const styledStateSpan = document.createElement('span')
46
- styledStateSpan.style.cssText = `color: ${
47
- foundState == 'OFF' ? '#ef4848 ; text-shadow: rgba(255, 169, 225, 0.44) 2px 1px 5px'
48
- : '#5cef48 ; text-shadow: rgba(255, 250, 169, 0.38) 2px 1px 5px' }`
59
+ styledStateSpan.style.cssText = stateStyles[foundState.toLowerCase()][env.ui.scheme]
49
60
  styledStateSpan.append(foundState) ; notif.append(styledStateSpan)
50
61
  }
51
62
  }
52
63
 
53
- function siteAlert(title = '', msg = '', btns = '', checkbox = '', width = '') {
54
- const alertID = chatgpt.alert(title, msg, btns, checkbox, width)
55
- return document.getElementById(alertID).firstChild
56
- }
57
-
58
- // Define SYNC function
59
-
60
64
  async function syncConfigToUI(options) { // eslint-disable-line
61
65
  await settings.load('extensionDisabled', Object.keys(settings.controls)) // load from Chrome storage to content.js config
62
66
  if (config.extensionDisabled) {
@@ -70,49 +74,29 @@
70
74
  }
71
75
  }
72
76
 
77
+ function getScheme() {
78
+ return document.documentElement.className
79
+ || (window.matchMedia?.('(prefers-color-scheme: dark)')?.matches ? 'dark' : 'light')
80
+ }
81
+
73
82
  // Run MAIN routine
74
83
 
75
84
  chatgpt.printAllFunctions() // to console
76
85
 
77
86
  // CHILL a bit if your hacks depend on delayed DOM content
78
87
  await chatgpt.isLoaded()
79
- await new Promise(resolve => setTimeout(resolve, 500)) // sleep .5s
80
-
81
- // Add/update TWEAKS style
82
- const tweaksStyleUpdated = 1732627011377 // timestamp of last edit for this file's tweaksStyle
83
- let tweaksStyle = document.getElementById('tweaks-style') // try to select existing style (from your other extensions)
84
- if (!tweaksStyle || parseInt(tweaksStyle.getAttribute('last-updated')) < tweaksStyleUpdated) {
85
- if (!tweaksStyle) { // outright missing, create/id/attr/append it first
86
- tweaksStyle = dom.create.elem('style', {
87
- id: 'tweaks-style', 'last-updated': tweaksStyleUpdated.toString() })
88
- document.head.append(tweaksStyle)
89
- }
90
- tweaksStyle.innerText = (
91
- '[class$="-modal"] { z-index: 13456 ; position: absolute }' // to be click-draggable
92
- + ( chatgpt.isDarkMode() ? '.chatgpt-modal > div { border: 1px solid white }' : '' )
93
- + '.chatgpt-modal button {'
94
- + 'font-size: 0.77rem ; text-transform: uppercase ;' // shrink/uppercase labels
95
- + 'border-radius: 0 !important ;' // square borders
96
- + 'transition: transform 0.1s ease-in-out, box-shadow 0.1s ease-in-out ;' // smoothen hover fx
97
- + 'cursor: pointer !important ;' // add finger cursor
98
- + 'padding: 5px !important ; min-width: 102px }' // resize
99
- + '.chatgpt-modal button:hover {' // add zoom, re-scheme
100
- + 'transform: scale(1.055) ; color: black !important ;'
101
- + `background-color: #${ chatgpt.isDarkMode() ? '00cfff' : '9cdaff' } !important }`
102
- + ( !env.browser.isMobile ? '.modal-buttons { margin-left: -13px !important }' : '' )
103
- )
104
- }; // eslint-disable-line
88
+ await new Promise(resolve => setTimeout(resolve, 500)); // sleep .5s
105
89
 
106
- // Add STARS styles for modals
107
- ['black', 'white'].forEach(color => document.head.append(
90
+ // Add RISING PARTICLES styles for modals
91
+ ['gray', 'white'].forEach(color => document.head.append(
108
92
  dom.create.elem('link', { rel: 'stylesheet',
109
- href: `https://assets.aiwebextensions.com/styles/css/${color}-rising-stars.min.css?v=542104c`
93
+ href: `https://assets.aiwebextensions.com/styles/rising-particles/dist/${color}.min.css?v=727feff`
110
94
  })))
111
95
 
112
96
  if (config.extensionDisabled) return
113
97
 
114
98
  if (!config.skipAlert) // alert to extension load
115
- chatgpt.alert('≫ ChatGPT extension loaded! 🚀', // title
99
+ modals.alert('≫ ChatGPT extension loaded! 🚀', // title
116
100
  'Success! Press Ctrl+Shift+J to view all chatgpt.js methods.', // msg
117
101
  function getHelp() { // button
118
102
  chrome.tabs.create({ url: `${app.urls.gitHub}/issues` }) },
@@ -120,6 +104,16 @@
120
104
  settings.save('skipAlert', !config.skipAlert) }
121
105
  )
122
106
 
107
+ // Monitor SCHEME PREF CHANGES to update modal colors + env.ui.scheme for your use
108
+ new MutationObserver(handleSchemePrefChange).observe( // for site scheme pref changes
109
+ document.documentElement, { attributes: true, attributeFilter: ['class'] })
110
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener( // for browser/system scheme pref changes
111
+ 'change', () => requestAnimationFrame(handleSchemePrefChange))
112
+ function handleSchemePrefChange() {
113
+ const displayedScheme = getScheme()
114
+ if (env.ui.scheme != displayedScheme) { env.ui.scheme = displayedScheme ; modals.stylize() }
115
+ }
116
+
123
117
  // Your code here...
124
118
  // Your code here...
125
119
  // Your code here...