@kudoai/chatgpt.js 3.3.5 → 3.5.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 (40) hide show
  1. package/README.md +29 -21
  2. package/chatgpt.js +149 -94
  3. package/dist/chatgpt.min.js +3 -3
  4. package/docs/README.md +29 -21
  5. package/docs/SECURITY.md +1 -3
  6. package/docs/USERGUIDE.md +3 -3
  7. package/package.json +18 -11
  8. package/starters/chrome/docs/README.md +10 -8
  9. package/starters/chrome/extension/components/icons.js +31 -0
  10. package/starters/chrome/extension/components/modals.js +148 -0
  11. package/starters/chrome/extension/content.js +108 -47
  12. package/starters/chrome/extension/icons/faded/icon128.png +0 -0
  13. package/starters/chrome/extension/icons/faded/icon16.png +0 -0
  14. package/starters/chrome/extension/icons/faded/icon32.png +0 -0
  15. package/starters/chrome/extension/icons/faded/icon64.png +0 -0
  16. package/starters/chrome/extension/lib/chatgpt.js +149 -94
  17. package/starters/chrome/extension/lib/dom.js +35 -0
  18. package/starters/chrome/extension/lib/settings.js +30 -0
  19. package/starters/chrome/extension/manifest.json +22 -22
  20. package/starters/chrome/extension/popup/controller.js +140 -0
  21. package/starters/chrome/extension/popup/index.html +7 -44
  22. package/starters/chrome/extension/popup/style.css +33 -10
  23. package/starters/chrome/extension/service-worker.js +41 -0
  24. package/starters/greasemonkey/chatgpt.js-greasemonkey-starter.user.js +12 -12
  25. package/starters/greasemonkey/docs/README.md +2 -0
  26. package/starters/chrome/extension/background.js +0 -14
  27. package/starters/chrome/extension/lib/settings-utils.js +0 -24
  28. package/starters/chrome/extension/popup/popup.js +0 -92
  29. package/starters/chrome/media/images/icons/refresh/icon16.png +0 -0
  30. package/starters/chrome/media/images/icons/refresh/icon50.png +0 -0
  31. /package/starters/chrome/{media/images → images}/icons/question-mark/icon16.png +0 -0
  32. /package/starters/chrome/{media/images → images}/icons/question-mark/icon512.png +0 -0
  33. /package/starters/chrome/{media/images → images}/screenshots/chatgpt-extension-in-list.png +0 -0
  34. /package/starters/chrome/{media/images → images}/screenshots/developer-mode-toggle.png +0 -0
  35. /package/starters/chrome/{media/images → images}/screenshots/developer-mode-toggle.psd +0 -0
  36. /package/starters/chrome/{media/images → images}/screenshots/extension-loaded.png +0 -0
  37. /package/starters/chrome/{media/images → images}/screenshots/load-unpacked-button.png +0 -0
  38. /package/starters/chrome/{media/images → images}/screenshots/reload-extension-button.png +0 -0
  39. /package/starters/chrome/{media/images → images}/screenshots/reload-page-button.png +0 -0
  40. /package/starters/chrome/{media/images → images}/screenshots/select-extension-folder.png +0 -0
@@ -28,7 +28,7 @@
28
28
 
29
29
  <br>
30
30
 
31
- <img src="../media/images/screenshots/extension-loaded.png">
31
+ <img src="../images/screenshots/extension-loaded.png">
32
32
 
33
33
  ## ⚡ Installation
34
34
 
@@ -39,36 +39,38 @@
39
39
  3. Visit `chrome://extensions` in Chrome (or any Chromium browser)
40
40
 
41
41
  4. Ensure **Develper mode** toggle is activated<br>
42
- ![](../media/images/screenshots/developer-mode-toggle.png)
42
+ ![](../images/screenshots/developer-mode-toggle.png)
43
43
 
44
44
  5. Click **Load unpacked**<br><br>
45
- <img src="../media/images/screenshots/load-unpacked-button.png">
45
+ <img src="../images/screenshots/load-unpacked-button.png">
46
46
  <br>
47
47
 
