@kudoai/chatgpt.js 3.3.4 → 3.4.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 (41) hide show
  1. package/README.md +35 -22
  2. package/chatgpt.js +130 -79
  3. package/dist/chatgpt.min.js +3 -3
  4. package/docs/README.md +35 -22
  5. package/docs/SECURITY.md +6 -1
  6. package/docs/USERGUIDE.md +4 -4
  7. package/package.json +18 -11
  8. package/starters/chrome/docs/README.md +13 -9
  9. package/starters/chrome/extension/components/icons.js +32 -0
  10. package/starters/chrome/extension/components/modals.js +121 -0
  11. package/starters/chrome/extension/content.js +121 -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 +130 -79
  17. package/starters/chrome/extension/lib/dom.js +28 -0
  18. package/starters/chrome/extension/lib/settings.js +30 -0
  19. package/starters/chrome/extension/manifest.json +25 -22
  20. package/starters/chrome/extension/popup/controller.js +138 -0
  21. package/starters/chrome/extension/popup/index.html +7 -44
  22. package/starters/chrome/extension/popup/style.css +15 -6
  23. package/starters/chrome/extension/service-worker.js +38 -0
  24. package/starters/docs/README.md +9 -2
  25. package/starters/greasemonkey/chatgpt.js-greasemonkey-starter.user.js +12 -12
  26. package/starters/greasemonkey/docs/README.md +2 -0
  27. package/starters/chrome/extension/background.js +0 -14
  28. package/starters/chrome/extension/lib/settings-utils.js +0 -24
  29. package/starters/chrome/extension/popup/popup.js +0 -92
  30. package/starters/chrome/media/images/icons/refresh/icon16.png +0 -0
  31. package/starters/chrome/media/images/icons/refresh/icon50.png +0 -0
  32. /package/starters/chrome/{media/images → images}/icons/question-mark/icon16.png +0 -0
  33. /package/starters/chrome/{media/images → images}/icons/question-mark/icon512.png +0 -0
  34. /package/starters/chrome/{media/images → images}/screenshots/chatgpt-extension-in-list.png +0 -0
  35. /package/starters/chrome/{media/images → images}/screenshots/developer-mode-toggle.png +0 -0
  36. /package/starters/chrome/{media/images → images}/screenshots/developer-mode-toggle.psd +0 -0
  37. /package/starters/chrome/{media/images → images}/screenshots/extension-loaded.png +0 -0
  38. /package/starters/chrome/{media/images → images}/screenshots/load-unpacked-button.png +0 -0
  39. /package/starters/chrome/{media/images → images}/screenshots/reload-extension-button.png +0 -0
  40. /package/starters/chrome/{media/images → images}/screenshots/reload-page-button.png +0 -0
  41. /package/starters/chrome/{media/images → images}/screenshots/select-extension-folder.png +0 -0
@@ -1,3 +1,5 @@
1
+ <a id="top"></a>
2
+
1
3
  <div align="center">
2
4
  <h6>
3
5
  <picture>
@@ -26,7 +28,7 @@
26
28
 
27
29
  <br>
28
30
 
29
- <img src="../media/images/screenshots/extension-loaded.png">
31
+ <img src="../images/screenshots/extension-loaded.png">
30
32
 
31
33
  ## ⚡ Installation
32
34
 
@@ -37,36 +39,38 @@
37
39
  3. Visit `chrome://extensions` in Chrome (or any Chromium browser)
38
40
 
39
41
  4. Ensure **Develper mode** toggle is activated<br>
40
- ![](../media/images/screenshots/developer-mode-toggle.png)
42
+ ![](../images/screenshots/developer-mode-toggle.png)
41
43
 
42
44
  5. Click **Load unpacked**<br><br>
43
- <img src="../media/images/screenshots/load-unpacked-button.png">
45
+ <img src="../images/screenshots/load-unpacked-button.png">
44
46
  <br>
45
47
 
46
48
  6. In pop-up window, select the **extension** folder > click **Select Folder**<br><br><br>
47
- <img src="../media/images/screenshots/select-extension-folder.png"><br><br>
49
+ <img src="../images/screenshots/select-extension-folder.png"><br><br>
48
50
 
49
51
  That's it! **ChatGPT Extension** will now appear in extension list:
50
52
 
51
53
  <br>
52
54
 
53
- <img src="../media/images/screenshots/chatgpt-extension-in-list.png">
55
+ <img src="../images/screenshots/chatgpt-extension-in-list.png">
54
56
 
55
57
  <p><br>
56
58
 
57
- **💡 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:_
58
60
 
59
61
  <div align="center">
60
62
 
61
63
  <br>
62
64
 
63
- <img src="../media/images/screenshots/reload-extension-button.png">
64
- <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">
65
67
 
66
68
  <p><br>
67
69
 
68
70
  </div>
69
71
 
