// ==UserScript== // @name ChatGPT Copy as Markdown with MathJax Support // @name:zh-CN 支持数学公式的ChatGPT Markdown一键复制 // @namespace http://tampermonkey.net/ // @version 0.6 // @description Copy the chatGPT Q&A content as a markdown text, with MathJax Render Support, you can use this together with 'OpenAI-ChatGPT LaTeX Auto Render (with MathJax V2)' that adds support for math render, based on 'chatGPT Markdown' by 赵巍໖. // @description:zh-cn 将chatGPT问答内容复制成markdown文本,并支持MathJax渲染内容导出,与'OpenAI-ChatGPT LaTeX Auto Render(with MathJax V2)'一起使用可以渲染公式, 基于赵巍໖的'chatGPT Markdown'。 // @license MIT // @author jbji // @match https://chat.openai.com/chat // @match https://chat.openai.com/chat/* // @icon https://chat.openai.com/favicon-32x32.png // @grant none // @downloadURL https://update.greasyfork.icu/scripts/456380/ChatGPT%20Copy%20as%20Markdown%20with%20MathJax%20Support.user.js // @updateURL https://update.greasyfork.icu/scripts/456380/ChatGPT%20Copy%20as%20Markdown%20with%20MathJax%20Support.meta.js // ==/UserScript== (function () { 'use strict'; var mathFixEnabled = true; function toMarkdown() { var main = document.querySelector("main"); var article = main.querySelector("div > div > div > div"); var chatBlocks = Array.from(article.children) .filter(v => v.getAttribute("class").indexOf("border") >= 0); // for chatgpt plus if (chatBlocks.length > 0 && chatBlocks[0].classList.contains("items-center")) { chatBlocks.shift(); // remove first element from array } var new_replacements = [ //['\\', '\\\\', 'backslash'], //Don't need this any more cause it would be checked. ['`', '\\`', 'codeblocks'], ['*', '\\*', 'asterisk'], ['_', '\\_', 'underscores'], ['{', '\\{', 'crulybraces'], ['}', '\\}', 'crulybraces'], ['[', '\\[', 'square brackets'], [']', '\\]', 'square brackets'], ['(', '\\(', 'parentheses'], [')', '\\)', 'parentheses'], ['#', '\\#', 'number signs'], ['+', '\\+', 'plussign'], ['-', '\\-', 'hyphen'], ['.', '\\.', 'dot'], ['!', '\\!', 'exclamation mark'], ['>', '\\>', 'angle brackets'] ]; // A State Machine used to match string and do replacement function replacementSkippingMath(string, char_pattern, replacement) { var inEquationState = 0; // 0:not in equation, 1:inline equation expecting $, 2: line euqation expecting $$ var result = ""; for (let i = 0; i < string.length; i++) { if(string[i] == '\\'){ result += string[i]; if (i+1 < string.length) result += string[i+1]; i++; // one more add to skip escaped char continue; } switch(inEquationState){ case 1: result += string[i]; if(string[i] === '$'){ inEquationState = 0; //simply exit and don't do further check continue; } break; case 2: result += string[i]; if(string[i] === '$'){ if (i+1 < string.length && string[i+1] === '$'){ //matched $$ result += '$'; inEquationState = 0; i++; // one more add } //else is unexpected behavior continue; } break; default: if(string[i] === '$'){ if (i+1 < string.length && string[i+1] === '$'){//matched $$ result += '$$'; inEquationState = 2; i++; // one more add }else{ //matched $ result += '$'; inEquationState = 1; } continue; }else if(string[i] === char_pattern[0]){ //do replacement result += replacement; }else{ result += string[i]; } } } return result; } function markdownEscape(string, skips) { skips = skips || [] //reduce function applied the function in the first with the second as input //this applies across the array with the first element inside as the initial 2nd param for the reduce func. return new_replacements.reduce(function (string, replacement) { var name = replacement[2] if (name && skips.indexOf(name) !== -1) { return string; } else { return replacementSkippingMath(string, replacement[0], replacement[1]); } }, string) } function replaceInnerNode(element) { if (element.outerHTML) { var htmlBak = element.outerHTML; if(mathFixEnabled){ //replace mathjax stuff var mathjaxBeginRegExp = /((.*?)<\/script>/s; match = latexMathNLRegExp.exec(htmlBak); if(match){ latexMath = "$$" + match[1] + "$$"; htmlBak = htmlBak.replace(match[0], latexMath); }else{ //then inline equations var latexMathRegExp = /