48
48
  6. In pop-up window, select the **extension** folder > click **Select Folder**<br><br><br>
49
- <img src="../media/images/screenshots/select-extension-folder.png"><br><br>
49
+ <img src="../images/screenshots/select-extension-folder.png"><br><br>
50
50
 
51
51
  That's it! **ChatGPT Extension** will now appear in extension list:
52
52
 
53
53
  <br>
54
54
 
55
- <img src="../media/images/screenshots/chatgpt-extension-in-list.png">
55
+ <img src="../images/screenshots/chatgpt-extension-in-list.png">
56
56
 
57
57
  <p><br>
58
58
 
59
- **💡 TIP:** _To reflect changes from source code, click **Reload** on extension tile + reload any Chrome tabs extension scripts are running on:_
59
+ **💡 TIP:** _To reflect changes from source code, click **Reload** on extension tile + reload any Chrome tabs content scripts are running on:_
60
60
 
61
61
  <div align="center">
62
62
 
63
63
  <br>
64
64
 
65
- <img src="../media/images/screenshots/reload-extension-button.png">
66
- <img src="../media/images/screenshots/reload-page-button.png">
65
+ <img src="../images/screenshots/reload-extension-button.png">
66
+ <img src="../images/screenshots/reload-page-button.png">
67
67
 
68
68
  <p><br>
69
69
 
70
70
  </div>
71
71
 
72
+ _For advanced Chrome API methods, see: https://developer.chrome.com/docs/extensions/reference/api_
73
+
72
74
  ## 🤖 Made with chatgpt.js
73
75
 
74
76
  These are some of the extensions featured by Google that use chatgpt.js:
@@ -0,0 +1,31 @@
1
+ // Requires lib/dom.js
2
+
3
+ window.icons = {
4
+ imports: {
5
+ import(deps) { // { app }
6
+ for (const depName in deps) this[depName] = deps[depName] }
7
+ },
8
+
9
+ create({ name, size = 16, width, height, ...additionalAttrs }) {
10
+ const iconData = icons[name],
11
+ iconAttrs = { width: width || size, height: height || size, ...additionalAttrs }
12
+ if (iconData.type == 'svg') {
13
+ const svg = dom.create.svgElem('svg', { viewBox: iconData.viewBox, ...iconAttrs })
14
+ iconData.elems.forEach(([tag, attrs]) => svg.append(dom.create.svgElem(tag, attrs)))
15
+ return svg
16
+ } else // img w/ src
17
+ return dom.create.elem('img', { src: iconData.src, ...iconAttrs })
18
+ },
19
+
20
+ plus: {
21
+ type: 'svg', viewBox: '0 0 1024 1024',
22
+ elems: [
23
+ [ 'path', { d: 'M899.901 600.38H600.728v299.173c0 74.383-179.503 74.383-179.503 0V600.38H122.051c-74.384 0-74.384-179.503 0-179.503h299.173V121.703c0-74.384 179.503-74.384 179.503 0v299.174H899.9c74.385 0 74.385 179.503.001 179.503z' }]
24
+ ]
25
+ },
26
+
27
+ questionMark: {
28
+ type: 'png',
29
+ get src() { return `${icons.imports.app.urls.assetHost}@b5551ac/images/icons/question-mark/icon16.png` }
30
+ }
31
+ };
@@ -0,0 +1,148 @@
1
+ // Requires lib/chatgpt.js + lib/dom.js
2
+
3
+ window.modals = {
4
+ stack: [], // of types of undismissed modals
5
+ get class() { return `${this.imports.app.cssPrefix}-modal` },
6
+
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 + starry bg
16
+ return alert
17
+ },
18
+
19
+ open(modalType) {
20
+ const modal = this[modalType]() // show modal
21
+ this.stack.unshift(modalType) // add to stack
22
+ this.init(alert) // add classes + starry bg
23
+ this.observeRemoval(modal, modalType) // to maintain stack for proper nav
24
+ },
25
+
26
+ init(modal) {
27
+ if (!this.styles) this.stylize() // to init/append stylesheet
28
+ modal.classList.add(this.class) ; modal.parentNode.classList.add(`${this.class}-bg`) // add classes
29
+ dom.fillStarryBG(modal) // add starry bg
30
+ },
31
+
32
+ stylize() {
33
+ if (!this.styles) {
34
+ this.styles = dom.create.elem('style') ; this.styles.id = `${this.class}-styles`
35
+ document.head.append(this.styles)
36
+ }
37
+ this.styles.innerText = (
38
+ `.${this.class} {` // modals
39
+ + 'font-family: -apple-system, system-ui, BlinkMacSystemFont, Segoe UI, Roboto,'
40
+ + 'Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, sans-serif ;'
41
+ + 'padding: 20px 25px 24px 25px !important ; font-size: 20px ;'
42
+ + `color: ${ this.imports.env.ui.scheme == 'dark' ? 'white' : 'black' } !important ;`
43
+ + `background-image: linear-gradient(180deg, ${
44
+ this.imports.env.ui.scheme == 'dark' ? '#99a8a6 -200px, black 200px'
45
+ : '#b6ebff -296px, white 171px' }) }`
46
+ + `.${this.class} [class*=modal-close-btn] {`
47
+ + 'position: absolute !important ; float: right ; top: 14px !important ; right: 16px !important ;'
48
+ + 'cursor: pointer ; width: 33px ; height: 33px ; border-radius: 20px }'
49
+ + `.${this.class} [class*=modal-close-btn] svg { height: 10px }`
50
+ + `.${this.class} [class*=modal-close-btn] path {`
51
+ + `${ this.imports.env.ui.scheme == 'dark' ? 'stroke: white ; fill: white'
52
+ : 'stroke: #9f9f9f ; fill: #9f9f9f' }}`
53
+ + ( this.imports.env.ui.scheme == 'dark' ? // invert dark mode hover paths
54
+ `.${this.class} [class*=modal-close-btn]:hover path { stroke: black ; fill: black }` : '' )
55
+ + `.${this.class} [class*=modal-close-btn]:hover { background-color: #f2f2f2 }` // hover underlay
56
+ + `.${this.class} [class*=modal-close-btn] svg { margin: 11.5px }` // center SVG for hover underlay
57
+ + `.${this.class} a {`
58
+ + `color: #${ this.imports.env.ui.scheme == 'dark' ? '00cfff' : '1e9ebb' } !important }`
59
+ + `.${this.class} h2 { font-weight: bold }`
60
+ + `.${this.class} button {`
61
+ + 'font-size: 14px ; text-transform: uppercase ;' // shrink/uppercase labels
62
+ + 'border-radius: 0 !important ;' // square borders
63
+ + 'transition: transform 0.1s ease-in-out, box-shadow 0.1s ease-in-out ;' // smoothen hover fx
64
+ + 'cursor: pointer !important ;' // add finger cursor
65
+ + `border: 1px solid ${ this.imports.env.ui.scheme == 'dark' ? 'white' : 'black' } !important ;`
66
+ + 'padding: 8px !important ; min-width: 102px }' // resize
67
+ + `.${this.class} button:hover {` // add zoom, re-scheme
68
+ + 'transform: scale(1.055) ; color: black !important ;'
69
+ + `background-color: #${ this.imports.env.ui.scheme == 'dark' ? '00cfff' : '9cdaff' } !important }`
70
+ + ( !this.imports.env.browser.isMobile ?
71
+ `.${this.class} .modal-buttons { margin-left: -13px !important }` : '' )
72
+ + `.about-em { color: ${ this.imports.env.ui.scheme == 'dark' ? 'white' : 'green' } !important }`
73
+ )
74
+ },
75
+
76
+ observeRemoval(modal, modalType) { // to maintain stack for proper nav
77
+ const modalBG = modal.parentNode
78
+ new MutationObserver(([mutation], obs) => {
79
+ mutation.removedNodes.forEach(removedNode => { if (removedNode == modalBG) {
80
+ if (this.stack[0] == modalType) { // new modal not launched, implement nav back logic
81
+ this.stack.shift() // remove this modal type from stack 1st
82
+ const prevModalType = this.stack[0]
83
+ if (prevModalType) { // open it
84
+ this.stack.shift() // remove type from stack since re-added on open
85
+ this.open(prevModalType)
86
+ }
87
+ }
88
+ obs.disconnect()
89
+ }})
90
+ }).observe(modalBG.parentNode, { childList: true, subtree: true })
91
+ },
92
+
93
+ about() {
94
+
95
+ // Show modal
96
+ const aboutModal = this.alert(
97
+ `${this.imports.app.symbol} ${chrome.runtime.getManifest().name}`, // title
98
+ '🧠 Author: ' // msg
99
+ + `<a href="${this.imports.app.author.url}">${this.imports.app.author.name}</a> `
100
+ + `& <a href="${this.imports.app.urls.contributors}">contributors</a>\n`
101
+ + `🏷️ Version: <span class="about-em">${this.imports.app.version}</span>\n`
102
+ + '📜 Open source code: '
103
+ + `<a href="${this.imports.app.urls.gitHub}" target="_blank" rel="nopener">`
104
+ + this.imports.app.urls.gitHub + '</a>\n'
105
+ + '⚡ Powered by: '
106
+ + `<a href="${this.imports.app.urls.chatgptJS}" target="_blank" rel="noopener">chatgpt.js</a>`,
107
+ [ function getSupport(){}, function rateUs(){}, function moreAiExtensions(){} ], // button labels
108
+ '', 656 // modal width
109
+ )
110
+
111
+ // Format text
112
+ aboutModal.querySelector('h2').style.cssText = (
113
+ 'text-align: center ; font-size: 51px ; line-height: 46px ; padding: 15px 0' )
114
+ aboutModal.querySelector('p').style.cssText = (
115
+ 'text-align: center ; overflow-wrap: anywhere ;'
116
+ + `margin: ${ this.imports.env.browser.isPortrait ? '6px 0 -16px' : '3px 0 0' }` )
117
+
118
+ // Hack buttons
119
+ aboutModal.querySelector('.modal-buttons').style.justifyContent = 'center'
120
+ aboutModal.querySelectorAll('button').forEach(btn => {
121
+ btn.style.cssText = 'height: 55px ; min-width: 136px ; text-align: center'
122
+
123
+ // Replace buttons w/ clones that don't dismiss modal
124
+ const btnClone = btn.cloneNode(true)
125
+ btn.parentNode.replaceChild(btnClone, btn) ; btn = btnClone
126
+ btn.onclick = () => this.safeWinOpen(
127
+ btn.textContent == 'Get Support' ? `${modals.imports.app.urls.gitHub}/issues`
128
+ : btn.textContent == 'Rate Us' ? `${modals.imports.app.urls.gitHub}/discussions`
129
+ : modals.imports.app.urls.relatedExtensions
130
+ )
131
+
132
+ // Prepend emoji
133
+ if (/support/i.test(btn.textContent))
134
+ btn.textContent = '🧠 ' + btn.textContent
135
+ else if (/rate/i.test(btn.textContent))
136
+ btn.textContent = '⭐ ' + btn.textContent
137
+ else if (/extensions/i.test(btn.textContent))
138
+ btn.textContent = '🧠 ' + btn.textContent
139
+
140
+ // Hide Dismiss button
141
+ else btn.style.display = 'none'
142
+ })
143
+
144
+ return aboutModal
145
+ },
146
+
147
+ safeWinOpen(url) { open(url, '_blank', 'noopener') } // to prevent backdoor vulnerabilities
148
+ };
@@ -1,37 +1,111 @@
1
1
  // NOTE: This script relies on the powerful chatgpt.js library @ https://chatgpt.js.org