72
+ _For advanced Chrome API methods, see: https://developer.chrome.com/docs/extensions/reference/api_
73
+
70
74
  ## 🤖 Made with chatgpt.js
71
75
 
72
76
  These are some of the extensions featured by Google that use chatgpt.js:
@@ -90,4 +94,4 @@ These are some of the extensions featured by Google that use chatgpt.js:
90
94
 
91
95
  #
92
96
 
93
- <a href="https://github.com/KudoAI/chatgpt.js-chrome-starter/issues">Get Help</a> / <a href="#">Back to top ↑</a>
97
+ <a href="https://github.com/KudoAI/chatgpt.js-chrome-starter/issues">Get Help</a> / <a href="#top">Back to top ↑</a>
@@ -0,0 +1,32 @@
1
+ // Requires lib/dom.js
2
+
3
+ window.icons = {
4
+
5
+ dependencies: {
6
+ import(dependencies) { // { app }
7
+ for (const name in dependencies) this[name] = dependencies[name] }
8
+ },
9
+
10
+ create({ name, size = 16, width, height, ...additionalAttrs }) {
11
+ const iconData = icons[name],
12
+ iconAttrs = { width: width || size, height: height || size, ...additionalAttrs }
13
+ if (iconData.type == 'svg') {
14
+ const svg = dom.create.svgElem('svg', { viewBox: iconData.viewBox, ...iconAttrs })
15
+ iconData.elems.forEach(([tag, attrs]) => svg.append(dom.create.svgElem(tag, attrs)))
16
+ return svg
17
+ } else // img w/ src
18
+ return dom.create.elem('img', { src: iconData.src, ...iconAttrs })
19
+ },
20
+
21
+ plus: {
22
+ type: 'svg', viewBox: '0 0 1024 1024',
23
+ elems: [
24
+ [ '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' }]
25
+ ]
26
+ },
27
+
28
+ questionMark: {
29
+ type: 'png',
30
+ get src() { return `${icons.dependencies.app.urls.assetHost}@b5551ac/images/icons/question-mark/icon16.png` }
31
+ }
32
+ };
@@ -0,0 +1,121 @@
1
+ // Requires lib/chatgpt.js + lib/dom.js
2
+
3
+ window.modals = {
4
+ stack: [], // of types of undismissed modals
5
+
6
+ dependencies: {
7
+ import(dependencies) { // { app, siteAlert }
8
+ for (const name in dependencies) this[name] = dependencies[name] }
9
+ },
10
+
11
+ open(modalType) {
12
+ this.stack.unshift(modalType) // add to stack
13
+ 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
17
+ this.observeRemoval(modal, modalType) // to maintain stack for proper nav
18
+ },
19
+
20
+ observeRemoval(modal, modalType) { // to maintain stack for proper nav
21
+ const modalBG = modal.parentNode
22
+ new MutationObserver(([mutation], obs) => {
23
+ mutation.removedNodes.forEach(removedNode => { if (removedNode == modalBG) {
24
+ if (this.stack[0] == modalType) { // new modal not launched, implement nav back logic
25
+ this.stack.shift() // remove this modal type from stack
26
+ const prevModalType = this.stack[0]
27
+ if (prevModalType) { // open it
28
+ this.stack.shift() // remove type from stack since re-added on open
29
+ this.open(prevModalType)
30
+ }
31
+ }
32
+ obs.disconnect()
33
+ }})
34
+ }).observe(modalBG.parentNode, { childList: true, subtree: true })
35
+ },
36
+
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
+ about() {
67
+
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
+ // 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
94
+ )
95
+
96
+ // 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'
99
+
100
+ // Hack buttons
101
+ aboutModal.querySelector('.modal-buttons').style.justifyContent = 'center'
102
+ aboutModal.querySelectorAll('button').forEach(btn => {
103
+ btn.style.cssText = 'cursor: pointer !important ; height: 43px'
104
+
105
+ // Prepend emoji
106
+ if (/support/i.test(btn.textContent))
107
+ btn.textContent = '🧠 ' + btn.textContent
108
+ else if (/rate/i.test(btn.textContent))
109
+ btn.textContent = '⭐ ' + btn.textContent
110
+ else if (/extensions/i.test(btn.textContent))
111
+ btn.textContent = '🧠 ' + btn.textContent
112
+
113
+ // Hide Dismiss button
114
+ else btn.style.display = 'none'
115
+ })
116
+
117
+ return aboutModal
118
+ },
119
+
120
+ safeWinOpen(url) { open(url, '_blank', 'noopener') } // to prevent backdoor vulnerabilities
121
+ };
@@ -1,62 +1,136 @@
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
20
- }
21
- return true;
22
- });
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))
23
9
 
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
- );}});
10
+ // Init ENV context
11
+ const env = { browser: { isMobile: chatgpt.browser.isMobile() }}
35
12
 
