(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(); })()