2
2
  // © 2023–2024 KudoAI & contributors under the MIT license
3
- // Source: https://github.com/KudoAI/chatgpt.js
4
- // Latest minified release: https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js/chatgpt.min.js
5
3
 
6
4
  (async () => {
7
5
 
8
- // Import libs
9
- const { config, settings } = await import(chrome.runtime.getURL('lib/settings-utils.js'));
10
- await import(chrome.runtime.getURL('lib/chatgpt.js'));
11
-
12
- // Add Chrome action msg listener
13
- chrome.runtime.onMessage.addListener((request) => {
14
- if (request.action === 'notify') notify(request.msg, request.position);
15
- else if (request.action === 'alert') alert(request.title, request.msg, request.btns);
16
- else if (typeof window[request.action] === 'function') {
17
- const args = Array.isArray(request.args) ? request.args // preserve array if supplied
18
- : request.args !== undefined ? [request.args] : []; // convert to array if single or no arg
19
- window[request.action](...args); // call expression functions
6
+ // Import JS resources
7
+ for (const resource of ['components/modals.js', 'lib/chatgpt.js', 'lib/dom.js', 'lib/settings.js'])
8
+ await import(chrome.runtime.getURL(resource))
9
+
10
+ // Init ENV context
11
+ const env = { browser: { isMobile: chatgpt.browser.isMobile() }, ui: { scheme: getScheme() }}
12
+ env.browser.isPortrait = env.browser.isMobile && (window.innerWidth < window.innerHeight)
13
+
14
+ // Import APP data
15
+ const { app } = await chrome.storage.sync.get('app')
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
20
+
21
+ // Add CHROME MSG listener
22
+ chrome.runtime.onMessage.addListener(req => { // from service-worker.js + popup/index.html
23
+ if (req.action == 'notify')
24
+ notify(...['msg', 'pos', 'notifDuration', 'shadow'].map(arg => req.options[arg]))
25
+ else if (req.action == 'alert')
26
+ modals.alert(...['title', 'msg', 'btns', 'checkbox', 'width'].map(arg => req.options[arg]))
27
+ else if (req.action == 'showAbout') chatgpt.isLoaded().then(() => { modals.open('about') })
28
+ else if (req.action == 'syncConfigToUI') syncConfigToUI(req.options)
29
+ })
30
+
31
+ // Init SETTINGS
32
+ await settings.load(Object.keys(settings.controls), 'skipAlert')
33
+
34
+ // Define FUNCTIONS
35
+
36
+ function notify(msg, pos = '', notifDuration = '', shadow = '') {
37
+
38
+ // Strip state word to append colored one later
39
+ const foundState = ['ON', 'OFF'].find(word => msg.includes(word))
40
+ if (foundState) msg = msg.replace(foundState, '')
41
+
42
+ // Show notification
43
+ chatgpt.notify(`${app.symbol} ${msg}`, pos, notifDuration, shadow || env.ui.scheme == 'dark' ? '' : 'shadow')
44
+ const notif = document.querySelector('.chatgpt-notif:last-child')
45
+
46
+ // Append styled state word
47
+ if (foundState) {
48
+ const styledStateSpan = dom.create.elem('span')
49
+ styledStateSpan.style.cssText = `color: ${
50
+ foundState == 'OFF' ? '#ef4848 ; text-shadow: rgba(255, 169, 225, 0.44) 2px 1px 5px'
51
+ : '#5cef48 ; text-shadow: rgba(255, 250, 169, 0.38) 2px 1px 5px' }`
52
+ styledStateSpan.append(foundState) ; notif.append(styledStateSpan)
53
+ }
54
+ }
55
+
56
+ async function syncConfigToUI(options) { // eslint-disable-line
57
+ await settings.load('extensionDisabled', Object.keys(settings.controls)) // load from Chrome storage to content.js config
58
+ if (config.extensionDisabled) {
59
+ // Remove all hacks
60
+ } else {
61
+ // Add/remove hacks to reflect each potentially updated setting per settings.controls in lib/settings.mjs
62
+ // e.g. if you created toolbar popup toggle to hide ChatGPT footer using hiddenFooter key...
63
+ // ...here you would use options?.updatedKey == 'hiddenFooter' && config.hiddenFooter...
64
+ // ...to conditionally append/remove hidden footer style...
65
+ // ...(initial style creation + append if config.hiddenFooter would go in main routine)
20
66
  }
21
- return true;
22
- });
67
+ }
68
+
69
+ function getScheme() {
70
+ return document.documentElement.className
71
+ || (window.matchMedia?.('(prefers-color-scheme: dark)')?.matches ? 'dark' : 'light')
72
+ }
73
+
74
+ // Run MAIN routine
75
+
76
+ chatgpt.printAllFunctions() // to console
23
77
 
24
- await chatgpt.isLoaded();
25
- chatgpt.printAllFunctions(); // to console
26
- settings.load('skipAlert').then(() => {
27
- if (!config.skipAlert) {
28
- chatgpt.alert('≫ ChatGPT extension loaded! 🚀', // title
29
- 'Success! Press Ctrl+Shift+J to view all chatgpt.js methods.', // msg
30
- function getHelp() { // button
31
- window.open(config.ghRepoURL + '/issues', '_blank', 'noopener'); },
32
- function dontShowAgain() { // checkbox
33
- settings.save('skipAlert', !config.skipAlert); }
34
- );}});
78
+ // CHILL a bit if your hacks depend on delayed DOM content
79
+ await chatgpt.isLoaded()
80
+ await new Promise(resolve => setTimeout(resolve, 500)); // sleep .5s
81
+
82
+ // Add STARS styles for modals
83
+ ['black', 'white'].forEach(color => document.head.append(
84
+ dom.create.elem('link', { rel: 'stylesheet',
85
+ href: `https://assets.aiwebextensions.com/styles/rising-stars/dist/${
86
+ color}.min.css?v=0cde30f9ae3ce99ae998141f6e7a36de9b0cc2e7`
87
+ })))
88
+
89
+ if (config.extensionDisabled) return
90
+
91
+ if (!config.skipAlert) // alert to extension load
92
+ modals.alert('≫ ChatGPT extension loaded! 🚀', // title
93
+ 'Success! Press Ctrl+Shift+J to view all chatgpt.js methods.', // msg
94
+ function getHelp() { // button
95
+ chrome.tabs.create({ url: `${app.urls.gitHub}/issues` }) },
96
+ function dontShowAgain() { // checkbox
97
+ settings.save('skipAlert', !config.skipAlert) }
98
+ )
99
+
100
+ // Monitor SCHEME PREF CHANGES to update modal colors + env.ui.scheme for your use
101
+ new MutationObserver(handleSchemePrefChange).observe( // for site scheme pref changes
102
+ document.documentElement, { attributes: true, attributeFilter: ['class'] })
103
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener( // for browser/system scheme pref changes
104
+ 'change', () => requestAnimationFrame(handleSchemePrefChange))
105
+ function handleSchemePrefChange() {
106
+ const displayedScheme = getScheme()
107
+ if (env.ui.scheme != displayedScheme) { env.ui.scheme = displayedScheme ; modals.stylize() }
108
+ }
35
109
 
36
110
  // Your code here...
37
111
  // Your code here...
@@ -39,24 +113,11 @@
39
113
  // Your code here...
40
114
  // Your code here...
41
115
  // Your code here...
116
+ // Your code here...
117
+ // Your code here...
118
+ // Your code here...
119
+ // Your code here...
120
+ // Your code here...
121
+ // Your code here...
42
122
 
43
- // Define FEEDBACK functions
44
-
45
- function notify(msg, position = '', notifDuration = '', shadow = '') {
46
- chatgpt.notify(`${ config.appSymbol } ${ msg }`, position, notifDuration,
47
- shadow || chatgpt.isDarkMode() ? '' : 'shadow' ); }
48
-
49
- function alert(title = '', msg = '', btns = '', checkbox = '', width = '') {
50
- return chatgpt.alert(`${ config.appSymbol } ${ title }`, msg, btns, checkbox, width );}
51
-
52
- // Define SYNC function
53
-
54
- syncExtension = () => {
55
- settings.load('extensionDisabled').then(() => {
56
- if (config.extensionDisabled) {
57
- // remove your hacks
58
- } else {
59
- // sync each potentially updated setting passed to settings.load()
60
- }});};
61
-
62
- })();
123
+ })()