@kudoai/chatgpt.js 3.6.0 → 3.6.2
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 +8 -15
- package/chatgpt.js +70 -52
- package/dist/chatgpt.min.js +27 -3
- package/docs/README.md +8 -8
- package/docs/USERGUIDE.md +3 -3
- package/package.json +7 -6
- package/starters/chrome/extension/components/icons.js +2 -5
- package/starters/chrome/extension/components/modals.js +81 -84
- package/starters/chrome/extension/content.js +2 -2
- package/starters/chrome/extension/lib/chatgpt.js +70 -52
- package/starters/chrome/extension/lib/dom.js +37 -15
- package/starters/chrome/extension/manifest.json +1 -1
- package/starters/chrome/extension/popup/controller.js +1 -1
- package/starters/greasemonkey/chatgpt.js-greasemonkey-starter.user.js +2 -2
|
@@ -1,12 +1,63 @@
|
|
|
1
|
-
// Requires lib/chatgpt.js + lib/dom.js
|
|
1
|
+
// Requires lib/chatgpt.js + lib/dom.js + app + env
|
|
2
2
|
|
|
3
3
|
window.modals = {
|
|
4
|
+
import(deps) { Object.assign(this.imports = this.imports || {}, deps) },
|
|
5
|
+
|
|
4
6
|
stack: [], // of types of undismissed modals
|
|
5
7
|
get class() { return `${this.imports.app.cssPrefix}-modal` },
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
about() {
|
|
10
|
+
|
|
11
|
+
// Show modal
|
|
12
|
+
const aboutModal = this.alert(
|
|
13
|
+
`${this.imports.app.symbol} ${chrome.runtime.getManifest().name}`, // title
|
|
14
|
+
'🧠 Author: ' // msg
|
|
15
|
+
+ `<a href="${this.imports.app.author.url}">${this.imports.app.author.name}</a> `
|
|
16
|
+
+ `& <a href="${this.imports.app.urls.contributors}">contributors</a>\n`
|
|
17
|
+
+ `🏷️ Version: <span class="about-em">${this.imports.app.version}</span>\n`
|
|
18
|
+
+ '📜 Open source code: '
|
|
19
|
+
+ `<a href="${this.imports.app.urls.gitHub}" target="_blank" rel="nopener">`
|
|
20
|
+
+ this.imports.app.urls.gitHub + '</a>\n'
|
|
21
|
+
+ '⚡ Powered by: '
|
|
22
|
+
+ `<a href="${this.imports.app.urls.chatgptJS}" target="_blank" rel="noopener">chatgpt.js</a>`,
|
|
23
|
+
[ function getSupport(){}, function rateUs(){}, function moreAiExtensions(){} ], // button labels
|
|
24
|
+
'', 656 // modal width
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
// Format text
|
|
28
|
+
aboutModal.querySelector('h2').style.cssText = (
|
|
29
|
+
'text-align: center ; font-size: 51px ; line-height: 46px ; padding: 15px 0' )
|
|
30
|
+
aboutModal.querySelector('p').style.cssText = (
|
|
31
|
+
'text-align: center ; overflow-wrap: anywhere ;'
|
|
32
|
+
+ `margin: ${ this.imports.env.browser.isPortrait ? '6px 0 -16px' : '3px 0 0' }` )
|
|
33
|
+
|
|
34
|
+
// Hack buttons
|
|
35
|
+
aboutModal.querySelector('.modal-buttons').style.justifyContent = 'center'
|
|
36
|
+
aboutModal.querySelectorAll('button').forEach(btn => {
|
|
37
|
+
btn.style.cssText = 'height: 55px ; min-width: 136px ; text-align: center'
|
|
38
|
+
|
|
39
|
+
// Replace buttons w/ clones that don't dismiss modal
|
|
40
|
+
const btnClone = btn.cloneNode(true)
|
|
41
|
+
btn.parentNode.replaceChild(btnClone, btn) ; btn = btnClone
|
|
42
|
+
btn.onclick = () => this.safeWinOpen(
|
|
43
|
+
btn.textContent == 'Get Support' ? `${modals.imports.app.urls.gitHub}/issues`
|
|
44
|
+
: btn.textContent == 'Rate Us' ? `${modals.imports.app.urls.gitHub}/discussions`
|
|
45
|
+
: modals.imports.app.urls.relatedExtensions
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
// Prepend emoji
|
|
49
|
+
if (/support/i.test(btn.textContent))
|
|
50
|
+
btn.textContent = '🧠 ' + btn.textContent
|
|
51
|
+
else if (/rate/i.test(btn.textContent))
|
|
52
|
+
btn.textContent = '⭐ ' + btn.textContent
|
|
53
|
+
else if (/extensions/i.test(btn.textContent))
|
|
54
|
+
btn.textContent = '🧠 ' + btn.textContent
|
|
55
|
+
|
|
56
|
+
// Hide Dismiss button
|
|
57
|
+
else btn.style.display = 'none'
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
return aboutModal
|
|
10
61
|
},
|
|
11
62
|
|
|
12
63
|
alert(title = '', msg = '', btns = '', checkbox = '', width = '') { // generic one from chatgpt.alert()
|
|
@@ -16,6 +67,30 @@ window.modals = {
|
|
|
16
67
|
return alert
|
|
17
68
|
},
|
|
18
69
|
|
|
70
|
+
init(modal) {
|
|
71
|
+
if (!modal) return // to support non-div this.open()s
|
|
72
|
+
if (!this.styles) this.stylize() // to init/append stylesheet
|
|
73
|
+
modal.classList.add('no-user-select', this.class) ; modal.parentNode.classList.add(`${this.class}-bg`)
|
|
74
|
+
dom.addRisingParticles(modal)
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
observeRemoval(modal, modalType) { // to maintain stack for proper nav
|
|
78
|
+
const modalBG = modal.parentNode
|
|
79
|
+
new MutationObserver(([mutation], obs) => {
|
|
80
|
+
mutation.removedNodes.forEach(removedNode => { if (removedNode == modalBG) {
|
|
81
|
+
if (this.stack[0] == modalType) { // new modal not launched, implement nav back logic
|
|
82
|
+
this.stack.shift() // remove this modal type from stack 1st
|
|
83
|
+
const prevModalType = this.stack[0]
|
|
84
|
+
if (prevModalType) { // open it
|
|
85
|
+
this.stack.shift() // remove type from stack since re-added on open
|
|
86
|
+
this.open(prevModalType)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
obs.disconnect()
|
|
90
|
+
}})
|
|
91
|
+
}).observe(modalBG.parentNode, { childList: true, subtree: true })
|
|
92
|
+
},
|
|
93
|
+
|
|
19
94
|
open(modalType) {
|
|
20
95
|
const modal = this[modalType]() // show modal
|
|
21
96
|
this.stack.unshift(modalType) // add to stack
|
|
@@ -23,12 +98,7 @@ window.modals = {
|
|
|
23
98
|
this.observeRemoval(modal, modalType) // to maintain stack for proper nav
|
|
24
99
|
},
|
|
25
100
|
|
|
26
|
-
|
|
27
|
-
if (!modal) return // to support non-div this.open()s
|
|
28
|
-
if (!this.styles) this.stylize() // to init/append stylesheet
|
|
29
|
-
modal.classList.add('no-user-select', this.class) ; modal.parentNode.classList.add(`${this.class}-bg`)
|
|
30
|
-
dom.addRisingParticles(modal)
|
|
31
|
-
},
|
|
101
|
+
safeWinOpen(url) { open(url, '_blank', 'noopener') }, // to prevent backdoor vulnerabilities
|
|
32
102
|
|
|
33
103
|
stylize() {
|
|
34
104
|
if (!this.styles) {
|
|
@@ -77,78 +147,5 @@ window.modals = {
|
|
|
77
147
|
`.${this.class} .modal-buttons { margin-left: -13px !important }` : '' )
|
|
78
148
|
+ `.about-em { color: ${ this.imports.env.ui.scheme == 'dark' ? 'white' : 'green' } !important }`
|
|
79
149
|
)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
observeRemoval(modal, modalType) { // to maintain stack for proper nav
|
|
83
|
-
const modalBG = modal.parentNode
|
|
84
|
-
new MutationObserver(([mutation], obs) => {
|
|
85
|
-
mutation.removedNodes.forEach(removedNode => { if (removedNode == modalBG) {
|
|
86
|
-
if (this.stack[0] == modalType) { // new modal not launched, implement nav back logic
|
|
87
|
-
this.stack.shift() // remove this modal type from stack 1st
|
|
88
|
-
const prevModalType = this.stack[0]
|
|
89
|
-
if (prevModalType) { // open it
|
|
90
|
-
this.stack.shift() // remove type from stack since re-added on open
|
|
91
|
-
this.open(prevModalType)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
obs.disconnect()
|
|
95
|
-
}})
|
|
96
|
-
}).observe(modalBG.parentNode, { childList: true, subtree: true })
|
|
97
|
-
},
|
|
98
|
-
|
|
99
|
-
about() {
|
|
100
|
-
|
|
101
|
-
// Show modal
|
|
102
|
-
const aboutModal = this.alert(
|
|
103
|
-
`${this.imports.app.symbol} ${chrome.runtime.getManifest().name}`, // title
|
|
104
|
-
'🧠 Author: ' // msg
|
|
105
|
-
+ `<a href="${this.imports.app.author.url}">${this.imports.app.author.name}</a> `
|
|
106
|
-
+ `& <a href="${this.imports.app.urls.contributors}">contributors</a>\n`
|
|
107
|
-
+ `🏷️ Version: <span class="about-em">${this.imports.app.version}</span>\n`
|
|
108
|
-
+ '📜 Open source code: '
|
|
109
|
-
+ `<a href="${this.imports.app.urls.gitHub}" target="_blank" rel="nopener">`
|
|
110
|
-
+ this.imports.app.urls.gitHub + '</a>\n'
|
|
111
|
-
+ '⚡ Powered by: '
|
|
112
|
-
+ `<a href="${this.imports.app.urls.chatgptJS}" target="_blank" rel="noopener">chatgpt.js</a>`,
|
|
113
|
-
[ function getSupport(){}, function rateUs(){}, function moreAiExtensions(){} ], // button labels
|
|
114
|
-
'', 656 // modal width
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
// Format text
|
|
118
|
-
aboutModal.querySelector('h2').style.cssText = (
|
|
119
|
-
'text-align: center ; font-size: 51px ; line-height: 46px ; padding: 15px 0' )
|
|
120
|
-
aboutModal.querySelector('p').style.cssText = (
|
|
121
|
-
'text-align: center ; overflow-wrap: anywhere ;'
|
|
122
|
-
+ `margin: ${ this.imports.env.browser.isPortrait ? '6px 0 -16px' : '3px 0 0' }` )
|
|
123
|
-
|
|
124
|
-
// Hack buttons
|
|
125
|
-
aboutModal.querySelector('.modal-buttons').style.justifyContent = 'center'
|
|
126
|
-
aboutModal.querySelectorAll('button').forEach(btn => {
|
|
127
|
-
btn.style.cssText = 'height: 55px ; min-width: 136px ; text-align: center'
|
|
128
|
-
|
|
129
|
-
// Replace buttons w/ clones that don't dismiss modal
|
|
130
|
-
const btnClone = btn.cloneNode(true)
|
|
131
|
-
btn.parentNode.replaceChild(btnClone, btn) ; btn = btnClone
|
|
132
|
-
btn.onclick = () => this.safeWinOpen(
|
|
133
|
-
btn.textContent == 'Get Support' ? `${modals.imports.app.urls.gitHub}/issues`
|
|
134
|
-
: btn.textContent == 'Rate Us' ? `${modals.imports.app.urls.gitHub}/discussions`
|
|
135
|
-
: modals.imports.app.urls.relatedExtensions
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
// Prepend emoji
|
|
139
|
-
if (/support/i.test(btn.textContent))
|
|
140
|
-
btn.textContent = '🧠 ' + btn.textContent
|
|
141
|
-
else if (/rate/i.test(btn.textContent))
|
|
142
|
-
btn.textContent = '⭐ ' + btn.textContent
|
|
143
|
-
else if (/extensions/i.test(btn.textContent))
|
|
144
|
-
btn.textContent = '🧠 ' + btn.textContent
|
|
145
|
-
|
|
146
|
-
// Hide Dismiss button
|
|
147
|
-
else btn.style.display = 'none'
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
return aboutModal
|
|
151
|
-
},
|
|
152
|
-
|
|
153
|
-
safeWinOpen(url) { open(url, '_blank', 'noopener') } // to prevent backdoor vulnerabilities
|
|
150
|
+
}
|
|
154
151
|
};
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
const { app } = await chrome.storage.sync.get('app')
|
|
16
16
|
|
|
17
17
|
// Export DEPENDENCIES to imported resources
|
|
18
|
-
dom.
|
|
19
|
-
modals.
|
|
18
|
+
dom.import({ env }) // for env.ui.scheme
|
|
19
|
+
modals.import({ app, env }) // for app data + env.<browser|ui> flags
|
|
20
20
|
|
|
21
21
|
// Add CHROME MSG listener
|
|
22
22
|
chrome.runtime.onMessage.addListener(req => { // from service-worker.js + popup/index.html
|
|
@@ -97,7 +97,8 @@ const chatgpt = {
|
|
|
97
97
|
if (event.button != 0) return // prevent non-left-click drag
|
|
98
98
|
if (getComputedStyle(event.target).cursor == 'pointer') return // prevent drag on interactive elems
|
|
99
99
|
chatgpt.draggableElem = event.currentTarget
|
|
100
|
-
chatgpt.draggableElem.style
|
|
100
|
+
Object.assign(chatgpt.draggableElem.style, {
|
|
101
|
+
cursor: 'grabbing', transition: '0.1s', willChange: 'transform', transform: 'scale(1.05)' })
|
|
101
102
|
event.preventDefault(); // prevent sub-elems like icons being draggable
|
|
102
103
|
['mousemove', 'mouseup'].forEach(eventType =>
|
|
103
104
|
document.addEventListener(eventType, handlers.drag[eventType]))
|
|
@@ -114,7 +115,8 @@ const chatgpt = {
|
|
|
114
115
|
},
|
|
115
116
|
|
|
116
117
|
mouseup() { // remove listeners, reset chatgpt.draggableElem
|
|
117
|
-
chatgpt.draggableElem.style
|
|
118
|
+
Object.assign(chatgpt.draggableElem.style, {
|
|
119
|
+
cursor: 'inherit', transition: 'inherit', willChange: 'auto', transform: 'scale(1)' });
|
|
118
120
|
['mousemove', 'mouseup'].forEach(eventType =>
|
|
119
121
|
document.removeEventListener(eventType, handlers.drag[eventType]))
|
|
120
122
|
chatgpt.draggableElem = null
|
|
@@ -165,19 +167,22 @@ const chatgpt = {
|
|
|
165
167
|
+ `background-color: ${ scheme == 'dark' ? 'black' : 'white' };`
|
|
166
168
|
+ 'transform: translateX(-3px) translateY(7px) ;' // offset to move-in from
|
|
167
169
|
+ 'max-width: 75vw ; word-wrap: break-word ; border-radius: 15px ;'
|
|
168
|
-
+ 'padding: 20px ; margin: 12px 23px ;
|
|
170
|
+
+ 'padding: 20px ; margin: 12px 23px ;'
|
|
171
|
+
+ `--shadow: 0 30px 60px rgba(0,0,0,0.12) ; box-shadow: var(--shadow) ;
|
|
172
|
+
-webkit-box-shadow: var(--shadow) ; -moz-box-shadow: var(--shadow) ;`
|
|
169
173
|
+ 'user-select: none ; -webkit-user-select: none ; -moz-user-select: none ; -o-user-select: none ;'
|
|
170
174
|
+ '-ms-user-select: none ;'
|
|
171
175
|
+ 'transition: var(--transition) ;' // for fade-in + move-in
|
|
172
176
|
+ '-webkit-transition: var(--transition) ; -moz-transition: var(--transition) ;'
|
|
173
177
|
+ '-o-transition: var(--transition) ; -ms-transition: var(--transition) }'
|
|
174
|
-
+
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
178
|
+
+ `.chatgpt-modal h2 { margin-bottom: 9px }
|
|
179
|
+
.chatgpt-modal a { color: ${ scheme == 'dark' ? '#00cfff' : '#1e9ebb' }}
|
|
180
|
+
.chatgpt-modal a:hover { text-decoration: underline }
|
|
181
|
+
.chatgpt-modal.animated > div {
|
|
182
|
+
z-index: 13456 ; opacity: 0.98 ; transform: translateX(0) translateY(0) }
|
|
183
|
+
@keyframes alert-zoom-fade-out {
|
|
184
|
+
0% { opacity: 1 } 50% { opacity: 0.25 ; transform: scale(1.05) }
|
|
185
|
+
100% { opacity: 0 ; transform: scale(1.35) }}`
|
|
181
186
|
|
|
182
187
|
// Button styles
|
|
183
188
|
+ '.modal-buttons { display: flex ; justify-content: flex-end ; margin: 20px -5px -3px 0 ;'
|
|
@@ -190,9 +195,11 @@ const chatgpt = {
|
|
|
190
195
|
+ `border: 1px solid ${ scheme == 'dark' ? 'white' : 'black' } ;`
|
|
191
196
|
+ `background: ${ scheme == 'dark' ? 'white' : 'black' } ;`
|
|
192
197
|
+ `color: ${ scheme == 'dark' ? 'black' : 'white' }}`
|
|
193
|
-
+
|
|
194
|
-
|
|
195
|
-
|
|
198
|
+
+ `.chatgpt-modal button:hover {
|
|
199
|
+
color: #3d5d71 ; border-color: #6d9cb9 ;
|
|
200
|
+
background-color: ${ scheme == 'dark' ? '#00cfff' : '#9cdaff' };
|
|
201
|
+
--shadow: 2px 1px ${ scheme == 'dark' ? '54px #00cfff' : '30px #9cdaff' };
|
|
202
|
+
box-shadow: var(--shadow) ; box-shadow: var(--shadow) ; box-shadow: var(--shadow) }`
|
|
196
203
|
+ '.modal-close-btn {'
|
|
197
204
|
+ 'cursor: pointer ; width: 29px ; height: 29px ; border-radius: 17px ;'
|
|
198
205
|
+ 'float: right ; position: relative ; right: -6px ; top: -5px }'
|
|
@@ -200,17 +207,18 @@ const chatgpt = {
|
|
|
200
207
|
+ `.modal-close-btn:hover { background-color: #f2f2f2${ scheme == 'dark' ? '00' : '' }}`
|
|
201
208
|
|
|
202
209
|
// Checkbox styles
|
|
203
|
-
+
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
210
|
+
+ `.chatgpt-modal .checkbox-group { margin-top: 15px }
|
|
211
|
+
.chatgpt-modal .checkbox-group label {
|
|
212
|
+
font-size: .7rem ; margin: -.04rem 0 0px .3rem
|
|
213
|
+
color: ${ scheme == 'dark' ? '#e1e1e1' : '#1e1e1e' }}
|
|
214
|
+
.chatgpt-modal input[type=checkbox] { transform: scale(0.7) ;
|
|
215
|
+
border: 1px solid ${ scheme == 'dark' ? 'white' : 'black' }}
|
|
216
|
+
.chatgpt-modal input[type=checkbox]:checked {
|
|
217
|
+
border: 1px solid ${ scheme == 'dark' ? 'white' : 'black' } ;
|
|
218
|
+
background-color: black ; position: inherit }
|
|
219
|
+
.chatgpt-modal input[type=checkbox]:focus {
|
|
220
|
+
outline: none ; box-shadow: none ; -webkit-box-shadow: none ; -moz-box-shadow: none }`
|
|
221
|
+
)
|
|
214
222
|
}
|
|
215
223
|
|
|
216
224
|
// Insert text into elements
|
|
@@ -277,7 +285,7 @@ const chatgpt = {
|
|
|
277
285
|
closeSVG.append(closeSVGpath); closeBtn.append(closeSVG);
|
|
278
286
|
|
|
279
287
|
// Assemble/append div
|
|
280
|
-
const modalElems = [closeBtn, modalTitle, modalMessage,
|
|
288
|
+
const modalElems = [closeBtn, modalTitle, modalMessage, checkboxDiv, modalButtons ];
|
|
281
289
|
modalElems.forEach((elem) => { modal.append(elem); });
|
|
282
290
|
modal.style.width = `${ width || 458 }px`;
|
|
283
291
|
modalContainer.append(modal); document.body.append(modalContainer);
|
|
@@ -942,7 +950,12 @@ const chatgpt = {
|
|
|
942
950
|
getLastResponse() { return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'); },
|
|
943
951
|
|
|
944
952
|
getNewChatButton() {
|
|
945
|
-
return document.querySelector(
|
|
953
|
+
return document.querySelector(
|
|
954
|
+
'button[data-testid*=new-chat-button],' // sidebar button (when logged in)
|
|
955
|
+
+ 'button:has([d^="M3.06957"]),' // Cycle Arrows icon (Temp chat mode)
|
|
956
|
+
+ 'button:has([d^="M15.6729"])' // Pencil icon (recorded chat mode)
|
|
957
|
+
)
|
|
958
|
+
},
|
|
946
959
|
|
|
947
960
|
getNewChatLink() { return document.querySelector('nav a[href="/"]'); },
|
|
948
961
|
getRegenerateButton() { return document.querySelector('button:has([d^="M3.06957"])'); },
|
|
@@ -978,14 +991,14 @@ const chatgpt = {
|
|
|
978
991
|
|
|
979
992
|
history: {
|
|
980
993
|
async isLoaded(timeout = null) {
|
|
981
|
-
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(false), timeout)) : null
|
|
994
|
+
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(false), timeout)) : null
|
|
982
995
|
const isLoadedPromise = new Promise(resolve => {
|
|
983
|
-
if (document.querySelector('nav')) resolve(true)
|
|
996
|
+
if (document.querySelector('nav')) resolve(true)
|
|
984
997
|
else new MutationObserver((_, obs) => {
|
|
985
|
-
if (document.querySelector('nav')) { obs.disconnect(); resolve(true)
|
|
986
|
-
}).observe(document.
|
|
987
|
-
})
|
|
988
|
-
return await ( timeoutPromise ? Promise.race([isLoadedPromise, timeoutPromise]) : isLoadedPromise )
|
|
998
|
+
if (document.querySelector('nav')) { obs.disconnect() ; resolve(true) }
|
|
999
|
+
}).observe(document.documentElement, { childList: true, subtree: true })
|
|
1000
|
+
})
|
|
1001
|
+
return await ( timeoutPromise ? Promise.race([isLoadedPromise, timeoutPromise]) : isLoadedPromise )
|
|
989
1002
|
}
|
|
990
1003
|
},
|
|
991
1004
|
|
|
@@ -1146,14 +1159,14 @@ const chatgpt = {
|
|
|
1146
1159
|
},
|
|
1147
1160
|
|
|
1148
1161
|
async isLoaded(timeout = null) {
|
|
1149
|
-
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(false), timeout)) : null
|
|
1162
|
+
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(false), timeout)) : null
|
|
1150
1163
|
const isLoadedPromise = new Promise(resolve => {
|
|
1151
|
-
if (chatgpt.getNewChatBtn()) resolve(true)
|
|
1164
|
+
if (chatgpt.getNewChatBtn()) resolve(true)
|
|
1152
1165
|
else new MutationObserver((_, obs) => {
|
|
1153
|
-
if (chatgpt.getNewChatBtn()) { obs.disconnect(); resolve(true)
|
|
1154
|
-
}).observe(document.
|
|
1155
|
-
})
|
|
1156
|
-
return await ( timeoutPromise ? Promise.race([isLoadedPromise, timeoutPromise]) : isLoadedPromise )
|
|
1166
|
+
if (chatgpt.getNewChatBtn()) { obs.disconnect() ; resolve(true) }
|
|
1167
|
+
}).observe(document.documentElement, { childList: true, subtree: true })
|
|
1168
|
+
})
|
|
1169
|
+
return await ( timeoutPromise ? Promise.race([isLoadedPromise, timeoutPromise]) : isLoadedPromise )
|
|
1157
1170
|
},
|
|
1158
1171
|
|
|
1159
1172
|
isLightMode() { return document.documentElement.classList.toString().includes('light'); },
|
|
@@ -1309,13 +1322,18 @@ const chatgpt = {
|
|
|
1309
1322
|
+ 'font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC",'
|
|
1310
1323
|
+ '"Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", sans-serif ;'
|
|
1311
1324
|
+ '.no-mobile-tap-outline { outline: none ; -webkit-tap-highlight-color: transparent }'
|
|
1312
|
-
+ 'background-color: black ; padding: 10px 13px 10px 18px ;
|
|
1325
|
+
+ 'background-color: black ; padding: 10px 13px 10px 18px ;' // bubble style
|
|
1326
|
+
+ 'border-radius: 11px ; border: 1px solid #f5f5f7 ;'
|
|
1313
1327
|
+ 'opacity: 0 ; position: fixed ; z-index: 9999 ; font-size: 1.8rem ; color: white ;' // visibility
|
|
1314
1328
|
+ 'user-select: none ; -webkit-user-select: none ; -moz-user-select: none ; -o-user-select: none ;'
|
|
1315
1329
|
+ '-ms-user-select: none ;'
|
|
1316
|
-
+ `transform: translateX(${
|
|
1317
|
-
|
|
1318
|
-
|
|
1330
|
+
+ `transform: translateX(${ // init off-screen for transition fx
|
|
1331
|
+
!notificationDiv.isRight ? '-' : '' }35px) ;`
|
|
1332
|
+
+ ( shadow ? `--shadow: -8px 13px 25px 0 ${ /\b(?:shadow|on)\b/i.test(shadow) ? 'gray' : shadow };
|
|
1333
|
+
box-shadow: var(--shadow) ; -webkit-box-shadow: var(--shadow) ; -moz-box-shadow: var(--shadow)`
|
|
1334
|
+
: '' ) + '}'
|
|
1335
|
+
+ `.notif-close-btn {
|
|
1336
|
+
cursor: pointer ; float: right ; position: relative ; right: -4px ; margin-left: -3px ;`
|
|
1319
1337
|
+ 'display: grid }' // top-align for non-OpenAI sites
|
|
1320
1338
|
+ '@keyframes notif-zoom-fade-out { 0% { opacity: 1 ; transform: scale(1) }' // transition out keyframes
|
|
1321
1339
|
+ '15% { opacity: 0.35 ; transform: rotateX(-27deg) scale(1.05) }'
|
|
@@ -1853,15 +1871,15 @@ const chatgpt = {
|
|
|
1853
1871
|
},
|
|
1854
1872
|
|
|
1855
1873
|
async isLoaded(timeout = 5000) {
|
|
1856
|
-
await chatgpt.isLoaded()
|
|
1857
|
-
const timeoutPromise = new Promise(resolve => setTimeout(() =>
|
|
1874
|
+
await chatgpt.isLoaded()
|
|
1875
|
+
const timeoutPromise = new Promise(resolve => setTimeout(() => resolve(false), timeout))
|
|
1858
1876
|
const isLoadedPromise = new Promise(resolve => {
|
|
1859
|
-
if (chatgpt.getNewChatLink()) resolve(true)
|
|
1877
|
+
if (chatgpt.getNewChatLink()) resolve(true)
|
|
1860
1878
|
else new MutationObserver((_, obs) => {
|
|
1861
|
-
if (chatgpt.getNewChatLink()) { obs.disconnect(); resolve(true)
|
|
1862
|
-
}).observe(document.
|
|
1863
|
-
})
|
|
1864
|
-
return await Promise.race([isLoadedPromise, timeoutPromise])
|
|
1879
|
+
if (chatgpt.getNewChatLink()) { obs.disconnect() ; resolve(true) }
|
|
1880
|
+
}).observe(document.documentElement, { childList: true, subtree: true })
|
|
1881
|
+
})
|
|
1882
|
+
return await Promise.race([isLoadedPromise, timeoutPromise])
|
|
1865
1883
|
}
|
|
1866
1884
|
},
|
|
1867
1885
|
|
|
@@ -1879,7 +1897,7 @@ const chatgpt = {
|
|
|
1879
1897
|
return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest');
|
|
1880
1898
|
},
|
|
1881
1899
|
|
|
1882
|
-
speak(msg, { voice = 2, pitch = 2, speed = 1.1, onend } = {} ) {
|
|
1900
|
+
speak(msg, { voice = 2, pitch = 2, speed = 1.1, onend } = {} ) {
|
|
1883
1901
|
// Example call: chatgpt.speak(await chatgpt.getLastResponse(), { voice: 1, pitch: 2, speed: 3 })
|
|
1884
1902
|
// - voice = index of voices available on user device
|
|
1885
1903
|
// - pitch = float for pitch of speech from 0 to 2
|
|
@@ -1900,8 +1918,8 @@ const chatgpt = {
|
|
|
1900
1918
|
}
|
|
1901
1919
|
|
|
1902
1920
|
try { // to speak msg
|
|
1903
|
-
const utterance = new SpeechSynthesisUtterance()
|
|
1904
|
-
Object.assign(utterance, { text: msg,
|
|
1921
|
+
const utterance = new SpeechSynthesisUtterance(), voices = speechSynthesis.getVoices()
|
|
1922
|
+
Object.assign(utterance, { text: msg, voice: voices[voice], pitch: pitch, speed: speed, onend: onend })
|
|
1905
1923
|
speechSynthesis.speak(utterance)
|
|
1906
1924
|
} catch (err) { console.error(err) }
|
|
1907
1925
|
},
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
// Copyright © 2023–2025 Adam Lui (https://github.com/adamlui) under the MIT license
|
|
2
|
+
// Source: https://github.com/adamlui/ai-web-extensions/blob/main/assets/lib/dom.js/src/dom.js
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
for (const depName in deps) this[depName] = deps[depName] }
|
|
6
|
-
},
|
|
4
|
+
window.dom = {
|
|
5
|
+
import(deps) { Object.assign(this.imports = this.imports || {}, deps) },
|
|
7
6
|
|
|
8
7
|
addRisingParticles(targetNode, { lightScheme = 'gray', darkScheme = 'white' } = {}) {
|
|
9
|
-
// Requires https://assets.aiwebextensions.com/styles/rising-particles/dist/<lightScheme
|
|
8
|
+
// * Requires https://assets.aiwebextensions.com/styles/rising-particles/dist/<lightScheme>.min.css
|
|
10
9
|
|
|
11
10
|
if (targetNode.querySelector('[id*=particles]')) return
|
|
12
11
|
const particlesDivsWrapper = document.createElement('div')
|
|
@@ -40,13 +39,14 @@ window.dom = {
|
|
|
40
39
|
return elem
|
|
41
40
|
},
|
|
42
41
|
|
|
43
|
-
style(content) {
|
|
42
|
+
style(content, attrs = {}) {
|
|
44
43
|
const style = document.createElement('style')
|
|
44
|
+
for (const attr in attrs) style.setAttribute(attr, attrs[attr])
|
|
45
45
|
if (content) style.innerText = content
|
|
46
46
|
return style
|
|
47
47
|
},
|
|
48
48
|
|
|
49
|
-
svgElem(type, attrs) {
|
|
49
|
+
svgElem(type, attrs = {}) {
|
|
50
50
|
const elem = document.createElementNS('http://www.w3.org/2000/svg', type)
|
|
51
51
|
for (const attr in attrs) elem.setAttributeNS(null, attr, attrs[attr])
|
|
52
52
|
return elem
|
|
@@ -60,17 +60,39 @@ window.dom = {
|
|
|
60
60
|
},
|
|
61
61
|
|
|
62
62
|
get: {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
|
|
64
|
+
computedSize(elems, { dimension } = {}) { // total width/height of elems (including margins)
|
|
65
|
+
// * Returns { width: totalWidth, height: totalHeight } if no dimension passed
|
|
66
|
+
// * Returns float if { dimension: 'width' | 'height' } passed
|
|
67
|
+
|
|
68
|
+
// Validate args
|
|
69
|
+
elems = elems instanceof NodeList ? [...elems] : [].concat(elems)
|
|
70
|
+
elems.forEach(elem => { if (!(elem instanceof Node))
|
|
71
|
+
throw new Error(`Invalid elem: Element "${JSON.stringify(elem)}" is not a valid DOM node`) })
|
|
72
|
+
const validDimensions = ['width', 'height'], dimensionsToCompute = [].concat(dimension || validDimensions)
|
|
73
|
+
dimensionsToCompute.forEach(dimension => { if (!validDimensions.includes(dimension))
|
|
74
|
+
throw new Error('Invalid dimension: Use \'width\' or \'height\'') })
|
|
75
|
+
|
|
76
|
+
// Compute dimensions
|
|
77
|
+
const computedDimensions = { width: 0, height: 0 }
|
|
78
|
+
elems.forEach(elem => {
|
|
67
79
|
const elemStyle = getComputedStyle(elem) ; if (elemStyle.display == 'none') return
|
|
68
|
-
|
|
69
|
-
|
|
80
|
+
if (dimensionsToCompute.includes('width'))
|
|
81
|
+
computedDimensions.width += elem.getBoundingClientRect().width
|
|
82
|
+
+ parseFloat(elemStyle.marginLeft) + parseFloat(elemStyle.marginRight)
|
|
83
|
+
if (dimensionsToCompute.includes('height'))
|
|
84
|
+
computedDimensions.height += elem.getBoundingClientRect().height
|
|
85
|
+
+ parseFloat(elemStyle.marginTop) + parseFloat(elemStyle.marginBottom)
|
|
70
86
|
})
|
|
71
|
-
|
|
87
|
+
|
|
88
|
+
// Return computed dimensions
|
|
89
|
+
return dimensionsToCompute.length > 1 ? computedDimensions // obj w/ width/height
|
|
90
|
+
: computedDimensions[dimensionsToCompute[0]] // single total val
|
|
72
91
|
},
|
|
73
92
|
|
|
93
|
+
computedHeight(elems) { return this.computedSize(elems, { dimension: 'height' }) }, // including margins
|
|
94
|
+
computedWidth(elems) { return this.computedSize(elems, { dimension: 'width' }) }, // including margins
|
|
95
|
+
|
|
74
96
|
loadedElem(selector, timeout = null) {
|
|
75
97
|
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(null), timeout)) : null
|
|
76
98
|
const isLoadedPromise = new Promise(resolve => {
|
|
@@ -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": "2025.2.
|
|
6
|
+
"version": "2025.2.11",
|
|
7
7
|
"author": "KudoAI",
|
|
8
8
|
"homepage_url": "https://github.com/KudoAI/chatgpt.js-chrome-starter",
|
|
9
9
|
"icons": {
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
// @description A Greasemonkey template to start using chatgpt.js like a boss
|
|
4
4
|
// @author chatgpt.js
|
|
5
5
|
// @namespace https://chatgpt.js.org
|
|
6
|
-
// @version 2025.2.
|
|
6
|
+
// @version 2025.2.11
|
|
7
7
|
// @license MIT
|
|
8
8
|
// @icon https://cdn.jsdelivr.net/gh/KudoAI/chatgpt.js@1fc50da/starters/greasemonkey/assets/images/icons/robot/icon48.png
|
|
9
9
|
// @icon64 https://cdn.jsdelivr.net/gh/KudoAI/chatgpt.js@1fc50da/starters/greasemonkey/assets/images/icons/robot/icon64.png
|
|
10
10
|
// @match *://chatgpt.com/*
|
|
11
|
-
// @require https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.6.
|
|
11
|
+
// @require https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.6.2/dist/chatgpt.min.js
|
|
12
12
|
// @grant GM_getValue
|
|
13
13
|
// @grant GM_setValue
|
|
14
14
|
// @noframes
|