ChatGPT Chat Exporter - PDF

Export your ChatGPT conversations as printable PDF files

  1. // ==UserScript==
  2. // @name ChatGPT Chat Exporter - PDF
  3. // @namespace https://github.com/rashidazarang/chatgpt-chat-exporter
  4. // @version 1.0.0
  5. // @description Export your ChatGPT conversations as printable PDF files
  6. // @author Rashid Azarang
  7. // @match https://chat.openai.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=openai.com
  9. // @grant none
  10. // @license MIT
  11. // @homepageURL https://github.com/rashidazarang/chatgpt-chat-exporter
  12. // @supportURL https://github.com/rashidazarang/chatgpt-chat-exporter/issues
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. // Add a button to the ChatGPT interface
  19. function addExportButton() {
  20. // Check if our button already exists
  21. if (document.getElementById('export-pdf-button')) return;
  22.  
  23. // Find a suitable location to add the button
  24. const targetElement = document.querySelector('nav');
  25. if (!targetElement) return;
  26.  
  27. // Create the button
  28. const button = document.createElement('button');
  29. button.id = 'export-pdf-button';
  30. button.innerHTML = 'Export as PDF';
  31. button.style.cssText = `
  32. margin: 10px;
  33. padding: 10px;
  34. border-radius: 5px;
  35. background-color: #10a37f;
  36. color: white;
  37. border: none;
  38. cursor: pointer;
  39. font-size: 14px;
  40. width: calc(100% - 20px);
  41. `;
  42.  
  43. // Add click event listener
  44. button.addEventListener('click', exportPDF);
  45.  
  46. // Add button to the page
  47. targetElement.appendChild(button);
  48. }
  49.  
  50. function formatDate(date = new Date()) {
  51. return date.toISOString().split('T')[0];
  52. }
  53.  
  54. function sanitize(text) {
  55. return text.replace(/</g, '&lt;').replace(/>/g, '&gt;');
  56. }
  57.  
  58. function extractFormattedContent() {
  59. const messages = document.querySelectorAll('.text-base');
  60. let html = '';
  61.  
  62. messages.forEach((msg, index) => {
  63. const sender = index % 2 === 0 ? 'You' : 'ChatGPT';
  64. const contentBlock = msg.querySelector('.whitespace-pre-wrap, .markdown, .prose');
  65. if (!contentBlock) return;
  66.  
  67. const clone = contentBlock.cloneNode(true);
  68.  
  69. clone.querySelectorAll('pre').forEach(pre => {
  70. const code = sanitize(pre.innerText.trim());
  71. pre.replaceWith(`<pre><code>${code}</code></pre>`);
  72. });
  73.  
  74. clone.querySelectorAll('img, canvas').forEach(el => {
  75. el.replaceWith('[Image or Canvas]');
  76. });
  77.  
  78. const cleanText = sanitize(clone.innerText.trim()).replace(/\n/g, '<br>');
  79.  
  80. html += `
  81. <div class="message">
  82. <div class="sender">${sender}</div>
  83. <div class="content">${cleanText}</div>
  84. </div>
  85. `;
  86. });
  87.  
  88. return html;
  89. }
  90.  
  91. function exportPDF() {
  92. const date = formatDate();
  93. const source = window.location.href;
  94. const conversationHTML = extractFormattedContent();
  95.  
  96. const html = `
  97. <!DOCTYPE html>
  98. <html>
  99. <head>
  100. <meta charset="utf-8">
  101. <title>ChatGPT Conversation - ${date}</title>
  102. <style>
  103. body {
  104. font-family: 'Segoe UI', sans-serif;
  105. max-width: 800px;
  106. margin: auto;
  107. padding: 2rem;
  108. background: #fff;
  109. color: #333;
  110. }
  111. h1 {
  112. text-align: center;
  113. }
  114. .meta {
  115. font-size: 0.9rem;
  116. color: #555;
  117. margin-bottom: 2rem;
  118. text-align: center;
  119. }
  120. .message {
  121. margin-bottom: 2rem;
  122. padding-bottom: 1rem;
  123. border-bottom: 1px solid #ddd;
  124. }
  125. .sender {
  126. font-weight: bold;
  127. font-size: 1.1rem;
  128. margin-bottom: 0.5rem;
  129. }
  130. pre {
  131. background: #f4f4f4;
  132. padding: 1rem;
  133. overflow-x: auto;
  134. border-radius: 5px;
  135. font-family: monospace;
  136. font-size: 0.9rem;
  137. }
  138. code {
  139. white-space: pre-wrap;
  140. }
  141. .content {
  142. line-height: 1.5;
  143. }
  144. </style>
  145. </head>
  146. <body>
  147. <h1>ChatGPT Conversation</h1>
  148. <div class="meta">
  149. <div><strong>Date:</strong> ${date}</div>
  150. <div><strong>Source:</strong> <a href="${source}">${source}</a></div>
  151. </div>
  152. ${conversationHTML}
  153. <script>
  154. window.onload = () => {
  155. window.print();
  156. };
  157. </script>
  158. </body>
  159. </html>
  160. `;
  161.  
  162. const blob = new Blob([html], { type: 'text/html' });
  163. const url = URL.createObjectURL(blob);
  164. window.open(url, '_blank');
  165. }
  166.  
  167. // Add the export button when the DOM is fully loaded
  168. if (document.readyState === 'loading') {
  169. document.addEventListener('DOMContentLoaded', addExportButton);
  170. } else {
  171. addExportButton();
  172. }
  173.  
  174. // Check periodically if the button needs to be added (for SPAs like ChatGPT)
  175. setInterval(addExportButton, 3000);
  176. })();

QingJ © 2025

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