DeepSeek Markdown Raw Viewer

针对大语言模型对话时markdown渲染错误或希望能够自己复制大语言模型原始输出的结果自行渲染的情况,提供了在网页上不渲染markdown的能力;仅支持chat.deepseek.com

  1. // ==UserScript==
  2. // @name DeepSeek Markdown Raw Viewer
  3. // @namespace 徐智昊(weibo:智昊今天玩什么)
  4. // @version 1.1
  5. // @description 针对大语言模型对话时markdown渲染错误或希望能够自己复制大语言模型原始输出的结果自行渲染的情况,提供了在网页上不渲染markdown的能力;仅支持chat.deepseek.com
  6. // @match https://chat.deepseek.com/*
  7. // @grant none
  8. // @run-at document-idle
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // 核心处理器:从 DOM 精准重建 Markdown
  16. const reconstructMarkdown = (container) => {
  17. let md = '';
  18. const ignoredClasses = ['ds-markdown-code-copy-button', 'md-code-block-banner'];
  19.  
  20. // 递归处理节点
  21. const processNode = (node, inCodeBlock = false) => {
  22. if (node.nodeType === Node.TEXT_NODE) {
  23. md += node.textContent.replace(/^#+/gm, '\\$&'); // 转义行首 #
  24. }
  25. else if (node.nodeType === Node.ELEMENT_NODE) {
  26. const tag = node.tagName.toLowerCase();
  27. const isIgnored = [...node.classList].some(c => ignoredClasses.includes(c));
  28.  
  29. if (isIgnored) return;
  30.  
  31. // 代码块边界检测
  32. const isCodeBlock = tag === 'pre' && node.querySelector('code');
  33. if (isCodeBlock && !inCodeBlock) {
  34. md += '```\n';
  35. inCodeBlock = true;
  36. } else if (inCodeBlock && !isCodeBlock) {
  37. md += '\n```\n';
  38. inCodeBlock = false;
  39. }
  40.  
  41. switch (tag) {
  42. case 'h1': md += `# ${node.textContent}\n\n`; break;
  43. case 'h2': md += `## ${node.textContent}\n\n`; break;
  44. case 'h3': md += `### ${node.textContent}\n\n`; break;
  45. case 'strong': md += `**${node.textContent}**`; break;
  46. case 'em': md += `*${node.textContent}*`; break;
  47. case 'code':
  48. if (!inCodeBlock) md += `\`${node.textContent}\``;
  49. else md += node.textContent;
  50. break;
  51. case 'a':
  52. const href = node.getAttribute('href') || '';
  53. md += `[${node.textContent}](${href})`;
  54. break;
  55. case 'span':
  56. // 处理数学公式
  57. if (node.classList.contains('katex-mathml')) {
  58. const annotation = node.querySelector('annotation');
  59. if (annotation) md += annotation.textContent;
  60. } else {
  61. md += node.textContent;
  62. }
  63. break;
  64. case 'div':
  65. case 'p':
  66. [...node.childNodes].forEach(child => processNode(child, inCodeBlock));
  67. md += '\n';
  68. break;
  69. default:
  70. [...node.childNodes].forEach(child => processNode(child, inCodeBlock));
  71. }
  72. }
  73. };
  74.  
  75. [...container.childNodes].forEach(node => processNode(node));
  76. return md.trim();
  77. };
  78.  
  79. // 渲染优化后的 Markdown 显示
  80. const renderRawMarkdown = (container) => {
  81. const rawMd = reconstructMarkdown(container);
  82. container.innerHTML = `
  83. <pre style="
  84. white-space: pre-wrap;
  85. font-family: 'Roboto Mono', monospace;
  86. background: #f8f8f8;
  87. padding: 12px;
  88. border-radius: 4px;
  89. border-left: 3px solid #6ce26c;
  90. margin: 0;
  91. overflow-x: auto;
  92. ">${escapeHtml(rawMd)}</pre>
  93. `;
  94. container.style.padding = '8px';
  95. };
  96.  
  97. // HTML 转义
  98. const escapeHtml = (str) => {
  99. const div = document.createElement('div');
  100. div.textContent = str;
  101. return div.innerHTML
  102. .replace(/^#+/gm, match => match.replace(/#/g, '&#35;')); // 保护标题符号
  103. };
  104.  
  105. // 主处理器
  106. const processContainers = () => {
  107. document.querySelectorAll('.ds-markdown:not([data-raw-md])').forEach(container => {
  108. container.dataset.rawMd = "processed";
  109. renderRawMarkdown(container);
  110. });
  111. };
  112.  
  113. // 监听动态内容
  114. new MutationObserver((mutations) => {
  115. const needsUpdate = [...mutations].some(mutation =>
  116. mutation.addedNodes.length > 0 ||
  117. (mutation.target.classList && mutation.target.classList.contains('ds-markdown'))
  118. );
  119. if (needsUpdate) processContainers();
  120. }).observe(document.body, { subtree: true, childList: true });
  121.  
  122. // 初始处理
  123. processContainers();
  124. })();
  125.  

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址