├── README-en.md ├── README.md ├── doc ├── add-to-chrome.png ├── example-deepseek.png ├── example-image.png ├── example-markdown.png ├── example-pdf.png ├── example.png ├── pic.png ├── popup.png ├── step1.png ├── step2.png ├── step3.png └── step4.png └── src ├── background.js ├── content.js ├── icons ├── icon120.png ├── icon128.png ├── icon16.png ├── icon32.png ├── icon48.png └── icon96.png ├── lib ├── canvas2image.js ├── html2canvas.min.js └── marked.min.js ├── manifest.json ├── popup.html ├── popup.js └── style.css /README-en.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | 简体中文 | English 7 |
8 | 9 | Export DeepSeek conversations to Markdown files 10 | 11 | [Chrome Web Store](https://chromewebstore.google.com/detail/deepseek2markdown/jfolcdbejlennfldgbninbjglbahaejn) 12 | 13 | > If the button does not respond, please refresh the page and try again. 14 | 15 | ## Features 16 | 17 | Plugin interface 18 | 19 |
20 | 21 |
22 | 23 | >If the code blocks do not display correctly when exporting images, try exporting again. 24 | 25 | Original conversation / Exported Markdown text (typora theme is bluetex): 26 | 27 |
28 | 29 |
30 | 31 | Original conversation / Exported PDF 32 | 33 |
34 | 35 |
36 | 37 | Original conversation / Exported image 38 | 39 |
40 | 41 |
42 | 43 | ## Usage 44 | 45 | ### Method 1: Download directly from the [Chrome Store](https://chromewebstore.google.com/detail/deepseek2markdown/jfolcdbejlennfldgbninbjglbahaejn) 46 | 47 | [link](https://chromewebstore.google.com/detail/deepseek2markdown/jfolcdbejlennfldgbninbjglbahaejn) 48 | 49 | ![add-to-chrome](./doc/add-to-chrome.png) 50 | 51 | ### Method 2: Load unpacked files 52 | 53 | 1. Open the Chrome browser's extension loading page chrome://extensions/ 54 | 55 |
56 | 57 |
58 | 59 | 2. Load the unpacked extension. Assuming the downloaded directory is D:\code\github\deepseek2markdown, you should open D:\code\github\deepseek2markdown\src and click "Select Folder" to load the contents of the src folder. 60 | 61 |
62 | 63 |
64 | 65 |
66 | 67 |
68 | 69 | 3. Pin the plugin in the plugin bar 70 | 71 | **Note: If you have opened the DeepSeek webpage before using the plugin for the first time, you should refresh it first.** 72 | 73 |
74 | 75 |
76 | 77 | 4. Click the button to extract the current conversation's markdown file. You can choose whether to export the thought chain. 78 | 79 |
80 | 81 |
82 | 83 | ## To-Do List 84 | 85 | - [ ] Support exporting to PDF 86 | - [x] Titles, thought chains, text styles (bold, italic, strikethrough), lists, tables, links and images, code, quotes, dividers, special symbols 87 | - [ ] Code block style optimization, support for formula export, support for highlighted text 88 | 89 | v0.3 90 | 91 | - [x] Support exporting specific messages from a conversation (custom selection of paragraphs, export only questions or answers) 92 | - [x] Popup supports switching between Chinese and English 93 | 94 | v0.2 95 | 96 | - [x] Support strict mode Markdown export 97 | - [x] Support filtering out server busy messages 98 | - [x] Added image export function 99 | 100 | v0.1 101 | 102 | - [x] Popup interface beautification 103 | - [x] Optimized markdown text export format 104 | - [x] Option to export thought chain 105 | - [x] Optimized code export style 106 | - [x] Support for exporting tables 107 | - [x] Support for exporting images 108 | - [x] Optimized export style for multi-level lists 109 | 110 | ## Feedback and Contributions 111 | 112 | If you encounter any issues or have suggestions for improvement, please feel free to submit an Issue or PR. 113 | 114 | ## Acknowledgments 115 | 116 | [DeepSeek-Chat-Exporter](https://github.com/blueberrycongee/DeepSeek-Chat-Exporter) 117 | 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | 简体中文 | English 7 |
8 | 9 | 导出DeepSeek的对话到Markdown文件 10 | 11 | [Chrome应用商店](https://chromewebstore.google.com/detail/deepseek2markdown/jfolcdbejlennfldgbninbjglbahaejn) 12 | 13 | > 如果点击按钮没反应,请直接刷新页面后进行尝试。 14 | 15 | ## 特性 16 | 17 | 插件界面 18 | 19 |
20 | 21 |
22 | 23 | > 导出图像的时候如果代码块显示不正常,可以尝试重新导出。 24 | 25 | 原始对话 / 导出Markdown文本(typora主题为[bluetex](https://github.com/DaYangtuo247/typora-blueTex-theme)): 26 | 27 |
28 | 29 |
30 | 31 | 原始对话 / 导出PDF 32 | 33 |
34 | 35 |
36 | 37 | 原始对话 / 导出图像 38 | 39 |
40 | 41 |
42 | 43 | ## 使用方法 44 | 45 | ### 方法一:直接从Chrome商店下载 46 | 47 | 直接从[Chrome商店](https://chromewebstore.google.com/detail/deepseek2markdown/jfolcdbejlennfldgbninbjglbahaejn)下载 48 | 49 | ![add-to-chrome](./doc/add-to-chrome.png) 50 | 51 | ### 方法二:加载已解压的文件 52 | 53 | 1. 打开Chrome浏览器的加载扩展程序页面 [chrome://extensions/](chrome://extensions/) 54 | 55 |
56 | 57 |
58 | 59 | 2. 加载已解压的扩展程序。假设我下载的目录为D:\code\github\deepseek2markdown,则应该打开D:\code\github\deepseek2markdown\src 并点击“选择文件夹”,加载src文件夹中的内容。 60 | 61 |
62 | 63 |
64 | 65 |
66 | 67 |
68 | 69 | 3. 在插件栏固定插件 70 | 71 | **注意:首次加载插件使用之前如果打开了DeepSeek的网页,应该先刷新一下。** 72 | 73 |
74 | 75 |
76 | 77 | 4. 点击按钮提取当前对话的markdown文件,可勾选是否导出思维链 78 | 79 |
80 | 81 |
82 | 83 | ## 待办事项 84 | 85 | - [ ] 支持导出为PDF 86 | - [x] 标题、思维链、文本样式(粗体、斜体、删除线)、列表、表格、链接和图片、代码、引用、分割线、特殊符号 87 | - [ ] 代码块样式优化、支持公式导出、支持高亮文本 88 | 89 | v0.3 90 | 91 | - [x] 支持选择一段对话中的特定消息导出(自定义选择段落、只导出提问或者回答) 92 | - [x] Popup界面支持中英文切换 93 | 94 | v0.2 95 | 96 | - [x] markdown严格模式导出 97 | - [x] 支持过滤掉服务器繁忙的消息 98 | - [x] 添加图片导出功能 99 | 100 | v0.1 101 | 102 | - [x] popup界面美化 103 | - [x] 导出markdown文本格式优化 104 | - [x] 可选择是否导出思维链 105 | - [x] 优化代码导出样式 106 | - [x] 支持导出表格 107 | - [x] 支持导出图片 108 | - [x] 优化多层列表的导出样式 109 | 110 | ## 反馈与贡献 111 | 112 | 如果您在使用过程中遇到问题或有改进建议,欢迎提交 Issue 或 PR 113 | 114 | ## 致谢 115 | 116 | [DeepSeek-Chat-Exporter](https://github.com/blueberrycongee/DeepSeek-Chat-Exporter) 117 | -------------------------------------------------------------------------------- /doc/add-to-chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderyjc/deepseek2markdown/7812ee5b12598c996538a7498600e008ecd43dd5/doc/add-to-chrome.png -------------------------------------------------------------------------------- /doc/example-deepseek.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderyjc/deepseek2markdown/7812ee5b12598c996538a7498600e008ecd43dd5/doc/example-deepseek.png -------------------------------------------------------------------------------- /doc/example-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderyjc/deepseek2markdown/7812ee5b12598c996538a7498600e008ecd43dd5/doc/example-image.png -------------------------------------------------------------------------------- /doc/example-markdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderyjc/deepseek2markdown/7812ee5b12598c996538a7498600e008ecd43dd5/doc/example-markdown.png -------------------------------------------------------------------------------- /doc/example-pdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderyjc/deepseek2markdown/7812ee5b12598c996538a7498600e008ecd43dd5/doc/example-pdf.png -------------------------------------------------------------------------------- /doc/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderyjc/deepseek2markdown/7812ee5b12598c996538a7498600e008ecd43dd5/doc/example.png -------------------------------------------------------------------------------- /doc/pic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderyjc/deepseek2markdown/7812ee5b12598c996538a7498600e008ecd43dd5/doc/pic.png -------------------------------------------------------------------------------- /doc/popup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderyjc/deepseek2markdown/7812ee5b12598c996538a7498600e008ecd43dd5/doc/popup.png -------------------------------------------------------------------------------- /doc/step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderyjc/deepseek2markdown/7812ee5b12598c996538a7498600e008ecd43dd5/doc/step1.png -------------------------------------------------------------------------------- /doc/step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderyjc/deepseek2markdown/7812ee5b12598c996538a7498600e008ecd43dd5/doc/step2.png -------------------------------------------------------------------------------- /doc/step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderyjc/deepseek2markdown/7812ee5b12598c996538a7498600e008ecd43dd5/doc/step3.png -------------------------------------------------------------------------------- /doc/step4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderyjc/deepseek2markdown/7812ee5b12598c996538a7498600e008ecd43dd5/doc/step4.png -------------------------------------------------------------------------------- /src/background.js: -------------------------------------------------------------------------------- 1 | chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { 2 | if (request.action === 'export') { 3 | chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { 4 | chrome.scripting.executeScript({ 5 | target: { tabId: tabs[0].id }, 6 | function: extractConversation 7 | }, (results) => { 8 | const markdown = results[0].result; 9 | sendResponse({ markdown }); 10 | }); 11 | }); 12 | return true; // 保持消息通道打开以等待响应 13 | } 14 | }); -------------------------------------------------------------------------------- /src/content.js: -------------------------------------------------------------------------------- 1 | // ===================== 2 | // 配置 3 | // ===================== 4 | const config = { 5 | mainPageSelector: '.cb86951c', // 主页面 6 | chatContainerSelector: '.dad65929', // 聊天框容器 7 | 8 | userClassPrefix: '_9663006', // 用户消息 class 前缀 9 | 10 | aiClassPrefix: '_4f9bf79', // AI消息相关 class 前缀 11 | 12 | aiChainOfThought: '._48edb25', // AI的思维链, 包含"已深度思考xxx秒"和"思考过程" 13 | searchHintSelector: '._58a6d71._19db599', // 搜索/思考时间, "已深度思考xxx秒" 14 | thinkingChainSelector: '.e1675d8b', // 思考链, "思考过程" 15 | 16 | userSessionTitleSelector: '.d8ed659a', // 用户会话标题 17 | finalAnswerSelector: 'div.ds-markdown.ds-markdown--block', // 回答的内容 18 | isExportChainOfThought: false, // 是否导出思考链 19 | isExportBusyServerMessages: false, // 是否导出繁忙消息,默认不导出 20 | }; 21 | 22 | function initConfig() { 23 | config.isExportChainOfThought = false; 24 | config.isExportBusyServerMessages = false; 25 | } 26 | 27 | // ===================== 28 | // 工具函数 29 | // ===================== 30 | function isUserMessage(node) { 31 | return node.classList.contains(config.userClassPrefix); 32 | } 33 | 34 | function isAIMessage(node) { 35 | return node.classList.contains(config.aiClassPrefix); 36 | } 37 | 38 | function getUserSessionTitle() { 39 | const thinkingNode = document.querySelector(config.userSessionTitleSelector); 40 | return thinkingNode ? `${thinkingNode.textContent.trim()}` : 'DeepSeek_Chat_Export'; 41 | } 42 | 43 | // 已深度思考xxx秒 44 | function extractSearchOrThinking(node) { 45 | const hintNode = node.querySelector(config.searchHintSelector); 46 | return hintNode ? `**${hintNode.textContent.trim()}**` : null; 47 | } 48 | 49 | // 思考过程 50 | function extractThinkingChain(node) { 51 | const thinkingNode = node.querySelector(config.thinkingChainSelector); 52 | const elements = thinkingNode.querySelectorAll('p'); 53 | output = ''; 54 | elements.forEach((element) => { 55 | if (element.textContent.trim() != '') { 56 | output += `> ${element.textContent.trim()}\n`; 57 | } else { 58 | output += '> \n'; 59 | } 60 | }); 61 | return thinkingNode ? output : null; 62 | } 63 | 64 | // 下载markdown内容 65 | function downloadMarkdown(markdown, title) { 66 | const blob = new Blob([markdown], { type: 'text/markdown' }); 67 | const url = URL.createObjectURL(blob); 68 | const a = document.createElement('a'); 69 | a.href = url; 70 | a.download = title + '.md'; 71 | a.click(); 72 | } 73 | 74 | // 下载PDF内容 75 | function downloadPDF(pdf) { 76 | const printWindow = window.open("", "_blank"); 77 | printWindow.document.write(pdf); 78 | printWindow.document.close(); 79 | setTimeout(() => { printWindow.print(); }, 500); 80 | } 81 | 82 | // 提取AI回答的内容 83 | function resolveTag_text(node) { 84 | let content = ''; 85 | node.childNodes.forEach((childNode) => { 86 | if (childNode.nodeType === Node.TEXT_NODE) { 87 | content += childNode.textContent.trim(); 88 | } 89 | else if (childNode.tagName === 'P') { 90 | if (childNode.childNodes.length > 1) { 91 | content += resolveTag_text(childNode) 92 | } else { 93 | content += childNode.textContent.trim(); 94 | } 95 | } 96 | else if (childNode.tagName === 'STRONG') { 97 | if (childNode.childNodes.length > 1) { 98 | content += resolveTag_text(childNode) 99 | } else { 100 | content += `**${childNode.textContent.trim()}**`; 101 | } 102 | } 103 | else if (childNode.tagName === 'CODE') { 104 | content += `\`${childNode.textContent.trim()}\``; 105 | } 106 | else if (childNode.tagName === 'EM') { 107 | content += `*${childNode.textContent.trim()}*`; 108 | } 109 | else if (childNode.tagName === 'A') { 110 | const href = childNode.getAttribute('href'); 111 | content += `[${childNode.textContent.trim()}](${href})`; 112 | } 113 | else if (childNode.tagName === 'BR') { 114 | content += '\n'; 115 | } 116 | else if (childNode.tagName === 'IMG') { 117 | const src = childNode.getAttribute('src'); 118 | content += `![${childNode.getAttribute('alt')}](${src})`; 119 | } 120 | else if (childNode.tagName === 'SPAN' && childNode.classList.contains('ds-markdown-cite')) { 121 | content += ''; 122 | } 123 | else if (childNode.classList && childNode.classList.contains('katex')) { 124 | const tex = childNode.querySelector('annotation[encoding="application/x-tex"]'); 125 | if (tex) { 126 | content += `$${tex.textContent.trim()}$`; 127 | } 128 | } 129 | else if (childNode.nodeType === Node.ELEMENT_NODE) { 130 | content += childNode.textContent.trim(); 131 | } 132 | }); 133 | return content; 134 | } 135 | 136 | function resolveTag_pre(preElement, language = 'python') { 137 | // 1. 提取
 中的文本内容
138 |     const codeContent = preElement.textContent || preElement.innerText;
139 | 
140 |     // 2. 格式化为 Markdown 代码块
141 |     const markdownCodeBlock = `\`\`\`\`${language}\n${codeContent}\n\`\`\`\``;
142 | 
143 |     return markdownCodeBlock;
144 | }
145 | 
146 | function resolveTag_table(tableElement) {
147 |     let markdown = '';
148 | 
149 |     // 1. 处理表头
150 |     const headerRow = tableElement.querySelector('thead tr');
151 |     if (headerRow) {
152 |         const headers = Array.from(headerRow.querySelectorAll('th')).map(
153 |             (th) => th.textContent.trim()
154 |         );
155 |         markdown += `| ${headers.join(' | ')} |\n`; // 表头行
156 |         markdown += `| ${headers.map(() => '---').join(' | ')} |\n`; // 分隔行
157 |     }
158 | 
159 |     // 2. 处理表格内容
160 |     const bodyRows = tableElement.querySelectorAll('tbody tr');
161 |     bodyRows.forEach((row) => {
162 |         const cells = Array.from(row.querySelectorAll('td')).map((td) =>
163 |             td.textContent.trim()
164 |         );
165 |         markdown += `| ${cells.join(' | ')} |\n`; // 数据行
166 |     });
167 | 
168 |     return markdown.trim(); // 去除末尾换行
169 | }
170 | 
171 | function resolveTag_ul_li(node) {
172 |     let markdown = '';
173 | 
174 |     // 递归处理子节点
175 |     function processNode(element, depth = 0) {
176 |         // 用于存储最终的 Markdown 内容
177 |         let markdown = '';
178 | 
179 |         // UL 标签
180 |         if (element.tagName === 'UL') {
181 |             // 遍历