chatGPT Markdown

Save the chatGPT Q&A content as a markdown text

  1. // ==UserScript==
  2. // @name chatGPT Markdown
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2
  5. // @description Save the chatGPT Q&A content as a markdown text
  6. // @author TripleTre
  7. // @match https://chat.openai.com/chat
  8. // @icon https://chat.openai.com/favicon-32x32.png
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function () {
  13. 'use strict';
  14.  
  15. function toMarkdown() {
  16. var main = document.querySelector("main");
  17. var article = main.querySelector("div > div > div > div");
  18. var chatBlocks = Array.from(article.children)
  19. .filter(v => v.getAttribute("class").indexOf("border") >= 0);
  20.  
  21. var replacements = [
  22. [/\*/g, '\\*', 'asterisks'],
  23. [/#/g, '\\#', 'number signs'],
  24. [/\//g, '\\/', 'slashes'],
  25. [/\(/g, '\\(', 'parentheses'],
  26. [/\)/g, '\\)', 'parentheses'],
  27. [/\[/g, '\\[', 'square brackets'],
  28. [/\]/g, '\\]', 'square brackets'],
  29. [/</g, '&lt;', 'angle brackets'],
  30. [/>/g, '&gt;', 'angle brackets'],
  31. [/_/g, '\\_', 'underscores'],
  32. [/`/g, '\\`', 'codeblocks']
  33. ];
  34.  
  35. function markdownEscape(string, skips) {
  36. skips = skips || []
  37. return replacements.reduce(function (string, replacement) {
  38. var name = replacement[2]
  39. return name && skips.indexOf(name) !== -1
  40. ? string
  41. : string.replace(replacement[0], replacement[1])
  42. }, string)
  43. }
  44.  
  45. function replaceInnerNode(element) {
  46. if (element.outerHTML) {
  47. var parser = new DOMParser();
  48. var nextDomString = element.outerHTML.replace(/<code>([\w\s-]*)<\/code>/g, (match) => {
  49. var doc = parser.parseFromString(match, "text/html");
  50. return "`" + "doc.body.textContent" + "`";
  51. });
  52. return parser.parseFromString(nextDomString, "text/html").body.children[0];
  53. }
  54. return element;
  55. }
  56.  
  57. var elementMap = {
  58. "P": function (element, result) {
  59. var p = replaceInnerNode(element);
  60. result += markdownEscape(p.textContent, ["codeblocks", "number signs"]);
  61. result += `\n\n`;
  62. return result;
  63. },
  64. "OL": function (element, result) {
  65. var ol = replaceInnerNode(element);
  66. var olStart = parseInt(ol.getAttribute("start") || "1");
  67. Array.from(ol.querySelectorAll("li")).forEach((li, index) => {
  68. result += `${index + olStart}. ${markdownEscape(li.textContent, ["codeblocks", "number signs"])}`;
  69. result += `\n`;
  70. });
  71. result += `\n\n`;
  72. return result;
  73. },
  74. "PRE": function (element, result) {
  75. var codeBlocks = Array.from(element.querySelectorAll("code"));
  76. var languageMarkedBlock = codeBlocks.find(v => /language-(\w+)/.test(v.getAttribute("class") || ""));
  77. var languageMark = languageMarkedBlock.getAttribute("class").match(/language-(\w+)/)[1] || "";
  78. result += "```" + languageMark + "\n";
  79. codeBlocks.forEach(block => {
  80. result += `${block.textContent}`;
  81. });
  82. result += "```\n";
  83. result += `\n\n`;
  84. return result;
  85. }
  86. };
  87. var TEXT_BLOCKS = Object.keys(elementMap);
  88.  
  89. var mdContent = chatBlocks.reduce((result, nextBlock, i) => {
  90. if (i % 2 === 0) { // title
  91. result += `## ${markdownEscape(nextBlock.textContent, ["codeblocks", "number signs"])}`;
  92. result += `\n\n`;
  93. } else {
  94. var iterator = document.createNodeIterator(
  95. nextBlock,
  96. NodeFilter.SHOW_ELEMENT,
  97. {
  98. acceptNode: element => TEXT_BLOCKS.indexOf(element.tagName.toUpperCase()) >= 0
  99. },
  100. false,
  101. );
  102. let next = iterator.nextNode();
  103. while (next) {
  104. result = elementMap[next.tagName.toUpperCase()](next, result);
  105. next = iterator.nextNode();
  106. }
  107. }
  108. return result;
  109. }, "");
  110. return mdContent;
  111. }
  112.  
  113. var copyHtml = `<div id="__copy__" style="z-index:9999999;cursor:pointer;position: fixed;top: 20px;right: 20px;width: 60px;height: 60px;background: #8bc34a;/* border: 1px solid #8bc34a; */border-radius: 50%;color: white;display: flex;justify-content: center;align-items: center;"><span>copy</span></div>`;
  114. var copyElement = document.createElement("div");
  115. document.body.appendChild(copyElement);
  116. copyElement.outerHTML = copyHtml;
  117. var copyAnchor = document.getElementById("__copy__");
  118. copyAnchor.addEventListener("click", () => {
  119. navigator.clipboard.writeText(toMarkdown()).then(() => {
  120. alert("done");
  121. });
  122. });
  123. console.log(mdContent);
  124. })();