| |
| |
| |
|
|
|
|
| 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) {
|
|
|
| let plainText = content;
|
| plainText = plainText.replace(/^#+\s+/gm, "");
|
| plainText = plainText.replace(/\*\*(.*?)\*\*/g, "$1");
|
| plainText = plainText.replace(/\*(.*?)\*/g, "$1");
|
| plainText = plainText.replace(/~~(.*?)~~/g, "$1");
|
| plainText = plainText.replace(/`(.*?)`/g, "$1");
|
| plainText = plainText.replace(/\[(.*?)\]\(.*?\)/g, "$1");
|
|
|
| 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();
|
| },
|
|
|
|
|
| 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;
|
|
|