@kudoai/chatgpt.js 3.6.3 → 3.7.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 +31 -7
- package/chatgpt.js +928 -935
- package/dist/chatgpt.min.js +16 -6
- package/docs/README.md +25 -8
- package/docs/USERGUIDE.md +63 -3
- package/package.json +7 -7
- package/starters/chrome/extension/components/icons.js +1 -1
- package/starters/chrome/extension/components/modals.js +8 -5
- package/starters/chrome/extension/content.js +8 -6
- package/starters/chrome/extension/lib/chatgpt.js +928 -935
- package/starters/chrome/extension/lib/dom.js +6 -6
- package/starters/chrome/extension/lib/settings.js +2 -2
- package/starters/chrome/extension/manifest.json +1 -1
- package/starters/chrome/extension/popup/controller.js +66 -74
- package/starters/chrome/extension/popup/index.html +1 -6
- package/starters/chrome/extension/popup/style.css +33 -34
- package/starters/chrome/extension/service-worker.js +12 -11
- package/starters/greasemonkey/chatgpt.js-greasemonkey-starter.user.js +2 -2
|
@@ -77,12 +77,12 @@ window.dom = {
|
|
|
77
77
|
const computedDimensions = { width: 0, height: 0 }
|
|
78
78
|
elems.forEach(elem => {
|
|
79
79
|
const elemStyle = getComputedStyle(elem) ; if (elemStyle.display == 'none') return
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
Object.keys(computedDimensions).forEach(dimension => {
|
|
81
|
+
if (dimensionsToCompute.includes(dimension))
|
|
82
|
+
computedDimensions[dimension] += elem.getBoundingClientRect()[dimension]
|
|
83
|
+
+ parseFloat(elemStyle[`margin${dimension == 'width' ? 'Left' : 'Top'}`])
|
|
84
|
+
+ parseFloat(elemStyle[`margin${dimension == 'width' ? 'Right' : 'Bottom'}`])
|
|
85
|
+
})
|
|
86
86
|
})
|
|
87
87
|
|
|
88
88
|
// Return computed dimensions
|
|
@@ -17,13 +17,13 @@ window.settings = {
|
|
|
17
17
|
|
|
18
18
|
load(...keys) {
|
|
19
19
|
return Promise.all(keys.flat().map(async key => // resolve promise when all keys load
|
|
20
|
-
window.config[key] = (await chrome.storage.
|
|
20
|
+
window.config[key] = (await chrome.storage.local.get(key))[key]
|
|
21
21
|
?? this.controls[key]?.defaultVal ?? this.controls[key]?.type == 'toggle'
|
|
22
22
|
))
|
|
23
23
|
},
|
|
24
24
|
|
|
25
25
|
save(key, val) {
|
|
26
|
-
chrome.storage.
|
|
26
|
+
chrome.storage.local.set({ [key]: val }) // save to Chrome extension storage
|
|
27
27
|
window.config[key] = val // save to memory
|
|
28
28
|
}
|
|
29
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": "2025.2.
|
|
6
|
+
"version": "2025.2.21",
|
|
7
7
|
"author": "KudoAI",
|
|
8
8
|
"homepage_url": "https://github.com/KudoAI/chatgpt.js-chrome-starter",
|
|
9
9
|
"icons": {
|
|
@@ -5,37 +5,41 @@
|
|
|
5
5
|
await import(chrome.runtime.getURL(resource))
|
|
6
6
|
|
|
7
7
|
// Init ENV context
|
|
8
|
-
const env = {
|
|
9
|
-
|
|
8
|
+
const env = {
|
|
9
|
+
site: /([^.]+)\.[^.]+$/.exec(new URL((await chrome.tabs.query(
|
|
10
|
+
{ active: true, currentWindow: true }))[0].url).hostname)?.[1]
|
|
11
|
+
}
|
|
10
12
|
|
|
11
|
-
// Import
|
|
12
|
-
const { app } = await chrome.storage.
|
|
13
|
-
icons.import({ app }) // for
|
|
13
|
+
// Import DATA
|
|
14
|
+
const { app } = await chrome.storage.local.get('app')
|
|
15
|
+
icons.import({ app }) // for src's using app.urls.assetHost
|
|
14
16
|
|
|
15
17
|
// Define FUNCTIONS
|
|
16
18
|
|
|
19
|
+
function notify(msg, pos = 'bottom-right') { sendMsgToActiveTab('notify', { msg, pos }) }
|
|
20
|
+
|
|
17
21
|
async function sendMsgToActiveTab(action, options) {
|
|
18
22
|
const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true })
|
|
19
23
|
return await chrome.tabs.sendMessage(activeTab.id, { action: action, options: { ...options }})
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
function
|
|
26
|
+
function settingIsEnabled(key) { return config[key] ^ /disabled|hidden/i.test(key) }
|
|
23
27
|
|
|
24
28
|
const sync = {
|
|
25
29
|
fade() {
|
|
26
30
|
|
|
27
|
-
//
|
|
31
|
+
// Toolbar icon
|
|
28
32
|
chrome.action.setIcon({ path: Object.fromEntries(
|
|
29
33
|
Object.keys(chrome.runtime.getManifest().icons).map(dimension =>
|
|
30
34
|
[dimension, `../icons/${ config.extensionDisabled ? 'faded/' : '' }icon${dimension}.png`]
|
|
31
35
|
))})
|
|
32
36
|
|
|
33
|
-
//
|
|
34
|
-
document.querySelectorAll('
|
|
35
|
-
.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
// Menu elems
|
|
38
|
+
document.querySelectorAll('.logo, .menu-title, .menu-entry').forEach((elem, idx) => {
|
|
39
|
+
elem.style.transition = config.extensionDisabled ? '' : 'opacity 0.15s ease-in'
|
|
40
|
+
setTimeout(() => elem.classList.toggle('disabled', config.extensionDisabled),
|
|
41
|
+
config.extensionDisabled ? 0 : idx *10) // fade-out abruptly, fade-in staggered
|
|
42
|
+
})
|
|
39
43
|
},
|
|
40
44
|
|
|
41
45
|
configToUI(options) { return sendMsgToActiveTab('syncConfigToUI', options) }
|
|
@@ -44,97 +48,85 @@
|
|
|
44
48
|
// Run MAIN routine
|
|
45
49
|
|
|
46
50
|
// Init MASTER TOGGLE
|
|
47
|
-
const masterToggle =
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
const masterToggle = {
|
|
52
|
+
div: document.querySelector('.master-toggle'),
|
|
53
|
+
switch: dom.create.elem('div', { class: 'toggle menu-icon highlight-on-hover' }),
|
|
54
|
+
track: dom.create.elem('span', { class: 'track' })
|
|
55
|
+
}
|
|
56
|
+
masterToggle.div.append(masterToggle.switch) ; masterToggle.switch.append(masterToggle.track)
|
|
57
|
+
await settings.load('extensionDisabled') ; masterToggle.switch.classList.toggle('on', !config.extensionDisabled)
|
|
58
|
+
masterToggle.div.onclick = () => {
|
|
59
|
+
env.extensionWasDisabled = config.extensionDisabled
|
|
60
|
+
masterToggle.switch.classList.toggle('on') ; settings.save('extensionDisabled', !config.extensionDisabled)
|
|
52
61
|
Object.keys(sync).forEach(key => sync[key]()) // sync fade + storage to UI
|
|
53
|
-
notify(`${
|
|
62
|
+
notify(`${app.name} ${ this.checked ? 'ON' : 'OFF' }`)
|
|
54
63
|
}
|
|
55
64
|
|
|
56
65
|
// Create CHILD menu entries on chatgpt.com
|
|
57
66
|
if (env.site == 'chatgpt') {
|
|
67
|
+
const childEntriesDiv = dom.create.elem('div') ; document.body.append(childEntriesDiv)
|
|
58
68
|
await settings.load(Object.keys(settings.controls))
|
|
59
|
-
|
|
60
|
-
// Create/insert child section
|
|
61
|
-
const togglesDiv = dom.create.elem('div', { class: 'menu' })
|
|
62
|
-
document.querySelector('.menu-header').insertAdjacentElement('afterend', togglesDiv)
|
|
63
|
-
|
|
64
|
-
// Create/insert child entries
|
|
65
69
|
Object.keys(settings.controls).forEach(key => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
} else
|
|
81
|
-
|
|
82
|
-
|
|
70
|
+
const controlType = settings.controls[key].type
|
|
71
|
+
|
|
72
|
+
// Init entry's elems
|
|
73
|
+
const entry = {
|
|
74
|
+
div: dom.create.elem('div', {
|
|
75
|
+
class: 'menu-entry highlight-on-hover', title: settings.controls[key].helptip || '' }),
|
|
76
|
+
leftElem: dom.create.elem('div', { class: `menu-icon ${ controlType || '' }` }),
|
|
77
|
+
label: dom.create.elem('span')
|
|
78
|
+
}
|
|
79
|
+
entry.label.textContent = settings.controls[key].label
|
|
80
|
+
entry.div.append(entry.leftElem, entry.label) ; childEntriesDiv.append(entry.div)
|
|
81
|
+
if (controlType == 'toggle') { // add track to left, init knob pos
|
|
82
|
+
entry.leftElem.append(dom.create.elem('span', { class: 'track' }))
|
|
83
|
+
entry.leftElem.classList.toggle('on', settingIsEnabled(key))
|
|
84
|
+
} else { // add symbol to left, append status to right
|
|
85
|
+
entry.leftElem.innerText = settings.controls[key].symbol
|
|
86
|
+
entry.label.innerText += `— ${settings.controls[key].status}`
|
|
83
87
|
}
|
|
84
88
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// Add listeners
|
|
90
|
-
if (settings.controls[key].type == 'toggle') {
|
|
91
|
-
menuItemDiv.onclick = () => menuInput.click()
|
|
92
|
-
menuInput.onclick = menuSlider.onclick = event => // prevent double toggle
|
|
93
|
-
event.stopImmediatePropagation()
|
|
94
|
-
menuInput.onchange = () => {
|
|
89
|
+
entry.div.onclick = () => {
|
|
90
|
+
if (controlType == 'toggle') {
|
|
91
|
+
entry.leftElem.classList.toggle('on')
|
|
95
92
|
settings.save(key, !config[key]) ; sync.configToUI({ updatedKey: key })
|
|
96
|
-
notify(`${settings.controls[key].label} ${
|
|
97
|
-
/disabled|hidden/i.test(key) != config[key] ? 'ON' : 'OFF' }`)
|
|
93
|
+
notify(`${settings.controls[key].label} ${ settingIsEnabled(key) ? 'ON' : 'OFF' }`)
|
|
98
94
|
}
|
|
99
|
-
} else if (settings.controls[key].type == 'prompt') {
|
|
100
|
-
// custom logic for each prompt based on key name
|
|
101
95
|
}
|
|
102
96
|
})
|
|
103
|
-
|
|
104
|
-
sync.fade() // in case master toggle off
|
|
105
97
|
}
|
|
106
98
|
|
|
99
|
+
sync.fade() // based on master toggle
|
|
100
|
+
|
|
107
101
|
// Create/append FOOTER container
|
|
108
102
|
const footer = dom.create.elem('footer') ; document.body.append(footer)
|
|
109
103
|
|
|
110
104
|
// Create/append CHATGPT.JS footer logo
|
|
111
|
-
const
|
|
105
|
+
const cjsSpan = dom.create.elem('span', { class: 'cjs-span' })
|
|
112
106
|
const cjsLogo = dom.create.elem('img', {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
app.urls.cjsAssetHost}/images/badges/powered-by-chatgpt.js${
|
|
117
|
-
event.type == 'mouseover' ? '' : '-faded' }.png?b2a1975`
|
|
118
|
-
cjsLogo.onclick = () => chrome.tabs.create({ url: app.urls.chatgptJS })
|
|
119
|
-
cjsDiv.append(cjsLogo) ; footer.append(cjsDiv)
|
|
107
|
+
src: `${app.urls.cjsAssetHost}/images/badges/powered-by-chatgpt.js.png?b2a1975` })
|
|
108
|
+
cjsSpan.onclick = () => { open(app.urls.chatgptJS) ; close() }
|
|
109
|
+
cjsSpan.append(cjsLogo) ; footer.append(cjsSpan)
|
|
120
110
|
|
|
121
111
|
// Create/append ABOUT footer button
|
|
122
112
|
const aboutSpan = dom.create.elem('span', {
|
|
123
|
-
title:
|
|
124
|
-
class: 'menu-icon
|
|
125
|
-
const aboutIcon = icons.create(
|
|
113
|
+
title: `About ${app.name}`,
|
|
114
|
+
class: 'menu-icon highlight-on-hover', style: 'right:30px ; padding-top: 2px' })
|
|
115
|
+
const aboutIcon = icons.create('questionMark', { width: 15, height: 13, style: 'margin-bottom: 0.04rem' })
|
|
126
116
|
aboutSpan.onclick = () => { chrome.runtime.sendMessage({ action: 'showAbout' }) ; close() }
|
|
127
117
|
aboutSpan.append(aboutIcon) ; footer.append(aboutSpan)
|
|
128
118
|
|
|
129
119
|
// Create/append RELATED EXTENSIONS footer button
|
|
130
120
|
const moreExtensionsSpan = dom.create.elem('span', {
|
|
131
121
|
title: 'More AI Extensions',
|
|
132
|
-
class: 'menu-icon
|
|
133
|
-
const moreExtensionsIcon = icons.create(
|
|
134
|
-
moreExtensionsSpan.onclick = () => {
|
|
122
|
+
class: 'menu-icon highlight-on-hover', style: 'right:2px ; padding-top: 2px' })
|
|
123
|
+
const moreExtensionsIcon = icons.create('plus')
|
|
124
|
+
moreExtensionsSpan.onclick = () => { open(app.urls.relatedExtensions) ; close() }
|
|
135
125
|
moreExtensionsSpan.append(moreExtensionsIcon) ; footer.append(moreExtensionsSpan)
|
|
136
126
|
|
|
137
|
-
// Remove
|
|
138
|
-
document.querySelectorAll('
|
|
127
|
+
// Remove LOADING SPINNER after imgs load
|
|
128
|
+
Promise.all([...document.querySelectorAll('img')].map(img =>
|
|
129
|
+
img.complete ? Promise.resolve() : new Promise(resolve => img.onload = resolve)
|
|
130
|
+
)).then(() => document.querySelectorAll('[class^=loading]').forEach(elem => elem.remove()))
|
|
139
131
|
|
|
140
132
|
})()
|
|
@@ -13,12 +13,7 @@
|
|
|
13
13
|
<img alt="" width=26 src="https://cdn.jsdelivr.net/gh/KudoAI/chatgpt.js@f0cdfc9/starters/chrome/extension/icons/icon32.png">
|
|
14
14
|
</div>
|
|
15
15
|
<div class="menu-title">ChatGPT Extension</div>
|
|
16
|
-
<div class="
|
|
17
|
-
<label class="toggle-switch menu-area menu-icon">
|
|
18
|
-
<input type="checkbox">
|
|
19
|
-
<span class="slider"></span>
|
|
20
|
-
</label>
|
|
21
|
-
</div>
|
|
16
|
+
<div class="master-toggle"></div>
|
|
22
17
|
</div>
|
|
23
18
|
<script src="controller.js"></script>
|
|
24
19
|
</body>
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
/* General
|
|
1
|
+
/* General */
|
|
2
2
|
html { height: fit-content ; min-height: 89px }
|
|
3
|
-
body {
|
|
4
|
-
|
|
5
|
-
/* General font */
|
|
6
|
-
body, button, input, select, textarea {
|
|
3
|
+
body {
|
|
4
|
+
width: max-content ; margin: 0 ; font-size: .905rem ;
|
|
7
5
|
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
8
|
-
"PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", sans-serif
|
|
9
|
-
font-size: .905rem ; user-select: none
|
|
6
|
+
"PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", sans-serif
|
|
10
7
|
}
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
|
|
9
|
+
/* Color/fade mods */
|
|
10
|
+
.highlight-on-hover:hover { background: rgb(100,149,237) }
|
|
11
|
+
.disabled { opacity: 0.3 ; pointer-events: none }
|
|
13
12
|
|
|
14
13
|
/* Loader */
|
|
15
14
|
.loading-bg {
|
|
@@ -32,46 +31,46 @@ a:focus, a:hover { text-decoration: underline ; color: inherit }
|
|
|
32
31
|
@keyframes loader-rotate {
|
|
33
32
|
0% { transform: scaleY(1) rotate(0deg) } 49.99% { transform: scaleY(1) rotate(135deg) }
|
|
34
33
|
50% { transform: scaleY(-1) rotate(0deg) } 100% { transform: scaleY(-1) rotate(-135deg) }
|
|
35
|
-
}
|
|
34
|
+
}
|
|
36
35
|
|
|
37
36
|
/* Header */
|
|
38
37
|
.menu-header {
|
|
39
|
-
border-bottom: solid 1px lightgrey ; padding: 5px 5px 5px 0; margin: 0 ;
|
|
40
|
-
display: flex; background: white ; align-items: center }
|
|
38
|
+
border-bottom: solid 1px lightgrey ; padding: 5px 5px 5px 0 ; margin: 0 ;
|
|
39
|
+
min-height: 38px ; display: flex; background: white ; align-items: center }
|
|
41
40
|
.logo { margin: 4px 8px 4px 12px ; position: relative ; top: 3px }
|
|
42
41
|
.menu-title { font-size: 0.85rem ; font-weight: 600 ; padding-right: 3px }
|
|
43
|
-
.
|
|
44
|
-
.main-toggle { margin-left: auto ; display: flex }
|
|
42
|
+
.master-toggle { margin-left: auto ; display: flex }
|
|
45
43
|
|
|
46
44
|
/* Menu item elements */
|
|
47
|
-
.menu-
|
|
45
|
+
.menu-entry {
|
|
48
46
|
position: relative ; align-items: center ; border-bottom: 1px solid lightgrey ;
|
|
49
|
-
display: flex ; min-height: 2rem ; padding
|
|
47
|
+
display: flex ; min-height: 2rem ; padding: 0 14px 0 2px ; white-space: nowrap ; font-size: 91%
|
|
50
48
|
}
|
|
51
49
|
.menu-icon { padding: 8px }
|
|
52
|
-
.menu-
|
|
53
|
-
.menu-
|
|
54
|
-
.menu-item > label > .slider { transform: scale(0.95) ; top: 1px } /* make child toggles smaller */
|
|
50
|
+
.menu-entry:hover span:not(.track), .menu-entry:hover .caret { filter: invert(1) } /* invert setting labels on hover */
|
|
51
|
+
.menu-entry > label > .track { transform: scale(0.95) ; top: 1px } /* make child toggles smaller */
|
|
55
52
|
.menu-prompt { margin-left: 2px } /* align non-toggle items */
|
|
56
53
|
|
|
57
54
|
/* Toggle elements */
|
|
58
|
-
.toggle
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
position: relative ; top: 0.05rem ; height: 10px ; width: 18px ; cursor: pointer ;
|
|
55
|
+
.toggle .track {
|
|
56
|
+
display: block ; border-radius: 7px ; position: relative ; transform: scale(0.95) ; background: white ;
|
|
57
|
+
border: 1px solid black ; top: 1px ; height: 10px ; width: 18px ; transition: background 0.08s ease-in-out
|
|
62
58
|
}
|
|
63
|
-
.toggle
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
.toggle.on .track { background: black ; transition: background 0.15s ease-in-out }
|
|
60
|
+
.toggle .track::before { /* knob */
|
|
61
|
+
content: "" ; position: absolute ; height: 0.625rem ; width: 0.625rem ; left: -0.04rem ; top: -0.05rem ;
|
|
62
|
+
border: 1px solid black ; border-radius: 50% ; background: white ; transition: transform 0.08s ease-in-out
|
|
66
63
|
}
|
|
67
|
-
.toggle
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
.toggle.on .track::before { transform: translateX(9px) ; transition: transform 0.15s ease-in-out }
|
|
65
|
+
|
|
70
66
|
/* Footer */
|
|
71
|
-
footer {
|
|
67
|
+
footer {
|
|
68
|
+
font-size: 12px ; text-align: center ; color: #999 ; background: #f5f5f5 ; height: 40px ; line-height: 40px }
|
|
72
69
|
footer > .menu-icon { position: absolute ; bottom: -10px ; opacity: 0.7 }
|
|
73
|
-
.
|
|
70
|
+
.cjs-span { position: absolute ; bottom: -.25rem ; left: 0.7rem }
|
|
71
|
+
.cjs-span img { opacity: 0.5 } .cjs-span:hover img { opacity: 1 ; transition: 0.25s }
|
|
74
72
|
|
|
75
|
-
/*
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
/* Non-baseline features */
|
|
74
|
+
@supports (overflow: clip) { body { overflow: clip }}
|
|
75
|
+
@supports (user-select: none) { body, button, input, select, textarea { user-select: none }}
|
|
76
|
+
@supports (cursor: pointer) { .highlight-on-hover:hover, .toggle .track, .cjs-span { cursor: pointer }}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
const chatgptURL = 'https://chatgpt.com'
|
|
2
|
+
|
|
1
3
|
// Init APP data
|
|
2
4
|
const app = {
|
|
5
|
+
name: chrome.runtime.getManifest().name,
|
|
3
6
|
version: chrome.runtime.getManifest().version, symbol: '🤖', cssPrefix: 'chatgpt-extension',
|
|
4
7
|
author: { name: 'KudoAI', url: 'https://kudoai.com' },
|
|
5
8
|
urls: {
|
|
@@ -12,12 +15,12 @@ const app = {
|
|
|
12
15
|
support: 'https://github.com/KudoAI/chatgpt.js-chrome-starter/issues'
|
|
13
16
|
}
|
|
14
17
|
}
|
|
15
|
-
chrome.storage.
|
|
18
|
+
chrome.storage.local.set({ app }) // save to Chrome storage
|
|
16
19
|
|
|
17
20
|
// Launch CHATGPT on install
|
|
18
21
|
chrome.runtime.onInstalled.addListener(details => {
|
|
19
|
-
if (details.reason == 'install')
|
|
20
|
-
chrome.tabs.create({ url:
|
|
22
|
+
if (details.reason == 'install') // to exclude updates
|
|
23
|
+
chrome.tabs.create({ url: chatgptURL })
|
|
21
24
|
})
|
|
22
25
|
|
|
23
26
|
// Sync SETTINGS to activated tabs
|
|
@@ -29,13 +32,11 @@ chrome.runtime.onMessage.addListener(async req => {
|
|
|
29
32
|
if (req.action == 'showAbout') {
|
|
30
33
|
const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true })
|
|
31
34
|
const chatgptTab = new URL(activeTab.url).hostname == 'chatgpt.com' ? activeTab
|
|
32
|
-
: await chrome.tabs.create({ url:
|
|
33
|
-
if (activeTab != chatgptTab)
|
|
34
|
-
chrome.tabs.onUpdated.addListener(
|
|
35
|
-
if (tabId == chatgptTab.id &&
|
|
36
|
-
chrome.tabs.onUpdated.removeListener(
|
|
37
|
-
|
|
38
|
-
}}))
|
|
39
|
-
chrome.tabs.sendMessage(chatgptTab.id, { action: 'showAbout' })
|
|
35
|
+
: await chrome.tabs.create({ url: chatgptURL })
|
|
36
|
+
if (activeTab != chatgptTab) new Promise(resolve => // after new tab loads
|
|
37
|
+
chrome.tabs.onUpdated.addListener(function loadedListener(tabId, changeInfo) {
|
|
38
|
+
if (tabId == chatgptTab.id && changeInfo.status == 'complete') {
|
|
39
|
+
chrome.tabs.onUpdated.removeListener(loadedListener) ; setTimeout(resolve, 500)
|
|
40
|
+
}})).then(() => chrome.tabs.sendMessage(chatgptTab.id, { action: 'showAbout' }))
|
|
40
41
|
}
|
|
41
42
|
})
|
|
@@ -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.21.1
|
|
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.
|
|
11
|
+
// @require https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.7.1/dist/chatgpt.min.js
|
|
12
12
|
// @grant GM_getValue
|
|
13
13
|
// @grant GM_setValue
|
|
14
14
|
// @noframes
|