DeepSeek Screenshoter

将DeepSeek回答内容转为图片并复制到剪贴板

  1. // ==UserScript==
  2. // @name DeepSeek Screenshoter
  3. // @namespace https://gf.qytechs.cn/users/1312316
  4. // @version 1.4.1
  5. // @description 将DeepSeek回答内容转为图片并复制到剪贴板
  6. // @author 星小韵
  7. // @iconURL https://www.google.com/s2/favicons?sz=64&domain=chat.deepseek.com
  8. // @icon64URL https://www.google.com/s2/favicons?sz=64&domain=chat.deepseek.com
  9. // @match https://chat.deepseek.com/*
  10. // @connect deepseek.com
  11. // @require https://fastly.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js
  12. // @grant GM_addStyle
  13. // @grant GM_setClipboard
  14. // @license MIT License
  15. // @namespace http://tampermonkey.net/
  16. // ==/UserScript==
  17.  
  18. (function () {
  19. "use strict";
  20.  
  21. // 顶部气泡提示系统
  22. function showToast(message, type = "info", duration = 3000) {
  23. const toast = document.createElement("div");
  24. toast.className = `ds-toast ds-toast-${type}`;
  25. toast.innerHTML = `
  26. ${type === "loading" ? `<div class="ds-spinner"></div>` : ""}
  27. <span>${message}</span>
  28. `;
  29.  
  30. document.body.appendChild(toast);
  31.  
  32. setTimeout(() => toast.classList.add("show"), 10);
  33. if (duration > 0) {
  34. setTimeout(() => {
  35. toast.classList.remove("show");
  36. setTimeout(() => toast.remove(), 300);
  37. }, duration);
  38. }
  39. return toast;
  40. }
  41.  
  42. // 全局样式
  43. GM_addStyle(`
  44. /* 统一按钮样式 */
  45. .ds-icon-button:hover {
  46. background-color: var(--ds-icon-button-hover-color, #44444D) !important;
  47. }
  48. /* 提示气泡 */
  49. [data-tooltip]::before, [data-tooltip]::after {
  50. transition: opacity 0.2s ease;
  51. opacity: 0;
  52. pointer-events: none;
  53. }
  54. [data-tooltip]::before {
  55. content: attr(data-tooltip);
  56. position: absolute;
  57. bottom: 100%;
  58. left: 50%;
  59. transform: translateX(-50%);
  60. background: rgb(0, 0, 0);
  61. color: white;
  62. padding: 4px 8px;
  63. border-radius: 4px;
  64. font-size: 12px;
  65. white-space: nowrap;
  66. margin-bottom: 6px;
  67. }
  68. [data-tooltip]::after {
  69. content: '';
  70. position: absolute;
  71. bottom: calc(100% - 4px);
  72. left: 50%;
  73. transform: translateX(-50%);
  74. border: 4px solid transparent;
  75. border-top-color: rgba(0, 0, 0, 0.8);
  76. }
  77. [data-tooltip]:hover::before,
  78. [data-tooltip]:hover::after {
  79. opacity: 1;
  80. }
  81. /* 消息气泡样式 */
  82. .ds-bubble {
  83. background: rgba(255,255,255,0.05);
  84. border-radius: 12px;
  85. padding: 16px;
  86. position: relative;
  87. color: #CDD4DF;
  88. line-height: 1.6;
  89. box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  90. word-break: break-word;
  91. overflow-wrap: anywhere;
  92. }
  93. .ds-bubble::before {
  94. content: '';
  95. position: absolute;
  96. left: -16px;
  97. top: 12px;
  98. border: 8px solid transparent;
  99. border-right-color: rgba(255,255,255,0.05);
  100. }
  101. /* 顶部提示 */
  102. .ds-toast {
  103. position: fixed;
  104. top: 20px;
  105. left: 50%;
  106. transform: translateX(-50%);
  107. background: rgba(40, 42, 54, 0.95);
  108. color: #FFFFFF;
  109. padding: 12px 24px;
  110. border-radius: 8px;
  111. font-size: 14px;
  112. font-family: -apple-system, system-ui;
  113. display: inline-flex;
  114. align-items: center;
  115. gap: 12px;
  116. opacity: 0;
  117. transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  118. z-index: 99999;
  119. box-shadow: 0 4px 12px rgba(0,0,0,0.25);
  120. backdrop-filter: blur(4px);
  121. }
  122. .ds-toast.show { opacity: 1; transform: translateX(-50%) translateY(10px); }
  123. .ds-spinner {
  124. width: 18px;
  125. height: 18px;
  126. border: 2px solid rgba(255,255,255,0.3);
  127. border-top-color: #00E599;
  128. border-radius: 50%;
  129. animation: ds-spin 0.8s linear infinite;
  130. }
  131. .ds-toast-success {
  132. background: rgba(0, 184, 120, 0.95);
  133. .ds-toast-error {
  134. background: rgba(255, 85, 85, 0.95);
  135. }
  136. @keyframes ds-spin { to { transform: rotate(360deg); } }
  137. /* 覆盖markdown样式 */
  138. .ds-capture-markdown * {
  139. color: #CDD4DF !important;
  140. font-family: system-ui !important;
  141. max-width: 100% !important;
  142. }
  143. .ds-capture-markdown pre {
  144. background: rgba(0,0,0,0.3) !important;
  145. padding: 12px !important;
  146. border-radius: 8px !important;
  147. }
  148. .ds-capture-markdown code {
  149. background: transparent !important;
  150. padding: 0 !important;
  151. }
  152. .ds-capture-markdown img {
  153. max-width: 100% !important;
  154. border-radius: 8px;
  155. }
  156. `);
  157.  
  158. // 创建截图按钮
  159. function createScreenshotButton(container) {
  160. const btn = document.createElement("div");
  161. btn.className = "ds-screenshot-btn";
  162. btn.setAttribute("data-tooltip", "截图");
  163. btn.style.cssText = `
  164. --ds-icon-button-text-color: #CDD4DF;
  165. padding: 4px;
  166. background: transparent;
  167. border-radius: 4px;
  168. cursor: pointer;
  169. display: inline-flex;
  170. align-items: center;
  171. justify-content: center;
  172. transition: background-color 0.2s ease;
  173. position: relative;
  174. `;
  175.  
  176. btn.innerHTML = `<svg viewBox="0 0 1024 1024" width="23" height="23"><path d="M853.333333 219.428571a73.142857 73.142857 0 0 1 73.142857 73.142858v487.619047a73.142857 73.142857 0 0 1-73.142857 73.142857H170.666667a73.142857 73.142857 0 0 1-73.142857-73.142857V292.571429a73.142857 73.142857 0 0 1 73.142857-73.142858h682.666666z m0 73.142858H170.666667v487.619047h682.666666V292.571429z m-341.333333 73.142857a170.666667 170.666667 0 1 1 0 341.333333 170.666667 170.666667 0 0 1 0-341.333333z m0 73.142857a97.52381 97.52381 0 1 0 0 195.047619 97.52381 97.52381 0 0 0 0-195.047619zM853.333333 97.52381v73.142857H512V97.52381h341.333333z" fill="currentColor"></path></svg>`;
  177.  
  178. btn.onclick = async () => {
  179. let loadingToast;
  180. try {
  181. loadingToast = showToast("正在生成截图...", "loading", 0);
  182. const DeepSeekFace = container.querySelector(".eb23581b.dfa60d66");
  183. const deepSearch = container.querySelector(
  184. ".f9bf7997.d7dc56a8.c05b5566 > .a6d716f5.db5991dd"
  185. );
  186. const deepThink = container.querySelector(".edb250b1");
  187. const markdownContent = container.querySelector(".ds-markdown");
  188.  
  189. // 创建截图容器
  190. const captureWrapper = document.createElement("div");
  191. captureWrapper.style.cssText = `
  192. background: #1A1B25;
  193. border-radius: 12px;
  194. position: fixed;
  195. left: -9999px;
  196. width: calc(100% - 32px);
  197. max-width: 680px;
  198. box-shadow: 0 0 12px rgba(0,0,0,0.2);
  199. padding: 20px;
  200. `;
  201.  
  202. // 消息气泡容器
  203. const messageContainer = document.createElement("div");
  204. messageContainer.style.cssText = `
  205. display: flex;
  206. gap: 16px;
  207. align-items: flex-start;
  208. margin-bottom: 20px;
  209. min-width: 300px;
  210. `;
  211.  
  212. // 左侧头像
  213. if (DeepSeekFace) {
  214. const cloneFace = DeepSeekFace.cloneNode(true);
  215. cloneFace.style.cssText = `
  216. width: 48px;
  217. height: 48px;
  218. flex-shrink: 0;
  219. margin-top: 2px;
  220. position: relative;
  221. z-index: 1;
  222. `;
  223. messageContainer.appendChild(cloneFace);
  224. }
  225.  
  226. // 右侧气泡
  227. const bubble = document.createElement("div");
  228. bubble.className = "ds-bubble";
  229. bubble.style.cssText = `
  230. flex: 1;
  231. min-width: 240px;
  232. max-width: calc(100% - 64px);
  233. margin-right: 8px;
  234. `;
  235.  
  236. // 克隆内容
  237. if (deepSearch) {
  238. const cloneSearch = deepSearch.cloneNode(true);
  239. bubble.appendChild(cloneSearch);
  240. if (deepThink) {
  241. cloneSearch.style.marginBottom = "10px";
  242. } else {
  243. cloneSearch.style.margin = "0";
  244. }
  245. }
  246. if (deepThink) {
  247. const cloneDeep = deepThink.cloneNode(true);
  248. cloneDeep.style.margin = "0";
  249. bubble.appendChild(cloneDeep);
  250. }
  251. if (markdownContent) {
  252. const cloneMarkdown = markdownContent.cloneNode(true);
  253. cloneMarkdown.className += " ds-capture-markdown";
  254. bubble.appendChild(cloneMarkdown);
  255. }
  256.  
  257. messageContainer.appendChild(bubble);
  258. captureWrapper.appendChild(messageContainer);
  259.  
  260. // 版权声明
  261. const footer = document.createElement("div");
  262. footer.style.cssText = `
  263. margin-top: 20px;
  264. padding-top: 12px;
  265. border-top: 1px solid rgba(255,255,255,0.1);
  266. color: rgba(205, 212, 223, 0.6);
  267. font-size: 12px;
  268. text-align: right;
  269. `;
  270. footer.textContent =
  271. "内容由 DeepSeek 生成,图片由 DeepSeek Screenshoter 生成";
  272. captureWrapper.appendChild(footer);
  273.  
  274. document.body.appendChild(captureWrapper);
  275.  
  276. // 生成截图
  277. const canvas = await html2canvas(captureWrapper, {
  278. useCORS: true,
  279. logging: false,
  280. scale: 2,
  281. backgroundColor: "#1A1B25",
  282. });
  283.  
  284. document.body.removeChild(captureWrapper);
  285.  
  286. canvas.toBlob(async (blob) => {
  287. try {
  288. await navigator.clipboard.write([
  289. new ClipboardItem({ "image/png": blob }),
  290. ]);
  291.  
  292. loadingToast.innerHTML = `
  293. <svg width="18" height="18" viewBox="0 0 24 24" fill="none">
  294. <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z" fill="#fff"/>
  295. </svg>
  296. <span>截图已复制到剪贴板!</span>
  297. `;
  298. loadingToast.classList.add("ds-toast-success");
  299. loadingToast.classList.remove("ds-toast-loading");
  300.  
  301. setTimeout(() => {
  302. loadingToast.classList.remove("show");
  303. setTimeout(() => loadingToast.remove(), 300);
  304. }, 2000);
  305. } catch (err) {
  306. console.error("复制失败:", err);
  307. throw new Error("clipboard-error");
  308. }
  309. }, "image/png");
  310. } catch (error) {
  311. if (loadingToast) {
  312. loadingToast.innerHTML = `
  313. <svg width="18" height="18" viewBox="0 0 24 24" fill="none">
  314. <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" fill="#fff"/>
  315. </svg>
  316. <span>${
  317. error.message === "clipboard-error"
  318. ? "请手动粘贴图片"
  319. : "生成失败,请重试"
  320. }</span>
  321. `;
  322. loadingToast.classList.add("ds-toast-error");
  323. loadingToast.classList.remove("ds-toast-loading", "show");
  324. setTimeout(() => {
  325. loadingToast.classList.add("show");
  326. setTimeout(() => {
  327. loadingToast.classList.remove("show");
  328. setTimeout(() => loadingToast.remove(), 300);
  329. }, 3000);
  330. }, 10);
  331. }
  332. }
  333. };
  334.  
  335. return btn;
  336. }
  337.  
  338. // 处理消息容器
  339. function processAnswers() {
  340. document.querySelectorAll(".f9bf7997.c05b5566").forEach((container) => {
  341. const buttonBar = container.querySelector(".ds-flex.abe97156");
  342. if (!buttonBar) return;
  343.  
  344. if (!buttonBar.querySelector(".ds-screenshot-btn")) {
  345. const btn = createScreenshotButton(container);
  346. buttonBar.appendChild(btn);
  347. }
  348. });
  349. }
  350.  
  351. // 初始运行
  352. processAnswers();
  353.  
  354. // 监听动态内容
  355. new MutationObserver((mutations) => {
  356. mutations.forEach((mutation) => {
  357. if (mutation.addedNodes.length) {
  358. setTimeout(processAnswers, 500);
  359. }
  360. });
  361. }).observe(document.body, {
  362. childList: true,
  363. subtree: true,
  364. });
  365. })();

QingJ © 2025

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