@kudoai/chatgpt.js 3.4.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +1 -1
- package/README.md +153 -86
- package/chatgpt.js +91 -76
- package/dist/chatgpt.min.js +4 -4
- package/docs/README.md +153 -86
- package/docs/SECURITY.md +16 -18
- package/docs/USERGUIDE.md +19 -5
- package/package.json +9 -6
- package/starters/chrome/LICENSE.md +3 -3
- package/starters/chrome/docs/README.md +5 -5
- package/starters/chrome/docs/SECURITY.md +3 -5
- package/starters/chrome/extension/components/icons.js +4 -5
- package/starters/chrome/extension/components/modals.js +98 -65
- package/starters/chrome/extension/content.js +41 -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 +91 -76
- package/starters/chrome/extension/lib/dom.js +70 -11
- package/starters/chrome/extension/lib/settings.js +8 -9
- package/starters/chrome/extension/manifest.json +2 -5
- package/starters/chrome/extension/popup/controller.js +18 -16
- package/starters/chrome/extension/popup/index.html +1 -1
- package/starters/chrome/extension/popup/style.css +26 -13
- package/starters/chrome/extension/service-worker.js +7 -4
- package/starters/chrome/images/icons/question-mark/icon16.png +0 -0
- package/starters/chrome/images/icons/question-mark/icon512.png +0 -0
- package/starters/docs/LICENSE.md +21 -1
- package/starters/docs/README.md +19 -6
- package/starters/greasemonkey/LICENSE.md +3 -3
- package/starters/greasemonkey/chatgpt.js-greasemonkey-starter.user.js +5 -6
- package/starters/greasemonkey/docs/README.md +1 -1
- package/starters/greasemonkey/docs/SECURITY.md +3 -5
- /package/starters/greasemonkey/{media → assets}/images/icons/robot/icon48.png +0 -0
- /package/starters/greasemonkey/{media → assets}/images/icons/robot/icon64.png +0 -0
- /package/starters/greasemonkey/{media → assets}/images/screenshots/chatgpt-userscript-on.png +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// © 2023–
|
|
1
|
+
// © 2023–2025 KudoAI & contributors under the MIT license.
|
|
2
2
|
// Source: https://github.com/KudoAI/chatgpt.js
|
|
3
3
|
// User guide: https://chatgptjs.org/userguide
|
|
4
4
|
// Latest minified release: https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js/chatgpt.min.js
|
|
@@ -23,7 +23,7 @@ const chatgpt = {
|
|
|
23
23
|
actAs(persona) {
|
|
24
24
|
// Prompts ChatGPT to act as a persona from https://github.com/KudoAI/chat-prompts/blob/main/personas.json
|
|
25
25
|
|
|
26
|
-
const promptsUrl = 'https://
|
|
26
|
+
const promptsUrl = 'https://cdn.jsdelivr.net/gh/KudoAI/chat-prompts/dist/personas.min.json';
|
|
27
27
|
return new Promise((resolve, reject) => {
|
|
28
28
|
const xhr = new XMLHttpRequest();
|
|
29
29
|
xhr.open('GET', promptsUrl, true); xhr.send();
|
|
@@ -73,7 +73,7 @@ const chatgpt = {
|
|
|
73
73
|
|
|
74
74
|
dismiss: {
|
|
75
75
|
click(event) {
|
|
76
|
-
if (event.target == event.currentTarget || event.target.closest('[class
|
|
76
|
+
if (event.target == event.currentTarget || event.target.closest('[class*=-close-btn]'))
|
|
77
77
|
dismissAlert()
|
|
78
78
|
},
|
|
79
79
|
|
|
@@ -99,7 +99,8 @@ const chatgpt = {
|
|
|
99
99
|
chatgpt.draggableElem = event.currentTarget
|
|
100
100
|
chatgpt.draggableElem.style.cursor = 'grabbing'
|
|
101
101
|
event.preventDefault(); // prevent sub-elems like icons being draggable
|
|
102
|
-
['mousemove', 'mouseup'].forEach(
|
|
102
|
+
['mousemove', 'mouseup'].forEach(eventType =>
|
|
103
|
+
document.addEventListener(eventType, handlers.drag[eventType]))
|
|
103
104
|
const draggableElemRect = chatgpt.draggableElem.getBoundingClientRect()
|
|
104
105
|
handlers.drag.offsetX = event.clientX - draggableElemRect.left +21
|
|
105
106
|
handlers.drag.offsetY = event.clientY - draggableElemRect.top +12
|
|
@@ -114,8 +115,8 @@ const chatgpt = {
|
|
|
114
115
|
|
|
115
116
|
mouseup() { // remove listeners, reset chatgpt.draggableElem
|
|
116
117
|
chatgpt.draggableElem.style.cursor = 'inherit';
|
|
117
|
-
['mousemove', 'mouseup'].forEach(
|
|
118
|
-
document.removeEventListener(
|
|
118
|
+
['mousemove', 'mouseup'].forEach(eventType =>
|
|
119
|
+
document.removeEventListener(eventType, handlers.drag[eventType]))
|
|
119
120
|
chatgpt.draggableElem = null
|
|
120
121
|
}
|
|
121
122
|
}
|
|
@@ -130,7 +131,7 @@ const chatgpt = {
|
|
|
130
131
|
modalMessage = document.createElement('p');
|
|
131
132
|
|
|
132
133
|
// Create/append/update modal style (if missing or outdated)
|
|
133
|
-
const thisUpdated =
|
|
134
|
+
const thisUpdated = 1735768363880 // timestamp of last edit for this file's `modalStyle`
|
|
134
135
|
let modalStyle = document.querySelector('#chatgpt-modal-style'); // try to select existing style
|
|
135
136
|
if (!modalStyle || parseInt(modalStyle.getAttribute('last-updated'), 10) < thisUpdated) { // if missing or outdated
|
|
136
137
|
if (!modalStyle) { // outright missing, create/id/attr/append it first
|
|
@@ -139,14 +140,21 @@ const chatgpt = {
|
|
|
139
140
|
document.head.append(modalStyle);
|
|
140
141
|
}
|
|
141
142
|
modalStyle.innerText = ( // update prev/new style contents
|
|
142
|
-
'.
|
|
143
|
+
'.chatgpt-modal {' // vars
|
|
144
|
+
+ '--transition: opacity 0.65s cubic-bezier(.165,.84,.44,1),' // for fade-in
|
|
145
|
+
+ 'transform 0.55s cubic-bezier(.165,.84,.44,1) ;' // for move-in
|
|
146
|
+
+ '--bg-transition: background-color 0.25s ease }' // for bg dim
|
|
147
|
+
|
|
148
|
+
+ '.no-mobile-tap-outline { outline: none ; -webkit-tap-highlight-color: transparent }'
|
|
143
149
|
|
|
144
150
|
// Background styles
|
|
145
151
|
+ '.chatgpt-modal {'
|
|
146
152
|
+ 'pointer-events: auto ;' // override any disabling from site modals (like guest login spam)
|
|
147
153
|
+ 'position: fixed ; top: 0 ; left: 0 ; width: 100% ; height: 100% ;' // expand to full view-port
|
|
148
|
-
+ '
|
|
149
|
-
+ '
|
|
154
|
+
+ 'display: flex ; justify-content: center ; align-items: center ; z-index: 9999 ;' // align
|
|
155
|
+
+ 'transition: var(--bg-transition) ;' // for bg dim
|
|
156
|
+
+ '-webkit-transition: var(--bg-transition) ; -moz-transition: var(--bg-transition) ;'
|
|
157
|
+
+ '-o-transition: var(--bg-transition) ; -ms-transition: var(--bg-transition) }'
|
|
150
158
|
|
|
151
159
|
// Alert styles
|
|
152
160
|
+ '.chatgpt-modal > div {'
|
|
@@ -156,13 +164,16 @@ const chatgpt = {
|
|
|
156
164
|
+ `color: ${ scheme == 'dark' ? 'white' : 'black' };`
|
|
157
165
|
+ `background-color: ${ scheme == 'dark' ? 'black' : 'white' };`
|
|
158
166
|
+ 'transform: translateX(-3px) translateY(7px) ;' // offset to move-in from
|
|
159
|
-
+ '
|
|
160
|
-
|
|
161
|
-
+ '
|
|
162
|
-
|
|
163
|
-
+ '
|
|
167
|
+
+ 'max-width: 75vw ; word-wrap: break-word ; border-radius: 15px ;'
|
|
168
|
+
+ 'padding: 20px ; margin: 12px 23px ; box-shadow: 0 30px 60px rgba(0,0,0,0.12) ;'
|
|
169
|
+
+ 'user-select: none ; -webkit-user-select: none ; -moz-user-select: none ; -o-user-select: none ;'
|
|
170
|
+
+ '-ms-user-select: none ;'
|
|
171
|
+
+ 'transition: var(--transition) ;' // for fade-in + move-in
|
|
172
|
+
+ '-webkit-transition: var(--transition) ; -moz-transition: var(--transition) ;'
|
|
173
|
+
+ '-o-transition: var(--transition) ; -ms-transition: var(--transition) }'
|
|
164
174
|
+ '.chatgpt-modal h2 { margin-bottom: 9px }'
|
|
165
175
|
+ `.chatgpt-modal a { color: ${ scheme == 'dark' ? '#00cfff' : '#1e9ebb' }}`
|
|
176
|
+
+ '.chatgpt-modal a:hover { text-decoration: underline }'
|
|
166
177
|
+ '.chatgpt-modal.animated > div { z-index: 13456 ; opacity: 0.98 ; transform: translateX(0) translateY(0) }'
|
|
167
178
|
+ '@keyframes alert-zoom-fade-out {'
|
|
168
179
|
+ '0% { opacity: 1 } 50% { opacity: 0.25 ; transform: scale(1.05) }'
|
|
@@ -193,12 +204,12 @@ const chatgpt = {
|
|
|
193
204
|
+ '.chatgpt-modal .checkbox-group label {'
|
|
194
205
|
+ 'font-size: .7rem ; margin: -.04rem 0 0px .3rem ;'
|
|
195
206
|
+ `color: ${ scheme == 'dark' ? '#e1e1e1' : '#1e1e1e' }}`
|
|
196
|
-
+ '.chatgpt-modal input[type=
|
|
207
|
+
+ '.chatgpt-modal input[type=checkbox] { transform: scale(0.7) ;'
|
|
197
208
|
+ `border: 1px solid ${ scheme == 'dark' ? 'white' : 'black' }}`
|
|
198
|
-
+ '.chatgpt-modal input[type=
|
|
209
|
+
+ '.chatgpt-modal input[type=checkbox]:checked {'
|
|
199
210
|
+ `border: 1px solid ${ scheme == 'dark' ? 'white' : 'black' } ;`
|
|
200
211
|
+ 'background-color: black ; position: inherit }'
|
|
201
|
-
+ '.chatgpt-modal input[type=
|
|
212
|
+
+ '.chatgpt-modal input[type=checkbox]:focus { outline: none ; box-shadow: none }'
|
|
202
213
|
);
|
|
203
214
|
}
|
|
204
215
|
|
|
@@ -281,7 +292,7 @@ const chatgpt = {
|
|
|
281
292
|
if (alertQueue.length === 1) {
|
|
282
293
|
modalContainer.style.display = '';
|
|
283
294
|
setTimeout(() => { // dim bg
|
|
284
|
-
modal.parentNode.style.backgroundColor = `rgba(67,
|
|
295
|
+
modal.parentNode.style.backgroundColor = `rgba(67,70,72,${ scheme == 'dark' ? 0.62 : 0.33 })`
|
|
285
296
|
modal.parentNode.classList.add('animated')
|
|
286
297
|
}, 100) // delay for transition fx
|
|
287
298
|
}
|
|
@@ -294,30 +305,29 @@ const chatgpt = {
|
|
|
294
305
|
|
|
295
306
|
// Define alert dismisser
|
|
296
307
|
const dismissAlert = () => {
|
|
297
|
-
modalContainer.style.backgroundColor = 'transparent'
|
|
298
|
-
modal.style.animation = 'alert-zoom-fade-out 0.
|
|
299
|
-
|
|
308
|
+
modalContainer.style.backgroundColor = 'transparent'
|
|
309
|
+
modal.style.animation = 'alert-zoom-fade-out 0.165s ease-out'
|
|
310
|
+
modal.onanimationend = () => {
|
|
300
311
|
|
|
301
312
|
// Remove alert
|
|
302
|
-
modalContainer.remove()
|
|
303
|
-
alertQueue = JSON.parse(localStorage.alertQueue)
|
|
304
|
-
alertQueue.shift()
|
|
305
|
-
localStorage.alertQueue = JSON.stringify(alertQueue)
|
|
306
|
-
document.removeEventListener('keydown', handlers.dismiss.key)
|
|
313
|
+
modalContainer.remove() // ...from DOM
|
|
314
|
+
alertQueue = JSON.parse(localStorage.alertQueue)
|
|
315
|
+
alertQueue.shift() // + memory
|
|
316
|
+
localStorage.alertQueue = JSON.stringify(alertQueue) // + storage
|
|
317
|
+
document.removeEventListener('keydown', handlers.dismiss.key) // prevent memory leaks
|
|
307
318
|
|
|
308
319
|
// Check for pending alerts in queue
|
|
309
320
|
if (alertQueue.length > 0) {
|
|
310
|
-
const nextAlert = document.getElementById(alertQueue[0])
|
|
321
|
+
const nextAlert = document.getElementById(alertQueue[0])
|
|
311
322
|
setTimeout(() => {
|
|
312
|
-
nextAlert.style.display = ''
|
|
313
|
-
setTimeout(() =>
|
|
314
|
-
}, 500)
|
|
323
|
+
nextAlert.style.display = ''
|
|
324
|
+
setTimeout(() => nextAlert.classList.add('animated'), 100)
|
|
325
|
+
}, 500)
|
|
315
326
|
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
316
329
|
|
|
317
|
-
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
return modalContainer.id; // if assignment used
|
|
330
|
+
return modalContainer.id // if assignment used
|
|
321
331
|
},
|
|
322
332
|
|
|
323
333
|
async askAndGetReply(query) {
|
|
@@ -455,7 +465,7 @@ const chatgpt = {
|
|
|
455
465
|
async isIdle(timeout = null) {
|
|
456
466
|
const obsConfig = { childList: true, subtree: true },
|
|
457
467
|
selectors = { msgDiv: 'div[data-message-author-role]',
|
|
458
|
-
replyDiv: 'div[data-message-author-role=
|
|
468
|
+
replyDiv: 'div[data-message-author-role=assistant]' };
|
|
459
469
|
|
|
460
470
|
// Create promises
|
|
461
471
|
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(false), timeout)) : null;
|
|
@@ -590,7 +600,7 @@ const chatgpt = {
|
|
|
590
600
|
// Create transcript from active chat
|
|
591
601
|
if (chatToGet == 'active' && /\/\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/.test(window.location.href)) {
|
|
592
602
|
const chatDivs = document.querySelectorAll('main > div > div > div > div > div > div[class*=group]');
|
|
593
|
-
if (chatDivs.length
|
|
603
|
+
if (!chatDivs.length) return console.error('Chat is empty!');
|
|
594
604
|
const msgs = []; let isUserMsg = true;
|
|
595
605
|
chatDivs.forEach((div) => {
|
|
596
606
|
const sender = isUserMsg ? 'USER' : 'CHATGPT'; isUserMsg = !isUserMsg;
|
|
@@ -620,7 +630,7 @@ const chatgpt = {
|
|
|
620
630
|
filename = `${ parsedHtml.querySelector('title').textContent || 'ChatGPT conversation' }.html`;
|
|
621
631
|
|
|
622
632
|
// Convert relative CSS paths to absolute ones
|
|
623
|
-
const cssLinks = parsedHtml.querySelectorAll('link[rel=
|
|
633
|
+
const cssLinks = parsedHtml.querySelectorAll('link[rel=stylesheet]');
|
|
624
634
|
cssLinks.forEach(link => {
|
|
625
635
|
const href = link.getAttribute('href');
|
|
626
636
|
if (href?.startsWith('/')) link.setAttribute('href', 'https://chat.openai.com' + href);
|
|
@@ -932,7 +942,7 @@ const chatgpt = {
|
|
|
932
942
|
getLastResponse() { return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'); },
|
|
933
943
|
|
|
934
944
|
getNewChatButton() {
|
|
935
|
-
return document.querySelector('button[data-testid*=
|
|
945
|
+
return document.querySelector('button[data-testid*=new-chat-button], button:has([d^="M15.6729"])'); },
|
|
936
946
|
|
|
937
947
|
getNewChatLink() { return document.querySelector('nav a[href="/"]'); },
|
|
938
948
|
getRegenerateButton() { return document.querySelector('button:has([d^="M3.06957"])'); },
|
|
@@ -949,8 +959,8 @@ const chatgpt = {
|
|
|
949
959
|
getResponseFromAPI(chatToGet, responseToGet) { return chatgpt.response.getFromAPI(chatToGet, responseToGet); },
|
|
950
960
|
getResponseFromDOM(pos) { return chatgpt.response.getFromDOM(pos); },
|
|
951
961
|
getScrollToBottomButton() { return document.querySelector('button:has([d^="M12 21C11.7348"])'); },
|
|
952
|
-
getSendButton() { return document.querySelector('[data-testid=
|
|
953
|
-
getStopButton() { return document.querySelector('button[data-testid=
|
|
962
|
+
getSendButton() { return document.querySelector('[data-testid=send-button]'); },
|
|
963
|
+
getStopButton() { return document.querySelector('button[data-testid=stop-button]'); },
|
|
954
964
|
|
|
955
965
|
getUserLanguage() {
|
|
956
966
|
return navigator.languages[0] || navigator.language || navigator.browserLanguage ||
|
|
@@ -1104,7 +1114,7 @@ const chatgpt = {
|
|
|
1104
1114
|
}
|
|
1105
1115
|
},
|
|
1106
1116
|
|
|
1107
|
-
isDarkMode() { return document.documentElement.
|
|
1117
|
+
isDarkMode() { return document.documentElement.className.includes('dark') },
|
|
1108
1118
|
isFullScreen() { return chatgpt.browser.isFullScreen(); },
|
|
1109
1119
|
|
|
1110
1120
|
async isIdle(timeout = null) {
|
|
@@ -1147,6 +1157,7 @@ const chatgpt = {
|
|
|
1147
1157
|
},
|
|
1148
1158
|
|
|
1149
1159
|
isLightMode() { return document.documentElement.classList.toString().includes('light'); },
|
|
1160
|
+
isTyping() { return !!this.getStopButton() },
|
|
1150
1161
|
|
|
1151
1162
|
logout() { window.location.href = 'https://chat.openai.com/auth/logout'; },
|
|
1152
1163
|
|
|
@@ -1211,7 +1222,7 @@ const chatgpt = {
|
|
|
1211
1222
|
}
|
|
1212
1223
|
|
|
1213
1224
|
const addElementsToMenu = () => {
|
|
1214
|
-
const optionButtons = document.querySelectorAll('a[role=
|
|
1225
|
+
const optionButtons = document.querySelectorAll('a[role=menuitem]');
|
|
1215
1226
|
let cssClasses;
|
|
1216
1227
|
|
|
1217
1228
|
for (const navLink of optionButtons)
|
|
@@ -1230,7 +1241,7 @@ const chatgpt = {
|
|
|
1230
1241
|
};
|
|
1231
1242
|
|
|
1232
1243
|
this.elements.push(newElement);
|
|
1233
|
-
const menuBtn = document.querySelector('nav button[id*=
|
|
1244
|
+
const menuBtn = document.querySelector('nav button[id*=headless]');
|
|
1234
1245
|
if (!this.addedEvent) { // to prevent adding more than one event
|
|
1235
1246
|
menuBtn?.addEventListener('click', () => { setTimeout(addElementsToMenu, 25); });
|
|
1236
1247
|
this.addedEvent = true; }
|
|
@@ -1239,12 +1250,12 @@ const chatgpt = {
|
|
|
1239
1250
|
},
|
|
1240
1251
|
|
|
1241
1252
|
close() {
|
|
1242
|
-
try { document.querySelector('nav [id*=
|
|
1253
|
+
try { document.querySelector('nav [id*=menu-button][aria-expanded=true]').click(); }
|
|
1243
1254
|
catch (err) { console.error(err.message); }
|
|
1244
1255
|
},
|
|
1245
1256
|
|
|
1246
1257
|
open() {
|
|
1247
|
-
try { document.querySelector('nav [id*=
|
|
1258
|
+
try { document.querySelector('nav [id*=menu-button][aria-expanded=false]').click(); }
|
|
1248
1259
|
catch (err) { console.error(err.message); }
|
|
1249
1260
|
}
|
|
1250
1261
|
},
|
|
@@ -1285,7 +1296,7 @@ const chatgpt = {
|
|
|
1285
1296
|
+ (notificationDiv.isRight ? 'Right' : 'Left');
|
|
1286
1297
|
|
|
1287
1298
|
// Create/append/update notification style (if missing or outdated)
|
|
1288
|
-
const thisUpdated =
|
|
1299
|
+
const thisUpdated = 1735767823541 // timestamp of last edit for this file's `notifStyle`
|
|
1289
1300
|
let notifStyle = document.querySelector('#chatgpt-notif-style'); // try to select existing style
|
|
1290
1301
|
if (!notifStyle || parseInt(notifStyle.getAttribute('last-updated'), 10) < thisUpdated) { // if missing or outdated
|
|
1291
1302
|
if (!notifStyle) { // outright missing, create/id/attr/append it first
|
|
@@ -1295,10 +1306,13 @@ const chatgpt = {
|
|
|
1295
1306
|
}
|
|
1296
1307
|
notifStyle.innerText = ( // update prev/new style contents
|
|
1297
1308
|
'.chatgpt-notif {'
|
|
1309
|
+
+ 'font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC",'
|
|
1310
|
+
+ '"Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", sans-serif ;'
|
|
1298
1311
|
+ '.no-mobile-tap-outline { outline: none ; -webkit-tap-highlight-color: transparent }'
|
|
1299
1312
|
+ 'background-color: black ; padding: 10px 13px 10px 18px ; border-radius: 11px ; border: 1px solid #f5f5f7 ;' // bubble style
|
|
1300
1313
|
+ 'opacity: 0 ; position: fixed ; z-index: 9999 ; font-size: 1.8rem ; color: white ;' // visibility
|
|
1301
|
-
+ '
|
|
1314
|
+
+ 'user-select: none ; -webkit-user-select: none ; -moz-user-select: none ; -o-user-select: none ;'
|
|
1315
|
+
+ '-ms-user-select: none ;'
|
|
1302
1316
|
+ `transform: translateX(${ !notificationDiv.isRight ? '-' : '' }35px) ;` // init off-screen for transition fx
|
|
1303
1317
|
+ ( shadow ? ( 'box-shadow: -8px 13px 25px 0 ' + ( /\b(?:shadow|on)\b/i.test(shadow) ? 'gray' : shadow )) : '' ) + '}'
|
|
1304
1318
|
+ '.notif-close-btn { cursor: pointer ; float: right ; position: relative ; right: -4px ; margin-left: -3px ;'
|
|
@@ -1518,7 +1532,7 @@ const chatgpt = {
|
|
|
1518
1532
|
},
|
|
1519
1533
|
|
|
1520
1534
|
getFromDOM(pos) {
|
|
1521
|
-
const responseDivs = document.querySelectorAll('div[data-message-author-role=
|
|
1535
|
+
const responseDivs = document.querySelectorAll('div[data-message-author-role=assistant]'),
|
|
1522
1536
|
strPos = pos.toString().toLowerCase();
|
|
1523
1537
|
let response = '';
|
|
1524
1538
|
if (!responseDivs.length) return console.error('No conversation found!');
|
|
@@ -1810,7 +1824,7 @@ const chatgpt = {
|
|
|
1810
1824
|
|
|
1811
1825
|
|
|
1812
1826
|
// Fix for blank background on dropdown elements
|
|
1813
|
-
if (element == 'dropdown') newElement.style.backgroundColor = 'var(--gray-900, rgb(32,
|
|
1827
|
+
if (element == 'dropdown') newElement.style.backgroundColor = 'var(--gray-900, rgb(32,33,35))';
|
|
1814
1828
|
|
|
1815
1829
|
this.elements.push(newElement);
|
|
1816
1830
|
this.activateObserver();
|
|
@@ -1825,7 +1839,7 @@ const chatgpt = {
|
|
|
1825
1839
|
isOff() { return !this.isOn(); },
|
|
1826
1840
|
isOn() {
|
|
1827
1841
|
const sidebar = (() => {
|
|
1828
|
-
return chatgpt.sidebar.exists() ? document.querySelector('[class*=
|
|
1842
|
+
return chatgpt.sidebar.exists() ? document.querySelector('[class*=sidebar]') : null; })();
|
|
1829
1843
|
if (!sidebar) { console.error('Sidebar element not found!'); return false; }
|
|
1830
1844
|
else return chatgpt.browser.isMobile() ?
|
|
1831
1845
|
document.documentElement.style.overflow == 'hidden'
|
|
@@ -1833,7 +1847,7 @@ const chatgpt = {
|
|
|
1833
1847
|
},
|
|
1834
1848
|
|
|
1835
1849
|
toggle() {
|
|
1836
|
-
const sidebarToggle = document.querySelector('button[data-testid*=
|
|
1850
|
+
const sidebarToggle = document.querySelector('button[data-testid*=sidebar-button]');
|
|
1837
1851
|
if (!sidebarToggle) console.error('Sidebar toggle not found!');
|
|
1838
1852
|
sidebarToggle.click();
|
|
1839
1853
|
},
|
|
@@ -1865,31 +1879,31 @@ const chatgpt = {
|
|
|
1865
1879
|
return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest');
|
|
1866
1880
|
},
|
|
1867
1881
|
|
|
1868
|
-
speak(msg,
|
|
1869
|
-
//
|
|
1870
|
-
//
|
|
1871
|
-
//
|
|
1872
|
-
//
|
|
1873
|
-
|
|
1874
|
-
const { voice = 2, pitch = 2, speed = 1.1 } = options;
|
|
1882
|
+
speak(msg, { voice = 2, pitch = 2, speed = 1.1, onend } = {} ) { // eslint-disable-line no-unused-vars
|
|
1883
|
+
// Example call: chatgpt.speak(await chatgpt.getLastResponse(), { voice: 1, pitch: 2, speed: 3 })
|
|
1884
|
+
// - voice = index of voices available on user device
|
|
1885
|
+
// - pitch = float for pitch of speech from 0 to 2
|
|
1886
|
+
// - speed = float for rate of speech from 0.1 to 10
|
|
1887
|
+
// - onend = callback function invoked when speech finishes playing
|
|
1875
1888
|
|
|
1876
1889
|
// Validate args
|
|
1877
|
-
if (typeof msg
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
if (
|
|
1881
|
-
return console.error(`Invalid
|
|
1890
|
+
if (typeof msg != 'string') return console.error('Message must be a string!')
|
|
1891
|
+
const validOptionKeys = ['voice', 'pitch', 'speed', 'onend']
|
|
1892
|
+
for (const key in arguments[1]) {
|
|
1893
|
+
if (!validOptionKeys.includes(key))
|
|
1894
|
+
return console.error(`Invalid option '${key}'. Valid keys are: ${validOptionKeys}`)
|
|
1895
|
+
const val = arguments[1][key]
|
|
1896
|
+
if (key != 'onend' && typeof val != 'number' && !/^\d+$/.test(val))
|
|
1897
|
+
return console.error(`Invalid ${key} value '${val}'. Must be a number!`)
|
|
1898
|
+
else if (key == 'onend' && typeof val != 'function')
|
|
1899
|
+
return console.error(`Invalid ${key} value. Must be a function!`)
|
|
1882
1900
|
}
|
|
1883
1901
|
|
|
1884
|
-
try { // to speak msg
|
|
1885
|
-
const
|
|
1886
|
-
|
|
1887
|
-
utterance
|
|
1888
|
-
|
|
1889
|
-
utterance.pitch = pitch;
|
|
1890
|
-
utterance.rate = speed;
|
|
1891
|
-
speechSynthesis.speak(utterance);
|
|
1892
|
-
} catch (err) { console.error( err); }
|
|
1902
|
+
try { // to speak msg
|
|
1903
|
+
const utterance = new SpeechSynthesisUtterance()
|
|
1904
|
+
Object.assign(utterance, { text: msg, ...arguments[1], voice: speechSynthesis.getVoices()[voice] })
|
|
1905
|
+
speechSynthesis.speak(utterance)
|
|
1906
|
+
} catch (err) { console.error(err) }
|
|
1893
1907
|
},
|
|
1894
1908
|
|
|
1895
1909
|
async summarize(text) {
|
|
@@ -1943,8 +1957,8 @@ const cjsBtnActions = ['click', 'get'], cjsTargetTypes = [ 'button', 'link', 'di
|
|
|
1943
1957
|
for (const btnAction of cjsBtnActions) {
|
|
1944
1958
|
chatgpt[btnAction + 'Button'] = function handleButton(buttonIdentifier) {
|
|
1945
1959
|
const button = /^[.#]/.test(buttonIdentifier) ? document.querySelector(buttonIdentifier)
|
|
1946
|
-
: /send/i.test(buttonIdentifier) ? document.querySelector('form button[class*=
|
|
1947
|
-
: /scroll/i.test(buttonIdentifier) ? document.querySelector('button[class*=
|
|
1960
|
+
: /send/i.test(buttonIdentifier) ? document.querySelector('form button[class*=bottom]')
|
|
1961
|
+
: /scroll/i.test(buttonIdentifier) ? document.querySelector('button[class*=cursor]')
|
|
1948
1962
|
: (function() { // get via text content
|
|
1949
1963
|
for (const button of document.querySelectorAll('button')) { // try buttons
|
|
1950
1964
|
if (button.textContent.toLowerCase().includes(buttonIdentifier.toLowerCase())) {
|
|
@@ -2019,8 +2033,9 @@ const cjsFuncSynonyms = [
|
|
|
2019
2033
|
['render', 'parse'],
|
|
2020
2034
|
['reply', 'response'],
|
|
2021
2035
|
['sentiment', 'attitude', 'emotion', 'feeling', 'opinion', 'perception'],
|
|
2022
|
-
['speak', 'say', 'speech', 'talk', 'tts'],
|
|
2036
|
+
['speak', 'play', 'say', 'speech', 'talk', 'tts'],
|
|
2023
2037
|
['summarize', 'tldr'],
|
|
2038
|
+
['typing', 'generating'],
|
|
2024
2039
|
['unminify', 'beautify', 'prettify', 'prettyPrint']
|
|
2025
2040
|
];
|
|
2026
2041
|
const camelCaser = (words) => {
|
|
@@ -1,11 +1,51 @@
|
|
|
1
1
|
window.dom = {
|
|
2
|
+
|
|
3
|
+
imports: {
|
|
4
|
+
import(deps) { // { config, env }
|
|
5
|
+
for (const depName in deps) this[depName] = deps[depName] }
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
addRisingParticles(targetNode, { lightScheme = 'gray', darkScheme = 'white' } = {}) {
|
|
9
|
+
// Requires https://assets.aiwebextensions.com/styles/rising-particles/dist/<lightScheme|darkScheme>.min.css
|
|
10
|
+
|
|
11
|
+
if (targetNode.querySelector('[id*=particles]')) return
|
|
12
|
+
const particlesDivsWrapper = document.createElement('div')
|
|
13
|
+
particlesDivsWrapper.style.cssText = (
|
|
14
|
+
'position: absolute ; top: 0 ; left: 0 ;' // hug targetNode's top-left corner
|
|
15
|
+
+ 'height: 100% ; width: 100% ; border-radius: 15px ; overflow: clip ;' // bound innards exactly by targetNode
|
|
16
|
+
+ 'z-index: -1' ); // allow interactive elems to be clicked
|
|
17
|
+
['sm', 'med', 'lg'].forEach(particleSize => {
|
|
18
|
+
const particlesDiv = document.createElement('div')
|
|
19
|
+
particlesDiv.id = this.imports.config?.bgAnimationsDisabled ? `particles-${particleSize}-off`
|
|
20
|
+
: `${( this.imports.env?.ui?.scheme || this.imports.env?.ui?.app?.scheme ) == 'dark' ? darkScheme
|
|
21
|
+
: lightScheme }-particles-${particleSize}`
|
|
22
|
+
particlesDivsWrapper.append(particlesDiv)
|
|
23
|
+
})
|
|
24
|
+
targetNode.prepend(particlesDivsWrapper)
|
|
25
|
+
},
|
|
26
|
+
|
|
2
27
|
create: {
|
|
28
|
+
anchor(linkHref, displayContent, attrs = {}) {
|
|
29
|
+
const anchor = document.createElement('a'),
|
|
30
|
+
defaultAttrs = { href: linkHref, target: '_blank', rel: 'noopener' },
|
|
31
|
+
finalAttrs = { ...defaultAttrs, ...attrs }
|
|
32
|
+
Object.entries(finalAttrs).forEach(([attr, value]) => anchor.setAttribute(attr, value))
|
|
33
|
+
if (displayContent) anchor.append(displayContent)
|
|
34
|
+
return anchor
|
|
35
|
+
},
|
|
36
|
+
|
|
3
37
|
elem(elemType, attrs = {}) {
|
|
4
38
|
const elem = document.createElement(elemType)
|
|
5
39
|
for (const attr in attrs) elem.setAttribute(attr, attrs[attr])
|
|
6
40
|
return elem
|
|
7
41
|
},
|
|
8
42
|
|
|
43
|
+
style(content) {
|
|
44
|
+
const style = document.createElement('style')
|
|
45
|
+
if (content) style.innerText = content
|
|
46
|
+
return style
|
|
47
|
+
},
|
|
48
|
+
|
|
9
49
|
svgElem(type, attrs) {
|
|
10
50
|
const elem = document.createElementNS('http://www.w3.org/2000/svg', type)
|
|
11
51
|
for (const attr in attrs) elem.setAttributeNS(null, attr, attrs[attr])
|
|
@@ -13,16 +53,35 @@ window.dom = {
|
|
|
13
53
|
}
|
|
14
54
|
},
|
|
15
55
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
56
|
+
cssSelectorize(classList) {
|
|
57
|
+
return classList.toString()
|
|
58
|
+
.replace(/([:[\]\\])/g, '\\$1') // escape special chars :[]\
|
|
59
|
+
.replace(/^| /g, '.') // prefix w/ dot, convert spaces to dots
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
get: {
|
|
63
|
+
computedWidth(...elems) { // including margins
|
|
64
|
+
let totalWidth = 0
|
|
65
|
+
elems.map(arg => arg instanceof NodeList ? [...arg] : arg).flat().forEach(elem => {
|
|
66
|
+
if (!(elem instanceof Element)) return
|
|
67
|
+
const elemStyle = getComputedStyle(elem) ; if (elemStyle.display == 'none') return
|
|
68
|
+
totalWidth += elem.getBoundingClientRect().width + parseFloat(elemStyle.marginLeft)
|
|
69
|
+
+ parseFloat(elemStyle.marginRight)
|
|
70
|
+
})
|
|
71
|
+
return totalWidth
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
loadedElem(selector, timeout = null) {
|
|
75
|
+
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(null), timeout)) : null
|
|
76
|
+
const isLoadedPromise = new Promise(resolve => {
|
|
77
|
+
const elem = document.querySelector(selector)
|
|
78
|
+
if (elem) resolve(elem)
|
|
79
|
+
else new MutationObserver((_, obs) => {
|
|
80
|
+
const elem = document.querySelector(selector)
|
|
81
|
+
if (elem) { obs.disconnect() ; resolve(elem) }
|
|
82
|
+
}).observe(document.documentElement, { childList: true, subtree: true })
|
|
83
|
+
})
|
|
84
|
+
return ( timeoutPromise ? Promise.race([isLoadedPromise, timeoutPromise]) : isLoadedPromise )
|
|
85
|
+
}
|
|
27
86
|
}
|
|
28
87
|
};
|
|
@@ -6,6 +6,7 @@ window.settings = {
|
|
|
6
6
|
// Add settings options as keys, with each key's value being an object that includes:
|
|
7
7
|
// - 'type': the control type (e.g. 'toggle' or 'prompt')
|
|
8
8
|
// - 'label': a descriptive label
|
|
9
|
+
// - 'defaultVal' (optional): default value of setting (true for toggles if unspecified, false otherwise)
|
|
9
10
|
// - 'symbol' (optional): for icon display (e.g. ⌚)
|
|
10
11
|
// NOTE: Toggles are disabled by default unless key name contains 'disabled' or 'hidden' (case insensitive)
|
|
11
12
|
// NOTE: Controls are displayed in top-to-bottom order
|
|
@@ -14,17 +15,15 @@ window.settings = {
|
|
|
14
15
|
// replyLanguage: { type: 'prompt', symbol: '🌐', label: 'Reply Language' }
|
|
15
16
|
},
|
|
16
17
|
|
|
17
|
-
load() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
window.config[key] = result[key] || false ; resolve()
|
|
24
|
-
}))))},
|
|
18
|
+
load(...keys) {
|
|
19
|
+
return Promise.all(keys.flat().map(async key => // resolve promise when all keys load
|
|
20
|
+
window.config[key] = (await chrome.storage.sync.get(key))[key]
|
|
21
|
+
?? this.controls[key]?.defaultVal ?? this.controls[key]?.type == 'toggle'
|
|
22
|
+
))
|
|
23
|
+
},
|
|
25
24
|
|
|
26
25
|
save(key, val) {
|
|
27
26
|
chrome.storage.sync.set({ [key]: val }) // save to Chrome extension storage
|
|
28
27
|
window.config[key] = val // save to memory
|
|
29
28
|
}
|
|
30
|
-
}
|
|
29
|
+
};
|
|
@@ -3,7 +3,7 @@
|
|
|
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": "
|
|
6
|
+
"version": "2025.2.1",
|
|
7
7
|
"author": "KudoAI",
|
|
8
8
|
"homepage_url": "https://github.com/KudoAI/chatgpt.js-chrome-starter",
|
|
9
9
|
"icons": {
|
|
@@ -18,10 +18,7 @@
|
|
|
18
18
|
"matches": [ "<all_urls>" ],
|
|
19
19
|
"resources": [ "components/modals.js", "lib/chatgpt.js", "lib/dom.js", "lib/settings.js" ]
|
|
20
20
|
}],
|
|
21
|
-
"content_scripts": [{
|
|
22
|
-
"matches": [ "https://chatgpt.com/*" ],
|
|
23
|
-
"js": [ "content.js" ]
|
|
24
|
-
}],
|
|
21
|
+
"content_scripts": [{ "matches": [ "https://chatgpt.com/*" ], "js": [ "content.js" ] }],
|
|
25
22
|
"background": { "service_worker": "service-worker.js" },
|
|
26
23
|
"minimum_chrome_version": "88"
|
|
27
24
|
}
|