Full document, spreadsheet, slideshow, and diagram tooling
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 96 lines 3.3 kB view raw
1/** 2 * DOCX Export (#103) 3 * 4 * Exports the TipTap editor content as a .docx file using the 5 * Word-compatible HTML approach. Wraps the editor HTML in a 6 * Word XML envelope that Microsoft Word and LibreOffice can open. 7 * 8 * This avoids heavy dependencies while producing correct output 9 * for the most common formatting: headings, lists, bold, italic, 10 * underline, tables, images, links, and code blocks. 11 */ 12 13const WORD_HTML_PREFIX = `<!DOCTYPE html> 14<html xmlns:o="urn:schemas-microsoft-com:office:office" 15 xmlns:w="urn:schemas-microsoft-com:office:word" 16 xmlns="http://www.w3.org/TR/REC-html40"> 17<head> 18<meta charset="utf-8"> 19<style> 20 body { font-family: Calibri, sans-serif; font-size: 11pt; line-height: 1.5; } 21 h1 { font-size: 20pt; font-weight: bold; } 22 h2 { font-size: 16pt; font-weight: bold; } 23 h3 { font-size: 13pt; font-weight: bold; } 24 table { border-collapse: collapse; width: 100%; } 25 td, th { border: 1px solid #999; padding: 4px 8px; } 26 th { background: #f0f0f0; font-weight: bold; } 27 code { font-family: Consolas, monospace; background: #f5f5f5; padding: 1px 3px; } 28 pre { font-family: Consolas, monospace; background: #f5f5f5; padding: 8px; white-space: pre-wrap; } 29 blockquote { border-left: 3px solid #ccc; margin-left: 0; padding-left: 12px; color: #555; } 30 a { color: #0563C1; } 31 ul[data-type="taskList"] li { list-style-type: none; } 32 ul[data-type="taskList"] li[data-checked="true"]::before { content: "☑ "; } 33 ul[data-type="taskList"] li[data-checked="false"]::before { content: "☐ "; } 34</style> 35<!--[if gte mso 9]> 36<xml> 37 <w:WordDocument> 38 <w:View>Print</w:View> 39 <w:Zoom>100</w:Zoom> 40 </w:WordDocument> 41</xml> 42<![endif]--> 43</head> 44<body>`; 45 46const WORD_HTML_SUFFIX = `</body></html>`; 47 48/** 49 * Clean up TipTap-specific HTML for Word compatibility. 50 */ 51export function cleanHtmlForWord(html: string): string { 52 let cleaned = html; 53 54 // Remove TipTap-specific attributes that Word doesn't understand 55 cleaned = cleaned.replace(/\s+data-type="[^"]*"/g, ''); 56 cleaned = cleaned.replace(/\s+data-checked="[^"]*"/g, ''); 57 cleaned = cleaned.replace(/\s+contenteditable="[^"]*"/g, ''); 58 cleaned = cleaned.replace(/\s+draggable="[^"]*"/g, ''); 59 cleaned = cleaned.replace(/\s+class="[^"]*"/g, ''); 60 61 // Convert task list checkboxes to text 62 cleaned = cleaned.replace(/<input[^>]*type="checkbox"[^>]*checked[^>]*>/gi, '☑ '); 63 cleaned = cleaned.replace(/<input[^>]*type="checkbox"[^>]*>/gi, '☐ '); 64 65 // Remove empty paragraphs that Word renders as extra space 66 cleaned = cleaned.replace(/<p><\/p>/g, '<p>&nbsp;</p>'); 67 68 return cleaned; 69} 70 71export interface DocxExportOptions { 72 editorHtml: string; 73 title: string; 74} 75 76/** 77 * Export editor content as a .docx file. 78 * Creates a Word-compatible HTML document and triggers download. 79 */ 80export function exportDocx({ editorHtml, title }: DocxExportOptions): void { 81 const cleanedHtml = cleanHtmlForWord(editorHtml); 82 const fullHtml = WORD_HTML_PREFIX + cleanedHtml + WORD_HTML_SUFFIX; 83 84 const blob = new Blob([fullHtml], { 85 type: 'application/vnd.ms-word', 86 }); 87 88 const url = URL.createObjectURL(blob); 89 const a = document.createElement('a'); 90 a.href = url; 91 a.download = `${title || 'document'}.doc`; 92 document.body.appendChild(a); 93 a.click(); 94 document.body.removeChild(a); 95 URL.revokeObjectURL(url); 96}