Last active
April 23, 2023 07:08
-
-
Save Boorj/5c327ba6473a13fa47bf17fd4d872a69 to your computer and use it in GitHub Desktop.
ChatGPT Bookmarklet: copy dialog to clipboard in markdown
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
javascript:(function(){document.body.appendChild(document.createElement('script')).src='https://gist.githubusercontent.com/Boorj/5c327ba6473a13fa47bf17fd4d872a69/raw/83feeda1e5c9436001300976316769ee0e5d77af/chatgpt-2-markdown.js';})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function () { | |
function markdownEscape(text) { | |
return text.replace(/\s+/g, " ") | |
/*.replace(/[\\\-*_>#]/g, "\\$&");*/ | |
.replace(/[\\*>#]/g, "\\$&"); | |
} | |
function repeat(str, times) { | |
return (new Array(times + 1)).join(str); | |
} | |
function childsToMarkdown(tree, mode) { | |
var res = ""; | |
for (let i = 0, l = tree.childNodes.length; i < l; ++i) { | |
res += nodeToMarkdown(tree.childNodes[i], mode); | |
} | |
return res; | |
} | |
function nodeToMarkdown(tree, mode) { | |
let nl = "\n\n"; | |
if (tree.nodeType == 3) { // Text node | |
return markdownEscape(tree.nodeValue); | |
} | |
else if (tree.nodeType == 1) { | |
if (mode == "block") { | |
switch (tree.tagName.toLowerCase()) { | |
case "br": | |
return nl; | |
case "hr": | |
return nl + "---" + nl; | |
// Block container elements | |
case "p": | |
case "div": | |
case "section": | |
case "address": | |
case "center": | |
return nl + childsToMarkdown(tree, "block") + nl; | |
case "ul": | |
return nl + childsToMarkdown(tree, "u") + nl; | |
case "ol": | |
return nl + childsToMarkdown(tree, "o") + nl; | |
case "pre": | |
return nl + " " + childsToMarkdown(tree, "inline") + nl; | |
case "code": | |
if (tree.childNodes.length == 1) { | |
break; // use the inline format | |
} | |
return nl + " " + childsToMarkdown(tree, "inline") + nl; | |
case "h1": | |
case "h2": | |
case "h3": | |
case "h4": | |
case "h5": | |
case "h6": | |
case "h7": | |
return nl + repeat("#", Number(tree.tagName[1])) + " " + childsToMarkdown(tree, "inline") + nl; | |
case "blockquote": | |
return nl + "> " + childsToMarkdown(tree, "inline") + nl; | |
} | |
} | |
if (/^[ou]+$/.test(mode)) { | |
if (tree.tagName == "LI") { | |
return "\n" + repeat(" ", mode.length - 1) + (mode[mode.length - 1] == "o" ? "1. " : "- ") + childsToMarkdown(tree, mode + "l"); | |
} | |
else { | |
console.log("[toMarkdown] - invalid element at this point " + mode.tagName); | |
return childsToMarkdown(tree, "inline"); | |
} | |
} | |
else if (/^[ou]+l$/.test(mode)) { | |
if (tree.tagName == "UL") { | |
return childsToMarkdown(tree, mode.substr(0, mode.length - 1) + "u"); | |
} | |
else if (tree.tagName == "OL") { | |
return childsToMarkdown(tree, mode.substr(0, mode.length - 1) + "o"); | |
} | |
} | |
// Inline tags | |
switch (tree.tagName.toLowerCase()) { | |
case "strong": | |
case "b": | |
return "**" + childsToMarkdown(tree, "inline") + "**"; | |
case "em": | |
case "i": | |
return "_" + childsToMarkdown(tree, "inline") + "_"; | |
case "code": // Inline version of code | |
return "`" + childsToMarkdown(tree, "inline") + "`"; | |
case "a": | |
return "[" + childsToMarkdown(tree, "inline") + "](" + tree.getAttribute("href") + ")"; | |
case "img": | |
return nl + "[_Image_: " + markdownEscape(tree.getAttribute("alt")) + "](" + tree.getAttribute("src") + ")" + nl; | |
case "script": | |
case "style": | |
case "meta": | |
return ""; | |
default: | |
console.log("[toMarkdown] - undefined element " + tree.tagName); | |
return childsToMarkdown(tree, mode); | |
} | |
} | |
} | |
function toMarkdown(node) { | |
return nodeToMarkdown(node, "block") | |
.replace(/[\n]{2,}/g, "\n\n") | |
.replace(/^[\n]+/, "") | |
.replace(/[\n]+$/, ""); | |
} | |
function main() { | |
const separator = '\n------------\n'; | |
let markdownLines = []; | |
const messages = document.querySelectorAll('div.group'); | |
function parseAnswerChild(child) { | |
let s = ''; | |
switch (child.localName) { | |
case "p": | |
const img = child.querySelector("img"); | |
if (img) { | |
const altText = img.alt || ""; | |
const url = img.src || ""; | |
const title = img.title || ""; | |
s += `![${altText}](${url} "${title}")\n`; | |
} | |
else { | |
s += toMarkdown(child); | |
/* let html = child.innerHTML; s += html; */ | |
} | |
break; | |
case "pre": | |
const lang = child.querySelector("span") ? child.querySelector("span").innerHTML : ""; | |
let code = ''; | |
code = code.replace(/<[^>]*>/g, ""); | |
code = child.querySelector("code") ? child.querySelector("code").textContent : ""; | |
s += "\n```" + lang + "\n" + code + "```\n"; | |
break; | |
case "ol": | |
const liElements = child.querySelectorAll("li"); | |
for (let i = 0; i < liElements.length; i++) { | |
s += `${i + 1}. ${liElements[i].textContent}\n`; | |
} | |
break; | |
case "table": | |
const headers = [...child.querySelectorAll("th")].map(header => header.textContent.trim()); | |
const rows = [...child.querySelectorAll("tbody tr")].map(row => [...row.querySelectorAll("td")].map(cell => cell.textContent.trim())); | |
const markdownTable = [headers, ...rows] | |
.map((row, index) => { | |
if (index === 0) { | |
return `| ${row.join(" | ")} |\n|${row.map(() => "-----") | |
.join("|")}|`; | |
} | |
return `| ${row.join(" | ")} |`; | |
}) | |
.join("\n"); | |
s += markdownTable + "\n"; | |
break; | |
default: | |
s += child.innerHTML + "\n"; | |
} | |
return s; | |
} | |
function parseMessage(group) { | |
if (group.classList.contains('dark:bg-gray-800')) { | |
const question = '# ' + group.innerText.trim() + '\n\n'; | |
markdownLines.push(question); | |
} | |
else { | |
let markdownAnswer = ''; | |
const allAnswerChildren = group.querySelectorAll('.prose > *'); | |
allAnswerChildren.forEach(function (child) { | |
markdownAnswer += parseAnswerChild(child) + '\n'; | |
}); | |
markdownLines.push(markdownAnswer); | |
markdownLines.push(separator); | |
} | |
} | |
messages.forEach(parseMessage); | |
const copyContent = markdownLines.join('\n'); | |
navigator.clipboard.writeText(copyContent); | |
alert('Chat history copied to clipboard as Markdown'); | |
} | |
main(); | |
})() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment