Elysia-Suite's picture
Upload 25 files
0b194e5 verified
/*
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;