import { Workspace, DocEntity, TodoEntity, Entity } from "./types"; import { OutputData } from "@editorjs/editorjs"; /** * Convert EditorJS OutputData to Markdown */ export function docToMarkdown(content: OutputData): string { return content.blocks .map((block) => { switch (block.type) { case "header": return `${"#".repeat((block.data as any).level)} ${(block.data as any).text}`; case "paragraph": return `${(block.data as any).text}\n`; case "list": if ((block.data as any).style === "ordered") { return ((block.data as any).items as Array<{ content: string }>) .map((item, i) => `${i + 1}. ${item.content}`) .join("\n"); } else { return ((block.data as any).items as Array<{ content: string }>) .map((item) => `- ${item.content}`) .join("\n"); } case "code": return `\`\`\`\n${(block.data as any).code}\n\`\`\``; case "quote": return `> ${(block.data as any).text}`; case "delimiter": return `---`; case "image": return `![${(block.data as any).caption || ""}](${(block.data as any).file.url})`; default: return ""; } }) .join("\n\n"); } /** * Convert EditorJS OutputData to HTML */ export function docToHtml(content: OutputData): string { const htmlBlocks = content.blocks.map((block) => { switch (block.type) { case "header": return `${(block.data as any).text}`; case "paragraph": return `

${(block.data as any).text}

`; case "list": const items = ((block.data as any).items as Array<{ content: string }>) .map((item) => `
  • ${item.content}
  • `) .join("\n"); if ((block.data as any).style === "ordered") { return `
      \n${items}\n
    `; } else { return ``; } case "code": return `
    ${(block.data as any).code}
    `; case "quote": return `
    ${(block.data as any).text}
    `; case "delimiter": return `
    `; case "image": return `${(block.data as any).caption || `; default: return ""; } }); return ` ${htmlBlocks.join("\n")} `; } /** * Export a Doc entity as JSON */ export function exportDocAsJson(doc: DocEntity): string { return JSON.stringify( { name: doc.name, type: "doc", content: doc.content, createdAt: new Date(doc.createdAt).toISOString(), updatedAt: new Date(doc.updatedAt).toISOString(), }, null, 2, ); } /** * Export a Doc entity as Markdown */ export function exportDocAsMarkdown(doc: DocEntity): string { const header = `# ${doc.name}\n\nCreated: ${new Date(doc.createdAt).toISOString()}\nUpdated: ${new Date(doc.updatedAt).toISOString()}\n\n---\n\n`; const content = docToMarkdown(doc.content); return header + content; } /** * Export a Doc entity as HTML */ export function exportDocAsHtml(doc: DocEntity): string { const html = docToHtml(doc.content); const headerHtml = `

    ${doc.name}

    Created: ${new Date(doc.createdAt).toISOString()}\nUpdated: ${new Date(doc.updatedAt).toISOString()}


    `; // Insert header before the first body element return html .replace("", `${doc.name}\n`) .replace("", `\n${headerHtml}\n`); } /** * Export a Todo entity as JSON */ export function exportTodoAsJson(todo: TodoEntity): string { return JSON.stringify( { name: todo.name, type: "todo", config: todo.config, tags: todo.tags, tasks: todo.tasks, createdAt: new Date(todo.createdAt).toISOString(), updatedAt: new Date(todo.updatedAt).toISOString(), }, null, 2, ); } /** * Export a Todo entity as CSV */ export function exportTodoAsCsv(todo: TodoEntity): string { const headers = [ "Title", "Description", "Status", "Tags", "Created", "Updated", ]; const rows = Object.values(todo.tasks).map((task) => [ task.title, task.description || "", todo.config.statuses.find((s) => s.id === task.statusId)?.name || "", task.tagIds.map((id) => todo.tags[id]?.name || "").join(";"), new Date(task.createdAt).toISOString(), new Date(task.updatedAt).toISOString(), ]); const csvContent = [ headers.join(","), ...rows.map((row) => row.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(","), ), ].join("\n"); return csvContent; } /** * Export a Todo entity as Markdown */ export function exportTodoAsMarkdown(todo: TodoEntity): string { let markdown = `# ${todo.name}\n\nCreated: ${new Date(todo.createdAt).toISOString()}\nUpdated: ${new Date(todo.updatedAt).toISOString()}\n\n`; // Group tasks by status const tasksByStatus: Record = {}; for (const status of todo.config.statuses) { tasksByStatus[status.id] = {}; } for (const [taskId, task] of Object.entries(todo.tasks)) { const statusId = task.statusId; if (!tasksByStatus[statusId]) { tasksByStatus[statusId] = {}; } tasksByStatus[statusId][taskId] = task; } // Add tasks by status for (const status of todo.config.statuses) { markdown += `## ${status.name}\n\n`; const tasks = tasksByStatus[status.id]; if (Object.keys(tasks).length === 0) { markdown += "- (No tasks)\n\n"; } else { for (const task of Object.values(tasks)) { const tags = task.tagIds .map((id) => todo.tags[id]?.name || "") .filter((n) => n) .join(", "); markdown += `- **${task.title}**${tags ? ` (${tags})` : ""}`; if (task.description) { markdown += `\n - ${task.description}`; } markdown += "\n"; } } markdown += "\n"; } return markdown; } /** * Export entire workspace as JSON */ export function exportWorkspaceAsJson(workspace: Workspace): string { return JSON.stringify(workspace, null, 2); } /** * Trigger download of a file */ export function downloadFile( content: string, filename: string, mimeType: string = "text/plain", ): void { const blob = new Blob([content], { type: mimeType }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } /** * Export any entity based on its type */ export function exportEntity( entity: Entity, format: "json" | "markdown" | "html" | "csv", ): string { if (entity.type === "doc") { switch (format) { case "json": return exportDocAsJson(entity); case "markdown": return exportDocAsMarkdown(entity); case "html": return exportDocAsHtml(entity); default: return exportDocAsJson(entity); } } else if (entity.type === "todo") { switch (format) { case "json": return exportTodoAsJson(entity); case "markdown": return exportTodoAsMarkdown(entity); case "csv": return exportTodoAsCsv(entity); default: return exportTodoAsJson(entity); } } return ""; }