@kudoai/chatgpt.js 3.7.1 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -107
- package/chatgpt.js +56 -47
- package/dist/chatgpt.min.js +7 -7
- package/docs/README.md +131 -107
- package/docs/USERGUIDE.md +5 -5
- package/package.json +24 -18
- package/starters/chrome/extension/components/icons.js +7 -2
- package/starters/chrome/extension/components/modals.js +31 -39
- package/starters/chrome/extension/content.js +16 -26
- package/starters/chrome/extension/lib/chatgpt.js +56 -47
- package/starters/chrome/extension/lib/dom.js +18 -16
- package/starters/chrome/extension/lib/settings.js +32 -6
- package/starters/chrome/extension/lib/ui.js +6 -0
- package/starters/chrome/extension/manifest.json +5 -13
- package/starters/chrome/extension/popup/controller.js +101 -58
- package/starters/chrome/extension/popup/index.html +9 -3
- package/starters/chrome/extension/popup/style.css +19 -9
- package/starters/chrome/extension/service-worker.js +6 -7
- package/starters/greasemonkey/chatgpt.js-greasemonkey-starter.user.js +2 -2
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
// Requires lib
|
|
1
|
+
// Requires lib/<chatgpt|dom>.js + app + env
|
|
2
2
|
|
|
3
3
|
window.modals = {
|
|
4
|
-
import(deps) { Object.assign(this.imports = this.imports || {}, deps) },
|
|
5
4
|
|
|
6
5
|
stack: [], // of types of undismissed modals
|
|
7
|
-
get class() { return `${
|
|
6
|
+
get class() { return `${app.cssPrefix}-modal` },
|
|
8
7
|
|
|
9
8
|
about() {
|
|
9
|
+
const { ui: { scheme }, browser: { isPortrait }} = env
|
|
10
10
|
|
|
11
11
|
// Show modal
|
|
12
12
|
const labelStyles = 'text-transform: uppercase ; font-size: 17px ; font-weight: bold ;'
|
|
13
|
-
+ `color: ${
|
|
13
|
+
+ `color: ${ scheme == 'dark' ? 'white' : '#494141' }`
|
|
14
14
|
const aboutModal = this.alert(
|
|
15
|
-
`${
|
|
15
|
+
`${app.symbol} ${chrome.runtime.getManifest().name}`, // title
|
|
16
16
|
`<span style="${labelStyles}">🧠 Author:</span> `
|
|
17
|
-
+ `<a href="${
|
|
18
|
-
+ `& <a href="${
|
|
17
|
+
+ `<a href="${app.author.url}">${app.author.name}</a> `
|
|
18
|
+
+ `& <a href="${app.urls.contributors}">contributors</a>\n`
|
|
19
19
|
+ `<span style="${labelStyles}">🏷️ Version:</span> `
|
|
20
|
-
+ `<span class="about-em">${
|
|
20
|
+
+ `<span class="about-em">${app.version}</span>\n`
|
|
21
21
|
+ `<span style="${labelStyles}">📜 Open source code:</span> `
|
|
22
|
-
+ `<a href="${
|
|
23
|
-
+
|
|
22
|
+
+ `<a href="${app.urls.gitHub}" target="_blank" rel="nopener">`
|
|
23
|
+
+ app.urls.gitHub + '</a>\n'
|
|
24
24
|
+ `<span style="${labelStyles}">⚡ Powered by:</span> `
|
|
25
|
-
+ `<a href="${
|
|
25
|
+
+ `<a href="${app.urls.chatgptJS}" target="_blank" rel="noopener">chatgpt.js</a>`,
|
|
26
26
|
[ function getSupport(){}, function rateUs(){}, function moreAiExtensions(){} ], // button labels
|
|
27
27
|
'', 656 // modal width
|
|
28
28
|
)
|
|
@@ -32,7 +32,7 @@ window.modals = {
|
|
|
32
32
|
'text-align: center ; font-size: 51px ; line-height: 46px ; padding: 15px 0' )
|
|
33
33
|
aboutModal.querySelector('p').style.cssText = (
|
|
34
34
|
'text-align: center ; overflow-wrap: anywhere ;'
|
|
35
|
-
+ `margin: ${
|
|
35
|
+
+ `margin: ${ isPortrait ? '6px 0 -16px' : '3px 0 0' }` )
|
|
36
36
|
|
|
37
37
|
// Hack buttons
|
|
38
38
|
aboutModal.querySelector('.modal-buttons').style.justifyContent = 'center'
|
|
@@ -40,12 +40,11 @@ window.modals = {
|
|
|
40
40
|
btn.style.cssText = 'height: 55px ; min-width: 136px ; text-align: center'
|
|
41
41
|
|
|
42
42
|
// Replace buttons w/ clones that don't dismiss modal
|
|
43
|
-
|
|
44
|
-
btn.parentNode.replaceChild(btnClone, btn) ; btn = btnClone
|
|
43
|
+
btn.replaceWith(btn = btn.cloneNode(true))
|
|
45
44
|
btn.onclick = () => this.safeWinOpen(
|
|
46
|
-
btn.textContent == 'Get Support' ? `${
|
|
47
|
-
: btn.textContent == 'Rate Us' ? `${
|
|
48
|
-
:
|
|
45
|
+
btn.textContent == 'Get Support' ? `${app.urls.gitHub}/issues`
|
|
46
|
+
: btn.textContent == 'Rate Us' ? `${app.urls.gitHub}/discussions`
|
|
47
|
+
: app.urls.relatedExtensions
|
|
49
48
|
)
|
|
50
49
|
|
|
51
50
|
// Prepend emoji
|
|
@@ -73,7 +72,7 @@ window.modals = {
|
|
|
73
72
|
init(modal) {
|
|
74
73
|
if (!modal) return // to support non-div this.open()s
|
|
75
74
|
if (!this.styles) this.stylize() // to init/append stylesheet
|
|
76
|
-
modal.classList.add(
|
|
75
|
+
modal.classList.add(this.class) ; modal.parentNode.classList.add(`${this.class}-bg`)
|
|
77
76
|
dom.addRisingParticles(modal)
|
|
78
77
|
},
|
|
79
78
|
|
|
@@ -104,34 +103,28 @@ window.modals = {
|
|
|
104
103
|
safeWinOpen(url) { open(url, '_blank', 'noopener') }, // to prevent backdoor vulnerabilities
|
|
105
104
|
|
|
106
105
|
stylize() {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
`.no-user-select {
|
|
113
|
-
user-select: none ; -webkit-user-select: none ; -moz-user-select: none ; -ms-user-select: none }`
|
|
114
|
-
+ `.${this.class} {` // modals
|
|
106
|
+
const { ui: { scheme }, browser: { isMobile }} = env
|
|
107
|
+
if (!this.styles) document.head.append(this.styles = dom.create.elem('style'))
|
|
108
|
+
this.styles.textContent = (
|
|
109
|
+
`.${this.class} {` // modals
|
|
110
|
+
+ 'user-select: none ; -webkit-user-select: none ; -moz-user-select: none ; -ms-user-select: none ;'
|
|
115
111
|
+ 'font-family: -apple-system, system-ui, BlinkMacSystemFont, Segoe UI, Roboto,'
|
|
116
112
|
+ 'Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, sans-serif ;'
|
|
117
113
|
+ 'padding: 20px 25px 24px 25px !important ; font-size: 20px ;'
|
|
118
|
-
+ `color: ${
|
|
114
|
+
+ `color: ${ scheme == 'dark' ? 'white' : 'black' } !important ;`
|
|
119
115
|
+ `background-image: linear-gradient(180deg, ${
|
|
120
|
-
|
|
121
|
-
: '#b6ebff -296px, white 171px' }) }`
|
|
116
|
+
scheme == 'dark' ? '#99a8a6 -200px, black 200px' : '#b6ebff -296px, white 171px' }) }`
|
|
122
117
|
+ `.${this.class} [class*=modal-close-btn] {`
|
|
123
118
|
+ 'position: absolute !important ; float: right ; top: 14px !important ; right: 16px !important ;'
|
|
124
119
|
+ 'cursor: pointer ; width: 33px ; height: 33px ; border-radius: 20px }'
|
|
125
120
|
+ `.${this.class} [class*=modal-close-btn] svg { height: 10px }`
|
|
126
121
|
+ `.${this.class} [class*=modal-close-btn] path {`
|
|
127
|
-
+ `${
|
|
128
|
-
|
|
129
|
-
+ ( this.imports.env.ui.scheme == 'dark' ? // invert dark mode hover paths
|
|
122
|
+
+ `${ scheme == 'dark' ? 'stroke: white ; fill: white' : 'stroke: #9f9f9f ; fill: #9f9f9f' }}`
|
|
123
|
+
+ ( scheme == 'dark' ? // invert dark mode hover paths
|
|
130
124
|
`.${this.class} [class*=modal-close-btn]:hover path { stroke: black ; fill: black }` : '' )
|
|
131
125
|
+ `.${this.class} [class*=modal-close-btn]:hover { background-color: #f2f2f2 }` // hover underlay
|
|
132
126
|
+ `.${this.class} [class*=modal-close-btn] svg { margin: 11.5px }` // center SVG for hover underlay
|
|
133
|
-
+ `.${this.class} a {`
|
|
134
|
-
+ `color: #${ this.imports.env.ui.scheme == 'dark' ? '00cfff' : '1e9ebb' } !important }`
|
|
127
|
+
+ `.${this.class} a { color: #${ scheme == 'dark' ? '00cfff' : '1e9ebb' } !important }`
|
|
135
128
|
+ `.${this.class} h2 { font-weight: bold }`
|
|
136
129
|
+ `.${this.class} button {`
|
|
137
130
|
+ '--btn-transition: transform 0.1s ease-in-out, box-shadow 0.1s ease-in-out ;'
|
|
@@ -141,14 +134,13 @@ window.modals = {
|
|
|
141
134
|
+ '-webkit-transition: var(--btn-transition) ; -moz-transition: var(--btn-transition) ;'
|
|
142
135
|
+ '-o-transition: var(--btn-transition) ; -ms-transition: var(--btn-transition) ;'
|
|
143
136
|
+ 'cursor: pointer !important ;' // add finger cursor
|
|
144
|
-
+ `border: 1px solid ${
|
|
137
|
+
+ `border: 1px solid ${ scheme == 'dark' ? 'white' : 'black' } !important ;`
|
|
145
138
|
+ 'padding: 8px !important ; min-width: 102px }' // resize
|
|
146
139
|
+ `.${this.class} button:hover {` // add zoom, re-scheme
|
|
147
140
|
+ 'transform: scale(1.055) ; color: black !important ;'
|
|
148
|
-
+ `background-color: #${
|
|
149
|
-
+ ( !this.
|
|
150
|
-
|
|
151
|
-
+ `.about-em { color: ${ this.imports.env.ui.scheme == 'dark' ? 'white' : 'green' } !important }`
|
|
141
|
+
+ `background-color: #${ scheme == 'dark' ? '00cfff' : '9cdaff' } !important }`
|
|
142
|
+
+ ( !isMobile ? `.${this.class} .modal-buttons { margin-left: -13px !important }` : '' )
|
|
143
|
+
+ `.about-em { color: ${ scheme == 'dark' ? 'white' : 'green' } !important }`
|
|
152
144
|
)
|
|
153
145
|
}
|
|
154
146
|
};
|
|
@@ -4,29 +4,23 @@
|
|
|
4
4
|
(async () => {
|
|
5
5
|
|
|
6
6
|
// Import JS resources
|
|
7
|
-
for (const resource of ['components/modals.js', 'lib/chatgpt.js', 'lib/dom.js', 'lib/settings.js'])
|
|
7
|
+
for (const resource of ['components/modals.js', 'lib/chatgpt.js', 'lib/dom.js', 'lib/settings.js', 'lib/ui.js'])
|
|
8
8
|
await import(chrome.runtime.getURL(resource))
|
|
9
9
|
|
|
10
10
|
// Init ENV context
|
|
11
|
-
|
|
11
|
+
window.env = { browser: { isMobile: chatgpt.browser.isMobile() }, ui: { scheme: ui.getScheme() }}
|
|
12
12
|
env.browser.isPortrait = env.browser.isMobile && (window.innerWidth < window.innerHeight)
|
|
13
13
|
|
|
14
14
|
// Import APP data
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
notify(...['msg', 'pos', 'notifDuration', 'shadow'].map(arg => req.options[arg]))
|
|
25
|
-
else if (req.action == 'alert')
|
|
26
|
-
modals.alert(...['title', 'msg', 'btns', 'checkbox', 'width'].map(arg => req.options[arg]))
|
|
27
|
-
else if (req.action == 'showAbout') {
|
|
28
|
-
config.skipAlert = true ; chatgpt.isLoaded().then(() => modals.open('about'))
|
|
29
|
-
} else if (req.action == 'syncConfigToUI') syncConfigToUI(req.options)
|
|
15
|
+
;({ app: window.app } = await chrome.storage.local.get('app'))
|
|
16
|
+
|
|
17
|
+
chrome.runtime.onMessage.addListener(({ action, options }) => { // from service-worker.js + popup/index.html
|
|
18
|
+
({
|
|
19
|
+
notify: () => notify(...['msg', 'pos', 'notifDuration', 'shadow'].map(arg => options[arg])),
|
|
20
|
+
alert: () => modals.alert(...['title', 'msg', 'btns', 'checkbox', 'width'].map(arg => options[arg])),
|
|
21
|
+
showAbout: () => { config.skipAlert = true ; chatgpt.isLoaded().then(() => modals.open('about')) },
|
|
22
|
+
syncConfigToUI: () => syncConfigToUI(options)
|
|
23
|
+
}[action]?.() || console.warn(`Received unsupported action: "${action}"`))
|
|
30
24
|
})
|
|
31
25
|
|
|
32
26
|
// Init SETTINGS
|
|
@@ -76,23 +70,19 @@
|
|
|
76
70
|
}
|
|
77
71
|
}
|
|
78
72
|
|
|
79
|
-
function getScheme() {
|
|
80
|
-
return document.documentElement.className
|
|
81
|
-
|| (window.matchMedia?.('(prefers-color-scheme: dark)')?.matches ? 'dark' : 'light')
|
|
82
|
-
}
|
|
83
|
-
|
|
84
73
|
// Run MAIN routine
|
|
85
74
|
|
|
86
75
|
chatgpt.printAllFunctions() // to console
|
|
87
76
|
|
|
88
77
|
// CHILL a bit if your hacks depend on delayed DOM content
|
|
89
78
|
await chatgpt.isLoaded()
|
|
90
|
-
await new Promise(resolve => setTimeout(resolve, 500))
|
|
79
|
+
await new Promise(resolve => setTimeout(resolve, 500)) // sleep .5s
|
|
91
80
|
|
|
92
81
|
// Add RISING PARTICLES styles for modals
|
|
93
|
-
['gray', 'white'].forEach(color => document.head.append(
|
|
82
|
+
;['gray', 'white'].forEach(color => document.head.append(
|
|
94
83
|
dom.create.elem('link', { rel: 'stylesheet',
|
|
95
|
-
href: `https://
|
|
84
|
+
href: `https://cdn.jsdelivr.net/gh/adamlui/ai-web-extensions@727feff/assets/styles/rising-particles/dist/${
|
|
85
|
+
color}.min.css`
|
|
96
86
|
})))
|
|
97
87
|
|
|
98
88
|
if (config.extensionDisabled) return
|
|
@@ -112,7 +102,7 @@
|
|
|
112
102
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener( // for browser/system scheme pref changes
|
|
113
103
|
'change', () => requestAnimationFrame(handleSchemePrefChange))
|
|
114
104
|
function handleSchemePrefChange() {
|
|
115
|
-
const displayedScheme = getScheme()
|
|
105
|
+
const displayedScheme = ui.getScheme()
|
|
116
106
|
if (env.ui.scheme != displayedScheme) { env.ui.scheme = displayedScheme ; modals.stylize() }
|
|
117
107
|
}
|
|
118
108
|
|
|
@@ -24,26 +24,34 @@ const chatgpt = {
|
|
|
24
24
|
|
|
25
25
|
selectors: {
|
|
26
26
|
btns: {
|
|
27
|
-
continue: 'button:has([class*=rotate] [d^="M4.47189"])',
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
continue: 'button:has(svg[class*=rotate] > path[d^="M4.47189"])',
|
|
28
|
+
createImage: 'button[data-testid="composer-create-image"]',
|
|
29
|
+
deepResearch: 'button[data-testid="composer-deep-research"]',
|
|
30
|
+
login: 'button[data-testid*=login]',
|
|
31
|
+
newChat: 'a[href="/"]:has(svg),' // Pencil button (when logged in)
|
|
32
|
+
+ 'button:has([d^="M3.06957"])', // Cycle Arrows button (in temp chat logged out)
|
|
33
|
+
regen: 'button[data-testid*=regenerate],' // oval button in place of chatbar on errors
|
|
34
|
+
// 'Try Again' entry of model selector below msg
|
|
35
|
+
+ 'div[role=menuitem] div:has(svg):has(path[d^="M3.06957"])',
|
|
36
|
+
scroll: 'button:has(> svg > path[d^="M12 21C11.7348"])',
|
|
37
|
+
search: 'button[data-testid="composer-button-search"]',
|
|
38
|
+
reason: 'button[data-testid="composer-button-reason"]',
|
|
39
|
+
send: 'button[data-testid=send-button]',
|
|
40
|
+
sidebar: 'button[data-testid*=sidebar-button]',
|
|
41
|
+
stop: 'button[data-testid=stop-button]',
|
|
42
|
+
upload: 'button:has(> svg > path[d^="M12 3C12.5523"])',
|
|
43
|
+
voice: 'button[data-testid*=composer-speech-button]'
|
|
36
44
|
},
|
|
37
45
|
chatDivs: {
|
|
38
|
-
convo: '
|
|
39
|
-
|
|
46
|
+
convo: 'div[class*=thread]', msg: 'div[data-message-author-role]',
|
|
47
|
+
reply: 'div[data-message-author-role=assistant]'
|
|
40
48
|
},
|
|
41
|
-
chatHistory: '
|
|
42
|
-
errors: { txt: '[class*=text-error]' },
|
|
43
|
-
footer: '.
|
|
44
|
-
header: 'main .sticky',
|
|
49
|
+
chatHistory: 'div#history',
|
|
50
|
+
errors: { toast: 'div.toast-root', txt: 'div[class*=text-error]' },
|
|
51
|
+
footer: 'div#thread-bottom-container > div:last-of-type > div, span.text-sm.leading-none',
|
|
52
|
+
header: 'div#page-header, main div.sticky:first-of-type',
|
|
45
53
|
links: { newChat: 'nav a[href="/"]', sidebarItem: 'nav a' },
|
|
46
|
-
sidebar: 'div
|
|
54
|
+
sidebar: 'div.bg-token-sidebar-surface-primary',
|
|
47
55
|
ssgManifest: 'script[src*="_ssgManifest.js"]'
|
|
48
56
|
},
|
|
49
57
|
|
|
@@ -120,14 +128,16 @@ const chatgpt = {
|
|
|
120
128
|
drag: {
|
|
121
129
|
mousedown(event) { // find modal, update styles, attach listeners, init XY offsets
|
|
122
130
|
if (event.button != 0) return // prevent non-left-click drag
|
|
123
|
-
if (getComputedStyle(event.target).cursor
|
|
131
|
+
if (!/auto|default/.test(getComputedStyle(event.target).cursor))
|
|
132
|
+
return // prevent drag on interactive elems
|
|
124
133
|
chatgpt.draggingModal = event.currentTarget
|
|
125
134
|
event.preventDefault() // prevent sub-elems like icons being draggable
|
|
126
135
|
Object.assign(chatgpt.draggingModal.style, {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
transition: '0.1s', willChange: 'transform', transform: 'scale(1.05)' })
|
|
137
|
+
document.body.style.cursor = 'grabbing' // update cursor
|
|
138
|
+
;[...chatgpt.draggingModal.children] // prevent hover FX if drag lags behind cursor
|
|
139
|
+
.forEach(child => child.style.pointerEvents = 'none')
|
|
140
|
+
;['mousemove', 'mouseup'].forEach(eventType => // add listeners
|
|
131
141
|
document.addEventListener(eventType, handlers.drag[eventType]))
|
|
132
142
|
const draggingModalRect = chatgpt.draggingModal.getBoundingClientRect()
|
|
133
143
|
handlers.drag.offsetX = event.clientX - draggingModalRect.left +21
|
|
@@ -143,10 +153,11 @@ const chatgpt = {
|
|
|
143
153
|
|
|
144
154
|
mouseup() { // restore styles/pointer events, remove listeners, reset chatgpt.draggingModal
|
|
145
155
|
Object.assign(chatgpt.draggingModal.style, { // restore styles
|
|
146
|
-
cursor: 'inherit', transition: 'inherit', willChange: 'auto', transform: 'scale(1)' })
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
156
|
+
cursor: 'inherit', transition: 'inherit', willChange: 'auto', transform: 'scale(1)' })
|
|
157
|
+
document.body.style.cursor = '' // restore cursor
|
|
158
|
+
;[...chatgpt.draggingModal.children] // restore pointer events
|
|
159
|
+
.forEach(child => child.style.pointerEvents = '')
|
|
160
|
+
;['mousemove', 'mouseup'].forEach(eventType => // remove listeners
|
|
150
161
|
document.removeEventListener(eventType, handlers.drag[eventType]))
|
|
151
162
|
chatgpt.draggingModal = null
|
|
152
163
|
}
|
|
@@ -170,7 +181,7 @@ const chatgpt = {
|
|
|
170
181
|
modalStyle.setAttribute('last-updated', thisUpdated.toString())
|
|
171
182
|
document.head.append(modalStyle)
|
|
172
183
|
}
|
|
173
|
-
modalStyle.
|
|
184
|
+
modalStyle.textContent = ( // update prev/new style contents
|
|
174
185
|
`.chatgpt-modal { /* vars */
|
|
175
186
|
--transition: opacity 0.65s cubic-bezier(.165,.84,.44,1), /* for fade-in */
|
|
176
187
|
transform 0.55s cubic-bezier(.165,.84,.44,1) ; /* for move-in */
|
|
@@ -222,7 +233,7 @@ const chatgpt = {
|
|
|
222
233
|
display: flex ; justify-content: flex-end ; margin: 20px -5px -3px 0 ;
|
|
223
234
|
${ isMobile ? 'flex-direction: column-reverse' : '' }}
|
|
224
235
|
.chatgpt-modal button {
|
|
225
|
-
font-size: 14px ; text-transform: uppercase ;
|
|
236
|
+
font-size: 14px ; text-transform: uppercase ; cursor: crosshair ;
|
|
226
237
|
margin-left: ${ isMobile ? 0 : 10 }px ; padding: ${ isMobile ? 15 : 8 }px 18px ;
|
|
227
238
|
${ isMobile ? 'margin-top: 5px ; margin-bottom: 3px ;' : '' }
|
|
228
239
|
border-radius: 0 ; border: 1px solid ${ scheme == 'dark' ? 'white' : 'black' };
|
|
@@ -260,7 +271,7 @@ const chatgpt = {
|
|
|
260
271
|
}
|
|
261
272
|
|
|
262
273
|
// Insert text into elems
|
|
263
|
-
modalTitle.
|
|
274
|
+
modalTitle.textContent = title || '' ; modalMessage.innerText = msg || '' ; chatgpt.renderHTML(modalMessage)
|
|
264
275
|
|
|
265
276
|
// Create/append buttons (if provided) to buttons div
|
|
266
277
|
const modalButtons = document.createElement('div')
|
|
@@ -297,7 +308,7 @@ const chatgpt = {
|
|
|
297
308
|
// Create/show label
|
|
298
309
|
const checkboxLabel = document.createElement('label')
|
|
299
310
|
checkboxLabel.onclick = () => { checkboxInput.checked = !checkboxInput.checked ; checkboxFn() }
|
|
300
|
-
checkboxLabel.textContent = checkboxFn.name
|
|
311
|
+
checkboxLabel.textContent = checkboxFn.name[0].toUpperCase() // capitalize first char
|
|
301
312
|
+ checkboxFn.name.slice(1) // format remaining chars
|
|
302
313
|
.replace(/([A-Z])/g, (match, letter) => ' ' + letter.toLowerCase()) // insert spaces, convert to lowercase
|
|
303
314
|
.replace(/\b(\w+)nt\b/gi, '$1n\'t') // insert apostrophe in 'nt' suffixes
|
|
@@ -646,7 +657,7 @@ const chatgpt = {
|
|
|
646
657
|
const msgs = [] ; let isUserMsg = true
|
|
647
658
|
chatDivs.forEach(div => {
|
|
648
659
|
const sender = isUserMsg ? 'USER' : 'CHATGPT'; isUserMsg = !isUserMsg
|
|
649
|
-
const msg =
|
|
660
|
+
const msg = [...div.childNodes].map(node => node.innerText)
|
|
650
661
|
.join('\n\n') // insert double line breaks between paragraphs
|
|
651
662
|
.replace('Copy code', '')
|
|
652
663
|
msgs.push(`${sender}: ${msg}`)
|
|
@@ -1148,7 +1159,7 @@ const chatgpt = {
|
|
|
1148
1159
|
}
|
|
1149
1160
|
},
|
|
1150
1161
|
|
|
1151
|
-
isDarkMode() { return document.documentElement.
|
|
1162
|
+
isDarkMode() { return document.documentElement.classList.contains('dark') },
|
|
1152
1163
|
isFullScreen() { return chatgpt.browser.isFullScreen() },
|
|
1153
1164
|
|
|
1154
1165
|
async isIdle(timeout = null) {
|
|
@@ -1189,7 +1200,7 @@ const chatgpt = {
|
|
|
1189
1200
|
return await ( timeoutPromise ? Promise.race([isLoadedPromise, timeoutPromise]) : isLoadedPromise )
|
|
1190
1201
|
},
|
|
1191
1202
|
|
|
1192
|
-
isLightMode() { return document.documentElement.classList.
|
|
1203
|
+
isLightMode() { return document.documentElement.classList.contains('light') },
|
|
1193
1204
|
isTempChat() { return location.search == '?temporary-chat=true' },
|
|
1194
1205
|
isTyping() { return !!this.getStopButton() },
|
|
1195
1206
|
login() { window.location.href = 'https://chat.openai.com/auth/login' },
|
|
@@ -1289,7 +1300,7 @@ const chatgpt = {
|
|
|
1289
1300
|
const notificationDiv = document.createElement('div') // make div
|
|
1290
1301
|
notificationDiv.id = Math.floor(chatgpt.randomFloat() * 1000000) + Date.now()
|
|
1291
1302
|
notificationDiv.classList.add('chatgpt-notif')
|
|
1292
|
-
notificationDiv.
|
|
1303
|
+
notificationDiv.textContent = msg // insert msg
|
|
1293
1304
|
document.body.append(notificationDiv) // insert into DOM
|
|
1294
1305
|
|
|
1295
1306
|
// Create/append close button
|
|
@@ -1322,7 +1333,7 @@ const chatgpt = {
|
|
|
1322
1333
|
notifStyle.setAttribute('last-updated', thisUpdated.toString())
|
|
1323
1334
|
document.head.append(notifStyle)
|
|
1324
1335
|
}
|
|
1325
|
-
notifStyle.
|
|
1336
|
+
notifStyle.textContent = ( // update prev/new style contents
|
|
1326
1337
|
'.chatgpt-notif {'
|
|
1327
1338
|
+ 'font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC",'
|
|
1328
1339
|
+ '"Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", sans-serif ;'
|
|
@@ -1358,17 +1369,17 @@ const chatgpt = {
|
|
|
1358
1369
|
notificationDiv.style.right = notificationDiv.isRight ? vpXoffset.toString() + 'px' : ''
|
|
1359
1370
|
notificationDiv.style.left = !notificationDiv.isRight ? vpXoffset.toString() + 'px' : ''
|
|
1360
1371
|
|
|
1361
|
-
//
|
|
1372
|
+
// Re-position old notifications
|
|
1362
1373
|
const thisQuadrantQueue = notifyProps.queue[notificationDiv.quadrant]
|
|
1363
1374
|
if (thisQuadrantQueue.length > 1) {
|
|
1364
1375
|
try { // to move old notifications
|
|
1365
1376
|
for (const divId of thisQuadrantQueue.slice(0, -1)) { // exclude new div
|
|
1366
1377
|
const oldDiv = document.getElementById(divId),
|
|
1367
1378
|
offsetProp = oldDiv.style.top ? 'top' : 'bottom', // pick property to change
|
|
1368
|
-
vOffset =
|
|
1379
|
+
vOffset = +parseInt(oldDiv.style[offsetProp]) +5 + oldDiv.getBoundingClientRect().height
|
|
1369
1380
|
oldDiv.style[offsetProp] = `${ vOffset }px` // change prop
|
|
1370
1381
|
}
|
|
1371
|
-
} catch (err) {}
|
|
1382
|
+
} catch (err) { console.warn('Failed to re-position notification:', err) }
|
|
1372
1383
|
}
|
|
1373
1384
|
|
|
1374
1385
|
// Show notification
|
|
@@ -1496,7 +1507,7 @@ const chatgpt = {
|
|
|
1496
1507
|
// Process text node
|
|
1497
1508
|
if (childNode.nodeType == Node.TEXT_NODE) {
|
|
1498
1509
|
const text = childNode.nodeValue,
|
|
1499
|
-
elems =
|
|
1510
|
+
elems = [...text.matchAll(reTags)]
|
|
1500
1511
|
|
|
1501
1512
|
// Process 1st element to render
|
|
1502
1513
|
if (elems.length > 0) {
|
|
@@ -1505,7 +1516,7 @@ const chatgpt = {
|
|
|
1505
1516
|
tagNode = document.createElement(tagName) ; tagNode.textContent = tagText
|
|
1506
1517
|
|
|
1507
1518
|
// Extract/set attributes
|
|
1508
|
-
const attrs =
|
|
1519
|
+
const attrs = [...tagAttrs.matchAll(reAttrs)]
|
|
1509
1520
|
attrs.forEach(attr => {
|
|
1510
1521
|
const name = attr[1], value = attr[2].replace(/['"]/g, '')
|
|
1511
1522
|
tagNode.setAttribute(name, value)
|
|
@@ -1541,9 +1552,8 @@ const chatgpt = {
|
|
|
1541
1552
|
// responseToGet = index of response to get (defaults to latest if '' unpassed)
|
|
1542
1553
|
// regenResponseToGet = index of regenerated response to get (defaults to latest if '' unpassed)
|
|
1543
1554
|
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
else return this.getFromAPI.apply(null, arguments)
|
|
1555
|
+
return this[`getFrom${ location.href.startsWith('https://chatgpt.com/c/') ? 'DOM' : 'API' }`]
|
|
1556
|
+
.apply(null, arguments)
|
|
1547
1557
|
},
|
|
1548
1558
|
|
|
1549
1559
|
getFromAPI(chatToGet, responseToGet) {
|
|
@@ -1604,7 +1614,7 @@ const chatgpt = {
|
|
|
1604
1614
|
const textArea = chatgpt.getChatBox()
|
|
1605
1615
|
if (!textArea) return console.error('Chatbar element not found!')
|
|
1606
1616
|
const msgP = document.createElement('p'); msgP.textContent = msg
|
|
1607
|
-
textArea.
|
|
1617
|
+
textArea.querySelector('p').replaceWith(msgP)
|
|
1608
1618
|
textArea.dispatchEvent(new Event('input', { bubbles: true })) // enable send button
|
|
1609
1619
|
setTimeout(function delaySend() {
|
|
1610
1620
|
const sendBtn = chatgpt.getSendButton()
|
|
@@ -1753,8 +1763,7 @@ const chatgpt = {
|
|
|
1753
1763
|
activateObserver() {
|
|
1754
1764
|
|
|
1755
1765
|
// Stop the previous observer to preserve resources
|
|
1756
|
-
if (this.observer instanceof MutationObserver)
|
|
1757
|
-
try { this.observer.disconnect() } catch (e) {}
|
|
1766
|
+
if (this.observer instanceof MutationObserver) this.observer.disconnect()
|
|
1758
1767
|
|
|
1759
1768
|
if (!this.elems.length) return console.error('🤖 chatgpt.js >> No elems to append!')
|
|
1760
1769
|
|
|
@@ -1857,7 +1866,7 @@ const chatgpt = {
|
|
|
1857
1866
|
isOn() {
|
|
1858
1867
|
const sidebar = (() => {
|
|
1859
1868
|
return chatgpt.sidebar.exists() ? document.querySelector(chatgpt.selectors.sidebar) : null })()
|
|
1860
|
-
if (!sidebar) { console.error('Sidebar element not found!')
|
|
1869
|
+
if (!sidebar) { return console.error('Sidebar element not found!') || false }
|
|
1861
1870
|
else return chatgpt.browser.isMobile() ?
|
|
1862
1871
|
document.documentElement.style.overflow == 'hidden'
|
|
1863
1872
|
: sidebar.style.visibility != 'hidden' && sidebar.style.width != '0px'
|
|
@@ -2095,7 +2104,7 @@ const cjsFuncSynonyms = [
|
|
|
2095
2104
|
// Define HELPER functions
|
|
2096
2105
|
|
|
2097
2106
|
function toCamelCase(words) {
|
|
2098
|
-
return words.map((word, idx) => idx == 0 ? word : word
|
|
2107
|
+
return words.map((word, idx) => idx == 0 ? word : word[0].toUpperCase() + word.slice(1)).join('') }
|
|
2099
2108
|
|
|
2100
2109
|
// Prefix console logs w/ '🤖 chatgpt.js >> '
|
|
2101
2110
|
const consolePrefix = '🤖 chatgpt.js >> ', ogError = console.error, ogInfo = console.info
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// Source: https://github.com/adamlui/ai-web-extensions/blob/main/assets/lib/dom.js/src/dom.js
|
|
3
3
|
|
|
4
4
|
window.dom = {
|
|
5
|
-
import(deps) { Object.assign(this.imports = this.imports || {}, deps) },
|
|
6
5
|
|
|
7
6
|
addRisingParticles(targetNode, { lightScheme = 'gray', darkScheme = 'white' } = {}) {
|
|
8
7
|
// * Requires https://assets.aiwebextensions.com/styles/rising-particles/dist/<lightScheme>.min.css
|
|
@@ -12,11 +11,11 @@ window.dom = {
|
|
|
12
11
|
particlesDivsWrapper.style.cssText = (
|
|
13
12
|
'position: absolute ; top: 0 ; left: 0 ;' // hug targetNode's top-left corner
|
|
14
13
|
+ 'height: 100% ; width: 100% ; border-radius: 15px ; overflow: clip ;' // bound innards exactly by targetNode
|
|
15
|
-
+ 'z-index: -1' )
|
|
16
|
-
['sm', 'med', 'lg'].forEach(particleSize => {
|
|
14
|
+
+ 'z-index: -1' ) // allow interactive elems to be clicked
|
|
15
|
+
;['sm', 'med', 'lg'].forEach(particleSize => {
|
|
17
16
|
const particlesDiv = document.createElement('div')
|
|
18
|
-
particlesDiv.id =
|
|
19
|
-
: `${(
|
|
17
|
+
particlesDiv.id = config?.bgAnimationsDisabled ? `particles-${particleSize}-off`
|
|
18
|
+
: `${( env?.ui?.scheme || env?.ui?.app?.scheme ) == 'dark' ? darkScheme
|
|
20
19
|
: lightScheme }-particles-${particleSize}`
|
|
21
20
|
particlesDivsWrapper.append(particlesDiv)
|
|
22
21
|
})
|
|
@@ -41,8 +40,9 @@ window.dom = {
|
|
|
41
40
|
|
|
42
41
|
style(content, attrs = {}) {
|
|
43
42
|
const style = document.createElement('style')
|
|
43
|
+
style.setAttribute('type', 'text/css') // support older browsers
|
|
44
44
|
for (const attr in attrs) style.setAttribute(attr, attrs[attr])
|
|
45
|
-
if (content) style.
|
|
45
|
+
if (content) style.textContent = content
|
|
46
46
|
return style
|
|
47
47
|
},
|
|
48
48
|
|
|
@@ -93,17 +93,19 @@ window.dom = {
|
|
|
93
93
|
computedHeight(elems) { return this.computedSize(elems, { dimension: 'height' }) }, // including margins
|
|
94
94
|
computedWidth(elems) { return this.computedSize(elems, { dimension: 'width' }) }, // including margins
|
|
95
95
|
|
|
96
|
-
loadedElem(selector, timeout = null) {
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
const elem = document.querySelector(selector)
|
|
100
|
-
if (elem) resolve(elem)
|
|
101
|
-
else new MutationObserver((_, obs) => {
|
|
96
|
+
loadedElem(selector, { timeout = null } = {}) {
|
|
97
|
+
const raceEntries = [
|
|
98
|
+
new Promise(resolve => { // when elem loads
|
|
102
99
|
const elem = document.querySelector(selector)
|
|
103
|
-
if (elem)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
100
|
+
if (elem) resolve(elem)
|
|
101
|
+
else new MutationObserver((_, obs) => {
|
|
102
|
+
const elem = document.querySelector(selector)
|
|
103
|
+
if (elem) { obs.disconnect() ; resolve(elem) }
|
|
104
|
+
}).observe(document.documentElement, { childList: true, subtree: true })
|
|
105
|
+
})
|
|
106
|
+
]
|
|
107
|
+
if (timeout) raceEntries.push(new Promise(resolve => setTimeout(() => resolve(null), timeout)))
|
|
108
|
+
return Promise.race(raceEntries)
|
|
107
109
|
}
|
|
108
110
|
}
|
|
109
111
|
};
|
|
@@ -1,29 +1,55 @@
|
|
|
1
1
|
window.config = {}
|
|
2
2
|
window.settings = {
|
|
3
3
|
|
|
4
|
-
// Init SETTINGS props (for popup menu)
|
|
5
4
|
controls: {
|
|
6
5
|
// Add settings options as keys, with each key's value being an object that includes:
|
|
7
|
-
// - 'type': the control type (e.g. 'toggle' or 'prompt')
|
|
6
|
+
// - 'type': the control type (e.g. 'toggle', 'link' or 'prompt')
|
|
8
7
|
// - 'label': a descriptive label
|
|
9
8
|
// - 'defaultVal' (optional): default value of setting (true for toggles if unspecified, false otherwise)
|
|
9
|
+
// - 'category' (optional): string key from this.categories to group control under
|
|
10
10
|
// - 'symbol' (optional): for icon display (e.g. ⌚)
|
|
11
|
-
//
|
|
12
|
-
|
|
11
|
+
// - 'helptip' (optional): tooltip to display on hover
|
|
12
|
+
|
|
13
|
+
// NOTE: Controls are displayed in top-to-bottom order (within categories and in top-level)
|
|
14
|
+
// NOTE: Toggles are disabled by default unless defaultVal is true
|
|
15
|
+
// ...or key name contains 'disabled' or 'hidden' (case insensitive)
|
|
16
|
+
|
|
13
17
|
// EXAMPLES:
|
|
14
18
|
// autoScrollDisabled: { type: 'toggle', label: 'Auto-Scroll' },
|
|
15
19
|
// replyLanguage: { type: 'prompt', symbol: '🌐', label: 'Reply Language' }
|
|
16
20
|
},
|
|
17
21
|
|
|
22
|
+
categories: {
|
|
23
|
+
// Add category entries as keys, with each key's value being an object that includes:
|
|
24
|
+
// - 'label': a descriptive label
|
|
25
|
+
// - 'symbol' (optional): for icon display (e.g. ⌚)
|
|
26
|
+
// - 'color' (optional): hex code (w/o #) of color for left-border
|
|
27
|
+
// - 'helptip' (optional): tooltip to display on hover
|
|
28
|
+
// - 'autoExpand' (optional): true/false to auto-expand categories on toolbar icon click
|
|
29
|
+
|
|
30
|
+
// NOTE: Categories are displayed in top-to-bottom order
|
|
31
|
+
|
|
32
|
+
// EXAMPLE:
|
|
33
|
+
// displaySettings: {
|
|
34
|
+
// symbol: '🖥️', color: '94fca2', label: 'Display Settings', helptip: 'Display-related settings' }
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
typeIsEnabled(key) { // for menu labels + notifs to return ON/OFF for type w/o suffix
|
|
38
|
+
const reInvertFlags = /disabled|hidden/i
|
|
39
|
+
return reInvertFlags.test(key) // flag in control key name
|
|
40
|
+
&& !reInvertFlags.test(this.controls[key]?.label) // but not in label name
|
|
41
|
+
? !config[key] : config[key] // so invert since flag reps opposite type state, else don't
|
|
42
|
+
},
|
|
43
|
+
|
|
18
44
|
load(...keys) {
|
|
19
45
|
return Promise.all(keys.flat().map(async key => // resolve promise when all keys load
|
|
20
|
-
|
|
46
|
+
config[key] = (await chrome.storage.local.get(key))[key]
|
|
21
47
|
?? this.controls[key]?.defaultVal ?? this.controls[key]?.type == 'toggle'
|
|
22
48
|
))
|
|
23
49
|
},
|
|
24
50
|
|
|
25
51
|
save(key, val) {
|
|
26
52
|
chrome.storage.local.set({ [key]: val }) // save to Chrome extension storage
|
|
27
|
-
|
|
53
|
+
config[key] = val // save to memory
|
|
28
54
|
}
|
|
29
55
|
};
|