Spaces:
Running
Running
| /* | |
| ELYSIA MARKDOWN STUDIO v1.0 - Export Module | |
| Multi-format document export | |
| */ | |
| import Utils from "./utils.js"; | |
| import DB from "./db.js"; | |
| const Export = { | |
| init() { | |
| document.getElementById("btn-export").addEventListener("click", () => { | |
| Utils.modal.open("modal-export"); | |
| }); | |
| document.querySelectorAll(".export-btn").forEach(btn => { | |
| btn.addEventListener("click", () => { | |
| const format = btn.getAttribute("data-format"); | |
| this.exportDocument(format); | |
| }); | |
| }); | |
| }, | |
| exportDocument(format) { | |
| const title = window.app?.currentDoc?.title || "document"; | |
| const content = window.app?.editor.getContent(); | |
| if (!content && format !== "all" && format !== "import") { | |
| Utils.toast.warning("No content to export"); | |
| return; | |
| } | |
| Utils.modal.close("modal-export"); | |
| switch (format) { | |
| case "md": | |
| this.exportMarkdown(content, title); | |
| break; | |
| case "html": | |
| this.exportHTML(content, title); | |
| break; | |
| case "artifact": | |
| this.exportArtifact(content, title); | |
| break; | |
| case "txt": | |
| this.exportPlainText(content, title); | |
| break; | |
| case "json": | |
| this.exportJSON(content, title); | |
| break; | |
| case "clipboard": | |
| this.copyToClipboard(content); | |
| break; | |
| case "all": | |
| this.exportAllDocuments(); | |
| break; | |
| case "import": | |
| this.importDocuments(); | |
| break; | |
| } | |
| }, | |
| exportMarkdown(content, title) { | |
| const filename = Utils.sanitizeFilename(title) + ".md"; | |
| Utils.downloadFile(content, filename, "text/markdown"); | |
| Utils.toast.success("Markdown exported!"); | |
| }, | |
| exportHTML(content, title) { | |
| const html = window.app?.preview.getHTML(); | |
| const fullHTML = `<!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>${title}</title> | |
| <style> | |
| body { | |
| max-width: 800px; | |
| margin: 2rem auto; | |
| padding: 2rem; | |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; | |
| line-height: 1.6; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| ${html} | |
| </body> | |
| </html>`; | |
| const filename = Utils.sanitizeFilename(title) + ".html"; | |
| Utils.downloadFile(fullHTML, filename, "text/html"); | |
| Utils.toast.success("HTML exported!"); | |
| }, | |
| exportArtifact(content, title) { | |
| const html = window.app?.preview.getHTML(); | |
| const artifact = `<!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>${title}</title> | |
| <!-- Prism.js for syntax highlighting --> | |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-tomorrow.min.css"> | |
| <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/prism.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-javascript.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-python.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-typescript.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-json.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-css.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-bash.min.js"></script> | |
| <!-- KaTeX for math --> | |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css"> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| padding: 2rem; | |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; | |
| } | |
| .container { | |
| max-width: 900px; | |
| margin: 0 auto; | |
| background: white; | |
| border-radius: 16px; | |
| padding: 3rem; | |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); | |
| } | |
| h1, h2, h3, h4, h5, h6 { | |
| color: #667eea; | |
| margin-top: 1.5rem; | |
| margin-bottom: 0.75rem; | |
| } | |
| h1 { | |
| font-size: 2.5rem; | |
| border-bottom: 3px solid #667eea; | |
| padding-bottom: 0.5rem; | |
| } | |
| p { | |
| margin-bottom: 1rem; | |
| line-height: 1.8; | |
| } | |
| code { | |
| background: #f5f5f5; | |
| padding: 0.2rem 0.4rem; | |
| border-radius: 4px; | |
| font-family: 'Courier New', monospace; | |
| } | |
| pre { | |
| background: #2d2d2d; | |
| color: #f8f8f2; | |
| padding: 1rem; | |
| border-radius: 8px; | |
| overflow-x: auto; | |
| margin: 1rem 0; | |
| } | |
| pre code { | |
| background: none; | |
| color: inherit; | |
| padding: 0; | |
| } | |
| a { | |
| color: #667eea; | |
| text-decoration: none; | |
| } | |
| a:hover { | |
| text-decoration: underline; | |
| } | |
| blockquote { | |
| border-left: 4px solid #667eea; | |
| padding-left: 1rem; | |
| margin: 1rem 0; | |
| font-style: italic; | |
| color: #666; | |
| } | |
| table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| margin: 1rem 0; | |
| } | |
| th, td { | |
| border: 1px solid #ddd; | |
| padding: 0.75rem; | |
| text-align: left; | |
| } | |
| th { | |
| background: #f5f5f5; | |
| font-weight: 600; | |
| } | |
| img { | |
| max-width: 100%; | |
| border-radius: 8px; | |
| margin: 1rem 0; | |
| } | |
| ul, ol { | |
| margin-left: 1.5rem; | |
| margin-bottom: 1rem; | |
| } | |
| li { | |
| margin-bottom: 0.5rem; | |
| } | |
| /* Task lists */ | |
| .task-list-item { | |
| list-style: none; | |
| margin-left: -1.5rem; | |
| } | |
| .task-list-item input[type="checkbox"] { | |
| margin-right: 0.5rem; | |
| } | |
| .footer { | |
| margin-top: 3rem; | |
| padding-top: 1rem; | |
| border-top: 2px solid #eee; | |
| text-align: center; | |
| color: #999; | |
| font-size: 0.875rem; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| ${html} | |
| <div class="footer"> | |
| Created with 💎 Elysia Markdown Studio | |
| </div> | |
| </div> | |
| <script> | |
| // Re-highlight code blocks after load | |
| document.addEventListener('DOMContentLoaded', () => { | |
| if (window.Prism) { | |
| Prism.highlightAll(); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html>`; | |
| const filename = Utils.sanitizeFilename(title) + "_artifact.html"; | |
| Utils.downloadFile(artifact, filename, "text/html"); | |
| Utils.toast.success("Artifact exported!"); | |
| }, | |
| exportPlainText(content, title) { | |
| // Remove markdown formatting | |
| let plainText = content; | |
| plainText = plainText.replace(/^#+\s+/gm, ""); // Headers | |
| plainText = plainText.replace(/\*\*(.*?)\*\*/g, "$1"); // Bold | |
| plainText = plainText.replace(/\*(.*?)\*/g, "$1"); // Italic | |
| plainText = plainText.replace(/~~(.*?)~~/g, "$1"); // Strikethrough | |
| plainText = plainText.replace(/`(.*?)`/g, "$1"); // Inline code | |
| plainText = plainText.replace(/\[(.*?)\]\(.*?\)/g, "$1"); // Links | |
| const filename = Utils.sanitizeFilename(title) + ".txt"; | |
| Utils.downloadFile(plainText, filename, "text/plain"); | |
| Utils.toast.success("Plain text exported!"); | |
| }, | |
| exportJSON(content, title) { | |
| const doc = window.app?.currentDoc || {}; | |
| const data = { | |
| title, | |
| content, | |
| wordCount: Utils.countWords(content), | |
| charCount: Utils.countChars(content), | |
| tags: doc.tags || [], | |
| exportDate: new Date().toISOString(), | |
| metadata: { | |
| createdAt: doc.createdAt, | |
| updatedAt: doc.updatedAt, | |
| favorite: doc.favorite | |
| } | |
| }; | |
| const json = JSON.stringify(data, null, 2); | |
| const filename = Utils.sanitizeFilename(title) + ".json"; | |
| Utils.downloadFile(json, filename, "application/json"); | |
| Utils.toast.success("JSON exported!"); | |
| }, | |
| async exportAllDocuments() { | |
| try { | |
| const allData = await DB.exportAll(); | |
| if (!allData) { | |
| Utils.toast.error("Failed to export documents"); | |
| return; | |
| } | |
| const json = JSON.stringify(allData, null, 2); | |
| const filename = `elysia-studio-backup-${Date.now()}.json`; | |
| Utils.downloadFile(json, filename, "application/json"); | |
| Utils.toast.success(`Exported ${allData.documents.length} documents!`); | |
| } catch (err) { | |
| console.error("Export all failed:", err); | |
| Utils.toast.error("Failed to export documents"); | |
| } | |
| }, | |
| importDocuments() { | |
| const input = document.createElement("input"); | |
| input.type = "file"; | |
| input.accept = ".json"; | |
| input.onchange = async e => { | |
| const file = e.target.files[0]; | |
| if (!file) return; | |
| try { | |
| const text = await file.text(); | |
| const data = JSON.parse(text); | |
| const success = await DB.importAll(data); | |
| if (success) { | |
| Utils.toast.success("Documents imported successfully!"); | |
| window.app?.documents.loadDocuments(); | |
| } | |
| } catch (err) { | |
| console.error("Import failed:", err); | |
| Utils.toast.error("Failed to import documents. Invalid file format."); | |
| } | |
| }; | |
| input.click(); | |
| }, | |
| // Copy markdown to clipboard | |
| async copyToClipboard(content) { | |
| try { | |
| await navigator.clipboard.writeText(content); | |
| Utils.toast.success("✅ Markdown copied to clipboard!"); | |
| } catch (err) { | |
| console.error("Clipboard copy failed:", err); | |
| Utils.toast.error("Failed to copy to clipboard"); | |
| } | |
| } | |
| }; | |
| export default Export; | |