@kudoai/chatgpt.js 3.7.0 → 3.8.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.
- package/README.md +132 -107
- package/chatgpt.js +41 -30
- package/dist/chatgpt.min.js +6 -6
- package/docs/README.md +131 -107
- package/docs/USERGUIDE.md +5 -5
- package/package.json +14 -13
- package/starters/chrome/extension/components/icons.js +8 -2
- package/starters/chrome/extension/components/modals.js +26 -33
- package/starters/chrome/extension/content.js +9 -10
- package/starters/chrome/extension/lib/chatgpt.js +41 -30
- package/starters/chrome/extension/lib/dom.js +4 -3
- package/starters/chrome/extension/lib/settings.js +31 -5
- package/starters/chrome/extension/manifest.json +3 -11
- package/starters/chrome/extension/popup/controller.js +96 -54
- package/starters/chrome/extension/popup/index.html +9 -3
- package/starters/chrome/extension/popup/style.css +17 -9
- package/starters/chrome/extension/service-worker.js +4 -4
- package/starters/greasemonkey/chatgpt.js-greasemonkey-starter.user.js +2 -2
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
// Requires lib/chatgpt.js + lib/dom.js + app + env
|
|
2
2
|
|
|
3
3
|
window.modals = {
|
|
4
|
-
import(deps) { Object.assign(this.imports
|
|
4
|
+
import(deps) { Object.assign(this.imports ||= {}, deps) },
|
|
5
5
|
|
|
6
6
|
stack: [], // of types of undismissed modals
|
|
7
7
|
get class() { return `${this.imports.app.cssPrefix}-modal` },
|
|
8
8
|
|
|
9
9
|
about() {
|
|
10
|
+
const { app, env: { ui: { scheme }, browser: { isPortrait }}} = this.imports
|
|
10
11
|
|
|
11
12
|
// Show modal
|
|
12
13
|
const labelStyles = 'text-transform: uppercase ; font-size: 17px ; font-weight: bold ;'
|
|
13
|
-
+ `color: ${
|
|
14
|
+
+ `color: ${ scheme == 'dark' ? 'white' : '#494141' }`
|
|
14
15
|
const aboutModal = this.alert(
|
|
15
|
-
`${
|
|
16
|
+
`${app.symbol} ${chrome.runtime.getManifest().name}`, // title
|
|
16
17
|
`<span style="${labelStyles}">🧠 Author:</span> `
|
|
17
|
-
+ `<a href="${
|
|
18
|
-
+ `& <a href="${
|
|
18
|
+
+ `<a href="${app.author.url}">${this.imports.app.author.name}</a> `
|
|
19
|
+
+ `& <a href="${app.urls.contributors}">contributors</a>\n`
|
|
19
20
|
+ `<span style="${labelStyles}">🏷️ Version:</span> `
|
|
20
|
-
+ `<span class="about-em">${
|
|
21
|
+
+ `<span class="about-em">${app.version}</span>\n`
|
|
21
22
|
+ `<span style="${labelStyles}">📜 Open source code:</span> `
|
|
22
|
-
+ `<a href="${
|
|
23
|
-
+
|
|
23
|
+
+ `<a href="${app.urls.gitHub}" target="_blank" rel="nopener">`
|
|
24
|
+
+ app.urls.gitHub + '</a>\n'
|
|
24
25
|
+ `<span style="${labelStyles}">⚡ Powered by:</span> `
|
|
25
|
-
+ `<a href="${
|
|
26
|
+
+ `<a href="${app.urls.chatgptJS}" target="_blank" rel="noopener">chatgpt.js</a>`,
|
|
26
27
|
[ function getSupport(){}, function rateUs(){}, function moreAiExtensions(){} ], // button labels
|
|
27
28
|
'', 656 // modal width
|
|
28
29
|
)
|
|
@@ -32,7 +33,7 @@ window.modals = {
|
|
|
32
33
|
'text-align: center ; font-size: 51px ; line-height: 46px ; padding: 15px 0' )
|
|
33
34
|
aboutModal.querySelector('p').style.cssText = (
|
|
34
35
|
'text-align: center ; overflow-wrap: anywhere ;'
|
|
35
|
-
+ `margin: ${
|
|
36
|
+
+ `margin: ${ isPortrait ? '6px 0 -16px' : '3px 0 0' }` )
|
|
36
37
|
|
|
37
38
|
// Hack buttons
|
|
38
39
|
aboutModal.querySelector('.modal-buttons').style.justifyContent = 'center'
|
|
@@ -40,8 +41,7 @@ window.modals = {
|
|
|
40
41
|
btn.style.cssText = 'height: 55px ; min-width: 136px ; text-align: center'
|
|
41
42
|
|
|
42
43
|
// Replace buttons w/ clones that don't dismiss modal
|
|
43
|
-
|
|
44
|
-
btn.parentNode.replaceChild(btnClone, btn) ; btn = btnClone
|
|
44
|
+
btn.replaceWith(btn = btn.cloneNode(true))
|
|
45
45
|
btn.onclick = () => this.safeWinOpen(
|
|
46
46
|
btn.textContent == 'Get Support' ? `${modals.imports.app.urls.gitHub}/issues`
|
|
47
47
|
: btn.textContent == 'Rate Us' ? `${modals.imports.app.urls.gitHub}/discussions`
|
|
@@ -73,7 +73,7 @@ window.modals = {
|
|
|
73
73
|
init(modal) {
|
|
74
74
|
if (!modal) return // to support non-div this.open()s
|
|
75
75
|
if (!this.styles) this.stylize() // to init/append stylesheet
|
|
76
|
-
modal.classList.add(
|
|
76
|
+
modal.classList.add(this.class) ; modal.parentNode.classList.add(`${this.class}-bg`)
|
|
77
77
|
dom.addRisingParticles(modal)
|
|
78
78
|
},
|
|
79
79
|
|
|
@@ -104,34 +104,28 @@ window.modals = {
|
|
|
104
104
|
safeWinOpen(url) { open(url, '_blank', 'noopener') }, // to prevent backdoor vulnerabilities
|
|
105
105
|
|
|
106
106
|
stylize() {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
document.head.append(this.styles)
|
|
110
|
-
}
|
|
107
|
+
const { env: { ui: { scheme }, browser: { isMobile }}} = this.imports
|
|
108
|
+
if (!this.styles) document.head.append(this.styles = dom.create.elem('style'))
|
|
111
109
|
this.styles.innerText = (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
+ `.${this.class} {` // modals
|
|
110
|
+
`.${this.class} {` // modals
|
|
111
|
+
+ 'user-select: none ; -webkit-user-select: none ; -moz-user-select: none ; -ms-user-select: none ;'
|
|
115
112
|
+ 'font-family: -apple-system, system-ui, BlinkMacSystemFont, Segoe UI, Roboto,'
|
|
116
113
|
+ 'Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, sans-serif ;'
|
|
117
114
|
+ 'padding: 20px 25px 24px 25px !important ; font-size: 20px ;'
|
|
118
|
-
+ `color: ${
|
|
115
|
+
+ `color: ${ scheme == 'dark' ? 'white' : 'black' } !important ;`
|
|
119
116
|
+ `background-image: linear-gradient(180deg, ${
|
|
120
|
-
|
|
121
|
-
: '#b6ebff -296px, white 171px' }) }`
|
|
117
|
+
scheme == 'dark' ? '#99a8a6 -200px, black 200px' : '#b6ebff -296px, white 171px' }) }`
|
|
122
118
|
+ `.${this.class} [class*=modal-close-btn] {`
|
|
123
119
|
+ 'position: absolute !important ; float: right ; top: 14px !important ; right: 16px !important ;'
|
|
124
120
|
+ 'cursor: pointer ; width: 33px ; height: 33px ; border-radius: 20px }'
|
|
125
121
|
+ `.${this.class} [class*=modal-close-btn] svg { height: 10px }`
|
|
126
122
|
+ `.${this.class} [class*=modal-close-btn] path {`
|
|
127
|
-
+ `${
|
|
128
|
-
|
|
129
|
-
+ ( this.imports.env.ui.scheme == 'dark' ? // invert dark mode hover paths
|
|
123
|
+
+ `${ scheme == 'dark' ? 'stroke: white ; fill: white' : 'stroke: #9f9f9f ; fill: #9f9f9f' }}`
|
|
124
|
+
+ ( scheme == 'dark' ? // invert dark mode hover paths
|
|
130
125
|
`.${this.class} [class*=modal-close-btn]:hover path { stroke: black ; fill: black }` : '' )
|
|
131
126
|
+ `.${this.class} [class*=modal-close-btn]:hover { background-color: #f2f2f2 }` // hover underlay
|
|
132
127
|
+ `.${this.class} [class*=modal-close-btn] svg { margin: 11.5px }` // center SVG for hover underlay
|
|
133
|
-
+ `.${this.class} a {`
|
|
134
|
-
+ `color: #${ this.imports.env.ui.scheme == 'dark' ? '00cfff' : '1e9ebb' } !important }`
|
|
128
|
+
+ `.${this.class} a { color: #${ scheme == 'dark' ? '00cfff' : '1e9ebb' } !important }`
|
|
135
129
|
+ `.${this.class} h2 { font-weight: bold }`
|
|
136
130
|
+ `.${this.class} button {`
|
|
137
131
|
+ '--btn-transition: transform 0.1s ease-in-out, box-shadow 0.1s ease-in-out ;'
|
|
@@ -141,14 +135,13 @@ window.modals = {
|
|
|
141
135
|
+ '-webkit-transition: var(--btn-transition) ; -moz-transition: var(--btn-transition) ;'
|
|
142
136
|
+ '-o-transition: var(--btn-transition) ; -ms-transition: var(--btn-transition) ;'
|
|
143
137
|
+ 'cursor: pointer !important ;' // add finger cursor
|
|
144
|
-
+ `border: 1px solid ${
|
|
138
|
+
+ `border: 1px solid ${ scheme == 'dark' ? 'white' : 'black' } !important ;`
|
|
145
139
|
+ 'padding: 8px !important ; min-width: 102px }' // resize
|
|
146
140
|
+ `.${this.class} button:hover {` // add zoom, re-scheme
|
|
147
141
|
+ 'transform: scale(1.055) ; color: black !important ;'
|
|
148
|
-
+ `background-color: #${
|
|
149
|
-
+ ( !this.
|
|
150
|
-
|
|
151
|
-
+ `.about-em { color: ${ this.imports.env.ui.scheme == 'dark' ? 'white' : 'green' } !important }`
|
|
142
|
+
+ `background-color: #${ scheme == 'dark' ? '00cfff' : '9cdaff' } !important }`
|
|
143
|
+
+ ( !isMobile ? `.${this.class} .modal-buttons { margin-left: -13px !important }` : '' )
|
|
144
|
+
+ `.about-em { color: ${ scheme == 'dark' ? 'white' : 'green' } !important }`
|
|
152
145
|
)
|
|
153
146
|
}
|
|
154
147
|
};
|
|
@@ -18,15 +18,13 @@
|
|
|
18
18
|
dom.import({ env }) // for env.ui.scheme
|
|
19
19
|
modals.import({ app, env }) // for app data + env.<browser|ui> flags
|
|
20
20
|
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
config.skipAlert = true ; chatgpt.isLoaded().then(() => modals.open('about'))
|
|
29
|
-
} else if (req.action == 'syncConfigToUI') syncConfigToUI(req.options)
|
|
21
|
+
chrome.runtime.onMessage.addListener(({ action, options }) => { // from service-worker.js + popup/index.html
|
|
22
|
+
({
|
|
23
|
+
notify: () => notify(...['msg', 'pos', 'notifDuration', 'shadow'].map(arg => options[arg])),
|
|
24
|
+
alert: () => modals.alert(...['title', 'msg', 'btns', 'checkbox', 'width'].map(arg => options[arg])),
|
|
25
|
+
showAbout: () => { config.skipAlert = true ; chatgpt.isLoaded().then(() => modals.open('about')) },
|
|
26
|
+
syncConfigToUI: () => syncConfigToUI(options)
|
|
27
|
+
}[action]?.())
|
|
30
28
|
})
|
|
31
29
|
|
|
32
30
|
// Init SETTINGS
|
|
@@ -92,7 +90,8 @@
|
|
|
92
90
|
// Add RISING PARTICLES styles for modals
|
|
93
91
|
['gray', 'white'].forEach(color => document.head.append(
|
|
94
92
|
dom.create.elem('link', { rel: 'stylesheet',
|
|
95
|
-
href: `https://
|
|
93
|
+
href: `https://cdn.jsdelivr.net/gh/adamlui/ai-web-extensions@727feff/assets/styles/rising-particles/dist/${
|
|
94
|
+
color}.min.css`
|
|
96
95
|
})))
|
|
97
96
|
|
|
98
97
|
if (config.extensionDisabled) return
|
|
@@ -24,24 +24,34 @@ const chatgpt = {
|
|
|
24
24
|
|
|
25
25
|
selectors: {
|
|
26
26
|
btns: {
|
|
27
|
-
continue: 'button
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
continue: 'button:has(svg[class*=rotate] > path[d^="M4.47189"])',
|
|
28
|
+
createImage: 'button[data-testid="composer-create-image"]',
|
|
29
|
+
deepResearch: 'button[data-testid="composer-deep-research"]',
|
|
30
|
+
login: 'button[data-testid*=login]',
|
|
31
|
+
newChat: 'a[href="/"]:has(svg),' // Pencil button (when logged in)
|
|
32
|
+
+ 'button:has([d^="M3.06957"])', // Cycle Arrows button (in temp chat logged out)
|
|
33
|
+
regen: 'button[data-testid*=regenerate],' // oval button in place of chatbar on errors
|
|
34
|
+
// 'Try Again' entry of model selector below msg
|
|
35
|
+
+ 'div[role=menuitem] div:has(svg):has(path[d^="M3.06957"])',
|
|
36
|
+
scroll: 'button:has(> svg > path[d^="M12 21C11.7348"])',
|
|
37
|
+
search: 'button[data-testid="composer-button-search"]',
|
|
38
|
+
reason: 'button[data-testid="composer-button-reason"]',
|
|
39
|
+
send: 'button[data-testid=send-button]',
|
|
40
|
+
sidebar: 'button[data-testid*=sidebar-button]',
|
|
41
|
+
stop: 'button[data-testid=stop-button]',
|
|
42
|
+
upload: 'button:has(> svg > path[d^="M12 3C12.5523"])',
|
|
43
|
+
voice: 'button[data-testid*=composer-speech-button]'
|
|
34
44
|
},
|
|
35
45
|
chatDivs: {
|
|
36
|
-
convo: '
|
|
37
|
-
|
|
46
|
+
convo: 'div[class*=thread]', msg: 'div[data-message-author-role]',
|
|
47
|
+
reply: 'div[data-message-author-role=assistant]'
|
|
38
48
|
},
|
|
39
|
-
chatHistory: '
|
|
40
|
-
errors: { txt: '[class*=text-error]' },
|
|
41
|
-
footer: '.
|
|
42
|
-
header: 'main .sticky',
|
|
49
|
+
chatHistory: 'div#history',
|
|
50
|
+
errors: { toast: 'div.toast-root', txt: 'div[class*=text-error]' },
|
|
51
|
+
footer: 'div#thread-bottom-container > div:last-of-type > div, span.text-sm.leading-none',
|
|
52
|
+
header: 'div#page-header, main div.sticky:first-of-type',
|
|
43
53
|
links: { newChat: 'nav a[href="/"]', sidebarItem: 'nav a' },
|
|
44
|
-
sidebar: 'div[class*=sidebar]',
|
|
54
|
+
sidebar: 'div[class*=sidebar]:has(nav > div#sidebar-header)',
|
|
45
55
|
ssgManifest: 'script[src*="_ssgManifest.js"]'
|
|
46
56
|
},
|
|
47
57
|
|
|
@@ -122,7 +132,8 @@ const chatgpt = {
|
|
|
122
132
|
chatgpt.draggingModal = event.currentTarget
|
|
123
133
|
event.preventDefault() // prevent sub-elems like icons being draggable
|
|
124
134
|
Object.assign(chatgpt.draggingModal.style, {
|
|
125
|
-
|
|
135
|
+
transition: '0.1s', willChange: 'transform', transform: 'scale(1.05)' })
|
|
136
|
+
document.body.style.cursor = 'grabbing'; // update cursor
|
|
126
137
|
[...chatgpt.draggingModal.children] // prevent hover FX if drag lags behind cursor
|
|
127
138
|
.forEach(child => child.style.pointerEvents = 'none');
|
|
128
139
|
['mousemove', 'mouseup'].forEach(eventType => // add listeners
|
|
@@ -141,7 +152,8 @@ const chatgpt = {
|
|
|
141
152
|
|
|
142
153
|
mouseup() { // restore styles/pointer events, remove listeners, reset chatgpt.draggingModal
|
|
143
154
|
Object.assign(chatgpt.draggingModal.style, { // restore styles
|
|
144
|
-
cursor: 'inherit', transition: 'inherit', willChange: 'auto', transform: 'scale(1)' })
|
|
155
|
+
cursor: 'inherit', transition: 'inherit', willChange: 'auto', transform: 'scale(1)' })
|
|
156
|
+
document.body.style.cursor = ''; // restore cursor
|
|
145
157
|
[...chatgpt.draggingModal.children] // restore pointer events
|
|
146
158
|
.forEach(child => child.style.pointerEvents = '');
|
|
147
159
|
['mousemove', 'mouseup'].forEach(eventType => // remove listeners
|
|
@@ -295,7 +307,7 @@ const chatgpt = {
|
|
|
295
307
|
// Create/show label
|
|
296
308
|
const checkboxLabel = document.createElement('label')
|
|
297
309
|
checkboxLabel.onclick = () => { checkboxInput.checked = !checkboxInput.checked ; checkboxFn() }
|
|
298
|
-
checkboxLabel.textContent = checkboxFn.name
|
|
310
|
+
checkboxLabel.textContent = checkboxFn.name[0].toUpperCase() // capitalize first char
|
|
299
311
|
+ checkboxFn.name.slice(1) // format remaining chars
|
|
300
312
|
.replace(/([A-Z])/g, (match, letter) => ' ' + letter.toLowerCase()) // insert spaces, convert to lowercase
|
|
301
313
|
.replace(/\b(\w+)nt\b/gi, '$1n\'t') // insert apostrophe in 'nt' suffixes
|
|
@@ -644,7 +656,7 @@ const chatgpt = {
|
|
|
644
656
|
const msgs = [] ; let isUserMsg = true
|
|
645
657
|
chatDivs.forEach(div => {
|
|
646
658
|
const sender = isUserMsg ? 'USER' : 'CHATGPT'; isUserMsg = !isUserMsg
|
|
647
|
-
const msg =
|
|
659
|
+
const msg = [...div.childNodes].map(node => node.innerText)
|
|
648
660
|
.join('\n\n') // insert double line breaks between paragraphs
|
|
649
661
|
.replace('Copy code', '')
|
|
650
662
|
msgs.push(`${sender}: ${msg}`)
|
|
@@ -1146,7 +1158,7 @@ const chatgpt = {
|
|
|
1146
1158
|
}
|
|
1147
1159
|
},
|
|
1148
1160
|
|
|
1149
|
-
isDarkMode() { return document.documentElement.
|
|
1161
|
+
isDarkMode() { return document.documentElement.classList.contains('dark') },
|
|
1150
1162
|
isFullScreen() { return chatgpt.browser.isFullScreen() },
|
|
1151
1163
|
|
|
1152
1164
|
async isIdle(timeout = null) {
|
|
@@ -1187,7 +1199,7 @@ const chatgpt = {
|
|
|
1187
1199
|
return await ( timeoutPromise ? Promise.race([isLoadedPromise, timeoutPromise]) : isLoadedPromise )
|
|
1188
1200
|
},
|
|
1189
1201
|
|
|
1190
|
-
isLightMode() { return document.documentElement.classList.
|
|
1202
|
+
isLightMode() { return document.documentElement.classList.contains('light') },
|
|
1191
1203
|
isTempChat() { return location.search == '?temporary-chat=true' },
|
|
1192
1204
|
isTyping() { return !!this.getStopButton() },
|
|
1193
1205
|
login() { window.location.href = 'https://chat.openai.com/auth/login' },
|
|
@@ -1363,7 +1375,7 @@ const chatgpt = {
|
|
|
1363
1375
|
for (const divId of thisQuadrantQueue.slice(0, -1)) { // exclude new div
|
|
1364
1376
|
const oldDiv = document.getElementById(divId),
|
|
1365
1377
|
offsetProp = oldDiv.style.top ? 'top' : 'bottom', // pick property to change
|
|
1366
|
-
vOffset =
|
|
1378
|
+
vOffset = +parseInt(oldDiv.style[offsetProp]) +5 + oldDiv.getBoundingClientRect().height
|
|
1367
1379
|
oldDiv.style[offsetProp] = `${ vOffset }px` // change prop
|
|
1368
1380
|
}
|
|
1369
1381
|
} catch (err) {}
|
|
@@ -1494,7 +1506,7 @@ const chatgpt = {
|
|
|
1494
1506
|
// Process text node
|
|
1495
1507
|
if (childNode.nodeType == Node.TEXT_NODE) {
|
|
1496
1508
|
const text = childNode.nodeValue,
|
|
1497
|
-
elems =
|
|
1509
|
+
elems = [...text.matchAll(reTags)]
|
|
1498
1510
|
|
|
1499
1511
|
// Process 1st element to render
|
|
1500
1512
|
if (elems.length > 0) {
|
|
@@ -1503,7 +1515,7 @@ const chatgpt = {
|
|
|
1503
1515
|
tagNode = document.createElement(tagName) ; tagNode.textContent = tagText
|
|
1504
1516
|
|
|
1505
1517
|
// Extract/set attributes
|
|
1506
|
-
const attrs =
|
|
1518
|
+
const attrs = [...tagAttrs.matchAll(reAttrs)]
|
|
1507
1519
|
attrs.forEach(attr => {
|
|
1508
1520
|
const name = attr[1], value = attr[2].replace(/['"]/g, '')
|
|
1509
1521
|
tagNode.setAttribute(name, value)
|
|
@@ -1539,9 +1551,8 @@ const chatgpt = {
|
|
|
1539
1551
|
// responseToGet = index of response to get (defaults to latest if '' unpassed)
|
|
1540
1552
|
// regenResponseToGet = index of regenerated response to get (defaults to latest if '' unpassed)
|
|
1541
1553
|
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
else return this.getFromAPI.apply(null, arguments)
|
|
1554
|
+
return this[`getFrom${ location.href.startsWith('https://chatgpt.com/c/') ? 'DOM' : 'API' }`]
|
|
1555
|
+
.apply(null, arguments)
|
|
1545
1556
|
},
|
|
1546
1557
|
|
|
1547
1558
|
getFromAPI(chatToGet, responseToGet) {
|
|
@@ -1602,7 +1613,7 @@ const chatgpt = {
|
|
|
1602
1613
|
const textArea = chatgpt.getChatBox()
|
|
1603
1614
|
if (!textArea) return console.error('Chatbar element not found!')
|
|
1604
1615
|
const msgP = document.createElement('p'); msgP.textContent = msg
|
|
1605
|
-
textArea.
|
|
1616
|
+
textArea.querySelector('p').replaceWith(msgP)
|
|
1606
1617
|
textArea.dispatchEvent(new Event('input', { bubbles: true })) // enable send button
|
|
1607
1618
|
setTimeout(function delaySend() {
|
|
1608
1619
|
const sendBtn = chatgpt.getSendButton()
|
|
@@ -1851,10 +1862,11 @@ const chatgpt = {
|
|
|
1851
1862
|
hide() { this.isOn() ? this.toggle() : console.info('Sidebar already hidden!') },
|
|
1852
1863
|
show() { this.isOff() ? this.toggle() : console.info('Sidebar already shown!') },
|
|
1853
1864
|
isOff() { return !this.isOn() },
|
|
1865
|
+
|
|
1854
1866
|
isOn() {
|
|
1855
1867
|
const sidebar = (() => {
|
|
1856
1868
|
return chatgpt.sidebar.exists() ? document.querySelector(chatgpt.selectors.sidebar) : null })()
|
|
1857
|
-
if (!sidebar) { console.error('Sidebar element not found!')
|
|
1869
|
+
if (!sidebar) { return console.error('Sidebar element not found!') || false }
|
|
1858
1870
|
else return chatgpt.browser.isMobile() ?
|
|
1859
1871
|
document.documentElement.style.overflow == 'hidden'
|
|
1860
1872
|
: sidebar.style.visibility != 'hidden' && sidebar.style.width != '0px'
|
|
@@ -2089,11 +2101,10 @@ const cjsFuncSynonyms = [
|
|
|
2089
2101
|
} while (aliasFuncCreated) // loop over new functions to encompass all variations
|
|
2090
2102
|
})()
|
|
2091
2103
|
|
|
2092
|
-
|
|
2093
2104
|
// Define HELPER functions
|
|
2094
2105
|
|
|
2095
2106
|
function toCamelCase(words) {
|
|
2096
|
-
return words.map((word, idx) => idx == 0 ? word : word
|
|
2107
|
+
return words.map((word, idx) => idx == 0 ? word : word[0].toUpperCase() + word.slice(1)).join('') }
|
|
2097
2108
|
|
|
2098
2109
|
// Prefix console logs w/ '🤖 chatgpt.js >> '
|
|
2099
2110
|
const consolePrefix = '🤖 chatgpt.js >> ', ogError = console.error, ogInfo = console.info
|
|
@@ -93,8 +93,9 @@ window.dom = {
|
|
|
93
93
|
computedHeight(elems) { return this.computedSize(elems, { dimension: 'height' }) }, // including margins
|
|
94
94
|
computedWidth(elems) { return this.computedSize(elems, { dimension: 'width' }) }, // including margins
|
|
95
95
|
|
|
96
|
-
loadedElem(selector, timeout = null) {
|
|
97
|
-
const timeoutPromise =
|
|
96
|
+
loadedElem(selector, { timeout = null } = {}) {
|
|
97
|
+
const timeoutPromise = new Promise(resolve =>
|
|
98
|
+
timeout ? setTimeout(() => resolve(null), timeout) : undefined)
|
|
98
99
|
const isLoadedPromise = new Promise(resolve => {
|
|
99
100
|
const elem = document.querySelector(selector)
|
|
100
101
|
if (elem) resolve(elem)
|
|
@@ -103,7 +104,7 @@ window.dom = {
|
|
|
103
104
|
if (elem) { obs.disconnect() ; resolve(elem) }
|
|
104
105
|
}).observe(document.documentElement, { childList: true, subtree: true })
|
|
105
106
|
})
|
|
106
|
-
return
|
|
107
|
+
return Promise.race([isLoadedPromise, timeoutPromise])
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
};
|
|
@@ -1,29 +1,55 @@
|
|
|
1
1
|
window.config = {}
|
|
2
2
|
window.settings = {
|
|
3
3
|
|
|
4
|
-
// Init SETTINGS props (for popup menu)
|
|
5
4
|
controls: {
|
|
6
5
|
// Add settings options as keys, with each key's value being an object that includes:
|
|
7
6
|
// - 'type': the control type (e.g. 'toggle' or 'prompt')
|
|
8
7
|
// - 'label': a descriptive label
|
|
9
8
|
// - 'defaultVal' (optional): default value of setting (true for toggles if unspecified, false otherwise)
|
|
9
|
+
// - 'category' (optional): string key from this.categories to group control under
|
|
10
10
|
// - 'symbol' (optional): for icon display (e.g. ⌚)
|
|
11
|
-
//
|
|
12
|
-
|
|
11
|
+
// - 'helptip' (optional): tooltip to display on hover
|
|
12
|
+
|
|
13
|
+
// NOTE: Controls are displayed in top-to-bottom order (within categories and in top-level)
|
|
14
|
+
// NOTE: Toggles are disabled by default unless defaultVal is true
|
|
15
|
+
// ...or key name contains 'disabled' or 'hidden' (case insensitive)
|
|
16
|
+
|
|
13
17
|
// EXAMPLES:
|
|
14
18
|
// autoScrollDisabled: { type: 'toggle', label: 'Auto-Scroll' },
|
|
15
19
|
// replyLanguage: { type: 'prompt', symbol: '🌐', label: 'Reply Language' }
|
|
16
20
|
},
|
|
17
21
|
|
|
22
|
+
categories: {
|
|
23
|
+
// Add category entries as keys, with each key's value being an object that includes:
|
|
24
|
+
// - 'label': a descriptive label
|
|
25
|
+
// - 'symbol' (optional): for icon display (e.g. ⌚)
|
|
26
|
+
// - 'color' (optional): hex code (w/o #) of color for left-border
|
|
27
|
+
// - 'helptip' (optional): tooltip to display on hover
|
|
28
|
+
// - 'autoExpand' (optional): true/false to auto-expand categories on toolbar icon click
|
|
29
|
+
|
|
30
|
+
// NOTE: Categories are displayed in top-to-bottom order
|
|
31
|
+
|
|
32
|
+
// EXAMPLE:
|
|
33
|
+
// displaySettings: {
|
|
34
|
+
// symbol: '🖥️', color: '94fca2', label: 'Display Settings', helptip: 'Display-related settings' }
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
typeIsEnabled(key) { // for menu labels + notifs to return ON/OFF for type w/o suffix
|
|
38
|
+
const reInvertFlags = /disabled|hidden/i
|
|
39
|
+
return reInvertFlags.test(key) // flag in control key name
|
|
40
|
+
&& !reInvertFlags.test(this.controls[key]?.label) // but not in label name
|
|
41
|
+
? !config[key] : config[key] // so invert since flag reps opposite type state, else don't
|
|
42
|
+
},
|
|
43
|
+
|
|
18
44
|
load(...keys) {
|
|
19
45
|
return Promise.all(keys.flat().map(async key => // resolve promise when all keys load
|
|
20
|
-
|
|
46
|
+
config[key] = (await chrome.storage.local.get(key))[key]
|
|
21
47
|
?? this.controls[key]?.defaultVal ?? this.controls[key]?.type == 'toggle'
|
|
22
48
|
))
|
|
23
49
|
},
|
|
24
50
|
|
|
25
51
|
save(key, val) {
|
|
26
52
|
chrome.storage.local.set({ [key]: val }) // save to Chrome extension storage
|
|
27
|
-
|
|
53
|
+
config[key] = val // save to memory
|
|
28
54
|
}
|
|
29
55
|
};
|
|
@@ -3,21 +3,13 @@
|
|
|
3
3
|
"name": "ChatGPT Extension",
|
|
4
4
|
"short_name": "ChatGPT 🧩",
|
|
5
5
|
"description": "A Chromium extension template to start using chatgpt.js like a boss!",
|
|
6
|
-
"version": "2025.
|
|
6
|
+
"version": "2025.4.28",
|
|
7
7
|
"author": "KudoAI",
|
|
8
8
|
"homepage_url": "https://github.com/KudoAI/chatgpt.js-chrome-starter",
|
|
9
|
-
"icons": {
|
|
10
|
-
"16": "icons/icon16.png",
|
|
11
|
-
"32": "icons/icon32.png",
|
|
12
|
-
"64": "icons/icon64.png",
|
|
13
|
-
"128": "icons/icon128.png"
|
|
14
|
-
},
|
|
9
|
+
"icons": { "16": "icons/icon16.png", "32": "icons/icon32.png", "64": "icons/icon64.png", "128": "icons/icon128.png" },
|
|
15
10
|
"permissions": [ "activeTab", "storage" ],
|
|
16
11
|
"action": { "default_popup": "popup/index.html" },
|
|
17
|
-
"web_accessible_resources": [{
|
|
18
|
-
"matches": [ "<all_urls>" ],
|
|
19
|
-
"resources": [ "components/modals.js", "lib/chatgpt.js", "lib/dom.js", "lib/settings.js" ]
|
|
20
|
-
}],
|
|
12
|
+
"web_accessible_resources": [{ "matches": [ "<all_urls>" ], "resources": [ "components/*", "lib/*" ]}],
|
|
21
13
|
"content_scripts": [{ "matches": [ "https://chatgpt.com/*" ], "js": [ "content.js" ] }],
|
|
22
14
|
"background": { "service_worker": "service-worker.js" },
|
|
23
15
|
"minimum_chrome_version": "88"
|