// ==UserScript==
// @name ChatGPT Chat Copy Code Button
// @description Add a 'Copy Code' button to the bottom right hand of code blocks on chatgpt.com with animation
// @name:zh-CN ChatGPT 聊天复制代码按钮和下载代码按钮
// @description:zh-CN 在 chatgpt.com 上代码块的右下角添加一个带有动画的“复制代码”按钮和下载代码按钮
// @name:ar ChatGPT زر نسخ رمز الدردشة
// @description:ar يخرج chatgpt.com أضف رسمًا متحركًا في الركن الأيمن السفلي من كتلة التعليمات البرمجية العلوية“نسخ الرمز”زر
// @name:bg ChatGPT Бутон за копиране на код за чат
// @description:bg съществуват chatgpt.com Добавете анимация в долния десен ъгъл на горния кодов блок“Копирайте кода”бутон
// @name:cs ChatGPT Tlačítko pro kopírování kódu chatu
// @description:cs existovat chatgpt.com Přidejte animaci do pravého dolního rohu horního bloku kódu“Kopírovat kód”tlačítko
// @name:da ChatGPT Chat kopi kode knap
// @description:da eksistere chatgpt.com Tilføj en animation i nederste højre hjørne af den øverste kodeblok“Kopiér kode”knap
// @name:de ChatGPT Schaltfläche zum Kopieren des Chat-Codes
// @description:de existieren chatgpt.com Fügen Sie in der unteren rechten Ecke des oberen Codeblocks eine Animation hinzu“Code kopieren”Taste
// @name:el ChatGPT Κουμπί αντιγραφής κωδικού συνομιλίας
// @description:el υπάρχω chatgpt.com Προσθέστε μια κινούμενη εικόνα στην κάτω δεξιά γωνία του επάνω μπλοκ κώδικα“Αντιγραφή κωδικού”κουμπί
// @name:en ChatGPT Chat copy code button
// @description:en exist chatgpt.com Add an animation in the lower right corner of the upper code block“Copy code”button
// @name:eo ChatGPT Babilejo kopia kodo butono
// @description:eo ekzisti chatgpt.com Aldonu animacion en la malsupra dekstra angulo de la supra kodbloko“Kopiu kodon”butonon
// @name:es ChatGPT Botón de copiar código de chat
// @description:es existir chatgpt.com Agregue una animación en la esquina inferior derecha del bloque de código superior“Copiar código”botón
// @name:fi ChatGPT Chat-koodin kopiointipainike
// @description:fi olemassa chatgpt.com Lisää animaatio ylemmän koodilohkon oikeaan alakulmaan“Kopioi koodi”-painiketta
// @name:fr ChatGPT Bouton de copie du code de chat
// @description:fr exister chatgpt.com Ajoutez une animation dans le coin inférieur droit du bloc de code supérieur“Copier le code”bouton
// @name:he ChatGPT לחצן העתקת קוד צ’אט
// @description:he לְהִתְקַיֵם chatgpt.com הוסף אנימציה בפינה הימנית התחתונה של גוש הקוד העליון“העתק קוד”לַחְצָן
// @name:hr ChatGPT Gumb za kopiranje koda za chat
// @description:hr postojati chatgpt.com Dodajte animaciju u donji desni kut gornjeg bloka koda“Kopiraj kod”dugme
// @name:hu ChatGPT Csevegési kódmásolás gomb
// @description:hu létezik chatgpt.com Adjon hozzá animációt a felső kódblokk jobb alsó sarkába“Kód másolása”gomb
// @name:id ChatGPT Tombol salin kode obrolan
// @description:id ada chatgpt.com Tambahkan animasi di sudut kanan bawah blok kode atas“Salin kode”tombol
// @name:it ChatGPT Pulsante copia codice chat
// @description:it esistere chatgpt.com Aggiungi un’animazione nell’angolo in basso a destra del blocco di codice superiore“Copia il codice”pulsante
// @name:ja ChatGPT チャットコードコピーボタン
// @description:ja 存在する chatgpt.com 上部のコード ブロックの右下隅にアニメーションを追加します。“コードをコピーする”ボタン
// @name:ka ChatGPT ჩეთის კოპირების კოდის ღილაკი
// @description:ka არსებობს chatgpt.com დაამატეთ ანიმაცია ზედა კოდის ბლოკის ქვედა მარჯვენა კუთხეში“დააკოპირეთ კოდი”ღილაკი
// @name:ko ChatGPT 채팅 코드 복사 버튼
// @description:ko 존재하다 chatgpt.com 상단 코드 블록의 오른쪽 하단에 애니메이션을 추가합니다.“코드 복사”단추
// @name:nl ChatGPT Knop voor chatkopieercode
// @description:nl bestaan chatgpt.com Voeg een animatie toe in de rechter benedenhoek van het bovenste codeblok“Kopieer code”knop
// @name:nb ChatGPT Chat kopi kode knapp
// @description:nb eksistere chatgpt.com Legg til en animasjon i nedre høyre hjørne av den øvre kodeblokken“Kopier koden”knapp
// @name:pl ChatGPT Przycisk kopiowania kodu czatu
// @description:pl istnieć chatgpt.com Dodaj animację w prawym dolnym rogu górnego bloku kodu“Skopiuj kod”przycisk
// @name:pt-BR ChatGPT Botão copiar código do bate-papo
// @description:pt-BR existir chatgpt.com Adicione uma animação no canto inferior direito do bloco de código superior“Copiar código”botão
// @name:ro ChatGPT Butonul de copiere a codului de chat
// @description:ro exista chatgpt.com Adăugați o animație în colțul din dreapta jos al blocului de cod de sus“Copiați codul”buton
// @name:ru ChatGPT Кнопка копирования кода в чате
// @description:ru существовать chatgpt.com Добавьте анимацию в правом нижнем углу верхнего блока кода.“Скопировать код”кнопка
// @name:sk ChatGPT Tlačidlo na kopírovanie kódu chatu
// @description:sk existujú chatgpt.com Pridajte animáciu do pravého dolného rohu horného bloku kódu“Kopírovať kód”tlačidlo
// @name:sr ChatGPT Дугме за копирање кода за ћаскање
// @description:sr постоје chatgpt.com Додајте анимацију у доњи десни угао горњег кодног блока“Копирај код”дугме
// @name:sv ChatGPT Knapp för chattkopiering
// @description:sv existera chatgpt.com Lägg till en animation i det nedre högra hörnet av det övre kodblocket“Kopiera kod”knapp
// @name:th ChatGPT ปุ่มคัดลอกรหัสแชท
// @description:th มีอยู่ chatgpt.com เพิ่มภาพเคลื่อนไหวที่มุมขวาล่างของบล็อกโค้ดด้านบน“คัดลอกรหัส”ปุ่ม
// @name:tr ChatGPT Sohbet kodunu kopyala düğmesi
// @description:tr var olmak chatgpt.com Üst kod bloğunun sağ alt köşesine bir animasyon ekleyin“Kodu kopyala”düğme
// @name:ug ChatGPT پاراڭلىشىش كودى كۇنۇپكىسى
// @description:ug مەۋجۇت chatgpt.com ئۈستۈنكى كود توپىنىڭ ئوڭ ئوڭ بۇلۇڭىغا كارتون قوشۇڭ“كودنى كۆچۈرۈڭ”كۇنۇپكا
// @name:uk ChatGPT Кнопка копіювання коду чату
// @description:uk існують chatgpt.com Додайте анімацію в нижній правий кут верхнього блоку коду“Скопіюйте код”кнопку
// @name:vi ChatGPT Nút sao chép mã trò chuyện
// @description:vi hiện hữu chatgpt.com Thêm hình động ở góc dưới bên phải của khối mã phía trên“Sao chép mã”cái nút
// @name:zh-TW ChatGPT 聊天複製代碼按鈕
// @description:zh-TW 在 chatgpt.com 上代碼區塊的右下角添加一個帶有動畫的“複製程式碼”按鈕
// @name:zh-HK ChatGPT 聊天複製代碼按鈕
// @description:zh-HK 在 chatgpt.com 上代碼區塊的右下角添加一個帶有動畫的“複製程式碼”按鈕
// @name:fr-CA ChatGPT Bouton de copie du code de chat
// @description:fr-CA exister chatgpt.com Ajoutez une animation dans le coin inférieur droit du bloc de code supérieur“Copier le code”bouton
// @match https://chatgpt.com/*
// @match https://share.nezhagpt.cloud/*
// @author YodaBets,人民的勤务员 <[email protected]>
// @namespace https://github.com/ChinaGodMan/UserScripts
// @supportURL https://github.com/ChinaGodMan/UserScripts/issues
// @homepageURL https://github.com/ChinaGodMan/UserScripts
// @license MIT
// @icon data:image/png;base64,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
// @compatible chrome
// @compatible firefox
// @compatible edge
// @compatible opera
// @compatible safari
// @version 1.1.0.0
// @Created 2024-09-22 07:06:07
// @modified 2024-09-22 07:06:07
// ==/UserScript==
(function () {
'use strict'
var EXPORT = true
const copyToClipboard = (text, button) => {
navigator.clipboard.writeText(text).then(() => {
console.log('Copied code to clipboard')
button.innerHTML = 'Copied!'
setTimeout(() => {
button.innerHTML = '' // Clear the text
const svgIcon = getSVGIcon() // Re-add the SVG icon
button.appendChild(svgIcon)
}, 2000)
}, (err) => {
console.error('Failed to copy code: ', err)
})
}
function getSVGIcon(isEx = false) {
const svgIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
svgIcon.setAttribute('width', '24')
svgIcon.setAttribute('height', '24')
svgIcon.setAttribute('fill', 'none')
svgIcon.setAttribute('viewBox', '0 0 24 24')
svgIcon.classList.add('icon-sm')
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
path.setAttribute('fill', 'currentColor')
path.setAttribute('fill-rule', 'evenodd')
if (isEx) {
path.setAttribute('transform', 'rotate(180 12 12)')
path.setAttribute('d', 'M5 20a1 1 0 0 1 1-1h12a1 1 0 1 1 0 2H6a1 1 0 0 1-1-1zm7-2a1 1 0 0 1-1-1V8.414L9.707 10.707a1 1 0 1 1-1.414-1.414l4-4a1 1 0 0 1 1.414 0l4 4a1 1 0 0 1-1.414 1.414L13 8.414V17a1 1 0 0 1-1 1z')
} else {
path.setAttribute('d', 'M7 5a3 3 0 0 1 3-3h9a3 3 0 0 1 3 3v9a3 3 0 0 1-3 3h-2v2a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3v-9a3 3 0 0 1 3-3h2zm2 2h5a3 3 0 0 1 3 3v5h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-9a1 1 0 0 0-1 1zM5 9a1 1 0 0 0-1 1v9a1 1 0 0 0 1 1h9a1 1 0 0 0 1-1v-9a1 1 0 0 0-1-1z')
}
path.setAttribute('clip-rule', 'evenodd')
svgIcon.appendChild(path)
return svgIcon
}
const addButton = (elem) => {
const button = document.createElement('button')
const svgIcon = getSVGIcon()
button.appendChild(svgIcon)
button.style.position = 'absolute'
button.style.bottom = '8px'
button.style.right = '8px'
button.style.fontSize = '12px'
button.style.padding = '4px 8px'
button.style.border = '1px solid #ccc'
button.style.borderRadius = '3px'
button.style.background = 'rgba(0,0,0,0.1)'
button.style.color = 'white'
button.style.cursor = 'pointer'
button.style.zIndex = '10'
button.style.transition = 'background-color 0.3s ease'
button.addEventListener('click', (e) => {
e.stopPropagation()
copyToClipboard(elem.querySelector('code').textContent, button)
})
button.addEventListener('mouseover', () => {
button.style.backgroundColor = 'rgba(0,0,0,0.2)'
})
button.addEventListener('mouseout', () => {
button.style.backgroundColor = 'rgba(0,0,0,0.1)'
})
elem.style.position = 'relative'
elem.appendChild(button)
}
const addexButton = (elem) => {
const button = document.createElement('button')
const svgIcon = getSVGIcon(true)
button.appendChild(svgIcon)
button.style.position = 'absolute'
button.style.bottom = '8px'
button.style.right = '48px'
button.style.fontSize = '12px'
button.style.padding = '4px 8px'
button.style.border = '1px solid #ccc'
button.style.borderRadius = '3px'
button.style.background = 'rgba(0,0,0,0.1)'
button.style.color = 'white'
button.style.cursor = 'pointer'
button.style.zIndex = '10'
button.style.transition = 'background-color 0.3s ease'
button.addEventListener('click', (e) => {
var languageDiv = elem.parentElement.parentElement.querySelector('div.flex.items-center.text-token-text-secondary')
e.stopPropagation()
exportCode(elem, languageDiv.textContent)
//copyToClipboard(elem.querySelector('code').textContent, button)
})
elem.style.position = 'relative'
elem.appendChild(button)
}
const observeCodeBlocks = () => {
const codeBlocks = document.querySelectorAll('pre:not(.copy-code-processed)')
if (codeBlocks.length) {
codeBlocks.forEach(block => {
addButton(block)
if (EXPORT) {
addexButton(block)
}
block.classList.add('copy-code-processed')
})
}
}
async function exportCode(codeBlock, language) {
let fileName
let fileExtension
let mimeType
// Determine filename, extension, and MIME type based on language
switch (language) {
case 'javascript':
case 'js':
fileName = 'script'
fileExtension = '.js'
mimeType = 'application/javascript'
break
case 'html':
fileName = 'index'
fileExtension = '.html'
mimeType = 'text/html'
break
case 'css':
fileName = 'styles'
fileExtension = '.css'
mimeType = 'text/css'
break
case 'python':
case 'py':
fileName = 'main'
fileExtension = '.py'
mimeType = 'text/x-python'
break
default:
// If language cannot be determined from <span>, fallback to provided language
switch (language.toLowerCase()) {
case 'javascript':
case 'js':
fileName = 'script'
fileExtension = '.js'
mimeType = 'application/javascript'
break
case 'html':
fileName = 'index'
fileExtension = '.html'
mimeType = 'text/html'
break
case 'css':
fileName = 'styles'
fileExtension = '.css'
mimeType = 'text/css'
break
case 'python':
case 'py':
fileName = 'main'
fileExtension = '.py'
mimeType = 'text/x-python'
break
case 'java':
fileName = 'Main'
fileExtension = '.java'
mimeType = 'text/x-java-source'
break
case 'kotlin':
fileName = 'Main'
fileExtension = '.kt'
mimeType = 'text/x-kotlin'
break
case 'c++':
case 'cpp':
fileName = 'main'
fileExtension = '.cpp'
mimeType = 'text/x-c++src'
break
case 'c#':
case 'csharp':
fileName = 'Program'
fileExtension = '.cs'
mimeType = 'text/x-csharp'
break
case 'c':
fileName = 'main'
fileExtension = '.c'
mimeType = 'text/x-csrc'
break
case 'ruby':
fileName = 'script'
fileExtension = '.rb'
mimeType = 'text/x-ruby'
break
case 'rust':
fileName = 'main'
fileExtension = '.rs'
mimeType = 'text/x-rustsrc'
break
case 'php':
fileName = 'script'
fileExtension = '.php'
mimeType = 'text/x-php'
break
case 'swift':
fileName = 'main'
fileExtension = '.swift'
mimeType = 'text/x-swift'
break
case 'typescript':
case 'ts':
fileName = 'script'
fileExtension = '.ts'
mimeType = 'application/typescript'
break
case 'go':
fileName = 'main'
fileExtension = '.go'
mimeType = 'text/x-go'
break
case 'perl':
fileName = 'script'
fileExtension = '.pl'
mimeType = 'text/x-perl'
break
case 'lua':
fileName = 'script'
fileExtension = '.lua'
mimeType = 'text/x-lua'
break
default:
fileName = 'code'
fileExtension = '.txt'
mimeType = 'text/plain'
break
}
break
}
// Create a Blob object with the code content
const blob = new Blob([codeBlock.querySelector('code').textContent], { type: mimeType })
try {
if (window.showSaveFilePicker) {
// Use File System Access API if available
const fileHandle = await window.showSaveFilePicker({
suggestedName: fileName + fileExtension,
types: [
{
description: language,
accept: {
[mimeType]: [fileExtension],
},
},
],
})
const writable = await fileHandle.createWritable()
await writable.write(blob)
await writable.close()
} else {
// Fallback for browsers that do not support showSaveFilePicker
const downloadLink = document.createElement('a')
downloadLink.href = URL.createObjectURL(blob)
downloadLink.download = fileName + fileExtension
downloadLink.style.display = 'none'
document.body.appendChild(downloadLink)
downloadLink.click()
URL.revokeObjectURL(downloadLink.href)
document.body.removeChild(downloadLink)
}
} catch (error) {
console.error('Save file dialog was canceled or failed', error)
}
}
setTimeout(() => {
const observer = new MutationObserver(observeCodeBlocks)
observer.observe(document.body, { childList: true, subtree: true })
observeCodeBlocks()
}, 5000)
})()