@kudoai/chatgpt.js 3.8.3 → 3.8.5

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.
@@ -3,8 +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.8.14",
7
- "author": "KudoAI",
6
+ "version": "2026.1.4",
8
7
  "homepage_url": "https://github.com/KudoAI/chatgpt.js-chrome-starter",
9
8
  "icons": { "16": "icons/icon16.png", "32": "icons/icon32.png", "64": "icons/icon64.png", "128": "icons/icon128.png" },
10
9
  "permissions": ["activeTab", "storage"],
@@ -8,61 +8,89 @@
8
8
  for (const resource of ['components/icons.js', 'lib/dom.js', 'lib/settings.js'])
9
9
  await import(chrome.runtime.getURL(resource))
10
10
 
11
- // Init ENV context
11
+ // Init DATA
12
12
  window.env = {
13
13
  site: new URL((await chrome.tabs.query({ active: true, currentWindow: true }))[0].url)
14
14
  .hostname.split('.').slice(-2, -1)[0], // extract 2nd-level domain
15
15
  menu: { isDark: document.documentElement.classList.contains('dark') }
16
16
  }
17
-
18
- // Import DATA
19
17
  ;({ app: window.app } = await chrome.storage.local.get('app'))
20
18
 
21
19
  // Define FUNCTIONS
22
20
 