36
- // Your code here...
37
- // Your code here...
38
- // Your code here...
39
- // Your code here...
40
- // Your code here...
41
- // Your code here...
13
+ // Import APP data
14
+ const { app } = await chrome.storage.sync.get('app')
15
+ modals.dependencies.import({ app, siteAlert })
16
+
17
+ // Add CHROME MSG listener
18
+ chrome.runtime.onMessage.addListener(req => { // from service-worker.js + popup/index.html
19
+ if (req.action == 'notify')
20
+ notify(...['msg', 'pos', 'notifDuration', 'shadow'].map(arg => req.options[arg]))
21
+ else if (req.action == 'alert')
22
+ siteAlert(...['title', 'msg', 'btns', 'checkbox', 'width'].map(arg => req.options[arg]))
23
+ else if (req.action == 'showAbout') chatgpt.isLoaded().then(() => { modals.open('about') })
24
+ else if (req.action == 'syncConfigToUI') syncConfigToUI(req.options)
25
+ })
26
+
27
+ // Init SETTINGS
28
+ await settings.load(Object.keys(settings.controls), 'skipAlert')
42
29
 
43
30
  // Define FEEDBACK functions
44
31
 
45
- function notify(msg, position = '', notifDuration = '', shadow = '') {
46
- chatgpt.notify(`${ config.appSymbol } ${ msg }`, position, notifDuration,
47
- shadow || chatgpt.isDarkMode() ? '' : 'shadow' ); }
32
+ function notify(msg, pos = '', notifDuration = '', shadow = '') {
48
33
 
49
- function alert(title = '', msg = '', btns = '', checkbox = '', width = '') {
50
- return chatgpt.alert(`${ config.appSymbol } ${ title }`, msg, btns, checkbox, width );}
34
+ // Strip state word to append colored one later
35
+ const foundState = ['ON', 'OFF'].find(word => msg.includes(word))
36
+ if (foundState) msg = msg.replace(foundState, '')
37
+
38
+ // Show notification
39
+ siteAlert(`${app.symbol} ${msg}`, pos, notifDuration,
40
+ shadow || chatgpt.isDarkMode() ? '' : 'shadow' )
41
+ const notif = document.querySelector('.chatgpt-notif:last-child')
42
+
43
+ // Append styled state word
44
+ if (foundState) {
45
+ 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' }`
49
+ styledStateSpan.append(foundState) ; notif.append(styledStateSpan)
50
+ }
51
+ }
52
+
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
+ }
51
57
 
52
58
  // Define SYNC function
53
59
 
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
- }});};
60
+ async function syncConfigToUI(options) { // eslint-disable-line
61
+ await settings.load('extensionDisabled', Object.keys(settings.controls)) // load from Chrome storage to content.js config
62
+ if (config.extensionDisabled) {
63
+ // Remove all hacks
64
+ } else {
65
+ // Add/remove hacks to reflect each potentially updated setting per settings.controls in lib/settings.mjs
66
+ // e.g. if you created toolbar popup toggle to hide ChatGPT footer using hiddenFooter key...
67
+ // ...here you would use options?.updatedKey == 'hiddenFooter' && config.hiddenFooter...
68
+ // ...to conditionally append/remove hidden footer style...
69
+ // ...(initial style creation + append if config.hiddenFooter would go in main routine)
70
+ }
71
+ }
72
+
73
+ // Run MAIN routine
74
+
75
+ chatgpt.printAllFunctions() // to console
76
+
77
+ // CHILL a bit if your hacks depend on delayed DOM content
78
+ 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
105
+
106
+ // Add STARS styles for modals
107
+ ['black', 'white'].forEach(color => document.head.append(
108
+ dom.create.elem('link', { rel: 'stylesheet',
109
+ href: `https://assets.aiwebextensions.com/styles/css/${color}-rising-stars.min.css?v=542104c`
110
+ })))
111
+
112
+ if (config.extensionDisabled) return
113
+
114
+ if (!config.skipAlert) // alert to extension load
115
+ chatgpt.alert('≫ ChatGPT extension loaded! 🚀', // title
116
+ 'Success! Press Ctrl+Shift+J to view all chatgpt.js methods.', // msg
117
+ function getHelp() { // button
118
+ chrome.tabs.create({ url: `${app.urls.gitHub}/issues` }) },
119
+ function dontShowAgain() { // checkbox
120
+ settings.save('skipAlert', !config.skipAlert) }
121
+ )
122
+
123
+ // Your code here...
124
+ // Your code here...
125
+ // Your code here...
126
+ // Your code here...
127
+ // Your code here...
128
+ // Your code here...
129
+ // Your code here...
130
+ // Your code here...
131
+ // Your code here...
132
+ // Your code here...
133
+ // Your code here...
134
+ // Your code here...
61
135
 
62
- })();
136
+ })()