导出DeepSeek回答为图片 | Export DeepSeek Answer to Image

将DeepSeek的回答导出为一张图片

目前为 2025-03-24 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name 导出DeepSeek回答为图片 | Export DeepSeek Answer to Image
  3. // @namespace http://github.com/byronleeeee/exportDeepseek
  4. // @version 1.0
  5. // @description 将DeepSeek的回答导出为一张图片
  6. // @author ByronLeeeee
  7. // @match *://chat.deepseek.com/*
  8. // @grant none
  9. // @require https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. // i18n translations
  17. const i18n = {
  18. 'zh': {
  19. exportBtn: '导出AI回答为图片',
  20. notFound: 'AI回答区域未找到!',
  21. selectScheme: '选择配色方案:',
  22. footer: '回答来自DeepSeek,仅供参考',
  23. lastAnswer: '最新回答',
  24. colorSchemes: [
  25. { name: '白色-蓝色', top: '#FFFFFF', bottom: '#4D6BFE', textTop: '#000000', textBottom: '#FFFFFF' },
  26. { name: '黑色-金色', top: '#121212', bottom: '#FFD700', textTop: '#FFFFFF', textBottom: '#000000' },
  27. { name: '浅灰-青色', top: '#F5F5F5', bottom: '#008080', textTop: '#000000', textBottom: '#FFFFFF' },
  28. { name: '深灰-紫色', top: '#333333', bottom: '#800080', textTop: '#FFFFFF', textBottom: '#FFFFFF' }
  29. ],
  30. error: '生成图片失败,请查看控制台了解详情。',
  31. cancel: '取消',
  32. export: '导出'
  33. },
  34. 'en': {
  35. exportBtn: 'Export AI Answer to Image',
  36. notFound: 'AI answer div not found!',
  37. selectScheme: 'Select a color scheme:',
  38. footer: 'Answer from DeepSeek, for reference only',
  39. lastAnswer: 'Last Answer',
  40. colorSchemes: [
  41. { name: 'White-Blue', top: '#FFFFFF', bottom: '#4D6BFE', textTop: '#000000', textBottom: '#FFFFFF' },
  42. { name: 'Black-Gold', top: '#121212', bottom: '#FFD700', textTop: '#FFFFFF', textBottom: '#000000' },
  43. { name: 'Light Gray-Teal', top: '#F5F5F5', bottom: '#008080', textTop: '#000000', textBottom: '#FFFFFF' },
  44. { name: 'Dark Gray-Purple', top: '#333333', bottom: '#800080', textTop: '#FFFFFF', textBottom: '#FFFFFF' }
  45. ],
  46. error: 'Failed to generate image. Check console for details.',
  47. cancel: 'Cancel',
  48. export: 'Export'
  49. }
  50. };
  51.  
  52. const userLang = (navigator.language || navigator.userLanguage).split('-')[0];
  53. const lang = i18n[userLang] ? userLang : 'en';
  54. const texts = i18n[lang];
  55.  
  56. // Add styles with dark mode support
  57. function addStyles() {
  58. const style = document.createElement('style');
  59. style.textContent = `
  60. .ai-export-fab {
  61. position: fixed;
  62. bottom: 24px;
  63. right: 24px;
  64. width: 56px;
  65. height: 56px;
  66. border-radius: 50%;
  67. background-color: #4D6BFE;
  68. color: white;
  69. display: flex;
  70. align-items: center;
  71. justify-content: center;
  72. cursor: pointer;
  73. box-shadow: 0 4px 8px rgba(0,0,0,0.2);
  74. z-index: 9999;
  75. transition: all 0.3s ease;
  76. }
  77. .ai-export-fab:hover {
  78. transform: scale(1.05);
  79. box-shadow: 0 6px 12px rgba(0,0,0,0.3);
  80. }
  81. .ai-export-fab-icon {
  82. display: flex;
  83. align-items: center;
  84. justify-content: center;
  85. }
  86. .ai-export-tooltip {
  87. position: absolute;
  88. background: rgba(0,0,0,0.7);
  89. color: white;
  90. padding: 5px 10px;
  91. border-radius: 4px;
  92. font-size: 12px;
  93. white-space: nowrap;
  94. right: 70px;
  95. opacity: 0;
  96. transition: opacity 0.3s;
  97. pointer-events: none;
  98. }
  99. .ai-export-fab:hover .ai-export-tooltip {
  100. opacity: 1;
  101. }
  102. .ai-color-scheme-modal {
  103. position: fixed;
  104. top: 0;
  105. left: 0;
  106. width: 100%;
  107. height: 100%;
  108. background-color: rgba(0,0,0,0.5);
  109. display: flex;
  110. align-items: center;
  111. justify-content: center;
  112. z-index: 10000;
  113. }
  114. .ai-modal-content {
  115. background-color: white;
  116. border-radius: 8px;
  117. padding: 20px;
  118. width: 300px;
  119. max-width: 90%;
  120. color: #000000; /* Default for light mode */
  121. }
  122. body.dark .ai-modal-content {
  123. background-color: #1e1e1e; /* Dark mode background */
  124. color: #ffffff; /* Dark mode text */
  125. }
  126. .ai-modal-header {
  127. font-size: 18px;
  128. font-weight: bold;
  129. margin-bottom: 15px;
  130. }
  131. .ai-scheme-options {
  132. display: grid;
  133. grid-template-columns: 1fr 1fr;
  134. gap: 10px;
  135. margin-bottom: 15px;
  136. }
  137. .ai-scheme-option {
  138. border: 2px solid transparent;
  139. border-radius: 6px;
  140. overflow: hidden;
  141. cursor: pointer;
  142. transition: all 0.2s;
  143. }
  144. .ai-scheme-option:hover {
  145. transform: translateY(-2px);
  146. }
  147. .ai-scheme-option.selected {
  148. border-color: #4D6BFE;
  149. }
  150. .ai-scheme-preview {
  151. display: flex;
  152. flex-direction: column;
  153. height: 100px;
  154. }
  155. .ai-scheme-top {
  156. flex: 3;
  157. display: flex;
  158. align-items: center;
  159. justify-content: center;
  160. font-weight: bold;
  161. }
  162. .ai-scheme-bottom {
  163. flex: 1;
  164. display: flex;
  165. align-items: center;
  166. justify-content: center;
  167. font-size: 12px;
  168. }
  169. .ai-modal-buttons {
  170. display: flex;
  171. justify-content: flex-end;
  172. gap: 10px;
  173. }
  174. .ai-modal-button {
  175. padding: 8px 16px;
  176. border-radius: 4px;
  177. border: none;
  178. cursor: pointer;
  179. font-weight: bold;
  180. }
  181. .ai-modal-button.primary {
  182. background-color: #4D6BFE;
  183. color: white;
  184. }
  185. .ai-modal-button.secondary {
  186. background-color: #f1f1f1;
  187. color: #333;
  188. }
  189. body.dark .ai-modal-button.secondary {
  190. background-color: #333333;
  191. color: #ffffff;
  192. }
  193. .ai-export-button {
  194. position: absolute;
  195. top: 10px;
  196. right: 10px;
  197. background-color: #4D6BFE;
  198. color: white;
  199. border: none;
  200. border-radius: 4px;
  201. padding: 4px 8px;
  202. font-size: 12px;
  203. cursor: pointer;
  204. opacity: 0;
  205. transition: opacity 0.2s;
  206. z-index: 100;
  207. }
  208. ._4f9bf79:hover .ai-export-button {
  209. opacity: 1;
  210. }
  211. `;
  212. document.head.appendChild(style);
  213. }
  214.  
  215. // Create FAB
  216. function createFAB() {
  217. const fab = document.createElement('div');
  218. fab.innerHTML = `
  219. <div class="ai-export-fab">
  220. <div class="ai-export-fab-icon">
  221. <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  222. <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
  223. <polyline points="7 10 12 15 17 10"></polyline>
  224. <line x1="12" y1="15" x2="12" y2="3"></line>
  225. </svg>
  226. </div>
  227. <span class="ai-export-tooltip">${texts.exportBtn} (${texts.lastAnswer})</span>
  228. </div>
  229. `;
  230. document.body.appendChild(fab);
  231.  
  232. fab.querySelector('.ai-export-fab').addEventListener('click', async () => {
  233. const aiDivs = document.querySelectorAll('div._4f9bf79._43c05b5');
  234. if (!aiDivs.length) {
  235. alert(texts.notFound);
  236. return;
  237. }
  238. showColorSchemeModal(aiDivs[aiDivs.length - 1]);
  239. });
  240. }
  241.  
  242. // Add export buttons to each AI answer
  243. function addExportButtonsToAnswers() {
  244. const observer = new MutationObserver(() => {
  245. const aiDivs = document.querySelectorAll('div._4f9bf79._43c05b5');
  246. aiDivs.forEach(aiDiv => {
  247. if (!aiDiv.querySelector('.ai-export-button')) {
  248. const exportButton = document.createElement('button');
  249. exportButton.className = 'ai-export-button';
  250. exportButton.textContent = texts.exportBtn;
  251. exportButton.addEventListener('click', () => {
  252. showColorSchemeModal(aiDiv);
  253. });
  254.  
  255. const parentDiv = aiDiv.closest('.ds-relative') || aiDiv;
  256. parentDiv.style.position = 'relative';
  257. parentDiv.appendChild(exportButton);
  258. }
  259. });
  260. });
  261.  
  262. observer.observe(document.body, { childList: true, subtree: true });
  263.  
  264. const aiDivs = document.querySelectorAll('div._4f9bf79._43c05b5');
  265. aiDivs.forEach(aiDiv => {
  266. if (!aiDiv.querySelector('.ai-export-button')) {
  267. const exportButton = document.createElement('button');
  268. exportButton.className = 'ai-export-button';
  269. exportButton.textContent = texts.exportBtn;
  270. exportButton.addEventListener('click', () => {
  271. showColorSchemeModal(aiDiv);
  272. });
  273.  
  274. const parentDiv = aiDiv.closest('.ds-relative') || aiDiv;
  275. parentDiv.style.position = 'relative';
  276. parentDiv.appendChild(exportButton);
  277. }
  278. });
  279. }
  280.  
  281. // Show color scheme modal
  282. function showColorSchemeModal(aiDiv) {
  283. const modal = document.createElement('div');
  284. modal.className = 'ai-color-scheme-modal';
  285. let selectedSchemeIndex = 0;
  286.  
  287. modal.innerHTML = `
  288. <div class="ai-modal-content">
  289. <div class="ai-modal-header">${texts.selectScheme}</div>
  290. <div class="ai-scheme-options">
  291. ${texts.colorSchemes.map((scheme, index) => `
  292. <div class="ai-scheme-option ${index === 0 ? 'selected' : ''}" data-index="${index}">
  293. <div class="ai-scheme-preview">
  294. <div class="ai-scheme-top" style="background-color:${scheme.top};color:${scheme.textTop}">AI</div>
  295. <div class="ai-scheme-bottom" style="background-color:${scheme.bottom};color:${scheme.textBottom}">DeepSeek</div>
  296. </div>
  297. </div>
  298. `).join('')}
  299. </div>
  300. <div class="ai-modal-buttons">
  301. <button class="ai-modal-button secondary" id="ai-cancel-btn">${texts.cancel}</button>
  302. <button class="ai-modal-button primary" id="ai-export-btn">${texts.export}</button>
  303. </div>
  304. </div>
  305. `;
  306.  
  307. document.body.appendChild(modal);
  308.  
  309. const schemeOptions = modal.querySelectorAll('.ai-scheme-option');
  310. schemeOptions.forEach(option => {
  311. option.addEventListener('click', () => {
  312. schemeOptions.forEach(opt => opt.classList.remove('selected'));
  313. option.classList.add('selected');
  314. selectedSchemeIndex = parseInt(option.dataset.index);
  315. });
  316. });
  317.  
  318. modal.querySelector('#ai-cancel-btn').addEventListener('click', () => {
  319. document.body.removeChild(modal);
  320. });
  321.  
  322. modal.querySelector('#ai-export-btn').addEventListener('click', async () => {
  323. document.body.removeChild(modal);
  324. await generateAndDownloadImage(aiDiv, texts.colorSchemes[selectedSchemeIndex], texts);
  325. });
  326. }
  327.  
  328. // Generate and download image (fixed typo)
  329. async function generateAndDownloadImage(aiDiv, colorScheme, texts) {
  330. const clonedDiv = aiDiv.cloneNode(true);
  331. const buttonDiv = clonedDiv.querySelector('.ds-flex[style*="margin-top"]');
  332. if (buttonDiv) buttonDiv.remove();
  333.  
  334. const container = document.createElement('div');
  335. container.style.width = '600px';
  336. container.style.borderRadius = '10px';
  337. container.style.overflow = 'hidden';
  338. container.style.boxShadow = '0 4px 8px rgba(0,0,0,0.1)';
  339. container.style.display = 'flex';
  340. container.style.flexDirection = 'column';
  341.  
  342. const topSection = document.createElement('div');
  343. topSection.style.padding = '20px';
  344. topSection.style.backgroundColor = colorScheme.top;
  345. topSection.style.color = colorScheme.textTop;
  346.  
  347. fixTextContrast(clonedDiv, colorScheme);
  348. topSection.appendChild(clonedDiv);
  349.  
  350. const bottomSection = document.createElement('div');
  351. bottomSection.textContent = texts.footer;
  352. bottomSection.style.padding = '10px';
  353. bottomSection.style.textAlign = 'center';
  354. bottomSection.style.fontSize = '14px';
  355. bottomSection.style.fontFamily = 'Arial, sans-serif';
  356. bottomSection.style.backgroundColor = colorScheme.bottom;
  357. bottomSection.style.color = colorScheme.textBottom;
  358.  
  359. container.appendChild(topSection);
  360. container.appendChild(bottomSection);
  361.  
  362. container.style.position = 'absolute';
  363. container.style.left = '-9999px';
  364. document.body.appendChild(container);
  365.  
  366. try {
  367. const canvas = await html2canvas(container, {
  368. scale: 2,
  369. backgroundColor: null,
  370. useCORS: true,
  371. logging: false
  372. });
  373.  
  374. const link = document.createElement('a');
  375. link.download = `ai_answer_${new Date().toISOString().split('T')[0]}.png`;
  376. link.href = canvas.toDataURL('image/png');
  377. link.click();
  378. } catch (error) {
  379. console.error('Error generating image:', error);
  380. alert(texts.error);
  381. }
  382.  
  383. document.body.removeChild(container);
  384. }
  385.  
  386. // Fix text contrast
  387. function fixTextContrast(element, colorScheme) {
  388. if (colorScheme.textTop === '#FFFFFF') {
  389. const allTextElements = element.querySelectorAll('*');
  390. allTextElements.forEach(el => {
  391. if (el.childNodes && Array.from(el.childNodes).some(node =>
  392. node.nodeType === Node.TEXT_NODE && node.textContent.trim().length > 0)) {
  393. const computedStyle = window.getComputedStyle(el);
  394. const currentColor = computedStyle.color;
  395. const isDarkColor = currentColor === 'rgb(0, 0, 0)' || currentColor === '#000000' || currentColor === 'black';
  396. const isCloseToBlack = currentColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/) &&
  397. parseInt(RegExp.$1) < 100 &&
  398. parseInt(RegExp.$2) < 100 &&
  399. parseInt(RegExp.$3) < 100;
  400. if (isDarkColor || isCloseToBlack) {
  401. el.style.color = '#FFFFFF';
  402. }
  403. }
  404. });
  405.  
  406. const codeElements = element.querySelectorAll('pre, code');
  407. codeElements.forEach(codeEl => {
  408. if (codeEl.tagName === 'PRE') {
  409. codeEl.style.backgroundColor = '#2A2A2A';
  410. codeEl.style.border = '1px solid #444';
  411. codeEl.style.color = '#E0E0E0';
  412. }
  413. if (codeEl.tagName === 'CODE' && codeEl.parentElement.tagName !== 'PRE') {
  414. codeEl.style.backgroundColor = '#3A3A3A';
  415. codeEl.style.color = '#E0E0E0';
  416. codeEl.style.padding = '2px 4px';
  417. codeEl.style.borderRadius = '3px';
  418. }
  419. const syntaxElements = codeEl.querySelectorAll('span');
  420. syntaxElements.forEach(span => {
  421. const spanColor = window.getComputedStyle(span).color;
  422. if (spanColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/) &&
  423. parseInt(RegExp.$1) < 100 &&
  424. parseInt(RegExp.$2) < 100 &&
  425. parseInt(RegExp.$3) < 100) {
  426. const r = parseInt(RegExp.$1);
  427. const g = parseInt(RegExp.$2);
  428. const b = parseInt(RegExp.$3);
  429. if (r > g && r > b) {
  430. span.style.color = '#FF9090';
  431. } else if (g > r && g > b) {
  432. span.style.color = '#90FF90';
  433. } else if (b > r && b > g) {
  434. span.style.color = '#9090FF';
  435. } else {
  436. span.style.color = '#E0E0E0';
  437. }
  438. }
  439. });
  440. });
  441. }
  442. }
  443.  
  444. // Initialize
  445. function init() {
  446. addStyles();
  447. createFAB();
  448. addExportButtonsToAnswers();
  449. }
  450.  
  451. if (document.readyState === 'loading') {
  452. document.addEventListener('DOMContentLoaded', init);
  453. } else {
  454. init();
  455. }
  456. })();

QingJ © 2025

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