23
21
  function createMenuEntry(entryData) {
24
-
25
- // Assemble elems
26
22
  const entry = {
27
23
  div: dom.create.elem('div', {
28
24
  id: entryData.key, class: 'menu-entry highlight-on-hover', title: entryData.helptip || '' }),
29
25
  leftElem: dom.create.elem('div', { class: `menu-icon ${ entryData.type || '' }`}),
30
- label: dom.create.elem('span')
26
+ label: dom.create.elem('span', { textContent: entryData.label })
31
27
  }
32
- entry.label.textContent = entryData.label
33
28
  entry.div.append(entry.leftElem, entry.label)
29
+
34
30
  if (entryData.type == 'toggle') { // add track to left, init knob pos
35
31
  entry.leftElem.append(dom.create.elem('span', { class: 'track' }))
36
32
  entry.leftElem.classList.toggle('on', settings.typeIsEnabled(entryData.key))
33
+
37
34
  } else { // add symbol to left, append status to right
38
35
  entry.leftElem.textContent = entryData.symbol || '⚙️' ; entry.label.style.flexGrow = 1
39
36
  if (entryData.status) entry.label.textContent += ` — ${entryData.status}`
40
37
  if (entryData.type == 'link') {
41
38
  entry.label.after(entry.rightElem = dom.create.elem('div', { class: 'menu-right-elem' }))
42
- entry.rightElem.append(icons.create({ key: 'open', size: 17, fill: 'black' }))
39
+ if (entryData.favicon) entry.favicon = dom.create.elem('img', { width: 15,
40
+ src: typeof entryData.favicon == 'string' ? entryData.favicon
41
+ : `https://www.google.com/s2/favicons?domain=${new URL(entryData.url).hostname}` })
42
+ entry.openIcon = icons.create({ key: 'open', size: 17, fill: 'black' })
43
+ entry.rightElem.append(entry.favicon || entry.openIcon)
44
+ if (entry.favicon) entry.rightElem.onmouseenter = entry.rightElem.onmouseleave = ({ type }) =>
45
+ entry.rightElem.firstChild.replaceWith(entry[type == 'mouseenter' ? 'openIcon' : 'favicon'])
43
46
  }
44
47
  }
45
- if (entryData.type == 'category')
48
+
49
+ if (entryData.type == 'category') // append drop-down caret
46
50
  entry.div.append(icons.create({ key: 'caretDown', size: 11, class: 'menu-caret menu-right-elem' }))
51
+
47
52
  else if (entryData.type == 'slider') { // append slider, add listeners, remove .highlight-on-hover
48
- entry.slider = dom.create.elem('input', { class: 'slider', type: 'range',
49
- min: entryData.min || 0, max: entryData.max || 100, value: config[entryData.key] })
53
+ const minVal = entryData.min ?? 0, maxVal = entryData.max ?? 100
54
+
55
+ // Create/append slider elems
56
+ entry.div.append(entry.slider = dom.create.elem('input', { class: 'slider', type: 'range',
57
+ min: minVal, max: maxVal, value: config[entryData.key] }))
58
+ entry.div.classList.remove('highlight-on-hover')
50
59
  if (entryData.step || env.browser.isFF) // use val from entryData or default to 2% in FF for being laggy
51
60
  entry.slider.step = entryData.step || ( 0.02 * entry.slider.max - entry.slider.min )
52
61
  entry.label.textContent += `: ${entry.slider.value}${ entryData.labelSuffix || '' }`
62
+ entry.label.append(entry.editLink = dom.create.elem('span', {
63
+ class: 'edit-link', role: 'button', tabindex: '0', 'aria-label': entryData.helptip, textContent: 'Edit'
64
+ }))
53
65
  entry.slider.style.setProperty('--track-fill-percent', `${ entry.slider.value / entry.slider.max *100 }%`)
66
+
67
+ // Add listeners
68
+ entry.editLink.onclick = () => {
69
+ const promptMsg = `Enter new value for ${entryData.label} (between ${minVal}–${maxVal}):`,
70
+ userVal = prompt(promptMsg, entry.slider.value)
71
+ if (userVal == null) return // user cancelled so do nothing
72
+ if (!/\d/.test(userVal)) return alert(`Enter a valid number between ${minVal} and ${maxVal}!`)
73
+ let validVal = parseInt(userVal.replace(/\D/g, '')) ; if (isNaN(validVal)) return
74
+ validVal = Math.max(minVal, Math.min(maxVal, validVal))
75
+ entry.slider.value = validVal ; settings.save(entryData.key, validVal)
76
+ sync.configToUI({ updatedKey: entryData.key })
77
+ entry.label.textContent = `${entryData.label}: ${validVal}${ entryData.labelSuffix || '' }`
78
+ entry.label.append(entry.editLink)
79
+ entry.slider.style.setProperty('--track-fill-percent', `${ validVal / entry.slider.max *100 }%`)
80
+ }
54
81
  entry.slider.oninput = ({ target: { value }}) => { // update label/color
55
82
  settings.save(entryData.key, parseInt(value)) ; sync.configToUI({ updatedKey: entryData.key })
56
83
  entry.label.textContent = `${entryData.label}: ${value}${ entryData.labelSuffix || '' }`
84
+ entry.label.append(entry.editLink)
57
85
  entry.slider.style.setProperty('--track-fill-percent', `${ value / entry.slider.max *100 }%`)
58
86
  }
59
- entry.div.onwheel = event => { // move slider by 2 steps
60
- entry.slider.value = parseInt(entry.slider.value) -Math.sign(event.deltaY) *2
87
+ entry.div.onwheel = ({ deltaY }) => { // move slider by 2 steps
88
+ entry.slider.value = parseInt(entry.slider.value) - Math.sign(deltaY) *2
61
89
  entry.slider.dispatchEvent(new Event('input'))
62
90
  }
63
- entry.div.append(entry.slider) ; entry.div.classList.remove('highlight-on-hover')
64
91
  }
65
- if (entryData.dependencies) {
92
+
93
+ if (entryData.dependencies) { // hide/show according to toggle state
66
94
  const toDisable = Object.values(entryData.dependencies).flat().some(dep => !settings.typeIsEnabled(dep))
67
95
  Object.assign(entry.div.style, {
68
96
  transition: '', minHeight: 'auto', opacity: +!toDisable,
@@ -83,8 +111,8 @@
83
111
  entry.leftElem.classList.toggle('on')
84
112
  settings.save(entryData.key, !config[entryData.key])
85
113
  sync.configToUI({ updatedKey: entryData.key })
86
- requestAnimationFrame(() => notify(`${entryData.label} ${chrome.i18n.getMessage(`state_${
87
- settings.typeIsEnabled(entryData.key) ? 'on' : 'off' }`).toUpperCase()}`))
114
+ requestAnimationFrame(() => notify(
115
+ `${entryData.label} ${['OFF', 'ON'][+settings.typeIsEnabled(entryData.key)]}`))
88
116
  },
89
117
  link: () => { open(entryData.url) ; close() }
90
118
  })[entryData.type]()
@@ -99,8 +127,7 @@
99
127
  for (const [ctrlKey, ctrlData] of Object.entries({ ...settings.categories, ...settings.controls }))
100
128
  if (Object.values(ctrlData.dependencies || {}).flat().includes(entryData.key)) {
101
129
  const depDiv = document.querySelector(`div#${ctrlKey}`) ; if (!depDiv) continue
102
- const ctgChildrenDiv = depDiv.closest('.categorized-entries'),
103
- ctgChildren = ctgChildrenDiv.querySelectorAll('.menu-entry'),
130
+ const ctgChildren = depDiv.closest('.categorized-entries').querySelectorAll('.menu-entry'),
104
131
  toDisable = !settings.typeIsEnabled(entryData.key)
105
132
  requestAnimationFrame(() => Object.assign(depDiv.closest('.categorized-entries').style, {
106
133
  height: `${dom.get.computedHeight(ctgChildren)}px`,
@@ -144,7 +171,7 @@
144
171
  if (elem.id && (elem.matches(`#${elem.id}:has(> div.link)`) || elem.id == 'aboutEntry'))
145
172
  return // never disable link/About entries
146
173
  elem.style.transition = config.extensionDisabled ? '' : 'opacity 0.15s ease-in'
147
- const toDisable = config.extensionIsDisabled || !depIsEnabled(elem.id)
174
+ const toDisable = config.extensionDisabled || !depIsEnabled(elem.id)
148
175
  if (elem.classList.contains('categorized-entries')) { // fade category strip
149
176
  elem.style.transition = toDisable ? 'none' : 'var(--border-transition)'
150
177
  elem.style.borderImage = elem.style.borderImage.replace(
@@ -207,7 +234,6 @@
207
234
  masterToggle.div.append(masterToggle.switch) ; masterToggle.switch.append(masterToggle.track)
208
235
  await settings.load('extensionDisabled') ; masterToggle.switch.classList.toggle('on', !config.extensionDisabled)
209
236
  masterToggle.div.onclick = () => {
210
- env.extensionWasDisabled = config.extensionDisabled
211
237
  masterToggle.switch.classList.toggle('on') ; settings.save('extensionDisabled', !config.extensionDisabled)
212
238
  Object.keys(sync).forEach(key => sync[key]()) // sync fade + storage to UI
213
239
  notify(`${app.name} ${ this.checked ? 'ON' : 'OFF' }`)
@@ -228,17 +254,17 @@
228
254
  Object.values(categorizedCtrls.general || {}).forEach(ctrl => menuEntriesDiv.append(createMenuEntry(ctrl)))
229
255
 
230
256
  // Create/append categorized controls
231
- Object.entries(categorizedCtrls).forEach(([category, ctrls]) => {
232
- if (category == 'general') return
233
- const ctgData = { ...settings.categories[category], key: category, type: 'category' },
234
- ctgChildrenDiv = dom.create.elem('div', { class: 'categorized-entries' })
235
- if (ctgData.color) { // color the stripe
236
- const [r, g, b] = ctgData.color.match(/\w\w/g).map(v => parseInt(v, 16))
257
+ Object.entries(settings.categories).forEach(([key, category]) => {
258
+ if (!categorizedCtrls[key]) return
259
+ category.key = key ; category.type = 'category'
260
+ const ctgChildrenDiv = dom.create.elem('div', { class: 'categorized-entries' })
261
+ if (category.color) { // color the stripe
262
+ const [r, g, b] = category.color.match(/\w\w/g).map(v => parseInt(v, 16))
237
263
  ctgChildrenDiv.style.borderImage = `linear-gradient(transparent, rgba(${r},${g},${b},${
238
264
  env.menu.isDark ? 0.5 : 1 })) 30 100% ${ env.menu.isDark ? '/ 100' : '' }`
239
265
  }
240
- menuEntriesDiv.append(createMenuEntry(ctgData), ctgChildrenDiv)
241
- Object.values(ctrls).forEach(ctrl => ctgChildrenDiv.append(createMenuEntry(ctrl)))
266
+ menuEntriesDiv.append(createMenuEntry(category), ctgChildrenDiv)
267
+ Object.values(categorizedCtrls[key]).forEach(ctrl => ctgChildrenDiv.append(createMenuEntry(ctrl)))
242
268
  })
243
269
  }
244
270
 
@@ -261,15 +287,18 @@
261
287
  aboutEntry.div.onclick = () => { chrome.runtime.sendMessage({ action: 'showAbout' }) ; close() }
262
288
 
263
289
  // Create/append CHATGPT entry
264
- const activeTabURL = (await chrome.tabs.query({ active: true, currentWindow: true }))[0].url,
290
+ const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true }),
265
291
  chatgptURL = chrome.runtime.getManifest().content_scripts[0].matches.map(url => url.replace(/\/\*$/, ''))
266
- if (!activeTabURL.includes(chatgptURL)) footer.before(createMenuEntry({
267
- key: 'chatgptEntry', type: 'link', symbol: '🤖', label: 'Open ChatGPT', url: chatgptURL, helptip: chatgptURL }))
292
+ if (!activeTab.url.includes(chatgptURL))
293
+ footer.before(createMenuEntry({
294
+ key: 'chatgptEntry', type: 'link', symbol: '🤖', url: chatgptURL, helptip: chatgptURL, label: 'Open ChatGPT',
295
+ favicon: 'data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%22180%22%20height=%22180%22%20fill=%22none%22%3E%3Cstyle%3E:root%7B--primary-fill:%23000;--secondary-fill:%23fff;%7D@media%20(prefers-color-scheme:dark)%7B:root%7B--primary-fill:%23fff;--secondary-fill:%23000;%7D%7D%3C/style%3E%3Cg%20clip-path=%22url(%23a)%22%3E%3Crect%20width=%22180%22%20height=%22180%22%20fill=%22var(--primary-fill)%22%20rx=%2290%22/%3E%3Cg%20clip-path=%22url(%23b)%22%3E%3Cpath%20fill=%22var(--secondary-fill)%22%20d=%22M75.91%2073.628V62.232c0-.96.36-1.68%201.199-2.16l22.912-13.194c3.119-1.8%206.838-2.639%2010.676-2.639%2014.394%200%2023.511%2011.157%2023.511%2023.032%200%20.839%200%201.799-.12%202.758l-23.752-13.914c-1.439-.84-2.879-.84-4.318%200L75.91%2073.627Zm53.499%2044.383v-27.23c0-1.68-.72-2.88-2.159-3.719L97.142%2069.55l9.836-5.638c.839-.48%201.559-.48%202.399%200l22.912%2013.195c6.598%203.839%2011.035%2011.995%2011.035%2019.912%200%209.116-5.397%2017.513-13.915%2020.992v.001Zm-60.577-23.99-9.836-5.758c-.84-.48-1.2-1.2-1.2-2.16v-26.39c0-12.834%209.837-22.55%2023.152-22.55%205.039%200%209.716%201.679%2013.676%204.678L70.993%2055.516c-1.44.84-2.16%202.039-2.16%203.719v34.787-.002Zm21.173%2012.234L75.91%2098.339V81.546l14.095-7.917%2014.094%207.917v16.793l-14.094%207.916Zm9.056%2036.467c-5.038%200-9.716-1.68-13.675-4.678l23.631-13.676c1.439-.839%202.159-2.038%202.159-3.718V85.863l9.956%205.757c.84.48%201.2%201.2%201.2%202.16v26.389c0%2012.835-9.957%2022.552-23.27%2022.552v.001Zm-28.43-26.75L47.72%20102.778c-6.599-3.84-11.036-11.996-11.036-19.913%200-9.236%205.518-17.513%2014.034-20.992v27.35c0%201.68.72%202.879%202.16%203.718l29.989%2017.393-9.837%205.638c-.84.48-1.56.48-2.399%200Zm-1.318%2019.673c-13.555%200-23.512-10.196-23.512-22.792%200-.959.12-1.919.24-2.879l23.63%2013.675c1.44.84%202.88.84%204.32%200l30.108-17.392v11.395c0%20.96-.361%201.68-1.2%202.16l-22.912%2013.194c-3.119%201.8-6.837%202.639-10.675%202.639Zm29.748%2014.274c14.515%200%2026.63-10.316%2029.39-23.991%2013.434-3.479%2022.071-16.074%2022.071-28.91%200-8.396-3.598-16.553-10.076-22.43.6-2.52.96-5.039.96-7.557%200-17.153-13.915-29.99-29.989-29.99-3.239%200-6.358.48-9.477%201.56-5.398-5.278-12.835-8.637-20.992-8.637-14.515%200-26.63%2010.316-29.39%2023.991-13.434%203.48-22.07%2016.074-22.07%2028.91%200%208.396%203.598%2016.553%2010.075%2022.431-.6%202.519-.96%205.038-.96%207.556%200%2017.154%2013.915%2029.989%2029.99%2029.989%203.238%200%206.357-.479%209.476-1.559%205.397%205.278%2012.835%208.637%2020.992%208.637Z%22/%3E%3C/g%3E%3C/g%3E%3Cdefs%3E%3CclipPath%20id=%22a%22%3E%3Cpath%20d=%22M0%200h180v180H0z%22/%3E%3C/clipPath%3E%3CclipPath%20id=%22b%22%3E%3Cpath%20d=%22M29.487%2029.964h121.035v119.954H29.487z%22/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E'
296
+ }))
268
297
 
269
298
  // Create/append LATEST CHANGES entry
270
299
  const latestChangesURL = `${app.urls.github}/commits`
271
300
  footer.before(createMenuEntry({
272
- key: 'latestChangesEntry', type: 'link', symbol: '🚀',
301
+ key: 'latestChangesEntry', type: 'link', symbol: '🚀', favicon: true,
273
302
  label: 'Latest Changes...', url: latestChangesURL, helptip: latestChangesURL
274
303
  }))
275
304
 
@@ -8,13 +8,12 @@ body {
8
8
  width: 268px ; margin: 0 ; font-size: .905rem ;
9
9
  font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto,
10
10
  "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", sans-serif ;
11
- background-image: linear-gradient(180deg, #b6ebff -163px, white 65px)
11
+ background-image: linear-gradient(180deg, #b6ebff -96px, white 65px)
12
12
  }
13
13
 
14
14
  /* Color/fade mods */
15
15
  .highlight-on-hover:hover { background: var(--entry-highlighted-bg) }
16
- .highlight-on-hover:hover span:not(.track), .highlight-on-hover:hover svg, span.menu-icon.highlight-on-hover:hover img {
17
- filter: invert(1) } /* invert setting labels on hover */
16
+ .highlight-on-hover:hover :where(span:not(.track), svg) { filter: invert(1) } /* invert setting labels on hover */
18
17
  .disabled { opacity: 0.3 !important ; pointer-events: none }
19
18
 
20
19
  /* Loader */
@@ -45,7 +44,7 @@ header {
45
44
  border-bottom: solid 1px lightgrey ; padding: 5px 5px 5px 0 ; margin: 0 ;
46
45
  height: 38px ; display: flex ; align-items: center }
47
46
  .logo { margin: 0 8px 0 12px ; position: relative ; top: 3px }
48
- .menu-title { font-size: 0.85rem ; font-weight: 600 ; padding-right: 3px }
47
+ .menu-title { font-size: 1rem ; font-weight: 600 ; padding: 4px }
49
48
  .master-toggle { margin-left: auto ; display: flex }
50
49
 
51
50
  /* Menu item elements */
@@ -84,6 +83,8 @@ div.menu-entry > input.slider {
84
83
  border: 4px solid var(--thumb-border) ; border-radius: 16px ; cursor: ew-resize ; transition: transform 0.05s ease
85
84
  }
86
85
  .slider::-webkit-slider-thumb:hover { transform: scaleX(1.325) }
86
+ .edit-link { text-transform: uppercase ; font-size: 0.65em ; margin-left: 0.75em ; opacity: 0.5 ; cursor: pointer }
87
+ .edit-link:hover { transition: opacity 0.1s ease-in-out ; opacity: 1 }
87
88
 
88
89
  /* Toggle elements */
89
90
  .toggle .track {
@@ -105,7 +106,7 @@ div#about > div.menu-icon { padding: 8px 10px 8px 11px }
105
106
  mask-image: var(--mask) ; -webkit-mask-image: var(--mask) /* eslint-disable-line */
106
107
  }
107
108
  .ticker-em { color: green } .highlight-on-hover:hover .ticker-em { color: #28ee28 }
108
- .ticker > div { animation: ticker linear 75s infinite }
109
+ .ticker > div { animation: ticker linear 85s infinite }
109
110
  @keyframes ticker { 0% { transform: translateX(100%) } 100% { transform: translateX(-2000%) }}
110
111
 
111
112
  /* Footer */
@@ -139,6 +140,7 @@ html.dark .menu-icon svg, html.dark .menu-right-elem, html.dark .menu-right-elem
139
140
  html.dark .menu-icon img { filter: brightness(0) invert(1) }
140
141
  html.dark .menu-icon:hover img { filter: none }
141
142
  html.dark .ticker-em { color: #28ee28 } html.dark .highlight-on-hover:hover .ticker-em { color: green }
143
+ html.dark #chatgptEntry:hover img { filter: invert(1) } /* to white favicon for legibility */
142
144
 
143
145
  /* Non-baseline features */
144
146
  @supports (cursor: pointer) { .highlight-on-hover:hover, .toggle .track, .chatgptjs-logo { cursor: pointer }}
@@ -8,33 +8,33 @@ chrome.storage.local.set({ app: {
8
8
  urls: {
9
9
  chatgptjs: 'https://chatgptjs.org',
10
10
  contributors: 'https://docs.chatgptjs.org/#-contributors',
11
- gitHub: 'https://github.com/KudoAI/chatgpt.js-chrome-starter',
11
+ github: 'https://github.com/KudoAI/chatgpt.js-chrome-starter',
12
12
  relatedExtensions: 'https://aiwebextensions.com',
13
13
  support: 'https://github.com/KudoAI/chatgpt.js-chrome-starter/issues'
14
14
  }
15
15
  }}) // save to Chrome storage
16
16
 
17
17
  // Launch CHATGPT on install
18
- chrome.runtime.onInstalled.addListener(details => {
19
- if (details.reason == 'install') // to exclude updates
18
+ chrome.runtime.onInstalled.addListener(({ reason }) => {
19
+ if (reason == 'install') // to exclude updates
20
20
  chrome.tabs.create({ url: chatgptURL })
21
21
  })
22
22
 
23
23
  // Sync SETTINGS to activated tabs
24
- chrome.tabs.onActivated.addListener(activeInfo =>
25
- chrome.tabs.sendMessage(activeInfo.tabId, { action: 'syncConfigToUI' }))
24
+ chrome.tabs.onActivated.addListener(({ tabId }) =>
25
+ chrome.tabs.sendMessage(tabId, { action: 'syncConfigToUI' }))
26
26
 
27
27
  // Show ABOUT modal on ChatGPT when toolbar button clicked
28
- chrome.runtime.onMessage.addListener(async req => {
29
- if (req.action == 'showAbout') {
28
+ chrome.runtime.onMessage.addListener(async ({ action }) => {
29
+ if (action == 'showAbout') {
30
30
  const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true })
31
31
  const chatgptTab = new URL(activeTab.url).hostname == 'chatgpt.com' ? activeTab
32
- : await chrome.tabs.create({ url: chatgptURL })
32
+ : await chrome.tabs.create({ url: chatgptURL })
33
33
  if (activeTab != chatgptTab) await new Promise(resolve => // after new tab loads
34
- chrome.tabs.onUpdated.addListener(function loadedListener(tabId, changeInfo) {
35
- if (tabId == chatgptTab.id && changeInfo.status == 'complete') {
36
- chrome.tabs.onUpdated.removeListener(loadedListener) ; setTimeout(resolve, 500)
34
+ chrome.tabs.onUpdated.addListener(function loadedListener(tabId, { status }) {
35
+ if (tabId == chatgptTab.id && status == 'complete') {
36
+ chrome.tabs.onUpdated.removeListener(loadedListener) ; setTimeout(resolve, 1500)
37
37
  }}))
38
- chrome.tabs.sendMessage(chatgptTab.id, { action: 'showAbout' })
38
+ chrome.tabs.sendMessage(chatgptTab.id, { action: 'showAbout', source: 'service-worker.js' })
39
39
  }
40
40
  })
@@ -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.8.14
6
+ // @version 2026.1.4
7
7
  // @license MIT
8
8
  // @icon data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='180'%20height='180'%20fill='none'%3e%3cstyle%3e%20:root%20{%20--primary-fill:%20%23000;%20--secondary-fill:%20%23fff;%20}%20@media%20(prefers-color-scheme:%20dark)%20{%20:root%20{%20--primary-fill:%20%23fff;%20--secondary-fill:%20%23000;%20}%20}%20%3c/style%3e%3cg%20clip-path='url(%23a)'%3e%3crect%20width='180'%20height='180'%20fill='var(--primary-fill)'%20rx='90'%20/%3e%3cg%20clip-path='url(%23b)'%3e%3cpath%20fill='var(--secondary-fill)'%20d='M75.91%2073.628V62.232c0-.96.36-1.68%201.199-2.16l22.912-13.194c3.119-1.8%206.838-2.639%2010.676-2.639%2014.394%200%2023.511%2011.157%2023.511%2023.032%200%20.839%200%201.799-.12%202.758l-23.752-13.914c-1.439-.84-2.879-.84-4.318%200L75.91%2073.627Zm53.499%2044.383v-27.23c0-1.68-.72-2.88-2.159-3.719L97.142%2069.55l9.836-5.638c.839-.48%201.559-.48%202.399%200l22.912%2013.195c6.598%203.839%2011.035%2011.995%2011.035%2019.912%200%209.116-5.397%2017.513-13.915%2020.992v.001Zm-60.577-23.99-9.836-5.758c-.84-.48-1.2-1.2-1.2-2.16v-26.39c0-12.834%209.837-22.55%2023.152-22.55%205.039%200%209.716%201.679%2013.676%204.678L70.993%2055.516c-1.44.84-2.16%202.039-2.16%203.719v34.787-.002Zm21.173%2012.234L75.91%2098.339V81.546l14.095-7.917%2014.094%207.917v16.793l-14.094%207.916Zm9.056%2036.467c-5.038%200-9.716-1.68-13.675-4.678l23.631-13.676c1.439-.839%202.159-2.038%202.159-3.718V85.863l9.956%205.757c.84.48%201.2%201.2%201.2%202.16v26.389c0%2012.835-9.957%2022.552-23.27%2022.552v.001Zm-28.43-26.75L47.72%20102.778c-6.599-3.84-11.036-11.996-11.036-19.913%200-9.236%205.518-17.513%2014.034-20.992v27.35c0%201.68.72%202.879%202.16%203.718l29.989%2017.393-9.837%205.638c-.84.48-1.56.48-2.399%200Zm-1.318%2019.673c-13.555%200-23.512-10.196-23.512-22.792%200-.959.12-1.919.24-2.879l23.63%2013.675c1.44.84%202.88.84%204.32%200l30.108-17.392v11.395c0%20.96-.361%201.68-1.2%202.16l-22.912%2013.194c-3.119%201.8-6.837%202.639-10.675%202.639Zm29.748%2014.274c14.515%200%2026.63-10.316%2029.39-23.991%2013.434-3.479%2022.071-16.074%2022.071-28.91%200-8.396-3.598-16.553-10.076-22.43.6-2.52.96-5.039.96-7.557%200-17.153-13.915-29.99-29.989-29.99-3.239%200-6.358.48-9.477%201.56-5.398-5.278-12.835-8.637-20.992-8.637-14.515%200-26.63%2010.316-29.39%2023.991-13.434%203.48-22.07%2016.074-22.07%2028.91%200%208.396%203.598%2016.553%2010.075%2022.431-.6%202.519-.96%205.038-.96%207.556%200%2017.154%2013.915%2029.989%2029.99%2029.989%203.238%200%206.357-.479%209.476-1.559%205.397%205.278%2012.835%208.637%2020.992%208.637Z'%20/%3e%3c/g%3e%3c/g%3e%3cdefs%3e%3cclipPath%20id='a'%3e%3cpath%20d='M0%200h180v180H0z'%20/%3e%3c/clipPath%3e%3cclipPath%20id='b'%3e%3cpath%20d='M29.487%2029.964h121.035v119.954H29.487z'%20/%3e%3c/clipPath%3e%3c/defs%3e%3c/svg%3e
9
9
  // @icon64 data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='180'%20height='180'%20fill='none'%3e%3cstyle%3e%20:root%20{%20--primary-fill:%20%23000;%20--secondary-fill:%20%23fff;%20}%20@media%20(prefers-color-scheme:%20dark)%20{%20:root%20{%20--primary-fill:%20%23fff;%20--secondary-fill:%20%23000;%20}%20}%20%3c/style%3e%3cg%20clip-path='url(%23a)'%3e%3crect%20width='180'%20height='180'%20fill='var(--primary-fill)'%20rx='90'%20/%3e%3cg%20clip-path='url(%23b)'%3e%3cpath%20fill='var(--secondary-fill)'%20d='M75.91%2073.628V62.232c0-.96.36-1.68%201.199-2.16l22.912-13.194c3.119-1.8%206.838-2.639%2010.676-2.639%2014.394%200%2023.511%2011.157%2023.511%2023.032%200%20.839%200%201.799-.12%202.758l-23.752-13.914c-1.439-.84-2.879-.84-4.318%200L75.91%2073.627Zm53.499%2044.383v-27.23c0-1.68-.72-2.88-2.159-3.719L97.142%2069.55l9.836-5.638c.839-.48%201.559-.48%202.399%200l22.912%2013.195c6.598%203.839%2011.035%2011.995%2011.035%2019.912%200%209.116-5.397%2017.513-13.915%2020.992v.001Zm-60.577-23.99-9.836-5.758c-.84-.48-1.2-1.2-1.2-2.16v-26.39c0-12.834%209.837-22.55%2023.152-22.55%205.039%200%209.716%201.679%2013.676%204.678L70.993%2055.516c-1.44.84-2.16%202.039-2.16%203.719v34.787-.002Zm21.173%2012.234L75.91%2098.339V81.546l14.095-7.917%2014.094%207.917v16.793l-14.094%207.916Zm9.056%2036.467c-5.038%200-9.716-1.68-13.675-4.678l23.631-13.676c1.439-.839%202.159-2.038%202.159-3.718V85.863l9.956%205.757c.84.48%201.2%201.2%201.2%202.16v26.389c0%2012.835-9.957%2022.552-23.27%2022.552v.001Zm-28.43-26.75L47.72%20102.778c-6.599-3.84-11.036-11.996-11.036-19.913%200-9.236%205.518-17.513%2014.034-20.992v27.35c0%201.68.72%202.879%202.16%203.718l29.989%2017.393-9.837%205.638c-.84.48-1.56.48-2.399%200Zm-1.318%2019.673c-13.555%200-23.512-10.196-23.512-22.792%200-.959.12-1.919.24-2.879l23.63%2013.675c1.44.84%202.88.84%204.32%200l30.108-17.392v11.395c0%20.96-.361%201.68-1.2%202.16l-22.912%2013.194c-3.119%201.8-6.837%202.639-10.675%202.639Zm29.748%2014.274c14.515%200%2026.63-10.316%2029.39-23.991%2013.434-3.479%2022.071-16.074%2022.071-28.91%200-8.396-3.598-16.553-10.076-22.43.6-2.52.96-5.039.96-7.557%200-17.153-13.915-29.99-29.989-29.99-3.239%200-6.358.48-9.477%201.56-5.398-5.278-12.835-8.637-20.992-8.637-14.515%200-26.63%2010.316-29.39%2023.991-13.434%203.48-22.07%2016.074-22.07%2028.91%200%208.396%203.598%2016.553%2010.075%2022.431-.6%202.519-.96%205.038-.96%207.556%200%2017.154%2013.915%2029.989%2029.99%2029.989%203.238%200%206.357-.479%209.476-1.559%205.397%205.278%2012.835%208.637%2020.992%208.637Z'%20/%3e%3c/g%3e%3c/g%3e%3cdefs%3e%3cclipPath%20id='a'%3e%3cpath%20d='M0%200h180v180H0z'%20/%3e%3c/clipPath%3e%3cclipPath%20id='b'%3e%3cpath%20d='M29.487%2029.964h121.035v119.954H29.487z'%20/%3e%3c/clipPath%3e%3c/defs%3e%3c/svg%3e
10
10
  // @match *://chatgpt.com/*
11
- // @require https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.8.3/dist/chatgpt.min.js
11
+ // @require https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.8.5/dist/chatgpt.min.js
12
12
  // @grant GM_getValue
13
13
  // @grant GM_setValue
14
14
  // @noframes
@@ -19,6 +19,7 @@
19
19
  // NOTE: This script relies on the powerful chatgpt.js library @ https://chatgpt.js.org © 2023–2025 KudoAI & contributors under the MIT license
20
20
 
21
21
  (async () => {
22
+ 'use strict'
22
23
 
23
24
  // Init config
24
25
  const config = { prefix: 'chatgptScript' } ; loadSetting('skipAlert')