/* ELYSIA MARKDOWN STUDIO v1.0 - Database Layer IndexedDB for documents storage */ import Utils from "./utils.js"; // Initialize Dexie Database const db = new Dexie("ElysiaMarkdownStudio"); // Version 2: Add indexes for search performance db.version(2) .stores({ documents: "++id, title, createdAt, updatedAt, favorite, *tags, collection", collections: "++id, name, createdAt", templates: "++id, name, category" }) .upgrade(tx => { // Migration: Ensure all documents have required fields return tx .table("documents") .toCollection() .modify(doc => { if (!doc.collection) doc.collection = null; if (!doc.tags) doc.tags = []; if (!doc.favorite) doc.favorite = false; }); }); // Backwards compatibility: Version 1 db.version(1).stores({ documents: "++id, title, createdAt, updatedAt, favorite, *tags", collections: "++id, name, createdAt", templates: "++id, name, category" }); const DB = { // Documents CRUD async createDocument(data) { try { const doc = { id: Utils.uuid(), title: data.title || "Untitled Document", content: data.content || "", tags: data.tags || [], favorite: data.favorite || false, collection: data.collection || null, createdAt: Date.now(), updatedAt: Date.now(), wordCount: Utils.countWords(data.content || ""), charCount: Utils.countChars(data.content || "") }; await db.documents.add(doc); Utils.toast.success("Document created!"); return doc; } catch (err) { console.error("Failed to create document:", err); Utils.toast.error("Failed to create document"); return null; } }, async updateDocument(id, updates) { try { const doc = await db.documents.get(id); if (!doc) throw new Error("Document not found"); const updated = { ...doc, ...updates, updatedAt: Date.now(), wordCount: Utils.countWords(updates.content || doc.content), charCount: Utils.countChars(updates.content || doc.content) }; await db.documents.put(updated); return updated; } catch (err) { console.error("Failed to update document:", err); Utils.toast.error("Failed to save document"); return null; } }, async getDocument(id) { try { return await db.documents.get(id); } catch (err) { console.error("Failed to get document:", err); return null; } }, async getAllDocuments(filter = "all") { try { let docs = await db.documents.toArray(); // Sort by updatedAt (most recent first) docs.sort((a, b) => b.updatedAt - a.updatedAt); // Apply filters if (filter === "recent") { const weekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000; docs = docs.filter(d => d.updatedAt > weekAgo); } else if (filter === "favorites") { docs = docs.filter(d => d.favorite); } return docs; } catch (err) { console.error("Failed to get documents:", err); return []; } }, async deleteDocument(id) { try { await db.documents.delete(id); Utils.toast.success("Document deleted"); return true; } catch (err) { console.error("Failed to delete document:", err); Utils.toast.error("Failed to delete document"); return false; } }, async toggleFavorite(id) { try { const doc = await db.documents.get(id); if (!doc) return false; await db.documents.update(id, { favorite: !doc.favorite, updatedAt: Date.now() }); return !doc.favorite; } catch (err) { console.error("Failed to toggle favorite:", err); return false; } }, // Search async searchDocuments(query) { try { const docs = await db.documents.toArray(); query = query.toLowerCase(); return docs .filter(doc => { return ( doc.title.toLowerCase().includes(query) || doc.content.toLowerCase().includes(query) || (doc.tags && doc.tags.some(tag => tag.toLowerCase().includes(query))) ); }) .sort((a, b) => b.updatedAt - a.updatedAt); } catch (err) { console.error("Search failed:", err); return []; } }, // Collections async createCollection(name) { try { const collection = { name, createdAt: Date.now() }; const id = await db.collections.add(collection); Utils.toast.success(`Collection "${name}" created`); return { id, ...collection }; } catch (err) { console.error("Failed to create collection:", err); Utils.toast.error("Failed to create collection"); return null; } }, async getCollections() { try { return await db.collections.toArray(); } catch (err) { console.error("Failed to get collections:", err); return []; } }, // Templates async saveTemplate(data) { try { const template = { name: data.name, category: data.category || "Custom", content: data.content, description: data.description || "", createdAt: Date.now() }; await db.templates.add(template); Utils.toast.success(`Template "${data.name}" saved`); return template; } catch (err) { console.error("Failed to save template:", err); Utils.toast.error("Failed to save template"); return null; } }, async getTemplates() { try { return await db.templates.toArray(); } catch (err) { console.error("Failed to get templates:", err); return []; } }, // Stats async getStats() { try { const docs = await db.documents.toArray(); const totalWords = docs.reduce((sum, doc) => sum + (doc.wordCount || 0), 0); const totalChars = docs.reduce((sum, doc) => sum + (doc.charCount || 0), 0); return { totalDocuments: docs.length, totalWords, totalChars, favorites: docs.filter(d => d.favorite).length }; } catch (err) { console.error("Failed to get stats:", err); return { totalDocuments: 0, totalWords: 0, totalChars: 0, favorites: 0 }; } }, // Export/Import async exportAll() { try { const docs = await db.documents.toArray(); const collections = await db.collections.toArray(); const templates = await db.templates.toArray(); return { documents: docs, collections, templates, exportDate: Date.now(), version: "1.0" }; } catch (err) { console.error("Export failed:", err); return null; } }, async importAll(data) { try { if (data.documents) { await db.documents.bulkPut(data.documents); } if (data.collections) { await db.collections.bulkPut(data.collections); } if (data.templates) { await db.templates.bulkPut(data.templates); } Utils.toast.success("Import successful!"); return true; } catch (err) { console.error("Import failed:", err); Utils.toast.error("Import failed"); return false; } } }; export default DB;