@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.
- package/README.md +29 -21
- package/chatgpt.js +149 -94
- package/dist/chatgpt.min.js +3 -3
- package/docs/README.md +29 -21
- package/docs/SECURITY.md +1 -3
- package/docs/USERGUIDE.md +3 -3
- package/package.json +18 -11
- package/starters/chrome/docs/README.md +10 -8
- package/starters/chrome/extension/components/icons.js +31 -0
- package/starters/chrome/extension/components/modals.js +148 -0
- package/starters/chrome/extension/content.js +108 -47
- package/starters/chrome/extension/icons/faded/icon128.png +0 -0
- package/starters/chrome/extension/icons/faded/icon16.png +0 -0
- package/starters/chrome/extension/icons/faded/icon32.png +0 -0
- package/starters/chrome/extension/icons/faded/icon64.png +0 -0
- package/starters/chrome/extension/lib/chatgpt.js +149 -94
- package/starters/chrome/extension/lib/dom.js +35 -0
- package/starters/chrome/extension/lib/settings.js +30 -0
- package/starters/chrome/extension/manifest.json +22 -22
- package/starters/chrome/extension/popup/controller.js +140 -0
- package/starters/chrome/extension/popup/index.html +7 -44
- package/starters/chrome/extension/popup/style.css +33 -10
- package/starters/chrome/extension/service-worker.js +41 -0
- package/starters/greasemonkey/chatgpt.js-greasemonkey-starter.user.js +12 -12
- package/starters/greasemonkey/docs/README.md +2 -0
- package/starters/chrome/extension/background.js +0 -14
- package/starters/chrome/extension/lib/settings-utils.js +0 -24
- package/starters/chrome/extension/popup/popup.js +0 -92
- package/starters/chrome/media/images/icons/refresh/icon16.png +0 -0
- package/starters/chrome/media/images/icons/refresh/icon50.png +0 -0
- /package/starters/chrome/{media/images → images}/icons/question-mark/icon16.png +0 -0
- /package/starters/chrome/{media/images → images}/icons/question-mark/icon512.png +0 -0
- /package/starters/chrome/{media/images → images}/screenshots/chatgpt-extension-in-list.png +0 -0
- /package/starters/chrome/{media/images → images}/screenshots/developer-mode-toggle.png +0 -0
- /package/starters/chrome/{media/images → images}/screenshots/developer-mode-toggle.psd +0 -0
- /package/starters/chrome/{media/images → images}/screenshots/extension-loaded.png +0 -0
- /package/starters/chrome/{media/images → images}/screenshots/load-unpacked-button.png +0 -0
- /package/starters/chrome/{media/images → images}/screenshots/reload-extension-button.png +0 -0
- /package/starters/chrome/{media/images → images}/screenshots/reload-page-button.png +0 -0
- /package/starters/chrome/{media/images → images}/screenshots/select-extension-folder.png +0 -0
package/chatgpt.js
CHANGED
|
@@ -8,7 +8,7 @@ localStorage.alertQueue = JSON.stringify([]);
|
|
|
8
8
|
localStorage.notifyProps = JSON.stringify({ queue: { topRight: [], bottomRight: [], bottomLeft: [], topLeft: [] }});
|
|
9
9
|
|
|
10
10
|
// Define chatgpt API
|
|
11
|
-
const chatgpt = {
|
|
11
|
+
const chatgpt = {
|
|
12
12
|
openAIaccessToken: {}, endpoints: {
|
|
13
13
|
assets: 'https://cdn.jsdelivr.net/gh/KudoAI/chatgpt.js',
|
|
14
14
|
openAI: {
|
|
@@ -64,9 +64,64 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
64
64
|
// [ title/msg = strings, btns = [named functions], checkbox = named function, width (px) = int ] = optional
|
|
65
65
|
// * Spaces are inserted into button labels by parsing function names in camel/kebab/snake case
|
|
66
66
|
|
|
67
|
+
// Init env context
|
|
67
68
|
const scheme = chatgpt.isDarkMode() ? 'dark' : 'light',
|
|
68
69
|
isMobile = chatgpt.browser.isMobile();
|
|
69
70
|
|
|
71
|
+
// Define event handlers
|
|
72
|
+
const handlers = {
|
|
73
|
+
|
|
74
|
+
dismiss: {
|
|
75
|
+
click(event) {
|
|
76
|
+
if (event.target == event.currentTarget || event.target.closest('[class*="-close-btn]'))
|
|
77
|
+
dismissAlert()
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
key(event) {
|
|
81
|
+
if (!/^(?: |Space|Enter|Return|Esc)/.test(event.key) || ![32, 13, 27].includes(event.keyCode))
|
|
82
|
+
return
|
|
83
|
+
for (const alertId of alertQueue) { // look to handle only if triggering alert is active
|
|
84
|
+
const alert = document.getElementById(alertId)
|
|
85
|
+
if (!alert || alert.style.display == 'none') return
|
|
86
|
+
if (event.key.startsWith('Esc') || event.keyCode == 27) dismissAlert() // and do nothing
|
|
87
|
+
else { // Space/Enter pressed
|
|
88
|
+
const mainButton = alert.querySelector('.modal-buttons').lastChild // look for main button
|
|
89
|
+
if (mainButton) { mainButton.click() ; event.preventDefault() } // click if found
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
drag: {
|
|
96
|
+
mousedown(event) { // find modal, attach listeners, init XY offsets
|
|
97
|
+
if (event.button != 0) return // prevent non-left-click drag
|
|
98
|
+
if (getComputedStyle(event.target).cursor == 'pointer') return // prevent drag on interactive elems
|
|
99
|
+
chatgpt.draggableElem = event.currentTarget
|
|
100
|
+
chatgpt.draggableElem.style.cursor = 'grabbing'
|
|
101
|
+
event.preventDefault(); // prevent sub-elems like icons being draggable
|
|
102
|
+
['mousemove', 'mouseup'].forEach(eventType =>
|
|
103
|
+
document.addEventListener(eventType, handlers.drag[eventType]))
|
|
104
|
+
const draggableElemRect = chatgpt.draggableElem.getBoundingClientRect()
|
|
105
|
+
handlers.drag.offsetX = event.clientX - draggableElemRect.left +21
|
|
106
|
+
handlers.drag.offsetY = event.clientY - draggableElemRect.top +12
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
mousemove(event) { // drag modal
|
|
110
|
+
if (!chatgpt.draggableElem) return
|
|
111
|
+
const newX = event.clientX - handlers.drag.offsetX,
|
|
112
|
+
newY = event.clientY - handlers.drag.offsetY
|
|
113
|
+
Object.assign(chatgpt.draggableElem.style, { left: `${newX}px`, top: `${newY}px` })
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
mouseup() { // remove listeners, reset chatgpt.draggableElem
|
|
117
|
+
chatgpt.draggableElem.style.cursor = 'inherit';
|
|
118
|
+
['mousemove', 'mouseup'].forEach(eventType =>
|
|
119
|
+
document.removeEventListener(eventType, handlers.drag[eventType]))
|
|
120
|
+
chatgpt.draggableElem = null
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
70
125
|
// Create modal parent/children elements
|
|
71
126
|
const modalContainer = document.createElement('div');
|
|
72
127
|
modalContainer.id = Math.floor(chatgpt.randomFloat() * 1000000) + Date.now();
|
|
@@ -76,7 +131,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
76
131
|
modalMessage = document.createElement('p');
|
|
77
132
|
|
|
78
133
|
// Create/append/update modal style (if missing or outdated)
|
|
79
|
-
const thisUpdated =
|
|
134
|
+
const thisUpdated = 1735475757891 // timestamp of last edit for this file's `modalStyle`
|
|
80
135
|
let modalStyle = document.querySelector('#chatgpt-modal-style'); // try to select existing style
|
|
81
136
|
if (!modalStyle || parseInt(modalStyle.getAttribute('last-updated'), 10) < thisUpdated) { // if missing or outdated
|
|
82
137
|
if (!modalStyle) { // outright missing, create/id/attr/append it first
|
|
@@ -88,26 +143,32 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
88
143
|
'.no-mobile-tap-outline { outline: none ; -webkit-tap-highlight-color: transparent }'
|
|
89
144
|
|
|
90
145
|
// Background styles
|
|
91
|
-
+ '.chatgpt-modal {'
|
|
146
|
+
+ '.chatgpt-modal {'
|
|
147
|
+
+ 'pointer-events: auto ;' // override any disabling from site modals (like guest login spam)
|
|
92
148
|
+ 'position: fixed ; top: 0 ; left: 0 ; width: 100% ; height: 100% ;' // expand to full view-port
|
|
93
|
-
+ 'background-color
|
|
94
|
-
+ 'transition: background-color 0.05s ease ;' // speed to transition in show alert routine
|
|
149
|
+
+ 'transition: background-color 0.25s ease !important ;' // speed to show bg dim
|
|
95
150
|
+ 'display: flex ; justify-content: center ; align-items: center ; z-index: 9999 }' // align
|
|
96
151
|
|
|
97
152
|
// Alert styles
|
|
98
153
|
+ '.chatgpt-modal > div {'
|
|
99
|
-
+ '
|
|
100
|
-
+ '
|
|
101
|
-
+ `
|
|
102
|
-
+
|
|
154
|
+
+ 'position: absolute ;' // to be click-draggable
|
|
155
|
+
+ 'opacity: 0 ;' // to fade-in
|
|
156
|
+
+ `border: 1px solid ${ scheme == 'dark' ? 'white' : '#b5b5b5' };`
|
|
157
|
+
+ `color: ${ scheme == 'dark' ? 'white' : 'black' };`
|
|
158
|
+
+ `background-color: ${ scheme == 'dark' ? 'black' : 'white' };`
|
|
159
|
+
+ 'transform: translateX(-3px) translateY(7px) ;' // offset to move-in from
|
|
160
|
+
+ 'transition: opacity 0.65s cubic-bezier(.165,.84,.44,1),' // for fade-ins
|
|
161
|
+
+ 'transform 0.55s cubic-bezier(.165,.84,.44,1) ;' // for move-ins
|
|
162
|
+
+ 'max-width: 75vw ; word-wrap: break-word ;'
|
|
103
163
|
+ 'padding: 20px ; margin: 12px 23px ; border-radius: 15px ; box-shadow: 0 30px 60px rgba(0, 0, 0, .12) ;'
|
|
104
164
|
+ ' -webkit-user-select: none ; -moz-user-select: none ; -ms-user-select: none ; user-select: none ; }'
|
|
105
165
|
+ '.chatgpt-modal h2 { margin-bottom: 9px }'
|
|
106
166
|
+ `.chatgpt-modal a { color: ${ scheme == 'dark' ? '#00cfff' : '#1e9ebb' }}`
|
|
107
|
-
+ '.chatgpt-modal
|
|
108
|
-
+ '
|
|
109
|
-
|
|
110
|
-
|
|
167
|
+
+ '.chatgpt-modal a:hover { text-decoration: underline }'
|
|
168
|
+
+ '.chatgpt-modal.animated > div { z-index: 13456 ; opacity: 0.98 ; transform: translateX(0) translateY(0) }'
|
|
169
|
+
+ '@keyframes alert-zoom-fade-out {'
|
|
170
|
+
+ '0% { opacity: 1 } 50% { opacity: 0.25 ; transform: scale(1.05) }'
|
|
171
|
+
+ '100% { opacity: 0 ; transform: scale(1.35) }}'
|
|
111
172
|
|
|
112
173
|
// Button styles
|
|
113
174
|
+ '.modal-buttons { display: flex ; justify-content: flex-end ; margin: 20px -5px -3px 0 ;'
|
|
@@ -134,13 +195,13 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
134
195
|
+ '.chatgpt-modal .checkbox-group label {'
|
|
135
196
|
+ 'font-size: .7rem ; margin: -.04rem 0 0px .3rem ;'
|
|
136
197
|
+ `color: ${ scheme == 'dark' ? '#e1e1e1' : '#1e1e1e' }}`
|
|
137
|
-
+ '.chatgpt-modal input[type=
|
|
198
|
+
+ '.chatgpt-modal input[type=checkbox] { transform: scale(0.7) ;'
|
|
138
199
|
+ `border: 1px solid ${ scheme == 'dark' ? 'white' : 'black' }}`
|
|
139
|
-
+ '.chatgpt-modal input[type=
|
|
200
|
+
+ '.chatgpt-modal input[type=checkbox]:checked {'
|
|
140
201
|
+ `border: 1px solid ${ scheme == 'dark' ? 'white' : 'black' } ;`
|
|
141
202
|
+ 'background-color: black ; position: inherit }'
|
|
142
|
-
+ '.chatgpt-modal input[type=
|
|
143
|
-
);
|
|
203
|
+
+ '.chatgpt-modal input[type=checkbox]:focus { outline: none ; box-shadow: none }'
|
|
204
|
+
);
|
|
144
205
|
}
|
|
145
206
|
|
|
146
207
|
// Insert text into elements
|
|
@@ -210,7 +271,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
210
271
|
const modalElems = [closeBtn, modalTitle, modalMessage, modalButtons, checkboxDiv];
|
|
211
272
|
modalElems.forEach((elem) => { modal.append(elem); });
|
|
212
273
|
modal.style.width = `${ width || 458 }px`;
|
|
213
|
-
modalContainer.append(modal); document.body.append(modalContainer);
|
|
274
|
+
modalContainer.append(modal); document.body.append(modalContainer);
|
|
214
275
|
|
|
215
276
|
// Enqueue alert
|
|
216
277
|
let alertQueue = JSON.parse(localStorage.alertQueue);
|
|
@@ -221,39 +282,22 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
221
282
|
modalContainer.style.display = 'none';
|
|
222
283
|
if (alertQueue.length === 1) {
|
|
223
284
|
modalContainer.style.display = '';
|
|
224
|
-
setTimeout(() => { //
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
285
|
+
setTimeout(() => { // dim bg
|
|
286
|
+
modal.parentNode.style.backgroundColor = `rgba(67, 70, 72, ${ scheme == 'dark' ? 0.62 : 0.33 })`
|
|
287
|
+
modal.parentNode.classList.add('animated')
|
|
288
|
+
}, 100) // delay for transition fx
|
|
228
289
|
}
|
|
229
290
|
|
|
230
|
-
//
|
|
231
|
-
const clickHandler = event => { // explicitly defined to support removal post-dismissal
|
|
232
|
-
if (event.target == event.currentTarget || event.target instanceof SVGPathElement) dismissAlert(); };
|
|
233
|
-
const keyHandler = event => { // to dismiss active alert
|
|
234
|
-
const dismissKeys = [' ', 'Spacebar', 'Enter', 'Return', 'Escape', 'Esc'],
|
|
235
|
-
dismissKeyCodes = [32, 13, 27];
|
|
236
|
-
if (dismissKeys.includes(event.key) || dismissKeyCodes.includes(event.keyCode)) {
|
|
237
|
-
for (const alertId of alertQueue) { // look to handle only if triggering alert is active
|
|
238
|
-
const alert = document.getElementById(alertId);
|
|
239
|
-
if (alert && alert.style.display !== 'none') { // active alert found
|
|
240
|
-
if (event.key.includes('Esc') || event.keyCode == 27) // esc pressed
|
|
241
|
-
dismissAlert(); // dismiss alert & do nothing
|
|
242
|
-
else if ([' ', 'Spacebar', 'Enter', 'Return'].includes(event.key) || [32, 13].includes(event.keyCode)) { // space/enter pressed
|
|
243
|
-
const mainButton = alert.querySelector('.modal-buttons').lastChild; // look for main button
|
|
244
|
-
if (mainButton) { mainButton.click(); event.preventDefault(); } // click if found
|
|
245
|
-
} return;
|
|
246
|
-
}}}};
|
|
247
|
-
|
|
248
|
-
// Add listeners to dismiss alert
|
|
291
|
+
// Add listeners
|
|
249
292
|
const dismissElems = [modalContainer, closeBtn, closeSVG, dismissBtn];
|
|
250
|
-
dismissElems.forEach(elem => elem.onclick =
|
|
251
|
-
document.addEventListener('keydown',
|
|
293
|
+
dismissElems.forEach(elem => elem.onclick = handlers.dismiss.click);
|
|
294
|
+
document.addEventListener('keydown', handlers.dismiss.key);
|
|
295
|
+
modal.onmousedown = handlers.drag.mousedown // enable click-dragging
|
|
252
296
|
|
|
253
297
|
// Define alert dismisser
|
|
254
298
|
const dismissAlert = () => {
|
|
255
299
|
modalContainer.style.backgroundColor = 'transparent';
|
|
256
|
-
modal.style.animation = 'alert-zoom-fade-out 0.
|
|
300
|
+
modal.style.animation = 'alert-zoom-fade-out 0.135s ease-out';
|
|
257
301
|
setTimeout(() => { // delay removal for fade-out
|
|
258
302
|
|
|
259
303
|
// Remove alert
|
|
@@ -261,7 +305,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
261
305
|
alertQueue = JSON.parse(localStorage.alertQueue);
|
|
262
306
|
alertQueue.shift(); // + memory
|
|
263
307
|
localStorage.alertQueue = JSON.stringify(alertQueue); // + storage
|
|
264
|
-
document.removeEventListener('keydown',
|
|
308
|
+
document.removeEventListener('keydown', handlers.dismiss.key); // prevent memory leaks
|
|
265
309
|
|
|
266
310
|
// Check for pending alerts in queue
|
|
267
311
|
if (alertQueue.length > 0) {
|
|
@@ -272,7 +316,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
272
316
|
}, 500);
|
|
273
317
|
}
|
|
274
318
|
|
|
275
|
-
},
|
|
319
|
+
}, 155);
|
|
276
320
|
};
|
|
277
321
|
|
|
278
322
|
return modalContainer.id; // if assignment used
|
|
@@ -413,7 +457,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
413
457
|
async isIdle(timeout = null) {
|
|
414
458
|
const obsConfig = { childList: true, subtree: true },
|
|
415
459
|
selectors = { msgDiv: 'div[data-message-author-role]',
|
|
416
|
-
replyDiv: 'div[data-message-author-role=
|
|
460
|
+
replyDiv: 'div[data-message-author-role=assistant]' };
|
|
417
461
|
|
|
418
462
|
// Create promises
|
|
419
463
|
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(false), timeout)) : null;
|
|
@@ -557,7 +601,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
557
601
|
.replace('Copy code', '');
|
|
558
602
|
msgs.push(sender + ': ' + msg);
|
|
559
603
|
});
|
|
560
|
-
transcript = msgs.join('\n\n');
|
|
604
|
+
transcript = msgs.join('\n\n');
|
|
561
605
|
|
|
562
606
|
// ...or from getChatData(chatToGet)
|
|
563
607
|
} else {
|
|
@@ -578,7 +622,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
578
622
|
filename = `${ parsedHtml.querySelector('title').textContent || 'ChatGPT conversation' }.html`;
|
|
579
623
|
|
|
580
624
|
// Convert relative CSS paths to absolute ones
|
|
581
|
-
const cssLinks = parsedHtml.querySelectorAll('link[rel=
|
|
625
|
+
const cssLinks = parsedHtml.querySelectorAll('link[rel=stylesheet]');
|
|
582
626
|
cssLinks.forEach(link => {
|
|
583
627
|
const href = link.getAttribute('href');
|
|
584
628
|
if (href?.startsWith('/')) link.setAttribute('href', 'https://chat.openai.com' + href);
|
|
@@ -606,7 +650,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
606
650
|
} else { // auto-save to file
|
|
607
651
|
|
|
608
652
|
if (format == 'md') { // remove extraneous HTML + fix file extension
|
|
609
|
-
const mdMatch =
|
|
653
|
+
const mdMatch = /<.*<h1(.|\n)*?href=".*?continue[^"]*".*?\/a>.*?<[^/]/.exec(transcript)[1];
|
|
610
654
|
transcript = mdMatch || transcript; filename = filename.replace('.html', '.md');
|
|
611
655
|
}
|
|
612
656
|
const blob = new Blob([transcript],
|
|
@@ -621,20 +665,20 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
621
665
|
focusChatbar() { chatgpt.getChatBox()?.focus(); },
|
|
622
666
|
|
|
623
667
|
footer: {
|
|
624
|
-
get() { return document.querySelector('
|
|
668
|
+
get() { return document.querySelector('.min-h-4'); },
|
|
625
669
|
|
|
626
|
-
hide() {
|
|
670
|
+
hide() {
|
|
627
671
|
const footer = chatgpt.footer.get();
|
|
628
672
|
if (!footer) return console.error('Footer element not found!');
|
|
629
673
|
if (footer.style.visibility == 'hidden') return console.info('Footer already hidden!');
|
|
630
|
-
footer.style.
|
|
674
|
+
footer.style.display = 'none';
|
|
631
675
|
},
|
|
632
676
|
|
|
633
677
|
show() {
|
|
634
678
|
const footer = chatgpt.footer.get();
|
|
635
679
|
if (!footer) return console.error('Footer element not found!');
|
|
636
680
|
if (footer.style.visibility != 'hidden') return console.info('Footer already shown!');
|
|
637
|
-
footer.style.
|
|
681
|
+
footer.style.display = 'inherit'
|
|
638
682
|
}
|
|
639
683
|
},
|
|
640
684
|
|
|
@@ -888,12 +932,15 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
888
932
|
getHeaderDiv() { return chatgpt.header.get(); },
|
|
889
933
|
getLastPrompt() { return chatgpt.getChatData('active', 'msg', 'user', 'latest'); },
|
|
890
934
|
getLastResponse() { return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'); },
|
|
891
|
-
|
|
935
|
+
|
|
936
|
+
getNewChatButton() {
|
|
937
|
+
return document.querySelector('button[data-testid*=new-chat-button], button:has([d^="M15.6729"])'); },
|
|
938
|
+
|
|
892
939
|
getNewChatLink() { return document.querySelector('nav a[href="/"]'); },
|
|
893
940
|
getRegenerateButton() { return document.querySelector('button:has([d^="M3.06957"])'); },
|
|
894
941
|
|
|
895
942
|
getResponse() {
|
|
896
|
-
// * Returns response via DOM by index arg if OpenAI chat page is active, otherwise uses API w/ following args:
|
|
943
|
+
// * Returns response via DOM by index arg if OpenAI chat page is active, otherwise uses API w/ following args:
|
|
897
944
|
// chatToGet = index|title|id of chat to get (defaults to latest if '' unpassed)
|
|
898
945
|
// responseToGet = index of response to get (defaults to latest if '' unpassed)
|
|
899
946
|
// regenResponseToGet = index of regenerated response to get (defaults to latest if '' unpassed)
|
|
@@ -904,8 +951,8 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
904
951
|
getResponseFromAPI(chatToGet, responseToGet) { return chatgpt.response.getFromAPI(chatToGet, responseToGet); },
|
|
905
952
|
getResponseFromDOM(pos) { return chatgpt.response.getFromDOM(pos); },
|
|
906
953
|
getScrollToBottomButton() { return document.querySelector('button:has([d^="M12 21C11.7348"])'); },
|
|
907
|
-
getSendButton() { return document.querySelector('[data-testid=
|
|
908
|
-
getStopButton() { return document.querySelector('button[data-testid=
|
|
954
|
+
getSendButton() { return document.querySelector('[data-testid=send-button]'); },
|
|
955
|
+
getStopButton() { return document.querySelector('button[data-testid=stop-button]'); },
|
|
909
956
|
|
|
910
957
|
getUserLanguage() {
|
|
911
958
|
return navigator.languages[0] || navigator.language || navigator.browserLanguage ||
|
|
@@ -1147,7 +1194,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1147
1194
|
}
|
|
1148
1195
|
|
|
1149
1196
|
else if (element == 'dropdown') {
|
|
1150
|
-
if (!attrs?.items || // there no are options to add
|
|
1197
|
+
if (!attrs?.items || // there no are options to add
|
|
1151
1198
|
!Array.isArray(attrs.items) || // it's not an array
|
|
1152
1199
|
!attrs.items.length) // the array is empty
|
|
1153
1200
|
attrs.items = [{ text: '🤖 chatgpt.js option', value: 'chatgpt.js option value' }]; // set default dropdown entry
|
|
@@ -1166,9 +1213,9 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1166
1213
|
}
|
|
1167
1214
|
|
|
1168
1215
|
const addElementsToMenu = () => {
|
|
1169
|
-
const optionButtons = document.querySelectorAll('a[role=
|
|
1216
|
+
const optionButtons = document.querySelectorAll('a[role=menuitem]');
|
|
1170
1217
|
let cssClasses;
|
|
1171
|
-
|
|
1218
|
+
|
|
1172
1219
|
for (const navLink of optionButtons)
|
|
1173
1220
|
if (navLink.textContent == 'Settings') {
|
|
1174
1221
|
cssClasses = navLink.classList;
|
|
@@ -1185,7 +1232,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1185
1232
|
};
|
|
1186
1233
|
|
|
1187
1234
|
this.elements.push(newElement);
|
|
1188
|
-
const menuBtn = document.querySelector('nav button[id*=
|
|
1235
|
+
const menuBtn = document.querySelector('nav button[id*=headless]');
|
|
1189
1236
|
if (!this.addedEvent) { // to prevent adding more than one event
|
|
1190
1237
|
menuBtn?.addEventListener('click', () => { setTimeout(addElementsToMenu, 25); });
|
|
1191
1238
|
this.addedEvent = true; }
|
|
@@ -1194,12 +1241,12 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1194
1241
|
},
|
|
1195
1242
|
|
|
1196
1243
|
close() {
|
|
1197
|
-
try { document.querySelector('nav [id*=
|
|
1244
|
+
try { document.querySelector('nav [id*=menu-button][aria-expanded=true]').click(); }
|
|
1198
1245
|
catch (err) { console.error(err.message); }
|
|
1199
1246
|
},
|
|
1200
1247
|
|
|
1201
1248
|
open() {
|
|
1202
|
-
try { document.querySelector('nav [id*=
|
|
1249
|
+
try { document.querySelector('nav [id*=menu-button][aria-expanded=false]').click(); }
|
|
1203
1250
|
catch (err) { console.error(err.message); }
|
|
1204
1251
|
}
|
|
1205
1252
|
},
|
|
@@ -1240,7 +1287,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1240
1287
|
+ (notificationDiv.isRight ? 'Right' : 'Left');
|
|
1241
1288
|
|
|
1242
1289
|
// Create/append/update notification style (if missing or outdated)
|
|
1243
|
-
const thisUpdated =
|
|
1290
|
+
const thisUpdated = 1735475527153 // timestamp of last edit for this file's `notifStyle`
|
|
1244
1291
|
let notifStyle = document.querySelector('#chatgpt-notif-style'); // try to select existing style
|
|
1245
1292
|
if (!notifStyle || parseInt(notifStyle.getAttribute('last-updated'), 10) < thisUpdated) { // if missing or outdated
|
|
1246
1293
|
if (!notifStyle) { // outright missing, create/id/attr/append it first
|
|
@@ -1250,12 +1297,14 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1250
1297
|
}
|
|
1251
1298
|
notifStyle.innerText = ( // update prev/new style contents
|
|
1252
1299
|
'.chatgpt-notif {'
|
|
1300
|
+
+ 'font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC",'
|
|
1301
|
+
+ '"Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", sans-serif ;'
|
|
1253
1302
|
+ '.no-mobile-tap-outline { outline: none ; -webkit-tap-highlight-color: transparent }'
|
|
1254
1303
|
+ 'background-color: black ; padding: 10px 13px 10px 18px ; border-radius: 11px ; border: 1px solid #f5f5f7 ;' // bubble style
|
|
1255
1304
|
+ 'opacity: 0 ; position: fixed ; z-index: 9999 ; font-size: 1.8rem ; color: white ;' // visibility
|
|
1256
1305
|
+ '-webkit-user-select: none ; -moz-user-select: none ; -ms-user-select: none ; user-select: none ;'
|
|
1257
1306
|
+ `transform: translateX(${ !notificationDiv.isRight ? '-' : '' }35px) ;` // init off-screen for transition fx
|
|
1258
|
-
+ ( shadow ? ( 'box-shadow: -8px 13px 25px 0 ' + ( /\b(shadow|on)\b/
|
|
1307
|
+
+ ( shadow ? ( 'box-shadow: -8px 13px 25px 0 ' + ( /\b(?:shadow|on)\b/i.test(shadow) ? 'gray' : shadow )) : '' ) + '}'
|
|
1259
1308
|
+ '.notif-close-btn { cursor: pointer ; float: right ; position: relative ; right: -4px ; margin-left: -3px ;'
|
|
1260
1309
|
+ 'display: grid }' // top-align for non-OpenAI sites
|
|
1261
1310
|
+ '@keyframes notif-zoom-fade-out { 0% { opacity: 1 ; transform: scale(1) }' // transition out keyframes
|
|
@@ -1263,7 +1312,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1263
1312
|
+ '45% { opacity: 0.05 ; transform: rotateX(-81deg) }'
|
|
1264
1313
|
+ '100% { opacity: 0 ; transform: rotateX(-180deg) scale(1.15) }}'
|
|
1265
1314
|
);
|
|
1266
|
-
}
|
|
1315
|
+
}
|
|
1267
1316
|
|
|
1268
1317
|
// Enqueue notification
|
|
1269
1318
|
let notifyProps = JSON.parse(localStorage.notifyProps);
|
|
@@ -1296,7 +1345,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1296
1345
|
notificationDiv.style.transition = 'transform 0.15s ease, opacity 0.15s ease';
|
|
1297
1346
|
}, 10);
|
|
1298
1347
|
|
|
1299
|
-
// Init delay before hiding
|
|
1348
|
+
// Init delay before hiding
|
|
1300
1349
|
const hideDelay = fadeDuration > notifDuration ? 0 // don't delay if fade exceeds notification duration
|
|
1301
1350
|
: notifDuration - fadeDuration; // otherwise delay for difference
|
|
1302
1351
|
|
|
@@ -1305,7 +1354,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1305
1354
|
notificationDiv.style.animation = `notif-zoom-fade-out ${ fadeDuration }s ease-out`;
|
|
1306
1355
|
clearTimeout(dismissFuncTID);
|
|
1307
1356
|
};
|
|
1308
|
-
const dismissFuncTID = setTimeout(dismissNotif, hideDelay * 1000); // maintain visibility for `hideDelay` secs, then dismiss
|
|
1357
|
+
const dismissFuncTID = setTimeout(dismissNotif, hideDelay * 1000); // maintain visibility for `hideDelay` secs, then dismiss
|
|
1309
1358
|
closeSVG.onclick = dismissNotif; // add to close button clicks
|
|
1310
1359
|
|
|
1311
1360
|
// Destroy notification
|
|
@@ -1341,8 +1390,9 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1341
1390
|
const functionNames = [];
|
|
1342
1391
|
for (const prop in this) {
|
|
1343
1392
|
if (typeof this[prop] == 'function') {
|
|
1344
|
-
const chatgptIsParent = !Object.keys(this)
|
|
1345
|
-
|
|
1393
|
+
const chatgptIsParent = !Object.keys(this)
|
|
1394
|
+
.find(obj => Object.keys(this[obj]).includes(this[prop].name))
|
|
1395
|
+
const functionParent = chatgptIsParent ? 'chatgpt' : 'other';
|
|
1346
1396
|
functionNames.push([functionParent, prop]);
|
|
1347
1397
|
} else if (typeof this[prop] == 'object') {
|
|
1348
1398
|
for (const nestedProp in this[prop]) {
|
|
@@ -1398,7 +1448,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1398
1448
|
|
|
1399
1449
|
renderHTML(node) {
|
|
1400
1450
|
const reTags = /<([a-z\d]+)\b([^>]*)>([\s\S]*?)<\/\1>/g,
|
|
1401
|
-
reAttributes = /(\S+)=['"]?((?:.(?!['"]?\s+\S+=|[>']))+.)['"]?/g,
|
|
1451
|
+
reAttributes = /(\S+)=['"]?((?:.(?!['"]?\s+\S+=|[>']))+.)['"]?/g, // eslint-disable-line
|
|
1402
1452
|
nodeContent = node.childNodes;
|
|
1403
1453
|
|
|
1404
1454
|
// Preserve consecutive spaces + line breaks
|
|
@@ -1453,7 +1503,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1453
1503
|
continue() { try { chatgpt.getContinueBtn().click(); } catch (err) { console.error(err.message); }},
|
|
1454
1504
|
|
|
1455
1505
|
get() {
|
|
1456
|
-
// * Returns response via DOM by index arg if OpenAI chat page is active, otherwise uses API w/ following args:
|
|
1506
|
+
// * Returns response via DOM by index arg if OpenAI chat page is active, otherwise uses API w/ following args:
|
|
1457
1507
|
// chatToGet = index|title|id of chat to get (defaults to latest if '' unpassed)
|
|
1458
1508
|
// responseToGet = index of response to get (defaults to latest if '' unpassed)
|
|
1459
1509
|
// regenResponseToGet = index of regenerated response to get (defaults to latest if '' unpassed)
|
|
@@ -1472,7 +1522,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1472
1522
|
},
|
|
1473
1523
|
|
|
1474
1524
|
getFromDOM(pos) {
|
|
1475
|
-
const responseDivs = document.querySelectorAll('div[data-message-author-role=
|
|
1525
|
+
const responseDivs = document.querySelectorAll('div[data-message-author-role=assistant]'),
|
|
1476
1526
|
strPos = pos.toString().toLowerCase();
|
|
1477
1527
|
let response = '';
|
|
1478
1528
|
if (!responseDivs.length) return console.error('No conversation found!');
|
|
@@ -1497,8 +1547,8 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1497
1547
|
: /^(?:10|ten)(?:th)?$/.test(strPos) ? 10 : 1 )
|
|
1498
1548
|
|
|
1499
1549
|
// Transform base number if suffixed
|
|
1500
|
-
* ( /(ty|ieth)$/.test(strPos) ? 10 : 1 ) // x 10 if -ty/ieth
|
|
1501
|
-
+ ( /teen(th)?$/.test(strPos) ? 10 : 0 ) // + 10 if -teen/teenth
|
|
1550
|
+
* ( /(?:ty|ieth)$/.test(strPos) ? 10 : 1 ) // x 10 if -ty/ieth
|
|
1551
|
+
+ ( /teen(?:th)?$/.test(strPos) ? 10 : 0 ) // + 10 if -teen/teenth
|
|
1502
1552
|
|
|
1503
1553
|
);
|
|
1504
1554
|
response = responseDivs[nthOfResponse - 1].textContent;
|
|
@@ -1682,14 +1732,14 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1682
1732
|
break;
|
|
1683
1733
|
}
|
|
1684
1734
|
}
|
|
1685
|
-
|
|
1735
|
+
|
|
1686
1736
|
// Apply CSS to make the added elements look like they belong to the website
|
|
1687
1737
|
this.elements.forEach(element => {
|
|
1688
1738
|
element.setAttribute('class', cssClasses);
|
|
1689
1739
|
element.style.maxHeight = element.style.minHeight = '44px'; // Fix the height of the element
|
|
1690
1740
|
element.style.margin = '2px 0';
|
|
1691
1741
|
});
|
|
1692
|
-
|
|
1742
|
+
|
|
1693
1743
|
// Create MutationObserver instance
|
|
1694
1744
|
const navBar = document.querySelector('nav');
|
|
1695
1745
|
if (!navBar) return console.error('Sidebar element not found!');
|
|
@@ -1746,7 +1796,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1746
1796
|
}
|
|
1747
1797
|
|
|
1748
1798
|
else if (element == 'dropdown') {
|
|
1749
|
-
if (!attrs?.items || // There no are options to add
|
|
1799
|
+
if (!attrs?.items || // There no are options to add
|
|
1750
1800
|
!Array.isArray(attrs.items) || // It's not an array
|
|
1751
1801
|
!attrs.items.length) // The array is empty
|
|
1752
1802
|
attrs.items = [{ text: '🤖 chatgpt.js option', value: 'chatgpt.js option value' }]; // Set default dropdown entry
|
|
@@ -1761,7 +1811,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1761
1811
|
newElement.add(optionElement);
|
|
1762
1812
|
});
|
|
1763
1813
|
}
|
|
1764
|
-
|
|
1814
|
+
|
|
1765
1815
|
|
|
1766
1816
|
// Fix for blank background on dropdown elements
|
|
1767
1817
|
if (element == 'dropdown') newElement.style.backgroundColor = 'var(--gray-900, rgb(32, 33, 35))';
|
|
@@ -1779,7 +1829,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1779
1829
|
isOff() { return !this.isOn(); },
|
|
1780
1830
|
isOn() {
|
|
1781
1831
|
const sidebar = (() => {
|
|
1782
|
-
return chatgpt.sidebar.exists() ? document.querySelector('[class*=
|
|
1832
|
+
return chatgpt.sidebar.exists() ? document.querySelector('[class*=sidebar]') : null; })();
|
|
1783
1833
|
if (!sidebar) { console.error('Sidebar element not found!'); return false; }
|
|
1784
1834
|
else return chatgpt.browser.isMobile() ?
|
|
1785
1835
|
document.documentElement.style.overflow == 'hidden'
|
|
@@ -1787,7 +1837,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1787
1837
|
},
|
|
1788
1838
|
|
|
1789
1839
|
toggle() {
|
|
1790
|
-
const sidebarToggle = document.querySelector('button[data-testid*=
|
|
1840
|
+
const sidebarToggle = document.querySelector('button[data-testid*=sidebar-button]');
|
|
1791
1841
|
if (!sidebarToggle) console.error('Sidebar toggle not found!');
|
|
1792
1842
|
sidebarToggle.click();
|
|
1793
1843
|
},
|
|
@@ -1862,7 +1912,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1862
1912
|
if (!outputLang) return console.error('outputLang (2nd) argument not supplied. Pass a language!');
|
|
1863
1913
|
for (let i = 0; i < arguments.length; i++) if (typeof arguments[i] !== 'string')
|
|
1864
1914
|
return console.error(`Argument ${ i + 1 } must be a string!`);
|
|
1865
|
-
chatgpt.send('Translate the following text to ' + outputLang
|
|
1915
|
+
chatgpt.send('Translate the following text to ' + outputLang
|
|
1866
1916
|
+ '. Only reply with the translation.\n\n' + text);
|
|
1867
1917
|
console.info('Translating text...');
|
|
1868
1918
|
await chatgpt.isIdle();
|
|
@@ -1872,14 +1922,19 @@ const chatgpt = { // eslint-disable-line no-redeclare
|
|
|
1872
1922
|
unminify() { chatgpt.code.unminify(); },
|
|
1873
1923
|
|
|
1874
1924
|
uuidv4() {
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
d =
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1925
|
+
try {
|
|
1926
|
+
// use native secure uuid generator when available
|
|
1927
|
+
return crypto.randomUUID();
|
|
1928
|
+
} catch(_e) {
|
|
1929
|
+
let d = new Date().getTime(); // get current timestamp in ms (to ensure UUID uniqueness)
|
|
1930
|
+
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
1931
|
+
const r = ( // generate random nibble
|
|
1932
|
+
( d + (window.crypto.getRandomValues(new Uint32Array(1))[0] / (Math.pow(2, 32) - 1))*16)%16 | 0 );
|
|
1933
|
+
d = Math.floor(d/16); // correspond each UUID digit to unique 4-bit chunks of timestamp
|
|
1934
|
+
return ( c == 'x' ? r : (r&0x3|0x8) ).toString(16); // generate random hexadecimal digit
|
|
1935
|
+
});
|
|
1936
|
+
return uuid;
|
|
1937
|
+
}
|
|
1883
1938
|
},
|
|
1884
1939
|
|
|
1885
1940
|
writeCode() { chatgpt.code.write(); }
|
|
@@ -1892,8 +1947,8 @@ const cjsBtnActions = ['click', 'get'], cjsTargetTypes = [ 'button', 'link', 'di
|
|
|
1892
1947
|
for (const btnAction of cjsBtnActions) {
|
|
1893
1948
|
chatgpt[btnAction + 'Button'] = function handleButton(buttonIdentifier) {
|
|
1894
1949
|
const button = /^[.#]/.test(buttonIdentifier) ? document.querySelector(buttonIdentifier)
|
|
1895
|
-
: /send/i.test(buttonIdentifier) ? document.querySelector('form button[class*=
|
|
1896
|
-
: /scroll/i.test(buttonIdentifier) ? document.querySelector('button[class*=
|
|
1950
|
+
: /send/i.test(buttonIdentifier) ? document.querySelector('form button[class*=bottom]')
|
|
1951
|
+
: /scroll/i.test(buttonIdentifier) ? document.querySelector('button[class*=cursor]')
|
|
1897
1952
|
: (function() { // get via text content
|
|
1898
1953
|
for (const button of document.querySelectorAll('button')) { // try buttons
|
|
1899
1954
|
if (button.textContent.toLowerCase().includes(buttonIdentifier.toLowerCase())) {
|
|
@@ -2011,7 +2066,7 @@ for (const prop in chatgpt) {
|
|
|
2011
2066
|
// Prefix console logs w/ '🤖 chatgpt.js >> '
|
|
2012
2067
|
const consolePrefix = '🤖 chatgpt.js >> ', ogError = console.error, ogInfo = console.info;
|
|
2013
2068
|
console.error = (...args) => {
|
|
2014
|
-
if (!args[0].startsWith(consolePrefix)) ogError(consolePrefix + args[0], ...args.slice(1));
|
|
2069
|
+
if (!args[0].startsWith(consolePrefix)) ogError(consolePrefix + args[0], ...args.slice(1));
|
|
2015
2070
|
else ogError(...args);
|
|
2016
2071
|
};
|
|
2017
2072
|
console.info = (msg) => {
|