@kudoai/chatgpt.js 3.6.2 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
- 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)
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.sync.get(key))[key]
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.sync.set({ [key]: val }) // save to Chrome extension 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.11",
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 = { site: /([^.]+)\.[^.]+$/.exec(new URL((await chrome.tabs.query(
9
- { active: true, currentWindow: true }))[0].url).hostname)?.[1] }
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 APP data
12
- const { app } = await chrome.storage.sync.get('app')
13
- icons.import({ app }) // for srcs using app.urls.assetHost
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 notify(msg, pos = 'bottom-right') { sendMsgToActiveTab('notify', { msg, pos }) }
26
+ function settingIsEnabled(key) { return config[key] ^ /disabled|hidden/i.test(key) }
23
27
 
24
28
  const sync = {
25
29
  fade() {
26
30
 
27
- // Update toolbar icon
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
- // Update menu contents
34
- document.querySelectorAll('div.logo, div.menu-title, div.menu')
35
- .forEach(elem => {
36
- elem.classList.remove(masterToggle.checked ? 'disabled' : 'enabled')
37
- elem.classList.add(masterToggle.checked ? 'enabled' : 'disabled')
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 = document.querySelector('input')
48
- await settings.load('extensionDisabled')
49
- masterToggle.checked = !config.extensionDisabled ; sync.fade()
50
- masterToggle.onchange = () => {
51
- settings.save('extensionDisabled', !config.extensionDisabled)
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(`${chrome.runtime.getManifest().name} ${ this.checked ? 'ON' : 'OFF' }`)
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
- // Init elems
68
- const menuItemDiv = dom.create.elem('div', {
69
- class: 'menu-item menu-area', title: settings.controls[key].helptip || '' })
70
- const menuLabel = dom.create.elem('label', { class: 'menu-icon' })
71
- const menuLabelSpan = dom.create.elem('span')
72
- let menuInput, menuSlider
73
- menuLabelSpan.textContent = settings.controls[key].label
74
- if (settings.controls[key].type == 'toggle') {
75
- menuInput = dom.create.elem('input', { type: 'checkbox' })
76
- menuInput.checked = /disabled|hidden/i.test(key) ^ config[key]
77
- menuSlider = dom.create.elem('span', { class: 'slider' })
78
- menuLabel.append(menuInput, menuSlider)
79
- menuLabel.classList.add('toggle-switch')
80
- } else if (settings.controls[key].type == 'prompt') {
81
- menuLabel.innerText = settings.controls[key].symbol
82
- menuLabel.classList.add('menu-prompt')
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
- // Assemble/append elems
86
- menuItemDiv.append(menuLabel, menuLabelSpan)
87
- togglesDiv.append(menuItemDiv)
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 cjsDiv = dom.create.elem('div', { class: 'chatgpt-js' })
105
+ const cjsSpan = dom.create.elem('span', { class: 'cjs-span' })
112
106
  const cjsLogo = dom.create.elem('img', {
113
- title: 'Powered by chatgpt.js',
114
- src: `${app.urls.cjsAssetHost}/images/badges/powered-by-chatgpt.js-faded.png?b2a1975` })
115
- cjsLogo.onmouseover = cjsLogo.onmouseout = event => cjsLogo.src = `${
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: 'About ChatGPT Extension',
124
- class: 'menu-icon menu-area', style: 'right:30px ; padding-top: 2px' })
125
- const aboutIcon = icons.create({ name: 'questionMark', width: 15, height: 13, style: 'margin-bottom: 0.04rem' })
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 menu-area', style: 'right:2px ; padding-top: 2px' })
133
- const moreExtensionsIcon = icons.create({ name: 'plus', size: 16 })
134
- moreExtensionsSpan.onclick = () => { chrome.tabs.create({ url: app.urls.relatedExtensions }) ; close() }
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 loading spinner
138
- document.querySelectorAll('[class^=loading]').forEach(elem => elem.remove())
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="main-toggle">
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 size */
1
+ /* General */
2
2
  html { height: fit-content ; min-height: 89px }
3
- body { width: max-content ; margin: 0 ; overflow: clip }
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
- a { color: #999 ; text-decoration: none }
12
- a:focus, a:hover { text-decoration: underline ; color: inherit }
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
- .menu-icons > .toggle-switch { transform: scale(1.1) } /* make master toggle bigger */
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-item {
45
+ .menu-entry {
48
46
  position: relative ; align-items: center ; border-bottom: 1px solid lightgrey ;
49
- display: flex ; min-height: 2rem ; padding-right: 14px ; white-space: nowrap ; font-size: 91%
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-area:focus, .menu-area:hover { background: rgb(100,149,237) ; cursor: pointer } /* add hover color/cursor */
53
- .menu-item:hover span:not(.slider) { filter: invert(1) } /* invert setting labels on hover */
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-switch input[type="checkbox"] { display: none } /* hide checkbox from toggles */
59
- .toggle-switch .slider { /* style sliders */
60
- background-color: white; display: block; border: 1px solid black; border-radius: 7px ;
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-switch .slider::before { /* style slider knobs */
64
- position: absolute ; height: 0.625rem ; width: 0.625rem ; left: -0.04rem ; top: -0.05rem ;
65
- border: 1px solid black ; border-radius: 50% ; background-color: white ; content: ""
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-switch input[type="checkbox"]:checked + .slider { background-color: black } /* color active slider */
68
- .toggle-switch input[type="checkbox"]:checked + .slider::before { transform: translateX(9px) } /* toggle knob right */
69
-
64
+ .toggle.on .track::before { transform: translateX(9px) ; transition: transform 0.15s ease-in-out }
65
+
70
66
  /* Footer */
71
- footer { font-size: 12px ; text-align: center ; color: #999 ; background: #f5f5f5 ; height: 40px ; line-height: 40px }
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
- .chatgpt-js { position: absolute ; bottom: -.25rem ; left: 0.7rem ; cursor: pointer }
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
- /* Master toggle effects */
76
- .disabled { opacity: 0.3 ; pointer-events: none }
77
- .enabled { opacity: 1 }
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.sync.set({ app }) // save to 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: 'https://chatgpt.com/' })
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: 'https://chatgpt.com/' })
33
- if (activeTab != chatgptTab) await new Promise(resolve => // after new tab loads
34
- chrome.tabs.onUpdated.addListener(async function statusListener(tabId, info) {
35
- if (tabId == chatgptTab.id && info.status == 'complete') {
36
- chrome.tabs.onUpdated.removeListener(statusListener)
37
- setTimeout(resolve, 2500)
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.11
6
+ // @version 2025.2.21
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.2/dist/chatgpt.min.js
11
+ // @require https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.7.0/dist/chatgpt.min.js
12
12
  // @grant GM_getValue
13
13
  // @grant GM_setValue
14
14
  // @noframes